MOBILE-4348 resource: Fix display options gathering
parent
c01ec44710
commit
13ddbfc4b9
|
@ -21,12 +21,11 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||||
import { CoreFileHelper } from '@services/file-helper';
|
import { CoreFileHelper } from '@services/file-helper';
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { AddonModResourceIndexComponent } from '../../components/index';
|
import { AddonModResourceIndexComponent } from '../../components/index';
|
||||||
import { AddonModResource, AddonModResourceCustomData } from '../resource';
|
import { AddonModResource } from '../resource';
|
||||||
import { AddonModResourceHelper } from '../resource-helper';
|
import { AddonModResourceHelper } from '../resource-helper';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to support resource modules.
|
* Handler to support resource modules.
|
||||||
|
@ -94,19 +93,20 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getResourceData(module, courseId, handlerData).then((extra) => {
|
const [hideButton, extraBadge] = await Promise.all([
|
||||||
handlerData.extraBadge = extra;
|
CoreUtils.ignoreErrors(this.hideOpenButton(module)),
|
||||||
|
CoreUtils.ignoreErrors(AddonModResourceHelper.getAfterLinkDetails(module, courseId)),
|
||||||
|
]);
|
||||||
|
|
||||||
return;
|
// Check if the button needs to be shown or not.
|
||||||
}).catch(() => {
|
if (hideButton !== undefined) {
|
||||||
// Ignore errors.
|
handlerData.button.hidden = hideButton;
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
handlerData.icon = this.getIconSrc(module);
|
|
||||||
} catch {
|
|
||||||
// Ignore errors.
|
|
||||||
}
|
}
|
||||||
|
if (extraBadge !== undefined) {
|
||||||
|
handlerData.extraBadge = extraBadge;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerData.icon = this.getIconSrc(module);
|
||||||
|
|
||||||
return handlerData;
|
return handlerData;
|
||||||
}
|
}
|
||||||
|
@ -127,102 +127,6 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
return status !== CoreConstants.DOWNLOADED || AddonModResourceHelper.isDisplayedInIframe(module);
|
return status !== CoreConstants.DOWNLOADED || AddonModResourceHelper.isDisplayedInIframe(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the activity icon and data.
|
|
||||||
*
|
|
||||||
* @param module The module object.
|
|
||||||
* @param courseId The course ID.
|
|
||||||
* @returns Resource data.
|
|
||||||
*/
|
|
||||||
protected async getResourceData(
|
|
||||||
module: CoreCourseModuleData,
|
|
||||||
courseId: number,
|
|
||||||
handlerData: CoreCourseModuleHandlerData,
|
|
||||||
): Promise<string> {
|
|
||||||
const promises: Promise<void>[] = [];
|
|
||||||
let options: AddonModResourceCustomData = {};
|
|
||||||
|
|
||||||
// Check if the button needs to be shown or not.
|
|
||||||
promises.push(this.hideOpenButton(module).then((hideOpenButton) => {
|
|
||||||
if (!handlerData.button) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlerData.button.hidden = hideOpenButton;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (module.customdata !== undefined) {
|
|
||||||
options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata));
|
|
||||||
} else {
|
|
||||||
// Get the resource data.
|
|
||||||
promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => {
|
|
||||||
options = CoreTextUtils.unserialize(info.displayoptions);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
if (module.contentsinfo) {
|
|
||||||
// No need to use the list of files.
|
|
||||||
return CoreTextUtils.cleanTags(module.afterlink);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!module.contents || !module.contents[0]) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const extra: string[] = [];
|
|
||||||
const files = module.contents;
|
|
||||||
const mainFile = files[0];
|
|
||||||
|
|
||||||
if (options.showsize) {
|
|
||||||
const size = options.filedetails
|
|
||||||
? options.filedetails.size
|
|
||||||
: files.reduce((result, file) => result + (file.filesize || 0), 0);
|
|
||||||
|
|
||||||
extra.push(CoreTextUtils.bytesToSize(size, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.showtype) {
|
|
||||||
// We should take it from options.filedetails.size if available but it's already translated.
|
|
||||||
extra.push(CoreMimetypeUtils.getMimetypeDescription(mainFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.showdate) {
|
|
||||||
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;
|
|
||||||
|
|
||||||
if (options.filedetails && options.filedetails.modifieddate) {
|
|
||||||
extra.push(Translate.instant(
|
|
||||||
'addon.mod_resource.modifieddate',
|
|
||||||
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
|
|
||||||
));
|
|
||||||
} else if (options.filedetails && options.filedetails.uploadeddate) {
|
|
||||||
extra.push(Translate.instant(
|
|
||||||
'addon.mod_resource.uploadeddate',
|
|
||||||
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
|
|
||||||
));
|
|
||||||
} else if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
|
|
||||||
/* Modified date may be up to several minutes later than uploaded date just because
|
|
||||||
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
|
|
||||||
extra.push(Translate.instant(
|
|
||||||
'addon.mod_resource.modifieddate',
|
|
||||||
{ $a: CoreTimeUtils.userDate((mainFile.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
extra.push(Translate.instant(
|
|
||||||
'addon.mod_resource.uploadeddate',
|
|
||||||
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extra.join(' · ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,8 +27,10 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
import { CoreUtilsOpenFileOptions } from '@services/utils/utils';
|
import { CoreUtilsOpenFileOptions } from '@services/utils/utils';
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { CorePath } from '@singletons/path';
|
import { CorePath } from '@singletons/path';
|
||||||
import { AddonModResource, AddonModResourceProvider } from './resource';
|
import { AddonModResource, AddonModResourceCustomData, AddonModResourceProvider } from './resource';
|
||||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides helper functions for resources.
|
* Service that provides helper functions for resources.
|
||||||
|
@ -222,5 +224,116 @@ export class AddonModResourceHelperProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource show options.
|
||||||
|
*
|
||||||
|
* @param module The module object.
|
||||||
|
* @param courseId The course ID.
|
||||||
|
* @returns Resource options.
|
||||||
|
*/
|
||||||
|
protected async getModuleOptions(module: CoreCourseModuleData, courseId: number): Promise<AddonModResourceCustomData> {
|
||||||
|
if (module.customdata !== undefined) {
|
||||||
|
const customData: { displayoptions: string } | string = CoreTextUtils.parseJSON(module.customdata);
|
||||||
|
const displayOptions = typeof customData === 'object' ? customData.displayoptions : customData;
|
||||||
|
|
||||||
|
return CoreTextUtils.unserialize(displayOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the resource data. Legacy version (from 3.5 to 3.6.6)
|
||||||
|
const info = await AddonModResource.getResourceData(courseId, module.id);
|
||||||
|
const options: AddonModResourceCustomData = CoreTextUtils.unserialize(info.displayoptions);
|
||||||
|
|
||||||
|
if (!module.contents?.[0] || options.filedetails !== undefined) {
|
||||||
|
// Contents attribute should be loaded at this point and it's needed to get mainFile.
|
||||||
|
// Filedetails won't be usually loaded, but if it's there's no need to check mainFile.
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill filedetails checking files in contents.
|
||||||
|
options.filedetails = {};
|
||||||
|
|
||||||
|
const files = module.contents;
|
||||||
|
const mainFile = files[0];
|
||||||
|
|
||||||
|
if (options.showsize) {
|
||||||
|
options.filedetails.size = files.reduce((result, file) => result + (file.filesize || 0), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.showtype) {
|
||||||
|
options.filedetails.type = CoreMimetypeUtils.getMimetypeDescription(mainFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.showdate) {
|
||||||
|
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;
|
||||||
|
|
||||||
|
if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
|
||||||
|
/* Modified date may be up to several minutes later than uploaded date just because
|
||||||
|
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
|
||||||
|
options.filedetails.modifieddate = mainFile.timemodified || 0;
|
||||||
|
} else {
|
||||||
|
options.filedetails.uploadeddate = timecreated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get afterlink details to be shown on the activity card.
|
||||||
|
*
|
||||||
|
* @param module The module object.
|
||||||
|
* @param courseId The course ID.
|
||||||
|
* @returns Description string to be shown on the activity card.
|
||||||
|
*/
|
||||||
|
async getAfterLinkDetails(
|
||||||
|
module: CoreCourseModuleData,
|
||||||
|
courseId: number,
|
||||||
|
): Promise<string> {
|
||||||
|
const options = await this.getModuleOptions(module, courseId);
|
||||||
|
|
||||||
|
if (!options.filedetails) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const details = options.filedetails;
|
||||||
|
|
||||||
|
const extra: string[] = [];
|
||||||
|
|
||||||
|
if (options.showsize && details.size) {
|
||||||
|
extra.push(CoreTextUtils.bytesToSize(details.size, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.showtype) {
|
||||||
|
// The order of this if conditions should not be changed.
|
||||||
|
if (details.extension) {
|
||||||
|
// From LMS 4.3 onwards only extension is shown.
|
||||||
|
extra.push(details.extension);
|
||||||
|
} else if (details.mimetype) {
|
||||||
|
// Mostly used from 3.7 to 4.2.
|
||||||
|
extra.push(CoreMimetypeUtils.getMimetypeDescription(details.mimetype));
|
||||||
|
} else if (details.type) {
|
||||||
|
// Used on 3.5 and 3.6 where mimetype populated on getModuleOptions using main file.
|
||||||
|
extra.push(details.type); // Already translated.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.showdate) {
|
||||||
|
if (details.modifieddate) {
|
||||||
|
extra.push(Translate.instant(
|
||||||
|
'addon.mod_resource.modifieddate',
|
||||||
|
{ $a: CoreTimeUtils.userDate(details.modifieddate * 1000, 'core.strftimedatetimeshort') },
|
||||||
|
));
|
||||||
|
} else if (details.uploadeddate) {
|
||||||
|
extra.push(Translate.instant(
|
||||||
|
'addon.mod_resource.uploadeddate',
|
||||||
|
{ $a: CoreTimeUtils.userDate(details.uploadeddate * 1000, 'core.strftimedatetimeshort') },
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return extra.join(' · ');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
export const AddonModResourceHelper = makeSingleton(AddonModResourceHelperProvider);
|
export const AddonModResourceHelper = makeSingleton(AddonModResourceHelperProvider);
|
||||||
|
|
|
@ -200,12 +200,20 @@ export type AddonModResourceResource = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AddonModResourceCustomData = {
|
export type AddonModResourceCustomData = {
|
||||||
showsize?: boolean;
|
|
||||||
filedetails?: {
|
filedetails?: {
|
||||||
size: number;
|
isref?: boolean; // If file is a reference the 'size' or 'date' attribute can not be cached.
|
||||||
modifieddate: number;
|
// If showsize is true.
|
||||||
uploadeddate: number;
|
size?: number; // Size in bytes.
|
||||||
|
// If showtype is true.
|
||||||
|
type?: string; // Mimetype description (already translated).
|
||||||
|
mimetype?: string; // @since LMS 3.7
|
||||||
|
extension?: string; // @since LMS 4.3
|
||||||
|
// If showdate is true.
|
||||||
|
modifieddate?: number; // Only if file has been modified.
|
||||||
|
uploadeddate?: number; // Only if file has NOT been modified.
|
||||||
|
|
||||||
};
|
};
|
||||||
|
showsize?: boolean;
|
||||||
showtype?: boolean;
|
showtype?: boolean;
|
||||||
showdate?: boolean;
|
showdate?: boolean;
|
||||||
printintro?: boolean;
|
printintro?: boolean;
|
||||||
|
|
Loading…
Reference in New Issue