forked from CIT/Vmeda.Online
		
	Merge pull request #2640 from NoelDeMartin/MOBILE-3320
MOBILE-3320: Initializers and singletons
This commit is contained in:
		
						commit
						1559bf27b7
					
				
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -3312,6 +3312,12 @@
 | 
			
		||||
      "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "@types/webpack-env": {
 | 
			
		||||
      "version": "1.16.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Fx+NpfOO0CpeYX2g9bkvX8O5qh9wrU1sOF4g8sft4Mu7z+qfe387YlyY8w8daDyDsKY5vUxM0yxkAYnbkRbZEw==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "@types/webpack-sources": {
 | 
			
		||||
      "version": "0.1.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz",
 | 
			
		||||
 | 
			
		||||
@ -131,6 +131,7 @@
 | 
			
		||||
    "@ionic/angular-toolkit": "^2.3.0",
 | 
			
		||||
    "@types/faker": "^5.1.3",
 | 
			
		||||
    "@types/node": "^12.12.64",
 | 
			
		||||
    "@types/webpack-env": "^1.16.0",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "4.3.0",
 | 
			
		||||
    "@typescript-eslint/parser": "4.3.0",
 | 
			
		||||
    "eslint": "^7.6.0",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								src/core/classes/application-init-status.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/core/classes/application-init-status.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
// (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 { ApplicationInitStatus, APP_INITIALIZER, Injectable, Injector } from '@angular/core';
 | 
			
		||||
import { setSingletonsInjector } from '@singletons';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class CoreApplicationInitStatus extends ApplicationInitStatus {
 | 
			
		||||
 | 
			
		||||
    constructor(injector: Injector) {
 | 
			
		||||
        setSingletonsInjector(injector);
 | 
			
		||||
 | 
			
		||||
        super(injector.get(APP_INITIALIZER, []));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,88 +0,0 @@
 | 
			
		||||
// (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 { Injector, Type } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stub class used to type anonymous classes created in CoreSingletonsFactory#makeSingleton method.
 | 
			
		||||
 */
 | 
			
		||||
class CoreSingleton {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Token that can be used to resolve instances from the injector.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreInjectionToken<Service> = Type<Service> | Type<unknown> | string;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Singleton class created using the factory.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreSingletonClass<Service> = typeof CoreSingleton & {
 | 
			
		||||
    instance: Service;
 | 
			
		||||
    setInstance(instance: Service): void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Factory used to create CoreSingleton classes that get instances from an injector.
 | 
			
		||||
 */
 | 
			
		||||
export class CoreSingletonsFactory {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Angular injector used to resolve singleton instances.
 | 
			
		||||
     */
 | 
			
		||||
    private injector?: Injector;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the injector that will be used to resolve instances in the singletons created with this factory.
 | 
			
		||||
     *
 | 
			
		||||
     * @param injector Injector.
 | 
			
		||||
     */
 | 
			
		||||
    setInjector(injector: Injector): void {
 | 
			
		||||
        this.injector = injector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Make a singleton that will hold an instance resolved from the factory injector.
 | 
			
		||||
     *
 | 
			
		||||
     * @param injectionToken Injection token used to resolve the singleton instance. This is usually the service class if the
 | 
			
		||||
     * provider was defined using a class or the string used in the `provide` key if it was defined using an object.
 | 
			
		||||
     */
 | 
			
		||||
    makeSingleton<Service>(injectionToken: CoreInjectionToken<Service>): CoreSingletonClass<Service> {
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-this-alias
 | 
			
		||||
        const factory = this;
 | 
			
		||||
 | 
			
		||||
        return class {
 | 
			
		||||
 | 
			
		||||
            private static serviceInstance: Service;
 | 
			
		||||
 | 
			
		||||
            static get instance(): Service {
 | 
			
		||||
                // Initialize instances lazily.
 | 
			
		||||
                if (!this.serviceInstance) {
 | 
			
		||||
                    if (!factory.injector) {
 | 
			
		||||
                        throw new Error('Can\'t resolve a singleton instance without an injector');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this.serviceInstance = factory.injector.get(injectionToken);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return this.serviceInstance;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            static setInstance(instance: Service): void {
 | 
			
		||||
                this.serviceInstance = instance;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -13,20 +13,13 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
 | 
			
		||||
import { Injector, NgModule } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { Platform } from '@ionic/angular';
 | 
			
		||||
import { ApplicationInitStatus, Injector, NgModule } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreApplicationInitStatus } from './classes/application-init-status';
 | 
			
		||||
import { CoreFeaturesModule } from './features/features.module';
 | 
			
		||||
import { CoreFile } from './services/file';
 | 
			
		||||
import { CoreInit, CoreInitDelegate } from './services/init';
 | 
			
		||||
import { CoreInterceptor } from './classes/interceptor';
 | 
			
		||||
import { CoreSites, CORE_SITE_SCHEMAS } from './services/sites';
 | 
			
		||||
import { CoreUpdateManager } from './services/update-manager';
 | 
			
		||||
import { setSingletonsInjector } from './singletons';
 | 
			
		||||
import { SITE_SCHEMA as FILEPOOL_SITE_SCHEMA } from './services/db/filepool';
 | 
			
		||||
import { SITE_SCHEMA as SITES_SITE_SCHEMA } from './services/db/sites';
 | 
			
		||||
import { SITE_SCHEMA as SYNC_SITE_SCHEMA } from './services/db/sync';
 | 
			
		||||
import { getDatabaseProviders } from './services/database';
 | 
			
		||||
import { getInitializerProviders } from './initializers';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -34,54 +27,9 @@ import { SITE_SCHEMA as SYNC_SITE_SCHEMA } from './services/db/sync';
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        { provide: HTTP_INTERCEPTORS, useClass: CoreInterceptor, multi: true },
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [
 | 
			
		||||
                FILEPOOL_SITE_SCHEMA,
 | 
			
		||||
                SITES_SITE_SCHEMA,
 | 
			
		||||
                SYNC_SITE_SCHEMA,
 | 
			
		||||
            ],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        { provide: ApplicationInitStatus, useClass: CoreApplicationInitStatus, deps: [Injector] },
 | 
			
		||||
        ...getDatabaseProviders(),
 | 
			
		||||
        ...getInitializerProviders(),
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreModule {
 | 
			
		||||
 | 
			
		||||
    constructor(platform: Platform, injector: Injector) {
 | 
			
		||||
        // Set the injector.
 | 
			
		||||
        setSingletonsInjector(injector);
 | 
			
		||||
 | 
			
		||||
        // Register a handler for platform ready.
 | 
			
		||||
        CoreInit.instance.registerProcess({
 | 
			
		||||
            name: 'CorePlatformReady',
 | 
			
		||||
            priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 400,
 | 
			
		||||
            blocking: true,
 | 
			
		||||
            load: async () => {
 | 
			
		||||
                await platform.ready();
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Register the update manager as an init process.
 | 
			
		||||
        CoreInit.instance.registerProcess(CoreUpdateManager.instance);
 | 
			
		||||
 | 
			
		||||
        // Restore the user's session during the init process.
 | 
			
		||||
        CoreInit.instance.registerProcess({
 | 
			
		||||
            name: 'CoreRestoreSession',
 | 
			
		||||
            priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 200,
 | 
			
		||||
            blocking: false,
 | 
			
		||||
            load: CoreSites.instance.restoreSession.bind(CoreSites.instance),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Register clear app tmp folder.
 | 
			
		||||
        CoreInit.instance.registerProcess({
 | 
			
		||||
            name: 'CoreClearTmpFolder',
 | 
			
		||||
            priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 150,
 | 
			
		||||
            blocking: false,
 | 
			
		||||
            load: CoreFile.instance.clearTmpFolder.bind(CoreFile.instance),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Execute the init processes.
 | 
			
		||||
        CoreInit.instance.executeInitProcesses();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export class CoreModule {}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import { NgModule } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/db/course';
 | 
			
		||||
import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/database/course';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    providers: [
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreCourseManualCompletionDBRecord, MANUAL_COMPLETION_TABLE } from './db/course';
 | 
			
		||||
import { CoreCourseManualCompletionDBRecord, MANUAL_COMPLETION_TABLE } from './database/course';
 | 
			
		||||
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
 | 
			
		||||
import { CoreCourseStatusDBRecord, COURSE_STATUS_TABLE } from './db/course';
 | 
			
		||||
import { CoreCourseStatusDBRecord, COURSE_STATUS_TABLE } from './database/course';
 | 
			
		||||
import { CoreCourseOffline } from './course-offline';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,9 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { Platform } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreInitDelegate } from '@services/init';
 | 
			
		||||
import { CoreEmulatorHelperProvider } from './services/emulator-helper';
 | 
			
		||||
import { CoreEmulatorComponentsModule } from './components/components.module';
 | 
			
		||||
 | 
			
		||||
@ -141,20 +140,18 @@ import { ZipMock } from './services/zip';
 | 
			
		||||
            deps: [Platform, File],
 | 
			
		||||
            useFactory: (platform: Platform, file: File): Zip => platform.is('cordova') ? new Zip() : new ZipMock(file),
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            deps: [Platform, CoreEmulatorHelperProvider],
 | 
			
		||||
            useFactory: (platform: Platform, helperProvider: CoreEmulatorHelperProvider) => () => {
 | 
			
		||||
                if (platform.is('cordova')) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return helperProvider.load();
 | 
			
		||||
            },
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class CoreEmulatorModule {
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        platform: Platform,
 | 
			
		||||
        initDelegate: CoreInitDelegate,
 | 
			
		||||
        helper: CoreEmulatorHelperProvider,
 | 
			
		||||
    ) {
 | 
			
		||||
 | 
			
		||||
        if (!platform.is('cordova')) {
 | 
			
		||||
            // Register an init process to load the Mocks that need it.
 | 
			
		||||
            initDelegate.registerProcess(helper);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export class CoreEmulatorModule {}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,6 @@ import { Injectable } from '@angular/core';
 | 
			
		||||
import { File } from '@ionic-native/file/ngx';
 | 
			
		||||
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreInitDelegate, CoreInitHandler } from '@services/init';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { FileMock } from './file';
 | 
			
		||||
@ -25,14 +24,8 @@ import { FileTransferErrorMock } from './file-transfer';
 | 
			
		||||
/**
 | 
			
		||||
 * Helper service for the emulator feature. It also acts as an init handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({
 | 
			
		||||
    providedIn: 'root',
 | 
			
		||||
})
 | 
			
		||||
export class CoreEmulatorHelperProvider implements CoreInitHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'CoreEmulator';
 | 
			
		||||
    priority = CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 500;
 | 
			
		||||
    blocking = true;
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreEmulatorHelperProvider {
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,7 @@ import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { NavController } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreApp, CoreRedirectData } from '@services/app';
 | 
			
		||||
import { CoreInit } from '@services/init';
 | 
			
		||||
import { SplashScreen } from '@singletons';
 | 
			
		||||
import { ApplicationInit, SplashScreen } from '@singletons';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
 | 
			
		||||
@ -39,7 +38,7 @@ export class CoreLoginInitPage implements OnInit {
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        // Wait for the app to be ready.
 | 
			
		||||
        await CoreInit.instance.ready();
 | 
			
		||||
        await ApplicationInit.instance.donePromise;
 | 
			
		||||
 | 
			
		||||
        // Check if there was a pending redirect.
 | 
			
		||||
        const redirectData = CoreApp.instance.getRedirect();
 | 
			
		||||
 | 
			
		||||
@ -15,10 +15,9 @@
 | 
			
		||||
import { NavController } from '@ionic/angular';
 | 
			
		||||
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreInit } from '@services/init';
 | 
			
		||||
import { CoreLoginInitPage } from '@features/login/pages/init/init';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { SplashScreen } from '@singletons';
 | 
			
		||||
import { ApplicationInit, SplashScreen } from '@singletons';
 | 
			
		||||
 | 
			
		||||
import { mock, mockSingleton, renderComponent, RenderConfig } from '@/testing/utils';
 | 
			
		||||
 | 
			
		||||
@ -29,7 +28,7 @@ describe('CoreLoginInitPage', () => {
 | 
			
		||||
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        mockSingleton(CoreApp, { getRedirect: () => ({}) });
 | 
			
		||||
        mockSingleton(CoreInit, { ready: () => Promise.resolve() });
 | 
			
		||||
        mockSingleton(ApplicationInit, { donePromise: Promise.resolve() });
 | 
			
		||||
        mockSingleton(CoreSites, { isLoggedIn: () => false });
 | 
			
		||||
        mockSingleton(SplashScreen, ['hide']);
 | 
			
		||||
 | 
			
		||||
@ -52,7 +51,7 @@ describe('CoreLoginInitPage', () => {
 | 
			
		||||
        const fixture = await renderComponent(CoreLoginInitPage, config);
 | 
			
		||||
 | 
			
		||||
        fixture.componentInstance.ngOnInit();
 | 
			
		||||
        await CoreInit.instance.ready();
 | 
			
		||||
        await ApplicationInit.instance.donePromise;
 | 
			
		||||
 | 
			
		||||
        expect(navController.navigateRoot).toHaveBeenCalledWith('/login/sites');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -15,8 +15,8 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { Router, CanLoad, CanActivate, UrlTree } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreInit } from '@services/init';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { ApplicationInit } from '@singletons';
 | 
			
		||||
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AuthGuard implements CanLoad, CanActivate {
 | 
			
		||||
@ -32,7 +32,7 @@ export class AuthGuard implements CanLoad, CanActivate {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async guard(): Promise<true | UrlTree> {
 | 
			
		||||
        await CoreInit.instance.ready();
 | 
			
		||||
        await ApplicationInit.instance.donePromise;
 | 
			
		||||
 | 
			
		||||
        return CoreSites.instance.isLoggedIn() || this.router.parseUrl('/login');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								src/core/initializers/clear-tmp-folder.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/initializers/clear-tmp-folder.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
// (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 { CoreFile } from '@services/file';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await CoreFile.instance.clearTmpFolder();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								src/core/initializers/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/core/initializers/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// (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 { APP_INITIALIZER, Provider } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
export function getInitializerProviders(): Provider[] {
 | 
			
		||||
    const context = require.context('./', false, /\.ts$/);
 | 
			
		||||
 | 
			
		||||
    return context.keys().reduce((providers, fileName) => {
 | 
			
		||||
        const name = (fileName.match(/^(?:\.\/)?(.+)\.ts$/) || [])[1];
 | 
			
		||||
 | 
			
		||||
        if (typeof name !== 'undefined' && name !== 'index') {
 | 
			
		||||
            providers.push({
 | 
			
		||||
                provide: APP_INITIALIZER,
 | 
			
		||||
                useValue: context(fileName).default,
 | 
			
		||||
                multi: true,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return providers;
 | 
			
		||||
    }, [] as Provider[]);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/core/initializers/initialize-databases.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/core/initializers/initialize-databases.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
// (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 { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { CoreCron } from '@services/cron';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreLocalNotifications } from '@services/local-notifications';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await Promise.all([
 | 
			
		||||
        CoreApp.instance.initialiseDatabase(),
 | 
			
		||||
        CoreConfig.instance.initialiseDatabase(),
 | 
			
		||||
        CoreCron.instance.initialiseDatabase(),
 | 
			
		||||
        CoreFilepool.instance.initialiseDatabase(),
 | 
			
		||||
        CoreLocalNotifications.instance.initialiseDatabase(),
 | 
			
		||||
        CoreSites.instance.initialiseDatabase(),
 | 
			
		||||
    ]);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/core/initializers/load-update-manager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/initializers/load-update-manager.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
// (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 { CoreUpdateManager } from '@services/update-manager';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await CoreUpdateManager.instance.load();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								src/core/initializers/override-window-open.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/core/initializers/override-window-open.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
// (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 { Platform } from '@singletons';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await Platform.instance.ready();
 | 
			
		||||
 | 
			
		||||
    if (!window.cordova?.InAppBrowser) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    window.open = window.cordova.InAppBrowser.open;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/core/initializers/prepare-automated-tests.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/core/initializers/prepare-automated-tests.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
// (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 { ApplicationRef } from '@angular/core';
 | 
			
		||||
import { CoreApp, CoreAppProvider } from '@services/app';
 | 
			
		||||
import { CoreCron, CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { Application } from '@singletons';
 | 
			
		||||
 | 
			
		||||
type AutomatedTestsWindow = Window & {
 | 
			
		||||
    appRef?: ApplicationRef;
 | 
			
		||||
    appProvider?: CoreAppProvider;
 | 
			
		||||
    cronProvider?: CoreCronDelegate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) {
 | 
			
		||||
    window.appRef = Application.instance;
 | 
			
		||||
    window.appProvider = CoreApp.instance;
 | 
			
		||||
    window.cronProvider = CoreCron.instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function(): void {
 | 
			
		||||
    if (!CoreAppProvider.isAutomated()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    initializeAutomatedTestsWindow(window);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/core/initializers/restore-session.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/initializers/restore-session.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
// (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 { CoreSites } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await CoreSites.instance.restoreSession();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/core/initializers/subscribe-to-keyboard-events.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/core/initializers/subscribe-to-keyboard-events.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// (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 { CoreApp } from '@services/app';
 | 
			
		||||
import { NgZone, Keyboard } from '@singletons';
 | 
			
		||||
 | 
			
		||||
export default function(): void {
 | 
			
		||||
    const zone = NgZone.instance;
 | 
			
		||||
    const app = CoreApp.instance;
 | 
			
		||||
    const keyboard = Keyboard.instance;
 | 
			
		||||
 | 
			
		||||
    // Execute callbacks in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
    keyboard.onKeyboardShow().subscribe(data => zone.run(() => app.onKeyboardShow(data.keyboardHeight)));
 | 
			
		||||
    keyboard.onKeyboardHide().subscribe(() => zone.run(() => app.onKeyboardHide()));
 | 
			
		||||
    keyboard.onKeyboardWillShow().subscribe(() => zone.run(() => app.onKeyboardWillShow()));
 | 
			
		||||
    keyboard.onKeyboardWillHide().subscribe(() => zone.run(() => app.onKeyboardWillHide()));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/core/initializers/wait-for-platform-ready.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/core/initializers/wait-for-platform-ready.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
// (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 { Platform } from '@singletons';
 | 
			
		||||
 | 
			
		||||
export default async function(): Promise<void> {
 | 
			
		||||
    await Platform.instance.ready();
 | 
			
		||||
}
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injectable, NgZone, ApplicationRef } from '@angular/core';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { Params, Router } from '@angular/router';
 | 
			
		||||
import { Connection } from '@ionic-native/network/ngx';
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,15 @@ import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
 | 
			
		||||
import { makeSingleton, Keyboard, Network, StatusBar, Platform, Device } from '@singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreColors } from '@singletons/colors';
 | 
			
		||||
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/db/app';
 | 
			
		||||
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Object responsible of managing schema versions.
 | 
			
		||||
 */
 | 
			
		||||
type SchemaVersionsManager = {
 | 
			
		||||
    get(schemaName: string): Promise<number>;
 | 
			
		||||
    set(schemaName: string, version: number): Promise<void>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Factory to provide some global functionalities, like access to the global app database.
 | 
			
		||||
@ -55,62 +63,18 @@ export class CoreAppProvider {
 | 
			
		||||
    protected forceOffline = false;
 | 
			
		||||
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected createVersionsTableReady: Promise<void>;
 | 
			
		||||
    protected schemaVersionsManager: Promise<SchemaVersionsManager>;
 | 
			
		||||
    protected resolveSchemaVersionsManager!: (schemaVersionsManager: SchemaVersionsManager) => void;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        appRef: ApplicationRef,
 | 
			
		||||
        zone: NgZone,
 | 
			
		||||
        protected router: Router,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreAppProvider');
 | 
			
		||||
    constructor(protected router: Router) {
 | 
			
		||||
        this.schemaVersionsManager = new Promise(resolve => this.resolveSchemaVersionsManager = resolve);
 | 
			
		||||
        this.db = CoreDB.instance.getDB(DBNAME);
 | 
			
		||||
 | 
			
		||||
        // Create the schema versions table.
 | 
			
		||||
        this.createVersionsTableReady = this.db.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA);
 | 
			
		||||
 | 
			
		||||
        Keyboard.instance.onKeyboardShow().subscribe((data) => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            zone.run(() => {
 | 
			
		||||
                document.body.classList.add('keyboard-is-open');
 | 
			
		||||
                this.setKeyboardShown(true);
 | 
			
		||||
                // Error on iOS calculating size.
 | 
			
		||||
                // More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, data.keyboardHeight);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        Keyboard.instance.onKeyboardHide().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            zone.run(() => {
 | 
			
		||||
                document.body.classList.remove('keyboard-is-open');
 | 
			
		||||
                this.setKeyboardShown(false);
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        Keyboard.instance.onKeyboardWillShow().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            zone.run(() => {
 | 
			
		||||
                this.keyboardOpening = true;
 | 
			
		||||
                this.keyboardClosing = false;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        Keyboard.instance.onKeyboardWillHide().subscribe(() => {
 | 
			
		||||
            // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | 
			
		||||
            zone.run(() => {
 | 
			
		||||
                this.keyboardOpening = false;
 | 
			
		||||
                this.keyboardClosing = true;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreAppProvider');
 | 
			
		||||
 | 
			
		||||
        // @todo
 | 
			
		||||
        // this.platform.registerBackButtonAction(() => {
 | 
			
		||||
        //     this.backButtonAction();
 | 
			
		||||
        // }, 100);
 | 
			
		||||
 | 
			
		||||
        // Export the app provider and appRef to control the application in Behat tests.
 | 
			
		||||
        if (CoreAppProvider.isAutomated()) {
 | 
			
		||||
            (<WindowForAutomatedTests> window).appProvider = this;
 | 
			
		||||
            (<WindowForAutomatedTests> window).appRef = appRef;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -122,6 +86,30 @@ export class CoreAppProvider {
 | 
			
		||||
        return !!navigator.webdriver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        await this.db.createTableFromSchema(SCHEMA_VERSIONS_TABLE_SCHEMA);
 | 
			
		||||
 | 
			
		||||
        this.resolveSchemaVersionsManager({
 | 
			
		||||
            get: async name => {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Fetch installed version of the schema.
 | 
			
		||||
                    const entry = await this.db.getRecord<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE_NAME, { name });
 | 
			
		||||
 | 
			
		||||
                    return entry.version;
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    // No installed version yet.
 | 
			
		||||
                    return  0;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            set: async (name, version) => {
 | 
			
		||||
                await this.db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name, version });
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the browser supports mediaDevices.getUserMedia.
 | 
			
		||||
     *
 | 
			
		||||
@ -158,20 +146,8 @@ export class CoreAppProvider {
 | 
			
		||||
    async createTablesFromSchema(schema: CoreAppSchema): Promise<void> {
 | 
			
		||||
        this.logger.debug(`Apply schema to app DB: ${schema.name}`);
 | 
			
		||||
 | 
			
		||||
        let oldVersion: number;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Wait for the schema versions table to be created.
 | 
			
		||||
            await this.createVersionsTableReady;
 | 
			
		||||
 | 
			
		||||
            // Fetch installed version of the schema.
 | 
			
		||||
            const entry = await this.db.getRecord<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name });
 | 
			
		||||
 | 
			
		||||
            oldVersion = entry.version;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // No installed version yet.
 | 
			
		||||
            oldVersion = 0;
 | 
			
		||||
        }
 | 
			
		||||
        const schemaVersionsManager = await this.schemaVersionsManager;
 | 
			
		||||
        const oldVersion = await schemaVersionsManager.get(schema.name);
 | 
			
		||||
 | 
			
		||||
        if (oldVersion >= schema.version) {
 | 
			
		||||
            // Version already installed, nothing else to do.
 | 
			
		||||
@ -188,7 +164,7 @@ export class CoreAppProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set installed version.
 | 
			
		||||
        await this.db.insertRecord(SCHEMA_VERSIONS_TABLE_NAME, { name: schema.name, version: schema.version });
 | 
			
		||||
        schemaVersionsManager.set(schema.name, schema.version);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -429,6 +405,44 @@ export class CoreAppProvider {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify that Keyboard has been shown.
 | 
			
		||||
     *
 | 
			
		||||
     * @param keyboardHeight Keyboard height.
 | 
			
		||||
     */
 | 
			
		||||
    onKeyboardShow(keyboardHeight: number): void {
 | 
			
		||||
        document.body.classList.add('keyboard-is-open');
 | 
			
		||||
        this.setKeyboardShown(true);
 | 
			
		||||
        // Error on iOS calculating size.
 | 
			
		||||
        // More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify that Keyboard has been hidden.
 | 
			
		||||
     */
 | 
			
		||||
    onKeyboardHide(): void {
 | 
			
		||||
        document.body.classList.remove('keyboard-is-open');
 | 
			
		||||
        this.setKeyboardShown(false);
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify that Keyboard is about to be shown.
 | 
			
		||||
     */
 | 
			
		||||
    onKeyboardWillShow(): void {
 | 
			
		||||
        this.keyboardOpening = true;
 | 
			
		||||
        this.keyboardClosing = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify that Keyboard is about to be hidden.
 | 
			
		||||
     */
 | 
			
		||||
    onKeyboardWillHide(): void {
 | 
			
		||||
        this.keyboardOpening = false;
 | 
			
		||||
        this.keyboardClosing = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set keyboard shown or hidden.
 | 
			
		||||
     *
 | 
			
		||||
@ -735,11 +749,3 @@ export type CoreAppSchema = {
 | 
			
		||||
     */
 | 
			
		||||
    migrate?(db: SQLiteDB, oldVersion: number): Promise<void>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extended window type for automated tests.
 | 
			
		||||
 */
 | 
			
		||||
export type WindowForAutomatedTests = Window & {
 | 
			
		||||
    appProvider?: CoreAppProvider;
 | 
			
		||||
    appRef?: ApplicationRef;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { SQLiteDB } from '@classes/sqlitedb';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CONFIG_TABLE_NAME, APP_SCHEMA, ConfigDBEntry } from '@services/db/config';
 | 
			
		||||
import { CONFIG_TABLE_NAME, APP_SCHEMA, ConfigDBEntry } from '@services/database/config';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Factory to provide access to dynamic and permanent config and settings.
 | 
			
		||||
@ -26,14 +26,24 @@ import { CONFIG_TABLE_NAME, APP_SCHEMA, ConfigDBEntry } from '@services/db/confi
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreConfigProvider {
 | 
			
		||||
 | 
			
		||||
    protected appDB: SQLiteDB;
 | 
			
		||||
    protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.appDB = CoreApp.instance.getDB();
 | 
			
		||||
        this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => {
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.instance.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.instance.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -43,9 +53,9 @@ export class CoreConfigProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async delete(name: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await this.appDB.deleteRecords(CONFIG_TABLE_NAME, { name });
 | 
			
		||||
        await db.deleteRecords(CONFIG_TABLE_NAME, { name });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -56,10 +66,10 @@ export class CoreConfigProvider {
 | 
			
		||||
     * @return Resolves upon success along with the config data. Reject on failure.
 | 
			
		||||
     */
 | 
			
		||||
    async get<T>(name: string, defaultValue?: T): Promise<T> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const entry = await this.appDB.getRecord<ConfigDBEntry>(CONFIG_TABLE_NAME, { name });
 | 
			
		||||
            const entry = await db.getRecord<ConfigDBEntry>(CONFIG_TABLE_NAME, { name });
 | 
			
		||||
 | 
			
		||||
            return entry.value;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
@ -79,9 +89,9 @@ export class CoreConfigProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async set(name: string, value: number | string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await this.appDB.insertRecord(CONFIG_TABLE_NAME, { name, value });
 | 
			
		||||
        await db.insertRecord(CONFIG_TABLE_NAME, { name, value });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 | 
			
		||||
import { Injectable, NgZone } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreApp, CoreAppProvider } from '@services/app';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
@ -23,7 +23,7 @@ import { CoreError } from '@classes/errors/error';
 | 
			
		||||
 | 
			
		||||
import { makeSingleton, Network } from '@singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/db/cron';
 | 
			
		||||
import { APP_SCHEMA, CRON_TABLE_NAME, CronDBEntry } from '@services/database/cron';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Service to handle cron processes. The registered processes will be executed every certain time.
 | 
			
		||||
@ -37,18 +37,16 @@ export class CoreCronDelegate {
 | 
			
		||||
    static readonly MAX_TIME_PROCESS = 120000; // Max time a process can block the queue. Defaults to 2 minutes.
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected appDB: SQLiteDB;
 | 
			
		||||
    protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | 
			
		||||
    protected handlers: { [s: string]: CoreCronHandler } = {};
 | 
			
		||||
    protected queuePromise: Promise<void> = Promise.resolve();
 | 
			
		||||
 | 
			
		||||
    constructor(zone: NgZone) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreCronDelegate');
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
        this.appDB = CoreApp.instance.getDB();
 | 
			
		||||
        this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
    constructor(zone: NgZone) {
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreCronDelegate');
 | 
			
		||||
 | 
			
		||||
        // When the app is re-connected, start network handlers that were stopped.
 | 
			
		||||
        Network.instance.onConnect().subscribe(() => {
 | 
			
		||||
@ -57,11 +55,19 @@ export class CoreCronDelegate {
 | 
			
		||||
                this.startNetworkHandlers();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        // Export the sync provider so Behat tests can trigger cron tasks without waiting.
 | 
			
		||||
        if (CoreAppProvider.isAutomated()) {
 | 
			
		||||
            (<WindowForAutomatedTests> window).cronProvider = this;
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.instance.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.instance.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -240,12 +246,11 @@ export class CoreCronDelegate {
 | 
			
		||||
     * @return Promise resolved with the handler's last execution time.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getHandlerLastExecutionTime(name: string): Promise<number> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const id = this.getHandlerLastExecutionId(name);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const entry = await this.appDB.getRecord<CronDBEntry>(CRON_TABLE_NAME, { id });
 | 
			
		||||
            const entry = await db.getRecord<CronDBEntry>(CRON_TABLE_NAME, { id });
 | 
			
		||||
 | 
			
		||||
            const time = Number(entry.value);
 | 
			
		||||
 | 
			
		||||
@ -400,15 +405,14 @@ export class CoreCronDelegate {
 | 
			
		||||
     * @return Promise resolved when the execution time is saved.
 | 
			
		||||
     */
 | 
			
		||||
    protected async setHandlerLastExecutionTime(name: string, time: number): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const id = this.getHandlerLastExecutionId(name);
 | 
			
		||||
        const entry = {
 | 
			
		||||
            id,
 | 
			
		||||
            value: time,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await this.appDB.insertRecord(CRON_TABLE_NAME, entry);
 | 
			
		||||
        await db.insertRecord(CRON_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -532,10 +536,3 @@ export interface CoreCronHandler {
 | 
			
		||||
     */
 | 
			
		||||
    execute?(siteId?: string, force?: boolean): Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Extended window type for automated tests.
 | 
			
		||||
 */
 | 
			
		||||
export type WindowForAutomatedTests = Window & {
 | 
			
		||||
    cronProvider?: CoreCronDelegate;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								src/core/services/database/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/services/database/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
// (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 { Provider } from '@angular/core';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
 | 
			
		||||
import { SITE_SCHEMA as FILEPOOL_SITE_SCHEMA } from './filepool';
 | 
			
		||||
import { SITE_SCHEMA as SITES_SITE_SCHEMA } from './sites';
 | 
			
		||||
import { SITE_SCHEMA as SYNC_SITE_SCHEMA } from './sync';
 | 
			
		||||
 | 
			
		||||
export function getDatabaseProviders(): Provider[] {
 | 
			
		||||
    return [{
 | 
			
		||||
        provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
        useValue: [
 | 
			
		||||
            FILEPOOL_SITE_SCHEMA,
 | 
			
		||||
            SITES_SITE_SCHEMA,
 | 
			
		||||
            SYNC_SITE_SCHEMA,
 | 
			
		||||
        ],
 | 
			
		||||
        multi: true,
 | 
			
		||||
    }];
 | 
			
		||||
}
 | 
			
		||||
@ -18,7 +18,6 @@ import { Md5 } from 'ts-md5/dist/md5';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreInit } from '@services/init';
 | 
			
		||||
import { CorePluginFile } from '@services/plugin-file-delegate';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreWS, CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
@ -31,7 +30,7 @@ import { CoreUtils, PromiseDefer } from '@services/utils/utils';
 | 
			
		||||
import { SQLiteDB } from '@classes/sqlitedb';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { makeSingleton, Network, NgZone, Translate } from '@singletons';
 | 
			
		||||
import { ApplicationInit, makeSingleton, Network, NgZone, Translate } from '@singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import {
 | 
			
		||||
    APP_SCHEMA,
 | 
			
		||||
@ -46,7 +45,7 @@ import {
 | 
			
		||||
    CoreFilepoolPackageEntry,
 | 
			
		||||
    CoreFilepoolQueueEntry,
 | 
			
		||||
    CoreFilepoolQueueDBEntry,
 | 
			
		||||
} from '@services/db/filepool';
 | 
			
		||||
} from '@services/database/filepool';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Factory for handling downloading files and retrieve downloaded files.
 | 
			
		||||
@ -75,8 +74,6 @@ export class CoreFilepoolProvider {
 | 
			
		||||
        'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))';
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected appDB: SQLiteDB;
 | 
			
		||||
    protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | 
			
		||||
    protected queueState = CoreFilepoolProvider.QUEUE_PAUSED;
 | 
			
		||||
    protected urlAttributes: RegExp[] = [
 | 
			
		||||
        new RegExp('(\\?|&)token=([A-Za-z0-9]*)'),
 | 
			
		||||
@ -92,23 +89,36 @@ export class CoreFilepoolProvider {
 | 
			
		||||
    protected packagesPromises: { [s: string]: { [s: string]: Promise<void> } } = {};
 | 
			
		||||
    protected filePromises: { [s: string]: { [s: string]: Promise<string> } } = {};
 | 
			
		||||
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreFilepoolProvider');
 | 
			
		||||
 | 
			
		||||
        this.appDB = CoreApp.instance.getDB();
 | 
			
		||||
        this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.instance.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.instance.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init some properties.
 | 
			
		||||
     */
 | 
			
		||||
    protected async init(): Promise<void> {
 | 
			
		||||
        // Waiting for the app to be ready to start processing the queue.
 | 
			
		||||
        await CoreInit.instance.ready();
 | 
			
		||||
        await ApplicationInit.instance.donePromise;
 | 
			
		||||
 | 
			
		||||
        this.checkQueueProcessing();
 | 
			
		||||
 | 
			
		||||
@ -265,11 +275,11 @@ export class CoreFilepoolProvider {
 | 
			
		||||
        options: CoreFilepoolFileOptions = {},
 | 
			
		||||
        link?: CoreFilepoolComponentLink,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Adding ${fileId} to the queue`);
 | 
			
		||||
 | 
			
		||||
        await this.appDB.insertRecord(QUEUE_TABLE_NAME, {
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await db.insertRecord(QUEUE_TABLE_NAME, {
 | 
			
		||||
            siteId,
 | 
			
		||||
            fileId,
 | 
			
		||||
            url,
 | 
			
		||||
@ -319,8 +329,6 @@ export class CoreFilepoolProvider {
 | 
			
		||||
        revision?: number,
 | 
			
		||||
        alreadyFixed?: boolean,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        if (!CoreFile.instance.isAvailable()) {
 | 
			
		||||
            throw new CoreError('File system cannot be used.');
 | 
			
		||||
        }
 | 
			
		||||
@ -399,7 +407,9 @@ export class CoreFilepoolProvider {
 | 
			
		||||
            // Update only when required.
 | 
			
		||||
            this.logger.debug(`Updating file ${fileId} which is already in queue`);
 | 
			
		||||
 | 
			
		||||
            return this.appDB.updateRecords(QUEUE_TABLE_NAME, newData, primaryKey).then(() =>
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
            return db.updateRecords(QUEUE_TABLE_NAME, newData, primaryKey).then(() =>
 | 
			
		||||
                this.getQueuePromise(siteId, fileId, true, onProgress));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -2111,9 +2121,8 @@ export class CoreFilepoolProvider {
 | 
			
		||||
     * @return Resolved with file object from DB on success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const entry = await this.appDB.getRecord<CoreFilepoolQueueEntry>(QUEUE_TABLE_NAME, { siteId, fileId });
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const entry = await db.getRecord<CoreFilepoolQueueEntry>(QUEUE_TABLE_NAME, { siteId, fileId });
 | 
			
		||||
 | 
			
		||||
        if (typeof entry === 'undefined') {
 | 
			
		||||
            throw new CoreError('File not found in queue.');
 | 
			
		||||
@ -2445,12 +2454,11 @@ export class CoreFilepoolProvider {
 | 
			
		||||
     * @return Resolved on success. Rejected on failure.
 | 
			
		||||
     */
 | 
			
		||||
    protected async processImportantQueueItem(): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        let items: CoreFilepoolQueueEntry[];
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            items = await this.appDB.getRecords<CoreFilepoolQueueEntry>(
 | 
			
		||||
            items = await db.getRecords<CoreFilepoolQueueEntry>(
 | 
			
		||||
                QUEUE_TABLE_NAME,
 | 
			
		||||
                undefined,
 | 
			
		||||
                'priority DESC, added ASC',
 | 
			
		||||
@ -2594,9 +2602,9 @@ export class CoreFilepoolProvider {
 | 
			
		||||
     * @return Resolved on success. Rejected on failure. It is advised to silently ignore failures.
 | 
			
		||||
     */
 | 
			
		||||
    protected async removeFromQueue(siteId: string, fileId: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await this.appDB.deleteRecords(QUEUE_TABLE_NAME, { siteId, fileId });
 | 
			
		||||
        await db.deleteRecords(QUEUE_TABLE_NAME, { siteId, fileId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -1,179 +0,0 @@
 | 
			
		||||
// (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 { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreUtils, PromiseDefer, OrderedPromiseData } from '@services/utils/utils';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Interface that all init handlers must implement.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreInitHandler = {
 | 
			
		||||
    /**
 | 
			
		||||
     * A name to identify the handler.
 | 
			
		||||
     */
 | 
			
		||||
    name: string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The highest priority is executed first. You should use values lower than MAX_RECOMMENDED_PRIORITY.
 | 
			
		||||
     */
 | 
			
		||||
    priority?: number;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set this to true when this process should be resolved before any following one.
 | 
			
		||||
     */
 | 
			
		||||
    blocking?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function to execute during the init process.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    load(): Promise<void>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Provider for initialisation mechanisms.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreInitDelegate {
 | 
			
		||||
 | 
			
		||||
    static readonly DEFAULT_PRIORITY = 100; // Default priority for init processes.
 | 
			
		||||
    static readonly MAX_RECOMMENDED_PRIORITY = 600;
 | 
			
		||||
 | 
			
		||||
    protected initProcesses: { [s: string]: CoreInitHandler } = {};
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected readiness?: CoreInitReadinessPromiseDefer<void>;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreInitDelegate');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Executes the registered init processes.
 | 
			
		||||
     *
 | 
			
		||||
     * Reserved for core use, do not call directly.
 | 
			
		||||
     */
 | 
			
		||||
    executeInitProcesses(): void {
 | 
			
		||||
        const ordered: CoreInitHandler[] = [];
 | 
			
		||||
 | 
			
		||||
        if (typeof this.readiness == 'undefined') {
 | 
			
		||||
            this.initReadiness();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Re-ordering by priority.
 | 
			
		||||
        for (const name in this.initProcesses) {
 | 
			
		||||
            ordered.push(this.initProcesses[name]);
 | 
			
		||||
        }
 | 
			
		||||
        ordered.sort((a, b) => (b.priority || 0) - (a.priority || 0));
 | 
			
		||||
 | 
			
		||||
        const orderedPromises: OrderedPromiseData[] = ordered.map((data: CoreInitHandler) => ({
 | 
			
		||||
            function: this.prepareProcess.bind(this, data),
 | 
			
		||||
            blocking: !!data.blocking,
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Execute all the processes in order to solve dependencies.
 | 
			
		||||
        CoreUtils.instance.executeOrderedPromises(orderedPromises).finally(this.readiness!.resolve);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init the readiness promise.
 | 
			
		||||
     */
 | 
			
		||||
    protected initReadiness(): void {
 | 
			
		||||
        this.readiness = CoreUtils.instance.promiseDefer();
 | 
			
		||||
 | 
			
		||||
        // eslint-disable-next-line promise/catch-or-return
 | 
			
		||||
        this.readiness.promise.then(() => this.readiness!.resolved = true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantly returns if the app is ready.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether it's ready.
 | 
			
		||||
     */
 | 
			
		||||
    isReady(): boolean {
 | 
			
		||||
        return this.readiness?.resolved || false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to return a function that executes the process.
 | 
			
		||||
     *
 | 
			
		||||
     * @param data The data of the process.
 | 
			
		||||
     * @return Promise of the process.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prepareProcess(data: CoreInitHandler): Promise<void> {
 | 
			
		||||
        this.logger.debug(`Executing init process '${data.name}'`);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await data.load();
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            this.logger.error('Error while calling the init process \'' + data.name + '\'. ' + e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notifies when the app is ready. This returns a promise that is resolved when the app is initialised.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Resolved when the app is initialised. Never rejected.
 | 
			
		||||
     */
 | 
			
		||||
    async ready(): Promise<void> {
 | 
			
		||||
        if (typeof this.readiness == 'undefined') {
 | 
			
		||||
            // Prevent race conditions if this is called before executeInitProcesses.
 | 
			
		||||
            this.initReadiness();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.readiness!.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Registers an initialisation process.
 | 
			
		||||
     *
 | 
			
		||||
     * @description
 | 
			
		||||
     * Init processes can be used to add initialisation logic to the app. Anything that should block the user interface while
 | 
			
		||||
     * some processes are done should be an init process. It is recommended to use a priority lower than MAX_RECOMMENDED_PRIORITY
 | 
			
		||||
     * to make sure that your process does not happen before some essential other core processes.
 | 
			
		||||
     *
 | 
			
		||||
     * An init process should never change state or prompt user interaction.
 | 
			
		||||
     *
 | 
			
		||||
     * This delegate cannot be used by site plugins.
 | 
			
		||||
     *
 | 
			
		||||
     * @param instance The instance of the handler.
 | 
			
		||||
     */
 | 
			
		||||
    registerProcess(handler: CoreInitHandler): void {
 | 
			
		||||
        if (typeof handler.priority == 'undefined') {
 | 
			
		||||
            handler.priority = CoreInitDelegate.DEFAULT_PRIORITY;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (typeof this.initProcesses[handler.name] != 'undefined') {
 | 
			
		||||
            this.logger.log(`Process '${handler.name}' already registered.`);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.log(`Registering process '${handler.name}'.`);
 | 
			
		||||
        this.initProcesses[handler.name] = handler;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class CoreInit extends makeSingleton(CoreInitDelegate) {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deferred promise for init readiness.
 | 
			
		||||
 */
 | 
			
		||||
type CoreInitReadinessPromiseDefer<T> = PromiseDefer<T> & {
 | 
			
		||||
    resolved?: boolean; // If true, readiness have been resolved.
 | 
			
		||||
};
 | 
			
		||||
@ -34,7 +34,7 @@ import {
 | 
			
		||||
    COMPONENTS_TABLE_NAME,
 | 
			
		||||
    SITES_TABLE_NAME,
 | 
			
		||||
    CodeRequestsQueueItem,
 | 
			
		||||
} from '@services/db/local-notifications';
 | 
			
		||||
} from '@services/database/local-notifications';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle local notifications.
 | 
			
		||||
@ -43,8 +43,6 @@ import {
 | 
			
		||||
export class CoreLocalNotificationsProvider {
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected appDB: SQLiteDB;
 | 
			
		||||
    protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | 
			
		||||
    protected codes: { [s: string]: number } = {};
 | 
			
		||||
    protected codeRequestsQueue: {[key: string]: CodeRequestsQueueItem} = {};
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
@ -58,17 +56,31 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
    protected updateSubscription?: Subscription;
 | 
			
		||||
    protected queueRunner: CoreQueueRunner; // Queue to decrease the number of concurrent calls to the plugin (see MOBILE-3477).
 | 
			
		||||
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreLocalNotificationsProvider');
 | 
			
		||||
        this.queueRunner = new CoreQueueRunner(10);
 | 
			
		||||
        this.appDB = CoreApp.instance.getDB();
 | 
			
		||||
        this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.init();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.instance.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.instance.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Init some properties.
 | 
			
		||||
     */
 | 
			
		||||
@ -212,8 +224,6 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
     * @return Promise resolved when the code is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getCode(table: string, id: string): Promise<number> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const key = table + '#' + id;
 | 
			
		||||
 | 
			
		||||
        // Check if the code is already in memory.
 | 
			
		||||
@ -221,23 +231,25 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
            return this.codes[key];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Check if we already have a code stored for that ID.
 | 
			
		||||
            const entry = await this.appDB.getRecord<{id: string; code: number}>(table, { id: id });
 | 
			
		||||
            const entry = await db.getRecord<{id: string; code: number}>(table, { id: id });
 | 
			
		||||
 | 
			
		||||
            this.codes[key] = entry.code;
 | 
			
		||||
 | 
			
		||||
            return entry.code;
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
            // No code stored for that ID. Create a new code for it.
 | 
			
		||||
            const entries = await this.appDB.getRecords<{id: string; code: number}>(table, undefined, 'code DESC');
 | 
			
		||||
            const entries = await db.getRecords<{id: string; code: number}>(table, undefined, 'code DESC');
 | 
			
		||||
 | 
			
		||||
            let newCode = 0;
 | 
			
		||||
            if (entries.length > 0) {
 | 
			
		||||
                newCode = entries[0].code + 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await this.appDB.insertRecord(table, { id: id, code: newCode });
 | 
			
		||||
            await db.insertRecord(table, { id: id, code: newCode });
 | 
			
		||||
            this.codes[key] = newCode;
 | 
			
		||||
 | 
			
		||||
            return newCode;
 | 
			
		||||
@ -324,10 +336,10 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
     * @return Promise resolved with a boolean indicating if promise is triggered (true) or not.
 | 
			
		||||
     */
 | 
			
		||||
    async isTriggered(notification: ILocalNotification, useQueue: boolean = true): Promise<boolean> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const stored = await this.appDB.getRecord<{ id: number; at: number }>(
 | 
			
		||||
            const stored = await db.getRecord<{ id: number; at: number }>(
 | 
			
		||||
                TRIGGERED_TABLE_NAME,
 | 
			
		||||
                { id: notification.id },
 | 
			
		||||
            );
 | 
			
		||||
@ -481,9 +493,9 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
     * @return Promise resolved when it is removed.
 | 
			
		||||
     */
 | 
			
		||||
    async removeTriggered(id: number): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        await this.appDB.deleteRecords(TRIGGERED_TABLE_NAME, { id: id });
 | 
			
		||||
        await db.deleteRecords(TRIGGERED_TABLE_NAME, { id: id });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -639,14 +651,13 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
     * @return Promise resolved when stored, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async trigger(notification: ILocalNotification): Promise<number> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const entry = {
 | 
			
		||||
            id: notification.id,
 | 
			
		||||
            at: notification.trigger && notification.trigger.at ? notification.trigger.at.getTime() : Date.now(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.appDB.insertRecord(TRIGGERED_TABLE_NAME, entry);
 | 
			
		||||
        return db.insertRecord(TRIGGERED_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -657,12 +668,12 @@ export class CoreLocalNotificationsProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async updateComponentName(oldName: string, newName: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        const oldId = COMPONENTS_TABLE_NAME + '#' + oldName;
 | 
			
		||||
        const newId = COMPONENTS_TABLE_NAME + '#' + newName;
 | 
			
		||||
 | 
			
		||||
        await this.appDB.updateRecords(COMPONENTS_TABLE_NAME, { id: newId }, { id: oldId });
 | 
			
		||||
        await db.updateRecords(COMPONENTS_TABLE_NAME, { id: newId }, { id: oldId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,7 @@ import {
 | 
			
		||||
    SiteDBEntry,
 | 
			
		||||
    CurrentSiteDBEntry,
 | 
			
		||||
    SchemaVersionsDBEntry,
 | 
			
		||||
} from '@services/db/sites';
 | 
			
		||||
} from '@services/database/sites';
 | 
			
		||||
import { CoreArray } from '../singletons/array';
 | 
			
		||||
 | 
			
		||||
export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS');
 | 
			
		||||
@ -61,25 +61,27 @@ export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS');
 | 
			
		||||
export class CoreSitesProvider {
 | 
			
		||||
 | 
			
		||||
    // Constants to validate a site version.
 | 
			
		||||
    protected readonly WORKPLACE_APP = 3;
 | 
			
		||||
    protected readonly MOODLE_APP = 2;
 | 
			
		||||
    protected readonly VALID_VERSION = 1;
 | 
			
		||||
    protected readonly INVALID_VERSION = -1;
 | 
			
		||||
    protected static readonly WORKPLACE_APP = 3;
 | 
			
		||||
    protected static readonly MOODLE_APP = 2;
 | 
			
		||||
    protected static readonly VALID_VERSION = 1;
 | 
			
		||||
    protected static readonly INVALID_VERSION = -1;
 | 
			
		||||
 | 
			
		||||
    protected isWPApp = false;
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected services = {};
 | 
			
		||||
    protected sessionRestored = false;
 | 
			
		||||
    protected currentSite?: CoreSite;
 | 
			
		||||
    protected sites: { [s: string]: CoreSite } = {};
 | 
			
		||||
    protected appDB: SQLiteDB;
 | 
			
		||||
    protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | 
			
		||||
    protected siteSchemasMigration: { [siteId: string]: Promise<void> } = {};
 | 
			
		||||
    protected siteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {};
 | 
			
		||||
    protected pluginsSiteSchemas: { [name: string]: CoreRegisteredSiteSchema } = {};
 | 
			
		||||
 | 
			
		||||
    // Variables for DB.
 | 
			
		||||
    protected appDB: Promise<SQLiteDB>;
 | 
			
		||||
    protected resolveAppDB!: (appDB: SQLiteDB) => void;
 | 
			
		||||
 | 
			
		||||
    constructor(@Optional() @Inject(CORE_SITE_SCHEMAS) siteSchemas: CoreSiteSchema[][] = []) {
 | 
			
		||||
        this.appDB = new Promise(resolve => this.resolveAppDB = resolve);
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreSitesProvider');
 | 
			
		||||
        this.siteSchemas = CoreArray.flatten(siteSchemas).reduce(
 | 
			
		||||
            (siteSchemas, schema) => {
 | 
			
		||||
@ -89,10 +91,19 @@ export class CoreSitesProvider {
 | 
			
		||||
            },
 | 
			
		||||
            this.siteSchemas,
 | 
			
		||||
        );
 | 
			
		||||
        this.appDB = CoreApp.instance.getDB();
 | 
			
		||||
        this.dbReady = CoreApp.instance.createTablesFromSchema(APP_SCHEMA).catch(() => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialise database.
 | 
			
		||||
     */
 | 
			
		||||
    async initialiseDatabase(): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await CoreApp.instance.createTablesFromSchema(APP_SCHEMA);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.resolveAppDB(CoreApp.instance.getDB());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -429,7 +440,7 @@ export class CoreSitesProvider {
 | 
			
		||||
            const info = await candidateSite.fetchSiteInfo();
 | 
			
		||||
 | 
			
		||||
            const result = this.isValidMoodleVersion(info);
 | 
			
		||||
            if (result != this.VALID_VERSION) {
 | 
			
		||||
            if (result != CoreSitesProvider.VALID_VERSION) {
 | 
			
		||||
                return this.treatInvalidAppVersion(result, siteUrl);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -491,7 +502,7 @@ export class CoreSitesProvider {
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Error invaliddevice is returned by Workplace server meaning the same as connecttoworkplaceapp.
 | 
			
		||||
            if (error && error.errorcode == 'invaliddevice') {
 | 
			
		||||
                return this.treatInvalidAppVersion(this.WORKPLACE_APP, siteUrl);
 | 
			
		||||
                return this.treatInvalidAppVersion(CoreSitesProvider.WORKPLACE_APP, siteUrl);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
@ -512,11 +523,11 @@ export class CoreSitesProvider {
 | 
			
		||||
        let translateParams;
 | 
			
		||||
 | 
			
		||||
        switch (result) {
 | 
			
		||||
            case this.MOODLE_APP:
 | 
			
		||||
            case CoreSitesProvider.MOODLE_APP:
 | 
			
		||||
                errorKey = 'core.login.connecttomoodleapp';
 | 
			
		||||
                errorCode = 'connecttomoodleapp';
 | 
			
		||||
                break;
 | 
			
		||||
            case this.WORKPLACE_APP:
 | 
			
		||||
            case CoreSitesProvider.WORKPLACE_APP:
 | 
			
		||||
                errorKey = 'core.login.connecttoworkplaceapp';
 | 
			
		||||
                errorCode = 'connecttoworkplaceapp';
 | 
			
		||||
                break;
 | 
			
		||||
@ -581,7 +592,7 @@ export class CoreSitesProvider {
 | 
			
		||||
     */
 | 
			
		||||
    protected isValidMoodleVersion(info: CoreSiteInfoResponse): number {
 | 
			
		||||
        if (!info) {
 | 
			
		||||
            return this.INVALID_VERSION;
 | 
			
		||||
            return CoreSitesProvider.INVALID_VERSION;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const version31 = 2016052300;
 | 
			
		||||
@ -606,7 +617,7 @@ export class CoreSitesProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Couldn't validate it.
 | 
			
		||||
        return this.INVALID_VERSION;
 | 
			
		||||
        return CoreSitesProvider.INVALID_VERSION;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -623,14 +634,14 @@ export class CoreSitesProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.isWPApp && isWorkplace) {
 | 
			
		||||
            return this.WORKPLACE_APP;
 | 
			
		||||
            return CoreSitesProvider.WORKPLACE_APP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.isWPApp && !isWorkplace) {
 | 
			
		||||
            return this.MOODLE_APP;
 | 
			
		||||
            return CoreSitesProvider.MOODLE_APP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.VALID_VERSION;
 | 
			
		||||
        return CoreSitesProvider.VALID_VERSION;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -669,8 +680,7 @@ export class CoreSitesProvider {
 | 
			
		||||
        config?: CoreSiteConfig,
 | 
			
		||||
        oauthId?: number,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const entry = {
 | 
			
		||||
            id,
 | 
			
		||||
            siteUrl,
 | 
			
		||||
@ -682,7 +692,7 @@ export class CoreSitesProvider {
 | 
			
		||||
            oauthId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await this.appDB.insertRecord(SITES_TABLE_NAME, entry);
 | 
			
		||||
        await db.insertRecord(SITES_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -893,8 +903,6 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise to be resolved when the site is deleted.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteSite(siteId: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Delete site ${siteId}`);
 | 
			
		||||
 | 
			
		||||
        if (typeof this.currentSite != 'undefined' && this.currentSite.id == siteId) {
 | 
			
		||||
@ -909,7 +917,9 @@ export class CoreSitesProvider {
 | 
			
		||||
        delete this.sites[siteId];
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.appDB.deleteRecords(SITES_TABLE_NAME, { id: siteId });
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
            await db.deleteRecords(SITES_TABLE_NAME, { id: siteId });
 | 
			
		||||
        } catch (err) {
 | 
			
		||||
            // DB remove shouldn't fail, but we'll go ahead even if it does.
 | 
			
		||||
        }
 | 
			
		||||
@ -926,9 +936,8 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved with true if there are sites and false if there aren't.
 | 
			
		||||
     */
 | 
			
		||||
    async hasSites(): Promise<boolean> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const count = await this.appDB.countRecords(SITES_TABLE_NAME);
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const count = await db.countRecords(SITES_TABLE_NAME);
 | 
			
		||||
 | 
			
		||||
        return count > 0;
 | 
			
		||||
    }
 | 
			
		||||
@ -940,8 +949,6 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved with the site.
 | 
			
		||||
     */
 | 
			
		||||
    async getSite(siteId?: string): Promise<CoreSite> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        if (!siteId) {
 | 
			
		||||
            if (this.currentSite) {
 | 
			
		||||
                return this.currentSite;
 | 
			
		||||
@ -954,7 +961,8 @@ export class CoreSitesProvider {
 | 
			
		||||
            return this.sites[siteId];
 | 
			
		||||
        } else {
 | 
			
		||||
            // Retrieve and create the site.
 | 
			
		||||
            const data = await this.appDB.getRecord<SiteDBEntry>(SITES_TABLE_NAME, { id: siteId });
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
            const data = await db.getRecord<SiteDBEntry>(SITES_TABLE_NAME, { id: siteId });
 | 
			
		||||
 | 
			
		||||
            return this.makeSiteFromSiteListEntry(data);
 | 
			
		||||
        }
 | 
			
		||||
@ -1025,9 +1033,8 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when the sites are retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const sites = await db.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
 | 
			
		||||
        const formattedSites: CoreSiteBasicInfo[] = [];
 | 
			
		||||
        sites.forEach((site) => {
 | 
			
		||||
@ -1089,9 +1096,8 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when the sites IDs are retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getLoggedInSitesIds(): Promise<string[]> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const sites = await this.appDB.getRecords<SiteDBEntry>(SITES_TABLE_NAME, { loggedOut : 0 });
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const sites = await db.getRecords<SiteDBEntry>(SITES_TABLE_NAME, { loggedOut : 0 });
 | 
			
		||||
 | 
			
		||||
        return sites.map((site) => site.id);
 | 
			
		||||
    }
 | 
			
		||||
@ -1102,9 +1108,8 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when the sites IDs are retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getSitesIds(): Promise<string[]> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const sites = await db.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
 | 
			
		||||
        return sites.map((site) => site.id);
 | 
			
		||||
    }
 | 
			
		||||
@ -1116,14 +1121,13 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when current site is stored.
 | 
			
		||||
     */
 | 
			
		||||
    async login(siteId: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const entry = {
 | 
			
		||||
            id: 1,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await this.appDB.insertRecord(CURRENT_SITE_TABLE_NAME, entry);
 | 
			
		||||
        await db.insertRecord(CURRENT_SITE_TABLE_NAME, entry);
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.LOGIN, {}, siteId);
 | 
			
		||||
    }
 | 
			
		||||
@ -1134,12 +1138,11 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when the user is logged out.
 | 
			
		||||
     */
 | 
			
		||||
    async logout(): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        let siteId: string | undefined;
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        if (this.currentSite) {
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
            const siteConfig = this.currentSite.getStoredConfig();
 | 
			
		||||
            siteId = this.currentSite.getId();
 | 
			
		||||
 | 
			
		||||
@ -1149,7 +1152,7 @@ export class CoreSitesProvider {
 | 
			
		||||
                promises.push(this.setSiteLoggedOut(siteId, true));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            promises.push(this.appDB.deleteRecords(CURRENT_SITE_TABLE_NAME, { id: 1 }));
 | 
			
		||||
            promises.push(db.deleteRecords(CURRENT_SITE_TABLE_NAME, { id: 1 }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@ -1169,12 +1172,12 @@ export class CoreSitesProvider {
 | 
			
		||||
            return Promise.reject(new CoreError('Session already restored.'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
        this.sessionRestored = true;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE_NAME, { id: 1 });
 | 
			
		||||
            const currentSite = await db.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE_NAME, { id: 1 });
 | 
			
		||||
            const siteId = currentSite.siteId;
 | 
			
		||||
            this.logger.debug(`Restore session in site ${siteId}`);
 | 
			
		||||
 | 
			
		||||
@ -1192,8 +1195,7 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async setSiteLoggedOut(siteId: string, loggedOut: boolean): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const site = await this.getSite(siteId);
 | 
			
		||||
        const newValues = {
 | 
			
		||||
            token: '', // Erase the token for security.
 | 
			
		||||
@ -1202,7 +1204,7 @@ export class CoreSitesProvider {
 | 
			
		||||
 | 
			
		||||
        site.setLoggedOut(loggedOut);
 | 
			
		||||
 | 
			
		||||
        await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
        await db.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1238,8 +1240,7 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return A promise resolved when the site is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async updateSiteTokenBySiteId(siteId: string, token: string, privateToken: string = ''): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const site = await this.getSite(siteId);
 | 
			
		||||
        const newValues = {
 | 
			
		||||
            token,
 | 
			
		||||
@ -1251,7 +1252,7 @@ export class CoreSitesProvider {
 | 
			
		||||
        site.privateToken = privateToken;
 | 
			
		||||
        site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore.
 | 
			
		||||
 | 
			
		||||
        await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
        await db.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1261,8 +1262,6 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return A promise resolved when the site is updated.
 | 
			
		||||
     */
 | 
			
		||||
    async updateSiteInfo(siteId: string): Promise<void> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const site = await this.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@ -1270,7 +1269,7 @@ export class CoreSitesProvider {
 | 
			
		||||
            site.setInfo(info);
 | 
			
		||||
 | 
			
		||||
            const versionCheck = this.isValidMoodleVersion(info);
 | 
			
		||||
            if (versionCheck != this.VALID_VERSION) {
 | 
			
		||||
            if (versionCheck != CoreSitesProvider.VALID_VERSION) {
 | 
			
		||||
                // The Moodle version is not supported, reject.
 | 
			
		||||
                return this.treatInvalidAppVersion(versionCheck, site.getURL(), site.getId());
 | 
			
		||||
            }
 | 
			
		||||
@ -1295,7 +1294,9 @@ export class CoreSitesProvider {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                await this.appDB.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
                const db = await this.appDB;
 | 
			
		||||
 | 
			
		||||
                await db.updateRecords(SITES_TABLE_NAME, newValues, { id: siteId });
 | 
			
		||||
            } finally {
 | 
			
		||||
                CoreEvents.trigger(CoreEvents.SITE_UPDATED, info, siteId);
 | 
			
		||||
            }
 | 
			
		||||
@ -1328,8 +1329,6 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved with the site IDs (array).
 | 
			
		||||
     */
 | 
			
		||||
    async getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string): Promise<string[]> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        // If prioritize is true, check current site first.
 | 
			
		||||
        if (prioritize && this.currentSite && this.currentSite.containsUrl(url)) {
 | 
			
		||||
            if (!username || this.currentSite?.getInfo()?.username == username) {
 | 
			
		||||
@ -1354,7 +1353,8 @@ export class CoreSitesProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const siteEntries = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
            const db = await this.appDB;
 | 
			
		||||
            const siteEntries = await db.getAllRecords<SiteDBEntry>(SITES_TABLE_NAME);
 | 
			
		||||
            const ids: string[] = [];
 | 
			
		||||
            const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
@ -1385,9 +1385,8 @@ export class CoreSitesProvider {
 | 
			
		||||
     * @return Promise resolved with the site ID.
 | 
			
		||||
     */
 | 
			
		||||
    async getStoredCurrentSiteId(): Promise<string> {
 | 
			
		||||
        await this.dbReady;
 | 
			
		||||
 | 
			
		||||
        const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE_NAME, { id: 1 });
 | 
			
		||||
        const db = await this.appDB;
 | 
			
		||||
        const currentSite = await db.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE_NAME, { id: 1 });
 | 
			
		||||
 | 
			
		||||
        return currentSite.siteId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { SYNC_TABLE_NAME, CoreSyncRecord } from '@services/db/sync';
 | 
			
		||||
import { SYNC_TABLE_NAME, CoreSyncRecord } from '@services/database/sync';
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Service that provides some features regarding synchronization.
 | 
			
		||||
 | 
			
		||||
@ -15,10 +15,9 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreConfig } from '@services/config';
 | 
			
		||||
import { CoreInitHandler, CoreInitDelegate } from '@services/init';
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
const VERSION_APPLIED = 'version_applied';
 | 
			
		||||
 | 
			
		||||
@ -28,12 +27,7 @@ const VERSION_APPLIED = 'version_applied';
 | 
			
		||||
 * This service handles processes that need to be run when updating the app, like migrate Ionic 1 database data to Ionic 3.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class CoreUpdateManagerProvider implements CoreInitHandler {
 | 
			
		||||
 | 
			
		||||
    // Data for init delegate.
 | 
			
		||||
    name = 'CoreUpdateManager';
 | 
			
		||||
    priority = CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 300;
 | 
			
		||||
    blocking = true;
 | 
			
		||||
export class CoreUpdateManagerProvider {
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,9 +26,7 @@ import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreWSError } from '@classes/errors/wserror';
 | 
			
		||||
import {
 | 
			
		||||
    makeSingleton, Clipboard, InAppBrowser, Platform, FileOpener, WebIntent, QRScanner, Translate,
 | 
			
		||||
} from '@singletons';
 | 
			
		||||
import { makeSingleton, Clipboard, InAppBrowser, FileOpener, WebIntent, QRScanner, Translate } from '@singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
 | 
			
		||||
type TreeNode<T> = T & { children: TreeNode<T>[] };
 | 
			
		||||
@ -48,9 +46,6 @@ export class CoreUtilsProvider {
 | 
			
		||||
 | 
			
		||||
    constructor(protected zone: NgZone) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreUtilsProvider');
 | 
			
		||||
 | 
			
		||||
        // eslint-disable-next-line promise/catch-or-return
 | 
			
		||||
        Platform.instance.ready().then(() => this.overrideWindowOpen());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1606,17 +1601,6 @@ export class CoreUtilsProvider {
 | 
			
		||||
        return new Promise(resolve => setTimeout(resolve, milliseconds));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Override native window.open with InAppBrowser if available.
 | 
			
		||||
     */
 | 
			
		||||
    private overrideWindowOpen() {
 | 
			
		||||
        if (!window.cordova?.InAppBrowser) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window.open = window.cordova.InAppBrowser.open;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class CoreUtils extends makeSingleton(CoreUtilsProvider) {}
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injector, NgZone as NgZoneService } from '@angular/core';
 | 
			
		||||
import { ApplicationRef, ApplicationInitStatus, Injector, NgZone as NgZoneService, Type } from '@angular/core';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
@ -52,9 +52,23 @@ import { Zip as ZipService } from '@ionic-native/zip/ngx';
 | 
			
		||||
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
import { CoreSingletonsFactory, CoreInjectionToken, CoreSingletonClass } from '@classes/singletons-factory';
 | 
			
		||||
/**
 | 
			
		||||
 * Injector instance used to resolve singletons.
 | 
			
		||||
 */
 | 
			
		||||
let singletonsInjector: Injector | null = null;
 | 
			
		||||
 | 
			
		||||
const factory = new CoreSingletonsFactory();
 | 
			
		||||
/**
 | 
			
		||||
 * Stub class used to type anonymous classes created in the makeSingleton method.
 | 
			
		||||
 */
 | 
			
		||||
class CoreSingleton {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Singleton class created using the factory.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreSingletonClass<Service> = typeof CoreSingleton & {
 | 
			
		||||
    instance: Service;
 | 
			
		||||
    setInstance(instance: Service): void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the injector that will be used to resolve instances in the singletons of this module.
 | 
			
		||||
@ -62,17 +76,38 @@ const factory = new CoreSingletonsFactory();
 | 
			
		||||
 * @param injector Module injector.
 | 
			
		||||
 */
 | 
			
		||||
export function setSingletonsInjector(injector: Injector): void {
 | 
			
		||||
    factory.setInjector(injector);
 | 
			
		||||
    singletonsInjector = injector;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Make a singleton for this module.
 | 
			
		||||
 * Make a singleton for the given injection token.
 | 
			
		||||
 *
 | 
			
		||||
 * @param injectionToken Injection token used to resolve the singleton instance. This is usually the service class if the
 | 
			
		||||
 * provider was defined using a class or the string used in the `provide` key if it was defined using an object.
 | 
			
		||||
 */
 | 
			
		||||
export function makeSingleton<Service>(injectionToken: CoreInjectionToken<Service>): CoreSingletonClass<Service> {
 | 
			
		||||
    return factory.makeSingleton(injectionToken);
 | 
			
		||||
export function makeSingleton<Service>(injectionToken: Type<Service> | Type<unknown> | string): CoreSingletonClass<Service> {
 | 
			
		||||
    return class {
 | 
			
		||||
 | 
			
		||||
        private static serviceInstance: Service;
 | 
			
		||||
 | 
			
		||||
        static get instance(): Service {
 | 
			
		||||
            // Initialize instances lazily.
 | 
			
		||||
            if (!this.serviceInstance) {
 | 
			
		||||
                if (!singletonsInjector) {
 | 
			
		||||
                    throw new Error('Can\'t resolve a singleton instance without an injector');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.serviceInstance = singletonsInjector.get(injectionToken);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this.serviceInstance;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static setInstance(instance: Service): void {
 | 
			
		||||
            this.serviceInstance = instance;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert ionic-native services to singleton.
 | 
			
		||||
@ -111,6 +146,8 @@ export class LoadingController extends makeSingleton(LoadingControllerService) {
 | 
			
		||||
export class ModalController extends makeSingleton(ModalControllerService) {}
 | 
			
		||||
export class ToastController extends makeSingleton(ToastControllerService) {}
 | 
			
		||||
export class GestureController extends makeSingleton(GestureControllerService) {}
 | 
			
		||||
export class ApplicationInit extends makeSingleton(ApplicationInitStatus) {}
 | 
			
		||||
export class Application extends makeSingleton(ApplicationRef) {}
 | 
			
		||||
 | 
			
		||||
// Convert external libraries injectables.
 | 
			
		||||
export class Translate extends makeSingleton(TranslateService) {}
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
import { Component, CUSTOM_ELEMENTS_SCHEMA, Type, ViewChild } from '@angular/core';
 | 
			
		||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { CoreSingletonClass } from '@classes/singletons-factory';
 | 
			
		||||
import { CoreSingletonClass } from '@singletons';
 | 
			
		||||
 | 
			
		||||
abstract class WrapperComponent<U> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,8 @@
 | 
			
		||||
      "cordova-plugin-inappbrowser",
 | 
			
		||||
      "cordova",
 | 
			
		||||
      "dom-mediacapture-record",
 | 
			
		||||
      "node"
 | 
			
		||||
      "node",
 | 
			
		||||
      "webpack-env"
 | 
			
		||||
    ],
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@classes/*": ["core/classes/*"],
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,8 @@
 | 
			
		||||
        "dom-mediacapture-record",
 | 
			
		||||
        "faker",
 | 
			
		||||
        "jest",
 | 
			
		||||
        "node"
 | 
			
		||||
        "node",
 | 
			
		||||
        "webpack-env"
 | 
			
		||||
    ],
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@classes/*": ["core/classes/*"],
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user