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