MOBILE-2748 mod: Create a new module icon component
This commit is contained in:
		
							parent
							
								
									aa8c6136de
								
							
						
					
					
						commit
						22bdbc1ddc
					
				| @ -6,7 +6,8 @@ | ||||
| <core-loading [hideUntil]="loaded" [fullscreen]="false" class="margin"> | ||||
|     <ion-item class="ion-text-wrap item-media" *ngFor="let entry of entries" detail="true" button | ||||
|         (click)="gotoCoureListModType(entry)"> | ||||
|         <img slot="start" [src]="entry.icon" alt="" role="presentation" class="core-module-icon"> | ||||
|         <core-mod-icon slot="start" [modicon]="entry.icon" [modname]="entry.modName" [showAlt]="false"> | ||||
|         </core-mod-icon> | ||||
|         <ion-label>{{ entry.name }}</ion-label> | ||||
|     </ion-item> | ||||
| </core-loading> | ||||
|  | ||||
| @ -17,7 +17,9 @@ | ||||
|                 <ion-card> | ||||
|                     <ion-item class="core-course-module-handler item-media ion-text-wrap" detail="false" (click)="action($event, item)" | ||||
|                         button> | ||||
|                         <img slot="start" [src]="item.iconUrl" alt="" role="presentation" *ngIf="item.iconUrl" class="core-module-icon"> | ||||
|                         <core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl" | ||||
|                             [modname]="item.modname" [componentId]="item.cmid" [showAlt]="false"> | ||||
|                         </core-mod-icon> | ||||
|                         <ion-label> | ||||
|                             <!-- Add the icon title so accessibility tools read it. --> | ||||
|                             <span class="sr-only" *ngIf="item.iconTitle">{{ item.iconTitle }}</span> | ||||
|  | ||||
| @ -5,10 +5,10 @@ | ||||
|     <ng-container *ngFor="let event of dayEvents.events"> | ||||
|         <ion-item class="ion-text-wrap core-course-module-handler item-media" detail="false" (click)="action($event, event.url)" | ||||
|             [attr.aria-label]="event.name" button> | ||||
|             <img slot="start" [src]="event.iconUrl" alt="" role="presentation" *ngIf="event.iconUrl" class="core-module-icon"> | ||||
|             <core-mod-icon *ngIf="event.iconUrl" slot="start" [modicon]="event.iconUrl" [componentId]="event.instance" | ||||
|                 [modname]="event.modulename"> | ||||
|             </core-mod-icon> | ||||
|             <ion-label> | ||||
|                 <!-- Add the icon title so accessibility tools read it. --> | ||||
|                 <span class="sr-only" *ngIf="event.iconTitle">{{ event.iconTitle }}</span> | ||||
|                 <p class="item-heading"> | ||||
|                     <core-format-text [text]="event.name" contextLevel="module" [contextInstanceId]="event.id" | ||||
|                         [courseId]="event.course && event.course.id"> | ||||
|  | ||||
| @ -104,7 +104,8 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { | ||||
|             return start <= event.timesort; | ||||
|         }).map(async (event) => { | ||||
|             event.iconUrl = await CoreCourse.getModuleIconSrc(event.icon.component); | ||||
|             event.iconTitle = event.modulename && CoreCourse.translateModuleName(event.modulename); | ||||
|             event.modulename = event.modulename || event.icon.component; | ||||
|             event.iconTitle = CoreCourse.translateModuleName(event.modulename); | ||||
| 
 | ||||
|             return event; | ||||
|         })); | ||||
|  | ||||
| @ -9,6 +9,10 @@ | ||||
|             padding: 6px; | ||||
|         } | ||||
| 
 | ||||
|         > core-mod-icon { | ||||
|             padding: 6px; | ||||
|         } | ||||
| 
 | ||||
|         &.addon-calendar-eventtype-category > ion-icon { | ||||
|             background-color: var(--addon-calendar-event-category-color); | ||||
|         } | ||||
| @ -25,4 +29,5 @@ | ||||
|             background-color: var(--addon-calendar-event-site-color); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -88,8 +88,9 @@ | ||||
|                                 <span class="addon-calendar-event-time"> | ||||
|                                     {{ event.timestart * 1000 | coreFormatDate: timeFormat }} | ||||
|                                 </span> | ||||
|                                 <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" | ||||
|                                     class="core-module-icon"> | ||||
|                                 <core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false" | ||||
|                                     [modname]="event.modulename" [componentId]="event.instance"> | ||||
|                                 </core-mod-icon> | ||||
|                                 <!-- Add the icon title so accessibility tools read it. --> | ||||
|                                 <span class="sr-only"> | ||||
|                                     {{ 'addon.calendar.type' + event.formattedType | translate }} | ||||
|  | ||||
| @ -144,15 +144,15 @@ | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .core-module-icon { | ||||
|     core-mod-icon { | ||||
|         margin-right: 1px; | ||||
|         margin-left: 1px; | ||||
|         --size: 16px; | ||||
|         display: inline-block; | ||||
|         vertical-align: bottom; | ||||
|         ::ng-deep img { | ||||
|             display: block; | ||||
|         } | ||||
|     .core-module-icon[slot="start"] { | ||||
|         padding: 6px; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,8 +6,8 @@ | ||||
|         <ng-container *ngFor="let event of filteredEvents"> | ||||
|             <ion-item class="ion-text-wrap addon-calendar-event" [attr.aria-label]="event.name" (click)="eventClicked(event)" button | ||||
|                 [ngClass]="['addon-calendar-eventtype-'+event.eventtype]" detail="true"> | ||||
|                 <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" slot="start" class="core-module-icon" alt="" | ||||
|                     role="presentation"> | ||||
|                 <core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [modname]="event.modulename" | ||||
|                     [componentId]="event.instance" [showAlt]="false"></core-mod-icon> | ||||
|                 <ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true"> | ||||
|                 </ion-icon> | ||||
|                 <ion-label> | ||||
|  | ||||
| @ -61,8 +61,9 @@ | ||||
|             <ng-container *ngFor="let event of filteredEvents"> | ||||
|                 <ion-item class="addon-calendar-event ion-text-wrap" [attr.aria-label]="event.name" (click)="gotoEvent(event.id)" | ||||
|                 [class.item-dimmed]="event.ispast" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button detail="true"> | ||||
|                     <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" slot="start" class="core-module-icon" alt="" | ||||
|                         role="presentation"> | ||||
|                     <core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [showAlt]="false" | ||||
|                         [modname]="event.modname" [componentId]="event.instance"> | ||||
|                     </core-mod-icon> | ||||
|                     <ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true"> | ||||
|                     </ion-icon> | ||||
|                     <ion-label> | ||||
|  | ||||
| @ -4,7 +4,8 @@ | ||||
|             <ion-back-button [text]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
|         <h1 *ngIf="event"> | ||||
|             <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon"> | ||||
|             <core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false" | ||||
|                 [modname]="event.modulename" [componentId]="event.instance"></core-mod-icon> | ||||
|             <ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true"></ion-icon> | ||||
|             <!-- Add the icon title so accessibility tools read it. --> | ||||
|             <span class="sr-only"> | ||||
|  | ||||
| @ -2,8 +2,9 @@ | ||||
|     ion-card ion-note { | ||||
|         font-size: 1.6rem; | ||||
|     } | ||||
|     h1 ion-icon, h1 img { | ||||
|     h1 ion-icon, h1 img, h1 core-mod-icon { | ||||
|         margin-left: 10px; | ||||
|         margin-right: 10px; | ||||
|         display: inline-block; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -77,8 +77,7 @@ | ||||
|                     </p> | ||||
|                     <ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url" | ||||
|                         [attr.aria-label]="activity.name" core-link capture="true"> | ||||
|                         <img slot="start" core-external-content [src]="activity.iconurl" alt="" *ngIf="activity.iconurl" | ||||
|                             class="core-module-icon"> | ||||
|                         <core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl"></core-mod-icon> | ||||
|                         <ion-label> | ||||
|                             <core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id" | ||||
|                                 [courseId]="courseId"> | ||||
|  | ||||
| @ -114,8 +114,8 @@ | ||||
|                             </p> | ||||
|                             <ion-item class="ion-text-wrap core-course-module-handler item-media" [attr.aria-label]="activity.name" | ||||
|                                 core-link *ngFor="let activity of competency.coursemodules" [href]="activity.url" capture="true"> | ||||
|                                 <img slot="start" [src]="activity.iconurl" core-external-content alt="" | ||||
|                                     *ngIf="activity.iconurl" class="core-module-icon"> | ||||
|                                 <core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl"> | ||||
|                                 </core-mod-icon> | ||||
|                                 <ion-label> | ||||
|                                     <core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id" | ||||
|                                         [courseId]="courseId"> | ||||
|  | ||||
| @ -17,13 +17,8 @@ import { Injectable, Type } from '@angular/core'; | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||
| import { CoreCourseModule } from '@features/course/services/course-helper'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreFilepool } from '@services/filepool'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { DomSanitizer, makeSingleton } from '@singletons'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonModLtiHelper } from '../lti-helper'; | ||||
| import { AddonModLti, AddonModLtiProvider } from '../lti'; | ||||
| import { AddonModLtiIndexComponent } from '../../components/index'; | ||||
| import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; | ||||
| 
 | ||||
| @ -62,6 +57,9 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple | ||||
|         const data = await super.getData(module, courseId, sectionId, forCoursePage); | ||||
|         data.showDownloadButton = false; | ||||
| 
 | ||||
|         // Handle custom icons.
 | ||||
|         data.icon =  module.modicon; | ||||
| 
 | ||||
|         data.buttons = [{ | ||||
|             icon: 'fas-external-link-alt', | ||||
|             label: 'addon.mod_lti.launchactivity', | ||||
| @ -71,49 +69,9 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple | ||||
|             }, | ||||
|         }]; | ||||
| 
 | ||||
|         // Handle custom icons.
 | ||||
|         CoreUtils.ignoreErrors(this.loadCustomIcon(module, courseId, data)); | ||||
| 
 | ||||
|         return data; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Load the custom icon. | ||||
|      * | ||||
|      * @param module Module. | ||||
|      * @param courseId Course ID. | ||||
|      * @param data Handler data. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async loadCustomIcon( | ||||
|         module: CoreCourseModule, | ||||
|         courseId: number, | ||||
|         handlerData: CoreCourseModuleHandlerData, | ||||
|     ): Promise<void> { | ||||
|         const lti = await AddonModLti.getLti(courseId, module.id); | ||||
| 
 | ||||
|         const icon = lti.secureicon || lti.icon; | ||||
|         if (!icon) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const siteId = CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
|         try { | ||||
|             await CoreFilepool.downloadUrl(siteId, icon, false, AddonModLtiProvider.COMPONENT, module.id); | ||||
| 
 | ||||
|             // Get the internal URL.
 | ||||
|             const url = await CoreFilepool.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id); | ||||
| 
 | ||||
|             handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(url); | ||||
|         } catch { | ||||
|             // Error downloading. If we're online we'll set the online url.
 | ||||
|             if (CoreApp.isOnline()) { | ||||
|                 handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(icon); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { Injectable, Type } from '@angular/core'; | ||||
| import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; | ||||
| import { CoreCourse, CoreCourseModuleContentFile } from '@features/course/services/course'; | ||||
| import { CoreCourse } from '@features/course/services/course'; | ||||
| import { CoreCourseModule } from '@features/course/services/course-helper'; | ||||
| import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| @ -23,7 +23,6 @@ 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 { CoreWSFile } from '@services/ws'; | ||||
| import { makeSingleton, Translate } from '@singletons'; | ||||
| import { AddonModResourceIndexComponent } from '../../components/index'; | ||||
| import { AddonModResource, AddonModResourceCustomData } from '../resource'; | ||||
| @ -94,7 +93,7 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase | ||||
|         }]; | ||||
| 
 | ||||
|         this.getResourceData(module, courseId, handlerData).then((data) => { | ||||
|             handlerData.icon = handlerData.icon || data.icon; | ||||
|             handlerData.icon = data.icon; | ||||
|             handlerData.extraBadge = data.extra; | ||||
|             handlerData.extraBadgeColor = 'light'; | ||||
| 
 | ||||
| @ -136,7 +135,6 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase | ||||
|         handlerData: CoreCourseModuleHandlerData, | ||||
|     ): Promise<AddonResourceHandlerData> { | ||||
|         const promises: Promise<void>[] = []; | ||||
|         let infoFiles: CoreWSFile[] = []; | ||||
|         let options: AddonModResourceCustomData = {}; | ||||
| 
 | ||||
|         // Check if the button needs to be shown or not.
 | ||||
| @ -150,12 +148,11 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase | ||||
|             return; | ||||
|         })); | ||||
| 
 | ||||
|         if ('customdata' in module && typeof module.customdata != 'undefined') { | ||||
|         if ('customdata' in module && module.customdata !== undefined) { | ||||
|             options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata)); | ||||
|         } else { | ||||
|             // Get the resource data.
 | ||||
|             promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => { | ||||
|                 infoFiles = info.contentfiles; | ||||
|                 options = CoreTextUtils.unserialize(info.displayoptions); | ||||
| 
 | ||||
|                 return; | ||||
| @ -164,28 +161,22 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase | ||||
| 
 | ||||
|         await Promise.all(promises); | ||||
| 
 | ||||
|         const files: (CoreCourseModuleContentFile | CoreWSFile)[] = module.contents && module.contents.length | ||||
|             ? module.contents | ||||
|             : infoFiles; | ||||
| 
 | ||||
|         const resourceData: AddonResourceHandlerData = { | ||||
|             icon: '', | ||||
|             extra: '', | ||||
|         }; | ||||
|         let mimetypeIcon = ''; | ||||
|         const extra: string[] = []; | ||||
| 
 | ||||
|         if ('contentsinfo' in module && module.contentsinfo) { | ||||
|             // No need to use the list of files.
 | ||||
|             const mimetype = module.contentsinfo.mimetypes[0]; | ||||
|             if (mimetype) { | ||||
|                 resourceData.icon = CoreMimetypeUtils.getMimetypeIcon(mimetype); | ||||
|                 mimetypeIcon = CoreMimetypeUtils.getMimetypeIcon(mimetype); | ||||
|             } | ||||
|             resourceData.extra = CoreTextUtils.cleanTags(module.afterlink); | ||||
|             extra.push(CoreTextUtils.cleanTags(module.afterlink)); | ||||
| 
 | ||||
|         } else if (files && files.length) { | ||||
|         } else if (module.contents && module.contents[0]) { | ||||
|             const files = module.contents; | ||||
|             const file = files[0]; | ||||
| 
 | ||||
|             resourceData.icon = CoreMimetypeUtils.getFileIcon(file.filename || ''); | ||||
|             mimetypeIcon = CoreMimetypeUtils.getFileIcon(file.filename || ''); | ||||
| 
 | ||||
|             if (options.showsize) { | ||||
|                 const size = options.filedetails | ||||
| @ -227,16 +218,12 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase | ||||
|                     )); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             resourceData.extra += extra.join(' '); | ||||
|         } | ||||
| 
 | ||||
|         // No previously set, just set the icon.
 | ||||
|         if (resourceData.icon == '') { | ||||
|             resourceData.icon = await CoreCourse.getModuleIconSrc(module.modname, module.modicon); | ||||
|         } | ||||
| 
 | ||||
|         return resourceData; | ||||
|         return { | ||||
|             icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon, mimetypeIcon), | ||||
|             extra: extra.join(' '), | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -119,9 +119,10 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple | ||||
|             handlerData.buttons[0].hidden = hideButton; | ||||
| 
 | ||||
|             if (module.contents && module.contents[0]) { | ||||
|                 const icon = AddonModUrl.guessIcon(module.contents[0].fileurl); | ||||
| 
 | ||||
|                 // Calculate the icon to use.
 | ||||
|                 handlerData.icon = await CoreCourse.getModuleIconSrc(module.modname, module.modicon) || | ||||
|                     AddonModUrl.guessIcon(module.contents[0].fileurl); | ||||
|                 handlerData.icon = await CoreCourse.getModuleIconSrc(module.modname, module.modicon, icon); | ||||
|             } | ||||
| 
 | ||||
|             return; | ||||
|  | ||||
| @ -45,8 +45,9 @@ | ||||
|                 <ion-card-content> | ||||
|                     <ng-container *ngFor="let module of section.modules"> | ||||
|                         <ion-item class="ion-no-padding" *ngIf="module.totalSize! > 0"> | ||||
|                             <img *ngIf="module.handlerData!.icon" [src]="module.handlerData!.icon" [alt]="module.modNameTranslated" | ||||
|                                 class="core-module-icon" slot="start"> | ||||
|                             <core-mod-icon slot="start" *ngIf="module.handlerData!.icon" | ||||
|                                 [modicon]="module.handlerData!.icon" [modname]="module.modname" [componentId]="module.instance"> | ||||
|                             </core-mod-icon> | ||||
|                             <ion-label class="ion-text-wrap"> | ||||
|                                 <h3 class="{{module.handlerData!.class}} addon-storagemanager-module-size"> | ||||
|                                     {{ module.name }} | ||||
|  | ||||
| @ -63,7 +63,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit { | ||||
|             section.modules.forEach((module) => { | ||||
|                 module.parentSection = section; | ||||
|                 module.totalSize = 0; | ||||
|                 module.modNameTranslated = CoreCourse.translateModuleName(module.modname) || ''; | ||||
| 
 | ||||
|                 // Note: This function only gets the size for modules which are downloadable.
 | ||||
|                 // For other modules it always returns 0, even if they have downloaded some files.
 | ||||
| @ -235,5 +234,4 @@ type AddonStorageManagerCourseSection = Omit<CoreCourseSection, 'modules'> & { | ||||
| type AddonStorageManagerModule = CoreCourseModule & { | ||||
|     parentSection?: AddonStorageManagerCourseSection; | ||||
|     totalSize?: number; | ||||
|     modNameTranslated?: string; | ||||
| }; | ||||
|  | ||||
| @ -40,6 +40,7 @@ import { CoreInputErrorsComponent } from './input-errors/input-errors'; | ||||
| import { CoreLoadingComponent } from './loading/loading'; | ||||
| import { CoreLocalFileComponent } from './local-file/local-file'; | ||||
| import { CoreMarkRequiredComponent } from './mark-required/mark-required'; | ||||
| import { CoreModIconComponent } from './mod-icon/mod-icon'; | ||||
| import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; | ||||
| import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar'; | ||||
| import { CoreProgressBarComponent } from './progress-bar/progress-bar'; | ||||
| @ -81,6 +82,7 @@ import { CoreButtonWithSpinnerComponent } from './button-with-spinner/button-wit | ||||
|         CoreLoadingComponent, | ||||
|         CoreLocalFileComponent, | ||||
|         CoreMarkRequiredComponent, | ||||
|         CoreModIconComponent, | ||||
|         CoreNavBarButtonsComponent, | ||||
|         CoreNavigationBarComponent, | ||||
|         CoreProgressBarComponent, | ||||
| @ -128,6 +130,7 @@ import { CoreButtonWithSpinnerComponent } from './button-with-spinner/button-wit | ||||
|         CoreLoadingComponent, | ||||
|         CoreLocalFileComponent, | ||||
|         CoreMarkRequiredComponent, | ||||
|         CoreModIconComponent, | ||||
|         CoreNavBarButtonsComponent, | ||||
|         CoreNavigationBarComponent, | ||||
|         CoreProgressBarComponent, | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/core/components/mod-icon/mod-icon.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/components/mod-icon/mod-icon.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| <img | ||||
|     *ngIf="!isLocalUrl" | ||||
|     [src]="icon" | ||||
|     [alt]="showAlt ? modNameTranslated : ''" | ||||
|     [attr.role]="!showAlt ? 'presentation' : null"  | ||||
|     class="core-module-icon" | ||||
|     core-external-content | ||||
|     [component]="linkIconWithComponent ? modname : null" | ||||
|     [componentId]="linkIconWithComponent ? componentId : null" | ||||
|     (error)="loadFallbackIcon()" | ||||
| > | ||||
| <img | ||||
|     *ngIf="isLocalUrl" | ||||
|     [src]="icon" | ||||
|     [alt]="showAlt ? modNameTranslated : ''" | ||||
|     [attr.role]="!showAlt ? 'presentation' : null"  | ||||
|     class="core-module-icon" | ||||
|     (error)="loadFallbackIcon()" | ||||
| > | ||||
							
								
								
									
										37
									
								
								src/core/components/mod-icon/mod-icon.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/core/components/mod-icon/mod-icon.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| :host { | ||||
|     --size: var(--module-icon-size); | ||||
|     --margin-end: 0px; | ||||
|     --margin-vertical: 0px; | ||||
| 
 | ||||
|     margin-top: var(--margin-vertical); | ||||
|     margin-bottom: var(--margin-vertical); | ||||
|     margin-right: var(--margin-end); | ||||
| } | ||||
| 
 | ||||
| img { | ||||
|     width: var(--size); | ||||
|     height: var(--size); | ||||
|     max-width: var(--size); | ||||
|     max-height: var(--size); | ||||
| 
 | ||||
|     &[alt] { | ||||
|         text-indent: -999999px; | ||||
|         white-space: nowrap; | ||||
|         overflow: hidden; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| :host-context(ion-item) { | ||||
|     --margin-vertical: 12px; | ||||
|     --margin-end: 32px; | ||||
| } | ||||
| 
 | ||||
| :host-context(ion-card ion-item) { | ||||
|     --margin-vertical: 12px; | ||||
|     --margin-end: 12px; | ||||
| } | ||||
| 
 | ||||
| :host-context([dir=rtl]) { | ||||
|     margin-right: unset; | ||||
|     margin-left: var(--margin-end); | ||||
| } | ||||
							
								
								
									
										88
									
								
								src/core/components/mod-icon/mod-icon.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/core/components/mod-icon/mod-icon.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, Input, OnChanges, OnInit, SimpleChange } from '@angular/core'; | ||||
| import { CoreCourse } from '@features/course/services/course'; | ||||
| 
 | ||||
| const assetsPath = 'assets/img/mod/'; | ||||
| const fallbackModName = 'external-tool'; | ||||
| 
 | ||||
| /** | ||||
|  * Component to handle a module icon. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'core-mod-icon', | ||||
|     templateUrl: 'mod-icon.html', | ||||
|     styleUrls: ['mod-icon.scss'], | ||||
| }) | ||||
| export class CoreModIconComponent implements OnInit, OnChanges { | ||||
| 
 | ||||
|     @Input() modname?; // The module name. Used also as component if set.
 | ||||
|     @Input() componentId?; // Component Id for external icons.
 | ||||
|     @Input() modicon?: string; // Module icon url or local url.
 | ||||
|     @Input() showAlt = true; // Show alt otherwise it's only presentation icon.
 | ||||
| 
 | ||||
|     icon = ''; | ||||
|     modNameTranslated = ''; | ||||
|     isLocalUrl = true; | ||||
|     linkIconWithComponent = false; | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         this.modNameTranslated = this.modname ? CoreCourse.translateModuleName(this.modname) || '' : ''; | ||||
| 
 | ||||
|         this.setIcon(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     ngOnChanges(changes: { [name: string]: SimpleChange }): void { | ||||
|         if (changes && changes.modicon && changes.modicon.previousValue) { | ||||
|             this.setIcon(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set icon. | ||||
|      */ | ||||
|     setIcon(): void { | ||||
|         this.icon = this.modicon || this.icon; | ||||
|         this.isLocalUrl = this.icon.startsWith(assetsPath); | ||||
| 
 | ||||
|         // Cache icon if the url is not the theme generic one.
 | ||||
|         // If modname is not set icon won't be cached.
 | ||||
|         // Also if the url matches the regexp (the theme will manage the image so it's not cached).
 | ||||
|         this.linkIconWithComponent = | ||||
|             this.modname && | ||||
|             this.componentId && | ||||
|             !this.isLocalUrl && | ||||
|             !this.icon.match('/theme/image.php/[^/]+/' + this.modname + '/[-0-9]*/'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Icon to load on error. | ||||
|      */ | ||||
|     loadFallbackIcon(): void { | ||||
|         this.isLocalUrl = true; | ||||
|         const moduleName = !this.modname || CoreCourse.CORE_MODULES.indexOf(this.modname) < 0 | ||||
|             ? fallbackModName | ||||
|             : this.modname; | ||||
| 
 | ||||
|         this.icon = assetsPath + moduleName + '.svg'; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -79,3 +79,8 @@ | ||||
|     left: 0; | ||||
|     right: unset; | ||||
| } | ||||
| 
 | ||||
| :host-context(ion-item) { | ||||
|     margin-top: 12px; | ||||
|     margin-bottom: 12px; | ||||
| } | ||||
| @ -113,8 +113,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges { | ||||
|      * Get the URL that should be handled and, if valid, handle it. | ||||
|      */ | ||||
|     protected async checkAndHandleExternalContent(): Promise<void> { | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         const siteId = this.siteId || currentSite?.getId(); | ||||
|         const siteId = this.siteId || CoreSites.getRequiredCurrentSite().getId(); | ||||
|         const tagName = this.element.tagName.toUpperCase(); | ||||
|         let targetAttr; | ||||
|         let url; | ||||
|  | ||||
| @ -13,8 +13,9 @@ | ||||
|             [button]="module.handlerData.action && module.uservisible" | ||||
|             detail="false"> | ||||
| 
 | ||||
|             <img slot="start" *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" [alt]="modNameTranslated" | ||||
|                 class="core-module-icon"> | ||||
|             <core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname" | ||||
|                 [componentId]="module.instance"> | ||||
|             </core-mod-icon> | ||||
| 
 | ||||
|             <ion-label class="core-module-title"> | ||||
|                 <p class="item-heading"> | ||||
|  | ||||
| @ -83,7 +83,7 @@ export class CoreCourseProvider { | ||||
| 
 | ||||
|     static readonly COMPONENT = 'CoreCourse'; | ||||
| 
 | ||||
|     protected readonly CORE_MODULES = [ | ||||
|     readonly CORE_MODULES = [ | ||||
|         'assign', 'assignment', 'book', 'chat', 'choice', 'data', 'database', 'date', 'external-tool', | ||||
|         'feedback', 'file', 'folder', 'forum', 'glossary', 'ims', 'imscp', 'label', 'lesson', 'lti', 'page', 'quiz', | ||||
|         'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop', 'h5pactivity', | ||||
| @ -402,15 +402,16 @@ export class CoreCourseProvider { | ||||
|         ): Promise<CoreCourseWSSection[]> => { | ||||
|             const params: CoreCourseGetContentsParams = { | ||||
|                 courseid: courseId!, | ||||
|                 options: [], | ||||
|             }; | ||||
|             params.options = []; | ||||
| 
 | ||||
|             const preSets: CoreSiteWSPreSets = { | ||||
|                 omitExpires: preferCache, | ||||
|                 updateFrequency: CoreSite.FREQUENCY_RARELY, | ||||
|             }; | ||||
| 
 | ||||
|             if (includeStealth) { | ||||
|                 params.options!.push({ | ||||
|                 params.options.push({ | ||||
|                     name: 'includestealthmodules', | ||||
|                     value: true, | ||||
|                 }); | ||||
| @ -418,13 +419,13 @@ export class CoreCourseProvider { | ||||
| 
 | ||||
|             // If modName is set, retrieve all modules of that type. Otherwise get only the module.
 | ||||
|             if (modName) { | ||||
|                 params.options!.push({ | ||||
|                 params.options.push({ | ||||
|                     name: 'modname', | ||||
|                     value: modName, | ||||
|                 }); | ||||
|                 preSets.cacheKey = this.getModuleByModNameCacheKey(modName); | ||||
|             } else { | ||||
|                 params.options!.push({ | ||||
|                 params.options.push({ | ||||
|                     name: 'cmid', | ||||
|                     value: moduleId, | ||||
|                 }); | ||||
| @ -630,7 +631,11 @@ export class CoreCourseProvider { | ||||
|      * @param modicon The mod icon string to use in case we are not using a core activity. | ||||
|      * @return The IMG src. | ||||
|      */ | ||||
|     async getModuleIconSrc(moduleName: string, modicon?: string): Promise<string> { | ||||
|     async getModuleIconSrc(moduleName: string, modicon?: string, mimetypeIcon = ''): Promise<string> { | ||||
|         if (mimetypeIcon) { | ||||
|             return mimetypeIcon; | ||||
|         } | ||||
| 
 | ||||
|         if (this.CORE_MODULES.indexOf(moduleName) < 0) { | ||||
|             if (modicon) { | ||||
|                 return modicon; | ||||
| @ -1489,7 +1494,7 @@ export type CoreCourseWSModule = { | ||||
|         label: string; | ||||
|         timestamp: number; | ||||
|     }[]; // @since 3.11. Activity dates.
 | ||||
|     contentsinfo?: { // Contents summary information.
 | ||||
|     contentsinfo?: { // @since v3.7.6 Contents summary information.
 | ||||
|         filescount: number; // Total number of files.
 | ||||
|         filessize: number; // Total files size.
 | ||||
|         lastmodified: number; // Last time files were modified.
 | ||||
|  | ||||
| @ -20,7 +20,7 @@ import { CoreSite } from '@classes/site'; | ||||
| import { CoreCourseModuleDefaultHandler } from './handlers/default-module'; | ||||
| import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; | ||||
| import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; | ||||
| import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from './course'; | ||||
| import { CoreCourse, CoreCourseWSModule } from './course'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreCourseModule } from './course-helper'; | ||||
|  | ||||
| @ -52,8 +52,11 @@ | ||||
|                                 > | ||||
|                                     <ion-icon *ngIf="row.icon" name="{{row.icon}}" slot="start" [attr.aria-label]="row.iconAlt"> | ||||
|                                     </ion-icon> | ||||
|                                     <img *ngIf="row.image" [src]="row.image" slot="start" class="core-module-icon" | ||||
|                                     <img *ngIf="row.image && !row.itemmodule" [src]="row.image" slot="start" class="core-module-icon" | ||||
|                                         [alt]="row.iconAlt"/> | ||||
|                                     <core-mod-icon *ngIf="row.image && row.itemmodule" [modicon]="row.image" slot="start" | ||||
|                                         [modname]="row.itemmodule"> | ||||
|                                     </core-mod-icon> | ||||
|                                     <span [innerHTML]="row.gradeitem"></span> | ||||
|                                 </th> | ||||
|                                 <ng-container *ngFor="let column of grades.columns"> | ||||
|  | ||||
| @ -114,7 +114,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { | ||||
|      * Update the table of grades. | ||||
|      */ | ||||
|     private async fetchGrades(): Promise<void> { | ||||
|         const table = await CoreGrades.getCourseGradesTable(this.grades.courseId!, this.grades.userId); | ||||
|         const table = await CoreGrades.getCourseGradesTable(this.grades.courseId, this.grades.userId); | ||||
|         const formattedTable = await CoreGradesHelper.formatGradesTable(table); | ||||
| 
 | ||||
|         this.grades.setTable(formattedTable); | ||||
| @ -192,7 +192,7 @@ class CoreGradesCourseManager extends CorePageItemsListManager<CoreGradesFormatt | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected async logActivity(): Promise<void> { | ||||
|         await CoreGrades.logCourseGradesView(this.courseId!, this.userId!); | ||||
|         await CoreGrades.logCourseGradesView(this.courseId, this.userId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -82,6 +82,11 @@ | ||||
|             height: 16px; | ||||
|         } | ||||
| 
 | ||||
|         core-mod-icon { | ||||
|             --size: 16px; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         ion-icon { | ||||
|             color: var(--icon-color); | ||||
|         } | ||||
| @ -119,6 +124,11 @@ | ||||
|                 background-color: var(--cell-hover); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         th, td { | ||||
|             height: var(--a11y-min-target-size); | ||||
|             vertical-align: middle; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -17,8 +17,9 @@ | ||||
|             <ion-item *ngIf="grade.itemname && grade.link" class="ion-text-wrap" detail="true" [href]="grade.link" core-link | ||||
|             capture="true"> | ||||
|                 <ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon> | ||||
|                 <img *ngIf="grade.image" [src]="grade.image" slot="start" class="core-module-icon" | ||||
|                     [alt]="grade.iconAlt"> | ||||
|                 <img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image && grade.itemmodule" slot="start" [alt]="grade.iconAlt"/> | ||||
|                 <core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule"> | ||||
|                 </core-mod-icon> | ||||
|                 <ion-label> | ||||
|                     <h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId"> | ||||
|                     </core-format-text></h2> | ||||
| @ -27,7 +28,9 @@ | ||||
| 
 | ||||
|             <ion-item *ngIf="grade.itemname && !grade.link" class="ion-text-wrap" > | ||||
|                 <ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon> | ||||
|                 <img *ngIf="grade.image" [src]="grade.image" slot="start" class="core-module-icon" [alt]="grade.iconAlt"/> | ||||
|                 <img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image" slot="start" [alt]="grade.iconAlt"/> | ||||
|                 <core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule"> | ||||
|                 </core-mod-icon> | ||||
|                 <ion-label> | ||||
|                     <h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId"> | ||||
|                     </core-format-text></h2> | ||||
|  | ||||
| @ -3,8 +3,8 @@ | ||||
|         <img [src]="item.avatarUrl" core-external-content alt="" role="presentation" | ||||
|             onError="this.src='assets/img/user-avatar.png'"> | ||||
|     </ion-avatar> | ||||
|     <img slot="start" *ngIf="item.iconUrl" [src]="item.iconUrl" core-external-content alt="" role="presentation" | ||||
|         class="core-module-icon"> | ||||
|     <core-mod-icon *ngIf="item.iconUrl" [modicon]="item.iconUrl" slot="start" [showAlt]="false"> | ||||
|     </core-mod-icon> | ||||
|     <ion-label> | ||||
|         <h2>{{ item.heading }}</h2> | ||||
|         <p *ngFor="let text of item.details">{{ text }}</p> | ||||
|  | ||||
| @ -527,36 +527,10 @@ img[core-external-content]:not([src]) { | ||||
|     visibility: hidden; | ||||
| } | ||||
| 
 | ||||
| // Activity modules | ||||
| .core-module-icon { | ||||
|     --size: var(--module-icon-size); | ||||
|     width: var(--size); | ||||
|     height: var(--size); | ||||
|     max-width: var(--size); | ||||
|     max-height: var(--size); | ||||
| } | ||||
| 
 | ||||
| ion-item img.core-module-icon[slot="start"] { | ||||
|     margin-top: 12px; | ||||
|     margin-bottom: 12px; | ||||
|     margin-right: 32px; | ||||
| } | ||||
| 
 | ||||
| ion-card ion-item img.core-module-icon[slot="start"] { | ||||
|     margin-top: 12px; | ||||
|     margin-bottom: 12px; | ||||
|     margin-right: 12px; | ||||
| } | ||||
| 
 | ||||
| ion-card ion-item:only-child { | ||||
|     --inner-border-width: 0; | ||||
| } | ||||
| 
 | ||||
| [dir=rtl] ion-item img.core-module-icon[slot="start"] { | ||||
|     margin-right: unset; | ||||
|     margin-left: 32px; | ||||
| } | ||||
| 
 | ||||
| .core-course-module-handler:not(.addon-mod-label-handler) .item-heading .filter_mathjaxloader_equation div { | ||||
|     display: inline !important; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user