diff --git a/src/addons/mod/lti/services/handlers/module.ts b/src/addons/mod/lti/services/handlers/module.ts
index 1494456b3..545aa262f 100644
--- a/src/addons/mod/lti/services/handlers/module.ts
+++ b/src/addons/mod/lti/services/handlers/module.ts
@@ -22,6 +22,7 @@ import { AddonModLtiHelper } from '../lti-helper';
import { AddonModLtiIndexComponent } from '../../components/index';
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
import { CoreCourse } from '@features/course/services/course';
+import { CoreSites } from '@services/sites';
/**
* Handler to support LTI modules.
@@ -86,6 +87,19 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
return module?.modicon ?? modicon ?? CoreCourse.getModuleIconSrc(this.modName);
}
+ /**
+ * @inheritdoc
+ */
+ iconIsShape(module?: CoreCourseModuleData | undefined, modicon?: string | undefined): boolean | undefined {
+ const iconUrl = module?.modicon ?? modicon;
+
+ if (!iconUrl) {
+ return true;
+ }
+
+ return iconUrl.startsWith(CoreSites.getRequiredCurrentSite().siteUrl);
+ }
+
}
export const AddonModLtiModuleHandler = makeSingleton(AddonModLtiModuleHandlerService);
diff --git a/src/core/components/mod-icon/mod-icon.html b/src/core/components/mod-icon/mod-icon.html
index 58842cb13..46777347a 100644
--- a/src/core/components/mod-icon/mod-icon.html
+++ b/src/core/components/mod-icon/mod-icon.html
@@ -1,5 +1,5 @@
+ class="core-module-icon" [class.no-filter]="noFilter" (error)="loadFallbackIcon()">
diff --git a/src/core/components/mod-icon/mod-icon.scss b/src/core/components/mod-icon/mod-icon.scss
index f5f7dbdcb..5ab0db1f0 100644
--- a/src/core/components/mod-icon/mod-icon.scss
+++ b/src/core/components/mod-icon/mod-icon.scss
@@ -42,6 +42,11 @@ img {
white-space: nowrap;
overflow: hidden;
}
+
+ &.no-filter {
+ --filter: none;
+ }
+
}
:host-context(ion-item) {
diff --git a/src/core/components/mod-icon/mod-icon.ts b/src/core/components/mod-icon/mod-icon.ts
index f2e59c7d7..1d2f018b2 100644
--- a/src/core/components/mod-icon/mod-icon.ts
+++ b/src/core/components/mod-icon/mod-icon.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import { CoreConstants, ModPurpose } from '@/core/constants';
-import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChange } from '@angular/core';
+import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChange } from '@angular/core';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
import { CoreSites } from '@services/sites';
@@ -34,9 +34,12 @@ export class CoreModIconComponent implements OnInit, OnChanges {
@Input() modname?: string; // The module name. Used also as component if set.
@Input() componentId?: number; // Component Id for external icons.
@Input() modicon?: string; // Module icon url or local url.
+ @Input() noFilter?: boolean; // Whether to disable filters.
@Input() showAlt = true; // Show alt otherwise it's only presentation icon.
@Input() purpose: ModPurpose = ModPurpose.MOD_PURPOSE_OTHER; // Purpose of the module.
+ @Output() failedLoading = new EventEmitter();
+
icon = '';
modNameTranslated = '';
isLocalUrl = true;
@@ -122,6 +125,8 @@ export class CoreModIconComponent implements OnInit, OnChanges {
}
this.icon = path + moduleName + '.svg';
+
+ this.failedLoading.emit();
}
}
diff --git a/src/core/features/course/services/module-delegate.ts b/src/core/features/course/services/module-delegate.ts
index f8dead8d3..0a6a2af3a 100644
--- a/src/core/features/course/services/module-delegate.ts
+++ b/src/core/features/course/services/module-delegate.ts
@@ -87,6 +87,15 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
*/
getIconSrc?(module?: CoreCourseModuleData, modicon?: string): Promise | string | undefined;
+ /**
+ * Check whether the icon should be treated as a shape or a rich image.
+ *
+ * @param module Module to get the icon from.
+ * @param modicon The mod icon string.
+ * @returns Whether the icon should be treated as a shape.
+ */
+ iconIsShape?(module?: CoreCourseModuleData, modicon?: string): Promise | boolean | undefined;
+
/**
* Check if this type of module supports a certain feature.
* If this function is implemented, the supportedFeatures object will be ignored.
@@ -396,6 +405,18 @@ export class CoreCourseModuleDelegateService extends CoreDelegate {
+ return await this.executeFunctionOnEnabled>(modname, 'iconIsShape', [module, modicon]);
+ }
+
/**
* Check if a certain type of module supports a certain feature.
*
diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html
index 70cc02b61..44236689c 100644
--- a/src/core/features/grades/pages/course/course.html
+++ b/src/core/features/grades/pages/course/course.html
@@ -48,7 +48,8 @@
+ [modname]="row.itemmodule" [noFilter]="row.imageIsShape === false"
+ (failedLoading)="failedLoadingRowImage(row)">
diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts
index c70adc1a8..508d99488 100644
--- a/src/core/features/grades/pages/course/course.page.ts
+++ b/src/core/features/grades/pages/course/course.page.ts
@@ -239,4 +239,13 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
infiniteComplete && infiniteComplete();
}
+ /**
+ * Handle row image failed loading.
+ *
+ * @param row Row data.
+ */
+ failedLoadingRowImage(row: CoreGradesFormattedTableRow): void {
+ delete row.imageIsShape;
+ }
+
}
diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts
index fb5d4b3f3..96c4ea06e 100644
--- a/src/core/features/grades/services/grades-helper.ts
+++ b/src/core/features/grades/services/grades-helper.ts
@@ -684,14 +684,16 @@ export class CoreGradesHelperProvider {
row.iconAlt = Translate.instant('core.grades.calculatedgrade');
} else if (text.indexOf('/mod/') > -1) {
const module = text.match(/mod\/([^/]*)\//);
- if (module?.[1] !== undefined) {
+ const modname = module?.[1];
+
+ if (modname !== undefined) {
+ const modicon = CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined;
+
row.itemtype = 'mod';
- row.itemmodule = module[1];
+ row.itemmodule = modname;
row.iconAlt = CoreCourse.translateModuleName(row.itemmodule) || '';
- row.image = await CoreCourseModuleDelegate.getModuleIconSrc(
- module[1],
- CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined,
- );
+ row.image = await CoreCourseModuleDelegate.getModuleIconSrc(modname, modicon);
+ row.imageIsShape = await CoreCourseModuleDelegate.moduleIconIsShape(modname, modicon);
}
} else {
if (row.rowspan && row.rowspan > 1) {
@@ -804,6 +806,7 @@ export type CoreGradesFormattedRowCommonData = {
rowclass?: string;
itemtype?: string;
image?: string;
+ imageIsShape?: boolean;
itemmodule?: string;
iconAlt?: string;
rowspan?: number;