forked from EVOgeek/Vmeda.Online
		
	
						commit
						63bd215afe
					
				| @ -11,6 +11,10 @@ | ||||
|     <!-- Activity info. --> | ||||
|     <core-course-module-info [module]="module" [courseId]="courseId" [description]="displayDescription && description" | ||||
|         [component]="component" [componentId]="componentId" (completionChanged)="onCompletionChange()"> | ||||
|         <div class="addon-mod_resource-afterlink ion-text-wrap" *ngIf="module.afterlink" description> | ||||
|             <core-format-text [text]="module.afterlink" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"> | ||||
|             </core-format-text> | ||||
|         </div> | ||||
|     </core-course-module-info> | ||||
| 
 | ||||
|     <ion-card class="core-warning-card" *ngIf="warning"> | ||||
|  | ||||
| @ -8,4 +8,8 @@ | ||||
|             font-size: 24px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .addon-mod_resource-afterlink { | ||||
|         font-size: var(--text-size); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -112,6 +112,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | ||||
|             throw new CoreError(Translate.instant('core.filenotfound')); | ||||
|         } | ||||
| 
 | ||||
|         this.module.afterlink = await AddonModResourceHelper.getAfterLinkDetails(this.module, this.courseId); | ||||
| 
 | ||||
|         // Get the resource instance to get the latest name/description and to know if it's embedded.
 | ||||
|         const resource = await AddonModResource.getResourceData(this.courseId, this.module.id); | ||||
|         this.description = resource.intro || ''; | ||||
|  | ||||
| @ -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<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 | ||||
|      */ | ||||
|  | ||||
| @ -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<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); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -840,6 +840,9 @@ body.core-iframe-fullscreen ion-router-outlet { | ||||
| .item-dimmed { | ||||
|     opacity: 0.7; | ||||
|     --background: var(--light); | ||||
|     ion-item { | ||||
|         --background: var(--light); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Extra text colors. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user