diff --git a/src/addons/mod/data/fields/latlong/component/latlong.ts b/src/addons/mod/data/fields/latlong/component/latlong.ts index 28d5038f5..a638544e4 100644 --- a/src/addons/mod/data/fields/latlong/component/latlong.ts +++ b/src/addons/mod/data/fields/latlong/component/latlong.ts @@ -16,11 +16,12 @@ import { AddonModDataFieldPluginComponent } from '@addons/mod/data/classes/field import { AddonModDataEntryField } from '@addons/mod/data/services/data'; import { Component } from '@angular/core'; import { FormBuilder } from '@angular/forms'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { SafeUrl } from '@angular/platform-browser'; import { CoreAnyError } from '@classes/errors/error'; import { CoreApp } from '@services/app'; import { CoreGeolocation, CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation'; import { CoreDomUtils } from '@services/utils/dom'; +import { DomSanitizer } from '@singletons'; /** * Component to render data latlong field. @@ -35,10 +36,7 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo east?: number; locationServicesEnabled = false; - constructor( - fb: FormBuilder, - protected sanitizer: DomSanitizer, - ) { + constructor(fb: FormBuilder) { super(fb); } @@ -82,7 +80,7 @@ export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginCo } } - return this.sanitizer.bypassSecurityTrustUrl(url); + return DomSanitizer.bypassSecurityTrustUrl(url); } /** diff --git a/src/addons/mod/lti/services/handlers/module.ts b/src/addons/mod/lti/services/handlers/module.ts index 59e2daa60..ea35c8d26 100644 --- a/src/addons/mod/lti/services/handlers/module.ts +++ b/src/addons/mod/lti/services/handlers/module.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, Type } from '@angular/core'; -import { DomSanitizer } from '@angular/platform-browser'; import { CoreConstants } from '@/core/constants'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; @@ -24,7 +23,7 @@ import { CoreFilepool } from '@services/filepool'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; -import { makeSingleton } from '@singletons'; +import { DomSanitizer, makeSingleton } from '@singletons'; import { AddonModLtiHelper } from '../lti-helper'; import { AddonModLti, AddonModLtiProvider } from '../lti'; import { AddonModLtiIndexComponent } from '../../components/index'; @@ -51,8 +50,6 @@ export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true, }; - constructor(protected sanitizer: DomSanitizer) {} - /** * @inheritdoc */ @@ -124,11 +121,11 @@ export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler // Get the internal URL. const url = await CoreFilepool.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id); - handlerData.icon = this.sanitizer.bypassSecurityTrustUrl(url); + handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(url); } catch { // Error downloading. If we're online we'll set the online url. if (CoreApp.isOnline()) { - handlerData.icon = this.sanitizer.bypassSecurityTrustUrl(icon); + handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(icon); } } } diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index cb1ecd059..3afcd69a2 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -15,7 +15,7 @@ import { Component, Input, Output, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, } from '@angular/core'; -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { SafeResourceUrl } from '@angular/platform-browser'; import { CoreFile } from '@services/file'; import { CoreDomUtils } from '@services/utils/dom'; @@ -23,6 +23,7 @@ import { CoreUrlUtils } from '@services/utils/url'; import { CoreIframeUtils } from '@services/utils/iframe'; import { CoreUtils } from '@services/utils/utils'; import { CoreLogger } from '@singletons/logger'; +import { DomSanitizer } from '@singletons'; @Component({ selector: 'core-iframe', @@ -46,10 +47,7 @@ export class CoreIframeComponent implements OnChanges { protected logger: CoreLogger; protected initialized = false; - constructor( - protected sanitizer: DomSanitizer, - ) { - + constructor() { this.logger = CoreLogger.getInstance('CoreIframe'); this.loaded = new EventEmitter(); } @@ -105,7 +103,7 @@ export class CoreIframeComponent implements OnChanges { await CoreIframeUtils.fixIframeCookies(url); - this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(CoreFile.convertFileSrc(url)); + this.safeUrl = DomSanitizer.bypassSecurityTrustResourceUrl(CoreFile.convertFileSrc(url)); // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM. setTimeout(() => { diff --git a/src/core/components/progress-bar/progress-bar.ts b/src/core/components/progress-bar/progress-bar.ts index 615aef875..ec65e69bc 100644 --- a/src/core/components/progress-bar/progress-bar.ts +++ b/src/core/components/progress-bar/progress-bar.ts @@ -13,8 +13,8 @@ // limitations under the License. import { Component, Input, OnChanges, SimpleChange, ChangeDetectionStrategy } from '@angular/core'; -import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; -import { Translate } from '@singletons'; +import { SafeStyle } from '@angular/platform-browser'; +import { DomSanitizer, Translate } from '@singletons'; /** * Component to show a progress bar and its value. @@ -40,8 +40,6 @@ export class CoreProgressBarComponent implements OnChanges { protected textSupplied = false; - constructor(private sanitizer: DomSanitizer) { } - /** * Detect changes on input properties. */ @@ -69,7 +67,7 @@ export class CoreProgressBarComponent implements OnChanges { this.text = String(this.progress); } - this.width = this.sanitizer.bypassSecurityTrustStyle(this.progress + '%'); + this.width = DomSanitizer.bypassSecurityTrustStyle(this.progress + '%'); } } diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index adb0641a3..a1f7c0418 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -23,7 +23,6 @@ import { Optional, ViewContainerRef, } from '@angular/core'; -import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { CoreEventLoadingChangedData, CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -94,7 +93,6 @@ export class CoreFormatTextDirective implements OnChanges { element: ElementRef, @Optional() protected content: IonContent, protected viewContainerRef: ViewContainerRef, - protected sanitizer: DomSanitizer, ) { this.element = element.nativeElement; this.element.classList.add('core-format-text-loading'); // Hide contents until they're treated. @@ -520,7 +518,7 @@ export class CoreFormatTextDirective implements OnChanges { // Important: We need to look for links first because in 'img' we add new links without core-link. anchors.forEach((anchor) => { // Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually. - const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content, this.sanitizer); + const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content); linkDir.capture = this.captureLinks ?? true; linkDir.inApp = this.openLinksInApp; linkDir.ngOnInit(); diff --git a/src/core/directives/link.ts b/src/core/directives/link.ts index 94aae1bf7..472b28a83 100644 --- a/src/core/directives/link.ts +++ b/src/core/directives/link.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Directive, Input, OnInit, ElementRef, Optional, SecurityContext } from '@angular/core'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { SafeUrl } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { CoreFileHelper } from '@services/file-helper'; @@ -25,6 +25,7 @@ import { CoreTextUtils } from '@services/utils/text'; import { CoreConstants } from '@/core/constants'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreCustomURLSchemes } from '@services/urlschemes'; +import { DomSanitizer } from '@singletons'; /** * Directive to open a link in external browser or in the app. @@ -48,7 +49,6 @@ export class CoreLinkDirective implements OnInit { constructor( element: ElementRef, @Optional() protected content: IonContent, - protected sanitizer: DomSanitizer, ) { this.element = element.nativeElement; } @@ -96,7 +96,7 @@ export class CoreLinkDirective implements OnInit { let href: string | null = null; if (this.href) { // Convert the URL back to string if needed. - href = typeof this.href === 'string' ? this.href : this.sanitizer.sanitize(SecurityContext.URL, this.href); + href = typeof this.href === 'string' ? this.href : DomSanitizer.sanitize(SecurityContext.URL, this.href); } href = href || this.element.getAttribute('href') || this.element.getAttribute('xlink:href'); diff --git a/src/core/directives/tests/format-text.test.ts b/src/core/directives/tests/format-text.test.ts index f0fc1d7d0..3bfc5afa8 100644 --- a/src/core/directives/tests/format-text.test.ts +++ b/src/core/directives/tests/format-text.test.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { NgZone } from '@angular/core'; import Faker from 'faker'; @@ -39,7 +38,7 @@ describe('CoreFormatTextDirective', () => { mockSingleton(Platform, { ready: () => Promise.resolve() }); mockSingleton(CoreConfig, { get: (_, defaultValue) => defaultValue }); - CoreDomUtils.setInstance(new CoreDomUtilsProvider(mock())); + CoreDomUtils.setInstance(new CoreDomUtilsProvider()); CoreUrlUtils.setInstance(new CoreUrlUtilsProvider()); CoreUtils.setInstance(new CoreUtilsProvider(mock())); diff --git a/src/core/services/tests/navigator.test.ts b/src/core/services/tests/navigator.test.ts index 5dffe7899..d2c3fe25d 100644 --- a/src/core/services/tests/navigator.test.ts +++ b/src/core/services/tests/navigator.test.ts @@ -44,7 +44,7 @@ describe('CoreNavigator', () => { mockSingleton(Router, router); mockSingleton(CoreUtils, new CoreUtilsProvider(mock())); mockSingleton(CoreUrlUtils, new CoreUrlUtilsProvider()); - mockSingleton(CoreTextUtils, new CoreTextUtilsProvider(mock())); + mockSingleton(CoreTextUtils, new CoreTextUtilsProvider()); mockSingleton(CoreSites, { getCurrentSiteId: () => 42, isLoggedIn: () => true }); mockSingleton(CoreMainMenu, { isMainMenuTab: path => Promise.resolve(currentMainMenuHandlers.includes(path)) }); }); diff --git a/src/core/services/tests/utils/text.test.ts b/src/core/services/tests/utils/text.test.ts index 19ba8fd6e..1ac99af3d 100644 --- a/src/core/services/tests/utils/text.test.ts +++ b/src/core/services/tests/utils/text.test.ts @@ -12,24 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { DomSanitizer } from '@angular/platform-browser'; - import { CoreApp } from '@services/app'; import { CoreTextUtilsProvider } from '@services/utils/text'; +import { DomSanitizer } from '@singletons'; -import { mock, mockSingleton } from '@/testing/utils'; +import { mockSingleton } from '@/testing/utils'; describe('CoreTextUtilsProvider', () => { const config = { platform: 'android' }; - let sanitizer: DomSanitizer; let textUtils: CoreTextUtilsProvider; beforeEach(() => { mockSingleton(CoreApp, [], { isAndroid: () => config.platform === 'android' }); + mockSingleton(DomSanitizer, [], { bypassSecurityTrustUrl: url => url }); - sanitizer = mock([], { bypassSecurityTrustUrl: url => url }); - textUtils = new CoreTextUtilsProvider(sanitizer); + textUtils = new CoreTextUtilsProvider(); }); it('adds ending slashes', () => { @@ -58,7 +56,7 @@ describe('CoreTextUtilsProvider', () => { // Assert expect(url).toEqual('geo:0,0?q=Moodle%20Spain%20HQ'); - expect(sanitizer.bypassSecurityTrustUrl).toHaveBeenCalled(); + expect(DomSanitizer.bypassSecurityTrustUrl).toHaveBeenCalled(); expect(CoreApp.isAndroid).toHaveBeenCalled(); }); @@ -74,7 +72,7 @@ describe('CoreTextUtilsProvider', () => { // Assert expect(url).toEqual('http://maps.google.com?q=Moodle%20Spain%20HQ'); - expect(sanitizer.bypassSecurityTrustUrl).toHaveBeenCalled(); + expect(DomSanitizer.bypassSecurityTrustUrl).toHaveBeenCalled(); expect(CoreApp.isAndroid).toHaveBeenCalled(); }); diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index b6ee0ee20..ffc7464f3 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Injectable, SimpleChange, ElementRef, KeyValueChanges } from '@angular/core'; -import { DomSanitizer } from '@angular/platform-browser'; import { IonContent } from '@ionic/angular'; import { ModalOptions, PopoverOptions, AlertOptions, AlertButton, TextFieldTypes } from '@ionic/core'; import { Md5 } from 'ts-md5'; @@ -62,7 +61,7 @@ export class CoreDomUtilsProvider { protected activeLoadingModals: CoreIonLoadingElement[] = []; protected logger: CoreLogger; - constructor(protected domSanitizer: DomSanitizer) { + constructor() { this.logger = CoreLogger.getInstance('CoreDomUtilsProvider'); this.init(); diff --git a/src/core/services/utils/text.ts b/src/core/services/utils/text.ts index 2045bc547..572594b66 100644 --- a/src/core/services/utils/text.ts +++ b/src/core/services/utils/text.ts @@ -13,13 +13,13 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { SafeUrl } from '@angular/platform-browser'; import { ModalOptions } from '@ionic/core'; import { CoreApp } from '@services/app'; import { CoreLang } from '@services/lang'; import { CoreAnyError, CoreError } from '@classes/errors/error'; -import { makeSingleton, Translate } from '@singletons'; +import { DomSanitizer, makeSingleton, Translate } from '@singletons'; import { CoreWSFile } from '@services/ws'; import { Locutus } from '@singletons/locutus'; import { CoreViewerTextComponent } from '@features/viewer/components/text/text'; @@ -94,8 +94,6 @@ export class CoreTextUtilsProvider { protected template: HTMLTemplateElement = document.createElement('template'); // A template element to convert HTML to element. - constructor(private sanitizer: DomSanitizer) { } - /** * Add ending slash from a path or URL. * @@ -156,7 +154,7 @@ export class CoreTextUtilsProvider { * @return URL to view the address. */ buildAddressURL(address: string): SafeUrl { - return this.sanitizer.bypassSecurityTrustUrl((CoreApp.isAndroid() ? 'geo:0,0?q=' : 'http://maps.google.com?q=') + + return DomSanitizer.bypassSecurityTrustUrl((CoreApp.isAndroid() ? 'geo:0,0?q=' : 'http://maps.google.com?q=') + encodeURIComponent(address)); } diff --git a/src/core/singletons/index.ts b/src/core/singletons/index.ts index 6d15cef42..d22a26290 100644 --- a/src/core/singletons/index.ts +++ b/src/core/singletons/index.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ApplicationRef, ApplicationInitStatus, Injector, NgZone as NgZoneService, Type } from '@angular/core'; +import { AbstractType, ApplicationInitStatus, ApplicationRef, Injector, NgZone as NgZoneService, Type } from '@angular/core'; import { Router as RouterService } from '@angular/router'; import { HttpClient } from '@angular/common/http'; +import { DomSanitizer as DomSanitizerService } from '@angular/platform-browser'; import { Platform as PlatformService, @@ -109,7 +110,7 @@ export function setCreateSingletonMethodProxy(method: typeof createSingletonMeth * @return Singleton proxy. */ export function makeSingleton( // eslint-disable-line @typescript-eslint/ban-types - injectionToken: Type | Type | string, + injectionToken: Type | AbstractType | Type | string, ): CoreSingletonProxy { const singleton = { setInstance(instance: Service) { @@ -199,6 +200,7 @@ export const ApplicationInit = makeSingleton(ApplicationInitStatus); export const Application = makeSingleton(ApplicationRef); export const NavController = makeSingleton(NavControllerService); export const Router = makeSingleton(RouterService); +export const DomSanitizer = makeSingleton(DomSanitizerService); // Convert external libraries injectables. export const Translate = makeSingleton(TranslateService);