MOBILE-4266 app: Sort some app initializers
This commit is contained in:
		
							parent
							
								
									d269225651
								
							
						
					
					
						commit
						6f4e3f7479
					
				@ -13,26 +13,11 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { AppComponent } from '@/app/app.component';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreLang, CoreLangProvider } from '@services/lang';
 | 
			
		||||
 | 
			
		||||
import { mock, mockSingleton, renderComponent } from '@/testing/utils';
 | 
			
		||||
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { Http } from '@singletons';
 | 
			
		||||
import { of } from 'rxjs';
 | 
			
		||||
import { CoreSite } from '@classes/sites/site';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { renderComponent } from '@/testing/utils';
 | 
			
		||||
 | 
			
		||||
describe('AppComponent', () => {
 | 
			
		||||
 | 
			
		||||
    let langProvider: CoreLangProvider;
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        langProvider = mockSingleton(CoreLang, mock({ getCurrentLanguage: async () => 'en' , clearCustomStrings: () => null }));
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        mockSingleton(Http, { get: () => of(null as any) });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should render', async () => {
 | 
			
		||||
        const fixture = await renderComponent(AppComponent);
 | 
			
		||||
 | 
			
		||||
@ -40,77 +25,4 @@ describe('AppComponent', () => {
 | 
			
		||||
        expect(fixture.nativeElement.querySelector('ion-router-outlet')).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('cleans up on logout', async () => {
 | 
			
		||||
        const fixture = await renderComponent(AppComponent);
 | 
			
		||||
        const navigator: CoreNavigatorService = mockSingleton(CoreNavigator, ['navigate']);
 | 
			
		||||
 | 
			
		||||
        fixture.componentInstance.ngOnInit();
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGOUT);
 | 
			
		||||
 | 
			
		||||
        expect(langProvider.clearCustomStrings).toHaveBeenCalled();
 | 
			
		||||
        expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('adds ionic platform and theme classes', async () => {
 | 
			
		||||
        const fixture = await renderComponent(AppComponent);
 | 
			
		||||
        const siteUrl = 'https://campus.example.edu';
 | 
			
		||||
        const themeName = 'mytheme';
 | 
			
		||||
        const themeName2 = 'anothertheme';
 | 
			
		||||
 | 
			
		||||
        fixture.componentInstance.ngOnInit();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('ionic7')).toBe(true);
 | 
			
		||||
 | 
			
		||||
        const site = mock(new CoreSite('42', siteUrl, 'token', { info: {
 | 
			
		||||
                sitename: 'Example Campus',
 | 
			
		||||
                username: 'admin',
 | 
			
		||||
                firstname: 'Admin',
 | 
			
		||||
                lastname: 'User',
 | 
			
		||||
                fullname: 'Admin User',
 | 
			
		||||
                lang: 'en',
 | 
			
		||||
                userid: 1,
 | 
			
		||||
                siteurl: siteUrl,
 | 
			
		||||
                userpictureurl: '',
 | 
			
		||||
                theme: themeName,
 | 
			
		||||
                functions: [],
 | 
			
		||||
        } }));
 | 
			
		||||
 | 
			
		||||
        mockSingleton(CoreSites, {
 | 
			
		||||
            getSite: () => Promise.resolve(site),
 | 
			
		||||
            getCurrentSiteId: () => '42',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGIN, {}, '42');
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        if (site.infos) {
 | 
			
		||||
            site.infos.theme = themeName2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42');
 | 
			
		||||
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGOUT);
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42');
 | 
			
		||||
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -14,34 +14,19 @@
 | 
			
		||||
 | 
			
		||||
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
 | 
			
		||||
import { IonRouterOutlet } from '@ionic/angular';
 | 
			
		||||
import { BackButtonEvent, ScrollDetail } from '@ionic/core';
 | 
			
		||||
import { BackButtonEvent } from '@ionic/core';
 | 
			
		||||
 | 
			
		||||
import { CoreLang } from '@services/lang';
 | 
			
		||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { NgZone, SplashScreen } from '@singletons';
 | 
			
		||||
import { CoreNetwork } from '@services/network';
 | 
			
		||||
import { SplashScreen } from '@singletons';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSubscriptions } from '@singletons/subscriptions';
 | 
			
		||||
import { CoreWindow } from '@singletons/window';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { CoreUrl } from '@singletons/url';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
import { register } from 'swiper/element/bundle';
 | 
			
		||||
import { CoreSiteInfo, CoreSiteInfoResponse } from '@classes/sites/unauthenticated-site';
 | 
			
		||||
 | 
			
		||||
const MOODLE_SITE_URL_PREFIX = 'url-';
 | 
			
		||||
const MOODLE_VERSION_PREFIX = 'version-';
 | 
			
		||||
const MOODLEAPP_VERSION_PREFIX = 'moodleapp-';
 | 
			
		||||
const MOODLE_SITE_THEME_PREFIX = 'theme-site-';
 | 
			
		||||
 | 
			
		||||
register();
 | 
			
		||||
 | 
			
		||||
@ -61,43 +46,6 @@ export class AppComponent implements OnInit, AfterViewInit {
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        const win = <any> window;
 | 
			
		||||
        CoreDomUtils.toggleModeClass('ionic7', true, { includeLegacy: true });
 | 
			
		||||
        CoreDomUtils.toggleModeClass('development', CoreConstants.BUILD.isDevelopment);
 | 
			
		||||
        this.addVersionClass(MOODLEAPP_VERSION_PREFIX, CoreConstants.CONFIG.versionname.replace('-dev', ''));
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGOUT, async () => {
 | 
			
		||||
            // Unload lang custom strings.
 | 
			
		||||
            CoreLang.clearCustomStrings();
 | 
			
		||||
 | 
			
		||||
            // Remove version classes from body.
 | 
			
		||||
            this.removeSiteClasses();
 | 
			
		||||
 | 
			
		||||
            // Go to sites page when user is logged out.
 | 
			
		||||
            await CoreNavigator.navigate('/login/sites', { reset: true });
 | 
			
		||||
 | 
			
		||||
            if (CoreSitePlugins.hasSitePluginsLoaded) {
 | 
			
		||||
                // Temporary fix. Reload the page to unload all plugins.
 | 
			
		||||
                window.location.reload();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Listen to scroll to add style when scroll is not 0.
 | 
			
		||||
        win.addEventListener('ionScroll', async ({ detail, target }: CustomEvent<ScrollDetail>) => {
 | 
			
		||||
            if ((target as HTMLElement).tagName != 'ION-CONTENT') {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            const content = (target as HTMLIonContentElement);
 | 
			
		||||
 | 
			
		||||
            const page = content.closest('.ion-page');
 | 
			
		||||
            if (!page) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            page.querySelector<HTMLIonHeaderElement>('ion-header')?.classList.toggle('core-header-shadow', detail.scrollTop > 0);
 | 
			
		||||
 | 
			
		||||
            const scrollElement = await content.getScrollElement();
 | 
			
		||||
            content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CorePlatform.resume.subscribe(() => {
 | 
			
		||||
            // Wait a second before setting it to false since in iOS there could be some frozen WS calls.
 | 
			
		||||
@ -119,41 +67,6 @@ export class AppComponent implements OnInit, AfterViewInit {
 | 
			
		||||
            CoreWindow.open(url);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGIN, async (data) => {
 | 
			
		||||
            if (data.siteId) {
 | 
			
		||||
                const site = await CoreSites.getSite(data.siteId);
 | 
			
		||||
                const info = site.getInfo();
 | 
			
		||||
                if (info) {
 | 
			
		||||
                    this.addSiteClasses(info);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.loadCustomStrings();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Site config is checked in login.
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGIN_SITE_CHECKED, (data) => {
 | 
			
		||||
            this.addSiteUrlClass(data.config.httpswwwroot);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.SITE_UPDATED, async (data) => {
 | 
			
		||||
            if (data.siteId === CoreSites.getCurrentSiteId()) {
 | 
			
		||||
                this.loadCustomStrings();
 | 
			
		||||
 | 
			
		||||
                this.addSiteClasses(data);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.SITE_ADDED, (data) => {
 | 
			
		||||
            if (data.siteId === CoreSites.getCurrentSiteId()) {
 | 
			
		||||
                this.loadCustomStrings();
 | 
			
		||||
 | 
			
		||||
                this.addSiteClasses(data);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.onPlatformReady();
 | 
			
		||||
 | 
			
		||||
        // Quit app with back button.
 | 
			
		||||
        document.addEventListener('ionBackButton', (event: BackButtonEvent) => {
 | 
			
		||||
            // This callback should have the lowest priority in the app.
 | 
			
		||||
@ -234,148 +147,4 @@ export class AppComponent implements OnInit, AfterViewInit {
 | 
			
		||||
        return promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Async init function on platform ready.
 | 
			
		||||
     */
 | 
			
		||||
    protected async onPlatformReady(): Promise<void> {
 | 
			
		||||
        await CorePlatform.ready();
 | 
			
		||||
 | 
			
		||||
        this.logger.debug('Platform is ready');
 | 
			
		||||
 | 
			
		||||
        // Refresh online status when changes.
 | 
			
		||||
        CoreNetwork.onChange().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            NgZone.run(() => {
 | 
			
		||||
                const isOnline = CoreNetwork.isOnline();
 | 
			
		||||
                const hadOfflineMessage = CoreDomUtils.hasModeClass('core-offline');
 | 
			
		||||
 | 
			
		||||
                CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
 | 
			
		||||
 | 
			
		||||
                if (isOnline && hadOfflineMessage) {
 | 
			
		||||
                    CoreDomUtils.toggleModeClass('core-online', true, { includeLegacy: true });
 | 
			
		||||
 | 
			
		||||
                    setTimeout(() => {
 | 
			
		||||
                        CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
 | 
			
		||||
                    }, 3000);
 | 
			
		||||
                } else if (!isOnline) {
 | 
			
		||||
                    CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const isOnline = CoreNetwork.isOnline();
 | 
			
		||||
        CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load custom lang strings. This cannot be done inside the lang provider because it causes circular dependencies.
 | 
			
		||||
     */
 | 
			
		||||
    protected loadCustomStrings(): void {
 | 
			
		||||
        const currentSite = CoreSites.getCurrentSite();
 | 
			
		||||
 | 
			
		||||
        if (currentSite) {
 | 
			
		||||
            CoreLang.loadCustomStringsFromSite(currentSite);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add version to html classes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param prefix Prefix to add to the class.
 | 
			
		||||
     * @param release Current release number of the site.
 | 
			
		||||
     */
 | 
			
		||||
    protected addVersionClass(prefix: string, release: string): void {
 | 
			
		||||
        const parts = release.split('.', 3);
 | 
			
		||||
 | 
			
		||||
        parts[1] = parts[1] || '0';
 | 
			
		||||
        parts[2] = parts[2] || '0';
 | 
			
		||||
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0], true, { includeLegacy: true });
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1], true, { includeLegacy: true });
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1] + '-' + parts[2], true, { includeLegacy: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to remove all mode classes form body.
 | 
			
		||||
     *
 | 
			
		||||
     * @param prefixes Prefixes of the class mode to be removed.
 | 
			
		||||
     */
 | 
			
		||||
    protected removeModeClasses(prefixes: string[]): void {
 | 
			
		||||
        for (const modeClass of CoreDomUtils.getModeClasses()) {
 | 
			
		||||
            if (!prefixes.some((prefix) => modeClass.startsWith(prefix))) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreDomUtils.toggleModeClass(modeClass, false, { includeLegacy: true });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add site classes to html.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteInfo Site Info.
 | 
			
		||||
     */
 | 
			
		||||
    protected addSiteClasses(siteInfo: CoreSiteInfo | CoreSiteInfoResponse): void {
 | 
			
		||||
        // Add version classes to html tag.
 | 
			
		||||
        this.removeSiteClasses();
 | 
			
		||||
 | 
			
		||||
        this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || ''));
 | 
			
		||||
        this.addSiteUrlClass(siteInfo.siteurl);
 | 
			
		||||
 | 
			
		||||
        if (siteInfo.theme) {
 | 
			
		||||
            CoreDomUtils.toggleModeClass(MOODLE_SITE_THEME_PREFIX + siteInfo.theme, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to remove all site mode classes form html.
 | 
			
		||||
     */
 | 
			
		||||
    protected removeSiteClasses(): void {
 | 
			
		||||
        // Remove version classes from html tag.
 | 
			
		||||
        this.removeModeClasses(
 | 
			
		||||
            [MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX, MOODLE_SITE_THEME_PREFIX],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts the provided URL into a CSS class that be used within the page.
 | 
			
		||||
     * This is primarily used to add the siteurl to the body tag as a CSS class.
 | 
			
		||||
     * Extracted from LMS url_to_class_name function.
 | 
			
		||||
     *
 | 
			
		||||
     * @param url Url.
 | 
			
		||||
     * @returns Class name
 | 
			
		||||
     */
 | 
			
		||||
    protected urlToClassName(url: string): string {
 | 
			
		||||
        const parsedUrl = CoreUrl.parse(url);
 | 
			
		||||
 | 
			
		||||
        if (!parsedUrl) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let className = parsedUrl.domain?.replace(/\./g, '-') || '';
 | 
			
		||||
 | 
			
		||||
        if (parsedUrl.port) {
 | 
			
		||||
            className += `--${parsedUrl.port}`;
 | 
			
		||||
        }
 | 
			
		||||
        if (parsedUrl.path) {
 | 
			
		||||
            const leading = new RegExp('^/+');
 | 
			
		||||
            const trailing = new RegExp('/+$');
 | 
			
		||||
            const path = parsedUrl.path.replace(leading, '').replace(trailing, '');
 | 
			
		||||
            if (path) {
 | 
			
		||||
                className += '--' + path.replace(/\//g, '-') || '';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return className;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add site url to html classes.
 | 
			
		||||
     */
 | 
			
		||||
    protected addSiteUrlClass(siteUrl: string): void {
 | 
			
		||||
        const className = this.urlToClassName(siteUrl);
 | 
			
		||||
 | 
			
		||||
        CoreDomUtils.toggleModeClass(MOODLE_SITE_URL_PREFIX + className, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/core/initializers/app.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/core/initializers/app.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
// (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 { CoreHTMLClasses } from '@singletons/html-classes';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * General App initializer.
 | 
			
		||||
 */
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    CoreHTMLClasses.initialize();
 | 
			
		||||
}
 | 
			
		||||
@ -28,6 +28,7 @@ import { AddonFilterMultilangHandler } from '@addons/filter/multilang/services/h
 | 
			
		||||
import { AddonFilterMultilang2Handler } from '@addons/filter/multilang2/services/handlers/multilang2';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreSites } from './sites';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Service to handle language features, like changing the current language.
 | 
			
		||||
@ -380,14 +381,22 @@ export class CoreLangProvider {
 | 
			
		||||
    /**
 | 
			
		||||
     * Loads custom strings obtained from site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param currentSite Current site object.
 | 
			
		||||
     * @param currentSite Current site object. If not defined, use current site.
 | 
			
		||||
     */
 | 
			
		||||
    loadCustomStringsFromSite(currentSite: CoreSite): void {
 | 
			
		||||
    loadCustomStringsFromSite(currentSite?: CoreSite): void {
 | 
			
		||||
        currentSite = currentSite ?? CoreSites.getCurrentSite();
 | 
			
		||||
 | 
			
		||||
        if (!currentSite) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const customStrings = currentSite.getStoredConfig('tool_mobile_customlangstrings');
 | 
			
		||||
 | 
			
		||||
        if (customStrings !== undefined) {
 | 
			
		||||
            this.loadCustomStrings(customStrings);
 | 
			
		||||
        if (customStrings === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.loadCustomStrings(customStrings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -15,8 +15,9 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { Network } from '@awesome-cordova-plugins/network/ngx';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { NgZone, makeSingleton } from '@singletons';
 | 
			
		||||
import { Observable, Subject, merge } from 'rxjs';
 | 
			
		||||
import { CoreDomUtils } from './utils/dom';
 | 
			
		||||
 | 
			
		||||
export enum CoreNetworkConnection {
 | 
			
		||||
    UNKNOWN = 'unknown',
 | 
			
		||||
@ -92,6 +93,40 @@ export class CoreNetworkService extends Network {
 | 
			
		||||
                this.fireObservable();
 | 
			
		||||
            }, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.onPlaformReady();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize the service when the platform is ready.
 | 
			
		||||
     */
 | 
			
		||||
    async onPlaformReady(): Promise<void> {
 | 
			
		||||
        await CorePlatform.ready();
 | 
			
		||||
 | 
			
		||||
        // Refresh online status when changes.
 | 
			
		||||
        CoreNetwork.onChange().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            NgZone.run(() => {
 | 
			
		||||
                const isOnline = this.isOnline();
 | 
			
		||||
 | 
			
		||||
                const hadOfflineMessage = CoreDomUtils.hasModeClass('core-offline');
 | 
			
		||||
 | 
			
		||||
                CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
 | 
			
		||||
 | 
			
		||||
                if (isOnline && hadOfflineMessage) {
 | 
			
		||||
                    CoreDomUtils.toggleModeClass('core-online', true, { includeLegacy: true });
 | 
			
		||||
 | 
			
		||||
                    setTimeout(() => {
 | 
			
		||||
                        CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
 | 
			
		||||
                    }, 3000);
 | 
			
		||||
                } else if (!isOnline) {
 | 
			
		||||
                    CoreDomUtils.toggleModeClass('core-online', false, { includeLegacy: true });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const isOnline = this.isOnline();
 | 
			
		||||
        CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ import {
 | 
			
		||||
} from '@services/database/sites';
 | 
			
		||||
import { CoreArray } from '../singletons/array';
 | 
			
		||||
import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreRedirectPayload } from './navigator';
 | 
			
		||||
import { CoreNavigator, CoreRedirectPayload } from './navigator';
 | 
			
		||||
import { CoreSitesFactory } from './sites-factory';
 | 
			
		||||
import { CoreText } from '@singletons/text';
 | 
			
		||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
 | 
			
		||||
@ -66,6 +66,7 @@ import { CoreCacheManager } from '@services/cache-manager';
 | 
			
		||||
import { CoreSiteInfo, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site';
 | 
			
		||||
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
 | 
			
		||||
import { firstValueFrom } from 'rxjs';
 | 
			
		||||
import { CoreHTMLClasses } from '@singletons/html-classes';
 | 
			
		||||
 | 
			
		||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
 | 
			
		||||
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
 | 
			
		||||
@ -111,6 +112,7 @@ export class CoreSitesProvider {
 | 
			
		||||
     * Initialize.
 | 
			
		||||
     */
 | 
			
		||||
    initialize(): void {
 | 
			
		||||
        // Initialize general site events.
 | 
			
		||||
        CoreEvents.on(CoreEvents.SITE_DELETED, async ({ siteId }) => {
 | 
			
		||||
            if (!siteId || !(siteId in this.siteTables)) {
 | 
			
		||||
                return;
 | 
			
		||||
@ -125,6 +127,57 @@ export class CoreSitesProvider {
 | 
			
		||||
            delete this.siteTables[siteId];
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGOUT, async () => {
 | 
			
		||||
            // Unload lang custom strings.
 | 
			
		||||
            CoreLang.clearCustomStrings();
 | 
			
		||||
 | 
			
		||||
            // Remove version classes from body.
 | 
			
		||||
            CoreHTMLClasses.removeSiteClasses();
 | 
			
		||||
 | 
			
		||||
            // Go to sites page when user is logged out.
 | 
			
		||||
            await CoreNavigator.navigate('/login/sites', { reset: true });
 | 
			
		||||
 | 
			
		||||
            if (CoreSitePlugins.hasSitePluginsLoaded) {
 | 
			
		||||
                // Temporary fix. Reload the page to unload all plugins.
 | 
			
		||||
                window.location.reload();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGIN, async (data) => {
 | 
			
		||||
            if (data.siteId) {
 | 
			
		||||
                const site = await CoreSites.getSite(data.siteId);
 | 
			
		||||
                const info = site.getInfo();
 | 
			
		||||
                if (info) {
 | 
			
		||||
                    CoreHTMLClasses.addSiteClasses(info);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreLang.loadCustomStringsFromSite();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Site config is checked in login.
 | 
			
		||||
        CoreEvents.on(CoreEvents.LOGIN_SITE_CHECKED, (data) => {
 | 
			
		||||
            CoreHTMLClasses.addSiteUrlClass(data.config.httpswwwroot);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.SITE_UPDATED, async (data) => {
 | 
			
		||||
            if (data.siteId !== CoreSites.getCurrentSiteId()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreLang.loadCustomStringsFromSite();
 | 
			
		||||
            CoreHTMLClasses.addSiteClasses(data);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.on(CoreEvents.SITE_ADDED, (data) => {
 | 
			
		||||
            if (data.siteId !== CoreSites.getCurrentSiteId()) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreLang.loadCustomStringsFromSite();
 | 
			
		||||
            CoreHTMLClasses.addSiteClasses(data);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreCacheManager.registerInvalidateListener(() => this.invalidateCaches());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								src/core/services/tests/sites.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/core/services/tests/sites.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
			
		||||
// (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 { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreLang, CoreLangProvider } from '@services/lang';
 | 
			
		||||
 | 
			
		||||
import { mock, mockSingleton } from '@/testing/utils';
 | 
			
		||||
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { Http } from '@singletons';
 | 
			
		||||
import { of } from 'rxjs';
 | 
			
		||||
import { CoreSite } from '@classes/sites/site';
 | 
			
		||||
import { CoreHTMLClasses } from '@singletons/html-classes';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
describe('CoreSitesProvider', () => {
 | 
			
		||||
 | 
			
		||||
    let langProvider: CoreLangProvider;
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        langProvider = mockSingleton(CoreLang, mock({ getCurrentLanguage: async () => 'en' , clearCustomStrings: () => null }));
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        mockSingleton(Http, { get: () => of(null as any) });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('cleans up on logout', async () => {
 | 
			
		||||
        const navigator: CoreNavigatorService = mockSingleton(CoreNavigator, ['navigate']);
 | 
			
		||||
 | 
			
		||||
        CoreSites.initialize();
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGOUT);
 | 
			
		||||
 | 
			
		||||
        expect(langProvider.clearCustomStrings).toHaveBeenCalled();
 | 
			
		||||
        expect(navigator.navigate).toHaveBeenCalledWith('/login/sites', { reset: true });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('adds ionic platform and theme classes', async () => {
 | 
			
		||||
        const siteUrl = 'https://campus.example.edu';
 | 
			
		||||
        const themeName = 'mytheme';
 | 
			
		||||
        const themeName2 = 'anothertheme';
 | 
			
		||||
 | 
			
		||||
        CoreHTMLClasses.initialize();
 | 
			
		||||
        CoreSites.initialize();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('ionic7')).toBe(true);
 | 
			
		||||
 | 
			
		||||
        const site = mock(new CoreSite('42', siteUrl, 'token', { info: {
 | 
			
		||||
                sitename: 'Example Campus',
 | 
			
		||||
                username: 'admin',
 | 
			
		||||
                firstname: 'Admin',
 | 
			
		||||
                lastname: 'User',
 | 
			
		||||
                fullname: 'Admin User',
 | 
			
		||||
                lang: 'en',
 | 
			
		||||
                userid: 1,
 | 
			
		||||
                siteurl: siteUrl,
 | 
			
		||||
                userpictureurl: '',
 | 
			
		||||
                theme: themeName,
 | 
			
		||||
                functions: [],
 | 
			
		||||
        } }));
 | 
			
		||||
 | 
			
		||||
        mockSingleton(CoreSites, {
 | 
			
		||||
            getSite: () => Promise.resolve(site),
 | 
			
		||||
            getCurrentSiteId: () => '42',
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGIN, {}, '42');
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        if (site.infos) {
 | 
			
		||||
            site.infos.theme = themeName2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.SITE_UPDATED, site.infos , '42');
 | 
			
		||||
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGOUT);
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(false);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.SITE_ADDED, site.infos , '42');
 | 
			
		||||
 | 
			
		||||
        // Wait the event to be processed.
 | 
			
		||||
        await CoreUtils.nextTick();
 | 
			
		||||
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName2)).toBe(true);
 | 
			
		||||
        expect(document.documentElement.classList.contains('theme-site-'+themeName)).toBe(false);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										163
									
								
								src/core/singletons/html-classes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/core/singletons/html-classes.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,163 @@
 | 
			
		||||
// (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 { CoreSiteInfo, CoreSiteInfoResponse } from '@classes/sites/unauthenticated-site';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreUrl } from './url';
 | 
			
		||||
import { CoreConstants } from '../constants';
 | 
			
		||||
import { ScrollDetail } from '@ionic/angular';
 | 
			
		||||
import { CoreDom } from './dom';
 | 
			
		||||
 | 
			
		||||
const MOODLE_SITE_URL_PREFIX = 'url-';
 | 
			
		||||
const MOODLE_VERSION_PREFIX = 'version-';
 | 
			
		||||
const MOODLEAPP_VERSION_PREFIX = 'moodleapp-';
 | 
			
		||||
const MOODLE_SITE_THEME_PREFIX = 'theme-site-';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Singleton with helper functions to manage HTML classes.
 | 
			
		||||
 */
 | 
			
		||||
export class CoreHTMLClasses {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize HTML classes.
 | 
			
		||||
     */
 | 
			
		||||
    static initialize(): void {
 | 
			
		||||
        CoreDomUtils.toggleModeClass('ionic7', true);
 | 
			
		||||
        CoreDomUtils.toggleModeClass('development', CoreConstants.BUILD.isDevelopment);
 | 
			
		||||
        CoreHTMLClasses.addVersionClass(MOODLEAPP_VERSION_PREFIX, CoreConstants.CONFIG.versionname.replace('-dev', ''));
 | 
			
		||||
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        const win = <any> window;
 | 
			
		||||
 | 
			
		||||
        // Listen to scroll to add style when scroll is not 0.
 | 
			
		||||
        win.addEventListener('ionScroll', async ({ detail, target }: CustomEvent<ScrollDetail>) => {
 | 
			
		||||
            if ((target as HTMLElement).tagName != 'ION-CONTENT') {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            const content = (target as HTMLIonContentElement);
 | 
			
		||||
 | 
			
		||||
            const page = content.closest('.ion-page');
 | 
			
		||||
            if (!page) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            page.querySelector<HTMLIonHeaderElement>('ion-header')?.classList.toggle('core-header-shadow', detail.scrollTop > 0);
 | 
			
		||||
 | 
			
		||||
            const scrollElement = await content.getScrollElement();
 | 
			
		||||
            content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement));
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add version to html classes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param prefix Prefix to add to the class.
 | 
			
		||||
     * @param release Current release number of the site.
 | 
			
		||||
     */
 | 
			
		||||
    static addVersionClass(prefix: string, release: string): void {
 | 
			
		||||
        const parts = release.split('.', 3);
 | 
			
		||||
 | 
			
		||||
        parts[1] = parts[1] || '0';
 | 
			
		||||
        parts[2] = parts[2] || '0';
 | 
			
		||||
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0], true, { includeLegacy: true });
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1], true, { includeLegacy: true });
 | 
			
		||||
        CoreDomUtils.toggleModeClass(prefix + parts[0] + '-' + parts[1] + '-' + parts[2], true, { includeLegacy: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to remove all mode classes form body.
 | 
			
		||||
     *
 | 
			
		||||
     * @param prefixes Prefixes of the class mode to be removed.
 | 
			
		||||
     */
 | 
			
		||||
    protected static removeModeClasses(prefixes: string[]): void {
 | 
			
		||||
        for (const modeClass of CoreDomUtils.getModeClasses()) {
 | 
			
		||||
            if (!prefixes.some((prefix) => modeClass.startsWith(prefix))) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreDomUtils.toggleModeClass(modeClass, false, { includeLegacy: true });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add site classes to html.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteInfo Site Info.
 | 
			
		||||
     */
 | 
			
		||||
    static addSiteClasses(siteInfo: CoreSiteInfo | CoreSiteInfoResponse): void {
 | 
			
		||||
        // Add version classes to html tag.
 | 
			
		||||
        this.removeSiteClasses();
 | 
			
		||||
 | 
			
		||||
        this.addVersionClass(MOODLE_VERSION_PREFIX, CoreSites.getReleaseNumber(siteInfo.release || ''));
 | 
			
		||||
        this.addSiteUrlClass(siteInfo.siteurl);
 | 
			
		||||
 | 
			
		||||
        if (siteInfo.theme) {
 | 
			
		||||
            CoreDomUtils.toggleModeClass(MOODLE_SITE_THEME_PREFIX + siteInfo.theme, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to remove all site mode classes form html.
 | 
			
		||||
     */
 | 
			
		||||
    static removeSiteClasses(): void {
 | 
			
		||||
        // Remove version classes from html tag.
 | 
			
		||||
        this.removeModeClasses(
 | 
			
		||||
            [MOODLE_VERSION_PREFIX, MOODLE_SITE_URL_PREFIX, MOODLE_SITE_THEME_PREFIX],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts the provided URL into a CSS class that be used within the page.
 | 
			
		||||
     * This is primarily used to add the siteurl to the body tag as a CSS class.
 | 
			
		||||
     * Extracted from LMS url_to_class_name function.
 | 
			
		||||
     *
 | 
			
		||||
     * @param url Url.
 | 
			
		||||
     * @returns Class name
 | 
			
		||||
     */
 | 
			
		||||
    protected static urlToClassName(url: string): string {
 | 
			
		||||
        const parsedUrl = CoreUrl.parse(url);
 | 
			
		||||
 | 
			
		||||
        if (!parsedUrl) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let className = parsedUrl.domain?.replace(/\./g, '-') || '';
 | 
			
		||||
 | 
			
		||||
        if (parsedUrl.port) {
 | 
			
		||||
            className += `--${parsedUrl.port}`;
 | 
			
		||||
        }
 | 
			
		||||
        if (parsedUrl.path) {
 | 
			
		||||
            const leading = new RegExp('^/+');
 | 
			
		||||
            const trailing = new RegExp('/+$');
 | 
			
		||||
            const path = parsedUrl.path.replace(leading, '').replace(trailing, '');
 | 
			
		||||
            if (path) {
 | 
			
		||||
                className += '--' + path.replace(/\//g, '-') || '';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return className;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to add site url to html classes.
 | 
			
		||||
     */
 | 
			
		||||
    static addSiteUrlClass(siteUrl: string): void {
 | 
			
		||||
        const className = this.urlToClassName(siteUrl);
 | 
			
		||||
 | 
			
		||||
        CoreDomUtils.toggleModeClass(MOODLE_SITE_URL_PREFIX + className, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user