From 8e87a1ade9dfdfb027254ab8d2c044a553f3a985 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 21 Feb 2018 16:18:03 +0100 Subject: [PATCH] MOBILE-2333 siteaddons: Run bootstrap JS and pass result to content JS --- src/app/app.module.ts | 2 + src/components/compile-html/compile-html.ts | 200 ------------------ src/core/compile/compile.module.ts | 27 +++ .../compile-html/compile-html.module.ts | 2 +- .../components/compile-html/compile-html.ts | 150 +++++++++++++ src/core/compile/providers/compile.ts | 142 +++++++++++++ .../addon-content/addon-content.html | 2 +- .../components/addon-content/addon-content.ts | 1 + .../components/components.module.ts | 4 +- .../components/module-index/module-index.html | 2 +- .../components/module-index/module-index.ts | 4 +- .../directives/call-ws-new-content.ts | 3 +- src/core/siteaddons/directives/new-content.ts | 3 +- .../pages/addon-page/addon-page.html | 2 +- .../siteaddons/pages/addon-page/addon-page.ts | 2 + src/core/siteaddons/providers/helper.ts | 150 ++++++++----- src/core/siteaddons/providers/siteaddons.ts | 52 +++-- src/core/siteaddons/siteaddons.module.ts | 1 - src/providers/addonmanager.ts | 18 +- 19 files changed, 488 insertions(+), 279 deletions(-) delete mode 100644 src/components/compile-html/compile-html.ts create mode 100644 src/core/compile/compile.module.ts rename src/{ => core/compile}/components/compile-html/compile-html.module.ts (95%) create mode 100644 src/core/compile/components/compile-html/compile-html.ts create mode 100644 src/core/compile/providers/compile.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dae354861..e2efc6119 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -67,6 +67,7 @@ import { CoreUserModule } from '@core/user/user.module'; import { CoreGradesModule } from '@core/grades/grades.module'; import { CoreSettingsModule } from '@core/settings/settings.module'; import { CoreSiteAddonsModule } from '@core/siteaddons/siteaddons.module'; +import { CoreCompileModule } from '@core/compile/compile.module'; // Addon modules. import { AddonCalendarModule } from '@addon/calendar/calendar.module'; @@ -146,6 +147,7 @@ export const CORE_PROVIDERS: any[] = [ CoreGradesModule, CoreSettingsModule, CoreSiteAddonsModule, + CoreCompileModule, AddonCalendarModule, AddonUserProfileFieldModule, AddonFilesModule, diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts deleted file mode 100644 index 14d3fc082..000000000 --- a/src/components/compile-html/compile-html.ts +++ /dev/null @@ -1,200 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// 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 { - Component, NgModule, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector, - SimpleChange, ChangeDetectorRef -} from '@angular/core'; -import { - IonicModule, NavController, Platform, ActionSheetController, AlertController, LoadingController, ModalController, - PopoverController, ToastController -} from 'ionic-angular'; -import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { CoreLoggerProvider } from '../../providers/logger'; - -// Import all modules that define components, directives and pipes. -import { CoreComponentsModule } from '../components.module'; -import { CoreDirectivesModule } from '../../directives/directives.module'; -import { CorePipesModule } from '../../pipes/pipes.module'; -import { CoreCourseComponentsModule } from '../../core/course/components/components.module'; -import { CoreCourseDirectivesModule } from '../../core/course/directives/directives.module'; -import { CoreCoursesComponentsModule } from '../../core/courses/components/components.module'; -import { CoreSiteAddonsDirectivesModule } from '../../core/siteaddons/directives/directives.module'; -import { CoreSiteHomeComponentsModule } from '../../core/sitehome/components/components.module'; -import { CoreUserComponentsModule } from '../../core/user/components/components.module'; - -// Import core providers. -import { CORE_PROVIDERS } from '../../app/app.module'; -import { CORE_CONTENTLINKS_PROVIDERS } from '../../core/contentlinks/contentlinks.module'; -import { CORE_COURSE_PROVIDERS } from '../../core/course/course.module'; -import { CORE_COURSES_PROVIDERS } from '../../core/courses/courses.module'; -import { CORE_FILEUPLOADER_PROVIDERS } from '../../core/fileuploader/fileuploader.module'; -import { CORE_GRADES_PROVIDERS } from '../../core/grades/grades.module'; -import { CORE_LOGIN_PROVIDERS } from '../../core/login/login.module'; -import { CORE_MAINMENU_PROVIDERS } from '../../core/mainmenu/mainmenu.module'; -import { CORE_SHAREDFILES_PROVIDERS } from '../../core/sharedfiles/sharedfiles.module'; -import { CORE_SITEADDONS_PROVIDERS } from '../../core/siteaddons/siteaddons.module'; -import { CORE_SITEHOME_PROVIDERS } from '../../core/sitehome/sitehome.module'; -import { CORE_USER_PROVIDERS } from '../../core/user/user.module'; -import { IONIC_NATIVE_PROVIDERS } from '../../core/emulator/emulator.module'; - -// Import other libraries and providers. -import { DomSanitizer } from '@angular/platform-browser'; -import { FormBuilder, Validators } from '@angular/forms'; -import { Http } from '@angular/http'; -import { HttpClient } from '@angular/common/http'; -import { CoreConfigConstants } from '../../configconstants'; -import { CoreConstants } from '../../core/constants'; -import * as moment from 'moment'; -import { Md5 } from 'ts-md5/dist/md5'; - -/** - * This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its - * components and directives are instantiated. - * - * IMPORTANT: Use this component only if it is a must. It will create and compile a new component and module everytime this - * component is used, so it can slow down the app. - * - * This component isn't part of CoreComponentsModule to prevent circular dependencies. If you want to use it, - * you need to import CoreCompileHtmlComponentsModule. - * - * You can provide some Javascript code (as text) to be executed inside the component. The context of the javascript code (this) - * will be the component instance created to compile the template. This means your javascript code can interact with the template. - * The component instance will have most of the providers so you can use them in the javascript code. E.g. if you want to use - * CoreAppProvider, you can do it with "this.CoreAppProvider". - */ -@Component({ - selector: 'core-compile-html', - template: '' -}) -export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { - // List of imports for dynamic module. Since the template can have any component we need to import all core components modules. - protected IMPORTS = [ - IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule, - CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule, - CoreCourseDirectivesModule, CoreSiteAddonsDirectivesModule - ]; - - // Other Ionic/Angular providers that don't depend on where they are injected. - protected OTHER_PROVIDERS = [ - TranslateService, Http, HttpClient, Platform, DomSanitizer, ActionSheetController, AlertController, LoadingController, - ModalController, PopoverController, ToastController, FormBuilder - ]; - - @Input() text: string; // The HTML text to display. - @Input() javascript: string; // The javascript to execute in the component. - - // Get the container where to put the content. - @ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef; - - protected componentRef: ComponentRef; - protected logger; - - constructor(logger: CoreLoggerProvider, protected compiler: Compiler, protected injector: Injector, - protected cdr: ChangeDetectorRef, protected navCtrl: NavController) { - this.logger = logger.getInstance('CoreCompileHtmlComponent'); - } - - /** - * Detect changes on input properties. - */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if ((changes.text || changes.javascript) && this.text) { - // Create a new component and a new module. - const component = this.createComponent(), - module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {}); - - // Compile the module and the component. - this.compiler.compileModuleAndAllComponentsAsync(module).then((factories) => { - // Search the factory of the component we just created. - let componentFactory; - for (const i in factories.componentFactories) { - const factory = factories.componentFactories[i]; - if (factory.componentType == component) { - componentFactory = factory; - break; - } - } - - // Destroy previous components. - this.componentRef && this.componentRef.destroy(); - - // Create the component. - this.componentRef = this.container.createComponent(componentFactory); - }); - } - } - - /** - * Component destroyed. - */ - ngOnDestroy(): void { - this.componentRef && this.componentRef.destroy(); - } - - /** - * Create a dynamic component to compile the HTML and run the javascript. - * - * @return {any} The component class. - */ - protected createComponent(): any { - // tslint:disable: no-this-assignment - const compileInstance = this, - providers = ( CORE_PROVIDERS).concat(CORE_CONTENTLINKS_PROVIDERS).concat(CORE_COURSE_PROVIDERS) - .concat(CORE_COURSES_PROVIDERS).concat(CORE_FILEUPLOADER_PROVIDERS).concat(CORE_GRADES_PROVIDERS) - .concat(CORE_LOGIN_PROVIDERS).concat(CORE_MAINMENU_PROVIDERS).concat(CORE_SHAREDFILES_PROVIDERS) - .concat(CORE_SITEHOME_PROVIDERS).concat(CORE_SITEADDONS_PROVIDERS).concat(CORE_USER_PROVIDERS) - .concat(IONIC_NATIVE_PROVIDERS).concat(this.OTHER_PROVIDERS); - - // Create the component, using the text as the template. - return Component({ - template: this.text - }) - (class CoreCompileHtmlFakeComponent implements OnInit { - - constructor() { - // 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) { - try { - // Inject the provider to the instance. We use the class name as the property name. - this[providerDef.name] = compileInstance.injector.get(providerDef); - } catch (ex) { - compileInstance.logger.warn('Error injecting provider', providerDef.name, ex); - } - } - } - - // Add some final components and providers. - this['ChangeDetectorRef'] = compileInstance.cdr; - this['NavController'] = compileInstance.navCtrl; - this['Validators'] = Validators; - this['CoreConfigConstants'] = CoreConfigConstants; - this['CoreConstants'] = CoreConstants; - this['moment'] = moment; - this['Md5'] = Md5; - this['componentContainer'] = compileInstance.container; - } - - ngOnInit(): void { - // If there is some javascript to run, do it now. - if (compileInstance.javascript) { - // tslint:disable: no-eval - eval(compileInstance.javascript); - } - } - }); - } -} diff --git a/src/core/compile/compile.module.ts b/src/core/compile/compile.module.ts new file mode 100644 index 000000000..610277494 --- /dev/null +++ b/src/core/compile/compile.module.ts @@ -0,0 +1,27 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { NgModule } from '@angular/core'; +import { CoreCompileProvider } from './providers/compile'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + CoreCompileProvider + ] +}) +export class CoreCompileModule { } diff --git a/src/components/compile-html/compile-html.module.ts b/src/core/compile/components/compile-html/compile-html.module.ts similarity index 95% rename from src/components/compile-html/compile-html.module.ts rename to src/core/compile/components/compile-html/compile-html.module.ts index 2bd45fc28..84616b2fa 100644 --- a/src/components/compile-html/compile-html.module.ts +++ b/src/core/compile/components/compile-html/compile-html.module.ts @@ -27,4 +27,4 @@ import { CoreCompileHtmlComponent } from './compile-html'; CoreCompileHtmlComponent ] }) -export class CoreCompileHtmlComponentsModule {} +export class CoreCompileHtmlComponentModule {} diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts new file mode 100644 index 000000000..8253e2a92 --- /dev/null +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -0,0 +1,150 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { + Component, NgModule, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, + SimpleChange, ChangeDetectorRef +} from '@angular/core'; +import { IonicModule, NavController } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreCompileProvider } from '../../../compile/providers/compile'; + +// Import all modules that define components, directives and pipes. +import { CoreComponentsModule } from '../../../../components/components.module'; +import { CoreDirectivesModule } from '../../../../directives/directives.module'; +import { CorePipesModule } from '../../../../pipes/pipes.module'; +import { CoreCourseComponentsModule } from '../../../course/components/components.module'; +import { CoreCourseDirectivesModule } from '../../../course/directives/directives.module'; +import { CoreCoursesComponentsModule } from '../../../courses/components/components.module'; +import { CoreSiteAddonsDirectivesModule } from '../../../siteaddons/directives/directives.module'; +import { CoreSiteHomeComponentsModule } from '../../../sitehome/components/components.module'; +import { CoreUserComponentsModule } from '../../../user/components/components.module'; + +/** + * This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its + * components and directives are instantiated. + * + * IMPORTANT: Use this component only if it is a must. It will create and compile a new component and module everytime this + * component is used, so it can slow down the app. + * + * This component has its own module to prevent circular dependencies. If you want to use it, + * you need to import CoreCompileHtmlComponentModule. + * + * You can provide some Javascript code (as text) to be executed inside the component. The context of the javascript code (this) + * will be the component instance created to compile the template. This means your javascript code can interact with the template. + * The component instance will have most of the providers so you can use them in the javascript code. E.g. if you want to use + * CoreAppProvider, you can do it with "this.CoreAppProvider". + */ +@Component({ + selector: 'core-compile-html', + template: '' +}) +export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { + // List of imports for dynamic module. Since the template can have any component we need to import all core components modules. + protected IMPORTS = [ + IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule, + CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule, + CoreCourseDirectivesModule, CoreSiteAddonsDirectivesModule + ]; + + @Input() text: string; // The HTML text to display. + @Input() javascript: string; // The Javascript to execute in the component. + @Input() jsData; // Data to pass to the fake component. + + // Get the container where to put the content. + @ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef; + + protected componentRef: ComponentRef; + + constructor(protected compileProvider: CoreCompileProvider, protected compiler: Compiler, + protected cdr: ChangeDetectorRef, protected navCtrl: NavController) { } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if ((changes.text || changes.javascript) && this.text) { + // Create a new component and a new module. + const component = this.createComponent(), + module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {}); + + // Compile the module and the component. + this.compiler.compileModuleAndAllComponentsAsync(module).then((factories) => { + // Search the factory of the component we just created. + let componentFactory; + for (const i in factories.componentFactories) { + const factory = factories.componentFactories[i]; + if (factory.componentType == component) { + componentFactory = factory; + break; + } + } + + // Destroy previous components. + this.componentRef && this.componentRef.destroy(); + + // Create the component. + this.componentRef = this.container.createComponent(componentFactory); + }); + } + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + this.componentRef && this.componentRef.destroy(); + } + + /** + * Create a dynamic component to compile the HTML and run the javascript. + * + * @return {any} The component class. + */ + protected createComponent(): any { + // tslint:disable: no-this-assignment + const compileInstance = this; + + // Create the component, using the text as the template. + return Component({ + template: this.text + }) + (class CoreCompileHtmlFakeComponent implements OnInit { + + constructor() { + // If there is some javascript to run, prepare the instance. + if (compileInstance.javascript) { + compileInstance.compileProvider.injectLibraries(this); + + // Add some more components and classes. + this['ChangeDetectorRef'] = compileInstance.cdr; + this['NavController'] = compileInstance.navCtrl; + this['componentContainer'] = compileInstance.container; + + // Add the data passed to the component. + for (const name in compileInstance.jsData) { + this[name] = compileInstance.jsData[name]; + } + } + } + + ngOnInit(): void { + // If there is some javascript to run, do it now. + if (compileInstance.javascript) { + compileInstance.compileProvider.executeJavascript(this, compileInstance.javascript); + } + } + }); + } +} diff --git a/src/core/compile/providers/compile.ts b/src/core/compile/providers/compile.ts new file mode 100644 index 000000000..25738aa15 --- /dev/null +++ b/src/core/compile/providers/compile.ts @@ -0,0 +1,142 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, Injector } from '@angular/core'; +import { + Platform, ActionSheetController, AlertController, LoadingController, ModalController, PopoverController, ToastController +} from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreLoggerProvider } from '../../../providers/logger'; + +// Import core providers. +import { CORE_PROVIDERS } from '../../../app/app.module'; +import { CORE_CONTENTLINKS_PROVIDERS } from '../../contentlinks/contentlinks.module'; +import { CORE_COURSE_PROVIDERS } from '../../course/course.module'; +import { CORE_COURSES_PROVIDERS } from '../../courses/courses.module'; +import { CORE_FILEUPLOADER_PROVIDERS } from '../../fileuploader/fileuploader.module'; +import { CORE_GRADES_PROVIDERS } from '../../grades/grades.module'; +import { CORE_LOGIN_PROVIDERS } from '../../login/login.module'; +import { CORE_MAINMENU_PROVIDERS } from '../../mainmenu/mainmenu.module'; +import { CORE_SHAREDFILES_PROVIDERS } from '../../sharedfiles/sharedfiles.module'; +import { CORE_SITEHOME_PROVIDERS } from '../../sitehome/sitehome.module'; +import { CORE_USER_PROVIDERS } from '../../user/user.module'; +import { IONIC_NATIVE_PROVIDERS } from '../../emulator/emulator.module'; + +// Import only this provider to prevent circular dependencies. +import { CoreSiteAddonsProvider } from '../../siteaddons/providers/siteaddons'; + +// Import other libraries and providers. +import { DomSanitizer } from '@angular/platform-browser'; +import { FormBuilder, Validators } from '@angular/forms'; +import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; +import { CoreConfigConstants } from '../../../configconstants'; +import { CoreConstants } from '../../constants'; +import * as moment from 'moment'; +import { Md5 } from 'ts-md5/dist/md5'; + +// Import core classes that can be useful for site addons. +import { CoreSyncBaseProvider } from '../../../classes/base-sync'; +import { CoreCache } from '../../../classes/cache'; +import { CoreDelegate } from '../../../classes/delegate'; +import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; +import { CoreContentLinksModuleGradeHandler } from '../../contentlinks/classes/module-grade-handler'; +import { CoreContentLinksModuleIndexHandler } from '../../contentlinks/classes/module-index-handler'; +import { CoreCourseModulePrefetchHandlerBase } from '../../course/classes/module-prefetch-handler'; + +/** + * Service to provide functionalities regarding compiling dynamic HTML and Javascript. + */ +@Injectable() +export class CoreCompileProvider { + + protected logger; + + // Other Ionic/Angular providers that don't depend on where they are injected. + protected OTHER_PROVIDERS = [ + TranslateService, Http, HttpClient, Platform, DomSanitizer, ActionSheetController, AlertController, LoadingController, + ModalController, PopoverController, ToastController, FormBuilder + ]; + + constructor(protected injector: Injector, logger: CoreLoggerProvider) { + this.logger = logger.getInstance('CoreCompileProvider'); + } + + /** + * Eval some javascript using the context of the function. + * + * @param {string} javascript The javascript to eval. + * @return {any} Result of the eval. + */ + protected evalInContext(javascript: string): any { + // tslint:disable: no-eval + return eval(javascript); + } + + /** + * Execute some javascript code, using a certain instance as the context. + * + * @param {any} instance Instance to use as the context. In the JS code, "this" will be this instance. + * @param {string} javascript The javascript code to eval. + * @return {any} Result of the javascript execution. + */ + executeJavascript(instance: any, javascript: string): any { + try { + return this.evalInContext.call(instance, javascript); + } catch (ex) { + this.logger.error('Error evaluating javascript', ex); + } + } + + /** + * Inject all the core libraries in a certain object. + * + * @param {any} instance The instance where to inject the libraries. + */ + injectLibraries(instance: any): void { + const providers = ( CORE_PROVIDERS).concat(CORE_CONTENTLINKS_PROVIDERS).concat(CORE_COURSE_PROVIDERS) + .concat(CORE_COURSES_PROVIDERS).concat(CORE_FILEUPLOADER_PROVIDERS).concat(CORE_GRADES_PROVIDERS) + .concat(CORE_LOGIN_PROVIDERS).concat(CORE_MAINMENU_PROVIDERS).concat(CORE_SHAREDFILES_PROVIDERS) + .concat(CORE_SITEHOME_PROVIDERS).concat([CoreSiteAddonsProvider]).concat(CORE_USER_PROVIDERS) + .concat(IONIC_NATIVE_PROVIDERS).concat(this.OTHER_PROVIDERS); + + // 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) { + try { + // Inject the provider to the instance. We use the class name as the property name. + instance[providerDef.name] = this.injector.get(providerDef); + } catch (ex) { + this.logger.warn('Error injecting provider', providerDef.name, ex); + } + } + } + + // Add some final classes. + instance['injector'] = this.injector; + instance['Validators'] = Validators; + instance['CoreConfigConstants'] = CoreConfigConstants; + instance['CoreConstants'] = CoreConstants; + instance['moment'] = moment; + instance['Md5'] = Md5; + instance['CoreSyncBaseProvider'] = CoreSyncBaseProvider; + instance['CoreCache'] = CoreCache; + instance['CoreDelegate'] = CoreDelegate; + instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase; + instance['CoreContentLinksModuleGradeHandler'] = CoreContentLinksModuleGradeHandler; + instance['CoreContentLinksModuleIndexHandler'] = CoreContentLinksModuleIndexHandler; + instance['CoreCourseModulePrefetchHandlerBase'] = CoreCourseModulePrefetchHandlerBase; + } +} diff --git a/src/core/siteaddons/components/addon-content/addon-content.html b/src/core/siteaddons/components/addon-content/addon-content.html index 3fb4aac18..6eb252ed4 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.html +++ b/src/core/siteaddons/components/addon-content/addon-content.html @@ -1,3 +1,3 @@ - + diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteaddons/components/addon-content/addon-content.ts index f7e506d0a..32db323e0 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -28,6 +28,7 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { @Input() component: string; @Input() method: string; @Input() args: any; + @Input() bootstrapResult: any; // Result of the bootstrap JS of the handler. @Output() onContentLoaded?: EventEmitter; // Emits an event when the content is loaded. @Output() onLoadingContent?: EventEmitter; // Emits an event when starts to load the content. diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts index 5b7747e16..200e8be8f 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteaddons/components/components.module.ts @@ -17,7 +17,7 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '../../../components/components.module'; -import { CoreCompileHtmlComponentsModule } from '../../../components/compile-html/compile-html.module'; +import { CoreCompileHtmlComponentModule } from '../../compile/components/compile-html/compile-html.module'; import { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-content'; import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index'; @@ -30,7 +30,7 @@ import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index' CommonModule, IonicModule, CoreComponentsModule, - CoreCompileHtmlComponentsModule, + CoreCompileHtmlComponentModule, TranslateModule.forChild() ], providers: [ diff --git a/src/core/siteaddons/components/module-index/module-index.html b/src/core/siteaddons/components/module-index/module-index.html index 6e6ef1862..d2078263f 100644 --- a/src/core/siteaddons/components/module-index/module-index.html +++ b/src/core/siteaddons/components/module-index/module-index.html @@ -9,4 +9,4 @@ - + diff --git a/src/core/siteaddons/components/module-index/module-index.ts b/src/core/siteaddons/components/module-index/module-index.ts index 19220cdd5..256390380 100644 --- a/src/core/siteaddons/components/module-index/module-index.ts +++ b/src/core/siteaddons/components/module-index/module-index.ts @@ -37,6 +37,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co component: string; method: string; args: any; + bootstrapResult: any; // Data for context menu. externalUrl: string; @@ -60,7 +61,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co this.refreshIcon = 'spinner'; if (this.module) { - const handler = this.siteAddonsProvider.getModuleSiteAddonHandler(this.module.modname); + const handler = this.siteAddonsProvider.getSiteAddonHandler(this.module.modname); if (handler) { this.component = handler.addon.component; this.method = handler.handlerSchema.method; @@ -68,6 +69,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co courseid: this.courseId, cmid: this.module.id }; + this.bootstrapResult = handler.bootstrapResult; } // Get the data for the context menu. diff --git a/src/core/siteaddons/directives/call-ws-new-content.ts b/src/core/siteaddons/directives/call-ws-new-content.ts index 38d2cd145..cf23d59f0 100644 --- a/src/core/siteaddons/directives/call-ws-new-content.ts +++ b/src/core/siteaddons/directives/call-ws-new-content.ts @@ -91,7 +91,8 @@ export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallW title: this.title, component: this.component, method: this.method, - args: args + args: args, + bootstrapResult: this.parentContent && this.parentContent.bootstrapResult }); } } diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteaddons/directives/new-content.ts index 4db053f85..a31920aa5 100644 --- a/src/core/siteaddons/directives/new-content.ts +++ b/src/core/siteaddons/directives/new-content.ts @@ -88,7 +88,8 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { title: this.title, component: this.component, method: this.method, - args: args + args: args, + bootstrapResult: this.parentContent && this.parentContent.bootstrapResult }); } }); diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteaddons/pages/addon-page/addon-page.html index 16b323097..c2a75a760 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.html +++ b/src/core/siteaddons/pages/addon-page/addon-page.html @@ -11,5 +11,5 @@ - + diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteaddons/pages/addon-page/addon-page.ts index 772c4dc91..714e50c74 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.ts +++ b/src/core/siteaddons/pages/addon-page/addon-page.ts @@ -32,12 +32,14 @@ export class CoreSiteAddonsAddonPage { component: string; method: string; args: any; + bootstrapResult: any; constructor(params: NavParams) { this.title = params.get('title'); this.component = params.get('component'); this.method = params.get('method'); this.args = params.get('args'); + this.bootstrapResult = params.get('bootstrapResult'); } /** diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 65d89d0b0..17431668a 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -18,16 +18,18 @@ import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; -import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../../core/mainmenu/providers/delegate'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; import { CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData -} from '../../../core/course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '../../../core/course/providers/module-prefetch-delegate'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; +} from '../../course/providers/module-delegate'; +import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; import { CoreSiteAddonsProvider } from './siteaddons'; import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; +import { CoreCompileProvider } from '../../compile/providers/compile'; /** * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site @@ -42,10 +44,42 @@ export class CoreSiteAddonsHelperProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private injector: Injector, private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, - private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate) { + private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, + private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } + /** + * Bootstrap a handler if it has some bootstrap JS. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + * @return {Promise} Promise resolved when done. The resolve param is the result of the javascript execution (if any). + */ + protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise { + if (!handlerSchema.bootstrap) { + return Promise.resolve(); + } + + const siteId = this.sitesProvider.getCurrentSiteId(), + preSets = {getFromCache: false}; // Try to ignore cache. + + return this.siteAddonsProvider.getContent(addon.component, handlerSchema.bootstrap, {}, preSets).then((result) => { + if (!result.javascript || this.sitesProvider.getCurrentSiteId() != siteId) { + // No javascript or site has changed, stop. + return; + } + + // Create a "fake" instance to hold all the libraries. + const instance = {}; + this.compileProvider.injectLibraries(instance); + + // Now execute the javascript using this instance. + return this.compileProvider.executeJavascript(instance, result.javascript); + }); + } + /** * Create a base handler for a site addon. * @@ -86,17 +120,6 @@ export class CoreSiteAddonsHelperProvider { return this.getHandlerPrefixForStrings(handlerName) + key; } - /** - * Get the unique name of a handler (addon + handler). - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler inside the addon. - * @return {string} Unique name. - */ - protected getHandlerUniqueName(addon: any, handlerName: string): string { - return addon.addon + '_' + handlerName; - } - /** * Check if a certain addon is a site addon and it's enabled in a certain site. * @@ -134,7 +157,7 @@ export class CoreSiteAddonsHelperProvider { } for (const lang in handlerSchema.lang) { - const prefix = this.getHandlerPrefixForStrings(this.getHandlerUniqueName(addon, handlerName)); + const prefix = this.getHandlerPrefixForStrings(this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName)); this.langProvider.addSiteAddonsStrings(lang, handlerSchema.lang[lang], prefix); } @@ -144,8 +167,11 @@ export class CoreSiteAddonsHelperProvider { * Load a site addon. * * @param {any} addon Data of the addon. + * @return {Promise} Promise resolved when loaded. */ - loadSiteAddon(addon: any): void { + loadSiteAddon(addon: any): Promise { + const promises = []; + try { if (!addon.parsedHandlers) { addon.parsedHandlers = JSON.parse(addon.handlers); @@ -153,11 +179,13 @@ export class CoreSiteAddonsHelperProvider { // Register all the handlers. for (const name in addon.parsedHandlers) { - this.registerHandler(addon, name, addon.parsedHandlers[name]); + promises.push(this.registerHandler(addon, name, addon.parsedHandlers[name])); } } catch (ex) { this.logger.warn('Error parsing site addon', ex); } + + return this.utils.allPromises(promises); } /** @@ -166,26 +194,42 @@ export class CoreSiteAddonsHelperProvider { * @param {any} addon Data of the addon. * @param {string} handlerName Name of the handler in the addon. * @param {any} handlerSchema Data about the handler. + * @return {Promise} Promise resolved when done. */ - registerHandler(addon: any, handlerName: string, handlerSchema: any): void { + registerHandler(addon: any, handlerName: string, handlerSchema: any): Promise { this.loadHandlerLangStrings(addon, handlerName, handlerSchema); - switch (handlerSchema.delegate) { - case 'CoreMainMenuDelegate': - this.registerMainMenuHandler(addon, handlerName, handlerSchema); - break; + // Wait for the bootstrap JS to be executed. + return this.bootstrapHandler(addon, handlerName, handlerSchema).then((result) => { + let uniqueName; - case 'CoreCourseModuleDelegate': - this.registerModuleHandler(addon, handlerName, handlerSchema); - break; + switch (handlerSchema.delegate) { + case 'CoreMainMenuDelegate': + uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result); + break; - case 'CoreUserDelegate': - this.registerUserProfileHandler(addon, handlerName, handlerSchema); - break; + case 'CoreCourseModuleDelegate': + uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result); + break; - default: - // Nothing to do. - } + case 'CoreUserDelegate': + uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result); + break; + + default: + // Nothing to do. + } + + if (uniqueName) { + // Store the handler data. + this.siteAddonsProvider.setSiteAddonHandler(uniqueName, { + addon: addon, + handlerName: handlerName, + handlerSchema: handlerSchema, + bootstrapResult: result + }); + } + }); } /** @@ -194,15 +238,18 @@ export class CoreSiteAddonsHelperProvider { * @param {any} addon Data of the addon. * @param {string} handlerName Name of the handler in the addon. * @param {any} handlerSchema Data about the handler. + * @param {any} [bootstrapResult] Result of executing the bootstrap JS. + * @return {string} A string to identify the handler. */ - protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any): void { + protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; } // Create the base handler. - const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + baseHandler = this.getBaseHandler(uniqueName), prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); let mainMenuHandler: CoreMainMenuHandler; @@ -219,12 +266,15 @@ export class CoreSiteAddonsHelperProvider { title: prefixedTitle, component: addon.component, method: handlerSchema.method, + bootstrapResult: bootstrapResult } }; } }); this.mainMenuDelegate.registerHandler(mainMenuHandler); + + return uniqueName; } /** @@ -233,8 +283,10 @@ export class CoreSiteAddonsHelperProvider { * @param {any} addon Data of the addon. * @param {string} handlerName Name of the handler in the addon. * @param {any} handlerSchema Data about the handler. + * @param {any} [bootstrapResult] Result of executing the bootstrap JS. + * @return {string} A string to identify the handler. */ - protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any): void { + protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -243,16 +295,10 @@ export class CoreSiteAddonsHelperProvider { // Create the base handler. const modName = addon.component.replace('mod_', ''), baseHandler = this.getBaseHandler(modName), - hasOfflineFunctions = !!(handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length); + hasOfflineFunctions = !!(handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length), + showDowloadButton = handlerSchema.downloadbutton; let moduleHandler: CoreCourseModuleHandler; - // Store the handler data. - this.siteAddonsProvider.setModuleSiteAddonHandler(modName, { - addon: addon, - handlerName: handlerName, - handlerSchema: handlerSchema - }); - // Extend the base handler, adding the properties required by the delegate. moduleHandler = Object.assign(baseHandler, { getData: (module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData => { @@ -260,7 +306,7 @@ export class CoreSiteAddonsHelperProvider { title: module.name, icon: handlerSchema.displaydata.icon, class: handlerSchema.displaydata.class, - showDownloadButton: hasOfflineFunctions, + showDownloadButton: typeof showDowloadButton != 'undefined' ? showDowloadButton : hasOfflineFunctions, action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { event.preventDefault(); event.stopPropagation(); @@ -285,6 +331,8 @@ export class CoreSiteAddonsHelperProvider { } this.moduleDelegate.registerHandler(moduleHandler); + + return modName; } /** @@ -293,15 +341,18 @@ export class CoreSiteAddonsHelperProvider { * @param {any} addon Data of the addon. * @param {string} handlerName Name of the handler in the addon. * @param {any} handlerSchema Data about the handler. + * @param {any} [bootstrapResult] Result of executing the bootstrap JS. + * @return {string} A string to identify the handler. */ - protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any): void { + protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; } // Create the base handler. - const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + baseHandler = this.getBaseHandler(uniqueName), prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); let userHandler: CoreUserProfileHandler; @@ -332,7 +383,8 @@ export class CoreSiteAddonsHelperProvider { args: { courseid: courseId, userid: user.id - } + }, + bootstrapResult: bootstrapResult }); } }; @@ -340,5 +392,7 @@ export class CoreSiteAddonsHelperProvider { }); this.userDelegate.registerHandler(userHandler); + + return uniqueName; } } diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 489f3eca7..662fb7da7 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -23,9 +23,9 @@ import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreConfigConstants } from '../../../configconstants'; /** - * Handler of a site addon representing a module. + * Handler of a site addon. */ -export interface CoreSiteAddonsModuleHandler { +export interface CoreSiteAddonsHandler { /** * The site addon data. * @type {any} @@ -43,6 +43,12 @@ export interface CoreSiteAddonsModuleHandler { * @type {any} */ handlerSchema: any; + + /** + * Result of executing the bootstrap JS. + * @type {any} + */ + bootstrapResult?: any; } export interface CoreSiteAddonsGetContentResult { @@ -79,7 +85,7 @@ export class CoreSiteAddonsProvider { protected ROOT_CACHE_KEY = 'CoreSiteAddons:'; protected logger; - protected moduleSiteAddons: {[modName: string]: CoreSiteAddonsModuleHandler} = {}; + protected siteAddons: {[name: string]: CoreSiteAddonsHandler} = {}; // Site addons registered. constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform) { @@ -179,10 +185,12 @@ export class CoreSiteAddonsProvider { * @param {string} component Component where the class is. E.g. mod_assign. * @param {string} method Method to execute in the class. * @param {any} args The params for the method. + * @param {CoreSiteWSPreSets} [preSets] Extra options. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved with the result. */ - getContent(component: string, method: string, args: any, siteId?: string): Promise { + getContent(component: string, method: string, args: any, preSets?: CoreSiteWSPreSets, siteId?: string) + : Promise { this.logger.debug(`Get content for component '${component}' and method '${method}'`); return this.sitesProvider.getSite(siteId).then((site) => { @@ -194,10 +202,11 @@ export class CoreSiteAddonsProvider { component: component, method: method, args: this.utils.objectToArrayOfObjects(argsToSend, 'name', 'value', true) - }, preSets = { - cacheKey: this.getContentCacheKey(component, method, args) }; + preSets = preSets || {}; + preSets.cacheKey = this.getContentCacheKey(component, method, args); + return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets); }).then((result) => { if (result.otherdata) { @@ -226,13 +235,24 @@ export class CoreSiteAddonsProvider { } /** - * Get the site addon handler for a certain module. + * Get the unique name of a handler (addon + handler). * - * @param {string} modName Name of the module. - * @return {CoreSiteAddonsModuleHandler} Handler. + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler inside the addon. + * @return {string} Unique name. */ - getModuleSiteAddonHandler(modName: string): CoreSiteAddonsModuleHandler { - return this.moduleSiteAddons[modName]; + getHandlerUniqueName(addon: any, handlerName: string): string { + return addon.addon + '_' + handlerName; + } + + /** + * Get a site addon handler. + * + * @param {string} name Unique name of the handler. + * @return {CoreSiteAddonsHandler} Handler. + */ + getSiteAddonHandler(name: string): CoreSiteAddonsHandler { + return this.siteAddons[name]; } /** @@ -328,12 +348,12 @@ export class CoreSiteAddonsProvider { } /** - * Set the site addon handler for a certain module. + * Store a site addon handler. * - * @param {string} modName Name of the module. - * @param {CoreSiteAddonsModuleHandler} handler Handler to set. + * @param {string} name A unique name to identify the handler. + * @param {CoreSiteAddonsHandler} handler Handler to set. */ - setModuleSiteAddonHandler(modName: string, handler: CoreSiteAddonsModuleHandler): void { - this.moduleSiteAddons[modName] = handler; + setSiteAddonHandler(name: string, handler: CoreSiteAddonsHandler): void { + this.siteAddons[name] = handler; } } diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteaddons/siteaddons.module.ts index 053905ad3..132e0d809 100644 --- a/src/core/siteaddons/siteaddons.module.ts +++ b/src/core/siteaddons/siteaddons.module.ts @@ -13,7 +13,6 @@ // limitations under the License. import { NgModule } from '@angular/core'; -import { Platform } from 'ionic-angular'; import { CoreSiteAddonsProvider } from './providers/siteaddons'; import { CoreSiteAddonsHelperProvider } from './providers/helper'; import { CoreSiteAddonsComponentsModule } from './components/components.module'; diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts index 53e5092f1..c03d74420 100644 --- a/src/providers/addonmanager.ts +++ b/src/providers/addonmanager.ts @@ -17,6 +17,7 @@ import { CoreEventsProvider } from './events'; import { CoreLoggerProvider } from './logger'; import { CoreSitesProvider } from './sites'; import { CoreSiteWSPreSets } from '../classes/site'; +import { CoreUtilsProvider } from './utils/utils'; import { CoreSiteAddonsProvider } from '../core/siteaddons/providers/siteaddons'; import { CoreSiteAddonsHelperProvider } from '../core/siteaddons/providers/helper'; @@ -29,7 +30,8 @@ export class CoreAddonManagerProvider { protected logger; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - private siteAddonsProvider: CoreSiteAddonsProvider, private siteAddonsHelperProvider: CoreSiteAddonsHelperProvider) { + private siteAddonsProvider: CoreSiteAddonsProvider, private siteAddonsHelperProvider: CoreSiteAddonsHelperProvider, + private utils: CoreUtilsProvider) { logger = logger.getInstance('CoreAddonManagerProvider'); // Fetch the addons on login. @@ -39,9 +41,10 @@ export class CoreAddonManagerProvider { // Addons fetched, check that site hasn't changed. if (siteId == this.sitesProvider.getCurrentSiteId() && addons.length) { // Site is still the same. Load the addons and trigger the event. - this.loadSiteAddons(addons); + this.loadSiteAddons(addons).then(() => { + eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); + }); - eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); } }); }); @@ -85,10 +88,15 @@ export class CoreAddonManagerProvider { * Load site addons. * * @param {any[]} addons The addons to load. + * @return {Promise} Promise resolved when loaded. */ - loadSiteAddons(addons: any[]): void { + loadSiteAddons(addons: any[]): Promise { + const promises = []; + addons.forEach((addon) => { - this.siteAddonsHelperProvider.loadSiteAddon(addon); + promises.push(this.siteAddonsHelperProvider.loadSiteAddon(addon)); }); + + return this.utils.allPromises(promises); } }