commit
						2d141bc104
					
				@ -14,7 +14,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
const minimatch = require('minimatch');
 | 
			
		||||
const { minimatch } = require('minimatch');
 | 
			
		||||
const { existsSync, readFileSync, writeFileSync, statSync, renameSync, rmSync } = require('fs');
 | 
			
		||||
const { readdir } = require('fs').promises;
 | 
			
		||||
const { mkdirSync, copySync } = require('fs-extra');
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injectable, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
 | 
			
		||||
import { Injectable, ViewContainerRef } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreFilterDefaultHandler } from '@features/filter/services/handlers/default-filter';
 | 
			
		||||
import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@features/filter/services/filter';
 | 
			
		||||
@ -32,10 +32,6 @@ export class AddonFilterDisplayH5PHandlerService extends CoreFilterDefaultHandle
 | 
			
		||||
 | 
			
		||||
    protected template = document.createElement('template'); // A template element to convert HTML to element.
 | 
			
		||||
 | 
			
		||||
    constructor(protected factoryResolver: ComponentFactoryResolver) {
 | 
			
		||||
        super();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
@ -95,8 +91,7 @@ export class AddonFilterDisplayH5PHandlerService extends CoreFilterDefaultHandle
 | 
			
		||||
            const url = placeholder.getAttribute('data-player-src') || '';
 | 
			
		||||
 | 
			
		||||
            // Create the component to display the player.
 | 
			
		||||
            const factory = this.factoryResolver.resolveComponentFactory(CoreH5PPlayerComponent);
 | 
			
		||||
            const componentRef = viewContainerRef.createComponent<CoreH5PPlayerComponent>(factory);
 | 
			
		||||
            const componentRef = viewContainerRef.createComponent<CoreH5PPlayerComponent>(CoreH5PPlayerComponent);
 | 
			
		||||
 | 
			
		||||
            componentRef.instance.src = url;
 | 
			
		||||
            componentRef.instance.component = component;
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { APP_INITIALIZER, COMPILER_OPTIONS, NgModule } from '@angular/core';
 | 
			
		||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
 | 
			
		||||
import { BrowserModule } from '@angular/platform-browser';
 | 
			
		||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 | 
			
		||||
import { RouteReuseStrategy } from '@angular/router';
 | 
			
		||||
@ -28,7 +28,7 @@ import { AddonsModule } from '@addons/addons.module';
 | 
			
		||||
 | 
			
		||||
import { AppComponent } from './app.component';
 | 
			
		||||
import { AppRoutingModule } from './app-routing.module';
 | 
			
		||||
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
 | 
			
		||||
 | 
			
		||||
import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CoreSiteInfoCronHandler } from '@services/handlers/site-info-cron';
 | 
			
		||||
import { moodleTransitionAnimation } from '@classes/page-transition';
 | 
			
		||||
@ -71,8 +71,6 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
 | 
			
		||||
        { provide: COMPILER_OPTIONS, useValue: {}, multi: true },
 | 
			
		||||
        { provide: JitCompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ import {
 | 
			
		||||
    OnChanges,
 | 
			
		||||
    DoCheck,
 | 
			
		||||
    ViewContainerRef,
 | 
			
		||||
    ComponentFactoryResolver,
 | 
			
		||||
    ComponentRef,
 | 
			
		||||
    KeyValueDiffers,
 | 
			
		||||
    SimpleChange,
 | 
			
		||||
@ -70,7 +69,8 @@ export class CoreDynamicComponent<ComponentClass> implements OnChanges, DoCheck
 | 
			
		||||
    @Input() data?: Record<string | number, unknown>;
 | 
			
		||||
 | 
			
		||||
    // Get the container where to put the dynamic component.
 | 
			
		||||
    @ViewChild('dynamicComponent', { read: ViewContainerRef }) set dynamicComponent(el: ViewContainerRef) {
 | 
			
		||||
    @ViewChild('dynamicComponent', { read: ViewContainerRef })
 | 
			
		||||
    set dynamicComponent(el: ViewContainerRef) {
 | 
			
		||||
        this.container = el;
 | 
			
		||||
 | 
			
		||||
        // Use a timeout to avoid ExpressionChangedAfterItHasBeenCheckedError.
 | 
			
		||||
@ -85,7 +85,6 @@ export class CoreDynamicComponent<ComponentClass> implements OnChanges, DoCheck
 | 
			
		||||
    protected lastComponent?: Type<unknown>;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        protected factoryResolver: ComponentFactoryResolver,
 | 
			
		||||
        differs: KeyValueDiffers,
 | 
			
		||||
        protected cdr: ChangeDetectorRef,
 | 
			
		||||
        protected element: ElementRef,
 | 
			
		||||
@ -96,7 +95,7 @@ export class CoreDynamicComponent<ComponentClass> implements OnChanges, DoCheck
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Detect changes on input properties.
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnChanges(changes: { [name: string]: SimpleChange }): void {
 | 
			
		||||
        if (changes.component && !this.component) {
 | 
			
		||||
@ -110,7 +109,7 @@ export class CoreDynamicComponent<ComponentClass> implements OnChanges, DoCheck
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngDoCheck(): void {
 | 
			
		||||
        if (this.instance) {
 | 
			
		||||
@ -172,8 +171,7 @@ export class CoreDynamicComponent<ComponentClass> implements OnChanges, DoCheck
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                // Create the component and add it to the container.
 | 
			
		||||
                const factory = this.factoryResolver.resolveComponentFactory(this.component);
 | 
			
		||||
                const componentRef = this.container.createComponent(factory);
 | 
			
		||||
                const componentRef = this.container.createComponent(this.component);
 | 
			
		||||
 | 
			
		||||
                this.instance = componentRef.instance;
 | 
			
		||||
            } catch (ex) {
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ import {
 | 
			
		||||
    ElementRef,
 | 
			
		||||
    ViewContainerRef,
 | 
			
		||||
    ViewChild,
 | 
			
		||||
    ComponentFactoryResolver,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
@ -78,7 +77,7 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy {
 | 
			
		||||
    protected mergedContextMenu?: CoreContextMenuComponent;
 | 
			
		||||
    protected createdMainContextMenuElement?: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    constructor(element: ElementRef, protected factoryResolver: ComponentFactoryResolver) {
 | 
			
		||||
    constructor(element: ElementRef) {
 | 
			
		||||
        this.element = element.nativeElement;
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreNavBarButtonsComponent');
 | 
			
		||||
 | 
			
		||||
@ -186,8 +185,7 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy {
 | 
			
		||||
     * @returns Created component.
 | 
			
		||||
     */
 | 
			
		||||
    protected createMainContextMenu(): CoreContextMenuComponent {
 | 
			
		||||
        const factory = this.factoryResolver.resolveComponentFactory(CoreContextMenuComponent);
 | 
			
		||||
        const componentRef = this.container.createComponent<CoreContextMenuComponent>(factory);
 | 
			
		||||
        const componentRef = this.container.createComponent(CoreContextMenuComponent);
 | 
			
		||||
 | 
			
		||||
        this.createdMainContextMenuElement = componentRef.location.nativeElement;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'core-compile-html',
 | 
			
		||||
    template: '<core-loading [hideUntil]="loaded"><ng-container #dynamicComponent></ng-container></core-loading>',
 | 
			
		||||
    template: '<core-loading [hideUntil]="loaded"><ng-container #dynamicComponent /></core-loading>',
 | 
			
		||||
    styles: [':host { display: contents; }'],
 | 
			
		||||
})
 | 
			
		||||
export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
@ -66,16 +66,16 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
    @Input() jsData?: Record<string, unknown>; // Data to pass to the fake component.
 | 
			
		||||
    @Input() extraImports: unknown[] = []; // Extra import modules.
 | 
			
		||||
    @Input() extraProviders: Type<unknown>[] = []; // Extra providers.
 | 
			
		||||
    @Input() forceCompile?: boolean; // Set it to true to force compile even if the text/javascript hasn't changed.
 | 
			
		||||
    @Input() forceCompile = false; // Set it to true to force compile even if the text/javascript hasn't changed.
 | 
			
		||||
    @Output() created = new EventEmitter<unknown>(); // Will emit an event when the component is instantiated.
 | 
			
		||||
    @Output() compiling = new EventEmitter<boolean>(); // Event that indicates whether the template is being compiled.
 | 
			
		||||
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    componentInstance?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
 | 
			
		||||
 | 
			
		||||
    // Get the container where to put the content.
 | 
			
		||||
    @ViewChild('dynamicComponent', { read: ViewContainerRef }) container?: ViewContainerRef;
 | 
			
		||||
 | 
			
		||||
    loaded?: boolean;
 | 
			
		||||
    componentInstance?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
 | 
			
		||||
 | 
			
		||||
    protected componentRef?: ComponentRef<unknown>;
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
    protected differ: KeyValueDiffer<unknown, unknown>; // To detect changes in the jsData input.
 | 
			
		||||
@ -114,6 +114,10 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnChanges(changes: Record<string, SimpleChange>): Promise<void> {
 | 
			
		||||
        if (!this.container) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Only compile if text/javascript has changed or the forceCompile flag has been set to true.
 | 
			
		||||
        if (this.text !== undefined && (changes.text || changes.javascript ||
 | 
			
		||||
                (changes.forceCompile && CoreUtils.isTrueOrOne(this.forceCompile)))) {
 | 
			
		||||
@ -124,16 +128,18 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                const componentClass = await this.getComponentClass();
 | 
			
		||||
                const factory = await CoreCompile.createAndCompileComponent(this.text, componentClass, this.extraImports);
 | 
			
		||||
 | 
			
		||||
                // Destroy previous components.
 | 
			
		||||
                this.componentRef?.destroy();
 | 
			
		||||
 | 
			
		||||
                if (factory) {
 | 
			
		||||
                // Create the component.
 | 
			
		||||
                    this.componentRef = this.container?.createComponent(factory);
 | 
			
		||||
                this.componentRef = await CoreCompile.createAndCompileComponent(
 | 
			
		||||
                    this.text,
 | 
			
		||||
                    componentClass,
 | 
			
		||||
                    this.container,
 | 
			
		||||
                    this.extraImports,
 | 
			
		||||
                );
 | 
			
		||||
                this.componentRef && this.created.emit(this.componentRef.instance);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.loaded = true;
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
@ -192,7 +198,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Component being initialized.
 | 
			
		||||
             * @inheritdoc
 | 
			
		||||
             */
 | 
			
		||||
            ngOnInit(): void {
 | 
			
		||||
                // If there is some javascript to run, do it now.
 | 
			
		||||
@ -204,7 +210,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
                for (const name in compileInstance.pendingCalls) {
 | 
			
		||||
                    const pendingCall = compileInstance.pendingCalls[name];
 | 
			
		||||
 | 
			
		||||
                    if (typeof this[name] == 'function') {
 | 
			
		||||
                    if (typeof this[name] === 'function') {
 | 
			
		||||
                        // Call the function.
 | 
			
		||||
                        Promise.resolve(this[name].apply(this, pendingCall.params)).then(pendingCall.defer.resolve)
 | 
			
		||||
                            .catch(pendingCall.defer.reject);
 | 
			
		||||
@ -218,21 +224,21 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Content has been initialized.
 | 
			
		||||
             * @inheritdoc
 | 
			
		||||
             */
 | 
			
		||||
            ngAfterContentInit(): void {
 | 
			
		||||
                this.callLifecycleHookOverride('ngAfterContentInit');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * View has been initialized.
 | 
			
		||||
             * @inheritdoc
 | 
			
		||||
             */
 | 
			
		||||
            ngAfterViewInit(): void {
 | 
			
		||||
                this.callLifecycleHookOverride('ngAfterViewInit');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Component destroyed.
 | 
			
		||||
             * @inheritdoc
 | 
			
		||||
             */
 | 
			
		||||
            ngOnDestroy(): void {
 | 
			
		||||
                this.callLifecycleHookOverride('ngOnDestroy');
 | 
			
		||||
@ -283,9 +289,9 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
 | 
			
		||||
     *                        once the component has been created.
 | 
			
		||||
     * @returns Result of the call. Undefined if no component instance or the function doesn't exist.
 | 
			
		||||
     */
 | 
			
		||||
    callComponentFunction(name: string, params?: unknown[], callWhenCreated: boolean = true): unknown {
 | 
			
		||||
    callComponentFunction(name: string, params?: unknown[], callWhenCreated = true): unknown {
 | 
			
		||||
        if (this.componentInstance) {
 | 
			
		||||
            if (typeof this.componentInstance[name] == 'function') {
 | 
			
		||||
            if (typeof this.componentInstance[name] === 'function') {
 | 
			
		||||
                return this.componentInstance[name].apply(this.componentInstance, params);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (callWhenCreated) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								src/core/features/compile/pipes/translate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/core/features/compile/pipes/translate.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 { Injectable, Pipe } from '@angular/core';
 | 
			
		||||
import { TranslatePipe } from '@ngx-translate/core';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Copy of translate pipe to use when compiling a dynamic component.
 | 
			
		||||
 * For some reason, when compiling a dynamic component the original translate pipe isn't found so we use this copy instead.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable()
 | 
			
		||||
@Pipe({
 | 
			
		||||
  name: 'translate',
 | 
			
		||||
  pure: false, // required to update the value when the promise is resolved
 | 
			
		||||
  standalone: true,
 | 
			
		||||
})
 | 
			
		||||
export class TranslatePipeForCompile extends TranslatePipe {}
 | 
			
		||||
@ -17,14 +17,13 @@ import {
 | 
			
		||||
    Injector,
 | 
			
		||||
    Component,
 | 
			
		||||
    NgModule,
 | 
			
		||||
    Compiler,
 | 
			
		||||
    ComponentFactory,
 | 
			
		||||
    ComponentRef,
 | 
			
		||||
    NgModuleRef,
 | 
			
		||||
    NO_ERRORS_SCHEMA,
 | 
			
		||||
    Type,
 | 
			
		||||
    Provider,
 | 
			
		||||
    createNgModule,
 | 
			
		||||
    ViewContainerRef,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { JitCompilerFactory } from '@angular/platform-browser-dynamic';
 | 
			
		||||
import {
 | 
			
		||||
    ActionSheetController,
 | 
			
		||||
    AlertController,
 | 
			
		||||
@ -34,6 +33,7 @@ import {
 | 
			
		||||
    ToastController,
 | 
			
		||||
} from '@ionic/angular';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { TranslatePipeForCompile } from '../pipes/translate';
 | 
			
		||||
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
@ -160,6 +160,8 @@ import { CorePromisedValue } from '@classes/promised-value';
 | 
			
		||||
import { CorePlatform } from '@services/platform';
 | 
			
		||||
import { CoreAutoLogoutService } from '@features/autologout/services/autologout';
 | 
			
		||||
 | 
			
		||||
import '@angular/compiler';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to provide functionalities regarding compiling dynamic HTML and Javascript.
 | 
			
		||||
 */
 | 
			
		||||
@ -167,7 +169,6 @@ import { CoreAutoLogoutService } from '@features/autologout/services/autologout'
 | 
			
		||||
export class CoreCompileProvider {
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected compiler: Compiler;
 | 
			
		||||
 | 
			
		||||
    // Other Ionic/Angular providers that don't depend on where they are injected.
 | 
			
		||||
    protected readonly OTHER_SERVICES: unknown[] = [
 | 
			
		||||
@ -186,10 +187,8 @@ export class CoreCompileProvider {
 | 
			
		||||
        getWorkshopComponentModules,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    constructor(protected injector: Injector, compilerFactory: JitCompilerFactory) {
 | 
			
		||||
    constructor(protected injector: Injector) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreCompileProvider');
 | 
			
		||||
 | 
			
		||||
        this.compiler = compilerFactory.createCompiler();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -197,14 +196,17 @@ export class CoreCompileProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @param template The template of the component.
 | 
			
		||||
     * @param componentClass The JS class of the component.
 | 
			
		||||
     * @param viewContainerRef View container reference to inject the component.
 | 
			
		||||
     * @param extraImports Extra imported modules if needed and not imported by this class.
 | 
			
		||||
     * @returns Promise resolved with the factory to instantiate the component.
 | 
			
		||||
     * @returns Promise resolved with the component reference.
 | 
			
		||||
     */
 | 
			
		||||
    async createAndCompileComponent<T = unknown>(
 | 
			
		||||
        template: string,
 | 
			
		||||
        componentClass: Type<T>,
 | 
			
		||||
        viewContainerRef: ViewContainerRef,
 | 
			
		||||
        extraImports: any[] = [], // eslint-disable-line @typescript-eslint/no-explicit-any
 | 
			
		||||
    ): Promise<ComponentFactory<T> | undefined> {
 | 
			
		||||
    ): Promise<ComponentRef<T> | undefined> {
 | 
			
		||||
 | 
			
		||||
        // Create the component using the template and the class.
 | 
			
		||||
        const component = Component({ template })(componentClass);
 | 
			
		||||
 | 
			
		||||
@ -213,17 +215,24 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...CoreArray.flatten(lazyImports),
 | 
			
		||||
            ...this.IMPORTS,
 | 
			
		||||
            ...extraImports,
 | 
			
		||||
            TranslatePipeForCompile,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Now create the module containing the component.
 | 
			
		||||
        const module = NgModule({ imports, declarations: [component], schemas: [NO_ERRORS_SCHEMA] })(class {});
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Compile the module and the component.
 | 
			
		||||
            const factories = await this.compiler.compileModuleAndAllComponentsAsync(module);
 | 
			
		||||
            viewContainerRef.clear();
 | 
			
		||||
 | 
			
		||||
            // Search and return the factory of the component we just created.
 | 
			
		||||
            return factories.componentFactories.find(factory => factory.componentType == component);
 | 
			
		||||
            // Now create the module containing the component.
 | 
			
		||||
            const ngModuleRef = createNgModule(
 | 
			
		||||
                NgModule({ imports, declarations: [component], schemas: [NO_ERRORS_SCHEMA] })(class {}),
 | 
			
		||||
                this.injector,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            return viewContainerRef.createComponent(
 | 
			
		||||
                component,
 | 
			
		||||
                {
 | 
			
		||||
                    environmentInjector: ngModuleRef,
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            this.logger.error('Error compiling template', template);
 | 
			
		||||
            this.logger.error(error);
 | 
			
		||||
@ -331,10 +340,10 @@ export class CoreCompileProvider {
 | 
			
		||||
        // We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
 | 
			
		||||
        for (const i in providers) {
 | 
			
		||||
            const providerDef = providers[i];
 | 
			
		||||
            if (typeof providerDef == 'function' && providerDef.name) {
 | 
			
		||||
            if (typeof providerDef === 'function' && providerDef.name) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Inject the provider to the instance. We use the class name as the property name.
 | 
			
		||||
                    instance[providerDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get(providerDef);
 | 
			
		||||
                    instance[providerDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get<Provider>(providerDef);
 | 
			
		||||
                } catch (ex) {
 | 
			
		||||
                    this.logger.error('Error injecting provider', providerDef.name, ex);
 | 
			
		||||
                }
 | 
			
		||||
@ -407,29 +416,6 @@ export class CoreCompileProvider {
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantiate a dynamic component.
 | 
			
		||||
     *
 | 
			
		||||
     * @param template The template of the component.
 | 
			
		||||
     * @param componentClass The JS class of the component.
 | 
			
		||||
     * @param injector The injector to use. It's recommended to pass it so NavController and similar can be injected.
 | 
			
		||||
     * @returns Promise resolved with the component instance.
 | 
			
		||||
     */
 | 
			
		||||
    async instantiateDynamicComponent<T = unknown>(
 | 
			
		||||
        template: string,
 | 
			
		||||
        componentClass: Type<T>,
 | 
			
		||||
        injector?: Injector,
 | 
			
		||||
    ): Promise<ComponentRef<T> | undefined> {
 | 
			
		||||
        injector = injector || this.injector;
 | 
			
		||||
 | 
			
		||||
        const factory = await this.createAndCompileComponent(template, componentClass);
 | 
			
		||||
 | 
			
		||||
        if (factory) {
 | 
			
		||||
            // Create and return the component.
 | 
			
		||||
            return factory.create(injector, undefined, undefined, injector.get(NgModuleRef));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const CoreCompile = makeSingleton(CoreCompileProvider);
 | 
			
		||||
 | 
			
		||||
@ -48,18 +48,18 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
 | 
			
		||||
    // Get the compile element. Don't set the right type to prevent circular dependencies.
 | 
			
		||||
    @ViewChild('compile') compileComponent?: CoreCompileHtmlComponent;
 | 
			
		||||
 | 
			
		||||
    @HostBinding('class') @Input() component!: string;
 | 
			
		||||
    @HostBinding('class') @Input() component = '';
 | 
			
		||||
    @Input() method!: string;
 | 
			
		||||
    @Input() args?: Record<string, unknown>;
 | 
			
		||||
    @Input() initResult?: CoreSitePluginsContent | null; // Result of the init WS call of the handler.
 | 
			
		||||
    @Input() data?: Record<string, unknown>; // Data to pass to the component.
 | 
			
		||||
    @Input() data: Record<string, unknown> = {}; // Data to pass to the component.
 | 
			
		||||
    @Input() preSets?: CoreSiteWSPreSets; // The preSets for the WS call.
 | 
			
		||||
    @Input() pageTitle?: string; // Current page title. It can be used by the "new-content" directives.
 | 
			
		||||
    @Output() onContentLoaded = new EventEmitter<CoreSitePluginsPluginContentLoadedData>(); // Emits event when content is loaded.
 | 
			
		||||
    @Output() onLoadingContent = new EventEmitter<boolean>(); // Emits an event when starts to load the content.
 | 
			
		||||
 | 
			
		||||
    content?: string; // Content.
 | 
			
		||||
    javascript?: string; // Javascript to execute.
 | 
			
		||||
    content = ''; // Content.
 | 
			
		||||
    javascript = ''; // Javascript to execute.
 | 
			
		||||
    otherData?: Record<string, unknown>; // Other data of the content.
 | 
			
		||||
    dataLoaded = false;
 | 
			
		||||
    invalidateObservable = new Subject<void>(); // An observable to notify observers when to invalidate data.
 | 
			
		||||
@ -120,13 +120,27 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
 | 
			
		||||
            this.jsData = Object.assign(this.data, CoreSitePlugins.createDataForJS(this.initResult, result));
 | 
			
		||||
 | 
			
		||||
            // Pass some methods as jsData so they can be called from the template too.
 | 
			
		||||
            this.jsData.fetchContent = refresh => this.fetchContent(refresh);
 | 
			
		||||
            this.jsData.openContent = (title, args, component, method, jsData, preSets, ptrEnabled) =>
 | 
			
		||||
                this.openContent(title, args, component, method, jsData, preSets, ptrEnabled);
 | 
			
		||||
            this.jsData.refreshContent = showSpinner => this.refreshContent(showSpinner);
 | 
			
		||||
            this.jsData.updateContent = (args, component, method, jsData, preSets) =>
 | 
			
		||||
                this.updateContent(args, component, method, jsData, preSets);
 | 
			
		||||
            this.jsData.updateModuleCourseContent = (cmId, alreadyFetched) => this.updateModuleCourseContent(cmId, alreadyFetched);
 | 
			
		||||
            this.jsData.fetchContent = (refresh?: boolean) => this.fetchContent(refresh);
 | 
			
		||||
            this.jsData.openContent = (
 | 
			
		||||
                title: string,
 | 
			
		||||
                args?: Record<string, unknown>,
 | 
			
		||||
                component?: string,
 | 
			
		||||
                method?: string,
 | 
			
		||||
                jsData?: Record<string, unknown> | boolean,
 | 
			
		||||
                preSets?: CoreSiteWSPreSets,
 | 
			
		||||
                ptrEnabled?: boolean,
 | 
			
		||||
            ) => this.openContent(title, args, component, method, jsData, preSets, ptrEnabled);
 | 
			
		||||
            this.jsData.refreshContent = (showSpinner?: boolean) => this.refreshContent(showSpinner);
 | 
			
		||||
            this.jsData.updateContent = (
 | 
			
		||||
                args?: Record<string, unknown>,
 | 
			
		||||
                component?: string,
 | 
			
		||||
                method?: string,
 | 
			
		||||
                jsData?: Record<string, unknown>,
 | 
			
		||||
                preSets?: CoreSiteWSPreSets,
 | 
			
		||||
            ) => this.updateContent(args, component, method, jsData, preSets);
 | 
			
		||||
            this.jsData.updateModuleCourseContent = (cmId: number, alreadyFetched?: boolean) =>
 | 
			
		||||
                this.updateModuleCourseContent(cmId, alreadyFetched);
 | 
			
		||||
            this.jsData.updateCachedContent = () => this.updateCachedContent();
 | 
			
		||||
 | 
			
		||||
            this.onContentLoaded.emit({ refresh: !!refresh, success: true });
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
@ -154,7 +168,7 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
 | 
			
		||||
     */
 | 
			
		||||
    openContent(
 | 
			
		||||
        title: string,
 | 
			
		||||
        args?: Record<string, unknown>,
 | 
			
		||||
        args: Record<string, unknown> = {},
 | 
			
		||||
        component?: string,
 | 
			
		||||
        method?: string,
 | 
			
		||||
        jsData?: Record<string, unknown> | boolean,
 | 
			
		||||
@ -167,7 +181,6 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
 | 
			
		||||
 | 
			
		||||
        component = component || this.component;
 | 
			
		||||
        method = method || this.method;
 | 
			
		||||
        args = args || {};
 | 
			
		||||
        const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));
 | 
			
		||||
 | 
			
		||||
        CoreNavigator.navigateToSitePath(`siteplugins/content/${component}/${method}/${hash}`, {
 | 
			
		||||
@ -187,7 +200,7 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
 | 
			
		||||
     *
 | 
			
		||||
     * @param showSpinner Whether to show spinner while refreshing.
 | 
			
		||||
     */
 | 
			
		||||
    async refreshContent(showSpinner: boolean = true): Promise<void> {
 | 
			
		||||
    async refreshContent(showSpinner = true): Promise<void> {
 | 
			
		||||
        if (showSpinner) {
 | 
			
		||||
            this.dataLoaded = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -16,10 +16,10 @@ import {
 | 
			
		||||
    AbstractType,
 | 
			
		||||
    ApplicationInitStatus,
 | 
			
		||||
    ApplicationRef,
 | 
			
		||||
    ComponentFactoryResolver as ComponentFactoryResolverService,
 | 
			
		||||
    Injector,
 | 
			
		||||
    NgZone as NgZoneService,
 | 
			
		||||
    Type,
 | 
			
		||||
    EnvironmentInjector,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { Router as RouterService } from '@angular/router';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
@ -204,7 +204,6 @@ export const Http = makeSingleton(HttpClient);
 | 
			
		||||
export const ActionSheetController = makeSingleton(ActionSheetControllerService);
 | 
			
		||||
export const AngularDelegate = makeSingleton(AngularDelegateService);
 | 
			
		||||
export const AlertController = makeSingleton(AlertControllerService);
 | 
			
		||||
export const ComponentFactoryResolver = makeSingleton(ComponentFactoryResolverService);
 | 
			
		||||
export const LoadingController = makeSingleton(LoadingControllerService);
 | 
			
		||||
export const ModalController = makeSingleton(ModalControllerService);
 | 
			
		||||
export const PopoverController = makeSingleton(PopoverControllerService);
 | 
			
		||||
@ -226,5 +225,5 @@ export const Translate: Omit<CoreSingletonProxy<TranslateService>, 'instant'> &
 | 
			
		||||
export const AngularFrameworkDelegate = asyncInstance(async () => {
 | 
			
		||||
    const injector = await singletonsInjector;
 | 
			
		||||
 | 
			
		||||
    return AngularDelegate.create(ComponentFactoryResolver.instance, injector);
 | 
			
		||||
    return AngularDelegate.create(injector.get(EnvironmentInjector), injector);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								src/types/angular.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/types/angular.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
// (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 { UrlTree } from '@angular/router';
 | 
			
		||||
import { NavigationOptions } from '@ionic/angular/common/providers/nav-controller';
 | 
			
		||||
 | 
			
		||||
declare module '@ionic/angular' {
 | 
			
		||||
 | 
			
		||||
    export interface NavController {
 | 
			
		||||
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        navigateForward(url: string | UrlTree | any[], options?: NavigationOptions): Promise<boolean | null>;
 | 
			
		||||
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 
			
		||||
        navigateRoot(url: string | UrlTree | any[], options?: NavigationOptions): Promise<boolean | null>;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user