diff --git a/src/addons/mod/resource/services/handlers/module.ts b/src/addons/mod/resource/services/handlers/module.ts index b4594ca6a64..55ba7518bf4 100644 --- a/src/addons/mod/resource/services/handlers/module.ts +++ b/src/addons/mod/resource/services/handlers/module.ts @@ -21,12 +21,11 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/ import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; import { CoreFileHelper } from '@services/file-helper'; import { CoreMimetypeUtils } from '@services/utils/mimetype'; -import { CoreTextUtils } from '@services/utils/text'; -import { CoreTimeUtils } from '@services/utils/time'; import { makeSingleton, Translate } from '@singletons'; import { AddonModResourceIndexComponent } from '../../components/index'; -import { AddonModResource, AddonModResourceCustomData } from '../resource'; +import { AddonModResource } from '../resource'; import { AddonModResourceHelper } from '../resource-helper'; +import { CoreUtils } from '@services/utils/utils'; /** * Handler to support resource modules. @@ -94,19 +93,20 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase }, }; - this.getResourceData(module, courseId, handlerData).then((extra) => { - handlerData.extraBadge = extra; + const [hideButton, extraBadge] = await Promise.all([ + CoreUtils.ignoreErrors(this.hideOpenButton(module)), + CoreUtils.ignoreErrors(AddonModResourceHelper.getAfterLinkDetails(module, courseId)), + ]); - return; - }).catch(() => { - // Ignore errors. - }); - - try { - handlerData.icon = this.getIconSrc(module); - } catch { - // Ignore errors. + // Check if the button needs to be shown or not. + if (hideButton !== undefined) { + handlerData.button.hidden = hideButton; } + if (extraBadge !== undefined) { + handlerData.extraBadge = extraBadge; + } + + handlerData.icon = this.getIconSrc(module); return handlerData; } @@ -127,102 +127,6 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase 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 { - const promises: Promise[] = []; - 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 */ diff --git a/src/addons/mod/resource/services/resource-helper.ts b/src/addons/mod/resource/services/resource-helper.ts index e781f0b545d..c66f0cb160c 100644 --- a/src/addons/mod/resource/services/resource-helper.ts +++ b/src/addons/mod/resource/services/resource-helper.ts @@ -27,8 +27,10 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; import { CoreUtilsOpenFileOptions } from '@services/utils/utils'; import { makeSingleton, Translate } from '@singletons'; import { CorePath } from '@singletons/path'; -import { AddonModResource, AddonModResourceProvider } from './resource'; +import { AddonModResource, AddonModResourceCustomData, AddonModResourceProvider } from './resource'; 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. @@ -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 { + 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 { + 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); diff --git a/src/addons/mod/resource/services/resource.ts b/src/addons/mod/resource/services/resource.ts index 2b590b2c60c..2b0d1afe35c 100644 --- a/src/addons/mod/resource/services/resource.ts +++ b/src/addons/mod/resource/services/resource.ts @@ -200,12 +200,20 @@ export type AddonModResourceResource = { }; export type AddonModResourceCustomData = { - showsize?: boolean; filedetails?: { - size: number; - modifieddate: number; - uploadeddate: number; + isref?: boolean; // If file is a reference the 'size' or 'date' attribute can not be cached. + // If showsize is true. + 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; showdate?: boolean; printintro?: boolean;