From b795a6230ed1a422b667d05b4777d60a5f136773 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 8 Feb 2018 13:31:31 +0100 Subject: [PATCH 01/32] MOBILE-2333 core: Implement compile HTML component --- .../compile-html/compile-html.module.ts | 30 +++++++ src/components/compile-html/compile-html.ts | 89 +++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/components/compile-html/compile-html.module.ts create mode 100644 src/components/compile-html/compile-html.ts diff --git a/src/components/compile-html/compile-html.module.ts b/src/components/compile-html/compile-html.module.ts new file mode 100644 index 000000000..2bd45fc28 --- /dev/null +++ b/src/components/compile-html/compile-html.module.ts @@ -0,0 +1,30 @@ +// (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 { CommonModule } from '@angular/common'; +import { CoreCompileHtmlComponent } from './compile-html'; + +@NgModule({ + declarations: [ + CoreCompileHtmlComponent + ], + imports: [ + CommonModule + ], + exports: [ + CoreCompileHtmlComponent + ] +}) +export class CoreCompileHtmlComponentsModule {} diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts new file mode 100644 index 000000000..68a4f24c0 --- /dev/null +++ b/src/components/compile-html/compile-html.ts @@ -0,0 +1,89 @@ +// (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, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef } from '@angular/core'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +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 { CoreCoursesComponentsModule } from '../../core/courses/components/components.module'; +import { CoreSiteHomeComponentsModule } from '../../core/sitehome/components/components.module'; +import { CoreUserComponentsModule } from '../../core/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 isn't part of CoreComponentsModule to prevent circular dependencies. If you want to use it, + * you need to import CoreCompileHtmlComponentsModule. + */ +@Component({ + selector: 'core-compile-html', + template: '' +}) +export class CoreCompileHtmlComponent implements OnInit, 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 + ]; + + @Input() text: string; // The HTML text to display. + + // Get the container where to put the content. + @ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef; + + protected componentRef: ComponentRef; + + constructor(protected compiler: Compiler) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + if (this.text) { + // Create a new component and a new module. + const component = Component({template: this.text})(class {}), + 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; + } + } + + // Create the component. + this.componentRef = this.container.createComponent(componentFactory); + }); + } + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + this.componentRef && this.componentRef.destroy(); + } +} From 9267c63698a6f88b76ed448a9b2c6966f23c6945 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 12 Feb 2018 11:51:16 +0100 Subject: [PATCH 02/32] MOBILE-2333 mainmenu: Support page params in handlers --- src/core/mainmenu/pages/menu/menu.html | 2 +- src/core/mainmenu/providers/delegate.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/mainmenu/pages/menu/menu.html b/src/core/mainmenu/pages/menu/menu.html index f053d132d..54839b8ee 100644 --- a/src/core/mainmenu/pages/menu/menu.html +++ b/src/core/mainmenu/pages/menu/menu.html @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/core/mainmenu/providers/delegate.ts b/src/core/mainmenu/providers/delegate.ts index 5d8b64865..ef87d35f3 100644 --- a/src/core/mainmenu/providers/delegate.ts +++ b/src/core/mainmenu/providers/delegate.ts @@ -82,6 +82,12 @@ export interface CoreMainMenuHandlerData { * @type {boolean} */ loading?: boolean; + + /** + * Params to pass to the page. + * @type {any} + */ + pageParams?: any; } /** From 7d3162585c586be1f48539daa428a3a4382c9505 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 13 Feb 2018 08:25:52 +0100 Subject: [PATCH 03/32] MOBILE-2333 siteaddons: Fetch and register basic site addons --- src/app/app.module.ts | 9 +- src/classes/delegate.ts | 2 +- src/core/siteaddons/providers/siteaddons.ts | 299 ++++++++++++++++++++ src/core/siteaddons/siteaddons.module.ts | 28 ++ src/providers/addonmanager.ts | 88 ++++++ src/providers/events.ts | 2 +- 6 files changed, 424 insertions(+), 4 deletions(-) create mode 100644 src/core/siteaddons/providers/siteaddons.ts create mode 100644 src/core/siteaddons/siteaddons.module.ts create mode 100644 src/providers/addonmanager.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7b0be5c01..376a2b963 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -49,6 +49,7 @@ import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreUpdateManagerProvider } from '@providers/update-manager'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CoreSyncProvider } from '@providers/sync'; +import { CoreAddonManagerProvider } from '@providers/addonmanager'; // Core modules. import { CoreComponentsModule } from '@components/components.module'; @@ -64,6 +65,7 @@ import { CoreContentLinksModule } from '@core/contentlinks/contentlinks.module'; 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'; // Addon modules. import { AddonCalendarModule } from '@addon/calendar/calendar.module'; @@ -111,6 +113,7 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader { CoreUserModule, CoreGradesModule, CoreSettingsModule, + CoreSiteAddonsModule, AddonCalendarModule, AddonUserProfileFieldModule, AddonFilesModule, @@ -153,12 +156,14 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader { CoreFilepoolProvider, CoreUpdateManagerProvider, CorePluginFileDelegate, - CoreSyncProvider + CoreSyncProvider, + CoreAddonManagerProvider ] }) export class AppModule { constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider, - sitesProvider: CoreSitesProvider) { + sitesProvider: CoreSitesProvider, addonManagerProvider: CoreAddonManagerProvider) { + // Inject CoreAddonManagerProvider even if it's not used to make sure it's initialized. // Register a handler for platform ready. initDelegate.registerProcess({ name: 'CorePlatformReady', diff --git a/src/classes/delegate.ts b/src/classes/delegate.ts index 00e1bf791..ac08c86eb 100644 --- a/src/classes/delegate.ts +++ b/src/classes/delegate.ts @@ -91,7 +91,7 @@ export class CoreDelegate { // Update handlers on this cases. eventsProvider.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this)); eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this)); - eventsProvider.on(CoreEventsProvider.REMOTE_ADDONS_LOADED, this.updateHandlers.bind(this)); + eventsProvider.on(CoreEventsProvider.SITE_ADDONS_LOADED, this.updateHandlers.bind(this)); } } diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts new file mode 100644 index 000000000..e483a992c --- /dev/null +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -0,0 +1,299 @@ +// (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 } from '@angular/core'; +import { NavController, NavOptions } from 'ionic-angular'; +import { CoreLoggerProvider } from '../../../providers/logger'; +import { CoreSite } from '../../../classes/site'; +import { CoreSitesProvider } from '../../../providers/sites'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../../core/mainmenu/providers/delegate'; +import { + CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData +} from '../../../core/course/providers/module-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; +import { CoreDelegateHandler } from '../../../classes/delegate'; + +/** + * Service to provide functionalities regarding site addons. + */ +@Injectable() +export class CoreSiteAddonsProvider { + protected ROOT_CACHE_KEY = 'CoreSiteAddons:'; + + protected logger; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, + private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, + private userDelegate: CoreUserDelegate) { + this.logger = logger.getInstance('CoreUserProvider'); + } + + /** + * Create a base handler for a site addon. + * + * @param {string} name Name of the handler. + * @return {CoreDelegateHandler} The base handler. + */ + protected getBaseHandler(name: string): CoreDelegateHandler { + return { + name: name, + isEnabled: (): boolean => { + return true; + } + }; + } + + /** + * Given a handler's unique name and the key of a string, return the full string key (prefixed). + * + * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @param {string} key The key of the string. + * @return {string} Full string key. + */ + protected getHandlerPrefixedString(handlerName: string, key: string): string { + if (name) { + return 'addon.' + handlerName + '.' + key; + } + + return ''; + } + + /** + * 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. + * + * @param {any} addon Data of the addon. + * @param {CoreSite} site Site affected. + * @return {boolean} Whether it's a site addon and it's enabled. + */ + isSiteAddonEnabled(addon: any, site: CoreSite): boolean { + if (!site.isFeatureDisabled('siteAddOn_' + addon.component + '_' + addon.addon) && addon.handlers) { + // Site addon not disabled. Check if it has handlers. + try { + if (!addon.parsedHandlers) { + addon.parsedHandlers = JSON.parse(addon.handlers); + } + + return !!(addon.parsedHandlers && Object.keys(addon.parsedHandlers).length); + } catch (ex) { + this.logger.warn('Error parsing site addon', ex); + } + } + + return false; + } + + /** + * Load a site addon. + * + * @param {any} addon Data of the addon. + */ + loadSiteAddon(addon: any): void { + try { + if (!addon.parsedHandlers) { + addon.parsedHandlers = JSON.parse(addon.handlers); + } + + // Register all the handlers. + for (const name in addon.parsedHandlers) { + this.registerHandler(addon, name, addon.parsedHandlers[name]); + } + } catch (ex) { + this.logger.warn('Error parsing site addon', ex); + } + } + + /** + * Register a site addon handler in the right delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + registerHandler(addon: any, handlerName: string, handlerSchema: any): void { + switch (handlerSchema.delegate) { + case 'CoreMainMenuDelegate': + this.registerMainMenuHandler(addon, handlerName, handlerSchema); + break; + + case 'CoreCourseModuleDelegate': + this.registerModuleHandler(addon, handlerName, handlerSchema); + break; + + case 'CoreUserDelegate': + this.registerUserProfileHandler(addon, handlerName, handlerSchema); + break; + + default: + // Nothing to do. + } + } + + /** + * Given a handler in an addon, register it in the main menu delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); + let mainMenuHandler: CoreMainMenuHandler; + + // Extend the base handler, adding the properties required by the delegate. + mainMenuHandler = Object.assign(baseHandler, { + priority: handlerSchema.priority, + getDisplayData: (): CoreMainMenuHandlerData => { + return { + title: prefixedTitle, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + page: 'CoreSiteAddonsAddonPage', + pageParams: { + title: prefixedTitle, + component: addon.component, + callback: handlerSchema.mainfunction, + contextId: handlerSchema.contextid + } + }; + } + }); + + this.mainMenuDelegate.registerHandler(mainMenuHandler); + } + + /** + * Given a handler in an addon, register it in the module delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const baseHandler = this.getBaseHandler(addon.component.replace('mod_', '')); + let moduleHandler: CoreCourseModuleHandler; + + // Extend the base handler, adding the properties required by the delegate. + moduleHandler = Object.assign(baseHandler, { + getData: (module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData => { + return { + title: module.name, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + showDownloadButton: handlerSchema.offlinefunctions && handlerSchema.offlinefunctions.length, + action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsAddonPage', { + title: module.name, + component: addon.component, + callback: handlerSchema.mainfunction, + contextId: handlerSchema.contextid, + args: { + course: courseId, + cmid: module.id + } + }, options); + } + }; + }, + getMainComponent: (course: any, module: any): any => { + // Singleactivity course format not supported with site addons. + } + }); + + this.moduleDelegate.registerHandler(moduleHandler); + } + + /** + * Given a handler in an addon, register it in the user profile delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); + let userHandler: CoreUserProfileHandler; + + // Extend the base handler, adding the properties required by the delegate. + userHandler = Object.assign(baseHandler, { + priority: handlerSchema.priority, + type: handlerSchema.type, + isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean => { + if (handlerSchema.restricted == 'current' && user.id != this.sitesProvider.getCurrentSite().getUserId()) { + return false; + } + + return true; + }, + getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { + return { + title: prefixedTitle, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + action: (event: Event, navCtrl: NavController, user: any, courseId?: number): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsAddonPage', { + title: prefixedTitle, + component: addon.component, + callback: handlerSchema.mainfunction, + contextId: handlerSchema.contextid, + args: { + course: courseId, + userid: user.id + } + }); + } + }; + } + }); + + this.userDelegate.registerHandler(userHandler); + } +} diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteaddons/siteaddons.module.ts new file mode 100644 index 000000000..00275ba92 --- /dev/null +++ b/src/core/siteaddons/siteaddons.module.ts @@ -0,0 +1,28 @@ +// (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 { Platform } from 'ionic-angular'; +import { CoreSiteAddonsProvider } from './providers/siteaddons'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + CoreSiteAddonsProvider + ] +}) +export class CoreSiteAddonsModule { } diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts new file mode 100644 index 000000000..106e3cc25 --- /dev/null +++ b/src/providers/addonmanager.ts @@ -0,0 +1,88 @@ +// (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 } from '@angular/core'; +import { CoreEventsProvider } from './events'; +import { CoreLoggerProvider } from './logger'; +import { CoreSitesProvider } from './sites'; +import { CoreSiteWSPreSets } from '../classes/site'; +import { CoreSiteAddonsProvider } from '../core/siteaddons/providers/siteaddons'; + +/** + * Provider with some helper functions regarding addons. + */ +@Injectable() +export class CoreAddonManagerProvider { + + protected logger; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, + private siteAddonsProvider: CoreSiteAddonsProvider) { + logger = logger.getInstance('CoreAddonManagerProvider'); + + // Fetch the addons on login. + eventsProvider.on(CoreEventsProvider.LOGIN, () => { + const siteId = this.sitesProvider.getCurrentSiteId(); + this.fetchSiteAddons(siteId).then((addons) => { + // Addons fetched, check that site hasn't changed. + if (siteId == this.sitesProvider.getCurrentSiteId()) { + // Site is still the same. Load the addons and trigger the event. + this.loadSiteAddons(addons); + + eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); + } + }); + }); + + // Unload addons on logout if any. + eventsProvider.on(CoreEventsProvider.LOGOUT, () => { + // @todo: Unload site addons. + }); + } + + /** + * Fetch site addons. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. Returns the list of addons to load. + */ + fetchSiteAddons(siteId?: string): Promise { + const addons = []; + + return this.sitesProvider.getSite(siteId).then((site) => { + // Get the list of addons. Try not to use cache. + return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { + data.plugins.forEach((addon: any) => { + // Check if it's a site addon and it's enabled. + if (this.siteAddonsProvider.isSiteAddonEnabled(addon, site)) { + addons.push(addon); + } + }); + + return addons; + }); + }); + } + + /** + * Load site addons. + * + * @param {any[]} addons The addons to load. + */ + loadSiteAddons(addons: any[]): void { + addons.forEach((addon) => { + this.siteAddonsProvider.loadSiteAddon(addon); + }); + } +} diff --git a/src/providers/events.ts b/src/providers/events.ts index bbe557636..2dff17b08 100644 --- a/src/providers/events.ts +++ b/src/providers/events.ts @@ -47,7 +47,7 @@ export class CoreEventsProvider { static PACKAGE_STATUS_CHANGED = 'package_status_changed'; static COURSE_STATUS_CHANGED = 'course_status_changed'; static SECTION_STATUS_CHANGED = 'section_status_changed'; - static REMOTE_ADDONS_LOADED = 'remote_addons_loaded'; + static SITE_ADDONS_LOADED = 'site_addons_loaded'; static LOGIN_SITE_CHECKED = 'login_site_checked'; static LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; static IAB_LOAD_START = 'inappbrowser_load_start'; From 91c8be2167021388e3658763e2f2520d2970aefd Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 13 Feb 2018 14:38:04 +0100 Subject: [PATCH 04/32] MOBILE-2333 core: Export lists of providers --- src/app/app.module.ts | 62 +++++++++++--------- src/core/contentlinks/contentlinks.module.ts | 11 ++-- src/core/course/course.module.ts | 20 ++++--- src/core/courses/courses.module.ts | 12 ++-- src/core/emulator/emulator.module.ts | 18 ++++++ src/core/fileuploader/fileuploader.module.ts | 14 +++-- src/core/grades/grades.module.ts | 12 ++-- src/core/login/login.module.ts | 9 ++- src/core/mainmenu/mainmenu.module.ts | 11 ++-- src/core/sharedfiles/sharedfiles.module.ts | 12 ++-- src/core/siteaddons/siteaddons.module.ts | 9 ++- src/core/sitehome/sitehome.module.ts | 10 +++- src/core/user/user.module.ts | 16 +++-- 13 files changed, 139 insertions(+), 77 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 376a2b963..893edcb9c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -81,6 +81,36 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader { return new TranslateHttpLoader(http, './assets/lang/', '.json'); } +// List of providers. +export const CORE_PROVIDERS: any[] = [ + CoreLoggerProvider, + CoreDbProvider, + CoreAppProvider, + CoreConfigProvider, + CoreLangProvider, + CoreTextUtilsProvider, + CoreDomUtilsProvider, + CoreTimeUtilsProvider, + CoreUrlUtilsProvider, + CoreUtilsProvider, + CoreMimetypeUtilsProvider, + CoreInitDelegate, + CoreFileProvider, + CoreWSProvider, + CoreEventsProvider, + CoreSitesFactoryProvider, + CoreSitesProvider, + CoreLocalNotificationsProvider, + CoreGroupsProvider, + CoreCronDelegate, + CoreFileSessionProvider, + CoreFilepoolProvider, + CoreUpdateManagerProvider, + CorePluginFileDelegate, + CoreSyncProvider, + CoreAddonManagerProvider +]; + @NgModule({ declarations: [ MoodleMobileApp @@ -126,39 +156,13 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader { entryComponents: [ MoodleMobileApp ], - providers: [ + providers: CORE_PROVIDERS.concat([ { provide: HTTP_INTERCEPTORS, useClass: CoreInterceptor, multi: true, - }, - CoreLoggerProvider, - CoreDbProvider, - CoreAppProvider, - CoreConfigProvider, - CoreLangProvider, - CoreTextUtilsProvider, - CoreDomUtilsProvider, - CoreTimeUtilsProvider, - CoreUrlUtilsProvider, - CoreUtilsProvider, - CoreMimetypeUtilsProvider, - CoreInitDelegate, - CoreFileProvider, - CoreWSProvider, - CoreEventsProvider, - CoreSitesFactoryProvider, - CoreSitesProvider, - CoreLocalNotificationsProvider, - CoreGroupsProvider, - CoreCronDelegate, - CoreFileSessionProvider, - CoreFilepoolProvider, - CoreUpdateManagerProvider, - CorePluginFileDelegate, - CoreSyncProvider, - CoreAddonManagerProvider - ] + } + ]) }) export class AppModule { constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider, diff --git a/src/core/contentlinks/contentlinks.module.ts b/src/core/contentlinks/contentlinks.module.ts index 05c3ea838..0abe605db 100644 --- a/src/core/contentlinks/contentlinks.module.ts +++ b/src/core/contentlinks/contentlinks.module.ts @@ -16,14 +16,17 @@ import { NgModule } from '@angular/core'; import { CoreContentLinksDelegate } from './providers/delegate'; import { CoreContentLinksHelperProvider } from './providers/helper'; +// List of providers. +export const CORE_CONTENTLINKS_PROVIDERS = [ + CoreContentLinksDelegate, + CoreContentLinksHelperProvider +]; + @NgModule({ declarations: [], imports: [ ], - providers: [ - CoreContentLinksDelegate, - CoreContentLinksHelperProvider - ], + providers: CORE_CONTENTLINKS_PROVIDERS, exports: [] }) export class CoreContentLinksModule {} diff --git a/src/core/course/course.module.ts b/src/core/course/course.module.ts index 9154daaab..d26fbd730 100644 --- a/src/core/course/course.module.ts +++ b/src/core/course/course.module.ts @@ -25,6 +25,16 @@ import { CoreCourseFormatSocialModule } from './formats/social/social.module'; import { CoreCourseFormatTopicsModule } from './formats/topics/topics.module'; import { CoreCourseFormatWeeksModule } from './formats/weeks/weeks.module'; +// List of providers (without handlers). +export const CORE_COURSE_PROVIDERS: any[] = [ + CoreCourseProvider, + CoreCourseHelperProvider, + CoreCourseFormatDelegate, + CoreCourseModuleDelegate, + CoreCourseModulePrefetchDelegate, + CoreCourseOptionsDelegate +]; + @NgModule({ declarations: [], imports: [ @@ -33,15 +43,9 @@ import { CoreCourseFormatWeeksModule } from './formats/weeks/weeks.module'; CoreCourseFormatWeeksModule, CoreCourseFormatSocialModule ], - providers: [ - CoreCourseProvider, - CoreCourseHelperProvider, - CoreCourseFormatDelegate, - CoreCourseModuleDelegate, - CoreCourseModulePrefetchDelegate, - CoreCourseOptionsDelegate, + providers: CORE_COURSE_PROVIDERS.concat([ CoreCourseFormatDefaultHandler - ], + ]), exports: [] }) export class CoreCourseModule {} diff --git a/src/core/courses/courses.module.ts b/src/core/courses/courses.module.ts index 2fde4b912..d38dad816 100644 --- a/src/core/courses/courses.module.ts +++ b/src/core/courses/courses.module.ts @@ -22,18 +22,22 @@ import { CoreCoursesMyOverviewLinkHandler } from './providers/my-overview-link-h import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate'; import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +// List of providers (without handlers). +export const CORE_COURSES_PROVIDERS: any[] = [ + CoreCoursesProvider, + CoreCoursesMyOverviewProvider +]; + @NgModule({ declarations: [], imports: [ ], - providers: [ - CoreCoursesProvider, + providers: CORE_COURSES_PROVIDERS.concat([ CoreCoursesMainMenuHandler, - CoreCoursesMyOverviewProvider, CoreCoursesCourseLinkHandler, CoreCoursesIndexLinkHandler, CoreCoursesMyOverviewLinkHandler - ], + ]), exports: [] }) export class CoreCoursesModule { diff --git a/src/core/emulator/emulator.module.ts b/src/core/emulator/emulator.module.ts index 353369710..8229d4bec 100644 --- a/src/core/emulator/emulator.module.ts +++ b/src/core/emulator/emulator.module.ts @@ -56,6 +56,24 @@ import { CoreUrlUtilsProvider } from '@providers/utils/url'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreInitDelegate } from '@providers/init'; +// List of Ionic Native providers. +export const IONIC_NATIVE_PROVIDERS = [ + Camera, + Clipboard, + File, + FileTransfer, + Globalization, + InAppBrowser, + Keyboard, + LocalNotifications, + MediaCapture, + Network, + SplashScreen, + StatusBar, + SQLite, + Zip +]; + /** * This module handles the emulation of Cordova plugins in browser and desktop. * diff --git a/src/core/fileuploader/fileuploader.module.ts b/src/core/fileuploader/fileuploader.module.ts index 5d52372bb..d0545d32e 100644 --- a/src/core/fileuploader/fileuploader.module.ts +++ b/src/core/fileuploader/fileuploader.module.ts @@ -22,21 +22,25 @@ import { CoreFileUploaderCameraHandler } from './providers/camera-handler'; import { CoreFileUploaderFileHandler } from './providers/file-handler'; import { CoreFileUploaderVideoHandler } from './providers/video-handler'; +// List of providers (without handlers). +export const CORE_FILEUPLOADER_PROVIDERS: any[] = [ + CoreFileUploaderProvider, + CoreFileUploaderHelperProvider, + CoreFileUploaderDelegate +]; + @NgModule({ declarations: [ ], imports: [ ], - providers: [ - CoreFileUploaderProvider, - CoreFileUploaderHelperProvider, - CoreFileUploaderDelegate, + providers: CORE_FILEUPLOADER_PROVIDERS.concat([ CoreFileUploaderAlbumHandler, CoreFileUploaderAudioHandler, CoreFileUploaderCameraHandler, CoreFileUploaderFileHandler, CoreFileUploaderVideoHandler - ] + ]) }) export class CoreFileUploaderModule { constructor(delegate: CoreFileUploaderDelegate, albumHandler: CoreFileUploaderAlbumHandler, diff --git a/src/core/grades/grades.module.ts b/src/core/grades/grades.module.ts index d090f2e00..136715c3d 100644 --- a/src/core/grades/grades.module.ts +++ b/src/core/grades/grades.module.ts @@ -29,21 +29,25 @@ import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreUserProvider } from '../user/providers/user'; +// List of providers (without handlers). +export const CORE_GRADES_PROVIDERS: any[] = [ + CoreGradesProvider, + CoreGradesHelperProvider +]; + @NgModule({ declarations: [ ], imports: [ CoreGradesComponentsModule ], - providers: [ - CoreGradesProvider, - CoreGradesHelperProvider, + providers: CORE_GRADES_PROVIDERS.concat([ CoreGradesMainMenuHandler, CoreGradesCourseOptionHandler, CoreGradesUserLinkHandler, CoreGradesOverviewLinkHandler, CoreGradesUserHandler - ] + ]) }) export class CoreGradesModule { constructor(mainMenuDelegate: CoreMainMenuDelegate, gradesMenuHandler: CoreGradesMainMenuHandler, diff --git a/src/core/login/login.module.ts b/src/core/login/login.module.ts index 8e4f9a3d8..55d40cff9 100644 --- a/src/core/login/login.module.ts +++ b/src/core/login/login.module.ts @@ -15,13 +15,16 @@ import { NgModule } from '@angular/core'; import { CoreLoginHelperProvider } from './providers/helper'; +// List of providers. +export const CORE_LOGIN_PROVIDERS = [ + CoreLoginHelperProvider +]; + @NgModule({ declarations: [ ], imports: [ ], - providers: [ - CoreLoginHelperProvider - ] + providers: CORE_LOGIN_PROVIDERS }) export class CoreLoginModule {} diff --git a/src/core/mainmenu/mainmenu.module.ts b/src/core/mainmenu/mainmenu.module.ts index 0a49f84b8..7873056cb 100644 --- a/src/core/mainmenu/mainmenu.module.ts +++ b/src/core/mainmenu/mainmenu.module.ts @@ -16,14 +16,17 @@ import { NgModule } from '@angular/core'; import { CoreMainMenuDelegate } from './providers/delegate'; import { CoreMainMenuProvider } from './providers/mainmenu'; +// List of providers. +export const CORE_MAINMENU_PROVIDERS = [ + CoreMainMenuDelegate, + CoreMainMenuProvider +]; + @NgModule({ declarations: [ ], imports: [ ], - providers: [ - CoreMainMenuDelegate, - CoreMainMenuProvider - ] + providers: CORE_MAINMENU_PROVIDERS }) export class CoreMainMenuModule {} diff --git a/src/core/sharedfiles/sharedfiles.module.ts b/src/core/sharedfiles/sharedfiles.module.ts index e58bbd904..fabe30c12 100644 --- a/src/core/sharedfiles/sharedfiles.module.ts +++ b/src/core/sharedfiles/sharedfiles.module.ts @@ -19,16 +19,20 @@ import { CoreSharedFilesHelperProvider } from './providers/helper'; import { CoreSharedFilesUploadHandler } from './providers/upload-handler'; import { CoreFileUploaderDelegate } from '../fileuploader/providers/delegate'; +// List of providers (without handlers). +export const CORE_SHAREDFILES_PROVIDERS: any[] = [ + CoreSharedFilesProvider, + CoreSharedFilesHelperProvider +]; + @NgModule({ declarations: [ ], imports: [ ], - providers: [ - CoreSharedFilesProvider, - CoreSharedFilesHelperProvider, + providers: CORE_SHAREDFILES_PROVIDERS.concat([ CoreSharedFilesUploadHandler - ] + ]) }) export class CoreSharedFilesModule { constructor(platform: Platform, delegate: CoreFileUploaderDelegate, handler: CoreSharedFilesUploadHandler, diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteaddons/siteaddons.module.ts index 00275ba92..f7f7f9298 100644 --- a/src/core/siteaddons/siteaddons.module.ts +++ b/src/core/siteaddons/siteaddons.module.ts @@ -16,13 +16,16 @@ import { NgModule } from '@angular/core'; import { Platform } from 'ionic-angular'; import { CoreSiteAddonsProvider } from './providers/siteaddons'; +// List of providers. +export const CORE_SITEADDONS_PROVIDERS = [ + CoreSiteAddonsProvider +]; + @NgModule({ declarations: [ ], imports: [ ], - providers: [ - CoreSiteAddonsProvider - ] + providers: CORE_SITEADDONS_PROVIDERS }) export class CoreSiteAddonsModule { } diff --git a/src/core/sitehome/sitehome.module.ts b/src/core/sitehome/sitehome.module.ts index c498d2b12..d24f3c2df 100644 --- a/src/core/sitehome/sitehome.module.ts +++ b/src/core/sitehome/sitehome.module.ts @@ -19,15 +19,19 @@ import { CoreSiteHomeIndexLinkHandler } from './providers/index-link-handler'; import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate'; import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +// List of providers (without handlers). +export const CORE_SITEHOME_PROVIDERS: any[] = [ + CoreSiteHomeProvider +]; + @NgModule({ declarations: [], imports: [ ], - providers: [ - CoreSiteHomeProvider, + providers: CORE_SITEHOME_PROVIDERS.concat([ CoreSiteHomeMainMenuHandler, CoreSiteHomeIndexLinkHandler - ], + ]), exports: [] }) export class CoreSiteHomeModule { diff --git a/src/core/user/user.module.ts b/src/core/user/user.module.ts index bfec54d89..12d3d78df 100644 --- a/src/core/user/user.module.ts +++ b/src/core/user/user.module.ts @@ -27,22 +27,26 @@ import { CoreUserParticipantsLinkHandler } from './providers/participants-link-h import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; import { CoreUserComponentsModule } from './components/components.module'; +// List of providers (without handlers). +export const CORE_USER_PROVIDERS: any[] = [ + CoreUserDelegate, + CoreUserProfileFieldDelegate, + CoreUserProvider, + CoreUserHelperProvider, +]; + @NgModule({ declarations: [ ], imports: [ CoreUserComponentsModule ], - providers: [ - CoreUserDelegate, - CoreUserProfileFieldDelegate, + providers: CORE_USER_PROVIDERS.concat([ CoreUserProfileMailHandler, - CoreUserProvider, - CoreUserHelperProvider, CoreUserProfileLinkHandler, CoreUserParticipantsCourseOptionHandler, CoreUserParticipantsLinkHandler - ] + ]) }) export class CoreUserModule { constructor(userDelegate: CoreUserDelegate, userProfileMailHandler: CoreUserProfileMailHandler, From ec4b76b439975a1848567889220a73098d66e5f8 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 13 Feb 2018 14:44:08 +0100 Subject: [PATCH 05/32] MOBILE-2333 compilehtml: Eval javascript and inject providers --- src/components/compile-html/compile-html.ts | 116 +++++++++++++++++++- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts index 68a4f24c0..2798df424 100644 --- a/src/components/compile-html/compile-html.ts +++ b/src/components/compile-html/compile-html.ts @@ -12,9 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, NgModule, Input, OnInit, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef } from '@angular/core'; -import { IonicModule } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; +import { + Component, NgModule, Input, OnInit, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector, 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'; @@ -23,15 +31,45 @@ import { CoreCoursesComponentsModule } from '../../core/courses/components/compo 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 + * 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', @@ -44,14 +82,25 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy { CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule ]; + // 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(protected compiler: Compiler) { } + constructor(logger: CoreLoggerProvider, protected compiler: Compiler, protected injector: Injector, + protected cdr: ChangeDetectorRef, protected navCtrl: NavController) { + this.logger = logger.getInstance('CoreCompileHtmlComponent'); + } /** * Component being initialized. @@ -59,7 +108,7 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy { ngOnInit(): void { if (this.text) { // Create a new component and a new module. - const component = Component({template: this.text})(class {}), + const component = this.createComponent(), module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {}); // Compile the module and the component. @@ -86,4 +135,59 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy { 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); + } + } + }); + } } From 5cb843793692989cdcd3a6b8998d43d942749554 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 14 Feb 2018 10:29:12 +0100 Subject: [PATCH 06/32] MOBILE-2333 siteaddons: Display site addon page --- src/components/compile-html/compile-html.ts | 14 ++- src/core/login/pages/reconnect/reconnect.ts | 2 + .../pages/addon-page/addon-page.html | 17 ++++ .../pages/addon-page/addon-page.module.ts | 36 ++++++++ .../siteaddons/pages/addon-page/addon-page.ts | 78 ++++++++++++++++ src/core/siteaddons/providers/siteaddons.ts | 91 +++++++++++++++++-- src/providers/addonmanager.ts | 7 +- 7 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 src/core/siteaddons/pages/addon-page/addon-page.html create mode 100644 src/core/siteaddons/pages/addon-page/addon-page.module.ts create mode 100644 src/core/siteaddons/pages/addon-page/addon-page.ts diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts index 2798df424..26b3cc480 100644 --- a/src/components/compile-html/compile-html.ts +++ b/src/components/compile-html/compile-html.ts @@ -13,7 +13,8 @@ // limitations under the License. import { - Component, NgModule, Input, OnInit, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector, ChangeDetectorRef + Component, NgModule, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector, + SimpleChange, ChangeDetectorRef } from '@angular/core'; import { IonicModule, NavController, Platform, ActionSheetController, AlertController, LoadingController, ModalController, @@ -75,7 +76,7 @@ import { Md5 } from 'ts-md5/dist/md5'; selector: 'core-compile-html', template: '' }) -export class CoreCompileHtmlComponent implements OnInit, OnDestroy { +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, @@ -103,10 +104,10 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy { } /** - * Component being initialized. + * Detect changes on input properties. */ - ngOnInit(): void { - if (this.text) { + 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 {}); @@ -123,6 +124,9 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy { } } + // Destroy previous components. + this.componentRef && this.componentRef.destroy(); + // Create the component. this.componentRef = this.container.createComponent(componentFactory); }); diff --git a/src/core/login/pages/reconnect/reconnect.ts b/src/core/login/pages/reconnect/reconnect.ts index 1dc684011..99e1ad423 100644 --- a/src/core/login/pages/reconnect/reconnect.ts +++ b/src/core/login/pages/reconnect/reconnect.ts @@ -88,6 +88,8 @@ export class CoreLoginReconnectPage { return site.getPublicConfig().then((config) => { this.logoUrl = config.logourl || config.compactlogourl; + }).catch(() => { + // Ignore errors. }); } }).catch(() => { diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteaddons/pages/addon-page/addon-page.html new file mode 100644 index 000000000..15297abc7 --- /dev/null +++ b/src/core/siteaddons/pages/addon-page/addon-page.html @@ -0,0 +1,17 @@ + + + {{ title }} + + + + + + + + + + + + + + diff --git a/src/core/siteaddons/pages/addon-page/addon-page.module.ts b/src/core/siteaddons/pages/addon-page/addon-page.module.ts new file mode 100644 index 000000000..f7dcf227a --- /dev/null +++ b/src/core/siteaddons/pages/addon-page/addon-page.module.ts @@ -0,0 +1,36 @@ +// (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 { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreSiteAddonsAddonPage } from './addon-page'; +import { CoreComponentsModule } from '../../../../components/components.module'; +import { CoreCompileHtmlComponentsModule } from '../../../../components/compile-html/compile-html.module'; + +/** + * Module to lazy load the page. + */ +@NgModule({ + declarations: [ + CoreSiteAddonsAddonPage + ], + imports: [ + CoreComponentsModule, + CoreCompileHtmlComponentsModule, + IonicPageModule.forChild(CoreSiteAddonsAddonPage), + TranslateModule.forChild() + ] +}) +export class CoreSiteAddonsAddonPageModule {} diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteaddons/pages/addon-page/addon-page.ts new file mode 100644 index 000000000..0ae787794 --- /dev/null +++ b/src/core/siteaddons/pages/addon-page/addon-page.ts @@ -0,0 +1,78 @@ +// (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 } from '@angular/core'; +import { IonicPage, NavParams } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; + +/** + * Page to render a site addon page. + */ +@IonicPage({ segment: 'core-site-addons-addon-page' }) +@Component({ + selector: 'page-core-site-addons-addon', + templateUrl: 'addon-page.html', +}) +export class CoreSiteAddonsAddonPage { + title: string; // Page title. + content: string; // Page content. + dataLoaded: boolean; + + protected component: string; + protected method: string; + protected args: any; + + constructor(params: NavParams, protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { + this.title = params.get('title'); + this.component = params.get('component'); + this.method = params.get('method'); + this.args = params.get('args'); + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + this.fetchContent().finally(() => { + this.dataLoaded = true; + }); + } + + /** + * Fetches the content of the page. + * + * @return {Promise} Promise resolved when done. + */ + fetchContent(): Promise { + return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { + this.content = result.html; + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); + }); + } + + /** + * Refresh the data. + * + * @param {any} refresher Refresher. + */ + refreshData(refresher: any): void { + this.siteAddonsProvider.invalidatePageContent(this.component, this.method, this.args).finally(() => { + this.fetchContent().finally(() => { + refresher.complete(); + }); + }); + } +} diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index e483a992c..d5e030e97 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -14,6 +14,7 @@ import { Injectable } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; +import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; @@ -24,6 +25,7 @@ import { } from '../../../core/course/providers/module-delegate'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; import { CoreDelegateHandler } from '../../../classes/delegate'; +import { CoreConfigConstants } from '../../../configconstants'; /** * Service to provide functionalities regarding site addons. @@ -36,7 +38,7 @@ export class CoreSiteAddonsProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, - private userDelegate: CoreUserDelegate) { + private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider) { this.logger = logger.getInstance('CoreUserProvider'); } @@ -55,6 +57,54 @@ export class CoreSiteAddonsProvider { }; } + /** + * Get a certain content for a site addon. + * + * @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 {string} [siteId] Site ID. If not defined, current site. + * @return {Promise<{html: string, javascript: string}>} Promise resolved with the content and the javascript. + */ + getContent(component: string, method: string, args: any, siteId?: string): Promise<{html: string, javascript: string}> { + this.logger.debug(`Get content for component '${component}' and method '${method}'`); + + return this.sitesProvider.getSite(siteId).then((site) => { + // Get current language to be added to params. + return this.langProvider.getCurrentLanguage().then((lang) => { + // Add some params that will always be sent. Clone the object so the original one isn't modified. + const argsToSend = this.utils.clone(args); + argsToSend.userid = args.userid || site.getUserId(); + argsToSend.appid = CoreConfigConstants.app_id; + argsToSend.versionname = CoreConfigConstants.versionname; + argsToSend.lang = lang; + + // Now call the WS. + const data = { + component: component, + method: method, + args: this.utils.objectToArrayOfObjects(argsToSend, 'name', 'value', true) + }, preSets = { + cacheKey: this.getContentCacheKey(component, method, args) + }; + + return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets); + }); + }); + } + + /** + * Get cache key for get content WS calls. + * + * @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. + * @return {string} Cache key. + */ + protected getContentCacheKey(component: string, method: string, args: any): string { + return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + JSON.stringify(args); + } + /** * Given a handler's unique name and the key of a string, return the full string key (prefixed). * @@ -81,6 +131,32 @@ export class CoreSiteAddonsProvider { return addon.addon + '_' + handlerName; } + /** + * Invalidate a page content. + * + * @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 {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidatePageContent(component: string, callback: string, args: any, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getContentCacheKey(component, callback, args)); + }); + } + + /** + * Check if the get content WS is available. + * + * @param {CoreSite} site The site to check. If not defined, current site. + */ + isGetContentAvailable(site?: CoreSite): boolean { + site = site || this.sitesProvider.getCurrentSite(); + + return site.wsAvailable('tool_mobile_get_content'); + } + /** * Check if a certain addon is a site addon and it's enabled in a certain site. * @@ -181,8 +257,7 @@ export class CoreSiteAddonsProvider { pageParams: { title: prefixedTitle, component: addon.component, - callback: handlerSchema.mainfunction, - contextId: handlerSchema.contextid + method: handlerSchema.method, } }; } @@ -223,10 +298,9 @@ export class CoreSiteAddonsProvider { navCtrl.push('CoreSiteAddonsAddonPage', { title: module.name, component: addon.component, - callback: handlerSchema.mainfunction, - contextId: handlerSchema.contextid, + method: handlerSchema.method, args: { - course: courseId, + courseid: courseId, cmid: module.id } }, options); @@ -282,10 +356,9 @@ export class CoreSiteAddonsProvider { navCtrl.push('CoreSiteAddonsAddonPage', { title: prefixedTitle, component: addon.component, - callback: handlerSchema.mainfunction, - contextId: handlerSchema.contextid, + method: handlerSchema.method, args: { - course: courseId, + courseid: courseId, userid: user.id } }); diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts index 106e3cc25..0b8b933c1 100644 --- a/src/providers/addonmanager.ts +++ b/src/providers/addonmanager.ts @@ -36,7 +36,7 @@ export class CoreAddonManagerProvider { const siteId = this.sitesProvider.getCurrentSiteId(); this.fetchSiteAddons(siteId).then((addons) => { // Addons fetched, check that site hasn't changed. - if (siteId == this.sitesProvider.getCurrentSiteId()) { + if (siteId == this.sitesProvider.getCurrentSiteId() && addons.length) { // Site is still the same. Load the addons and trigger the event. this.loadSiteAddons(addons); @@ -61,6 +61,11 @@ export class CoreAddonManagerProvider { const addons = []; return this.sitesProvider.getSite(siteId).then((site) => { + if (!this.siteAddonsProvider.isGetContentAvailable(site)) { + // Cannot load site addons, so there's no point to fetch them. + return addons; + } + // Get the list of addons. Try not to use cache. return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { data.plugins.forEach((addon: any) => { From 3f5ba683ef86a130bf916fc9d2c4773de9b49505 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 14 Feb 2018 12:39:19 +0100 Subject: [PATCH 07/32] MOBILE-2333 siteaddons: Support lang and fix custom strings --- src/core/siteaddons/providers/siteaddons.ts | 41 ++++- src/providers/lang.ts | 179 ++++++++++---------- 2 files changed, 122 insertions(+), 98 deletions(-) diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index d5e030e97..1633aae7c 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -105,6 +105,20 @@ export class CoreSiteAddonsProvider { return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + JSON.stringify(args); } + /** + * Given a handler's unique name, return the prefix to add to its string keys. + * + * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @return {string} Prefix. + */ + protected getHandlerPrefixForStrings(handlerName: string): string { + if (handlerName) { + return 'addon.' + handlerName + '.'; + } + + return ''; + } + /** * Given a handler's unique name and the key of a string, return the full string key (prefixed). * @@ -113,11 +127,7 @@ export class CoreSiteAddonsProvider { * @return {string} Full string key. */ protected getHandlerPrefixedString(handlerName: string, key: string): string { - if (name) { - return 'addon.' + handlerName + '.' + key; - } - - return ''; + return this.getHandlerPrefixForStrings(handlerName) + key; } /** @@ -181,6 +191,25 @@ export class CoreSiteAddonsProvider { return false; } + /** + * Load the lang strings for a handler. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + loadHandlerLangStrings(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema.lang) { + return; + } + + for (const lang in handlerSchema.lang) { + const prefix = this.getHandlerPrefixForStrings(this.getHandlerUniqueName(addon, handlerName)); + + this.langProvider.addSiteAddonsStrings(lang, handlerSchema.lang[lang], prefix); + } + } + /** * Load a site addon. * @@ -209,6 +238,8 @@ export class CoreSiteAddonsProvider { * @param {any} handlerSchema Data about the handler. */ registerHandler(addon: any, handlerName: string, handlerSchema: any): void { + this.loadHandlerLangStrings(addon, handlerName, handlerSchema); + switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': this.registerMainMenuHandler(addon, handlerName, handlerSchema); diff --git a/src/providers/lang.ts b/src/providers/lang.ts index f8b62fae2..71fc34ca0 100644 --- a/src/providers/lang.ts +++ b/src/providers/lang.ts @@ -28,8 +28,9 @@ import { Observable } from 'rxjs'; export class CoreLangProvider { protected fallbackLanguage = CoreConfigConstants.default_lang || 'en'; protected currentLanguage: string; // Save current language in a variable to speed up the get function. - protected customStrings = {}; + protected customStrings = {}; // Strings defined using the admin tool. protected customStringsRaw: string; + protected siteAddonsStrings = {}; // Strings defined by site addons. constructor(private translate: TranslateService, private configProvider: CoreConfigProvider, platform: Platform, private globalization: Globalization) { @@ -43,8 +44,47 @@ export class CoreLangProvider { moment.locale(language); }); }); + } - this.decorateTranslate(); + /** + * Add a set of site addons strings for a certain language. + * + * @param {string} lang The language where to add the strings. + * @param {any} strings Object with the strings to add. + * @param {string} [prefix] A prefix to add to all keys. + */ + addSiteAddonsStrings(lang: string, strings: any, prefix?: string): void { + // Initialize structures if they don't exist. + if (!this.siteAddonsStrings[lang]) { + this.siteAddonsStrings[lang] = {}; + } + if (!this.translate.translations[lang]) { + this.translate.translations[lang] = {}; + } + + for (const key in strings) { + const prefixedKey = prefix + key, + value = strings[key]; + + if (this.customStrings[lang] && this.customStrings[lang][prefixedKey]) { + // This string is overridden by a custom string, ignore it. + continue; + } + + if (!this.siteAddonsStrings[lang][prefixedKey]) { + // It's a new site addon string. Store the original value. + this.siteAddonsStrings[lang][prefixedKey] = { + original: this.translate.translations[lang][prefixedKey], + value: value + }; + } else { + // Site addon string already defined. Store the new value. + this.siteAddonsStrings[lang][prefixedKey].value = value; + } + + // Store the string in the translations table. + this.translate.translations[lang][prefixedKey] = value; + } } /** @@ -69,51 +109,17 @@ export class CoreLangProvider { * Clear current custom strings. */ clearCustomStrings(): void { + this.unloadStrings(this.customStrings); this.customStrings = {}; this.customStringsRaw = ''; } /** - * Function to "decorate" the TranslateService. - * Basically, it extends the translate functions to use the custom lang strings. + * Clear current site addons strings. */ - decorateTranslate(): void { - const originalGet = this.translate.get, - originalInstant = this.translate.instant; - - // Redefine translate.get. - this.translate.get = (key: string | string[], interpolateParams?: object): Observable => { - // Always call the original get function to avoid having to create our own Observables. - if (typeof key == 'string') { - const value = this.getCustomString(key); - if (typeof value != 'undefined') { - key = value; - } - } else { - key = this.getCustomStrings(key).translations; - } - - return originalGet.apply(this.translate, [key, interpolateParams]); - }; - - // Redefine translate.instant. - this.translate.instant = (key: string | string[], interpolateParams?: object): any => { - if (typeof key == 'string') { - const value = this.getCustomString(key); - if (typeof value != 'undefined') { - return value; - } - - return originalInstant.apply(this.translate, [key, interpolateParams]); - } else { - const result = this.getCustomStrings(key); - if (result.allFound) { - return result.translations; - } - - return originalInstant.apply(this.translate, [result.translations]); - } - }; + clearSiteAddonsStrings(): void { + this.unloadStrings(this.siteAddonsStrings); + this.siteAddonsStrings = {}; } /** @@ -125,6 +131,15 @@ export class CoreLangProvider { return this.customStrings; } + /** + * Get all current site addons strings. + * + * @return {any} Site addons strings. + */ + getAllSiteAddonsStrings(): any { + return this.siteAddonsStrings; + } + /** * Get current language. * @@ -174,57 +189,6 @@ export class CoreLangProvider { }); } - /** - * Get a custom string for a certain key. - * - * @param {string} key The key of the translation to get. - * @return {string} Translation, undefined if not found. - */ - getCustomString(key: string): string { - const customStrings = this.getCustomStringsForLanguage(); - if (customStrings && typeof customStrings[key] != 'undefined') { - return customStrings[key]; - } - } - - /** - * Get custom strings for several keys. - * - * @param {string[]} keys The keys of the translations to get. - * @return {any} Object with translations and a boolean indicating if all translations were found in custom strings. - */ - getCustomStrings(keys: string[]): any { - const customStrings = this.getCustomStringsForLanguage(), - translations = []; - let allFound = true; - - keys.forEach((key: string) => { - if (customStrings && typeof customStrings[key] != 'undefined') { - translations.push(customStrings[key]); - } else { - allFound = false; - translations.push(key); - } - }); - - return { - allFound: allFound, - translations: translations - }; - } - - /** - * Get custom strings for a certain language. - * - * @param {string} [lang] The language to get. If not defined, return current language. - * @return {any} Custom strings. - */ - getCustomStringsForLanguage(lang?: string): any { - lang = lang || this.currentLanguage; - - return this.customStrings[lang]; - } - /** * Load certain custom strings. * @@ -259,7 +223,36 @@ export class CoreLangProvider { this.customStrings[lang] = {}; } - this.customStrings[lang][values[0]] = values[1]; + // Store the original value of the custom string. + this.customStrings[lang][values[0]] = { + original: this.translate.translations[lang][values[0]], + value: values[1] + }; + + // Store the string in the translations table. + this.translate.translations[lang][values[0]] = values[1]; }); } + + /** + * Unload custom or site addon strings, removing the to the translations table. + * + * @param {any} strings Strings to unload. + */ + protected unloadStrings(strings: any): void { + // Iterate over all languages and strings. + for (const lang in strings) { + const langStrings = strings[lang]; + for (const key in langStrings) { + const entry = langStrings[key]; + if (entry.original) { + // The string had a value, restore it. + this.translate.translations[lang][key] = entry.original; + } else { + // The string didn't exist, delete it. + delete this.translate.translations[lang][key]; + } + } + } + } } From c3ca95e838350c3a5336cf52c5b9a12bde643743 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 15 Feb 2018 14:57:18 +0100 Subject: [PATCH 08/32] MOBILE-2333 siteaddons: Support singleactivity with site addons CoreSiteAddonsProvider provider code was split in two to prevent circular dependencies CoreSiteAddonsModuleIndexComponent --- .../addon-content/addon-content.html | 3 + .../components/addon-content/addon-content.ts | 68 ++++ .../components/components.module.ts | 44 +++ .../components/module-index/module-index.html | 2 + .../components/module-index/module-index.ts | 74 ++++ .../pages/addon-page/addon-page.html | 6 +- .../pages/addon-page/addon-page.module.ts | 2 + .../siteaddons/pages/addon-page/addon-page.ts | 35 +- src/core/siteaddons/providers/helper.ts | 339 ++++++++++++++++++ src/core/siteaddons/providers/siteaddons.ts | 321 ++--------------- src/core/siteaddons/siteaddons.module.ts | 6 +- src/providers/addonmanager.ts | 7 +- 12 files changed, 585 insertions(+), 322 deletions(-) create mode 100644 src/core/siteaddons/components/addon-content/addon-content.html create mode 100644 src/core/siteaddons/components/addon-content/addon-content.ts create mode 100644 src/core/siteaddons/components/components.module.ts create mode 100644 src/core/siteaddons/components/module-index/module-index.html create mode 100644 src/core/siteaddons/components/module-index/module-index.ts create mode 100644 src/core/siteaddons/providers/helper.ts diff --git a/src/core/siteaddons/components/addon-content/addon-content.html b/src/core/siteaddons/components/addon-content/addon-content.html new file mode 100644 index 000000000..3fb4aac18 --- /dev/null +++ b/src/core/siteaddons/components/addon-content/addon-content.html @@ -0,0 +1,3 @@ + + + diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteaddons/components/addon-content/addon-content.ts new file mode 100644 index 000000000..a842088d1 --- /dev/null +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -0,0 +1,68 @@ +// (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, OnInit, Input } from '@angular/core'; +import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; + +/** + * Component to render a site addon content. + */ +@Component({ + selector: 'core-site-addons-addon-content', + templateUrl: 'addon-content.html', +}) +export class CoreSiteAddonsAddonContentComponent implements OnInit { + @Input() component: string; + @Input() method: string; + @Input() args: any; + + content: string; // Content. + javascript: string; // Javascript to execute. + dataLoaded: boolean; + + constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.fetchContent().finally(() => { + this.dataLoaded = true; + }); + } + + /** + * Fetches the content to render. + * + * @return {Promise} Promise resolved when done. + */ + fetchContent(): Promise { + return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { + this.content = result.html; + this.javascript = result.javascript; + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); + }); + } + + /** + * Refresh the data. + */ + refreshData(): Promise { + return this.siteAddonsProvider.invalidateContent(this.component, this.method, this.args).finally(() => { + return this.fetchContent(); + }); + } +} diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts new file mode 100644 index 000000000..929c13dd5 --- /dev/null +++ b/src/core/siteaddons/components/components.module.ts @@ -0,0 +1,44 @@ +// (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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { CoreComponentsModule } from '../../../components/components.module'; +import { CoreCompileHtmlComponentsModule } from '../../../components/compile-html/compile-html.module'; +import { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-content'; +import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index'; + +@NgModule({ + declarations: [ + CoreSiteAddonsAddonContentComponent, + CoreSiteAddonsModuleIndexComponent + ], + imports: [ + CommonModule, + IonicModule, + CoreComponentsModule, + CoreCompileHtmlComponentsModule + ], + providers: [ + ], + exports: [ + CoreSiteAddonsAddonContentComponent, + CoreSiteAddonsModuleIndexComponent + ], + entryComponents: [ + CoreSiteAddonsModuleIndexComponent + ] +}) +export class CoreSiteAddonsComponentsModule {} diff --git a/src/core/siteaddons/components/module-index/module-index.html b/src/core/siteaddons/components/module-index/module-index.html new file mode 100644 index 000000000..72cb07d7c --- /dev/null +++ b/src/core/siteaddons/components/module-index/module-index.html @@ -0,0 +1,2 @@ + + diff --git a/src/core/siteaddons/components/module-index/module-index.ts b/src/core/siteaddons/components/module-index/module-index.ts new file mode 100644 index 000000000..b0c52f36f --- /dev/null +++ b/src/core/siteaddons/components/module-index/module-index.ts @@ -0,0 +1,74 @@ +// (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, OnInit, Input, ViewChild } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreCourseModuleMainComponent } from '../../../course/providers/module-delegate'; +import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; + +/** + * Component that displays the index of a module site addon. + */ +@Component({ + selector: 'core-site-addons-module-index', + templateUrl: 'module-index.html', +}) +export class CoreSiteAddonsModuleIndexComponent implements OnInit, CoreCourseModuleMainComponent { + @Input() module: any; // The module. + @Input() courseId: number; // Course ID the module belongs to. + + @ViewChild(CoreSiteAddonsAddonContentComponent) addonContent: CoreSiteAddonsAddonContentComponent; + + component: string; + method: string; + args: any; + + constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + if (this.module) { + const handler = this.siteAddonsProvider.getModuleSiteAddonHandler(this.module.modname); + if (handler) { + this.component = handler.addon.component; + this.method = handler.handlerSchema.method; + this.args = { + courseid: this.courseId, + cmid: this.module.id + }; + } + } + } + + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void): Promise { + if (this.addonContent) { + return Promise.resolve(this.addonContent.refreshData()).finally(() => { + refresher.complete(); + }); + } else { + refresher.complete(); + + return Promise.resolve(); + } + } +} diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteaddons/pages/addon-page/addon-page.html index 15297abc7..14555dcec 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.html +++ b/src/core/siteaddons/pages/addon-page/addon-page.html @@ -8,10 +8,8 @@ - + - - - + diff --git a/src/core/siteaddons/pages/addon-page/addon-page.module.ts b/src/core/siteaddons/pages/addon-page/addon-page.module.ts index f7dcf227a..021766bd3 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.module.ts +++ b/src/core/siteaddons/pages/addon-page/addon-page.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreSiteAddonsAddonPage } from './addon-page'; import { CoreComponentsModule } from '../../../../components/components.module'; import { CoreCompileHtmlComponentsModule } from '../../../../components/compile-html/compile-html.module'; +import { CoreSiteAddonsComponentsModule } from '../../components/components.module'; /** * Module to lazy load the page. @@ -29,6 +30,7 @@ import { CoreCompileHtmlComponentsModule } from '../../../../components/compile- imports: [ CoreComponentsModule, CoreCompileHtmlComponentsModule, + CoreSiteAddonsComponentsModule, IonicPageModule.forChild(CoreSiteAddonsAddonPage), TranslateModule.forChild() ] diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteaddons/pages/addon-page/addon-page.ts index 0ae787794..6501c247f 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.ts +++ b/src/core/siteaddons/pages/addon-page/addon-page.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; import { IonicPage, NavParams } from 'ionic-angular'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSiteAddonsAddonContentComponent } from '../../components/addon-content/addon-content'; /** * Page to render a site addon page. @@ -26,9 +27,9 @@ import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; templateUrl: 'addon-page.html', }) export class CoreSiteAddonsAddonPage { + @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; + title: string; // Page title. - content: string; // Page content. - dataLoaded: boolean; protected component: string; protected method: string; @@ -41,38 +42,14 @@ export class CoreSiteAddonsAddonPage { this.args = params.get('args'); } - /** - * View loaded. - */ - ionViewDidLoad(): void { - this.fetchContent().finally(() => { - this.dataLoaded = true; - }); - } - - /** - * Fetches the content of the page. - * - * @return {Promise} Promise resolved when done. - */ - fetchContent(): Promise { - return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { - this.content = result.html; - }).catch((error) => { - this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); - }); - } - /** * Refresh the data. * * @param {any} refresher Refresher. */ refreshData(refresher: any): void { - this.siteAddonsProvider.invalidatePageContent(this.component, this.method, this.args).finally(() => { - this.fetchContent().finally(() => { - refresher.complete(); - }); + this.content.refreshData().finally(() => { + refresher.complete(); }); } } diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts new file mode 100644 index 000000000..5e326759c --- /dev/null +++ b/src/core/siteaddons/providers/helper.ts @@ -0,0 +1,339 @@ +// (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 } from '@angular/core'; +import { NavController, NavOptions } from 'ionic-angular'; +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 { + CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData +} from '../../../core/course/providers/module-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; +import { CoreDelegateHandler } from '../../../classes/delegate'; +import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; +import { CoreSiteAddonsProvider } from './siteaddons'; + +/** + * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site + * addons. + * + * This code is split from CoreSiteAddonsProvider to prevent circular dependencies. + */ +@Injectable() +export class CoreSiteAddonsHelperProvider { + protected logger; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, + private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, + private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, + private siteAddonsProvider: CoreSiteAddonsProvider) { + this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); + } + + /** + * Create a base handler for a site addon. + * + * @param {string} name Name of the handler. + * @return {CoreDelegateHandler} The base handler. + */ + protected getBaseHandler(name: string): CoreDelegateHandler { + return { + name: name, + isEnabled: (): boolean => { + return true; + } + }; + } + + /** + * Given a handler's unique name, return the prefix to add to its string keys. + * + * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @return {string} Prefix. + */ + protected getHandlerPrefixForStrings(handlerName: string): string { + if (handlerName) { + return 'addon.' + handlerName + '.'; + } + + return ''; + } + + /** + * Given a handler's unique name and the key of a string, return the full string key (prefixed). + * + * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @param {string} key The key of the string. + * @return {string} Full string key. + */ + protected getHandlerPrefixedString(handlerName: string, key: string): string { + 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. + * + * @param {any} addon Data of the addon. + * @param {CoreSite} site Site affected. + * @return {boolean} Whether it's a site addon and it's enabled. + */ + isSiteAddonEnabled(addon: any, site: CoreSite): boolean { + if (!site.isFeatureDisabled('siteAddOn_' + addon.component + '_' + addon.addon) && addon.handlers) { + // Site addon not disabled. Check if it has handlers. + try { + if (!addon.parsedHandlers) { + addon.parsedHandlers = JSON.parse(addon.handlers); + } + + return !!(addon.parsedHandlers && Object.keys(addon.parsedHandlers).length); + } catch (ex) { + this.logger.warn('Error parsing site addon', ex); + } + } + + return false; + } + + /** + * Load the lang strings for a handler. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + loadHandlerLangStrings(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema.lang) { + return; + } + + for (const lang in handlerSchema.lang) { + const prefix = this.getHandlerPrefixForStrings(this.getHandlerUniqueName(addon, handlerName)); + + this.langProvider.addSiteAddonsStrings(lang, handlerSchema.lang[lang], prefix); + } + } + + /** + * Load a site addon. + * + * @param {any} addon Data of the addon. + */ + loadSiteAddon(addon: any): void { + try { + if (!addon.parsedHandlers) { + addon.parsedHandlers = JSON.parse(addon.handlers); + } + + // Register all the handlers. + for (const name in addon.parsedHandlers) { + this.registerHandler(addon, name, addon.parsedHandlers[name]); + } + } catch (ex) { + this.logger.warn('Error parsing site addon', ex); + } + } + + /** + * Register a site addon handler in the right delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + registerHandler(addon: any, handlerName: string, handlerSchema: any): void { + this.loadHandlerLangStrings(addon, handlerName, handlerSchema); + + switch (handlerSchema.delegate) { + case 'CoreMainMenuDelegate': + this.registerMainMenuHandler(addon, handlerName, handlerSchema); + break; + + case 'CoreCourseModuleDelegate': + this.registerModuleHandler(addon, handlerName, handlerSchema); + break; + + case 'CoreUserDelegate': + this.registerUserProfileHandler(addon, handlerName, handlerSchema); + break; + + default: + // Nothing to do. + } + } + + /** + * Given a handler in an addon, register it in the main menu delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); + let mainMenuHandler: CoreMainMenuHandler; + + // Extend the base handler, adding the properties required by the delegate. + mainMenuHandler = Object.assign(baseHandler, { + priority: handlerSchema.priority, + getDisplayData: (): CoreMainMenuHandlerData => { + return { + title: prefixedTitle, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + page: 'CoreSiteAddonsAddonPage', + pageParams: { + title: prefixedTitle, + component: addon.component, + method: handlerSchema.method, + } + }; + } + }); + + this.mainMenuDelegate.registerHandler(mainMenuHandler); + } + + /** + * Given a handler in an addon, register it in the module delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const modName = addon.component.replace('mod_', ''), + baseHandler = this.getBaseHandler(modName); + 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 => { + return { + title: module.name, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + showDownloadButton: handlerSchema.offlinefunctions && handlerSchema.offlinefunctions.length, + action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsAddonPage', { + title: module.name, + component: addon.component, + method: handlerSchema.method, + args: { + courseid: courseId, + cmid: module.id + } + }, options); + } + }; + }, + getMainComponent: (course: any, module: any): any => { + return CoreSiteAddonsModuleIndexComponent; + } + }); + + this.moduleDelegate.registerHandler(moduleHandler); + } + + /** + * Given a handler in an addon, register it in the user profile delegate. + * + * @param {any} addon Data of the addon. + * @param {string} handlerName Name of the handler in the addon. + * @param {any} handlerSchema Data about the handler. + */ + protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any): void { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), + prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); + let userHandler: CoreUserProfileHandler; + + // Extend the base handler, adding the properties required by the delegate. + userHandler = Object.assign(baseHandler, { + priority: handlerSchema.priority, + type: handlerSchema.type, + isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean => { + if (handlerSchema.restricted == 'current' && user.id != this.sitesProvider.getCurrentSite().getUserId()) { + return false; + } + + return true; + }, + getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { + return { + title: prefixedTitle, + icon: handlerSchema.displaydata.icon, + class: handlerSchema.displaydata.class, + action: (event: Event, navCtrl: NavController, user: any, courseId?: number): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsAddonPage', { + title: prefixedTitle, + component: addon.component, + method: handlerSchema.method, + args: { + courseid: courseId, + userid: user.id + } + }); + } + }; + } + }); + + this.userDelegate.registerHandler(userHandler); + } +} diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 1633aae7c..f4b4a0ea5 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -13,20 +13,36 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../../core/mainmenu/providers/delegate'; -import { - CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData -} from '../../../core/course/providers/module-delegate'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate'; -import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreConfigConstants } from '../../../configconstants'; +/** + * Handler of a site addon representing a module. + */ +export interface CoreSiteAddonsModuleHandler { + /** + * The site addon data. + * @type {any} + */ + addon: any; + + /** + * Name of the handler. + * @type {string} + */ + handlerName: string; + + /** + * Data of the handler. + * @type {any} + */ + handlerSchema: any; +} + /** * Service to provide functionalities regarding site addons. */ @@ -35,28 +51,13 @@ export class CoreSiteAddonsProvider { protected ROOT_CACHE_KEY = 'CoreSiteAddons:'; protected logger; + protected moduleSiteAddons: {[modName: string]: CoreSiteAddonsModuleHandler} = {}; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, - private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider) { + private langProvider: CoreLangProvider) { this.logger = logger.getInstance('CoreUserProvider'); } - /** - * Create a base handler for a site addon. - * - * @param {string} name Name of the handler. - * @return {CoreDelegateHandler} The base handler. - */ - protected getBaseHandler(name: string): CoreDelegateHandler { - return { - name: name, - isEnabled: (): boolean => { - return true; - } - }; - } - /** * Get a certain content for a site addon. * @@ -106,39 +107,13 @@ export class CoreSiteAddonsProvider { } /** - * Given a handler's unique name, return the prefix to add to its string keys. + * Get the site addon handler for a certain module. * - * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). - * @return {string} Prefix. + * @param {string} modName Name of the module. + * @return {CoreSiteAddonsModuleHandler} Handler. */ - protected getHandlerPrefixForStrings(handlerName: string): string { - if (handlerName) { - return 'addon.' + handlerName + '.'; - } - - return ''; - } - - /** - * Given a handler's unique name and the key of a string, return the full string key (prefixed). - * - * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). - * @param {string} key The key of the string. - * @return {string} Full string key. - */ - protected getHandlerPrefixedString(handlerName: string, key: string): string { - 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; + getModuleSiteAddonHandler(modName: string): CoreSiteAddonsModuleHandler { + return this.moduleSiteAddons[modName]; } /** @@ -150,7 +125,7 @@ export class CoreSiteAddonsProvider { * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the data is invalidated. */ - invalidatePageContent(component: string, callback: string, args: any, siteId?: string): Promise { + invalidateContent(component: string, callback: string, args: any, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { return site.invalidateWsCacheForKey(this.getContentCacheKey(component, callback, args)); }); @@ -168,236 +143,12 @@ export class CoreSiteAddonsProvider { } /** - * Check if a certain addon is a site addon and it's enabled in a certain site. + * Set the site addon handler for a certain module. * - * @param {any} addon Data of the addon. - * @param {CoreSite} site Site affected. - * @return {boolean} Whether it's a site addon and it's enabled. + * @param {string} modName Name of the module. + * @param {CoreSiteAddonsModuleHandler} handler Handler to set. */ - isSiteAddonEnabled(addon: any, site: CoreSite): boolean { - if (!site.isFeatureDisabled('siteAddOn_' + addon.component + '_' + addon.addon) && addon.handlers) { - // Site addon not disabled. Check if it has handlers. - try { - if (!addon.parsedHandlers) { - addon.parsedHandlers = JSON.parse(addon.handlers); - } - - return !!(addon.parsedHandlers && Object.keys(addon.parsedHandlers).length); - } catch (ex) { - this.logger.warn('Error parsing site addon', ex); - } - } - - return false; - } - - /** - * Load the lang strings for a handler. - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. - * @param {any} handlerSchema Data about the handler. - */ - loadHandlerLangStrings(addon: any, handlerName: string, handlerSchema: any): void { - if (!handlerSchema.lang) { - return; - } - - for (const lang in handlerSchema.lang) { - const prefix = this.getHandlerPrefixForStrings(this.getHandlerUniqueName(addon, handlerName)); - - this.langProvider.addSiteAddonsStrings(lang, handlerSchema.lang[lang], prefix); - } - } - - /** - * Load a site addon. - * - * @param {any} addon Data of the addon. - */ - loadSiteAddon(addon: any): void { - try { - if (!addon.parsedHandlers) { - addon.parsedHandlers = JSON.parse(addon.handlers); - } - - // Register all the handlers. - for (const name in addon.parsedHandlers) { - this.registerHandler(addon, name, addon.parsedHandlers[name]); - } - } catch (ex) { - this.logger.warn('Error parsing site addon', ex); - } - } - - /** - * Register a site addon handler in the right delegate. - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. - * @param {any} handlerSchema Data about the handler. - */ - registerHandler(addon: any, handlerName: string, handlerSchema: any): void { - this.loadHandlerLangStrings(addon, handlerName, handlerSchema); - - switch (handlerSchema.delegate) { - case 'CoreMainMenuDelegate': - this.registerMainMenuHandler(addon, handlerName, handlerSchema); - break; - - case 'CoreCourseModuleDelegate': - this.registerModuleHandler(addon, handlerName, handlerSchema); - break; - - case 'CoreUserDelegate': - this.registerUserProfileHandler(addon, handlerName, handlerSchema); - break; - - default: - // Nothing to do. - } - } - - /** - * Given a handler in an addon, register it in the main menu delegate. - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. - * @param {any} handlerSchema Data about the handler. - */ - protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any): void { - if (!handlerSchema || !handlerSchema.displaydata) { - // Required data not provided, stop. - return; - } - - // Create the base handler. - const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), - prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let mainMenuHandler: CoreMainMenuHandler; - - // Extend the base handler, adding the properties required by the delegate. - mainMenuHandler = Object.assign(baseHandler, { - priority: handlerSchema.priority, - getDisplayData: (): CoreMainMenuHandlerData => { - return { - title: prefixedTitle, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - page: 'CoreSiteAddonsAddonPage', - pageParams: { - title: prefixedTitle, - component: addon.component, - method: handlerSchema.method, - } - }; - } - }); - - this.mainMenuDelegate.registerHandler(mainMenuHandler); - } - - /** - * Given a handler in an addon, register it in the module delegate. - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. - * @param {any} handlerSchema Data about the handler. - */ - protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any): void { - if (!handlerSchema || !handlerSchema.displaydata) { - // Required data not provided, stop. - return; - } - - // Create the base handler. - const baseHandler = this.getBaseHandler(addon.component.replace('mod_', '')); - let moduleHandler: CoreCourseModuleHandler; - - // Extend the base handler, adding the properties required by the delegate. - moduleHandler = Object.assign(baseHandler, { - getData: (module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData => { - return { - title: module.name, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - showDownloadButton: handlerSchema.offlinefunctions && handlerSchema.offlinefunctions.length, - action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { - event.preventDefault(); - event.stopPropagation(); - - navCtrl.push('CoreSiteAddonsAddonPage', { - title: module.name, - component: addon.component, - method: handlerSchema.method, - args: { - courseid: courseId, - cmid: module.id - } - }, options); - } - }; - }, - getMainComponent: (course: any, module: any): any => { - // Singleactivity course format not supported with site addons. - } - }); - - this.moduleDelegate.registerHandler(moduleHandler); - } - - /** - * Given a handler in an addon, register it in the user profile delegate. - * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. - * @param {any} handlerSchema Data about the handler. - */ - protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any): void { - if (!handlerSchema || !handlerSchema.displaydata) { - // Required data not provided, stop. - return; - } - - // Create the base handler. - const baseHandler = this.getBaseHandler(this.getHandlerUniqueName(addon, handlerName)), - prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let userHandler: CoreUserProfileHandler; - - // Extend the base handler, adding the properties required by the delegate. - userHandler = Object.assign(baseHandler, { - priority: handlerSchema.priority, - type: handlerSchema.type, - isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean => { - if (handlerSchema.restricted == 'current' && user.id != this.sitesProvider.getCurrentSite().getUserId()) { - return false; - } - - return true; - }, - getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { - return { - title: prefixedTitle, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - action: (event: Event, navCtrl: NavController, user: any, courseId?: number): void => { - event.preventDefault(); - event.stopPropagation(); - - navCtrl.push('CoreSiteAddonsAddonPage', { - title: prefixedTitle, - component: addon.component, - method: handlerSchema.method, - args: { - courseid: courseId, - userid: user.id - } - }); - } - }; - } - }); - - this.userDelegate.registerHandler(userHandler); + setModuleSiteAddonHandler(modName: string, handler: CoreSiteAddonsModuleHandler): void { + this.moduleSiteAddons[modName] = handler; } } diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteaddons/siteaddons.module.ts index f7f7f9298..053905ad3 100644 --- a/src/core/siteaddons/siteaddons.module.ts +++ b/src/core/siteaddons/siteaddons.module.ts @@ -15,16 +15,20 @@ 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'; // List of providers. export const CORE_SITEADDONS_PROVIDERS = [ - CoreSiteAddonsProvider + CoreSiteAddonsProvider, + CoreSiteAddonsHelperProvider ]; @NgModule({ declarations: [ ], imports: [ + CoreSiteAddonsComponentsModule ], providers: CORE_SITEADDONS_PROVIDERS }) diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts index 0b8b933c1..53e5092f1 100644 --- a/src/providers/addonmanager.ts +++ b/src/providers/addonmanager.ts @@ -18,6 +18,7 @@ import { CoreLoggerProvider } from './logger'; import { CoreSitesProvider } from './sites'; import { CoreSiteWSPreSets } from '../classes/site'; import { CoreSiteAddonsProvider } from '../core/siteaddons/providers/siteaddons'; +import { CoreSiteAddonsHelperProvider } from '../core/siteaddons/providers/helper'; /** * Provider with some helper functions regarding addons. @@ -28,7 +29,7 @@ export class CoreAddonManagerProvider { protected logger; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - private siteAddonsProvider: CoreSiteAddonsProvider) { + private siteAddonsProvider: CoreSiteAddonsProvider, private siteAddonsHelperProvider: CoreSiteAddonsHelperProvider) { logger = logger.getInstance('CoreAddonManagerProvider'); // Fetch the addons on login. @@ -70,7 +71,7 @@ export class CoreAddonManagerProvider { return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { data.plugins.forEach((addon: any) => { // Check if it's a site addon and it's enabled. - if (this.siteAddonsProvider.isSiteAddonEnabled(addon, site)) { + if (this.siteAddonsHelperProvider.isSiteAddonEnabled(addon, site)) { addons.push(addon); } }); @@ -87,7 +88,7 @@ export class CoreAddonManagerProvider { */ loadSiteAddons(addons: any[]): void { addons.forEach((addon) => { - this.siteAddonsProvider.loadSiteAddon(addon); + this.siteAddonsHelperProvider.loadSiteAddon(addon); }); } } From dc88b83bbb57b40dbfb4d26fff03690133aae8a0 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 16 Feb 2018 11:34:29 +0100 Subject: [PATCH 09/32] MOBILE-2333 siteaddons: Prefetch offlinefunctions --- .../mod/book/providers/prefetch-handler.ts | 16 -- .../course/classes/module-prefetch-handler.ts | 14 +- .../providers/module-prefetch-delegate.ts | 4 +- .../classes/module-prefetch-handler.ts | 224 ++++++++++++++++++ src/core/siteaddons/providers/helper.ts | 19 +- src/core/siteaddons/providers/siteaddons.ts | 70 +++++- src/providers/filepool.ts | 18 +- 7 files changed, 331 insertions(+), 34 deletions(-) create mode 100644 src/core/siteaddons/classes/module-prefetch-handler.ts diff --git a/src/addon/mod/book/providers/prefetch-handler.ts b/src/addon/mod/book/providers/prefetch-handler.ts index ca99a83fc..17aa4b636 100644 --- a/src/addon/mod/book/providers/prefetch-handler.ts +++ b/src/addon/mod/book/providers/prefetch-handler.ts @@ -78,22 +78,6 @@ export class AddonModBookPrefetchHandler extends CoreCourseModulePrefetchHandler return this.bookProvider.invalidateContent(moduleId, courseId); } - /** - * Invalidate WS calls needed to determine module status. - * - * @param {any} module Module. - * @param {number} courseId Course ID the module belongs to. - * @return {Promise} Promise resolved when invalidated. - */ - invalidateModule(module: any, courseId: number): Promise { - const promises = []; - - promises.push(this.bookProvider.invalidateBookData(courseId)); - promises.push(this.courseProvider.invalidateModule(module.id)); - - return Promise.all(promises); - } - /** * Whether or not the handler is enabled on a site level. * diff --git a/src/core/course/classes/module-prefetch-handler.ts b/src/core/course/classes/module-prefetch-handler.ts index 7fde10f08..c078e1b6a 100644 --- a/src/core/course/classes/module-prefetch-handler.ts +++ b/src/core/course/classes/module-prefetch-handler.ts @@ -133,10 +133,11 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref * * @param {any} module The module object returned by WS. * @param {number} courseId Course ID. + * @param {string} [dirPath] Path of the directory where to store all the content files. @see downloadOrPrefetch. * @return {Promise} Promise resolved when all content is downloaded. */ - download(module: any, courseId: number): Promise { - return this.downloadOrPrefetch(module, courseId, false); + download(module: any, courseId: number, dirPath?: string): Promise { + return this.downloadOrPrefetch(module, courseId, false, dirPath); } /** @@ -332,8 +333,8 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref } /** - * Invalidate WS calls needed to determine module status. It doesn't need to invalidate check updates. - * It should NOT invalidate files nor all the prefetched data. + * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). + * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. * * @param {any} module Module. * @param {number} courseId Course ID the module belongs to. @@ -409,10 +410,11 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref * @param {any} module Module. * @param {number} courseId Course ID the module belongs to. * @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section. + * @param {string} [dirPath] Path of the directory where to store all the content files. @see downloadOrPrefetch. * @return {Promise} Promise resolved when done. */ - prefetch(module: any, courseId?: number, single?: boolean): Promise { - return this.downloadOrPrefetch(module, courseId, true); + prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise { + return this.downloadOrPrefetch(module, courseId, true, dirPath); } /** diff --git a/src/core/course/providers/module-prefetch-delegate.ts b/src/core/course/providers/module-prefetch-delegate.ts index 76589a93e..dce46a02a 100644 --- a/src/core/course/providers/module-prefetch-delegate.ts +++ b/src/core/course/providers/module-prefetch-delegate.ts @@ -141,8 +141,8 @@ export interface CoreCourseModulePrefetchHandler extends CoreDelegateHandler { hasUpdates?(module: any, courseId: number, moduleUpdates: any[]): boolean | Promise; /** - * Invalidate WS calls needed to determine module status. It doesn't need to invalidate check updates. - * It should NOT invalidate files nor all the prefetched data. + * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable). + * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data. * * @param {any} module Module. * @param {number} courseId Course ID the module belongs to. diff --git a/src/core/siteaddons/classes/module-prefetch-handler.ts b/src/core/siteaddons/classes/module-prefetch-handler.ts new file mode 100644 index 000000000..3972f0d56 --- /dev/null +++ b/src/core/siteaddons/classes/module-prefetch-handler.ts @@ -0,0 +1,224 @@ +// (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 { Injector } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../../siteaddons/providers/siteaddons'; +import { CoreCourseModulePrefetchHandlerBase } from '../../course/classes/module-prefetch-handler'; + +/** + * Handler to prefetch a site addon. + */ +export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetchHandlerBase { + protected ROOT_CACHE_KEY = 'CoreSiteAddonsModulePrefetchHandler:'; + + constructor(injector: Injector, protected siteAddonsProvider: CoreSiteAddonsProvider, component: string, modName: string, + protected handlerSchema: any) { + super(injector); + + this.component = component; + this.name = modName; + this.isResource = handlerSchema.isresource; + + if (handlerSchema.updatesnames) { + try { + this.updatesNames = new RegExp(handlerSchema.updatesnames); + } catch (ex) { + // Ignore errors. + } + } + } + + /** + * Download or prefetch the content. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {boolean} [prefetch] True to prefetch, false to download right away. + * @param {string} [dirPath] Path of the directory where to store all the content files. This is to keep the files + * relative paths and make the package work in an iframe. Undefined to download the files + * in the filepool root folder. + * @return {Promise} Promise resolved when all content is downloaded. Data returned is not reliable. + */ + downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { + return this.prefetchPackage(module, courseId, false, this.downloadOrPrefetchAddon.bind(this), undefined, prefetch, dirPath); + } + + /** + * Download or prefetch the addon, downloading the files and calling the needed WS. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section. + * @param {string} [siteId] Site ID. If not defined, current site. + * @param {boolean} [prefetch] True to prefetch, false to download right away. + * @param {string} [dirPath] Path of the directory where to store all the content files. @see downloadOrPrefetch. + * @return {Promise} Promise resolved when done. + */ + protected downloadOrPrefetchAddon(module: any, courseId: number, single?: boolean, siteId?: string, prefetch?: boolean, + dirPath?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + + const promises = [], + args = { + courseid: courseId, + cmid: module.id, + userid: site.getUserId() + }; + + // Download the files (if any). + promises.push(this.downloadOrPrefetchFiles(site.id, module, courseId, prefetch, dirPath)); + + // Call all the offline functions. + for (const method in this.handlerSchema.offlinefunctions) { + if (site.wsAvailable(method)) { + // The method is a WS. + const paramsList = this.handlerSchema.offlinefunctions[method], + cacheKey = this.siteAddonsProvider.getCallWSCacheKey(method, args); + let params = {}; + + if (!paramsList.length) { + // No params defined, send the default ones. + params = args; + } else { + for (const i in paramsList) { + const paramName = paramsList[i]; + + if (typeof args[paramName] != 'undefined') { + params[paramName] = args[paramName]; + } else { + // The param is not one of the default ones. Try to calculate the param to use. + const value = this.getDownloadParam(module, courseId, paramName); + if (typeof value != 'undefined') { + params[paramName] = value; + } + } + } + } + + promises.push(this.siteAddonsProvider.callWS(method, params, {cacheKey: cacheKey})); + } else { + // It's a method to get content. + promises.push(this.siteAddonsProvider.getContent(this.component, method, args)); + } + } + + return Promise.all(promises); + }); + } + + /** + * Download or prefetch the addon files. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {boolean} [prefetch] True to prefetch, false to download right away. + * @param {string} [dirPath] Path of the directory where to store all the content files. @see downloadOrPrefetch. + * @return {Promise} Promise resolved when done. + */ + protected downloadOrPrefetchFiles(siteId: string, module: any, courseId: number, prefetch?: boolean, dirPath?: string) + : Promise { + // Load module contents (ignore cache so we always have the latest data). + return this.loadContents(module, courseId, true).then(() => { + // Get the intro files. + return this.getIntroFiles(module, courseId); + }).then((introFiles) => { + const contentFiles = this.getContentDownloadableFiles(module), + promises = []; + + if (dirPath) { + // Download intro files in filepool root folder. + promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, introFiles, prefetch, false, + this.component, module.id)); + + // Download content files inside dirPath. + promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, contentFiles, prefetch, false, + this.component, module.id, dirPath)); + } else { + // No dirPath, download everything in filepool root folder. + const files = introFiles.concat(contentFiles); + promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, files, prefetch, false, + this.component, module.id)); + } + + return Promise.all(promises); + }); + } + + /** + * Get the value of a WS param for prefetch. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {string} paramName Name of the param as defined by the handler. + * @return {any} The value. + */ + protected getDownloadParam(module: any, courseId: number, paramName: string): any { + switch (paramName) { + case 'courseids': + // The WS needs the list of course IDs. Create the list. + return [courseId]; + + case this.component + 'id': + // The WS needs the instance id. + return module.instance; + + default: + // No more params supported for now. + } + } + + /** + * Invalidate the prefetched content. + * + * @param {number} moduleId The module ID. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateContent(moduleId: number, courseId: number): Promise { + const promises = [], + currentSite = this.sitesProvider.getCurrentSite(), + siteId = currentSite.getId(), + args = { + courseid: courseId, + cmid: moduleId, + userid: currentSite.getUserId() + }; + + // Invalidate files and the module. + promises.push(this.filepoolProvider.invalidateFilesByComponent(siteId, this.component, moduleId)); + promises.push(this.courseProvider.invalidateModule(moduleId, siteId)); + + // Also invalidate all the WS calls. + for (const method in this.handlerSchema.offlinefunctions) { + if (currentSite.wsAvailable(method)) { + // The method is a WS. + promises.push(currentSite.invalidateWsCacheForKey(this.siteAddonsProvider.getCallWSCacheKey(method, args))); + } else { + // It's a method to get content. + promises.push(this.siteAddonsProvider.invalidateContent(this.component, method, args)); + } + } + + return this.utils.allPromises(promises); + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 5e326759c..463f5b079 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; @@ -22,10 +22,12 @@ import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } fr 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'; import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; import { CoreSiteAddonsProvider } from './siteaddons'; +import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; /** * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site @@ -37,10 +39,10 @@ import { CoreSiteAddonsProvider } from './siteaddons'; export class CoreSiteAddonsHelperProvider { protected logger; - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, + 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 siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } @@ -240,7 +242,8 @@ export class CoreSiteAddonsHelperProvider { // Create the base handler. const modName = addon.component.replace('mod_', ''), - baseHandler = this.getBaseHandler(modName); + baseHandler = this.getBaseHandler(modName), + hasOfflineFunctions = !!(handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length); let moduleHandler: CoreCourseModuleHandler; // Store the handler data. @@ -257,7 +260,7 @@ export class CoreSiteAddonsHelperProvider { title: module.name, icon: handlerSchema.displaydata.icon, class: handlerSchema.displaydata.class, - showDownloadButton: handlerSchema.offlinefunctions && handlerSchema.offlinefunctions.length, + showDownloadButton: hasOfflineFunctions, action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { event.preventDefault(); event.stopPropagation(); @@ -279,6 +282,12 @@ export class CoreSiteAddonsHelperProvider { } }); + if (hasOfflineFunctions) { + // Register the prefetch handler. + this.prefetchDelegate.registerHandler(new CoreSiteAddonsModulePrefetchHandler( + this.injector, this.siteAddonsProvider, addon.component, modName, handlerSchema)); + } + this.moduleDelegate.registerHandler(moduleHandler); } diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index f4b4a0ea5..fdd3dff6d 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; -import { CoreSite } from '../../../classes/site'; +import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreConfigConstants } from '../../../configconstants'; @@ -58,6 +58,45 @@ export class CoreSiteAddonsProvider { this.logger = logger.getInstance('CoreUserProvider'); } + /** + * Call a WS for a site addon. + * + * @param {string} method WS method to use. + * @param {any} data Data to send to the WS. + * @param {CoreSiteWSPreSets} [preSets] Extra options. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the response. + */ + callWS(method: string, data: any, preSets?: CoreSiteWSPreSets, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + preSets = preSets || {}; + preSets.cacheKey = preSets.cacheKey || this.getCallWSCacheKey(method, data); + + return site.read(method, data, preSets); + }); + } + + /** + * Get cache key for a WS call. + * + * @param {string} method Name of the method. + * @param {any} data Data to identify the WS call. + * @return {string} Cache key. + */ + getCallWSCacheKey(method: string, data: any): string { + return this.getCallWSCommonCacheKey(method) + ':' + this.utils.sortAndStringify(data); + } + + /** + * Get common cache key for a WS call. + * + * @param {string} method Name of the method. + * @return {string} Cache key. + */ + protected getCallWSCommonCacheKey(method: string): string { + return this.ROOT_CACHE_KEY + method; + } + /** * Get a certain content for a site addon. * @@ -103,7 +142,7 @@ export class CoreSiteAddonsProvider { * @return {string} Cache key. */ protected getContentCacheKey(component: string, method: string, args: any): string { - return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + JSON.stringify(args); + return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + this.utils.sortAndStringify(args); } /** @@ -116,6 +155,33 @@ export class CoreSiteAddonsProvider { return this.moduleSiteAddons[modName]; } + /** + * Invalidate all WS call to a certain method. + * + * @param {string} method WS method to use. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateAllCallWSForMethod(method: string, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getCallWSCommonCacheKey(method)); + }); + } + + /** + * Invalidate a WS call. + * + * @param {string} method WS method to use. + * @param {any} data Data to send to the WS. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCallWS(method: string, data: any, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCallWSCacheKey(method, data)); + }); + } + /** * Invalidate a page content. * diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index cc677fef9..259ba617f 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -961,10 +961,12 @@ export class CoreFilepoolProvider { * @param {boolean} [ignoreStale] True if 'stale' should be ignored. Only if prefetch=false. * @param {string} [component] The component to link the file to. * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {string} [dirPath] Name of the directory where to store the files (inside filepool dir). If not defined, store + * the files directly inside the filepool folder. * @return {Promise} Resolved on success. */ downloadOrPrefetchFiles(siteId: string, files: any[], prefetch: boolean, ignoreStale?: boolean, component?: string, - componentId?: string | number): Promise { + componentId?: string | number, dirPath?: string): Promise { const promises = []; // Download files. @@ -975,13 +977,23 @@ export class CoreFilepoolProvider { isexternalfile: file.isexternalfile, repositorytype: file.repositorytype }; + let path; + + if (dirPath) { + // Calculate the path to the file. + path = file.filename; + if (file.filepath !== '/') { + path = file.filepath.substr(1) + path; + } + path = this.textUtils.concatenatePaths(dirPath, path); + } if (prefetch) { promises.push(this.addToQueueByUrl( - siteId, url, component, componentId, timemodified, undefined, undefined, 0, options)); + siteId, url, component, componentId, timemodified, path, undefined, 0, options)); } else { promises.push(this.downloadUrl( - siteId, url, ignoreStale, component, componentId, timemodified, undefined, undefined, options)); + siteId, url, ignoreStale, component, componentId, timemodified, path, undefined, options)); } }); From adbee2991c44bff4aeee698db5b0fd92dfd9fcf9 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 15 Feb 2018 10:06:57 +0100 Subject: [PATCH 10/32] MOBILE-2333 core: Implement download-file directive --- src/app/app.module.ts | 4 +- src/components/file/file.ts | 135 ++-------------- src/directives/directives.module.ts | 3 + src/directives/download-file.ts | 60 +++++++ src/providers/file-helper.ts | 240 ++++++++++++++++++++++++++++ 5 files changed, 322 insertions(+), 120 deletions(-) create mode 100644 src/directives/download-file.ts create mode 100644 src/providers/file-helper.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 893edcb9c..dae354861 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -50,6 +50,7 @@ import { CoreUpdateManagerProvider } from '@providers/update-manager'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CoreSyncProvider } from '@providers/sync'; import { CoreAddonManagerProvider } from '@providers/addonmanager'; +import { CoreFileHelperProvider } from '@providers/file-helper'; // Core modules. import { CoreComponentsModule } from '@components/components.module'; @@ -108,7 +109,8 @@ export const CORE_PROVIDERS: any[] = [ CoreUpdateManagerProvider, CorePluginFileDelegate, CoreSyncProvider, - CoreAddonManagerProvider + CoreAddonManagerProvider, + CoreFileHelperProvider ]; @NgModule({ diff --git a/src/components/file/file.ts b/src/components/file/file.ts index 20023d454..7ed4d61c6 100644 --- a/src/components/file/file.ts +++ b/src/components/file/file.ts @@ -13,11 +13,10 @@ // limitations under the License. import { Component, Input, Output, OnInit, OnDestroy, EventEmitter } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; -import { CoreFileProvider } from '@providers/file'; import { CoreFilepoolProvider } from '@providers/filepool'; +import { CoreFileHelperProvider } from '@providers/file-helper'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; @@ -36,7 +35,6 @@ export class CoreFileComponent implements OnInit, OnDestroy { @Input() file: any; // The file. Must have a property 'filename' and a 'fileurl' or 'url' @Input() component?: string; // Component the file belongs to. @Input() componentId?: string | number; // Component ID. - @Input() timemodified?: number; // If set, the value will be used to check if the file is outdated. @Input() canDelete?: boolean | string; // Whether file can be deleted. @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded. // Use it for files that you cannot determine if they're outdated or not. @@ -52,12 +50,14 @@ export class CoreFileComponent implements OnInit, OnDestroy { protected fileUrl: string; protected siteId: string; protected fileSize: number; + protected state: string; + protected timemodified: number; protected observer; - constructor(private translate: TranslateService, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private domUtils: CoreDomUtilsProvider, private filepoolProvider: CoreFilepoolProvider, - private fileProvider: CoreFileProvider, private appProvider: CoreAppProvider, - private mimeUtils: CoreMimetypeUtilsProvider, private eventsProvider: CoreEventsProvider) { + constructor(private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private domUtils: CoreDomUtilsProvider, + private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider, + private fileHelper: CoreFileHelperProvider, private mimeUtils: CoreMimetypeUtilsProvider, + private eventsProvider: CoreEventsProvider) { this.onDelete = new EventEmitter(); } @@ -68,9 +68,9 @@ export class CoreFileComponent implements OnInit, OnDestroy { this.canDelete = this.utils.isTrueOrOne(this.canDelete); this.alwaysDownload = this.utils.isTrueOrOne(this.alwaysDownload); this.canDownload = this.utils.isTrueOrOne(this.canDownload); - this.timemodified = this.timemodified || 0; - this.fileUrl = this.file.fileurl || this.file.url; + this.fileUrl = this.fileHelper.getFileUrl(this.file); + this.timemodified = this.fileHelper.getFileTimemodified(this.file); this.siteId = this.sitesProvider.getCurrentSiteId(); this.fileSize = this.file.filesize; this.fileName = this.file.filename; @@ -102,6 +102,7 @@ export class CoreFileComponent implements OnInit, OnDestroy { return this.filepoolProvider.getFileStateByUrl(this.siteId, this.fileUrl, this.timemodified).then((state) => { const canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles(); + this.state = state; this.isDownloaded = state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED; this.isDownloading = canDownload && state === CoreConstants.DOWNLOADING; this.showDownload = canDownload && (state === CoreConstants.NOT_DOWNLOADED || state === CoreConstants.OUTDATED || @@ -109,123 +110,19 @@ export class CoreFileComponent implements OnInit, OnDestroy { }); } - /** - * Download the file. - * - * @return {Promise} Promise resolved when file is downloaded. - */ - protected downloadFile(): Promise { - if (!this.sitesProvider.getCurrentSite().canDownloadFiles()) { - this.domUtils.showErrorModal('core.cannotdownloadfiles', true); - - return Promise.reject(null); - } - - this.isDownloading = true; - - return this.filepoolProvider.downloadUrl(this.siteId, this.fileUrl, false, this.component, this.componentId, - this.timemodified, undefined, undefined, this.file).catch(() => { - - // Call calculateState to make sure we have the right state. - return this.calculateState().then(() => { - if (this.isDownloaded) { - return this.filepoolProvider.getInternalUrlByUrl(this.siteId, this.fileUrl); - } else { - return Promise.reject(null); - } - }); - }); - } - /** * Convenience function to open a file, downloading it if needed. * * @return {Promise} Promise resolved when file is opened. */ protected openFile(): Promise { - const fixedUrl = this.sitesProvider.getCurrentSite().fixPluginfileURL(this.fileUrl); - let promise; - - if (this.fileProvider.isAvailable()) { - promise = Promise.resolve().then(() => { - // The file system is available. - const isWifi = !this.appProvider.isNetworkAccessLimited(), - isOnline = this.appProvider.isOnline(); - - if (this.isDownloaded && !this.showDownload) { - // File is downloaded, get the local file URL. - return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl, - this.component, this.componentId, this.timemodified, false, false, this.file); - } else { - if (!isOnline && !this.isDownloaded) { - // Not downloaded and user is offline, reject. - return Promise.reject(this.translate.instant('core.networkerrormsg')); - } - - const isDownloading = this.isDownloading; - this.isDownloading = true; // This check could take a while, show spinner. - - return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, this.fileSize).then(() => { - if (isDownloading) { - // It's already downloading, stop. - return; - } - - // Download and then return the local URL. - return this.downloadFile(); - }, () => { - // Start the download if in wifi, but return the URL right away so the file is opened. - if (isWifi && isOnline) { - this.downloadFile(); - } - - if (isDownloading || !this.isDownloaded || isOnline) { - // Not downloaded or outdated and online, return the online URL. - return fixedUrl; - } else { - // Outdated but offline, so we return the local URL. - return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl, - this.component, this.componentId, this.timemodified, false, false, this.file); - } - }); - } - }); - } else { - // Use the online URL. - promise = Promise.resolve(fixedUrl); - } - - return promise.then((url) => { - if (!url) { - return; - } - - if (url.indexOf('http') === 0) { - return this.utils.openOnlineFile(url).catch((error) => { - // Error opening the file, some apps don't allow opening online files. - if (!this.fileProvider.isAvailable()) { - return Promise.reject(error); - } else if (this.isDownloading) { - return Promise.reject(this.translate.instant('core.erroropenfiledownloading')); - } - - let subPromise; - - if (status === CoreConstants.NOT_DOWNLOADED) { - // File is not downloaded, download and then return the local URL. - subPromise = this.downloadFile(); - } else { - // File is outdated and can't be opened in online, return the local URL. - subPromise = this.filepoolProvider.getInternalUrlByUrl(this.siteId, this.fileUrl); - } - - return subPromise.then((url) => { - return this.utils.openFile(url); - }); - }); - } else { - return this.utils.openFile(url); + return this.fileHelper.downloadAndOpenFile(this.file, this.component, this.componentId, this.state, (event) => { + if (event && event.calculating) { + // The process is calculating some data required for the download, show the spinner. + this.isDownloading = true; } + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); }); } diff --git a/src/directives/directives.module.ts b/src/directives/directives.module.ts index f28c00562..916710a90 100644 --- a/src/directives/directives.module.ts +++ b/src/directives/directives.module.ts @@ -14,6 +14,7 @@ import { NgModule } from '@angular/core'; import { CoreAutoFocusDirective } from './auto-focus'; +import { CoreDownloadFileDirective } from './download-file'; import { CoreExternalContentDirective } from './external-content'; import { CoreFormatTextDirective } from './format-text'; import { CoreLinkDirective } from './link'; @@ -25,6 +26,7 @@ import { CoreLongPressDirective } from './long-press'; @NgModule({ declarations: [ CoreAutoFocusDirective, + CoreDownloadFileDirective, CoreExternalContentDirective, CoreFormatTextDirective, CoreKeepKeyboardDirective, @@ -36,6 +38,7 @@ import { CoreLongPressDirective } from './long-press'; imports: [], exports: [ CoreAutoFocusDirective, + CoreDownloadFileDirective, CoreExternalContentDirective, CoreFormatTextDirective, CoreKeepKeyboardDirective, diff --git a/src/directives/download-file.ts b/src/directives/download-file.ts new file mode 100644 index 000000000..f715d15ee --- /dev/null +++ b/src/directives/download-file.ts @@ -0,0 +1,60 @@ +// (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 { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { CoreFileHelperProvider } from '../providers/file-helper'; +import { CoreDomUtilsProvider } from '../providers/utils/dom'; +import { CoreUtilsProvider } from '../providers/utils/utils'; + +/** + * Directive to allow downloading and open a file. When the item with this directive is clicked, the file will be + * downloaded (if needed) and opened. + */ +@Directive({ + selector: '[core-download-file]' +}) +export class CoreDownloadFileDirective implements OnInit { + @Input('core-download-file') file: any; // The file to download. + @Input() component?: string; // Component to link the file to. + @Input() componentId?: string | number; // Component ID to use in conjunction with the component. + + protected element: HTMLElement; + + constructor(element: ElementRef, protected domUtils: CoreDomUtilsProvider, protected fileHelper: CoreFileHelperProvider) { + this.element = element.nativeElement || element; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.element.addEventListener('click', (ev: Event): void => { + if (!this.file) { + return; + } + + ev.preventDefault(); + ev.stopPropagation(); + + const modal = this.domUtils.showModalLoading(); + + this.fileHelper.downloadAndOpenFile(this.file, this.component, this.componentId).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); + }).finally(() => { + modal.dismiss(); + }); + }); + } +} diff --git a/src/providers/file-helper.ts b/src/providers/file-helper.ts new file mode 100644 index 000000000..a7a936747 --- /dev/null +++ b/src/providers/file-helper.ts @@ -0,0 +1,240 @@ +// (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 } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreAppProvider } from './app'; +import { CoreFileProvider } from './file'; +import { CoreFilepoolProvider } from './filepool'; +import { CoreSitesProvider } from './sites'; +import { CoreUtilsProvider } from './utils/utils'; +import { CoreConstants } from '../core/constants'; + +/** + * Provider to provide some helper functions regarding files and packages. + */ +@Injectable() +export class CoreFileHelperProvider { + + constructor(private fileProvider: CoreFileProvider, private filepoolProvider: CoreFilepoolProvider, + private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, private translate: TranslateService, + private utils: CoreUtilsProvider) { } + + /** + * Convenience function to open a file, downloading it if needed. + * + * @param {any} file The file to download. + * @param {string} [component] The component to link the file to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {string} [state] The file's state. If not provided, it will be calculated. + * @param {Function} [onProgress] Function to call on progress. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise} Resolved on success. + */ + downloadAndOpenFile(file: any, component: string, componentId: string | number, state?: string, + onProgress?: (event: any) => any, siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + const fileUrl = this.getFileUrl(file), + timemodified = this.getFileTimemodified(file); + + return this.downloadFileIfNeeded(file, fileUrl, component, componentId, timemodified, state, onProgress, siteId) + .then((url) => { + if (!url) { + return; + } + + if (url.indexOf('http') === 0) { + return this.utils.openOnlineFile(url).catch((error) => { + // Error opening the file, some apps don't allow opening online files. + if (!this.fileProvider.isAvailable()) { + return Promise.reject(error); + } + + // Get the state. + if (state) { + return state; + } else { + return this.filepoolProvider.getFileStateByUrl(siteId, fileUrl, timemodified); + } + }).then((state) => { + if (state == CoreConstants.DOWNLOADING) { + return Promise.reject(this.translate.instant('core.erroropenfiledownloading')); + } + + let promise; + + if (state === CoreConstants.NOT_DOWNLOADED) { + // File is not downloaded, download and then return the local URL. + promise = this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); + } else { + // File is outdated and can't be opened in online, return the local URL. + promise = this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + } + + return promise.then((url) => { + return this.utils.openFile(url); + }); + }); + } else { + return this.utils.openFile(url); + } + }); + } + + /** + * Download a file if it needs to be downloaded. + * + * @param {any} file The file to download. + * @param {string} fileUrl The file URL. + * @param {string} [component] The component to link the file to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {number} [timemodified] The time this file was modified. + * @param {string} [state] The file's state. If not provided, it will be calculated. + * @param {Function} [onProgress] Function to call on progress. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise} Resolved with the URL to use on success. + */ + protected downloadFileIfNeeded(file: any, fileUrl: string, component?: string, componentId?: string | number, + timemodified?: number, state?: string, onProgress?: (event: any) => any, siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + return this.sitesProvider.getSite(siteId).then((site) => { + const fixedUrl = site.fixPluginfileURL(fileUrl); + + if (this.fileProvider.isAvailable()) { + let promise; + if (state) { + promise = Promise.resolve(state); + } else { + // Calculate the state. + promise = this.filepoolProvider.getFileStateByUrl(siteId, fileUrl, timemodified); + } + + return promise.then((state) => { + // The file system is available. + const isWifi = !this.appProvider.isNetworkAccessLimited(), + isOnline = this.appProvider.isOnline(); + + if (state == CoreConstants.DOWNLOADED) { + // File is downloaded, get the local file URL. + return this.filepoolProvider.getUrlByUrl( + siteId, fileUrl, component, componentId, timemodified, false, false, file); + } else { + if (!isOnline && !this.isStateDownloaded(state)) { + // Not downloaded and user is offline, reject. + return Promise.reject(this.translate.instant('core.networkerrormsg')); + } + + if (onProgress) { + // This call can take a while. Send a fake event to notify that we're doing some calculations. + onProgress({calculating: true}); + } + + return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, file.filesize).then(() => { + if (state == CoreConstants.DOWNLOADING) { + // It's already downloading, stop. + return; + } + + // Download and then return the local URL. + return this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); + }, () => { + // Start the download if in wifi, but return the URL right away so the file is opened. + if (isWifi && isOnline) { + this.downloadFile(fileUrl, component, componentId, timemodified, onProgress, file, siteId); + } + + if (!this.isStateDownloaded(state) || isOnline) { + // Not downloaded or online, return the online URL. + return fixedUrl; + } else { + // Outdated but offline, so we return the local URL. + return this.filepoolProvider.getUrlByUrl( + siteId, fileUrl, component, componentId, timemodified, false, false, file); + } + }); + } + }); + } else { + // Use the online URL. + return fixedUrl; + } + }); + } + + /** + * Download the file. + * + * @param {string} fileUrl The file URL. + * @param {string} [component] The component to link the file to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {number} [timemodified] The time this file was modified. + * @param {Function} [onProgress] Function to call on progress. + * @param {any} [file] The file to download. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise} Resolved with internal URL on success, rejected otherwise. + */ + downloadFile(fileUrl: string, component?: string, componentId?: string | number, timemodified?: number, + onProgress?: (event: any) => any, file?: any, siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + // Get the site and check if it can download files. + return this.sitesProvider.getSite(siteId).then((site) => { + if (!site.canDownloadFiles()) { + return Promise.reject(this.translate.instant('core.cannotdownloadfiles')); + } + + return this.filepoolProvider.downloadUrl(siteId, fileUrl, false, component, componentId, + timemodified, onProgress, undefined, file).catch((error) => { + + // Download failed, check the state again to see if the file was downloaded before. + return this.filepoolProvider.getFileStateByUrl(siteId, fileUrl, timemodified).then((state) => { + if (this.isStateDownloaded(state)) { + return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + } else { + return Promise.reject(error); + } + }); + }); + }); + } + + /** + * Get the file's URL. + * + * @param {any} file The file. + */ + getFileUrl(file: any): string { + return file.fileurl || file.url; + } + + /** + * Get the file's timemodified. + * + * @param {any} file The file. + */ + getFileTimemodified(file: any): number { + return file.timemodified || 0; + } + + /** + * Check if a state is downloaded or outdated. + * + * @param {string} state The state to check. + */ + isStateDownloaded(state: string): boolean { + return state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED; + } +} From fec9fa6efa6c0dc47382aef97f932da3da4e9836 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 19 Feb 2018 09:26:56 +0100 Subject: [PATCH 11/32] MOBILE-2333 course: Implement download module main file directive --- src/components/compile-html/compile-html.ts | 4 +- .../course/directives/directives.module.ts | 27 +++ .../directives/download-module-main-file.ts | 83 +++++++ src/core/course/providers/course.ts | 2 +- src/core/course/providers/helper.ts | 227 +++++++++++++++++- .../providers/module-prefetch-delegate.ts | 13 +- .../classes/module-prefetch-handler.ts | 12 +- src/core/siteaddons/providers/siteaddons.ts | 40 ++- src/providers/file-helper.ts | 27 +++ src/providers/filepool.ts | 2 +- 10 files changed, 429 insertions(+), 8 deletions(-) create mode 100644 src/core/course/directives/directives.module.ts create mode 100644 src/core/course/directives/download-module-main-file.ts diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts index 26b3cc480..44cb24bac 100644 --- a/src/components/compile-html/compile-html.ts +++ b/src/components/compile-html/compile-html.ts @@ -28,6 +28,7 @@ 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 { CoreSiteHomeComponentsModule } from '../../core/sitehome/components/components.module'; import { CoreUserComponentsModule } from '../../core/user/components/components.module'; @@ -80,7 +81,8 @@ 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 + CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule, + CoreCourseDirectivesModule ]; // Other Ionic/Angular providers that don't depend on where they are injected. diff --git a/src/core/course/directives/directives.module.ts b/src/core/course/directives/directives.module.ts new file mode 100644 index 000000000..9b43d41cc --- /dev/null +++ b/src/core/course/directives/directives.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 { CoreCourseDownloadModuleMainFileDirective } from './download-module-main-file'; + +@NgModule({ + declarations: [ + CoreCourseDownloadModuleMainFileDirective + ], + imports: [], + exports: [ + CoreCourseDownloadModuleMainFileDirective + ] +}) +export class CoreCourseDirectivesModule {} diff --git a/src/core/course/directives/download-module-main-file.ts b/src/core/course/directives/download-module-main-file.ts new file mode 100644 index 000000000..b2a8d4324 --- /dev/null +++ b/src/core/course/directives/download-module-main-file.ts @@ -0,0 +1,83 @@ +// (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 { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { CoreCourseProvider } from '../providers/course'; +import { CoreCourseHelperProvider } from '../providers/helper'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; + +/** + * Directive to allow downloading and open the main file of a module. + * When the item with this directive is clicked, the module will be downloaded (if needed) and opened. + * This is meant for modules like mod_resource. + * + * This directive must receive either a module or a moduleId. If no files are provided, it will use module.contents. + */ +@Directive({ + selector: '[core-course-download-module-main-file]' +}) +export class CoreCourseDownloadModuleMainFileDirective implements OnInit { + @Input() module: any; // The module. + @Input() moduleId: string | number; // The module ID. Required if module is not supplied. + @Input() courseId: string | number; // The course ID. + @Input() component?: string; // Component to link the file to. + @Input() componentId?: string | number; // Component ID to use in conjunction with the component. If not defined, use moduleId. + @Input() files?: any[]; // List of files of the module. If not provided, use module.contents. + + protected element: HTMLElement; + + constructor(element: ElementRef, protected domUtils: CoreDomUtilsProvider, protected courseHelper: CoreCourseHelperProvider, + protected courseProvider: CoreCourseProvider) { + this.element = element.nativeElement || element; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.element.addEventListener('click', (ev: Event): void => { + if (!this.module && !this.moduleId) { + return; + } + + ev.preventDefault(); + ev.stopPropagation(); + + const modal = this.domUtils.showModalLoading(), + courseId = typeof this.courseId == 'string' ? parseInt(this.courseId, 10) : this.courseId; + let promise; + + if (this.module) { + // We already have the module. + promise = Promise.resolve(module); + } else { + // Try to get the module from cache. + this.moduleId = typeof this.moduleId == 'string' ? parseInt(this.moduleId, 10) : this.moduleId; + promise = this.courseProvider.getModule(this.moduleId, courseId); + } + + promise.then((module) => { + const componentId = this.componentId || module.id; + + return this.courseHelper.downloadModuleAndOpenFile(module, courseId, this.component, componentId, this.files); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); + }).finally(() => { + modal.dismiss(); + }); + }); + } +} diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index 9239b8921..083ed039f 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -201,7 +201,7 @@ export class CoreCourseProvider { * @return {Promise} Promise resolved with the module. */ getModule(moduleId: number, courseId?: number, sectionId?: number, preferCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); let promise; diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index fedffa140..5bd0893d9 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -15,8 +15,11 @@ import { Injectable } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; +import { CoreFileProvider } from '@providers/file'; import { CoreFilepoolProvider } from '@providers/filepool'; +import { CoreFileHelperProvider } from '@providers/file-helper'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -116,7 +119,8 @@ export class CoreCourseHelperProvider { private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider, - private eventsProvider: CoreEventsProvider) { } + private eventsProvider: CoreEventsProvider, private fileHelper: CoreFileHelperProvider, + private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider) { } /** * This function treats every module on the sections provided to load the handler data, treat completion @@ -470,6 +474,227 @@ export class CoreCourseHelperProvider { }); } + /** + * Convenience function to open a module main file, downloading the package if needed. + * This is meant for modules like mod_resource. + * + * @param {any} module The module to download. + * @param {number} courseId The course ID of the module. + * @param {string} [component] The component to link the files to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {any[]} [files] List of files of the module. If not provided, use module.contents. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise} Resolved on success. + */ + downloadModuleAndOpenFile(module: any, courseId: number, component?: string, componentId?: string | number, files?: any[], + siteId?: string): Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + let promise; + if (files) { + promise = Promise.resolve(files); + } else { + promise = this.courseProvider.loadModuleContents(module, courseId).then(() => { + files = module.contents; + }); + } + + // Make sure that module contents are loaded. + return promise.then(() => { + if (!files || !files.length) { + return Promise.reject(null); + } + + return this.sitesProvider.getSite(siteId); + }).then((site) => { + const mainFile = files[0], + fileUrl = this.fileHelper.getFileUrl(mainFile); + + // Check if the file should be opened in browser. + if (this.fileHelper.shouldOpenInBrowser(mainFile)) { + if (this.appProvider.isOnline()) { + // Open in browser. + let fixedUrl = site.fixPluginfileURL(fileUrl).replace('&offline=1', ''); + // Remove forcedownload when followed by another param. + fixedUrl = fixedUrl.replace(/forcedownload=\d+&/, ''); + // Remove forcedownload when not followed by any param. + fixedUrl = fixedUrl.replace(/[\?|\&]forcedownload=\d+/, ''); + + this.utils.openInBrowser(fixedUrl); + + if (this.fileProvider.isAvailable()) { + // Download the file if needed (file outdated or not downloaded). + // Download will be in background, don't return the promise. + this.downloadModule(module, courseId, component, componentId, files, siteId); + } + + return; + } else { + // Not online, get the offline file. It will fail if not found. + return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl).then((path) => { + return this.utils.openFile(path); + }).catch((error) => { + return Promise.reject(this.translate.instant('core.networkerrormsg')); + }); + } + } + + // File shouldn't be opened in browser. Download the module if it needs to be downloaded. + return this.downloadModuleWithMainFileIfNeeded(module, courseId, component, componentId, files, siteId) + .then((result) => { + if (result.path.indexOf('http') === 0) { + return this.utils.openOnlineFile(result.path).catch((error) => { + // Error opening the file, some apps don't allow opening online files. + if (!this.fileProvider.isAvailable()) { + return Promise.reject(error); + } else if (result.status === CoreConstants.DOWNLOADING) { + return Promise.reject(this.translate.instant('core.erroropenfiledownloading')); + } + + let promise; + if (result.status === CoreConstants.NOT_DOWNLOADED) { + // Not downloaded, download it now and return the local file. + promise = this.downloadModule(module, courseId, component, componentId, files, siteId).then(() => { + return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + }); + } else { + // File is outdated or stale and can't be opened in online, return the local URL. + promise = this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + } + + return promise.then((path) => { + return this.utils.openFile(path); + }); + }); + } else { + return this.utils.openFile(result.path); + } + }); + }); + } + + /** + * Convenience function to download a module that has a main file and return the local file's path and other info. + * This is meant for modules like mod_resource. + * + * @param {any} module The module to download. + * @param {number} courseId The course ID of the module. + * @param {string} [component] The component to link the files to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {any[]} [files] List of files of the module. If not provided, use module.contents. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise<{fixedUrl: string, path: string, status: string}>} Promise resolved when done. + */ + protected downloadModuleWithMainFileIfNeeded(module: any, courseId: number, component?: string, componentId?: string | number, + files?: any[], siteId?: string): Promise<{fixedUrl: string, path: string, status: string}> { + + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + if (!files || !files.length) { + // Module not valid, stop. + return Promise.reject(null); + } + + const mainFile = files[0], + fileUrl = this.fileHelper.getFileUrl(mainFile), + timemodified = this.fileHelper.getFileTimemodified(mainFile), + prefetchHandler = this.prefetchDelegate.getPrefetchHandlerFor(module), + result = { + fixedUrl: undefined, + path: undefined, + status: undefined + }; + + return this.sitesProvider.getSite(siteId).then((site) => { + const fixedUrl = site.fixPluginfileURL(fileUrl); + result.fixedUrl = fixedUrl; + + if (this.fileProvider.isAvailable()) { + // The file system is available. + return this.filepoolProvider.getPackageStatus(siteId, component, componentId).then((status) => { + result.status = status; + + const isWifi = !this.appProvider.isNetworkAccessLimited(), + isOnline = this.appProvider.isOnline(); + + if (status === CoreConstants.DOWNLOADED) { + // Get the local file URL. + return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + } else if (status === CoreConstants.DOWNLOADING && !this.appProvider.isDesktop()) { + // Return the online URL. + return fixedUrl; + } else { + if (!isOnline && status === CoreConstants.NOT_DOWNLOADED) { + // Not downloaded and we're offline, reject. + return Promise.reject(this.translate.instant('core.networkerrormsg')); + } + + return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, mainFile.filesize).then(() => { + // Download and then return the local URL. + return this.downloadModule(module, courseId, component, componentId, files, siteId).then(() => { + return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl); + }); + }, () => { + // Start the download if in wifi, but return the URL right away so the file is opened. + if (isWifi && isOnline) { + this.downloadModule(module, courseId, component, componentId, files, siteId); + } + + if (!this.fileHelper.isStateDownloaded(status) || isOnline) { + // Not downloaded or online, return the online URL. + return fixedUrl; + } else { + // Outdated but offline, so we return the local URL. Use getUrlByUrl so it's added to the queue. + return this.filepoolProvider.getUrlByUrl(siteId, fileUrl, component, componentId, timemodified, + false, false, mainFile); + } + }); + } + }).then((path) => { + result.path = path; + + return result; + }); + } else { + // We use the live URL. + result.path = fixedUrl; + + return result; + } + }); + } + + /** + * Convenience function to download a module. + * + * @param {any} module The module to download. + * @param {number} courseId The course ID of the module. + * @param {string} [component] The component to link the files to. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {any[]} [files] List of files of the module. If not provided, use module.contents. + * @param {string} [siteId] The site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + downloadModule(module: any, courseId: number, component?: string, componentId?: string | number, files?: any[], siteId?: string) + : Promise { + + const prefetchHandler = this.prefetchDelegate.getPrefetchHandlerFor(module); + + if (prefetchHandler) { + // Use the prefetch handler to download the module. + if (prefetchHandler.download) { + return prefetchHandler.download(module, courseId); + } else { + return prefetchHandler.prefetch(module, courseId, true); + } + } + + // There's no prefetch handler for the module, just download the files. + files = files || module.contents; + + return this.filepoolProvider.downloadOrPrefetchFiles(siteId, files, false, false, component, componentId); + } + /** * Fill the Context Menu for a certain module. * diff --git a/src/core/course/providers/module-prefetch-delegate.ts b/src/core/course/providers/module-prefetch-delegate.ts index dce46a02a..84f4b0b15 100644 --- a/src/core/course/providers/module-prefetch-delegate.ts +++ b/src/core/course/providers/module-prefetch-delegate.ts @@ -86,9 +86,20 @@ export interface CoreCourseModulePrefetchHandler extends CoreDelegateHandler { * @param {any} module Module. * @param {number} courseId Course ID the module belongs to. * @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section. + * @param {string} [dirPath] Path of the directory where to store all the content files. * @return {Promise} Promise resolved when done. */ - prefetch(module: any, courseId?: number, single?: boolean): Promise; + prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string): Promise; + + /** + * Download the module. + * + * @param {any} module The module object returned by WS. + * @param {number} courseId Course ID. + * @param {string} [dirPath] Path of the directory where to store all the content files. + * @return {Promise} Promise resolved when all content is downloaded. + */ + download?(module: any, courseId: number, dirPath?: string): Promise; /** * Check if a certain module can use core_course_check_updates to check if it has updates. diff --git a/src/core/siteaddons/classes/module-prefetch-handler.ts b/src/core/siteaddons/classes/module-prefetch-handler.ts index 3972f0d56..f87750000 100644 --- a/src/core/siteaddons/classes/module-prefetch-handler.ts +++ b/src/core/siteaddons/classes/module-prefetch-handler.ts @@ -109,7 +109,17 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc promises.push(this.siteAddonsProvider.callWS(method, params, {cacheKey: cacheKey})); } else { // It's a method to get content. - promises.push(this.siteAddonsProvider.getContent(this.component, method, args)); + promises.push(this.siteAddonsProvider.getContent(this.component, method, args).then((result) => { + const subPromises = []; + + // Prefetch the files in the content. + if (result.files && result.files.length) { + subPromises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, result.files, prefetch, false, + this.component, module.id, dirPath)); + } + + return Promise.all(subPromises); + })); } } diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index fdd3dff6d..37be3d7fe 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -43,6 +43,32 @@ export interface CoreSiteAddonsModuleHandler { handlerSchema: any; } +export interface CoreSiteAddonsGetContentResult { + /** + * The content in HTML. + * @type {string} + */ + html: string; + + /** + * The javascript for the content. + * @type {string} + */ + javascript: string; + + /** + * The files for the content. + * @type {any[]} + */ + files?: any[]; + + /** + * Other data. + * @type {any} + */ + otherdata?: any; +} + /** * Service to provide functionalities regarding site addons. */ @@ -104,9 +130,9 @@ export class CoreSiteAddonsProvider { * @param {string} method Method to execute in the class. * @param {any} args The params for the method. * @param {string} [siteId] Site ID. If not defined, current site. - * @return {Promise<{html: string, javascript: string}>} Promise resolved with the content and the javascript. + * @return {Promise} Promise resolved with the result. */ - getContent(component: string, method: string, args: any, siteId?: string): Promise<{html: string, javascript: string}> { + getContent(component: string, method: string, args: any, siteId?: string): Promise { this.logger.debug(`Get content for component '${component}' and method '${method}'`); return this.sitesProvider.getSite(siteId).then((site) => { @@ -129,6 +155,16 @@ export class CoreSiteAddonsProvider { }; return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets); + }).then((result) => { + if (result.otherdata) { + try { + result.otherdata = JSON.parse(result.otherdata); + } catch (ex) { + // Ignore errors. + } + } + + return result; }); }); } diff --git a/src/providers/file-helper.ts b/src/providers/file-helper.ts index a7a936747..88feef0dc 100644 --- a/src/providers/file-helper.ts +++ b/src/providers/file-helper.ts @@ -237,4 +237,31 @@ export class CoreFileHelperProvider { isStateDownloaded(state: string): boolean { return state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED; } + + /** + * Whether the file has to be opened in browser (external repository). + * The file must have a mimetype attribute. + * + * @param {any} file The file to check. + * @return {boolean} Whether the file should be opened in browser. + */ + shouldOpenInBrowser(file: any): boolean { + if (!file || !file.isexternalfile || !file.mimetype) { + return false; + } + + const mimetype = file.mimetype; + if (mimetype.indexOf('application/vnd.google-apps.') != -1) { + // Google Docs file, always open in browser. + return true; + } + + if (file.repositorytype == 'onedrive') { + // In OneDrive, open in browser the office docs + return mimetype.indexOf('application/vnd.openxmlformats-officedocument') != -1 || + mimetype == 'text/plain' || mimetype == 'document/unknown'; + } + + return false; + } } diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index 259ba617f..c6fa08632 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -2718,7 +2718,7 @@ export class CoreFilepoolProvider { */ storePackageStatus(siteId: string, status: string, component: string, componentId?: string | number, extra?: string) : Promise { - this.logger.debug(`Set status '${status}'' for package ${component} ${componentId}`); + this.logger.debug(`Set status '${status}' for package ${component} ${componentId}`); componentId = this.fixComponentId(componentId); return this.sitesProvider.getSite(siteId).then((site) => { From fb278791c195ddf00d4af073a3d446044c8791e0 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 19 Feb 2018 13:01:15 +0100 Subject: [PATCH 12/32] MOBILE-2333 siteaddons: Implement new-content directive --- src/components/compile-html/compile-html.ts | 3 +- .../directives/download-module-main-file.ts | 2 - .../components/addon-content/addon-content.ts | 18 ++++++ .../directives/directives.module.ts | 27 ++++++++ src/core/siteaddons/directives/new-content.ts | 64 +++++++++++++++++++ 5 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/core/siteaddons/directives/directives.module.ts create mode 100644 src/core/siteaddons/directives/new-content.ts diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts index 44cb24bac..14d3fc082 100644 --- a/src/components/compile-html/compile-html.ts +++ b/src/components/compile-html/compile-html.ts @@ -30,6 +30,7 @@ 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'; @@ -82,7 +83,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { protected IMPORTS = [ IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule, - CoreCourseDirectivesModule + CoreCourseDirectivesModule, CoreSiteAddonsDirectivesModule ]; // Other Ionic/Angular providers that don't depend on where they are injected. diff --git a/src/core/course/directives/download-module-main-file.ts b/src/core/course/directives/download-module-main-file.ts index b2a8d4324..569a215fb 100644 --- a/src/core/course/directives/download-module-main-file.ts +++ b/src/core/course/directives/download-module-main-file.ts @@ -13,11 +13,9 @@ // limitations under the License. import { Directive, Input, OnInit, ElementRef } from '@angular/core'; -import { NavController } from 'ionic-angular'; import { CoreCourseProvider } from '../providers/course'; import { CoreCourseHelperProvider } from '../providers/helper'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; /** * Directive to allow downloading and open the main file of a module. diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteaddons/components/addon-content/addon-content.ts index a842088d1..bf1d8317e 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -65,4 +65,22 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { return this.fetchContent(); }); } + + /** + * Update the content, usually with a different method or params. + * + * @param {string} component New component. + * @param {string} method New method. + * @param {any} args New params. + */ + updateContent(component: string, method: string, args: any): void { + this.component = component; + this.method = method; + this.args = args; + this.dataLoaded = false; + + this.fetchContent().finally(() => { + this.dataLoaded = true; + }); + } } diff --git a/src/core/siteaddons/directives/directives.module.ts b/src/core/siteaddons/directives/directives.module.ts new file mode 100644 index 000000000..709291135 --- /dev/null +++ b/src/core/siteaddons/directives/directives.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 { CoreSiteAddonsNewContentDirective } from './new-content'; + +@NgModule({ + declarations: [ + CoreSiteAddonsNewContentDirective + ], + imports: [], + exports: [ + CoreSiteAddonsNewContentDirective + ] +}) +export class CoreSiteAddonsDirectivesModule {} diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteaddons/directives/new-content.ts new file mode 100644 index 000000000..b4cd49b0c --- /dev/null +++ b/src/core/siteaddons/directives/new-content.ts @@ -0,0 +1,64 @@ +// (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 { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; + +/** + * Directive to display a new site addon content when clicked. This new content can be displayed in a new page or in the + * current page (only if the current page is already displaying a site addon content). + */ +@Directive({ + selector: '[core-site-addons-new-content]' +}) +export class CoreSiteAddonsNewContentDirective implements OnInit { + @Input() component: string; // The component of the new content. + @Input() method: string; // The method to get the new content. + @Input() args: any; // The params to get the new content. + @Input() title: string; // The title to display with the new content. Only if samePage=false. + @Input() samePage?: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + + protected element: HTMLElement; + + constructor(element: ElementRef, protected utils: CoreUtilsProvider, protected navCtrl: NavController, + @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent) { + this.element = element.nativeElement || element; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.element.addEventListener('click', (ev: Event): void => { + ev.preventDefault(); + ev.stopPropagation(); + + if (this.utils.isTrueOrOne(this.samePage)) { + // Update the parent content (if it exists). + if (this.parentContent) { + this.parentContent.updateContent(this.component, this.method, this.args); + } + } else { + this.navCtrl.push('CoreSiteAddonsAddonPage', { + title: this.title, + component: this.component, + method: this.method, + args: this.args + }); + } + }); + } +} From 6d9df59e67afdde8fe7fc79dc698704ff9c35490 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 19 Feb 2018 15:58:39 +0100 Subject: [PATCH 13/32] MOBILE-2333 siteaddons: Display context menu for module site addons --- src/core/course/providers/helper.ts | 7 +- .../components/addon-content/addon-content.ts | 18 +++- .../components/components.module.ts | 4 +- .../components/module-index/module-index.html | 12 ++- .../components/module-index/module-index.ts | 85 +++++++++++++++++-- .../pages/addon-page/addon-page.html | 2 +- .../pages/addon-page/addon-page.module.ts | 4 - .../siteaddons/pages/addon-page/addon-page.ts | 10 +-- .../pages/module-index/module-index.html | 15 ++++ .../pages/module-index/module-index.module.ts | 34 ++++++++ .../pages/module-index/module-index.ts | 51 +++++++++++ src/core/siteaddons/providers/helper.ts | 10 +-- 12 files changed, 222 insertions(+), 30 deletions(-) create mode 100644 src/core/siteaddons/pages/module-index/module-index.html create mode 100644 src/core/siteaddons/pages/module-index/module-index.module.ts create mode 100644 src/core/siteaddons/pages/module-index/module-index.ts diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index 5bd0893d9..3c015a056 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -830,8 +830,13 @@ export class CoreCourseHelperProvider { moduleInfo.statusIcon = 'spinner'; break; case CoreConstants.OUTDATED: - moduleInfo.statusIcon = 'ion-android-refresh'; + moduleInfo.statusIcon = 'refresh'; break; + case CoreConstants.DOWNLOADED: + if (!this.prefetchDelegate.canCheckUpdates()) { + moduleInfo.statusIcon = 'refresh'; + break; + } default: moduleInfo.statusIcon = ''; break; diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteaddons/components/addon-content/addon-content.ts index bf1d8317e..ea2ee703f 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; @@ -27,12 +27,17 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { @Input() component: string; @Input() method: string; @Input() args: any; + @Output() onContentLoaded?: EventEmitter; // Emits an event when the content is loaded. + @Output() onLoadingContent?: EventEmitter; // Emits an event when starts to load the content. content: string; // Content. javascript: string; // Javascript to execute. dataLoaded: boolean; - constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { } + constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { + this.onContentLoaded = new EventEmitter(); + this.onLoadingContent = new EventEmitter(); + } /** * Component being initialized. @@ -46,12 +51,17 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { /** * Fetches the content to render. * + * @param {boolean} [refresh] Whether the user is refreshing. * @return {Promise} Promise resolved when done. */ - fetchContent(): Promise { + fetchContent(refresh?: boolean): Promise { + this.onLoadingContent.emit(refresh); + return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { this.content = result.html; this.javascript = result.javascript; + + this.onContentLoaded.emit(refresh); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); }); @@ -62,7 +72,7 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { */ refreshData(): Promise { return this.siteAddonsProvider.invalidateContent(this.component, this.method, this.args).finally(() => { - return this.fetchContent(); + return this.fetchContent(true); }); } diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts index 929c13dd5..5b7747e16 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteaddons/components/components.module.ts @@ -15,6 +15,7 @@ import { NgModule } from '@angular/core'; 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 { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-content'; @@ -29,7 +30,8 @@ import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index' CommonModule, IonicModule, CoreComponentsModule, - CoreCompileHtmlComponentsModule + CoreCompileHtmlComponentsModule, + 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 72cb07d7c..6e6ef1862 100644 --- a/src/core/siteaddons/components/module-index/module-index.html +++ b/src/core/siteaddons/components/module-index/module-index.html @@ -1,2 +1,12 @@ - + + + + + + + + + + + diff --git a/src/core/siteaddons/components/module-index/module-index.ts b/src/core/siteaddons/components/module-index/module-index.ts index b0c52f36f..19220cdd5 100644 --- a/src/core/siteaddons/components/module-index/module-index.ts +++ b/src/core/siteaddons/components/module-index/module-index.ts @@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, Input, ViewChild } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreTextUtilsProvider } from '../../../../providers/utils/text'; import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; import { CoreCourseModuleMainComponent } from '../../../course/providers/module-delegate'; +import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; +import { CoreCourseHelperProvider } from '../../../course/providers/helper'; import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; /** @@ -24,7 +28,7 @@ import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-cont selector: 'core-site-addons-module-index', templateUrl: 'module-index.html', }) -export class CoreSiteAddonsModuleIndexComponent implements OnInit, CoreCourseModuleMainComponent { +export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent { @Input() module: any; // The module. @Input() courseId: number; // Course ID the module belongs to. @@ -34,12 +38,27 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, CoreCourseMod method: string; args: any; - constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + // Data for context menu. + externalUrl: string; + description: string; + refreshIcon: string; + prefetchStatusIcon: string; + prefetchText: string; + size: string; + + protected isDestroyed = false; + protected statusObserver; + + constructor(protected siteAddonsProvider: CoreSiteAddonsProvider, protected courseHelper: CoreCourseHelperProvider, + protected prefetchDelegate: CoreCourseModulePrefetchDelegate, protected textUtils: CoreTextUtilsProvider, + protected translate: TranslateService) { } /** * Component being initialized. */ ngOnInit(): void { + this.refreshIcon = 'spinner'; + if (this.module) { const handler = this.siteAddonsProvider.getModuleSiteAddonHandler(this.module.modname); if (handler) { @@ -50,6 +69,10 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, CoreCourseMod cmid: this.module.id }; } + + // Get the data for the context menu. + this.description = this.module.description; + this.externalUrl = this.module.url; } } @@ -62,13 +85,65 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, CoreCourseMod */ doRefresh(refresher?: any, done?: () => void): Promise { if (this.addonContent) { + this.refreshIcon = 'spinner'; + return Promise.resolve(this.addonContent.refreshData()).finally(() => { - refresher.complete(); + refresher && refresher.complete(); + done && done(); }); } else { - refresher.complete(); + refresher && refresher.complete(); + done && done(); return Promise.resolve(); } } + + /** + * Function called when the data of the site addon content is loaded. + */ + contentLoaded(refresh: boolean): void { + this.refreshIcon = 'refresh'; + + // Check if there is a prefetch handler for this type of module. + if (this.prefetchDelegate.getPrefetchHandlerFor(this.module)) { + this.courseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component); + } + } + + /** + * Function called when starting to load the data of the site addon content. + */ + contentLoading(refresh: boolean): void { + this.refreshIcon = 'spinner'; + } + + /** + * Expand the description. + */ + expandDescription(): void { + this.textUtils.expandText(this.translate.instant('core.description'), this.description, this.component, this.module.id); + } + + /** + * Prefetch the module. + */ + prefetch(): void { + this.courseHelper.contextMenuPrefetch(this, this.module, this.courseId); + } + + /** + * Confirm and remove downloaded files. + */ + removeFiles(): void { + this.courseHelper.confirmAndRemoveFiles(this.module, this.courseId); + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + this.isDestroyed = true; + this.statusObserver && this.statusObserver.off(); + } } diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteaddons/pages/addon-page/addon-page.html index 14555dcec..16b323097 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.html +++ b/src/core/siteaddons/pages/addon-page/addon-page.html @@ -3,7 +3,7 @@ {{ title }} - + diff --git a/src/core/siteaddons/pages/addon-page/addon-page.module.ts b/src/core/siteaddons/pages/addon-page/addon-page.module.ts index 021766bd3..053a147b4 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.module.ts +++ b/src/core/siteaddons/pages/addon-page/addon-page.module.ts @@ -16,8 +16,6 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreSiteAddonsAddonPage } from './addon-page'; -import { CoreComponentsModule } from '../../../../components/components.module'; -import { CoreCompileHtmlComponentsModule } from '../../../../components/compile-html/compile-html.module'; import { CoreSiteAddonsComponentsModule } from '../../components/components.module'; /** @@ -28,8 +26,6 @@ import { CoreSiteAddonsComponentsModule } from '../../components/components.modu CoreSiteAddonsAddonPage ], imports: [ - CoreComponentsModule, - CoreCompileHtmlComponentsModule, CoreSiteAddonsComponentsModule, IonicPageModule.forChild(CoreSiteAddonsAddonPage), TranslateModule.forChild() diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteaddons/pages/addon-page/addon-page.ts index 6501c247f..772c4dc91 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.ts +++ b/src/core/siteaddons/pages/addon-page/addon-page.ts @@ -14,8 +14,6 @@ import { Component, ViewChild } from '@angular/core'; import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; import { CoreSiteAddonsAddonContentComponent } from '../../components/addon-content/addon-content'; /** @@ -31,11 +29,11 @@ export class CoreSiteAddonsAddonPage { title: string; // Page title. - protected component: string; - protected method: string; - protected args: any; + component: string; + method: string; + args: any; - constructor(params: NavParams, protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { + constructor(params: NavParams) { this.title = params.get('title'); this.component = params.get('component'); this.method = params.get('method'); diff --git a/src/core/siteaddons/pages/module-index/module-index.html b/src/core/siteaddons/pages/module-index/module-index.html new file mode 100644 index 000000000..463923a47 --- /dev/null +++ b/src/core/siteaddons/pages/module-index/module-index.html @@ -0,0 +1,15 @@ + + + {{ title }} + + + + + + + + + + + + diff --git a/src/core/siteaddons/pages/module-index/module-index.module.ts b/src/core/siteaddons/pages/module-index/module-index.module.ts new file mode 100644 index 000000000..9ba33ae12 --- /dev/null +++ b/src/core/siteaddons/pages/module-index/module-index.module.ts @@ -0,0 +1,34 @@ +// (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 { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreSiteAddonsModuleIndexPage } from './module-index'; +import { CoreSiteAddonsComponentsModule } from '../../components/components.module'; + +/** + * Module to lazy load the page. + */ +@NgModule({ + declarations: [ + CoreSiteAddonsModuleIndexPage + ], + imports: [ + CoreSiteAddonsComponentsModule, + IonicPageModule.forChild(CoreSiteAddonsModuleIndexPage), + TranslateModule.forChild() + ] +}) +export class CoreSiteAddonsAddonPageModule {} diff --git a/src/core/siteaddons/pages/module-index/module-index.ts b/src/core/siteaddons/pages/module-index/module-index.ts new file mode 100644 index 000000000..264a023e3 --- /dev/null +++ b/src/core/siteaddons/pages/module-index/module-index.ts @@ -0,0 +1,51 @@ +// (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, ViewChild } from '@angular/core'; +import { IonicPage, NavParams } from 'ionic-angular'; +import { CoreSiteAddonsModuleIndexComponent } from '../../components/module-index/module-index'; + +/** + * Page to render the index page of a module site addon. + */ +@IonicPage({ segment: 'core-site-addons-module-index-page' }) +@Component({ + selector: 'page-core-site-addons-module-index', + templateUrl: 'module-index.html', +}) +export class CoreSiteAddonsModuleIndexPage { + @ViewChild(CoreSiteAddonsModuleIndexComponent) content: CoreSiteAddonsModuleIndexComponent; + + title: string; // Page title. + + module: any; + courseId: number; + + constructor(params: NavParams) { + this.title = params.get('title'); + this.module = params.get('module'); + this.courseId = params.get('courseId'); + } + + /** + * Refresh the data. + * + * @param {any} refresher Refresher. + */ + refreshData(refresher: any): void { + this.content.doRefresh().finally(() => { + refresher.complete(); + }); + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 463f5b079..65d89d0b0 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -265,14 +265,10 @@ export class CoreSiteAddonsHelperProvider { event.preventDefault(); event.stopPropagation(); - navCtrl.push('CoreSiteAddonsAddonPage', { + navCtrl.push('CoreSiteAddonsModuleIndexPage', { title: module.name, - component: addon.component, - method: handlerSchema.method, - args: { - courseid: courseId, - cmid: module.id - } + module: module, + courseId: courseId }, options); } }; From 2c858bc9d2ab932a603c67ef33464424e40507f6 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 20 Feb 2018 13:24:01 +0100 Subject: [PATCH 14/32] MOBILE-2333 siteaddons: Implement call WS directives --- .../siteaddons/classes/call-ws-directive.ts | 123 ++++++++++++++++++ .../components/addon-content/addon-content.ts | 25 +++- .../directives/call-ws-new-content.ts | 100 ++++++++++++++ src/core/siteaddons/directives/call-ws.ts | 82 ++++++++++++ .../directives/directives.module.ts | 6 + src/core/siteaddons/directives/new-content.ts | 33 ++++- src/core/siteaddons/providers/siteaddons.ts | 107 +++++++++++++-- 7 files changed, 453 insertions(+), 23 deletions(-) create mode 100644 src/core/siteaddons/classes/call-ws-directive.ts create mode 100644 src/core/siteaddons/directives/call-ws-new-content.ts create mode 100644 src/core/siteaddons/directives/call-ws.ts diff --git a/src/core/siteaddons/classes/call-ws-directive.ts b/src/core/siteaddons/classes/call-ws-directive.ts new file mode 100644 index 000000000..45069b43d --- /dev/null +++ b/src/core/siteaddons/classes/call-ws-directive.ts @@ -0,0 +1,123 @@ +// (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 { Input, OnInit, OnDestroy, ElementRef } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { Subscription } from 'rxjs'; + +/** + * Base class for directives to call a WS when the element is clicked. + * + * The directives that inherit from this class will call a WS method when the element is clicked. + */ +export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { + @Input() name: string; // The name of the WS to call. + @Input() params: any; // The params for the WS call. + @Input() preSets: any; // The preSets for the WS call. + @Input() confirmMessage: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. + @Input() useOtherDataForWS: any[]; // Whether to include other data in the params for the WS. + // @see CoreSiteAddonsProvider.loadOtherDataInArgs. + + protected element: HTMLElement; + protected invalidateObserver: Subscription; + + constructor(element: ElementRef, protected translate: TranslateService, protected domUtils: CoreDomUtilsProvider, + protected siteAddonsProvider: CoreSiteAddonsProvider, protected parentContent: CoreSiteAddonsAddonContentComponent) { + this.element = element.nativeElement || element; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.element.addEventListener('click', (ev: Event): void => { + ev.preventDefault(); + ev.stopPropagation(); + + if (typeof this.confirmMessage != 'undefined') { + // Ask for confirm. + this.domUtils.showConfirm(this.confirmMessage || this.translate.instant('core.areyousure')).then(() => { + this.callWS(); + }).catch(() => { + // User cancelled, ignore. + }); + } else { + this.callWS(); + } + }); + + if (this.parentContent && this.parentContent.invalidateObservable) { + this.invalidateObserver = this.parentContent.invalidateObservable.subscribe(() => { + this.invalidate(); + }); + } + } + + /** + * Call a WS. + * + * @return {Promise} Promise resolved when done. + */ + protected callWS(): Promise { + const modal = this.domUtils.showModalLoading(); + + let params = this.params; + + if (this.parentContent) { + params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); + } + + return this.siteAddonsProvider.callWS(this.name, params, this.preSets).then((result) => { + return this.wsCallSuccess(result); + }).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'core.serverconnection', true); + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * Function called when the WS call is successful. + * + * @param {any} result Result of the WS call. + */ + protected wsCallSuccess(result: any): void { + // Function to be overridden. + } + + /** + * Invalidate the WS call. + * + * @return {Promise} Promise resolved when done. + */ + invalidate(): Promise { + let params = this.params; + + if (this.parentContent) { + params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); + } + + return this.siteAddonsProvider.invalidateCallWS(this.name, params, this.preSets); + } + + /** + * Directive destroyed. + */ + ngOnDestroy(): void { + this.invalidateObserver && this.invalidateObserver.unsubscribe(); + } +} diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteaddons/components/addon-content/addon-content.ts index ea2ee703f..f7e506d0a 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -15,6 +15,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { Subject } from 'rxjs'; /** * Component to render a site addon content. @@ -32,20 +33,21 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { content: string; // Content. javascript: string; // Javascript to execute. + otherData: any; // Other data of the content. dataLoaded: boolean; + invalidateObservable: Subject; // An observable to notify observers when to invalidate data. constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { this.onContentLoaded = new EventEmitter(); this.onLoadingContent = new EventEmitter(); + this.invalidateObservable = new Subject(); } /** * Component being initialized. */ ngOnInit(): void { - this.fetchContent().finally(() => { - this.dataLoaded = true; - }); + this.fetchContent(); } /** @@ -60,17 +62,28 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { this.content = result.html; this.javascript = result.javascript; + this.otherData = result.otherdata; this.onContentLoaded.emit(refresh); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true); + }).finally(() => { + this.dataLoaded = true; }); } /** * Refresh the data. + * + * @param {boolean} [showSpinner] Whether to show spinner while refreshing. */ - refreshData(): Promise { + refreshData(showSpinner?: boolean): Promise { + if (showSpinner) { + this.dataLoaded = false; + } + + this.invalidateObservable.next(); // Notify observers. + return this.siteAddonsProvider.invalidateContent(this.component, this.method, this.args).finally(() => { return this.fetchContent(true); }); @@ -89,8 +102,6 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { this.args = args; this.dataLoaded = false; - this.fetchContent().finally(() => { - this.dataLoaded = true; - }); + this.fetchContent(); } } diff --git a/src/core/siteaddons/directives/call-ws-new-content.ts b/src/core/siteaddons/directives/call-ws-new-content.ts new file mode 100644 index 000000000..265927830 --- /dev/null +++ b/src/core/siteaddons/directives/call-ws-new-content.ts @@ -0,0 +1,100 @@ +// (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 { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; + +/** + * Directive to call a WS when the element is clicked and load a new content passing the WS result as args. This new content + * can be displayed in a new page or in the same page (only if current page is already displaying a site addon content). + * + * If you don't need to load some new content when done, @see CoreSiteAddonsCallWSDirective. + * + * @see CoreSiteAddonsCallWSBaseDirective. + * + * Example usages: + * + * A button to get some data from the server without using cache, showing default confirm and displaying a new page: + * + * + * + * A button to get some data from the server using cache, without confirm, displaying new content in same page and using + * userid from otherdata: + * + * + */ +@Directive({ + selector: '[core-site-addons-call-ws-new-content]' +}) +export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallWSBaseDirective { + @Input() component: string; // The component of the new content. + @Input() method: string; // The method to get the new content. + @Input() args: any; // The params to get the new content. + @Input() title: string; // The title to display with the new content. Only if samePage=false. + @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. + + protected element: HTMLElement; + + constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, + siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, + protected utils: CoreUtilsProvider, protected navCtrl: NavController) { + super(element, translate, domUtils, siteAddonsProvider, parentContent); + } + + /** + * Function called when the WS call is successful. + * + * @param {any} result Result of the WS call. + */ + protected wsCallSuccess(result: any): void { + let args = this.args || {}; + + if (this.parentContent) { + args = this.siteAddonsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + } + + // Add the properties from the WS call result to the args. + args = Object.assign(args, result); + + if (this.utils.isTrueOrOne(this.samePage)) { + // Update the parent content (if it exists). + if (this.parentContent) { + this.parentContent.updateContent(this.component, this.method, args); + } + } else { + this.navCtrl.push('CoreSiteAddonsAddonPage', { + title: this.title, + component: this.component, + method: this.method, + args: args + }); + } + } +} diff --git a/src/core/siteaddons/directives/call-ws.ts b/src/core/siteaddons/directives/call-ws.ts new file mode 100644 index 000000000..f825b9d1e --- /dev/null +++ b/src/core/siteaddons/directives/call-ws.ts @@ -0,0 +1,82 @@ +// (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 { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; + +/** + * Directive to call a WS when the element is clicked. The action to do when the WS call is successful depends on the input data: + * display a message, go back or refresh current view. + * + * If you want to load a new content when the WS call is done, @see CoreSiteAddonsCallWSNewContentDirective. + * + * @see CoreSiteAddonsCallWSBaseDirective. + * + * Example usages: + * + * A button to send some data to the server without using cache, displaying default messages and refreshing on success: + * + * + * + * A button to send some data to the server using cache, without confirm, going back on success and using userid from otherdata: + * + * + */ +@Directive({ + selector: '[core-site-addons-call-ws]' +}) +export class CoreSiteAddonsCallWSDirective extends CoreSiteAddonsCallWSBaseDirective { + @Input() successMessage: string; // Message to show on success. If not supplied, no message. If empty, default message. + @Input() goBackOnSuccess: boolean | string; // Whether to go back if the WS call is successful. + @Input() refreshOnSuccess: boolean | string; // Whether to refresh the current view if the WS call is successful. + + protected element: HTMLElement; + + constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, + siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, + protected utils: CoreUtilsProvider, protected navCtrl: NavController) { + super(element, translate, domUtils, siteAddonsProvider, parentContent); + } + + /** + * Function called when the WS call is successful. + * + * @param {any} result Result of the WS call. + */ + protected wsCallSuccess(result: any): void { + if (typeof this.successMessage != 'undefined') { + // Display the success message. + this.domUtils.showToast(this.successMessage || this.translate.instant('core.success')); + } + + if (this.utils.isTrueOrOne(this.goBackOnSuccess)) { + this.navCtrl.pop(); + } else if (this.utils.isTrueOrOne(this.refreshOnSuccess) && this.parentContent) { + this.parentContent.refreshData(true); + } + } +} diff --git a/src/core/siteaddons/directives/directives.module.ts b/src/core/siteaddons/directives/directives.module.ts index 709291135..6bcf9302d 100644 --- a/src/core/siteaddons/directives/directives.module.ts +++ b/src/core/siteaddons/directives/directives.module.ts @@ -13,14 +13,20 @@ // limitations under the License. import { NgModule } from '@angular/core'; +import { CoreSiteAddonsCallWSDirective } from './call-ws'; +import { CoreSiteAddonsCallWSNewContentDirective } from './call-ws-new-content'; import { CoreSiteAddonsNewContentDirective } from './new-content'; @NgModule({ declarations: [ + CoreSiteAddonsCallWSDirective, + CoreSiteAddonsCallWSNewContentDirective, CoreSiteAddonsNewContentDirective ], imports: [], exports: [ + CoreSiteAddonsCallWSDirective, + CoreSiteAddonsCallWSNewContentDirective, CoreSiteAddonsNewContentDirective ] }) diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteaddons/directives/new-content.ts index b4cd49b0c..f809148e6 100644 --- a/src/core/siteaddons/directives/new-content.ts +++ b/src/core/siteaddons/directives/new-content.ts @@ -15,11 +15,28 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; /** * Directive to display a new site addon content when clicked. This new content can be displayed in a new page or in the * current page (only if the current page is already displaying a site addon content). + * + * Example usages: + * + * A button to go to a new content page: + * + * + * + * A button to load new content in current page using a param from otherdata: + * + * */ @Directive({ selector: '[core-site-addons-new-content]' @@ -29,12 +46,14 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { @Input() method: string; // The method to get the new content. @Input() args: any; // The params to get the new content. @Input() title: string; // The title to display with the new content. Only if samePage=false. - @Input() samePage?: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. protected element: HTMLElement; constructor(element: ElementRef, protected utils: CoreUtilsProvider, protected navCtrl: NavController, - @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent) { + @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent, + protected siteAddonsProvider: CoreSiteAddonsProvider) { this.element = element.nativeElement || element; } @@ -46,17 +65,23 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { ev.preventDefault(); ev.stopPropagation(); + let args = this.args; + + if (this.parentContent) { + args = this.siteAddonsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + } + if (this.utils.isTrueOrOne(this.samePage)) { // Update the parent content (if it exists). if (this.parentContent) { - this.parentContent.updateContent(this.component, this.method, this.args); + this.parentContent.updateContent(this.component, this.method, args); } } else { this.navCtrl.push('CoreSiteAddonsAddonPage', { title: this.title, component: this.component, method: this.method, - args: this.args + args: args }); } }); diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 37be3d7fe..489f3eca7 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -13,6 +13,8 @@ // limitations under the License. import { Injectable } from '@angular/core'; +import { Platform } from 'ionic-angular'; +import { CoreAppProvider } from '../../../providers/app'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site'; @@ -80,10 +82,58 @@ export class CoreSiteAddonsProvider { protected moduleSiteAddons: {[modName: string]: CoreSiteAddonsModuleHandler} = {}; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, - private langProvider: CoreLangProvider) { + private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform) { this.logger = logger.getInstance('CoreUserProvider'); } + /** + * Add some params that will always be sent for get content. + * + * @param {any} args Original params. + * @param {CoreSite} [site] Site. If not defined, current site. + * @return {Promise} Promise resolved with the new params. + */ + protected addDefaultArgs(args: any, site?: CoreSite): Promise { + args = args || {}; + site = site || this.sitesProvider.getCurrentSite(); + + return this.langProvider.getCurrentLanguage().then((lang) => { + + // Clone the object so the original one isn't modified. + const argsToSend = this.utils.clone(args); + + argsToSend.userid = args.userid || site.getUserId(); + argsToSend.appid = CoreConfigConstants.app_id; + argsToSend.appversioncode = CoreConfigConstants.versioncode; + argsToSend.appversionname = CoreConfigConstants.versionname; + argsToSend.applang = lang; + argsToSend.appcustomurlscheme = CoreConfigConstants.customurlscheme; + argsToSend.appisdesktop = this.appProvider.isDesktop(); + argsToSend.appismobile = this.appProvider.isMobile(); + argsToSend.appiswide = this.appProvider.isWide(); + + if (argsToSend.appisdevice) { + if (this.platform.is('ios')) { + argsToSend.appplatform = 'ios'; + } else { + argsToSend.appplatform = 'android'; + } + } else if (argsToSend.appisdesktop) { + if (this.appProvider.isMac()) { + argsToSend.appplatform = 'mac'; + } else if (this.appProvider.isLinux()) { + argsToSend.appplatform = 'linux'; + } else { + argsToSend.appplatform = 'windows'; + } + } else { + argsToSend.appplatform = 'browser'; + } + + return argsToSend; + }); + } + /** * Call a WS for a site addon. * @@ -120,7 +170,7 @@ export class CoreSiteAddonsProvider { * @return {string} Cache key. */ protected getCallWSCommonCacheKey(method: string): string { - return this.ROOT_CACHE_KEY + method; + return this.ROOT_CACHE_KEY + 'ws:' + method; } /** @@ -136,15 +186,9 @@ export class CoreSiteAddonsProvider { this.logger.debug(`Get content for component '${component}' and method '${method}'`); return this.sitesProvider.getSite(siteId).then((site) => { - // Get current language to be added to params. - return this.langProvider.getCurrentLanguage().then((lang) => { - // Add some params that will always be sent. Clone the object so the original one isn't modified. - const argsToSend = this.utils.clone(args); - argsToSend.userid = args.userid || site.getUserId(); - argsToSend.appid = CoreConfigConstants.app_id; - argsToSend.versionname = CoreConfigConstants.versionname; - argsToSend.lang = lang; + // Add some params that will always be sent. + return this.addDefaultArgs(args, site).then((argsToSend) => { // Now call the WS. const data = { component: component, @@ -209,12 +253,13 @@ export class CoreSiteAddonsProvider { * * @param {string} method WS method to use. * @param {any} data Data to send to the WS. + * @param {CoreSiteWSPreSets} [preSets] Extra options. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the data is invalidated. */ - invalidateCallWS(method: string, data: any, siteId?: string): Promise { + invalidateCallWS(method: string, data: any, preSets?: CoreSiteWSPreSets, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - return site.invalidateWsCacheForKey(this.getCallWSCacheKey(method, data)); + return site.invalidateWsCacheForKey(preSets.cacheKey || this.getCallWSCacheKey(method, data)); }); } @@ -244,6 +289,44 @@ export class CoreSiteAddonsProvider { return site.wsAvailable('tool_mobile_get_content'); } + /** + * Load other data into args as determined by useOtherData list. + * If useOtherData is undefined, it won't add any data. + * If useOtherData is defined but empty (null, false or empty string) it will copy all the data from otherData to args. + * If useOtherData is an array, it will only copy the properties whose names are in the array. + * + * @param {any} args The current args. + * @param {any} otherData All the other data. + * @param {any[]} useOtherData Names of the attributes to include. + * @return {any} New args. + */ + loadOtherDataInArgs(args: any, otherData: any, useOtherData: any[]): any { + if (!args) { + args = {}; + } else { + args = this.utils.clone(args); + } + + otherData = otherData || {}; + + if (typeof useOtherData == 'undefined') { + // No need to add other data, return args as they are. + return args; + } else if (!useOtherData) { + // Use other data is defined but empty. Add all the data to args. + for (const name in otherData) { + args[name] = otherData[name]; + } + } else { + for (const i in useOtherData) { + const name = useOtherData[i]; + args[name] = otherData[name]; + } + } + + return args; + } + /** * Set the site addon handler for a certain module. * From 91b020e2fbdc5ec708fdea7569ca6817f9309389 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 20 Feb 2018 15:35:53 +0100 Subject: [PATCH 15/32] MOBILE-2333 siteaddons: Allow sending form data --- .../siteaddons/classes/call-ws-directive.ts | 36 ++++++++++++------ src/core/siteaddons/directives/new-content.ts | 11 +++++- src/providers/utils/dom.ts | 37 +++++++++++++++++++ 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/core/siteaddons/classes/call-ws-directive.ts b/src/core/siteaddons/classes/call-ws-directive.ts index 45069b43d..be26166fb 100644 --- a/src/core/siteaddons/classes/call-ws-directive.ts +++ b/src/core/siteaddons/classes/call-ws-directive.ts @@ -31,6 +31,8 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { @Input() confirmMessage: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. @Input() useOtherDataForWS: any[]; // Whether to include other data in the params for the WS. // @see CoreSiteAddonsProvider.loadOtherDataInArgs. + @Input() form: string; // ID or name to identify a form. The form will be obtained from document.forms. + // If supplied and form is found, the form data will be retrieved and sent to the WS. protected element: HTMLElement; protected invalidateObserver: Subscription; @@ -73,13 +75,8 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { * @return {Promise} Promise resolved when done. */ protected callWS(): Promise { - const modal = this.domUtils.showModalLoading(); - - let params = this.params; - - if (this.parentContent) { - params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); - } + const modal = this.domUtils.showModalLoading(), + params = this.getParamsForWS(); return this.siteAddonsProvider.callWS(this.name, params, this.preSets).then((result) => { return this.wsCallSuccess(result); @@ -90,6 +87,25 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { }); } + /** + * Get the params for the WS call. + * + * @return {any} Params. + */ + protected getParamsForWS(): any { + let params = this.params || {}; + + if (this.parentContent) { + params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); + } + + if (this.form && document.forms[this.form]) { + params = Object.assign(params, this.domUtils.getDataFromForm(document.forms[this.form])); + } + + return params; + } + /** * Function called when the WS call is successful. * @@ -105,11 +121,7 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { * @return {Promise} Promise resolved when done. */ invalidate(): Promise { - let params = this.params; - - if (this.parentContent) { - params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); - } + const params = this.getParamsForWS(); return this.siteAddonsProvider.invalidateCallWS(this.name, params, this.preSets); } diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteaddons/directives/new-content.ts index f809148e6..4db053f85 100644 --- a/src/core/siteaddons/directives/new-content.ts +++ b/src/core/siteaddons/directives/new-content.ts @@ -14,6 +14,7 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreSiteAddonsProvider } from '../providers/siteaddons'; import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; @@ -48,11 +49,13 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { @Input() title: string; // The title to display with the new content. Only if samePage=false. @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. + @Input() form: string; // ID or name to identify a form. The form will be obtained from document.forms. + // If supplied and form is found, the form data will be retrieved and sent to the new content. protected element: HTMLElement; constructor(element: ElementRef, protected utils: CoreUtilsProvider, protected navCtrl: NavController, - @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent, + @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent, protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { this.element = element.nativeElement || element; } @@ -65,12 +68,16 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { ev.preventDefault(); ev.stopPropagation(); - let args = this.args; + let args = this.args || {}; if (this.parentContent) { args = this.siteAddonsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); } + if (this.form && document.forms[this.form]) { + args = Object.assign(args, this.domUtils.getDataFromForm(document.forms[this.form])); + } + if (this.utils.isTrueOrOne(this.samePage)) { // Update the parent content (if it exists). if (this.parentContent) { diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 72843064c..f28c8b5e7 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -264,6 +264,43 @@ export class CoreDomUtilsProvider { } } + /** + * Get the data from a form. It will only collect elements that have a name. + * + * @param {HTMLFormElement} form The form to get the data from. + * @return {any} Object with the data. The keys are the names of the inputs. + */ + getDataFromForm(form: HTMLFormElement): any { + if (!form || !form.elements) { + return {}; + } + + const data = {}; + + for (let i = 0; i < form.elements.length; i++) { + const element: any = form.elements[i], + name = element.name || ''; + + // Ignore submit inputs. + if (!name || element.type == 'submit' || element.tagName == 'BUTTON') { + return; + } + + // Get the value. + if (element.type == 'checkbox') { + data[name] = !!element.checked; + } else if (element.type == 'radio') { + if (element.checked) { + data[name] = element.value; + } + } else { + data[name] = element.value; + } + } + + return data; + } + /** * Returns height of an element. * From 823ea35b69bfbfe273c5fab848291958bd35065b Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 20 Feb 2018 16:28:35 +0100 Subject: [PATCH 16/32] MOBILE-2333 siteaddons: Implement directive to call WS on load --- .../classes/call-ws-click-directive.ts | 72 +++++++++++++++++++ .../siteaddons/classes/call-ws-directive.ts | 26 +------ .../directives/call-ws-new-content.ts | 10 ++- .../siteaddons/directives/call-ws-on-load.ts | 58 +++++++++++++++ src/core/siteaddons/directives/call-ws.ts | 10 ++- .../directives/directives.module.ts | 3 + .../pages/module-index/module-index.html | 2 +- 7 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 src/core/siteaddons/classes/call-ws-click-directive.ts create mode 100644 src/core/siteaddons/directives/call-ws-on-load.ts diff --git a/src/core/siteaddons/classes/call-ws-click-directive.ts b/src/core/siteaddons/classes/call-ws-click-directive.ts new file mode 100644 index 000000000..7c055187d --- /dev/null +++ b/src/core/siteaddons/classes/call-ws-click-directive.ts @@ -0,0 +1,72 @@ +// (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 { Input, OnInit, ElementRef } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSiteAddonsCallWSBaseDirective } from './call-ws-directive'; + +/** + * Base class for directives to call a WS when the element is clicked. + * + * The directives that inherit from this class will call a WS method when the element is clicked. + * + * @see CoreSiteAddonsCallWSBaseDirective + */ +export class CoreSiteAddonsCallWSOnClickBaseDirective extends CoreSiteAddonsCallWSBaseDirective implements OnInit { + @Input() confirmMessage: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. + + constructor(element: ElementRef, protected translate: TranslateService, protected domUtils: CoreDomUtilsProvider, + protected siteAddonsProvider: CoreSiteAddonsProvider, protected parentContent: CoreSiteAddonsAddonContentComponent) { + super(element, translate, domUtils, siteAddonsProvider, parentContent); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + super.ngOnInit(); + + this.element.addEventListener('click', (ev: Event): void => { + ev.preventDefault(); + ev.stopPropagation(); + + if (typeof this.confirmMessage != 'undefined') { + // Ask for confirm. + this.domUtils.showConfirm(this.confirmMessage || this.translate.instant('core.areyousure')).then(() => { + this.callWS(); + }).catch(() => { + // User cancelled, ignore. + }); + } else { + this.callWS(); + } + }); + } + + /** + * Call a WS. + * + * @return {Promise} Promise resolved when done. + */ + protected callWS(): Promise { + const modal = this.domUtils.showModalLoading(); + + return super.callWS().finally(() => { + modal.dismiss(); + }); + } +} diff --git a/src/core/siteaddons/classes/call-ws-directive.ts b/src/core/siteaddons/classes/call-ws-directive.ts index be26166fb..453ac0285 100644 --- a/src/core/siteaddons/classes/call-ws-directive.ts +++ b/src/core/siteaddons/classes/call-ws-directive.ts @@ -20,15 +20,12 @@ import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content import { Subscription } from 'rxjs'; /** - * Base class for directives to call a WS when the element is clicked. - * - * The directives that inherit from this class will call a WS method when the element is clicked. + * Base class for directives that need to call a WS. */ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { @Input() name: string; // The name of the WS to call. @Input() params: any; // The params for the WS call. @Input() preSets: any; // The preSets for the WS call. - @Input() confirmMessage: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. @Input() useOtherDataForWS: any[]; // Whether to include other data in the params for the WS. // @see CoreSiteAddonsProvider.loadOtherDataInArgs. @Input() form: string; // ID or name to identify a form. The form will be obtained from document.forms. @@ -46,22 +43,6 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { * Component being initialized. */ ngOnInit(): void { - this.element.addEventListener('click', (ev: Event): void => { - ev.preventDefault(); - ev.stopPropagation(); - - if (typeof this.confirmMessage != 'undefined') { - // Ask for confirm. - this.domUtils.showConfirm(this.confirmMessage || this.translate.instant('core.areyousure')).then(() => { - this.callWS(); - }).catch(() => { - // User cancelled, ignore. - }); - } else { - this.callWS(); - } - }); - if (this.parentContent && this.parentContent.invalidateObservable) { this.invalidateObserver = this.parentContent.invalidateObservable.subscribe(() => { this.invalidate(); @@ -75,15 +56,12 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { * @return {Promise} Promise resolved when done. */ protected callWS(): Promise { - const modal = this.domUtils.showModalLoading(), - params = this.getParamsForWS(); + const params = this.getParamsForWS(); return this.siteAddonsProvider.callWS(this.name, params, this.preSets).then((result) => { return this.wsCallSuccess(result); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.serverconnection', true); - }).finally(() => { - modal.dismiss(); }); } diff --git a/src/core/siteaddons/directives/call-ws-new-content.ts b/src/core/siteaddons/directives/call-ws-new-content.ts index 265927830..38d2cd145 100644 --- a/src/core/siteaddons/directives/call-ws-new-content.ts +++ b/src/core/siteaddons/directives/call-ws-new-content.ts @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { Directive, Input, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSiteAddonsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; /** @@ -27,7 +27,7 @@ import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content * * If you don't need to load some new content when done, @see CoreSiteAddonsCallWSDirective. * - * @see CoreSiteAddonsCallWSBaseDirective. + * @see CoreSiteAddonsCallWSOnClickBaseDirective. * * Example usages: * @@ -52,7 +52,7 @@ import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content @Directive({ selector: '[core-site-addons-call-ws-new-content]' }) -export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallWSBaseDirective { +export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallWSOnClickBaseDirective { @Input() component: string; // The component of the new content. @Input() method: string; // The method to get the new content. @Input() args: any; // The params to get the new content. @@ -60,8 +60,6 @@ export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallW @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. - protected element: HTMLElement; - constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, protected utils: CoreUtilsProvider, protected navCtrl: NavController) { diff --git a/src/core/siteaddons/directives/call-ws-on-load.ts b/src/core/siteaddons/directives/call-ws-on-load.ts new file mode 100644 index 000000000..9a2a1a8c2 --- /dev/null +++ b/src/core/siteaddons/directives/call-ws-on-load.ts @@ -0,0 +1,58 @@ +// (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 { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; + +/** + * Directive to call a WS as soon as its loaded. + * This directive is meant for actions to do in the background, like calling logging WebServices. + * + * If you want to call a WS when the user clicks on a certain element, @see CoreSiteAddonsCallWSDirective. + * + * @see CoreSiteAddonsCallWSBaseDirective. + * + * Example usage: + * + * + */ +@Directive({ + selector: '[core-site-addons-call-ws-on-load]' +}) +export class CoreSiteAddonsCallWSOnLoadDirective extends CoreSiteAddonsCallWSBaseDirective implements OnInit { + + constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, + siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent) { + super(element, translate, domUtils, siteAddonsProvider, parentContent); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + super.ngOnInit(); + + // Call the WS immediately. + this.callWS().catch(() => { + // Ignore errors. + }); + } +} diff --git a/src/core/siteaddons/directives/call-ws.ts b/src/core/siteaddons/directives/call-ws.ts index f825b9d1e..360a7f39d 100644 --- a/src/core/siteaddons/directives/call-ws.ts +++ b/src/core/siteaddons/directives/call-ws.ts @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { Directive, Input, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSiteAddonsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; /** @@ -27,7 +27,7 @@ import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content * * If you want to load a new content when the WS call is done, @see CoreSiteAddonsCallWSNewContentDirective. * - * @see CoreSiteAddonsCallWSBaseDirective. + * @see CoreSiteAddonsCallWSOnClickBaseDirective. * * Example usages: * @@ -49,13 +49,11 @@ import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content @Directive({ selector: '[core-site-addons-call-ws]' }) -export class CoreSiteAddonsCallWSDirective extends CoreSiteAddonsCallWSBaseDirective { +export class CoreSiteAddonsCallWSDirective extends CoreSiteAddonsCallWSOnClickBaseDirective { @Input() successMessage: string; // Message to show on success. If not supplied, no message. If empty, default message. @Input() goBackOnSuccess: boolean | string; // Whether to go back if the WS call is successful. @Input() refreshOnSuccess: boolean | string; // Whether to refresh the current view if the WS call is successful. - protected element: HTMLElement; - constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, protected utils: CoreUtilsProvider, protected navCtrl: NavController) { diff --git a/src/core/siteaddons/directives/directives.module.ts b/src/core/siteaddons/directives/directives.module.ts index 6bcf9302d..973182d91 100644 --- a/src/core/siteaddons/directives/directives.module.ts +++ b/src/core/siteaddons/directives/directives.module.ts @@ -15,18 +15,21 @@ import { NgModule } from '@angular/core'; import { CoreSiteAddonsCallWSDirective } from './call-ws'; import { CoreSiteAddonsCallWSNewContentDirective } from './call-ws-new-content'; +import { CoreSiteAddonsCallWSOnLoadDirective } from './call-ws-on-load'; import { CoreSiteAddonsNewContentDirective } from './new-content'; @NgModule({ declarations: [ CoreSiteAddonsCallWSDirective, CoreSiteAddonsCallWSNewContentDirective, + CoreSiteAddonsCallWSOnLoadDirective, CoreSiteAddonsNewContentDirective ], imports: [], exports: [ CoreSiteAddonsCallWSDirective, CoreSiteAddonsCallWSNewContentDirective, + CoreSiteAddonsCallWSOnLoadDirective, CoreSiteAddonsNewContentDirective ] }) diff --git a/src/core/siteaddons/pages/module-index/module-index.html b/src/core/siteaddons/pages/module-index/module-index.html index 463923a47..a0e0ea045 100644 --- a/src/core/siteaddons/pages/module-index/module-index.html +++ b/src/core/siteaddons/pages/module-index/module-index.html @@ -8,7 +8,7 @@ - + From 8e87a1ade9dfdfb027254ab8d2c044a553f3a985 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 21 Feb 2018 16:18:03 +0100 Subject: [PATCH 17/32] 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); } } From 141f979ceaa1326ec0f900bd066d4c14dcb81d02 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 23 Feb 2018 12:28:36 +0100 Subject: [PATCH 18/32] MOBILE-2333 siteaddons: Support restrict users and courses --- src/core/siteaddons/providers/helper.ts | 67 ++++++++++++++++----- src/core/siteaddons/providers/siteaddons.ts | 31 +--------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 17431668a..2869aca16 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -30,6 +30,7 @@ import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/m import { CoreSiteAddonsProvider } from './siteaddons'; import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; import { CoreCompileProvider } from '../../compile/providers/compile'; +import { CoreCoursesProvider } from '../../courses/providers/courses'; /** * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site @@ -45,30 +46,37 @@ export class CoreSiteAddonsHelperProvider { private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, - private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider) { + private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, + private coursesProvider: CoreCoursesProvider) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } /** - * Bootstrap a handler if it has some bootstrap JS. + * Bootstrap a handler if it has some bootstrap method. * * @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). + * @return {Promise<{restrict?: any, jsResult?: any}>} Promise resolved when done. It returns the "restrict" of the handler and + * the result of the javascript execution (if any). */ - protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise { + protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise<{restrict?: any, jsResult?: any}> { if (!handlerSchema.bootstrap) { - return Promise.resolve(); + 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) => { + const data = { + restrict: result.restrict, + jsResult: undefined + }; + if (!result.javascript || this.sitesProvider.getCurrentSiteId() != siteId) { // No javascript or site has changed, stop. - return; + return data; } // Create a "fake" instance to hold all the libraries. @@ -76,7 +84,9 @@ export class CoreSiteAddonsHelperProvider { this.compileProvider.injectLibraries(instance); // Now execute the javascript using this instance. - return this.compileProvider.executeJavascript(instance, result.javascript); + data.jsResult = this.compileProvider.executeJavascript(instance, result.javascript); + + return data; }); } @@ -205,15 +215,16 @@ export class CoreSiteAddonsHelperProvider { switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': - uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result); + uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result.jsResult, result.restrict); break; case 'CoreCourseModuleDelegate': - uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result); + uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result.jsResult, result.restrict); break; case 'CoreUserDelegate': - uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result); + uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result.jsResult, + result.restrict); break; default: @@ -239,9 +250,11 @@ export class CoreSiteAddonsHelperProvider { * @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. + * @param {any} [restrict] List of users and courses the handler is restricted to. * @return {string} A string to identify the handler. */ - protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { + protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) + : string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -284,9 +297,11 @@ export class CoreSiteAddonsHelperProvider { * @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. + * @param {any} [restrict] List of users and courses the handler is restricted to. * @return {string} A string to identify the handler. */ - protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { + protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) + : string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -342,9 +357,11 @@ export class CoreSiteAddonsHelperProvider { * @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. + * @param {any} [restrict] List of users and courses the handler is restricted to. * @return {string} A string to identify the handler. */ - protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any): string { + protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) + : string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -360,11 +377,31 @@ export class CoreSiteAddonsHelperProvider { userHandler = Object.assign(baseHandler, { priority: handlerSchema.priority, type: handlerSchema.type, - isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean => { - if (handlerSchema.restricted == 'current' && user.id != this.sitesProvider.getCurrentSite().getUserId()) { + isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => { + if (handlerSchema.restricttocurrentuser && user.id != this.sitesProvider.getCurrentSite().getUserId()) { + // Only enabled for current user. return false; } + if (restrict) { + if (restrict.users && restrict.users.indexOf(user.id) == -1) { + // User is not in the list of restricted users. + return false; + } else if (restrict.courses && restrict.courses.indexOf(courseId) == -1) { + // Course is not in the list of restricted courses. + return false; + } + } + + if (handlerSchema.restricttoenrolledcourses || typeof handlerSchema.restricttoenrolledcourses == 'undefined') { + // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course. + return this.coursesProvider.getUserCourse(courseId, true).then(() => { + return true; + }).catch(() => { + return false; + }); + } + return true; }, getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 662fb7da7..dd3bd05ae 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -51,32 +51,6 @@ export interface CoreSiteAddonsHandler { bootstrapResult?: any; } -export interface CoreSiteAddonsGetContentResult { - /** - * The content in HTML. - * @type {string} - */ - html: string; - - /** - * The javascript for the content. - * @type {string} - */ - javascript: string; - - /** - * The files for the content. - * @type {any[]} - */ - files?: any[]; - - /** - * Other data. - * @type {any} - */ - otherdata?: any; -} - /** * Service to provide functionalities regarding site addons. */ @@ -187,10 +161,9 @@ export class CoreSiteAddonsProvider { * @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. + * @return {Promise} Promise resolved with the result. */ - getContent(component: string, method: string, args: any, preSets?: CoreSiteWSPreSets, 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) => { From b2c4b65024914c85fe59a3593bc004b2eadac6fa Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 23 Feb 2018 15:53:45 +0100 Subject: [PATCH 19/32] MOBILE-2333 siteaddons: Support course options site addon --- src/core/course/pages/section/section.html | 2 +- src/core/course/pages/section/section.ts | 8 +- src/core/course/providers/options-delegate.ts | 6 + .../classes/module-prefetch-handler.ts | 67 +-------- .../components/components.module.ts | 10 +- .../course-option/course-option.html | 6 + .../components/course-option/course-option.ts | 66 +++++++++ src/core/siteaddons/providers/helper.ts | 140 +++++++++++++++--- src/core/siteaddons/providers/siteaddons.ts | 93 +++++++++++- 9 files changed, 303 insertions(+), 95 deletions(-) create mode 100644 src/core/siteaddons/components/course-option/course-option.html create mode 100644 src/core/siteaddons/components/course-option/course-option.ts diff --git a/src/core/course/pages/section/section.html b/src/core/course/pages/section/section.html index 83bf61d17..56cd025b4 100644 --- a/src/core/course/pages/section/section.html +++ b/src/core/course/pages/section/section.html @@ -30,7 +30,7 @@ - + diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 0e0ced8b7..093e0a29f 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -45,7 +45,6 @@ export class CoreCourseSectionPage implements OnDestroy { sectionId: number; sectionNumber: number; courseHandlers: CoreCourseOptionsHandlerToDisplay[]; - handlerData: any = {}; // Data to send to the handlers components. dataLoaded: boolean; downloadEnabled: boolean; downloadEnabledIcon = 'square-outline'; // Disabled by default. @@ -70,7 +69,6 @@ export class CoreCourseSectionPage implements OnDestroy { this.sectionId = navParams.get('sectionId'); this.sectionNumber = navParams.get('sectionNumber'); this.module = navParams.get('module'); - this.handlerData.courseId = this.course.id; // Get the title to display. We dont't have sections yet. this.title = courseFormatDelegate.getCourseTitle(this.course); @@ -194,6 +192,12 @@ export class CoreCourseSectionPage implements OnDestroy { // Load the course handlers. promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, false).then((handlers) => { + // Add the courseId to the handler component data. + handlers.forEach((handler) => { + handler.data.componentData = handler.data.componentData || {}; + handler.data.componentData.courseId = this.course.id; + }); + this.courseHandlers = handlers; })); diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts index 35182a369..e73a4b6ab 100644 --- a/src/core/course/providers/options-delegate.ts +++ b/src/core/course/providers/options-delegate.ts @@ -90,6 +90,12 @@ export interface CoreCourseOptionsHandlerData { * When the component is created, it will receive the courseId as input. */ component: any; + + /** + * Data to pass to the component. All the properties in this object will be passed to the component as inputs. + * @type {any} + */ + componentData?: any; } /** diff --git a/src/core/siteaddons/classes/module-prefetch-handler.ts b/src/core/siteaddons/classes/module-prefetch-handler.ts index f87750000..31c502892 100644 --- a/src/core/siteaddons/classes/module-prefetch-handler.ts +++ b/src/core/siteaddons/classes/module-prefetch-handler.ts @@ -80,48 +80,8 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc promises.push(this.downloadOrPrefetchFiles(site.id, module, courseId, prefetch, dirPath)); // Call all the offline functions. - for (const method in this.handlerSchema.offlinefunctions) { - if (site.wsAvailable(method)) { - // The method is a WS. - const paramsList = this.handlerSchema.offlinefunctions[method], - cacheKey = this.siteAddonsProvider.getCallWSCacheKey(method, args); - let params = {}; - - if (!paramsList.length) { - // No params defined, send the default ones. - params = args; - } else { - for (const i in paramsList) { - const paramName = paramsList[i]; - - if (typeof args[paramName] != 'undefined') { - params[paramName] = args[paramName]; - } else { - // The param is not one of the default ones. Try to calculate the param to use. - const value = this.getDownloadParam(module, courseId, paramName); - if (typeof value != 'undefined') { - params[paramName] = value; - } - } - } - } - - promises.push(this.siteAddonsProvider.callWS(method, params, {cacheKey: cacheKey})); - } else { - // It's a method to get content. - promises.push(this.siteAddonsProvider.getContent(this.component, method, args).then((result) => { - const subPromises = []; - - // Prefetch the files in the content. - if (result.files && result.files.length) { - subPromises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, result.files, prefetch, false, - this.component, module.id, dirPath)); - } - - return Promise.all(subPromises); - })); - } - } + promises.push(this.siteAddonsProvider.prefetchFunctions(this.component, args, this.handlerSchema, courseId, + module, prefetch, dirPath, site)); return Promise.all(promises); }); @@ -165,29 +125,6 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc }); } - /** - * Get the value of a WS param for prefetch. - * - * @param {any} module The module object returned by WS. - * @param {number} courseId Course ID. - * @param {string} paramName Name of the param as defined by the handler. - * @return {any} The value. - */ - protected getDownloadParam(module: any, courseId: number, paramName: string): any { - switch (paramName) { - case 'courseids': - // The WS needs the list of course IDs. Create the list. - return [courseId]; - - case this.component + 'id': - // The WS needs the instance id. - return module.instance; - - default: - // No more params supported for now. - } - } - /** * Invalidate the prefetched content. * diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts index 200e8be8f..61021a6b9 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteaddons/components/components.module.ts @@ -20,11 +20,13 @@ import { CoreComponentsModule } from '../../../components/components.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'; +import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-option'; @NgModule({ declarations: [ CoreSiteAddonsAddonContentComponent, - CoreSiteAddonsModuleIndexComponent + CoreSiteAddonsModuleIndexComponent, + CoreSiteAddonsCourseOptionComponent ], imports: [ CommonModule, @@ -37,10 +39,12 @@ import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index' ], exports: [ CoreSiteAddonsAddonContentComponent, - CoreSiteAddonsModuleIndexComponent + CoreSiteAddonsModuleIndexComponent, + CoreSiteAddonsCourseOptionComponent ], entryComponents: [ - CoreSiteAddonsModuleIndexComponent + CoreSiteAddonsModuleIndexComponent, + CoreSiteAddonsCourseOptionComponent ] }) export class CoreSiteAddonsComponentsModule {} diff --git a/src/core/siteaddons/components/course-option/course-option.html b/src/core/siteaddons/components/course-option/course-option.html new file mode 100644 index 000000000..ada863f5e --- /dev/null +++ b/src/core/siteaddons/components/course-option/course-option.html @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/core/siteaddons/components/course-option/course-option.ts b/src/core/siteaddons/components/course-option/course-option.ts new file mode 100644 index 000000000..99a26249a --- /dev/null +++ b/src/core/siteaddons/components/course-option/course-option.ts @@ -0,0 +1,66 @@ +// (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, OnInit, Input, ViewChild } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; + +/** + * Component that displays the index of a course option site addon. + */ +@Component({ + selector: 'core-site-addons-course-option', + templateUrl: 'course-option.html', +}) +export class CoreSiteAddonsCourseOptionComponent implements OnInit { + @Input() courseId: number; + @Input() handlerUniqueName: string; + + @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; + + component: string; + method: string; + args: any; + bootstrapResult: any; + + constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + if (this.handlerUniqueName) { + const handler = this.siteAddonsProvider.getSiteAddonHandler(this.handlerUniqueName); + if (handler) { + this.component = handler.addon.component; + this.method = handler.handlerSchema.method; + this.args = { + courseid: this.courseId, + }; + this.bootstrapResult = handler.bootstrapResult; + } + } + } + + /** + * Refresh the data. + * + * @param {any} refresher Refresher. + */ + refreshData(refresher: any): void { + this.content.refreshData().finally(() => { + refresher.complete(); + }); + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 2869aca16..66885636a 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -24,9 +24,13 @@ import { CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../../course/providers/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate'; +import { + CoreCourseOptionsDelegate, CoreCourseOptionsHandler, CoreCourseOptionsHandlerData +} from '../../course/providers/options-delegate'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; +import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; import { CoreSiteAddonsProvider } from './siteaddons'; import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; import { CoreCompileProvider } from '../../compile/providers/compile'; @@ -47,7 +51,7 @@ export class CoreSiteAddonsHelperProvider { private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, - private coursesProvider: CoreCoursesProvider) { + private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } @@ -130,6 +134,54 @@ export class CoreSiteAddonsHelperProvider { return this.getHandlerPrefixForStrings(handlerName) + key; } + /** + * Check if a handler is enabled for a certain course. + * + * @param {number} courseId Course ID to check. + * @param {boolean} [restrictEnrolled] If true or undefined, handler is only enabled for courses the user is enrolled in. + * @param {any} [restrict] Users and courses the handler is restricted to. + * @return {boolean | Promise} Whether the handler is enabled. + */ + protected isHandlerEnabledForCourse(courseId: number, restrictEnrolled?: boolean, restrict?: any): boolean | Promise { + if (restrict && restrict.courses && restrict.courses.indexOf(courseId) == -1) { + // Course is not in the list of restricted courses. + return false; + } + + if (restrictEnrolled || typeof restrictEnrolled == 'undefined') { + // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course. + return this.coursesProvider.getUserCourse(courseId, true).then(() => { + return true; + }).catch(() => { + return false; + }); + } + + return true; + } + + /** + * Check if a handler is enabled for a certain user. + * + * @param {number} userId User ID to check. + * @param {boolean} [restrictCurrent] Whether handler is only enabled for current user. + * @param {any} [restrict] Users and courses the handler is restricted to. + * @return {boolean} Whether the handler is enabled. + */ + protected isHandlerEnabledForUser(userId: number, restrictCurrent?: boolean, restrict?: any): boolean { + if (restrictCurrent && userId != this.sitesProvider.getCurrentSite().getUserId()) { + // Only enabled for current user. + return false; + } + + if (restrict && restrict.users && restrict.users.indexOf(userId) == -1) { + // User is not in the list of restricted users. + return false; + } + + return true; + } + /** * Check if a certain addon is a site addon and it's enabled in a certain site. * @@ -227,6 +279,11 @@ export class CoreSiteAddonsHelperProvider { result.restrict); break; + case 'CoreCourseOptionsDelegate': + uniqueName = this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result.jsResult, + result.restrict); + break; + default: // Nothing to do. } @@ -243,6 +300,60 @@ export class CoreSiteAddonsHelperProvider { }); } + /** + * Given a handler in an addon, register it in the course options delegate. + * + * @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. + * @param {any} [restrict] List of users and courses the handler is restricted to. + * @return {string} A string to identify the handler. + */ + protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, + restrict?: any): string { + if (!handlerSchema || !handlerSchema.displaydata) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + baseHandler = this.getBaseHandler(uniqueName), + prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); + let handler: CoreCourseOptionsHandler; + + // Extend the base handler, adding the properties required by the delegate. + handler = Object.assign(baseHandler, { + priority: handlerSchema.priority, + isEnabledForCourse: (courseId: number, accessData: any, navOptions?: any, admOptions?: any) + : boolean | Promise => { + return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict); + }, + getDisplayData: (courseId: number): CoreCourseOptionsHandlerData => { + return { + title: prefixedTitle, + class: handlerSchema.displaydata.class, + component: CoreSiteAddonsCourseOptionComponent, + componentData: { + handlerUniqueName: uniqueName + } + }; + }, + prefetch: (course: any): Promise => { + const args = { + courseid: course.id, + }; + + return this.siteAddonsProvider.prefetchFunctions(addon.component, args, handlerSchema, course.id, undefined, true); + } + }); + + this.courseOptionsDelegate.registerHandler(handler); + + return uniqueName; + } + /** * Given a handler in an addon, register it in the main menu delegate. * @@ -378,31 +489,14 @@ export class CoreSiteAddonsHelperProvider { priority: handlerSchema.priority, type: handlerSchema.type, isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => { - if (handlerSchema.restricttocurrentuser && user.id != this.sitesProvider.getCurrentSite().getUserId()) { - // Only enabled for current user. + // First check if it's enabled for the user. + const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, restrict); + if (!enabledForUser) { return false; } - if (restrict) { - if (restrict.users && restrict.users.indexOf(user.id) == -1) { - // User is not in the list of restricted users. - return false; - } else if (restrict.courses && restrict.courses.indexOf(courseId) == -1) { - // Course is not in the list of restricted courses. - return false; - } - } - - if (handlerSchema.restricttoenrolledcourses || typeof handlerSchema.restricttoenrolledcourses == 'undefined') { - // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course. - return this.coursesProvider.getUserCourse(courseId, true).then(() => { - return true; - }).catch(() => { - return false; - }); - } - - return true; + // Enabled for user, check if it's enabled for the course. + return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict); }, getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { return { diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index dd3bd05ae..94fca9258 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -15,6 +15,7 @@ import { Injectable } from '@angular/core'; import { Platform } from 'ionic-angular'; import { CoreAppProvider } from '../../../providers/app'; +import { CoreFilepoolProvider } from '../../../providers/filepool'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site'; @@ -62,7 +63,8 @@ export class CoreSiteAddonsProvider { 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) { + private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform, + private filepoolProvider: CoreFilepoolProvider) { this.logger = logger.getInstance('CoreUserProvider'); } @@ -207,6 +209,30 @@ export class CoreSiteAddonsProvider { return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + this.utils.sortAndStringify(args); } + /** + * Get the value of a WS param for prefetch. + * + * @param {string} component The component of the handler. + * @param {string} paramName Name of the param as defined by the handler. + * @param {number} [courseId] Course ID (if prefetching a course). + * @param {any} [module] The module object returned by WS (if prefetching a module). + * @return {any} The value. + */ + protected getDownloadParam(component: string, paramName: string, courseId?: number, module?: any): any { + switch (paramName) { + case 'courseids': + // The WS needs the list of course IDs. Create the list. + return [courseId]; + + case component + 'id': + // The WS needs the instance id. + return module && module.instance; + + default: + // No more params supported for now. + } + } + /** * Get the unique name of a handler (addon + handler). * @@ -320,6 +346,71 @@ export class CoreSiteAddonsProvider { return args; } + /** + * Prefetch offline functions for a site addon handler. + * + * @param {string} component The component of the handler. + * @param {any} args Params to send to the get_content calls. + * @param {any} handlerSchema The handler schema. + * @param {number} [courseId] Course ID (if prefetching a course). + * @param {any} [module] The module object returned by WS (if prefetching a module). + * @param {boolean} [prefetch] True to prefetch, false to download right away. + * @param {string} [dirPath] Path of the directory where to store all the content files. + * @param {CoreSite} [site] Site. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + prefetchFunctions(component: string, args: any, handlerSchema: any, courseId?: number, module?: any, prefetch?: boolean, + dirPath?: string, site?: CoreSite): Promise { + site = site || this.sitesProvider.getCurrentSite(); + + const promises = []; + + for (const method in handlerSchema.offlinefunctions) { + if (site.wsAvailable(method)) { + // The method is a WS. + const paramsList = handlerSchema.offlinefunctions[method], + cacheKey = this.getCallWSCacheKey(method, args); + let params = {}; + + if (!paramsList.length) { + // No params defined, send the default ones. + params = args; + } else { + for (const i in paramsList) { + const paramName = paramsList[i]; + + if (typeof args[paramName] != 'undefined') { + params[paramName] = args[paramName]; + } else { + // The param is not one of the default ones. Try to calculate the param to use. + const value = this.getDownloadParam(component, paramName, courseId, module); + if (typeof value != 'undefined') { + params[paramName] = value; + } + } + } + } + + promises.push(this.callWS(method, params, {cacheKey: cacheKey})); + } else { + // It's a method to get content. + promises.push(this.getContent(component, method, args).then((result) => { + const subPromises = []; + + // Prefetch the files in the content. + if (result.files && result.files.length) { + subPromises.push(this.filepoolProvider.downloadOrPrefetchFiles(site.id, result.files, prefetch, false, + component, module.id, dirPath)); + } + + return Promise.all(subPromises); + })); + } + } + + return Promise.all(promises); + } + /** * Store a site addon handler. * From 52a73f4138847edd115b3d09b3f1d06b7b7d508b Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 28 Feb 2018 16:14:37 +0100 Subject: [PATCH 20/32] MOBILE-2333 siteaddons: Support basic course format site addons --- .../components/components.module.ts | 10 ++- .../course-format/course-format.html | 1 + .../components/course-format/course-format.ts | 68 +++++++++++++++++++ src/core/siteaddons/providers/helper.ts | 55 ++++++++++++++- 4 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/core/siteaddons/components/course-format/course-format.html create mode 100644 src/core/siteaddons/components/course-format/course-format.ts diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts index 61021a6b9..15a6f3019 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteaddons/components/components.module.ts @@ -21,12 +21,14 @@ import { CoreCompileHtmlComponentModule } from '../../compile/components/compile import { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-content'; import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index'; import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-option'; +import { CoreSiteAddonsCourseFormatComponent } from './course-format/course-format'; @NgModule({ declarations: [ CoreSiteAddonsAddonContentComponent, CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent + CoreSiteAddonsCourseOptionComponent, + CoreSiteAddonsCourseFormatComponent ], imports: [ CommonModule, @@ -40,11 +42,13 @@ import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-opti exports: [ CoreSiteAddonsAddonContentComponent, CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent + CoreSiteAddonsCourseOptionComponent, + CoreSiteAddonsCourseFormatComponent ], entryComponents: [ CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent + CoreSiteAddonsCourseOptionComponent, + CoreSiteAddonsCourseFormatComponent ] }) export class CoreSiteAddonsComponentsModule {} diff --git a/src/core/siteaddons/components/course-format/course-format.html b/src/core/siteaddons/components/course-format/course-format.html new file mode 100644 index 000000000..69f8afc63 --- /dev/null +++ b/src/core/siteaddons/components/course-format/course-format.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/siteaddons/components/course-format/course-format.ts b/src/core/siteaddons/components/course-format/course-format.ts new file mode 100644 index 000000000..5dd0e1148 --- /dev/null +++ b/src/core/siteaddons/components/course-format/course-format.ts @@ -0,0 +1,68 @@ +// (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, OnInit, Input, ViewChild } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; + +/** + * Component that displays the index of a course format site addon. + */ +@Component({ + selector: 'core-site-addons-course-format', + templateUrl: 'course-format.html', +}) +export class CoreSiteAddonsCourseFormatComponent implements OnInit { + @Input() course: any; // The course to render. + @Input() sections: any[]; // List of course sections. + @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. + + @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; + + component: string; + method: string; + args: any; + bootstrapResult: any; + + constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + if (this.course && this.course.format) { + const handler = this.siteAddonsProvider.getSiteAddonHandler(this.course.format); + if (handler) { + this.component = handler.addon.component; + this.method = handler.handlerSchema.method; + this.args = { + courseid: this.course.id, + downloadenabled: this.downloadEnabled + }; + this.bootstrapResult = handler.bootstrapResult; + } + } + } + + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void): Promise { + return Promise.resolve(this.content.refreshData()); + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 66885636a..d51bfa002 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -27,10 +27,12 @@ import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module- import { CoreCourseOptionsDelegate, CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; +import { CoreCourseFormatDelegate, CoreCourseFormatHandler } from '../../course/providers/format-delegate'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; +import { CoreSiteAddonsCourseFormatComponent } from '../components/course-format/course-format'; import { CoreSiteAddonsProvider } from './siteaddons'; import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; import { CoreCompileProvider } from '../../compile/providers/compile'; @@ -51,7 +53,8 @@ export class CoreSiteAddonsHelperProvider { private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, - private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate) { + private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, + private courseFormatDelegate: CoreCourseFormatDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } @@ -284,6 +287,11 @@ export class CoreSiteAddonsHelperProvider { result.restrict); break; + case 'CoreCourseFormatDelegate': + uniqueName = this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result.jsResult, + result.restrict); + break; + default: // Nothing to do. } @@ -300,6 +308,51 @@ export class CoreSiteAddonsHelperProvider { }); } + /** + * Given a handler in an addon, register it in the course format delegate. + * + * @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. + * @param {any} [restrict] List of users and courses the handler is restricted to. + * @return {string} A string to identify the handler. + */ + protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, + restrict?: any): string { + if (!handlerSchema) { + // Required data not provided, stop. + return; + } + + // Create the base handler. + const formatName = addon.component.replace('format_', ''), + baseHandler = this.getBaseHandler(formatName); + let handler: CoreCourseFormatHandler; + + // Extend the base handler, adding the properties required by the delegate. + handler = Object.assign(baseHandler, { + canViewAllSections: (course: any): boolean => { + return typeof handlerSchema.canviewallsections != 'undefined' ? handlerSchema.canviewallsections : true; + }, + displayEnableDownload: (course: any): boolean => { + return typeof handlerSchema.displayenabledownload != 'undefined' ? handlerSchema.displayenabledownload : true; + }, + displaySectionSelector: (course: any): boolean => { + return typeof handlerSchema.displaysectionselector != 'undefined' ? handlerSchema.displaysectionselector : true; + }, + getCourseFormatComponent: (course: any): any => { + if (handlerSchema.method) { + return CoreSiteAddonsCourseFormatComponent; + } + } + }); + + this.courseFormatDelegate.registerHandler(handler); + + return formatName; + } + /** * Given a handler in an addon, register it in the course options delegate. * From ffa5a53fc627381fb6b3ca80ef931342923dd5b0 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 1 Mar 2018 13:08:27 +0100 Subject: [PATCH 21/32] MOBILE-2333 siteaddons: Support multiple templates in get_content --- .../addon-content/addon-content.html | 2 +- .../components/addon-content/addon-content.ts | 6 +- src/core/siteaddons/providers/helper.ts | 75 ++++++++----------- src/core/siteaddons/providers/siteaddons.ts | 31 +++++++- 4 files changed, 66 insertions(+), 48 deletions(-) diff --git a/src/core/siteaddons/components/addon-content/addon-content.html b/src/core/siteaddons/components/addon-content/addon-content.html index 6eb252ed4..a22885173 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 32db323e0..557ba1258 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteaddons/components/addon-content/addon-content.ts @@ -28,7 +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. + @Input() bootstrapResult: any; // Result of the bootstrap WS call 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. @@ -37,6 +37,7 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { otherData: any; // Other data of the content. dataLoaded: boolean; invalidateObservable: Subject; // An observable to notify observers when to invalidate data. + jsData: any; // Data to pass to the component. constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { this.onContentLoaded = new EventEmitter(); @@ -61,9 +62,10 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { this.onLoadingContent.emit(refresh); return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { - this.content = result.html; + this.content = result.templates.length ? result.templates[0].html : ''; // Load first template. this.javascript = result.javascript; this.otherData = result.otherdata; + this.jsData = this.siteAddonsProvider.createDataForJS(this.bootstrapResult, result); this.onContentLoaded.emit(refresh); }).catch((error) => { diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index d51bfa002..c8e0b6c18 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -64,10 +64,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. - * @return {Promise<{restrict?: any, jsResult?: any}>} Promise resolved when done. It returns the "restrict" of the handler and - * the result of the javascript execution (if any). + * @return {Promise} Promise resolved when done. It returns the results of the getContent call and the data returned by + * the bootstrap JS (if any). */ - protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise<{restrict?: any, jsResult?: any}> { + protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise { if (!handlerSchema.bootstrap) { return Promise.resolve({}); } @@ -76,24 +76,25 @@ export class CoreSiteAddonsHelperProvider { preSets = {getFromCache: false}; // Try to ignore cache. return this.siteAddonsProvider.getContent(addon.component, handlerSchema.bootstrap, {}, preSets).then((result) => { - const data = { - restrict: result.restrict, - jsResult: undefined - }; - if (!result.javascript || this.sitesProvider.getCurrentSiteId() != siteId) { // No javascript or site has changed, stop. - return data; + return result; } // Create a "fake" instance to hold all the libraries. const instance = {}; this.compileProvider.injectLibraries(instance); - // Now execute the javascript using this instance. - data.jsResult = this.compileProvider.executeJavascript(instance, result.javascript); + // Add some data of the WS call result. + const jsData = this.siteAddonsProvider.createDataForJS(result); + for (const name in jsData) { + instance[name] = jsData[name]; + } - return data; + // Now execute the javascript using this instance. + result.jsResult = this.compileProvider.executeJavascript(instance, result.javascript); + + return result; }); } @@ -270,26 +271,23 @@ export class CoreSiteAddonsHelperProvider { switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': - uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result.jsResult, result.restrict); + uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result); break; case 'CoreCourseModuleDelegate': - uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result.jsResult, result.restrict); + uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result); break; case 'CoreUserDelegate': - uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result.jsResult, - result.restrict); + uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result); break; case 'CoreCourseOptionsDelegate': - uniqueName = this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result.jsResult, - result.restrict); + uniqueName = this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result); break; case 'CoreCourseFormatDelegate': - uniqueName = this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result.jsResult, - result.restrict); + uniqueName = this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result); break; default: @@ -314,12 +312,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. - * @param {any} [restrict] List of users and courses the handler is restricted to. + * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, - restrict?: any): string { + protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema) { // Required data not provided, stop. return; @@ -359,12 +355,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. - * @param {any} [restrict] List of users and courses the handler is restricted to. + * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, - restrict?: any): string { + protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -381,7 +375,7 @@ export class CoreSiteAddonsHelperProvider { priority: handlerSchema.priority, isEnabledForCourse: (courseId: number, accessData: any, navOptions?: any, admOptions?: any) : boolean | Promise => { - return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict); + return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, bootstrapResult.restrict); }, getDisplayData: (courseId: number): CoreCourseOptionsHandlerData => { return { @@ -413,12 +407,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. - * @param {any} [restrict] List of users and courses the handler is restricted to. + * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) - : string { + protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -460,12 +452,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. - * @param {any} [restrict] List of users and courses the handler is restricted to. + * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) - : string { + protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -520,12 +510,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. - * @param {any} [restrict] List of users and courses the handler is restricted to. + * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult?: any, restrict?: any) - : string { + protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema || !handlerSchema.displaydata) { // Required data not provided, stop. return; @@ -543,13 +531,14 @@ export class CoreSiteAddonsHelperProvider { type: handlerSchema.type, isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => { // First check if it's enabled for the user. - const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, restrict); + const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, + bootstrapResult.restrict); if (!enabledForUser) { return false; } // Enabled for user, check if it's enabled for the course. - return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, restrict); + return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, bootstrapResult.restrict); }, getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { return { diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 94fca9258..f6051d5d4 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -46,7 +46,7 @@ export interface CoreSiteAddonsHandler { handlerSchema: any; /** - * Result of executing the bootstrap JS. + * Result of the bootstrap WS call. * @type {any} */ bootstrapResult?: any; @@ -134,6 +134,31 @@ export class CoreSiteAddonsProvider { }); } + /** + * Given the result of a bootstrap get_content and, optionally, the result of another get_content, + * build an object with the data to pass to the JS of the get_content. + * + * @param {any} bootstrapResult Result of the bootstrap WS call. + * @param {any} [contentResult] Result of the content WS call (if any). + * @return {any} An object with the data to pass to the JS. + */ + createDataForJS(bootstrapResult: any, contentResult?: any): any { + // First of all, add the data returned by the bootstrap JS (if any). + const data = this.utils.clone(bootstrapResult.jsResult || {}); + + // Now add some data returned by the bootstrap WS call. + data.BOOTSTRAP_TEMPLATES = this.utils.objectToKeyValueMap(bootstrapResult.templates, 'id', 'html'); + data.BOOTSTRAP_OTHERDATA = bootstrapResult.otherdata; + + if (contentResult) { + // Now add the data returned by the content WS call. + data.CONTENT_TEMPLATES = this.utils.objectToKeyValueMap(contentResult.templates, 'id', 'html'); + data.CONTENT_OTHERDATA = contentResult.otherdata; + } + + return data; + } + /** * Get cache key for a WS call. * @@ -186,10 +211,12 @@ export class CoreSiteAddonsProvider { }).then((result) => { if (result.otherdata) { try { - result.otherdata = JSON.parse(result.otherdata); + result.otherdata = JSON.parse(result.otherdata) || {}; } catch (ex) { // Ignore errors. } + } else { + result.otherdata = {}; } return result; From d98088f6a6749621dcd40f34df521744546be56d Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 2 Mar 2018 15:25:00 +0100 Subject: [PATCH 22/32] MOBILE-2333 siteaddons: Support creating new components Now site addons can return a ComponentRef instead of the component class in the getComponent functions of the delegates. This means they can create a new component on the fly using CoreCompileProvider.instantiateDynamicComponent --- src/addon/mod/book/components/index/index.ts | 2 +- .../mod/book/providers/module-handler.ts | 8 +- .../mod/label/providers/module-handler.ts | 8 +- .../checkbox/providers/handler.ts | 8 +- .../datetime/providers/handler.ts | 8 +- .../menu/providers/handler.ts | 8 +- .../text/providers/handler.ts | 8 +- .../textarea/providers/handler.ts | 8 +- .../dynamic-component/dynamic-component.ts | 45 +++++++--- src/components/split-view/split-view.ts | 4 +- .../components/compile-html/compile-html.ts | 68 ++++---------- src/core/compile/providers/compile.ts | 90 ++++++++++++++++++- src/core/course/components/format/format.ts | 24 +++-- src/core/course/components/module/module.ts | 4 +- .../components/singleactivity.ts | 9 +- .../singleactivity/providers/handler.ts | 8 +- src/core/course/pages/section/section.ts | 7 +- src/core/course/providers/format-delegate.ts | 78 ++++++++++------ src/core/course/providers/helper.ts | 8 +- src/core/course/providers/module-delegate.ts | 20 +++-- src/core/course/providers/options-delegate.ts | 27 +++--- .../course-list-item/course-list-item.ts | 5 +- .../course-progress/course-progress.ts | 4 +- .../overview-events/overview-events.ts | 9 +- src/core/grades/components/course/course.ts | 5 +- .../grades/providers/course-option-handler.ts | 8 +- .../directives/call-ws-new-content.ts | 2 +- .../siteaddons/directives/call-ws-on-load.ts | 1 - src/core/siteaddons/directives/new-content.ts | 2 +- src/core/siteaddons/providers/helper.ts | 10 ++- src/core/siteaddons/providers/siteaddons.ts | 5 +- .../user-profile-field/user-profile-field.ts | 9 +- .../user/providers/course-option-handler.ts | 8 +- .../providers/user-profile-field-delegate.ts | 22 +++-- src/directives/auto-focus.ts | 4 +- src/directives/download-file.ts | 1 - src/directives/format-text.ts | 2 +- src/directives/link.ts | 2 +- src/directives/user-link.ts | 4 +- 39 files changed, 353 insertions(+), 200 deletions(-) diff --git a/src/addon/mod/book/components/index/index.ts b/src/addon/mod/book/components/index/index.ts index 8e2b4bb92..1183a04e4 100644 --- a/src/addon/mod/book/components/index/index.ts +++ b/src/addon/mod/book/components/index/index.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, Optional } from '@angular/core'; -import { NavParams, NavController, Content, PopoverController } from 'ionic-angular'; +import { Content, PopoverController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; diff --git a/src/addon/mod/book/providers/module-handler.ts b/src/addon/mod/book/providers/module-handler.ts index d88e95a24..e440fcf33 100644 --- a/src/addon/mod/book/providers/module-handler.ts +++ b/src/addon/mod/book/providers/module-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; import { AddonModBookProvider } from './book'; import { AddonModBookIndexComponent } from '../components/index/index'; @@ -60,12 +60,14 @@ export class AddonModBookModuleHandler implements CoreCourseModuleHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. * The component returned must implement CoreCourseModuleMainComponent. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course object. * @param {any} module The module object. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getMainComponent(course: any, module: any): any { + getMainComponent(injector: Injector, course: any, module: any): any | Promise { return AddonModBookIndexComponent; } } diff --git a/src/addon/mod/label/providers/module-handler.ts b/src/addon/mod/label/providers/module-handler.ts index 90cfdd780..06ab1e7f7 100644 --- a/src/addon/mod/label/providers/module-handler.ts +++ b/src/addon/mod/label/providers/module-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; /** @@ -58,12 +58,14 @@ export class AddonModLabelModuleHandler implements CoreCourseModuleHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. * The component returned must implement CoreCourseModuleMainComponent. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course object. * @param {any} module The module object. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getMainComponent(course: any, module: any): any { + getMainComponent(injector: Injector, course: any, module: any): any | Promise { // There's no need to implement this because label cannot be used in singleactivity course format. } } diff --git a/src/addon/userprofilefield/checkbox/providers/handler.ts b/src/addon/userprofilefield/checkbox/providers/handler.ts index a3dbb51e3..b160a08bd 100644 --- a/src/addon/userprofilefield/checkbox/providers/handler.ts +++ b/src/addon/userprofilefield/checkbox/providers/handler.ts @@ -12,7 +12,7 @@ // 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 { Injectable, Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldCheckboxComponent } from '../component/checkbox'; @@ -60,10 +60,12 @@ export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFiel /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any { + getComponent(injector: Injector): any | Promise { return AddonUserProfileFieldCheckboxComponent; } } diff --git a/src/addon/userprofilefield/datetime/providers/handler.ts b/src/addon/userprofilefield/datetime/providers/handler.ts index d8135b94d..819d75f4c 100644 --- a/src/addon/userprofilefield/datetime/providers/handler.ts +++ b/src/addon/userprofilefield/datetime/providers/handler.ts @@ -12,7 +12,7 @@ // 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 { Injectable, Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldDatetimeComponent } from '../component/datetime'; @@ -62,10 +62,12 @@ export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFiel /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any { + getComponent(injector: Injector): any | Promise { return AddonUserProfileFieldDatetimeComponent; } } diff --git a/src/addon/userprofilefield/menu/providers/handler.ts b/src/addon/userprofilefield/menu/providers/handler.ts index 931cef566..c02edb812 100644 --- a/src/addon/userprofilefield/menu/providers/handler.ts +++ b/src/addon/userprofilefield/menu/providers/handler.ts @@ -12,7 +12,7 @@ // 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 { Injectable, Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldMenuComponent } from '../component/menu'; @@ -60,10 +60,12 @@ export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHan /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any { + getComponent(injector: Injector): any | Promise { return AddonUserProfileFieldMenuComponent; } } diff --git a/src/addon/userprofilefield/text/providers/handler.ts b/src/addon/userprofilefield/text/providers/handler.ts index 1953660ce..7f9c3df5e 100644 --- a/src/addon/userprofilefield/text/providers/handler.ts +++ b/src/addon/userprofilefield/text/providers/handler.ts @@ -12,7 +12,7 @@ // 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 { Injectable, Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextComponent } from '../component/text'; @@ -57,10 +57,12 @@ export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHan /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any { + getComponent(injector: Injector): any | Promise { return AddonUserProfileFieldTextComponent; } } diff --git a/src/addon/userprofilefield/textarea/providers/handler.ts b/src/addon/userprofilefield/textarea/providers/handler.ts index 1c07057db..c945e07aa 100644 --- a/src/addon/userprofilefield/textarea/providers/handler.ts +++ b/src/addon/userprofilefield/textarea/providers/handler.ts @@ -12,7 +12,7 @@ // 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 { Injectable, Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../../../core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextareaComponent } from '../component/textarea'; @@ -66,10 +66,12 @@ export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFiel /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any { + getComponent(injector: Injector): any | Promise { return AddonUserProfileFieldTextareaComponent; } } diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts index f2a045043..b8dd46fa2 100644 --- a/src/components/dynamic-component/dynamic-component.ts +++ b/src/components/dynamic-component/dynamic-component.ts @@ -13,9 +13,10 @@ // limitations under the License. import { - Component, Input, ViewChild, OnInit, OnChanges, DoCheck, ViewContainerRef, ComponentFactoryResolver, - KeyValueDiffers, SimpleChange + Component, Input, ViewChild, OnInit, OnChanges, DoCheck, ViewContainerRef, ComponentFactoryResolver, ComponentRef, + KeyValueDiffers, SimpleChange, ChangeDetectorRef, Optional, ElementRef } from '@angular/core'; +import { NavController } from 'ionic-angular'; import { CoreLoggerProvider } from '@providers/logger'; /** @@ -39,6 +40,10 @@ import { CoreLoggerProvider } from '@providers/logger'; * * Please notice that the component that you pass needs to be declared in entryComponents of the module to be created dynamically. * + * Alternatively, you can also supply a ComponentRef instead of the class of the component. In this case, the component won't + * be instantiated because it already is, it will be attached to the view and the right data will be passed to it. + * Passing ComponentRef is meant for site addons, so we'll inject a NavController instance to the component. + * * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, * if no component is supplied then the template will show the message "Cannot render the data.". */ @@ -62,7 +67,8 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { protected logger: any; protected differ: any; // To detect changes in the data input. - constructor(logger: CoreLoggerProvider, private factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers) { + constructor(logger: CoreLoggerProvider, protected factoryResolver: ComponentFactoryResolver, differs: KeyValueDiffers, + @Optional() protected navCtrl: NavController, protected cdr: ChangeDetectorRef, protected element: ElementRef) { this.logger = logger.getInstance('CoreDynamicComponent'); this.differ = differs.find([]).create(); } @@ -128,21 +134,32 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { return true; } - try { - // Create the component and add it to the container. - const factory = this.factoryResolver.resolveComponentFactory(this.component), - componentRef = this.container.createComponent(factory); + if (this.component instanceof ComponentRef) { + // A ComponentRef was supplied instead of the component class. Add it to the view. + this.container.insert(this.component.hostView); + this.instance = this.component.instance; - this.instance = componentRef.instance; + // This feature is usually meant for site addons. Inject some properties. + this.instance['ChangeDetectorRef'] = this.cdr; + this.instance['NavController'] = this.navCtrl; + this.instance['componentContainer'] = this.element.nativeElement; + } else { + try { + // Create the component and add it to the container. + const factory = this.factoryResolver.resolveComponentFactory(this.component), + componentRef = this.container.createComponent(factory); - this.setInputData(); + this.instance = componentRef.instance; + } catch (ex) { + this.logger.error('Error creating component', ex); - return true; - } catch (ex) { - this.logger.error('Error creating component', ex); - - return false; + return false; + } } + + this.setInputData(); + + return true; } /** diff --git a/src/components/split-view/split-view.ts b/src/components/split-view/split-view.ts index 0ab53cca2..7d5a936b7 100644 --- a/src/components/split-view/split-view.ts +++ b/src/components/split-view/split-view.ts @@ -14,7 +14,7 @@ // Code based on https://github.com/martinpritchardelevate/ionic-split-pane-demo -import { Component, ViewChild, Input, ElementRef, OnInit } from '@angular/core'; +import { Component, ViewChild, Input, ElementRef, OnInit, Optional } from '@angular/core'; import { NavController, Nav } from 'ionic-angular'; /** @@ -54,7 +54,7 @@ export class CoreSplitViewComponent implements OnInit { // Empty placeholder for the 'detail' page. detailPage: any = null; - constructor(private masterNav: NavController, element: ElementRef) { + constructor(@Optional() private masterNav: NavController, element: ElementRef) { this.element = element.nativeElement; } diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts index 8253e2a92..a7773a965 100644 --- a/src/core/compile/components/compile-html/compile-html.ts +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -13,24 +13,12 @@ // limitations under the License. import { - Component, NgModule, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, - SimpleChange, ChangeDetectorRef + Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ViewChild, ComponentRef, SimpleChange, ChangeDetectorRef, + ElementRef, Optional } from '@angular/core'; -import { IonicModule, NavController } from 'ionic-angular'; -import { TranslateModule } from '@ngx-translate/core'; +import { NavController } from 'ionic-angular'; 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. @@ -51,13 +39,6 @@ import { CoreUserComponentsModule } from '../../../user/components/components.mo 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. @@ -66,9 +47,12 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { @ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef; protected componentRef: ComponentRef; + protected element; - constructor(protected compileProvider: CoreCompileProvider, protected compiler: Compiler, - protected cdr: ChangeDetectorRef, protected navCtrl: NavController) { } + constructor(protected compileProvider: CoreCompileProvider, protected cdr: ChangeDetectorRef, element: ElementRef, + @Optional() protected navCtrl: NavController) { + this.element = element.nativeElement; + } /** * Detect changes on input properties. @@ -76,26 +60,14 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { 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; - } - } - + this.compileProvider.createAndCompileComponent(this.text, this.getComponentClass()).then((factory) => { // Destroy previous components. this.componentRef && this.componentRef.destroy(); - // Create the component. - this.componentRef = this.container.createComponent(componentFactory); + if (factory) { + // Create the component. + this.componentRef = this.container.createComponent(factory); + } }); } } @@ -108,20 +80,16 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { } /** - * Create a dynamic component to compile the HTML and run the javascript. + * Get a class that defines the dynamic component. * * @return {any} The component class. */ - protected createComponent(): any { + protected getComponentClass(): 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 { - + return class CoreCompileHtmlFakeComponent implements OnInit { constructor() { // If there is some javascript to run, prepare the instance. if (compileInstance.javascript) { @@ -130,7 +98,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { // Add some more components and classes. this['ChangeDetectorRef'] = compileInstance.cdr; this['NavController'] = compileInstance.navCtrl; - this['componentContainer'] = compileInstance.container; + this['componentContainer'] = compileInstance.element; // Add the data passed to the component. for (const name in compileInstance.jsData) { @@ -145,6 +113,6 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { compileInstance.compileProvider.executeJavascript(this, compileInstance.javascript); } } - }); + }; } } diff --git a/src/core/compile/providers/compile.ts b/src/core/compile/providers/compile.ts index 25738aa15..1a7e10015 100644 --- a/src/core/compile/providers/compile.ts +++ b/src/core/compile/providers/compile.ts @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable, Injector } from '@angular/core'; +import { Injectable, Injector, Component, NgModule, Compiler, ComponentFactory, ComponentRef, NgModuleRef } from '@angular/core'; import { - Platform, ActionSheetController, AlertController, LoadingController, ModalController, PopoverController, ToastController + Platform, ActionSheetController, AlertController, LoadingController, ModalController, PopoverController, ToastController, + IonicModule } from 'ionic-angular'; -import { TranslateService } from '@ngx-translate/core'; +import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { CoreLoggerProvider } from '../../../providers/logger'; // Import core providers. @@ -55,6 +56,24 @@ import { CoreContentLinksModuleGradeHandler } from '../../contentlinks/classes/m import { CoreContentLinksModuleIndexHandler } from '../../contentlinks/classes/module-index-handler'; import { CoreCourseModulePrefetchHandlerBase } from '../../course/classes/module-prefetch-handler'; +// 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'; + +// Import some components listed in entryComponents so they can be injected dynamically. +import { CoreCourseUnsupportedModuleComponent } from '../../course/components/unsupported-module/unsupported-module'; +import { CoreCourseFormatSingleActivityComponent } from '../../course/formats/singleactivity/components/singleactivity'; +import { CoreSiteAddonsModuleIndexComponent } from '../../siteaddons/components/module-index/module-index'; +import { CoreSiteAddonsCourseOptionComponent } from '../../siteaddons/components/course-option/course-option'; +import { CoreSiteAddonsCourseFormatComponent } from '../../siteaddons/components/course-format/course-format'; + /** * Service to provide functionalities regarding compiling dynamic HTML and Javascript. */ @@ -69,10 +88,46 @@ export class CoreCompileProvider { ModalController, PopoverController, ToastController, FormBuilder ]; - constructor(protected injector: Injector, logger: CoreLoggerProvider) { + // 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 + ]; + + constructor(protected injector: Injector, logger: CoreLoggerProvider, protected compiler: Compiler) { this.logger = logger.getInstance('CoreCompileProvider'); } + /** + * Create and compile a dynamic component. + * + * @param {string} template The template of the component. + * @param {any} componentClass The JS class of the component. + * @return {Promise>} Promise resolved with the factory to instantiate the component. + */ + createAndCompileComponent(template: string, componentClass: any): Promise> { + // Create the component using the template and the class. + const component = Component({ + template: template + }) + (componentClass); + + // Now create the module containing the component. + const module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {}); + + // Compile the module and the component. + return this.compiler.compileModuleAndAllComponentsAsync(module).then((factories) => { + // Search and return the factory of the component we just created. + for (const i in factories.componentFactories) { + const factory = factories.componentFactories[i]; + if (factory.componentType == component) { + return factory; + } + } + }); + } + /** * Eval some javascript using the context of the function. * @@ -124,6 +179,9 @@ export class CoreCompileProvider { } } + // Inject current service. + instance['CoreCompileProvider'] = this; + // Add some final classes. instance['injector'] = this.injector; instance['Validators'] = Validators; @@ -138,5 +196,29 @@ export class CoreCompileProvider { instance['CoreContentLinksModuleGradeHandler'] = CoreContentLinksModuleGradeHandler; instance['CoreContentLinksModuleIndexHandler'] = CoreContentLinksModuleIndexHandler; instance['CoreCourseModulePrefetchHandlerBase'] = CoreCourseModulePrefetchHandlerBase; + instance['CoreCourseUnsupportedModuleComponent'] = CoreCourseUnsupportedModuleComponent; + instance['CoreCourseFormatSingleActivityComponent'] = CoreCourseFormatSingleActivityComponent; + instance['CoreSiteAddonsModuleIndexComponent'] = CoreSiteAddonsModuleIndexComponent; + instance['CoreSiteAddonsCourseOptionComponent'] = CoreSiteAddonsCourseOptionComponent; + instance['CoreSiteAddonsCourseFormatComponent'] = CoreSiteAddonsCourseFormatComponent; + } + + /** + * Instantiate a dynamic component. + * + * @param {string} template The template of the component. + * @param {any} componentClass The JS class of the component. + * @param {Injector} [injector] The injector to use. It's recommended to pass it so NavController and similar can be injected. + * @return {Promise>} Promise resolved with the component instance. + */ + instantiateDynamicComponent(template: string, componentClass: any, injector?: Injector): Promise> { + injector = injector || this.injector; + + return this.createAndCompileComponent(template, componentClass).then((factory) => { + if (factory) { + // Create and return the component. + return factory.create(injector, undefined, undefined, injector.get(NgModuleRef)); + } + }); } } diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index b4a2985a9..51cd9555c 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -13,7 +13,7 @@ // limitations under the License. import { - Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter, ViewChildren, QueryList + Component, Input, OnInit, OnChanges, OnDestroy, SimpleChange, Output, EventEmitter, ViewChildren, QueryList, Injector } from '@angular/core'; import { Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; @@ -69,7 +69,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { protected sectionStatusObserver; - constructor(private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, + constructor(private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, private injector: Injector, private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider, eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, private content: Content, prefetchDelegate: CoreCourseModulePrefetchDelegate) { @@ -194,19 +194,29 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { protected getComponents(): void { if (this.course) { if (!this.courseFormatComponent) { - this.courseFormatComponent = this.cfDelegate.getCourseFormatComponent(this.course); + this.cfDelegate.getCourseFormatComponent(this.injector, this.course).then((component) => { + this.courseFormatComponent = component; + }); } if (!this.courseSummaryComponent) { - this.courseSummaryComponent = this.cfDelegate.getCourseSummaryComponent(this.course); + this.cfDelegate.getCourseSummaryComponent(this.injector, this.course).then((component) => { + this.courseSummaryComponent = component; + }); } if (!this.sectionSelectorComponent) { - this.sectionSelectorComponent = this.cfDelegate.getSectionSelectorComponent(this.course); + this.cfDelegate.getSectionSelectorComponent(this.injector, this.course).then((component) => { + this.sectionSelectorComponent = component; + }); } if (!this.singleSectionComponent) { - this.singleSectionComponent = this.cfDelegate.getSingleSectionComponent(this.course); + this.cfDelegate.getSingleSectionComponent(this.injector, this.course).then((component) => { + this.singleSectionComponent = component; + }); } if (!this.allSectionsComponent) { - this.allSectionsComponent = this.cfDelegate.getAllSectionsComponent(this.course); + this.cfDelegate.getAllSectionsComponent(this.injector, this.course).then((component) => { + this.allSectionsComponent = component; + }); } } } diff --git a/src/core/course/components/module/module.ts b/src/core/course/components/module/module.ts index 00d0bbf1f..fef73a640 100644 --- a/src/core/course/components/module/module.ts +++ b/src/core/course/components/module/module.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; +import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; @@ -67,7 +67,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { protected prefetchHandler: CoreCourseModulePrefetchHandler; protected statusObserver; - constructor(protected navCtrl: NavController, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, + constructor(@Optional() protected navCtrl: NavController, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, protected domUtils: CoreDomUtilsProvider, protected courseHelper: CoreCourseHelperProvider, protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider) { this.completionChanged = new EventEmitter(); diff --git a/src/core/course/formats/singleactivity/components/singleactivity.ts b/src/core/course/formats/singleactivity/components/singleactivity.ts index a94f8730f..a68a60fda 100644 --- a/src/core/course/formats/singleactivity/components/singleactivity.ts +++ b/src/core/course/formats/singleactivity/components/singleactivity.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnChanges, SimpleChange, ViewChild } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChange, ViewChild, Injector } from '@angular/core'; import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module'; import { CoreDynamicComponent } from '../../../../../components/dynamic-component/dynamic-component'; @@ -36,7 +36,7 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges { componentClass: any; // The class of the component to render. data: any = {}; // Data to pass to the component. - constructor(private moduleDelegate: CoreCourseModuleDelegate) { } + constructor(private moduleDelegate: CoreCourseModuleDelegate, private injector: Injector) { } /** * Detect changes on input properties. @@ -47,8 +47,9 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges { const module = this.sections[0] && this.sections[0].modules && this.sections[0].modules[0]; if (module && !this.componentClass) { // We haven't obtained the class yet. Get it now. - this.componentClass = this.moduleDelegate.getMainComponent(this.course, module) || - CoreCourseUnsupportedModuleComponent; + this.moduleDelegate.getMainComponent(this.injector, this.course, module).then((component) => { + this.componentClass = component || CoreCourseUnsupportedModuleComponent; + }); } this.data.courseId = this.course.id; diff --git a/src/core/course/formats/singleactivity/providers/handler.ts b/src/core/course/formats/singleactivity/providers/handler.ts index ed7d44eef..9dd528021 100644 --- a/src/core/course/formats/singleactivity/providers/handler.ts +++ b/src/core/course/formats/singleactivity/providers/handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { CoreCourseFormatHandler } from '../../../providers/format-delegate'; import { CoreCourseFormatSingleActivityComponent } from '../components/singleactivity'; @@ -86,11 +86,13 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa * Return the Component to use to display the course format instead of using the default one. * Use it if you want to display a format completely different from the default one. * If you want to customize the default format there are several methods to customize parts of it. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getCourseFormatComponent(course: any): any { + getCourseFormatComponent(injector: Injector, course: any): any | Promise { return CoreCourseFormatSingleActivityComponent; } } diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 093e0a29f..ac47adf2b 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild, OnDestroy } from '@angular/core'; +import { Component, ViewChild, OnDestroy, Injector } from '@angular/core'; import { IonicPage, NavParams, Content, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; @@ -63,7 +63,7 @@ export class CoreCourseSectionPage implements OnDestroy { private courseFormatDelegate: CoreCourseFormatDelegate, private courseOptionsDelegate: CoreCourseOptionsDelegate, private translate: TranslateService, private courseHelper: CoreCourseHelperProvider, eventsProvider: CoreEventsProvider, private textUtils: CoreTextUtilsProvider, private coursesProvider: CoreCoursesProvider, - sitesProvider: CoreSitesProvider, private navCtrl: NavController, + sitesProvider: CoreSitesProvider, private navCtrl: NavController, private injector: Injector, private prefetchDelegate: CoreCourseModulePrefetchDelegate) { this.course = navParams.get('course'); this.sectionId = navParams.get('sectionId'); @@ -191,7 +191,8 @@ export class CoreCourseSectionPage implements OnDestroy { })); // Load the course handlers. - promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, false).then((handlers) => { + promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false) + .then((handlers) => { // Add the courseId to the handler component data. handlers.forEach((handler) => { handler.data.componentData = handler.data.componentData || {}; diff --git a/src/core/course/providers/format-delegate.ts b/src/core/course/providers/format-delegate.ts index 2398f4192..55f064f2a 100644 --- a/src/core/course/providers/format-delegate.ts +++ b/src/core/course/providers/format-delegate.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; @@ -85,44 +85,54 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler { * Return the Component to use to display the course format instead of using the default one. * Use it if you want to display a format completely different from the default one. * If you want to customize the default format there are several methods to customize parts of it. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getCourseFormatComponent?(course: any): any; + getCourseFormatComponent?(injector: Injector, course: any): any | Promise; /** * Return the Component to use to display the course summary inside the default course format. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getCourseSummaryComponent?(course: any): any; + getCourseSummaryComponent?(injector: Injector, course: any): any | Promise; /** * Return the Component to use to display the section selector inside the default course format. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getSectionSelectorComponent?(course: any): any; + getSectionSelectorComponent?(injector: Injector, course: any): any | Promise; /** * Return the Component to use to display a single section. This component will only be used if the user is viewing a * single section. If all the sections are displayed at once then it won't be used. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getSingleSectionComponent?(course: any): any; + getSingleSectionComponent?(injector: Injector, course: any): any | Promise; /** * Return the Component to use to display all sections in a course. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getAllSectionsComponent?(course: any): any; + getAllSectionsComponent?(injector: Injector, course: any): any | Promise; /** * Invalidate the data required to load the course format. @@ -199,31 +209,40 @@ export class CoreCourseFormatDelegate extends CoreDelegate { /** * Get the component to use to display all sections in a course. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getAllSectionsComponent(course: any): any { - return this.executeFunction(course.format, 'getAllSectionsComponent', [course]); + getAllSectionsComponent(injector: Injector, course: any): Promise { + return Promise.resolve(this.executeFunction(course.format, 'getAllSectionsComponent', [injector, course])).catch((e) => { + this.logger.error('Error getting all sections component', e); + }); } /** * Get the component to use to display a course format. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getCourseFormatComponent(course: any): any { - return this.executeFunction(course.format, 'getCourseFormatComponent', [course]); + getCourseFormatComponent(injector: Injector, course: any): Promise { + return Promise.resolve(this.executeFunction(course.format, 'getCourseFormatComponent', [injector, course])).catch((e) => { + this.logger.error('Error getting course format component', e); + }); } /** * Get the component to use to display the course summary in the default course format. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getCourseSummaryComponent(course: any): any { - return this.executeFunction(course.format, 'getCourseSummaryComponent', [course]); + getCourseSummaryComponent(injector: Injector, course: any): Promise { + return Promise.resolve(this.executeFunction(course.format, 'getCourseSummaryComponent', [injector, course])).catch((e) => { + this.logger.error('Error getting course summary component', e); + }); } /** @@ -259,22 +278,29 @@ export class CoreCourseFormatDelegate extends CoreDelegate { /** * Get the component to use to display the section selector inside the default course format. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getSectionSelectorComponent(course: any): any { - return this.executeFunction(course.format, 'getSectionSelectorComponent', [course]); + getSectionSelectorComponent(injector: Injector, course: any): Promise { + return Promise.resolve(this.executeFunction(course.format, 'getSectionSelectorComponent', [injector, course])) + .catch((e) => { + this.logger.error('Error getting section selector component', e); + }); } /** * Get the component to use to display a single section. This component will only be used if the user is viewing * a single section. If all the sections are displayed at once then it won't be used. * + * @param {Injector} injector Injector. * @param {any} course The course to render. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getSingleSectionComponent(course: any): any { - return this.executeFunction(course.format, 'getSingleSectionComponent', [course]); + getSingleSectionComponent(injector: Injector, course: any): Promise { + return Promise.resolve(this.executeFunction(course.format, 'getSingleSectionComponent', [injector, course])).catch((e) => { + this.logger.error('Error getting single section component', e); + }); } /** diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index 3c015a056..c883ed13b 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; @@ -120,7 +120,7 @@ export class CoreCourseHelperProvider { private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider, private eventsProvider: CoreEventsProvider, private fileHelper: CoreFileHelperProvider, - private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider) { } + private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private injector: Injector) { } /** * This function treats every module on the sections provided to load the handler data, treat completion @@ -275,7 +275,7 @@ export class CoreCourseHelperProvider { if (courseHandlers) { promise = Promise.resolve(courseHandlers); } else { - promise = this.courseOptionsDelegate.getHandlersToDisplay(course); + promise = this.courseOptionsDelegate.getHandlersToDisplay(this.injector, course); } return promise.then((handlers: CoreCourseOptionsHandlerToDisplay[]) => { @@ -323,7 +323,7 @@ export class CoreCourseHelperProvider { subPromises.push(this.courseProvider.getSections(course.id, false, true).then((courseSections) => { sections = courseSections; })); - subPromises.push(this.courseOptionsDelegate.getHandlersToDisplay(course).then((cHandlers) => { + subPromises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, course).then((cHandlers) => { handlers = cHandlers; })); diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts index 53f910b08..27cc1eab8 100644 --- a/src/core/course/providers/module-delegate.ts +++ b/src/core/course/providers/module-delegate.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; @@ -38,12 +38,14 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler { /** * Get the component to render the module. This is needed to support singleactivity course format. * The component returned must implement CoreCourseModuleMainComponent. + * It's recommended to return the class of the component, but you can also return an instance of the component. * + * @param {Injector} injector Injector. * @param {any} course The course object. * @param {any} module The module object. - * @return {any} The component to use, undefined if not found. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getMainComponent(course: any, module: any): any; + getMainComponent(injector: Injector, course: any, module: any): any | Promise; } /** @@ -176,17 +178,17 @@ export class CoreCourseModuleDelegate extends CoreDelegate { /** * Get the component to render the module. * + * @param {Injector} injector Injector. * @param {any} course The course object. * @param {any} module The module object. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getMainComponent?(course: any, module: any): any { + getMainComponent(injector: Injector, course: any, module: any): Promise { const handler = this.enabledHandlers[module.modname]; if (handler && handler.getMainComponent) { - const component = handler.getMainComponent(course, module); - if (component) { - return component; - } + return Promise.resolve(handler.getMainComponent(injector, course, module)).catch((err) => { + this.logger.error('Error getting main component', err); + }); } } diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts index e73a4b6ab..93fe43a1e 100644 --- a/src/core/course/providers/options-delegate.ts +++ b/src/core/course/providers/options-delegate.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; @@ -45,10 +45,11 @@ export interface CoreCourseOptionsHandler extends CoreDelegateHandler { /** * Returns the data needed to render the handler. * + * @param {Injector} injector Injector. * @param {number} courseId The course ID. - * @return {CoreCourseOptionsHandlerData} Data. + * @return {CoreCourseOptionsHandlerData|Promise} Data or promise resolved with the data. */ - getDisplayData?(courseId: number): CoreCourseOptionsHandlerData; + getDisplayData?(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise; /** * Should invalidate the data to determine if the handler is enabled for a certain course. @@ -243,6 +244,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate { * Get the list of handlers that should be displayed for a course. * This function should be called only when the handlers need to be displayed, since it can call several WebServices. * + * @param {Injector} injector Injector. * @param {any} course The course object. * @param {boolean} [refresh] True if it should refresh the list. * @param {boolean} [isGuest] Whether it's guest. @@ -250,7 +252,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate { * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. * @return {Promise} Promise resolved with array of handlers. */ - getHandlersToDisplay(course: any, refresh?: boolean, isGuest?: boolean, navOptions?: any, admOptions?: any): + getHandlersToDisplay(injector: Injector, course: any, refresh?: boolean, isGuest?: boolean, navOptions?: any, admOptions?: any): Promise { course.id = parseInt(course.id, 10); @@ -269,14 +271,19 @@ export class CoreCourseOptionsDelegate extends CoreDelegate { // Call getHandlersForAccess to make sure the handlers have been loaded. return this.getHandlersForAccess(course.id, refresh, accessData, course.navOptions, course.admOptions); }).then(() => { - const handlersToDisplay: CoreCourseOptionsHandlerToDisplay[] = []; + const handlersToDisplay: CoreCourseOptionsHandlerToDisplay[] = [], + promises = []; this.coursesHandlers[course.id].enabledHandlers.forEach((handler) => { - handlersToDisplay.push({ - data: handler.getDisplayData(course), - priority: handler.priority, - prefetch: handler.prefetch - }); + promises.push(Promise.resolve(handler.getDisplayData(injector, course)).then((data) => { + handlersToDisplay.push({ + data: data, + priority: handler.priority, + prefetch: handler.prefetch + }); + }).catch((err) => { + this.logger.error('Error getting data for handler', handler.name, err); + })); }); // Sort them by priority. diff --git a/src/core/courses/components/course-list-item/course-list-item.ts b/src/core/courses/components/course-list-item/course-list-item.ts index d3805a1f0..3a6e47343 100644 --- a/src/core/courses/components/course-list-item/course-list-item.ts +++ b/src/core/courses/components/course-list-item/course-list-item.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreCoursesProvider } from '../../providers/courses'; @@ -31,7 +31,8 @@ import { CoreCoursesProvider } from '../../providers/courses'; export class CoreCoursesCourseListItemComponent implements OnInit { @Input() course: any; // The course to render. - constructor(private navCtrl: NavController, private translate: TranslateService, private coursesProvider: CoreCoursesProvider) { + constructor(@Optional() private navCtrl: NavController, private translate: TranslateService, + private coursesProvider: CoreCoursesProvider) { } /** diff --git a/src/core/courses/components/course-progress/course-progress.ts b/src/core/courses/components/course-progress/course-progress.ts index f5152e3ff..77c81a975 100644 --- a/src/core/courses/components/course-progress/course-progress.ts +++ b/src/core/courses/components/course-progress/course-progress.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { Component, Input, OnInit, OnDestroy, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; @@ -44,7 +44,7 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { protected isDestroyed = false; protected courseStatusObserver; - constructor(private navCtrl: NavController, private courseHelper: CoreCourseHelperProvider, + constructor(@Optional() private navCtrl: NavController, private courseHelper: CoreCourseHelperProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) { // Listen for status change in course. diff --git a/src/core/courses/components/overview-events/overview-events.ts b/src/core/courses/components/overview-events/overview-events.ts index 4ffc4e3fb..50b866569 100644 --- a/src/core/courses/components/overview-events/overview-events.ts +++ b/src/core/courses/components/overview-events/overview-events.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, OnChanges, EventEmitter, SimpleChange } from '@angular/core'; +import { Component, Input, Output, OnChanges, EventEmitter, SimpleChange, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; @@ -43,9 +43,10 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges { next30Days: any[] = []; future: any[] = []; - constructor(private navCtrl: NavController, private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, - private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, - private courseProvider: CoreCourseProvider, private contentLinksHelper: CoreContentLinksHelperProvider) { + constructor(@Optional() private navCtrl: NavController, private utils: CoreUtilsProvider, + private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider, + private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, + private contentLinksHelper: CoreContentLinksHelperProvider) { this.loadMore = new EventEmitter(); } diff --git a/src/core/grades/components/course/course.ts b/src/core/grades/components/course/course.ts index 181080f4d..4dc6e1ba3 100644 --- a/src/core/grades/components/course/course.ts +++ b/src/core/grades/components/course/course.ts @@ -39,8 +39,9 @@ export class CoreGradesCourseComponent { gradesTable: any; constructor(private gradesProvider: CoreGradesProvider, private domUtils: CoreDomUtilsProvider, navParams: NavParams, - private gradesHelper: CoreGradesHelperProvider, private sitesProvider: CoreSitesProvider, private navCtrl: NavController, - private appProvider: CoreAppProvider, @Optional() private svComponent: CoreSplitViewComponent) { + private gradesHelper: CoreGradesHelperProvider, private sitesProvider: CoreSitesProvider, + @Optional() private navCtrl: NavController, private appProvider: CoreAppProvider, + @Optional() private svComponent: CoreSplitViewComponent) { } /** diff --git a/src/core/grades/providers/course-option-handler.ts b/src/core/grades/providers/course-option-handler.ts index 54f9396e7..87ac8540f 100644 --- a/src/core/grades/providers/course-option-handler.ts +++ b/src/core/grades/providers/course-option-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; import { CoreCourseProvider } from '../../course/providers/course'; @@ -80,9 +80,11 @@ export class CoreGradesCourseOptionHandler implements CoreCourseOptionsHandler { /** * Returns the data needed to render the handler. * - * @return {CoreCourseOptionsHandlerData} Data needed to render the handler. + * @param {Injector} injector Injector. + * @param {number} courseId The course ID. + * @return {CoreCourseOptionsHandlerData|Promise} Data or promise resolved with the data. */ - getDisplayData(): CoreCourseOptionsHandlerData { + getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise { return { title: 'core.grades.grades', class: 'core-grades-course-handler', diff --git a/src/core/siteaddons/directives/call-ws-new-content.ts b/src/core/siteaddons/directives/call-ws-new-content.ts index cf23d59f0..f4d0666e9 100644 --- a/src/core/siteaddons/directives/call-ws-new-content.ts +++ b/src/core/siteaddons/directives/call-ws-new-content.ts @@ -62,7 +62,7 @@ export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallW constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, - protected utils: CoreUtilsProvider, protected navCtrl: NavController) { + protected utils: CoreUtilsProvider, @Optional() protected navCtrl: NavController) { super(element, translate, domUtils, siteAddonsProvider, parentContent); } diff --git a/src/core/siteaddons/directives/call-ws-on-load.ts b/src/core/siteaddons/directives/call-ws-on-load.ts index 9a2a1a8c2..bca0ab323 100644 --- a/src/core/siteaddons/directives/call-ws-on-load.ts +++ b/src/core/siteaddons/directives/call-ws-on-load.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; -import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteaddons/directives/new-content.ts index a31920aa5..6e11a17ac 100644 --- a/src/core/siteaddons/directives/new-content.ts +++ b/src/core/siteaddons/directives/new-content.ts @@ -54,7 +54,7 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { protected element: HTMLElement; - constructor(element: ElementRef, protected utils: CoreUtilsProvider, protected navCtrl: NavController, + constructor(element: ElementRef, protected utils: CoreUtilsProvider, @Optional() protected navCtrl: NavController, @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent, protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { this.element = element.nativeElement || element; diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index c8e0b6c18..1bfa8336a 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -43,6 +43,9 @@ import { CoreCoursesProvider } from '../../courses/providers/courses'; * addons. * * This code is split from CoreSiteAddonsProvider to prevent circular dependencies. + * + * @todo: Support ViewChild and similar in site addons. Possible solution: make components and directives inject the instance + * inside the host DOM element? */ @Injectable() export class CoreSiteAddonsHelperProvider { @@ -337,7 +340,7 @@ export class CoreSiteAddonsHelperProvider { displaySectionSelector: (course: any): boolean => { return typeof handlerSchema.displaysectionselector != 'undefined' ? handlerSchema.displaysectionselector : true; }, - getCourseFormatComponent: (course: any): any => { + getCourseFormatComponent: (injector: Injector, course: any): any | Promise => { if (handlerSchema.method) { return CoreSiteAddonsCourseFormatComponent; } @@ -377,7 +380,8 @@ export class CoreSiteAddonsHelperProvider { : boolean | Promise => { return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, bootstrapResult.restrict); }, - getDisplayData: (courseId: number): CoreCourseOptionsHandlerData => { + getDisplayData: (injector: Injector, courseId: number): + CoreCourseOptionsHandlerData | Promise => { return { title: prefixedTitle, class: handlerSchema.displaydata.class, @@ -488,7 +492,7 @@ export class CoreSiteAddonsHelperProvider { } }; }, - getMainComponent: (course: any, module: any): any => { + getMainComponent: (injector: Injector, course: any, module: any): any | Promise => { return CoreSiteAddonsModuleIndexComponent; } }); diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index f6051d5d4..777973182 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -144,7 +144,10 @@ export class CoreSiteAddonsProvider { */ createDataForJS(bootstrapResult: any, contentResult?: any): any { // First of all, add the data returned by the bootstrap JS (if any). - const data = this.utils.clone(bootstrapResult.jsResult || {}); + let data = this.utils.clone(bootstrapResult.jsResult || {}); + if (typeof data == 'boolean') { + data = {}; + } // Now add some data returned by the bootstrap WS call. data.BOOTSTRAP_TEMPLATES = this.utils.objectToKeyValueMap(bootstrapResult.templates, 'id', 'html'); diff --git a/src/core/user/components/user-profile-field/user-profile-field.ts b/src/core/user/components/user-profile-field/user-profile-field.ts index 65b401a32..7d410d3c3 100644 --- a/src/core/user/components/user-profile-field/user-profile-field.ts +++ b/src/core/user/components/user-profile-field/user-profile-field.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, Injector } from '@angular/core'; import { CoreUserProfileFieldDelegate } from '../../providers/user-profile-field-delegate'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -33,13 +33,16 @@ export class CoreUserProfileFieldComponent implements OnInit { componentClass: any; // The class of the component to render. data: any = {}; // Data to pass to the component. - constructor(private ufDelegate: CoreUserProfileFieldDelegate, private utilsProvider: CoreUtilsProvider) { } + constructor(private ufDelegate: CoreUserProfileFieldDelegate, private utilsProvider: CoreUtilsProvider, + private injector: Injector) { } /** * Component being initialized. */ ngOnInit(): void { - this.componentClass = this.ufDelegate.getComponent(this.field, this.signup); + this.ufDelegate.getComponent(this.injector, this.field, this.signup).then((component) => { + this.componentClass = component; + }); this.data.field = this.field; this.data.edit = this.utilsProvider.isTrueOrOne(this.edit); diff --git a/src/core/user/providers/course-option-handler.ts b/src/core/user/providers/course-option-handler.ts index c595341b8..8266e95af 100644 --- a/src/core/user/providers/course-option-handler.ts +++ b/src/core/user/providers/course-option-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; import { CoreCourseProvider } from '../../course/providers/course'; @@ -79,9 +79,11 @@ export class CoreUserParticipantsCourseOptionHandler implements CoreCourseOption /** * Returns the data needed to render the handler. * - * @return {CoreCourseOptionsHandlerData} Data needed to render the handler. + * @param {Injector} injector Injector. + * @param {number} courseId The course ID. + * @return {CoreCourseOptionsHandlerData|Promise} Data or promise resolved with the data. */ - getDisplayData(): CoreCourseOptionsHandlerData { + getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise { return { title: 'core.user.participants', class: 'core-user-participants-handler', diff --git a/src/core/user/providers/user-profile-field-delegate.ts b/src/core/user/providers/user-profile-field-delegate.ts index 41c4b3d47..b8ba951c8 100644 --- a/src/core/user/providers/user-profile-field-delegate.ts +++ b/src/core/user/providers/user-profile-field-delegate.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; @@ -22,10 +22,12 @@ export interface CoreUserProfileFieldHandler extends CoreDelegateHandler { /** * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. * - * @return {any} The component to use, undefined if not found. + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ - getComponent(): any; + getComponent(injector: Injector): any | Promise; /** * Get the data to send for the field based on the input data. @@ -75,17 +77,23 @@ export class CoreUserProfileFieldDelegate extends CoreDelegate { /** * Get the component to use to display an user field. * + * @param {Injector} injector Injector. * @param {any} field User field to get the directive for. * @param {boolean} signup True if user is in signup page. - * @return {any} The component to use, undefined if not found. + * @return {Promise} Promise resolved with component to use, undefined if not found. */ - getComponent(field: any, signup: boolean): any { + getComponent(injector: Injector, field: any, signup: boolean): Promise { const type = field.type || field.datatype; + let result; if (signup) { - return this.executeFunction(type, 'getComponent'); + result = this.executeFunction(type, 'getComponent', [injector]); } else { - return this.executeFunctionOnEnabled(type, 'getComponent'); + result = this.executeFunctionOnEnabled(type, 'getComponent', [injector]); } + + return Promise.resolve(result).catch((err) => { + this.logger.error('Error getting component for field', type, err); + }); } /** diff --git a/src/directives/auto-focus.ts b/src/directives/auto-focus.ts index ce2aa6608..c5f0db88e 100644 --- a/src/directives/auto-focus.ts +++ b/src/directives/auto-focus.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -31,7 +31,7 @@ export class CoreAutoFocusDirective implements OnInit { protected element: HTMLElement; constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, - private navCtrl: NavController) { + @Optional() private navCtrl: NavController) { this.element = element.nativeElement || element; } diff --git a/src/directives/download-file.ts b/src/directives/download-file.ts index f715d15ee..3418d87d1 100644 --- a/src/directives/download-file.ts +++ b/src/directives/download-file.ts @@ -13,7 +13,6 @@ // limitations under the License. import { Directive, Input, OnInit, ElementRef } from '@angular/core'; -import { NavController } from 'ionic-angular'; import { CoreFileHelperProvider } from '../providers/file-helper'; import { CoreDomUtilsProvider } from '../providers/utils/dom'; import { CoreUtilsProvider } from '../providers/utils/utils'; diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index b65a5107d..3261db59b 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -62,7 +62,7 @@ export class CoreFormatTextDirective implements OnChanges { private textUtils: CoreTextUtilsProvider, private translate: TranslateService, private platform: Platform, private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private loggerProvider: CoreLoggerProvider, private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider, - private contentLinksHelper: CoreContentLinksHelperProvider, private navCtrl: NavController, + private contentLinksHelper: CoreContentLinksHelperProvider, @Optional() private navCtrl: NavController, @Optional() private content: Content) { this.element = element.nativeElement; this.element.classList.add('opacity-hide'); // Hide contents until they're treated. diff --git a/src/directives/link.ts b/src/directives/link.ts index d37179cf0..75c50b186 100644 --- a/src/directives/link.ts +++ b/src/directives/link.ts @@ -39,7 +39,7 @@ export class CoreLinkDirective implements OnInit { constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider, - private contentLinksHelper: CoreContentLinksHelperProvider, private navCtrl: NavController, + private contentLinksHelper: CoreContentLinksHelperProvider, @Optional() private navCtrl: NavController, @Optional() private content: Content) { // This directive can be added dynamically. In that case, the first param is the anchor HTMLElement. this.element = element.nativeElement || element; diff --git a/src/directives/user-link.ts b/src/directives/user-link.ts index c24adb892..d1714bacb 100644 --- a/src/directives/user-link.ts +++ b/src/directives/user-link.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; /** @@ -27,7 +27,7 @@ export class CoreUserLinkDirective implements OnInit { protected element: HTMLElement; - constructor(element: ElementRef, private navCtrl: NavController) { + constructor(element: ElementRef, @Optional() private navCtrl: NavController) { // This directive can be added dynamically. In that case, the first param is the anchor HTMLElement. this.element = element.nativeElement || element; } From 97cd791659b0a338a76fc1098a0399ab3d9471cc Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 8 Mar 2018 08:19:05 +0100 Subject: [PATCH 23/32] MOBILE-2333 mainmenu: Redirect to init if not logged in --- src/core/mainmenu/pages/menu/menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index acd3a99eb..d909aa13a 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -77,7 +77,7 @@ export class CoreMainMenuPage implements OnDestroy { */ ionViewDidLoad(): void { if (!this.sitesProvider.isLoggedIn()) { - this.navCtrl.setRoot('CoreLoginSitesPage'); + this.navCtrl.setRoot('CoreLoginInitPage'); return; } From bb17dbfdab692d29b1d11be2195b88efbd444e02 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 8 Mar 2018 15:42:44 +0100 Subject: [PATCH 24/32] MOBILE-2333 siteaddons: Support user profile field plugins --- .../components/compile-html/compile-html.ts | 12 +- .../components/components.module.ts | 10 +- .../user-profile-field.html | 1 + .../user-profile-field/user-profile-field.ts | 87 ++++++++++++ src/core/siteaddons/providers/helper.ts | 132 +++++++++++++++--- 5 files changed, 213 insertions(+), 29 deletions(-) create mode 100644 src/core/siteaddons/components/user-profile-field/user-profile-field.html create mode 100644 src/core/siteaddons/components/user-profile-field/user-profile-field.ts diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts index a7773a965..3583a76df 100644 --- a/src/core/compile/components/compile-html/compile-html.ts +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -18,6 +18,7 @@ import { } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreCompileProvider } from '../../../compile/providers/compile'; +import { BehaviorSubject } from 'rxjs'; /** * This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its @@ -48,10 +49,12 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { protected componentRef: ComponentRef; protected element; + componentObservable: BehaviorSubject; // An observable to notify observers when the component is instantiated. constructor(protected compileProvider: CoreCompileProvider, protected cdr: ChangeDetectorRef, element: ElementRef, @Optional() protected navCtrl: NavController) { this.element = element.nativeElement; + this.componentObservable = new BehaviorSubject(null); } /** @@ -67,6 +70,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { if (factory) { // Create the component. this.componentRef = this.container.createComponent(factory); + this.componentObservable.next(this.componentRef.instance); } }); } @@ -99,11 +103,11 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { this['ChangeDetectorRef'] = compileInstance.cdr; this['NavController'] = compileInstance.navCtrl; this['componentContainer'] = compileInstance.element; + } - // Add the data passed to the component. - for (const name in compileInstance.jsData) { - this[name] = compileInstance.jsData[name]; - } + // Add the data passed to the component. + for (const name in compileInstance.jsData) { + this[name] = compileInstance.jsData[name]; } } diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteaddons/components/components.module.ts index 15a6f3019..ac5daadeb 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteaddons/components/components.module.ts @@ -22,13 +22,15 @@ import { CoreSiteAddonsAddonContentComponent } from './addon-content/addon-conte import { CoreSiteAddonsModuleIndexComponent } from './module-index/module-index'; import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-option'; import { CoreSiteAddonsCourseFormatComponent } from './course-format/course-format'; +import { CoreSiteAddonsUserProfileFieldComponent } from './user-profile-field/user-profile-field'; @NgModule({ declarations: [ CoreSiteAddonsAddonContentComponent, CoreSiteAddonsModuleIndexComponent, CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent + CoreSiteAddonsCourseFormatComponent, + CoreSiteAddonsUserProfileFieldComponent ], imports: [ CommonModule, @@ -43,12 +45,14 @@ import { CoreSiteAddonsCourseFormatComponent } from './course-format/course-form CoreSiteAddonsAddonContentComponent, CoreSiteAddonsModuleIndexComponent, CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent + CoreSiteAddonsCourseFormatComponent, + CoreSiteAddonsUserProfileFieldComponent ], entryComponents: [ CoreSiteAddonsModuleIndexComponent, CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent + CoreSiteAddonsCourseFormatComponent, + CoreSiteAddonsUserProfileFieldComponent ] }) export class CoreSiteAddonsComponentsModule {} diff --git a/src/core/siteaddons/components/user-profile-field/user-profile-field.html b/src/core/siteaddons/components/user-profile-field/user-profile-field.html new file mode 100644 index 000000000..fec5e4726 --- /dev/null +++ b/src/core/siteaddons/components/user-profile-field/user-profile-field.html @@ -0,0 +1 @@ + diff --git a/src/core/siteaddons/components/user-profile-field/user-profile-field.ts b/src/core/siteaddons/components/user-profile-field/user-profile-field.ts new file mode 100644 index 000000000..4e0714b36 --- /dev/null +++ b/src/core/siteaddons/components/user-profile-field/user-profile-field.ts @@ -0,0 +1,87 @@ +// (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, OnInit, Input, ViewChild, OnDestroy } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreCompileHtmlComponent } from '../../../compile/components/compile-html/compile-html'; +import { Subscription } from 'rxjs'; + +/** + * Component that displays a user profile field created using a site addon. + */ +@Component({ + selector: 'core-site-addons-user-profile-field', + templateUrl: 'user-profile-field.html', +}) +export class CoreSiteAddonsUserProfileFieldComponent implements OnInit, OnDestroy { + @Input() field: any; // The profile field to be rendered. + @Input() signup = false; // True if editing the field in signup. Defaults to false. + @Input() edit = false; // True if editing the field. Defaults to false. + @Input() form?: any; // Form where to add the form control. Required if edit=true or signup=true. + @Input() registerAuth?: string; // Register auth method. E.g. 'email'. + + @ViewChild(CoreCompileHtmlComponent) compileComponent: CoreCompileHtmlComponent; + + content = ''; // Content. + jsData; + protected componentObserver: Subscription; + + constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + + /** + * Component being initialized. + */ + ngOnInit(): void { + + // Pass the input data to the component. + this.jsData = { + field: this.field, + signup: this.signup, + edit: this.edit, + form: this.form, + registerAuth: this.registerAuth + }; + + if (this.field) { + // Retrieve the handler data. + const handler = this.siteAddonsProvider.getSiteAddonHandler(this.field.type || this.field.datatype), + handlerSchema = handler && handler.handlerSchema; + + if (handlerSchema) { + // Load first template. + if (handlerSchema.methodTemplates && handlerSchema.methodTemplates.length) { + this.content = handler.handlerSchema.methodTemplates[0].html; + } + + // Wait for the instance to be created. + if (this.compileComponent && this.compileComponent.componentObservable && + handlerSchema.methodJSResult && handlerSchema.methodJSResult.componentInit) { + this.componentObserver = this.compileComponent.componentObservable.subscribe((instance) => { + if (instance) { + // Instance created, call component init. + handlerSchema.methodJSResult.componentInit.apply(instance); + } + }); + } + } + } + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + this.componentObserver && this.componentObserver.unsubscribe(); + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 1bfa8336a..abf076fdb 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -29,10 +29,14 @@ import { } from '../../course/providers/options-delegate'; import { CoreCourseFormatDelegate, CoreCourseFormatHandler } from '../../course/providers/format-delegate'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; +import { + CoreUserProfileFieldDelegate, CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData +} from '../../user/providers/user-profile-field-delegate'; import { CoreDelegateHandler } from '../../../classes/delegate'; import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; import { CoreSiteAddonsCourseFormatComponent } from '../components/course-format/course-format'; +import { CoreSiteAddonsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; import { CoreSiteAddonsProvider } from './siteaddons'; import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; import { CoreCompileProvider } from '../../compile/providers/compile'; @@ -57,7 +61,7 @@ export class CoreSiteAddonsHelperProvider { private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, - private courseFormatDelegate: CoreCourseFormatDelegate) { + private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); } @@ -65,20 +69,31 @@ export class CoreSiteAddonsHelperProvider { * Bootstrap a handler if it has some bootstrap method. * * @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. It returns the results of the getContent call and the data returned by * the bootstrap JS (if any). */ - protected bootstrapHandler(addon: any, handlerName: string, handlerSchema: any): Promise { + protected bootstrapHandler(addon: any, handlerSchema: any): Promise { if (!handlerSchema.bootstrap) { return Promise.resolve({}); } + return this.executeMethodAndJS(addon, handlerSchema.bootstrap); + } + + /** + * Execute a get_content method and run its javascript (if any). + * + * @param {any} addon Data of the addon. + * @param {string} method The method to call. + * @return {Promise} Promise resolved when done. It returns the results of the getContent call and the data returned by + * the JS (if any). + */ + protected executeMethodAndJS(addon: any, method: string): Promise { const siteId = this.sitesProvider.getCurrentSiteId(), preSets = {getFromCache: false}; // Try to ignore cache. - return this.siteAddonsProvider.getContent(addon.component, handlerSchema.bootstrap, {}, preSets).then((result) => { + return this.siteAddonsProvider.getContent(addon.component, method, {}, preSets).then((result) => { if (!result.javascript || this.sitesProvider.getCurrentSiteId() != siteId) { // No javascript or site has changed, stop. return result; @@ -269,43 +284,52 @@ export class CoreSiteAddonsHelperProvider { this.loadHandlerLangStrings(addon, handlerName, handlerSchema); // Wait for the bootstrap JS to be executed. - return this.bootstrapHandler(addon, handlerName, handlerSchema).then((result) => { - let uniqueName; + return this.bootstrapHandler(addon, handlerSchema).then((result) => { + let promise; switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': - uniqueName = this.registerMainMenuHandler(addon, handlerName, handlerSchema, result); + promise = Promise.resolve(this.registerMainMenuHandler(addon, handlerName, handlerSchema, result)); break; case 'CoreCourseModuleDelegate': - uniqueName = this.registerModuleHandler(addon, handlerName, handlerSchema, result); + promise = Promise.resolve(this.registerModuleHandler(addon, handlerName, handlerSchema, result)); break; case 'CoreUserDelegate': - uniqueName = this.registerUserProfileHandler(addon, handlerName, handlerSchema, result); + promise = Promise.resolve(this.registerUserProfileHandler(addon, handlerName, handlerSchema, result)); break; case 'CoreCourseOptionsDelegate': - uniqueName = this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result); + promise = Promise.resolve(this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result)); break; case 'CoreCourseFormatDelegate': - uniqueName = this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result); + promise = Promise.resolve(this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result)); + break; + + case 'CoreUserProfileFieldDelegate': + promise = Promise.resolve(this.registerUserProfileFieldHandler(addon, handlerName, handlerSchema, result)); break; default: // Nothing to do. + promise = Promise.resolve(); } - if (uniqueName) { - // Store the handler data. - this.siteAddonsProvider.setSiteAddonHandler(uniqueName, { - addon: addon, - handlerName: handlerName, - handlerSchema: handlerSchema, - bootstrapResult: result - }); - } + return promise.then((uniqueName) => { + if (uniqueName) { + // Store the handler data. + this.siteAddonsProvider.setSiteAddonHandler(uniqueName, { + addon: addon, + handlerName: handlerName, + handlerSchema: handlerSchema, + bootstrapResult: result + }); + } + }); + }).catch((err) => { + this.logger.error('Error executing bootstrap method', handlerSchema.bootstrap, err); }); } @@ -527,12 +551,18 @@ export class CoreSiteAddonsHelperProvider { const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), baseHandler = this.getBaseHandler(uniqueName), prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let userHandler: CoreUserProfileHandler; + let userHandler: CoreUserProfileHandler, + type = handlerSchema.type; + + // Only support TYPE_COMMUNICATION and TYPE_NEW_PAGE. + if (type != CoreUserDelegate.TYPE_COMMUNICATION) { + type = CoreUserDelegate.TYPE_NEW_PAGE; + } // Extend the base handler, adding the properties required by the delegate. userHandler = Object.assign(baseHandler, { priority: handlerSchema.priority, - type: handlerSchema.type, + type: type, isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => { // First check if it's enabled for the user. const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, @@ -572,4 +602,62 @@ export class CoreSiteAddonsHelperProvider { return uniqueName; } + + /** + * Given a handler in an addon, register it in the user profile field delegate. + * + * @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 the bootstrap WS call. + * @return {string|Promise} A string (or a promise resolved with a string) to identify the handler. + */ + protected registerUserProfileFieldHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any) + : string | Promise { + if (!handlerSchema || !handlerSchema.method) { + // Required data not provided, stop. + return; + } + + // Execute the main method and its JS. The template returned will be used in the profile field component. + return this.executeMethodAndJS(addon, handlerSchema.method).then((result) => { + // Create the base handler. + const fieldType = addon.component.replace('profilefield_', ''), + baseHandler = this.getBaseHandler(fieldType); + let fieldHandler: CoreUserProfileFieldHandler; + + // Store in handlerSchema some data required by the component. + handlerSchema.methodTemplates = result.templates; + handlerSchema.methodJSResult = result.jsResult; + + // Extend the base handler, adding the properties required by the delegate. + fieldHandler = Object.assign(baseHandler, { + getData: (field: any, signup: boolean, registerAuth: string, formValues: any): + Promise | CoreUserProfileFieldHandlerData => { + if (result && result.jsResult && result.jsResult.getData) { + // The JS of the main method implements the getData function, use it. + return result.jsResult.getData(); + } + + // No getData function implemented, use a default behaviour. + const name = 'profile_field_' + field.shortname; + + return { + type: field.type || field.datatype, + name: name, + value: formValues[name] + }; + }, + getComponent: (injector: Injector): any | Promise => { + return CoreSiteAddonsUserProfileFieldComponent; + } + }); + + this.profileFieldDelegate.registerHandler(fieldHandler); + + return fieldType; + }).catch((err) => { + this.logger.error('Error executing main method', handlerSchema.method, err); + }); + } } From 0669ba9592ca119e00d46c43b2d5fd32a739aa31 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 9 Mar 2018 09:00:43 +0100 Subject: [PATCH 25/32] MOBILE-2333 siteaddons: Improve logs and remove addon manager --- src/app/app.module.ts | 5 +- src/core/siteaddons/providers/helper.ts | 111 +++++++++++++++++++++-- src/core/siteaddons/siteaddons.module.ts | 6 +- src/providers/addonmanager.ts | 102 --------------------- src/providers/ws.ts | 5 + 5 files changed, 112 insertions(+), 117 deletions(-) delete mode 100644 src/providers/addonmanager.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e2efc6119..a6bee2732 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -49,7 +49,6 @@ import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreUpdateManagerProvider } from '@providers/update-manager'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CoreSyncProvider } from '@providers/sync'; -import { CoreAddonManagerProvider } from '@providers/addonmanager'; import { CoreFileHelperProvider } from '@providers/file-helper'; // Core modules. @@ -110,7 +109,6 @@ export const CORE_PROVIDERS: any[] = [ CoreUpdateManagerProvider, CorePluginFileDelegate, CoreSyncProvider, - CoreAddonManagerProvider, CoreFileHelperProvider ]; @@ -170,8 +168,7 @@ export const CORE_PROVIDERS: any[] = [ }) export class AppModule { constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider, - sitesProvider: CoreSitesProvider, addonManagerProvider: CoreAddonManagerProvider) { - // Inject CoreAddonManagerProvider even if it's not used to make sure it's initialized. + sitesProvider: CoreSitesProvider) { // Register a handler for platform ready. initDelegate.registerProcess({ name: 'CorePlatformReady', diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index abf076fdb..015afa844 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -14,6 +14,7 @@ import { Injectable, Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; +import { CoreEventsProvider } from '../../../providers/events'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; @@ -54,15 +55,39 @@ import { CoreCoursesProvider } from '../../courses/providers/courses'; @Injectable() export class CoreSiteAddonsHelperProvider { protected logger; + protected hasSiteAddonsLoaded = false; 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 compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, + private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, eventsProvider: CoreEventsProvider, private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); + + // Fetch the addons on login. + eventsProvider.on(CoreEventsProvider.LOGIN, () => { + const siteId = this.sitesProvider.getCurrentSiteId(); + this.fetchSiteAddons(siteId).then((addons) => { + // 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).then(() => { + eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); + }); + + } + }); + }); + + // Unload addons on logout if any. + eventsProvider.on(CoreEventsProvider.LOGOUT, () => { + if (this.hasSiteAddonsLoaded) { + // Temporary fix. Reload the page to unload all plugins. + window.location.reload(); + } + }); } /** @@ -116,6 +141,35 @@ export class CoreSiteAddonsHelperProvider { }); } + /** + * Fetch site addons. + * + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. Returns the list of addons to load. + */ + fetchSiteAddons(siteId?: string): Promise { + const addons = []; + + return this.sitesProvider.getSite(siteId).then((site) => { + if (!this.siteAddonsProvider.isGetContentAvailable(site)) { + // Cannot load site addons, so there's no point to fetch them. + return addons; + } + + // Get the list of addons. Try not to use cache. + return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { + data.plugins.forEach((addon: any) => { + // Check if it's a site addon and it's enabled. + if (this.isSiteAddonEnabled(addon, site)) { + addons.push(addon); + } + }); + + return addons; + }); + }); + } + /** * Create a base handler for a site addon. * @@ -256,11 +310,15 @@ export class CoreSiteAddonsHelperProvider { loadSiteAddon(addon: any): Promise { const promises = []; + this.logger.debug('Load site addon:', addon); + try { if (!addon.parsedHandlers) { addon.parsedHandlers = JSON.parse(addon.handlers); } + this.hasSiteAddonsLoaded = true; + // Register all the handlers. for (const name in addon.parsedHandlers) { promises.push(this.registerHandler(addon, name, addon.parsedHandlers[name])); @@ -272,6 +330,22 @@ export class CoreSiteAddonsHelperProvider { return this.utils.allPromises(promises); } + /** + * Load site addons. + * + * @param {any[]} addons The addons to load. + * @return {Promise} Promise resolved when loaded. + */ + loadSiteAddons(addons: any[]): Promise { + const promises = []; + + addons.forEach((addon) => { + promises.push(this.loadSiteAddon(addon)); + }); + + return this.utils.allPromises(promises); + } + /** * Register a site addon handler in the right delegate. * @@ -343,10 +417,7 @@ export class CoreSiteAddonsHelperProvider { * @return {string} A string to identify the handler. */ protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - if (!handlerSchema) { - // Required data not provided, stop. - return; - } + this.logger.debug('Register site addon in course format delegate:', addon, handlerSchema, bootstrapResult); // Create the base handler. const formatName = addon.component.replace('format_', ''), @@ -386,11 +457,15 @@ export class CoreSiteAddonsHelperProvider { * @return {string} A string to identify the handler. */ protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - if (!handlerSchema || !handlerSchema.displaydata) { + if (!handlerSchema.displaydata) { // Required data not provided, stop. + this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + return; } + this.logger.debug('Register site addon in course option delegate:', addon, handlerSchema, bootstrapResult); + // Create the base handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), baseHandler = this.getBaseHandler(uniqueName), @@ -439,11 +514,15 @@ export class CoreSiteAddonsHelperProvider { * @return {string} A string to identify the handler. */ protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - if (!handlerSchema || !handlerSchema.displaydata) { + if (!handlerSchema.displaydata) { // Required data not provided, stop. + this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + return; } + this.logger.debug('Register site addon in main menu delegate:', addon, handlerSchema, bootstrapResult); + // Create the base handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), baseHandler = this.getBaseHandler(uniqueName), @@ -484,11 +563,15 @@ export class CoreSiteAddonsHelperProvider { * @return {string} A string to identify the handler. */ protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - if (!handlerSchema || !handlerSchema.displaydata) { + if (!handlerSchema.displaydata) { // Required data not provided, stop. + this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + return; } + this.logger.debug('Register site addon in module delegate:', addon, handlerSchema, bootstrapResult); + // Create the base handler. const modName = addon.component.replace('mod_', ''), baseHandler = this.getBaseHandler(modName), @@ -542,11 +625,15 @@ export class CoreSiteAddonsHelperProvider { * @return {string} A string to identify the handler. */ protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - if (!handlerSchema || !handlerSchema.displaydata) { + if (!handlerSchema.displaydata) { // Required data not provided, stop. + this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + return; } + this.logger.debug('Register site addon in user profile delegate:', addon, handlerSchema, bootstrapResult); + // Create the base handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), baseHandler = this.getBaseHandler(uniqueName), @@ -614,11 +701,15 @@ export class CoreSiteAddonsHelperProvider { */ protected registerUserProfileFieldHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any) : string | Promise { - if (!handlerSchema || !handlerSchema.method) { + if (!handlerSchema.method) { // Required data not provided, stop. + this.logger.warn('Ignore site addon because it doesn\'t provide method', addon, handlerSchema); + return; } + this.logger.debug('Register site addon in user profile field delegate:', addon, handlerSchema, bootstrapResult); + // Execute the main method and its JS. The template returned will be used in the profile field component. return this.executeMethodAndJS(addon, handlerSchema.method).then((result) => { // Create the base handler. diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteaddons/siteaddons.module.ts index 132e0d809..35493a853 100644 --- a/src/core/siteaddons/siteaddons.module.ts +++ b/src/core/siteaddons/siteaddons.module.ts @@ -31,4 +31,8 @@ export const CORE_SITEADDONS_PROVIDERS = [ ], providers: CORE_SITEADDONS_PROVIDERS }) -export class CoreSiteAddonsModule { } +export class CoreSiteAddonsModule { + constructor(helper: CoreSiteAddonsHelperProvider) { + // Inject the helper even if it isn't used so it's instantiated. + } +} diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts deleted file mode 100644 index c03d74420..000000000 --- a/src/providers/addonmanager.ts +++ /dev/null @@ -1,102 +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 { Injectable } from '@angular/core'; -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'; - -/** - * Provider with some helper functions regarding addons. - */ -@Injectable() -export class CoreAddonManagerProvider { - - protected logger; - - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - private siteAddonsProvider: CoreSiteAddonsProvider, private siteAddonsHelperProvider: CoreSiteAddonsHelperProvider, - private utils: CoreUtilsProvider) { - logger = logger.getInstance('CoreAddonManagerProvider'); - - // Fetch the addons on login. - eventsProvider.on(CoreEventsProvider.LOGIN, () => { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.fetchSiteAddons(siteId).then((addons) => { - // 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).then(() => { - eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); - }); - - } - }); - }); - - // Unload addons on logout if any. - eventsProvider.on(CoreEventsProvider.LOGOUT, () => { - // @todo: Unload site addons. - }); - } - - /** - * Fetch site addons. - * - * @param {string} [siteId] Site ID. If not defined, current site. - * @return {Promise} Promise resolved when done. Returns the list of addons to load. - */ - fetchSiteAddons(siteId?: string): Promise { - const addons = []; - - return this.sitesProvider.getSite(siteId).then((site) => { - if (!this.siteAddonsProvider.isGetContentAvailable(site)) { - // Cannot load site addons, so there's no point to fetch them. - return addons; - } - - // Get the list of addons. Try not to use cache. - return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { - data.plugins.forEach((addon: any) => { - // Check if it's a site addon and it's enabled. - if (this.siteAddonsHelperProvider.isSiteAddonEnabled(addon, site)) { - addons.push(addon); - } - }); - - return addons; - }); - }); - } - - /** - * Load site addons. - * - * @param {any[]} addons The addons to load. - * @return {Promise} Promise resolved when loaded. - */ - loadSiteAddons(addons: any[]): Promise { - const promises = []; - - addons.forEach((addon) => { - promises.push(this.siteAddonsHelperProvider.loadSiteAddon(addon)); - }); - - return this.utils.allPromises(promises); - } -} diff --git a/src/providers/ws.ts b/src/providers/ws.ts index 462678683..01ccd7c71 100644 --- a/src/providers/ws.ts +++ b/src/providers/ws.ts @@ -523,6 +523,11 @@ export class CoreWSProvider { } if (typeof data.exception !== 'undefined') { + // Special debugging for site plugins, otherwise it's hard to debug errors if the data is cached. + if (method == 'tool_mobile_get_content') { + this.logger.error('Error calling WS', method, data); + } + return Promise.reject(data); } From 557011d560f0e8de7c48aae377696b8b65d721cc Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 9 Mar 2018 11:32:41 +0100 Subject: [PATCH 26/32] MOBILE-2333 siteaddons: Move code to handler classes --- src/core/siteaddons/classes/base-handler.ts | 32 ++ .../classes/course-format-handler.ts | 74 ++++ .../classes/course-option-handler.ts | 80 +++++ .../siteaddons/classes/main-menu-handler.ts | 50 +++ src/core/siteaddons/classes/module-handler.ts | 74 ++++ src/core/siteaddons/classes/user-handler.ts | 100 ++++++ .../classes/user-profile-field-handler.ts | 59 ++++ src/core/siteaddons/providers/helper.ts | 320 +++--------------- src/core/siteaddons/providers/siteaddons.ts | 51 ++- 9 files changed, 567 insertions(+), 273 deletions(-) create mode 100644 src/core/siteaddons/classes/base-handler.ts create mode 100644 src/core/siteaddons/classes/course-format-handler.ts create mode 100644 src/core/siteaddons/classes/course-option-handler.ts create mode 100644 src/core/siteaddons/classes/main-menu-handler.ts create mode 100644 src/core/siteaddons/classes/module-handler.ts create mode 100644 src/core/siteaddons/classes/user-handler.ts create mode 100644 src/core/siteaddons/classes/user-profile-field-handler.ts diff --git a/src/core/siteaddons/classes/base-handler.ts b/src/core/siteaddons/classes/base-handler.ts new file mode 100644 index 000000000..bfdd31ef9 --- /dev/null +++ b/src/core/siteaddons/classes/base-handler.ts @@ -0,0 +1,32 @@ +// (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 { CoreDelegateHandler } from '../../../classes/delegate'; + +/** + * Super class for handlers for site addons. + */ +export class CoreSiteAddonsBaseHandler implements CoreDelegateHandler { + + constructor(public name: string) { } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/core/siteaddons/classes/course-format-handler.ts b/src/core/siteaddons/classes/course-format-handler.ts new file mode 100644 index 000000000..fddf165a2 --- /dev/null +++ b/src/core/siteaddons/classes/course-format-handler.ts @@ -0,0 +1,74 @@ +// (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 { Injector } from '@angular/core'; +import { CoreCourseFormatHandler } from '../../course/providers/format-delegate'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSiteAddonsCourseFormatComponent } from '../components/course-format/course-format'; + +/** + * Handler to support a course format using a site addon. + */ +export class CoreSiteAddonsCourseFormatHandler extends CoreSiteAddonsBaseHandler implements CoreCourseFormatHandler { + + constructor(name: string, protected handlerSchema: any) { + super(name); + } + + /** + * Whether it allows seeing all sections at the same time. Defaults to true. + * + * @param {any} course The course to check. + * @type {boolean} Whether it can view all sections. + */ + canViewAllSections(course: any): boolean { + return typeof this.handlerSchema.canviewallsections != 'undefined' ? this.handlerSchema.canviewallsections : true; + } + + /** + * Whether the option to enable section/module download should be displayed. Defaults to true. + * + * @param {any} course The course to check. + * @type {boolean} Whether the option to enable section/module download should be displayed. + */ + displayEnableDownload(course: any): boolean { + return typeof this.handlerSchema.displayenabledownload != 'undefined' ? this.handlerSchema.displayenabledownload : true; + } + + /** + * Whether the default section selector should be displayed. Defaults to true. + * + * @param {any} course The course to check. + * @type {boolean} Whether the default section selector should be displayed. + */ + displaySectionSelector(course: any): boolean { + return typeof this.handlerSchema.displaysectionselector != 'undefined' ? this.handlerSchema.displaysectionselector : true; + } + + /** + * Return the Component to use to display the course format instead of using the default one. + * Use it if you want to display a format completely different from the default one. + * If you want to customize the default format there are several methods to customize parts of it. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} course The course to render. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getCourseFormatComponent(injector: Injector, course: any): any | Promise { + if (this.handlerSchema.method) { + return CoreSiteAddonsCourseFormatComponent; + } + } +} diff --git a/src/core/siteaddons/classes/course-option-handler.ts b/src/core/siteaddons/classes/course-option-handler.ts new file mode 100644 index 000000000..b0041f401 --- /dev/null +++ b/src/core/siteaddons/classes/course-option-handler.ts @@ -0,0 +1,80 @@ +// (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 { Injector } from '@angular/core'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; + +/** + * Handler to display a site addon in course options. + */ +export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler implements CoreCourseOptionsHandler { + priority: number; + + constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, + protected bootstrapResult: any, protected siteAddonsProvider: CoreSiteAddonsProvider) { + super(name); + + this.priority = handlerSchema.priority; + } + + /** + * Whether or not the handler is enabled for a certain course. + * + * @param {number} courseId The course ID. + * @param {any} accessData Access type and data. Default, guest, ... + * @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { + return this.siteAddonsProvider.isHandlerEnabledForCourse( + courseId, this.handlerSchema.restricttoenrolledcourses, this.bootstrapResult.restrict); + } + + /** + * Returns the data needed to render the handler. + * + * @param {Injector} injector Injector. + * @param {number} courseId The course ID. + * @return {CoreCourseOptionsHandlerData|Promise} Data or promise resolved with the data. + */ + getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise { + return { + title: this.title, + class: this.handlerSchema.displaydata.class, + component: CoreSiteAddonsCourseOptionComponent, + componentData: { + handlerUniqueName: this.name + } + }; + } + + /** + * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. + * + * @param {any} course The course. + * @return {Promise} Promise resolved when done. + */ + prefetch(course: any): Promise { + const args = { + courseid: course.id, + }, + component = this.addon.component; + + return this.siteAddonsProvider.prefetchFunctions(component, args, this.handlerSchema, course.id, undefined, true); + } +} diff --git a/src/core/siteaddons/classes/main-menu-handler.ts b/src/core/siteaddons/classes/main-menu-handler.ts new file mode 100644 index 000000000..a66a01788 --- /dev/null +++ b/src/core/siteaddons/classes/main-menu-handler.ts @@ -0,0 +1,50 @@ +// (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 { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; + +/** + * Handler to display a site addon in the main menu. + */ +export class CoreSiteAddonsMainMenuHandler extends CoreSiteAddonsBaseHandler implements CoreMainMenuHandler { + priority: number; + + constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, + protected bootstrapResult: any) { + super(name); + + this.priority = handlerSchema.priority; + } + + /** + * Returns the data needed to render the handler. + * + * @return {CoreMainMenuHandlerData} Data. + */ + getDisplayData(): CoreMainMenuHandlerData { + return { + title: this.title, + icon: this.handlerSchema.displaydata.icon, + class: this.handlerSchema.displaydata.class, + page: 'CoreSiteAddonsAddonPage', + pageParams: { + title: this.title, + component: this.addon.component, + method: this.handlerSchema.method, + bootstrapResult: this.bootstrapResult + } + }; + } +} diff --git a/src/core/siteaddons/classes/module-handler.ts b/src/core/siteaddons/classes/module-handler.ts new file mode 100644 index 000000000..29bfb911b --- /dev/null +++ b/src/core/siteaddons/classes/module-handler.ts @@ -0,0 +1,74 @@ +// (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 { Injector } from '@angular/core'; +import { NavController, NavOptions } from 'ionic-angular'; +import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../../course/providers/module-delegate'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; + +/** + * Handler to support a module using a site addon. + */ +export class CoreSiteAddonsModuleHandler extends CoreSiteAddonsBaseHandler implements CoreCourseModuleHandler { + priority: number; + + constructor(name: string, protected handlerSchema: any) { + super(name); + } + + /** + * Get the data required to display the module in the course contents view. + * + * @param {any} module The module object. + * @param {number} courseId The course ID. + * @param {number} sectionId The section ID. + * @return {CoreCourseModuleHandlerData} Data to render the module. + */ + getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { + const hasOffline = !!(this.handlerSchema.offlinefunctions && Object.keys(this.handlerSchema.offlinefunctions).length), + showDowloadButton = this.handlerSchema.downloadbutton; + + return { + title: module.name, + icon: this.handlerSchema.displaydata.icon, + class: this.handlerSchema.displaydata.class, + showDownloadButton: typeof showDowloadButton != 'undefined' ? showDowloadButton : hasOffline, + action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsModuleIndexPage', { + title: module.name, + module: module, + courseId: courseId + }, options); + } + }; + } + + /** + * Get the component to render the module. This is needed to support singleactivity course format. + * The component returned must implement CoreCourseModuleMainComponent. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} course The course object. + * @param {any} module The module object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getMainComponent(injector: Injector, course: any, module: any): any | Promise { + return CoreSiteAddonsModuleIndexComponent; + } +} diff --git a/src/core/siteaddons/classes/user-handler.ts b/src/core/siteaddons/classes/user-handler.ts new file mode 100644 index 000000000..482af10db --- /dev/null +++ b/src/core/siteaddons/classes/user-handler.ts @@ -0,0 +1,100 @@ +// (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 { NavController } from 'ionic-angular'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; +import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; + +/** + * Handler to display a site addon in the user profile. + */ +export class CoreSiteAddonsUserProfileHandler extends CoreSiteAddonsBaseHandler implements CoreUserProfileHandler { + /** + * The highest priority is displayed first. + * @type {number} + */ + priority: number; + + /** + * A type should be specified among these: + * - TYPE_COMMUNICATION: will be displayed under the user avatar. Should have icon. Spinner not used. + * - TYPE_NEW_PAGE: will be displayed as a list of items. Should have icon. Spinner not used. + * Default value if none is specified. + * - TYPE_ACTION: will be displayed as a button and should not redirect to any state. Spinner use is recommended. + * @type {string} + */ + type: string; + + constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, + protected bootstrapResult: any, protected siteAddonsProvider: CoreSiteAddonsProvider) { + super(name); + + this.priority = handlerSchema.priority; + + // Only support TYPE_COMMUNICATION and TYPE_NEW_PAGE. + this.type = handlerSchema.type != CoreUserDelegate.TYPE_COMMUNICATION ? + CoreUserDelegate.TYPE_NEW_PAGE : CoreUserDelegate.TYPE_COMMUNICATION; + } + + /** + * Whether or not the handler is enabled for a user. + * @param {any} user User object. + * @param {number} courseId Course ID where to show. + * @param {any} [navOptions] Navigation options for the course. + * @param {any} [admOptions] Admin options for the course. + * @return {boolean|Promise} Whether or not the handler is enabled for a user. + */ + isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { + // First check if it's enabled for the user. + const enabledForUser = this.siteAddonsProvider.isHandlerEnabledForUser(user.id, this.handlerSchema.restricttocurrentuser, + this.bootstrapResult.restrict); + if (!enabledForUser) { + return false; + } + + // Enabled for user, check if it's enabled for the course. + return this.siteAddonsProvider.isHandlerEnabledForCourse( + courseId, this.handlerSchema.restricttoenrolledcourses, this.bootstrapResult.restrict); + } + + /** + * Returns the data needed to render the handler. + * @param {any} user User object. + * @param {number} courseId Course ID where to show. + * @return {CoreUserProfileHandlerData} Data to be shown. + */ + getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData { + return { + title: this.title, + icon: this.handlerSchema.displaydata.icon, + class: this.handlerSchema.displaydata.class, + action: (event: Event, navCtrl: NavController, user: any, courseId?: number): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreSiteAddonsAddonPage', { + title: this.title, + component: this.addon.component, + method: this.handlerSchema.method, + args: { + courseid: courseId, + userid: user.id + }, + bootstrapResult: this.bootstrapResult + }); + } + }; + } +} diff --git a/src/core/siteaddons/classes/user-profile-field-handler.ts b/src/core/siteaddons/classes/user-profile-field-handler.ts new file mode 100644 index 000000000..2ab04e807 --- /dev/null +++ b/src/core/siteaddons/classes/user-profile-field-handler.ts @@ -0,0 +1,59 @@ +// (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 { Injector } from '@angular/core'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../user/providers/user-profile-field-delegate'; +import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSiteAddonsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; + +/** + * Handler to display a site addon in the user profile. + */ +export class CoreSiteAddonsUserProfileFieldHandler extends CoreSiteAddonsBaseHandler implements CoreUserProfileFieldHandler { + + constructor(name: string) { + super(name); + } + + /** + * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector): any | Promise { + return CoreSiteAddonsUserProfileFieldComponent; + } + + /** + * Get the data to send for the field based on the input data. + * @param {any} field User field to get the data for. + * @param {boolean} signup True if user is in signup page. + * @param {string} [registerAuth] Register auth method. E.g. 'email'. + * @param {any} formValues Form Values. + * @return {Promise|CoreUserProfileFieldHandlerData} Data to send for the field. + */ + getData(field: any, signup: boolean, registerAuth: string, formValues: any): + Promise | CoreUserProfileFieldHandlerData { + // No getData function implemented, use a default behaviour. + const name = 'profile_field_' + field.shortname; + + return { + type: field.type || field.datatype, + name: name, + value: formValues[name] + }; + } +} diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteaddons/providers/helper.ts index 015afa844..099864fe0 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteaddons/providers/helper.ts @@ -13,35 +13,32 @@ // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { NavController, NavOptions } from 'ionic-angular'; import { CoreEventsProvider } from '../../../providers/events'; import { CoreLangProvider } from '../../../providers/lang'; import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreMainMenuDelegate, CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; -import { - CoreCourseModuleDelegate, CoreCourseModuleHandler, CoreCourseModuleHandlerData -} from '../../course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate'; -import { - CoreCourseOptionsDelegate, CoreCourseOptionsHandler, CoreCourseOptionsHandlerData -} from '../../course/providers/options-delegate'; -import { CoreCourseFormatDelegate, CoreCourseFormatHandler } from '../../course/providers/format-delegate'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; -import { - CoreUserProfileFieldDelegate, CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData -} from '../../user/providers/user-profile-field-delegate'; -import { CoreDelegateHandler } from '../../../classes/delegate'; -import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; -import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; -import { CoreSiteAddonsCourseFormatComponent } from '../components/course-format/course-format'; -import { CoreSiteAddonsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; import { CoreSiteAddonsProvider } from './siteaddons'; -import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; import { CoreCompileProvider } from '../../compile/providers/compile'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; + +// Delegates +import { CoreMainMenuDelegate } from '../../mainmenu/providers/delegate'; +import { CoreCourseModuleDelegate } from '../../course/providers/module-delegate'; +import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate'; +import { CoreCourseOptionsDelegate } from '../../course/providers/options-delegate'; +import { CoreCourseFormatDelegate } from '../../course/providers/format-delegate'; +import { CoreUserDelegate } from '../../user/providers/user-delegate'; +import { CoreUserProfileFieldDelegate } from '../../user/providers/user-profile-field-delegate'; + +// Handler classes. +import { CoreSiteAddonsCourseFormatHandler } from '../classes/course-format-handler'; +import { CoreSiteAddonsCourseOptionHandler } from '../classes/course-option-handler'; +import { CoreSiteAddonsModuleHandler } from '../classes/module-handler'; +import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; +import { CoreSiteAddonsMainMenuHandler } from '../classes/main-menu-handler'; +import { CoreSiteAddonsUserProfileHandler } from '../classes/user-handler'; +import { CoreSiteAddonsUserProfileFieldHandler } from '../classes/user-profile-field-handler'; /** * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site @@ -61,8 +58,8 @@ export class CoreSiteAddonsHelperProvider { private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private siteAddonsProvider: CoreSiteAddonsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, - private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, eventsProvider: CoreEventsProvider, - private coursesProvider: CoreCoursesProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, + private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, + private courseOptionsDelegate: CoreCourseOptionsDelegate, eventsProvider: CoreEventsProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate) { this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); @@ -170,21 +167,6 @@ export class CoreSiteAddonsHelperProvider { }); } - /** - * Create a base handler for a site addon. - * - * @param {string} name Name of the handler. - * @return {CoreDelegateHandler} The base handler. - */ - protected getBaseHandler(name: string): CoreDelegateHandler { - return { - name: name, - isEnabled: (): boolean => { - return true; - } - }; - } - /** * Given a handler's unique name, return the prefix to add to its string keys. * @@ -210,54 +192,6 @@ export class CoreSiteAddonsHelperProvider { return this.getHandlerPrefixForStrings(handlerName) + key; } - /** - * Check if a handler is enabled for a certain course. - * - * @param {number} courseId Course ID to check. - * @param {boolean} [restrictEnrolled] If true or undefined, handler is only enabled for courses the user is enrolled in. - * @param {any} [restrict] Users and courses the handler is restricted to. - * @return {boolean | Promise} Whether the handler is enabled. - */ - protected isHandlerEnabledForCourse(courseId: number, restrictEnrolled?: boolean, restrict?: any): boolean | Promise { - if (restrict && restrict.courses && restrict.courses.indexOf(courseId) == -1) { - // Course is not in the list of restricted courses. - return false; - } - - if (restrictEnrolled || typeof restrictEnrolled == 'undefined') { - // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course. - return this.coursesProvider.getUserCourse(courseId, true).then(() => { - return true; - }).catch(() => { - return false; - }); - } - - return true; - } - - /** - * Check if a handler is enabled for a certain user. - * - * @param {number} userId User ID to check. - * @param {boolean} [restrictCurrent] Whether handler is only enabled for current user. - * @param {any} [restrict] Users and courses the handler is restricted to. - * @return {boolean} Whether the handler is enabled. - */ - protected isHandlerEnabledForUser(userId: number, restrictCurrent?: boolean, restrict?: any): boolean { - if (restrictCurrent && userId != this.sitesProvider.getCurrentSite().getUserId()) { - // Only enabled for current user. - return false; - } - - if (restrict && restrict.users && restrict.users.indexOf(userId) == -1) { - // User is not in the list of restricted users. - return false; - } - - return true; - } - /** * Check if a certain addon is a site addon and it's enabled in a certain site. * @@ -419,30 +353,9 @@ export class CoreSiteAddonsHelperProvider { protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { this.logger.debug('Register site addon in course format delegate:', addon, handlerSchema, bootstrapResult); - // Create the base handler. - const formatName = addon.component.replace('format_', ''), - baseHandler = this.getBaseHandler(formatName); - let handler: CoreCourseFormatHandler; - - // Extend the base handler, adding the properties required by the delegate. - handler = Object.assign(baseHandler, { - canViewAllSections: (course: any): boolean => { - return typeof handlerSchema.canviewallsections != 'undefined' ? handlerSchema.canviewallsections : true; - }, - displayEnableDownload: (course: any): boolean => { - return typeof handlerSchema.displayenabledownload != 'undefined' ? handlerSchema.displayenabledownload : true; - }, - displaySectionSelector: (course: any): boolean => { - return typeof handlerSchema.displaysectionselector != 'undefined' ? handlerSchema.displaysectionselector : true; - }, - getCourseFormatComponent: (injector: Injector, course: any): any | Promise => { - if (handlerSchema.method) { - return CoreSiteAddonsCourseFormatComponent; - } - } - }); - - this.courseFormatDelegate.registerHandler(handler); + // Create and register the handler. + const formatName = addon.component.replace('format_', ''); + this.courseFormatDelegate.registerHandler(new CoreSiteAddonsCourseFormatHandler(formatName, handlerSchema)); return formatName; } @@ -466,40 +379,12 @@ export class CoreSiteAddonsHelperProvider { this.logger.debug('Register site addon in course option delegate:', addon, handlerSchema, bootstrapResult); - // Create the base handler. + // Create and register the handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), - baseHandler = this.getBaseHandler(uniqueName), - prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let handler: CoreCourseOptionsHandler; + prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); - // Extend the base handler, adding the properties required by the delegate. - handler = Object.assign(baseHandler, { - priority: handlerSchema.priority, - isEnabledForCourse: (courseId: number, accessData: any, navOptions?: any, admOptions?: any) - : boolean | Promise => { - return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, bootstrapResult.restrict); - }, - getDisplayData: (injector: Injector, courseId: number): - CoreCourseOptionsHandlerData | Promise => { - return { - title: prefixedTitle, - class: handlerSchema.displaydata.class, - component: CoreSiteAddonsCourseOptionComponent, - componentData: { - handlerUniqueName: uniqueName - } - }; - }, - prefetch: (course: any): Promise => { - const args = { - courseid: course.id, - }; - - return this.siteAddonsProvider.prefetchFunctions(addon.component, args, handlerSchema, course.id, undefined, true); - } - }); - - this.courseOptionsDelegate.registerHandler(handler); + this.courseOptionsDelegate.registerHandler(new CoreSiteAddonsCourseOptionHandler(uniqueName, prefixedTitle, addon, + handlerSchema, bootstrapResult, this.siteAddonsProvider)); return uniqueName; } @@ -523,32 +408,12 @@ export class CoreSiteAddonsHelperProvider { this.logger.debug('Register site addon in main menu delegate:', addon, handlerSchema, bootstrapResult); - // Create the base handler. + // Create and register the handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), - baseHandler = this.getBaseHandler(uniqueName), - prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let mainMenuHandler: CoreMainMenuHandler; + prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); - // Extend the base handler, adding the properties required by the delegate. - mainMenuHandler = Object.assign(baseHandler, { - priority: handlerSchema.priority, - getDisplayData: (): CoreMainMenuHandlerData => { - return { - title: prefixedTitle, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - page: 'CoreSiteAddonsAddonPage', - pageParams: { - title: prefixedTitle, - component: addon.component, - method: handlerSchema.method, - bootstrapResult: bootstrapResult - } - }; - } - }); - - this.mainMenuDelegate.registerHandler(mainMenuHandler); + this.mainMenuDelegate.registerHandler( + new CoreSiteAddonsMainMenuHandler(uniqueName, prefixedTitle, addon, handlerSchema, bootstrapResult)); return uniqueName; } @@ -572,46 +437,17 @@ export class CoreSiteAddonsHelperProvider { this.logger.debug('Register site addon in module delegate:', addon, handlerSchema, bootstrapResult); - // Create the base handler. - const modName = addon.component.replace('mod_', ''), - baseHandler = this.getBaseHandler(modName), - hasOfflineFunctions = !!(handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length), - showDowloadButton = handlerSchema.downloadbutton; - let moduleHandler: CoreCourseModuleHandler; + // Create and register the handler. + const modName = addon.component.replace('mod_', ''); - // Extend the base handler, adding the properties required by the delegate. - moduleHandler = Object.assign(baseHandler, { - getData: (module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData => { - return { - title: module.name, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - showDownloadButton: typeof showDowloadButton != 'undefined' ? showDowloadButton : hasOfflineFunctions, - action: (event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void => { - event.preventDefault(); - event.stopPropagation(); + this.moduleDelegate.registerHandler(new CoreSiteAddonsModuleHandler(modName, handlerSchema)); - navCtrl.push('CoreSiteAddonsModuleIndexPage', { - title: module.name, - module: module, - courseId: courseId - }, options); - } - }; - }, - getMainComponent: (injector: Injector, course: any, module: any): any | Promise => { - return CoreSiteAddonsModuleIndexComponent; - } - }); - - if (hasOfflineFunctions) { + if (handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length) { // Register the prefetch handler. this.prefetchDelegate.registerHandler(new CoreSiteAddonsModulePrefetchHandler( this.injector, this.siteAddonsProvider, addon.component, modName, handlerSchema)); } - this.moduleDelegate.registerHandler(moduleHandler); - return modName; } @@ -634,58 +470,12 @@ export class CoreSiteAddonsHelperProvider { this.logger.debug('Register site addon in user profile delegate:', addon, handlerSchema, bootstrapResult); - // Create the base handler. + // Create and register the handler. const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), - baseHandler = this.getBaseHandler(uniqueName), - prefixedTitle = this.getHandlerPrefixedString(baseHandler.name, handlerSchema.displaydata.title); - let userHandler: CoreUserProfileHandler, - type = handlerSchema.type; + prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); - // Only support TYPE_COMMUNICATION and TYPE_NEW_PAGE. - if (type != CoreUserDelegate.TYPE_COMMUNICATION) { - type = CoreUserDelegate.TYPE_NEW_PAGE; - } - - // Extend the base handler, adding the properties required by the delegate. - userHandler = Object.assign(baseHandler, { - priority: handlerSchema.priority, - type: type, - isEnabledForUser: (user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise => { - // First check if it's enabled for the user. - const enabledForUser = this.isHandlerEnabledForUser(user.id, handlerSchema.restricttocurrentuser, - bootstrapResult.restrict); - if (!enabledForUser) { - return false; - } - - // Enabled for user, check if it's enabled for the course. - return this.isHandlerEnabledForCourse(courseId, handlerSchema.restricttoenrolledcourses, bootstrapResult.restrict); - }, - getDisplayData: (user: any, courseId: number): CoreUserProfileHandlerData => { - return { - title: prefixedTitle, - icon: handlerSchema.displaydata.icon, - class: handlerSchema.displaydata.class, - action: (event: Event, navCtrl: NavController, user: any, courseId?: number): void => { - event.preventDefault(); - event.stopPropagation(); - - navCtrl.push('CoreSiteAddonsAddonPage', { - title: prefixedTitle, - component: addon.component, - method: handlerSchema.method, - args: { - courseid: courseId, - userid: user.id - }, - bootstrapResult: bootstrapResult - }); - } - }; - } - }); - - this.userDelegate.registerHandler(userHandler); + this.userDelegate.registerHandler(new CoreSiteAddonsUserProfileHandler(uniqueName, prefixedTitle, addon, handlerSchema, + bootstrapResult, this.siteAddonsProvider)); return uniqueName; } @@ -712,37 +502,23 @@ export class CoreSiteAddonsHelperProvider { // Execute the main method and its JS. The template returned will be used in the profile field component. return this.executeMethodAndJS(addon, handlerSchema.method).then((result) => { - // Create the base handler. + // Create and register the handler. const fieldType = addon.component.replace('profilefield_', ''), - baseHandler = this.getBaseHandler(fieldType); - let fieldHandler: CoreUserProfileFieldHandler; + fieldHandler = new CoreSiteAddonsUserProfileFieldHandler(fieldType); // Store in handlerSchema some data required by the component. handlerSchema.methodTemplates = result.templates; handlerSchema.methodJSResult = result.jsResult; - // Extend the base handler, adding the properties required by the delegate. - fieldHandler = Object.assign(baseHandler, { - getData: (field: any, signup: boolean, registerAuth: string, formValues: any): - Promise | CoreUserProfileFieldHandlerData => { - if (result && result.jsResult && result.jsResult.getData) { - // The JS of the main method implements the getData function, use it. - return result.jsResult.getData(); + if (result && result.jsResult) { + // Override default handler functions with the result of the method JS. + for (const property in fieldHandler) { + if (property != 'constructor' && typeof fieldHandler[property] == 'function' && + typeof result.jsResult[property] == 'function') { + fieldHandler[property] = result.jsResult[property].bind(fieldHandler); } - - // No getData function implemented, use a default behaviour. - const name = 'profile_field_' + field.shortname; - - return { - type: field.type || field.datatype, - name: name, - value: formValues[name] - }; - }, - getComponent: (injector: Injector): any | Promise => { - return CoreSiteAddonsUserProfileFieldComponent; } - }); + } this.profileFieldDelegate.registerHandler(fieldHandler); diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts index 777973182..6103c3b09 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteaddons/providers/siteaddons.ts @@ -22,6 +22,7 @@ import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; import { CoreConfigConstants } from '../../../configconstants'; +import { CoreCoursesProvider } from '../../courses/providers/courses'; /** * Handler of a site addon. @@ -64,7 +65,7 @@ export class CoreSiteAddonsProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform, - private filepoolProvider: CoreFilepoolProvider) { + private filepoolProvider: CoreFilepoolProvider, private coursesProvider: CoreCoursesProvider) { this.logger = logger.getInstance('CoreUserProvider'); } @@ -338,6 +339,54 @@ export class CoreSiteAddonsProvider { return site.wsAvailable('tool_mobile_get_content'); } + /** + * Check if a handler is enabled for a certain course. + * + * @param {number} courseId Course ID to check. + * @param {boolean} [restrictEnrolled] If true or undefined, handler is only enabled for courses the user is enrolled in. + * @param {any} [restrict] Users and courses the handler is restricted to. + * @return {boolean | Promise} Whether the handler is enabled. + */ + isHandlerEnabledForCourse(courseId: number, restrictEnrolled?: boolean, restrict?: any): boolean | Promise { + if (restrict && restrict.courses && restrict.courses.indexOf(courseId) == -1) { + // Course is not in the list of restricted courses. + return false; + } + + if (restrictEnrolled || typeof restrictEnrolled == 'undefined') { + // Only enabled for courses the user is enrolled to. Check if the user is enrolled in the course. + return this.coursesProvider.getUserCourse(courseId, true).then(() => { + return true; + }).catch(() => { + return false; + }); + } + + return true; + } + + /** + * Check if a handler is enabled for a certain user. + * + * @param {number} userId User ID to check. + * @param {boolean} [restrictCurrent] Whether handler is only enabled for current user. + * @param {any} [restrict] Users and courses the handler is restricted to. + * @return {boolean} Whether the handler is enabled. + */ + isHandlerEnabledForUser(userId: number, restrictCurrent?: boolean, restrict?: any): boolean { + if (restrictCurrent && userId != this.sitesProvider.getCurrentSite().getUserId()) { + // Only enabled for current user. + return false; + } + + if (restrict && restrict.users && restrict.users.indexOf(userId) == -1) { + // User is not in the list of restricted users. + return false; + } + + return true; + } + /** * Load other data into args as determined by useOtherData list. * If useOtherData is undefined, it won't add any data. From 046b30005134bc97a600922722fd2111e8618294 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 9 Mar 2018 14:36:30 +0100 Subject: [PATCH 27/32] MOBILE-2333 siteplugins: Rename site addons to site plugins --- src/app/app.module.ts | 4 +- src/classes/delegate.ts | 2 +- .../dynamic-component/dynamic-component.ts | 4 +- src/core/compile/providers/compile.ts | 22 +- .../course-format/course-format.html | 1 - .../pages/module-index/module-index.html | 15 - .../classes/base-handler.ts | 4 +- .../classes/call-ws-click-directive.ts | 15 +- .../classes/call-ws-directive.ts | 17 +- .../classes/course-format-handler.ts | 10 +- .../classes/course-option-handler.ts | 24 +- .../classes/main-menu-handler.ts | 12 +- .../classes/module-handler.ts | 12 +- .../classes/module-prefetch-handler.ts | 24 +- .../classes/user-handler.ts | 20 +- .../classes/user-profile-field-handler.ts | 10 +- .../components/components.module.ts | 40 +-- .../course-format/course-format.html | 1 + .../components/course-format/course-format.ts | 18 +- .../course-option/course-option.html | 2 +- .../components/course-option/course-option.ts | 18 +- .../components/module-index/module-index.html | 2 +- .../components/module-index/module-index.ts | 26 +- .../plugin-content/plugin-content.html} | 0 .../plugin-content/plugin-content.ts} | 18 +- .../user-profile-field.html | 0 .../user-profile-field/user-profile-field.ts | 12 +- .../directives/call-ws-new-content.ts | 34 +-- .../directives/call-ws-on-load.ts | 20 +- .../directives/call-ws.ts | 26 +- .../directives/directives.module.ts | 26 +- .../directives/new-content.ts | 30 +- .../pages/module-index/module-index.html | 15 + .../pages/module-index/module-index.module.ts | 12 +- .../pages/module-index/module-index.ts | 12 +- .../pages/plugin-page/plugin-page.html} | 4 +- .../pages/plugin-page/plugin-page.module.ts} | 12 +- .../pages/plugin-page/plugin-page.ts} | 14 +- .../providers/helper.ts | 284 +++++++++--------- .../providers/siteplugins.ts} | 48 +-- .../siteplugins.module.ts} | 20 +- .../providers/participants-link-handler.ts | 2 +- src/core/user/providers/user.ts | 2 +- src/providers/events.ts | 2 +- src/providers/init.ts | 2 +- src/providers/lang.ts | 38 +-- 46 files changed, 469 insertions(+), 467 deletions(-) delete mode 100644 src/core/siteaddons/components/course-format/course-format.html delete mode 100644 src/core/siteaddons/pages/module-index/module-index.html rename src/core/{siteaddons => siteplugins}/classes/base-handler.ts (89%) rename src/core/{siteaddons => siteplugins}/classes/call-ws-click-directive.ts (77%) rename src/core/{siteaddons => siteplugins}/classes/call-ws-directive.ts (80%) rename src/core/{siteaddons => siteplugins}/classes/course-format-handler.ts (87%) rename src/core/{siteaddons => siteplugins}/classes/course-option-handler.ts (74%) rename src/core/{siteaddons => siteplugins}/classes/main-menu-handler.ts (79%) rename src/core/{siteaddons => siteplugins}/classes/module-handler.ts (86%) rename src/core/{siteaddons => siteplugins}/classes/module-prefetch-handler.ts (86%) rename src/core/{siteaddons => siteplugins}/classes/user-handler.ts (82%) rename src/core/{siteaddons => siteplugins}/classes/user-profile-field-handler.ts (84%) rename src/core/{siteaddons => siteplugins}/components/components.module.ts (52%) create mode 100644 src/core/siteplugins/components/course-format/course-format.html rename src/core/{siteaddons => siteplugins}/components/course-format/course-format.ts (72%) rename src/core/{siteaddons => siteplugins}/components/course-option/course-option.html (53%) rename src/core/{siteaddons => siteplugins}/components/course-option/course-option.ts (68%) rename src/core/{siteaddons => siteplugins}/components/module-index/module-index.html (80%) rename src/core/{siteaddons => siteplugins}/components/module-index/module-index.ts (81%) rename src/core/{siteaddons/components/addon-content/addon-content.html => siteplugins/components/plugin-content/plugin-content.html} (100%) rename src/core/{siteaddons/components/addon-content/addon-content.ts => siteplugins/components/plugin-content/plugin-content.ts} (83%) rename src/core/{siteaddons => siteplugins}/components/user-profile-field/user-profile-field.html (100%) rename src/core/{siteaddons => siteplugins}/components/user-profile-field/user-profile-field.ts (88%) rename src/core/{siteaddons => siteplugins}/directives/call-ws-new-content.ts (72%) rename src/core/{siteaddons => siteplugins}/directives/call-ws-on-load.ts (64%) rename src/core/{siteaddons => siteplugins}/directives/call-ws.ts (74%) rename src/core/{siteaddons => siteplugins}/directives/directives.module.ts (51%) rename src/core/{siteaddons => siteplugins}/directives/new-content.ts (72%) create mode 100644 src/core/siteplugins/pages/module-index/module-index.html rename src/core/{siteaddons => siteplugins}/pages/module-index/module-index.module.ts (71%) rename src/core/{siteaddons => siteplugins}/pages/module-index/module-index.ts (75%) rename src/core/{siteaddons/pages/addon-page/addon-page.html => siteplugins/pages/plugin-page/plugin-page.html} (59%) rename src/core/{siteaddons/pages/addon-page/addon-page.module.ts => siteplugins/pages/plugin-page/plugin-page.module.ts} (72%) rename src/core/{siteaddons/pages/addon-page/addon-page.ts => siteplugins/pages/plugin-page/plugin-page.ts} (76%) rename src/core/{siteaddons => siteplugins}/providers/helper.ts (54%) rename src/core/{siteaddons/providers/siteaddons.ts => siteplugins/providers/siteplugins.ts} (94%) rename src/core/{siteaddons/siteaddons.module.ts => siteplugins/siteplugins.module.ts} (62%) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a6bee2732..406450a66 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -65,7 +65,7 @@ import { CoreContentLinksModule } from '@core/contentlinks/contentlinks.module'; 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 { CoreSitePluginsModule } from '@core/siteplugins/siteplugins.module'; import { CoreCompileModule } from '@core/compile/compile.module'; // Addon modules. @@ -144,7 +144,7 @@ export const CORE_PROVIDERS: any[] = [ CoreUserModule, CoreGradesModule, CoreSettingsModule, - CoreSiteAddonsModule, + CoreSitePluginsModule, CoreCompileModule, AddonCalendarModule, AddonUserProfileFieldModule, diff --git a/src/classes/delegate.ts b/src/classes/delegate.ts index ac08c86eb..7f19b8c00 100644 --- a/src/classes/delegate.ts +++ b/src/classes/delegate.ts @@ -91,7 +91,7 @@ export class CoreDelegate { // Update handlers on this cases. eventsProvider.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this)); eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this)); - eventsProvider.on(CoreEventsProvider.SITE_ADDONS_LOADED, this.updateHandlers.bind(this)); + eventsProvider.on(CoreEventsProvider.SITE_PLUGINS_LOADED, this.updateHandlers.bind(this)); } } diff --git a/src/components/dynamic-component/dynamic-component.ts b/src/components/dynamic-component/dynamic-component.ts index b8dd46fa2..c41afc529 100644 --- a/src/components/dynamic-component/dynamic-component.ts +++ b/src/components/dynamic-component/dynamic-component.ts @@ -42,7 +42,7 @@ import { CoreLoggerProvider } from '@providers/logger'; * * Alternatively, you can also supply a ComponentRef instead of the class of the component. In this case, the component won't * be instantiated because it already is, it will be attached to the view and the right data will be passed to it. - * Passing ComponentRef is meant for site addons, so we'll inject a NavController instance to the component. + * Passing ComponentRef is meant for site plugins, so we'll inject a NavController instance to the component. * * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, * if no component is supplied then the template will show the message "Cannot render the data.". @@ -139,7 +139,7 @@ export class CoreDynamicComponent implements OnInit, OnChanges, DoCheck { this.container.insert(this.component.hostView); this.instance = this.component.instance; - // This feature is usually meant for site addons. Inject some properties. + // This feature is usually meant for site plugins. Inject some properties. this.instance['ChangeDetectorRef'] = this.cdr; this.instance['NavController'] = this.navCtrl; this.instance['componentContainer'] = this.element.nativeElement; diff --git a/src/core/compile/providers/compile.ts b/src/core/compile/providers/compile.ts index 1a7e10015..33b43e5b1 100644 --- a/src/core/compile/providers/compile.ts +++ b/src/core/compile/providers/compile.ts @@ -35,7 +35,7 @@ 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 { CoreSitePluginsProvider } from '../../siteplugins/providers/siteplugins'; // Import other libraries and providers. import { DomSanitizer } from '@angular/platform-browser'; @@ -47,7 +47,7 @@ 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 core classes that can be useful for site plugins. import { CoreSyncBaseProvider } from '../../../classes/base-sync'; import { CoreCache } from '../../../classes/cache'; import { CoreDelegate } from '../../../classes/delegate'; @@ -63,16 +63,16 @@ 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 { CoreSitePluginsDirectivesModule } from '../../siteplugins/directives/directives.module'; import { CoreSiteHomeComponentsModule } from '../../sitehome/components/components.module'; import { CoreUserComponentsModule } from '../../user/components/components.module'; // Import some components listed in entryComponents so they can be injected dynamically. import { CoreCourseUnsupportedModuleComponent } from '../../course/components/unsupported-module/unsupported-module'; import { CoreCourseFormatSingleActivityComponent } from '../../course/formats/singleactivity/components/singleactivity'; -import { CoreSiteAddonsModuleIndexComponent } from '../../siteaddons/components/module-index/module-index'; -import { CoreSiteAddonsCourseOptionComponent } from '../../siteaddons/components/course-option/course-option'; -import { CoreSiteAddonsCourseFormatComponent } from '../../siteaddons/components/course-format/course-format'; +import { CoreSitePluginsModuleIndexComponent } from '../../siteplugins/components/module-index/module-index'; +import { CoreSitePluginsCourseOptionComponent } from '../../siteplugins/components/course-option/course-option'; +import { CoreSitePluginsCourseFormatComponent } from '../../siteplugins/components/course-format/course-format'; /** * Service to provide functionalities regarding compiling dynamic HTML and Javascript. @@ -92,7 +92,7 @@ export class CoreCompileProvider { protected IMPORTS = [ IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreSiteHomeComponentsModule, CoreUserComponentsModule, - CoreCourseDirectivesModule, CoreSiteAddonsDirectivesModule + CoreCourseDirectivesModule, CoreSitePluginsDirectivesModule ]; constructor(protected injector: Injector, logger: CoreLoggerProvider, protected compiler: Compiler) { @@ -163,7 +163,7 @@ export class CoreCompileProvider { 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(CORE_SITEHOME_PROVIDERS).concat([CoreSitePluginsProvider]).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. @@ -198,9 +198,9 @@ export class CoreCompileProvider { instance['CoreCourseModulePrefetchHandlerBase'] = CoreCourseModulePrefetchHandlerBase; instance['CoreCourseUnsupportedModuleComponent'] = CoreCourseUnsupportedModuleComponent; instance['CoreCourseFormatSingleActivityComponent'] = CoreCourseFormatSingleActivityComponent; - instance['CoreSiteAddonsModuleIndexComponent'] = CoreSiteAddonsModuleIndexComponent; - instance['CoreSiteAddonsCourseOptionComponent'] = CoreSiteAddonsCourseOptionComponent; - instance['CoreSiteAddonsCourseFormatComponent'] = CoreSiteAddonsCourseFormatComponent; + instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent; + instance['CoreSitePluginsCourseOptionComponent'] = CoreSitePluginsCourseOptionComponent; + instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent; } /** diff --git a/src/core/siteaddons/components/course-format/course-format.html b/src/core/siteaddons/components/course-format/course-format.html deleted file mode 100644 index 69f8afc63..000000000 --- a/src/core/siteaddons/components/course-format/course-format.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/core/siteaddons/pages/module-index/module-index.html b/src/core/siteaddons/pages/module-index/module-index.html deleted file mode 100644 index a0e0ea045..000000000 --- a/src/core/siteaddons/pages/module-index/module-index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - {{ title }} - - - - - - - - - - - - diff --git a/src/core/siteaddons/classes/base-handler.ts b/src/core/siteplugins/classes/base-handler.ts similarity index 89% rename from src/core/siteaddons/classes/base-handler.ts rename to src/core/siteplugins/classes/base-handler.ts index bfdd31ef9..61ee1c8ad 100644 --- a/src/core/siteaddons/classes/base-handler.ts +++ b/src/core/siteplugins/classes/base-handler.ts @@ -15,9 +15,9 @@ import { CoreDelegateHandler } from '../../../classes/delegate'; /** - * Super class for handlers for site addons. + * Super class for handlers for site plugins. */ -export class CoreSiteAddonsBaseHandler implements CoreDelegateHandler { +export class CoreSitePluginsBaseHandler implements CoreDelegateHandler { constructor(public name: string) { } diff --git a/src/core/siteaddons/classes/call-ws-click-directive.ts b/src/core/siteplugins/classes/call-ws-click-directive.ts similarity index 77% rename from src/core/siteaddons/classes/call-ws-click-directive.ts rename to src/core/siteplugins/classes/call-ws-click-directive.ts index 7c055187d..944c6a6b6 100644 --- a/src/core/siteaddons/classes/call-ws-click-directive.ts +++ b/src/core/siteplugins/classes/call-ws-click-directive.ts @@ -15,23 +15,24 @@ import { Input, OnInit, ElementRef } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; -import { CoreSiteAddonsCallWSBaseDirective } from './call-ws-directive'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; +import { CoreSitePluginsCallWSBaseDirective } from './call-ws-directive'; /** * Base class for directives to call a WS when the element is clicked. * * The directives that inherit from this class will call a WS method when the element is clicked. * - * @see CoreSiteAddonsCallWSBaseDirective + * @see CoreSitePluginsCallWSBaseDirective */ -export class CoreSiteAddonsCallWSOnClickBaseDirective extends CoreSiteAddonsCallWSBaseDirective implements OnInit { +export class CoreSitePluginsCallWSOnClickBaseDirective extends CoreSitePluginsCallWSBaseDirective implements OnInit { @Input() confirmMessage: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. constructor(element: ElementRef, protected translate: TranslateService, protected domUtils: CoreDomUtilsProvider, - protected siteAddonsProvider: CoreSiteAddonsProvider, protected parentContent: CoreSiteAddonsAddonContentComponent) { - super(element, translate, domUtils, siteAddonsProvider, parentContent); + protected sitePluginsProvider: CoreSitePluginsProvider, + protected parentContent: CoreSitePluginsPluginContentComponent) { + super(element, translate, domUtils, sitePluginsProvider, parentContent); } /** diff --git a/src/core/siteaddons/classes/call-ws-directive.ts b/src/core/siteplugins/classes/call-ws-directive.ts similarity index 80% rename from src/core/siteaddons/classes/call-ws-directive.ts rename to src/core/siteplugins/classes/call-ws-directive.ts index 453ac0285..72f9f945e 100644 --- a/src/core/siteaddons/classes/call-ws-directive.ts +++ b/src/core/siteplugins/classes/call-ws-directive.ts @@ -15,19 +15,19 @@ import { Input, OnInit, OnDestroy, ElementRef } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; import { Subscription } from 'rxjs'; /** * Base class for directives that need to call a WS. */ -export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { +export class CoreSitePluginsCallWSBaseDirective implements OnInit, OnDestroy { @Input() name: string; // The name of the WS to call. @Input() params: any; // The params for the WS call. @Input() preSets: any; // The preSets for the WS call. @Input() useOtherDataForWS: any[]; // Whether to include other data in the params for the WS. - // @see CoreSiteAddonsProvider.loadOtherDataInArgs. + // @see CoreSitePluginsProvider.loadOtherDataInArgs. @Input() form: string; // ID or name to identify a form. The form will be obtained from document.forms. // If supplied and form is found, the form data will be retrieved and sent to the WS. @@ -35,7 +35,8 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { protected invalidateObserver: Subscription; constructor(element: ElementRef, protected translate: TranslateService, protected domUtils: CoreDomUtilsProvider, - protected siteAddonsProvider: CoreSiteAddonsProvider, protected parentContent: CoreSiteAddonsAddonContentComponent) { + protected sitePluginsProvider: CoreSitePluginsProvider, + protected parentContent: CoreSitePluginsPluginContentComponent) { this.element = element.nativeElement || element; } @@ -58,7 +59,7 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { protected callWS(): Promise { const params = this.getParamsForWS(); - return this.siteAddonsProvider.callWS(this.name, params, this.preSets).then((result) => { + return this.sitePluginsProvider.callWS(this.name, params, this.preSets).then((result) => { return this.wsCallSuccess(result); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'core.serverconnection', true); @@ -74,7 +75,7 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { let params = this.params || {}; if (this.parentContent) { - params = this.siteAddonsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); + params = this.sitePluginsProvider.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); } if (this.form && document.forms[this.form]) { @@ -101,7 +102,7 @@ export class CoreSiteAddonsCallWSBaseDirective implements OnInit, OnDestroy { invalidate(): Promise { const params = this.getParamsForWS(); - return this.siteAddonsProvider.invalidateCallWS(this.name, params, this.preSets); + return this.sitePluginsProvider.invalidateCallWS(this.name, params, this.preSets); } /** diff --git a/src/core/siteaddons/classes/course-format-handler.ts b/src/core/siteplugins/classes/course-format-handler.ts similarity index 87% rename from src/core/siteaddons/classes/course-format-handler.ts rename to src/core/siteplugins/classes/course-format-handler.ts index fddf165a2..197c58bec 100644 --- a/src/core/siteaddons/classes/course-format-handler.ts +++ b/src/core/siteplugins/classes/course-format-handler.ts @@ -14,13 +14,13 @@ import { Injector } from '@angular/core'; import { CoreCourseFormatHandler } from '../../course/providers/format-delegate'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; -import { CoreSiteAddonsCourseFormatComponent } from '../components/course-format/course-format'; +import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CoreSitePluginsCourseFormatComponent } from '../components/course-format/course-format'; /** - * Handler to support a course format using a site addon. + * Handler to support a course format using a site plugin. */ -export class CoreSiteAddonsCourseFormatHandler extends CoreSiteAddonsBaseHandler implements CoreCourseFormatHandler { +export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandler implements CoreCourseFormatHandler { constructor(name: string, protected handlerSchema: any) { super(name); @@ -68,7 +68,7 @@ export class CoreSiteAddonsCourseFormatHandler extends CoreSiteAddonsBaseHandler */ getCourseFormatComponent(injector: Injector, course: any): any | Promise { if (this.handlerSchema.method) { - return CoreSiteAddonsCourseFormatComponent; + return CoreSitePluginsCourseFormatComponent; } } } diff --git a/src/core/siteaddons/classes/course-option-handler.ts b/src/core/siteplugins/classes/course-option-handler.ts similarity index 74% rename from src/core/siteaddons/classes/course-option-handler.ts rename to src/core/siteplugins/classes/course-option-handler.ts index b0041f401..fa9509836 100644 --- a/src/core/siteaddons/classes/course-option-handler.ts +++ b/src/core/siteplugins/classes/course-option-handler.ts @@ -13,19 +13,19 @@ // limitations under the License. import { Injector } from '@angular/core'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; -import { CoreSiteAddonsCourseOptionComponent } from '../components/course-option/course-option'; +import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CoreSitePluginsCourseOptionComponent } from '../components/course-option/course-option'; /** - * Handler to display a site addon in course options. + * Handler to display a site plugin in course options. */ -export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler implements CoreCourseOptionsHandler { +export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandler implements CoreCourseOptionsHandler { priority: number; - constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, - protected bootstrapResult: any, protected siteAddonsProvider: CoreSiteAddonsProvider) { + constructor(name: string, protected title: string, protected plugin: any, protected handlerSchema: any, + protected bootstrapResult: any, protected sitePluginsProvider: CoreSitePluginsProvider) { super(name); this.priority = handlerSchema.priority; @@ -41,7 +41,7 @@ export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler * @return {boolean|Promise} True or promise resolved with true if enabled. */ isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - return this.siteAddonsProvider.isHandlerEnabledForCourse( + return this.sitePluginsProvider.isHandlerEnabledForCourse( courseId, this.handlerSchema.restricttoenrolledcourses, this.bootstrapResult.restrict); } @@ -56,7 +56,7 @@ export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler return { title: this.title, class: this.handlerSchema.displaydata.class, - component: CoreSiteAddonsCourseOptionComponent, + component: CoreSitePluginsCourseOptionComponent, componentData: { handlerUniqueName: this.name } @@ -64,7 +64,7 @@ export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler } /** - * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. + * Called when a course is downloaded. It should prefetch all the data to be able to see the plugin in offline. * * @param {any} course The course. * @return {Promise} Promise resolved when done. @@ -73,8 +73,8 @@ export class CoreSiteAddonsCourseOptionHandler extends CoreSiteAddonsBaseHandler const args = { courseid: course.id, }, - component = this.addon.component; + component = this.plugin.component; - return this.siteAddonsProvider.prefetchFunctions(component, args, this.handlerSchema, course.id, undefined, true); + return this.sitePluginsProvider.prefetchFunctions(component, args, this.handlerSchema, course.id, undefined, true); } } diff --git a/src/core/siteaddons/classes/main-menu-handler.ts b/src/core/siteplugins/classes/main-menu-handler.ts similarity index 79% rename from src/core/siteaddons/classes/main-menu-handler.ts rename to src/core/siteplugins/classes/main-menu-handler.ts index a66a01788..e43b4df8a 100644 --- a/src/core/siteaddons/classes/main-menu-handler.ts +++ b/src/core/siteplugins/classes/main-menu-handler.ts @@ -13,15 +13,15 @@ // limitations under the License. import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSitePluginsBaseHandler } from './base-handler'; /** - * Handler to display a site addon in the main menu. + * Handler to display a site plugin in the main menu. */ -export class CoreSiteAddonsMainMenuHandler extends CoreSiteAddonsBaseHandler implements CoreMainMenuHandler { +export class CoreSitePluginsMainMenuHandler extends CoreSitePluginsBaseHandler implements CoreMainMenuHandler { priority: number; - constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, + constructor(name: string, protected title: string, protected plugin: any, protected handlerSchema: any, protected bootstrapResult: any) { super(name); @@ -38,10 +38,10 @@ export class CoreSiteAddonsMainMenuHandler extends CoreSiteAddonsBaseHandler imp title: this.title, icon: this.handlerSchema.displaydata.icon, class: this.handlerSchema.displaydata.class, - page: 'CoreSiteAddonsAddonPage', + page: 'CoreSitePluginsPluginPage', pageParams: { title: this.title, - component: this.addon.component, + component: this.plugin.component, method: this.handlerSchema.method, bootstrapResult: this.bootstrapResult } diff --git a/src/core/siteaddons/classes/module-handler.ts b/src/core/siteplugins/classes/module-handler.ts similarity index 86% rename from src/core/siteaddons/classes/module-handler.ts rename to src/core/siteplugins/classes/module-handler.ts index 29bfb911b..a9d0eb098 100644 --- a/src/core/siteaddons/classes/module-handler.ts +++ b/src/core/siteplugins/classes/module-handler.ts @@ -15,13 +15,13 @@ import { Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../../course/providers/module-delegate'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; -import { CoreSiteAddonsModuleIndexComponent } from '../components/module-index/module-index'; +import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CoreSitePluginsModuleIndexComponent } from '../components/module-index/module-index'; /** - * Handler to support a module using a site addon. + * Handler to support a module using a site plugin. */ -export class CoreSiteAddonsModuleHandler extends CoreSiteAddonsBaseHandler implements CoreCourseModuleHandler { +export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler implements CoreCourseModuleHandler { priority: number; constructor(name: string, protected handlerSchema: any) { @@ -49,7 +49,7 @@ export class CoreSiteAddonsModuleHandler extends CoreSiteAddonsBaseHandler imple event.preventDefault(); event.stopPropagation(); - navCtrl.push('CoreSiteAddonsModuleIndexPage', { + navCtrl.push('CoreSitePluginsModuleIndexPage', { title: module.name, module: module, courseId: courseId @@ -69,6 +69,6 @@ export class CoreSiteAddonsModuleHandler extends CoreSiteAddonsBaseHandler imple * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ getMainComponent(injector: Injector, course: any, module: any): any | Promise { - return CoreSiteAddonsModuleIndexComponent; + return CoreSitePluginsModuleIndexComponent; } } diff --git a/src/core/siteaddons/classes/module-prefetch-handler.ts b/src/core/siteplugins/classes/module-prefetch-handler.ts similarity index 86% rename from src/core/siteaddons/classes/module-prefetch-handler.ts rename to src/core/siteplugins/classes/module-prefetch-handler.ts index 31c502892..a6b83c784 100644 --- a/src/core/siteaddons/classes/module-prefetch-handler.ts +++ b/src/core/siteplugins/classes/module-prefetch-handler.ts @@ -13,16 +13,16 @@ // limitations under the License. import { Injector } from '@angular/core'; -import { CoreSiteAddonsProvider } from '../../siteaddons/providers/siteaddons'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreCourseModulePrefetchHandlerBase } from '../../course/classes/module-prefetch-handler'; /** - * Handler to prefetch a site addon. + * Handler to prefetch a module site plugin. */ -export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetchHandlerBase { - protected ROOT_CACHE_KEY = 'CoreSiteAddonsModulePrefetchHandler:'; +export class CoreSitePluginsModulePrefetchHandler extends CoreCourseModulePrefetchHandlerBase { + protected ROOT_CACHE_KEY = 'CoreSitePluginsModulePrefetchHandler:'; - constructor(injector: Injector, protected siteAddonsProvider: CoreSiteAddonsProvider, component: string, modName: string, + constructor(injector: Injector, protected sitePluginsProvider: CoreSitePluginsProvider, component: string, modName: string, protected handlerSchema: any) { super(injector); @@ -51,11 +51,11 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc * @return {Promise} Promise resolved when all content is downloaded. Data returned is not reliable. */ downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise { - return this.prefetchPackage(module, courseId, false, this.downloadOrPrefetchAddon.bind(this), undefined, prefetch, dirPath); + return this.prefetchPackage(module, courseId, false, this.downloadPrefetchPlugin.bind(this), undefined, prefetch, dirPath); } /** - * Download or prefetch the addon, downloading the files and calling the needed WS. + * Download or prefetch the plugin, downloading the files and calling the needed WS. * * @param {any} module The module object returned by WS. * @param {number} courseId Course ID. @@ -65,7 +65,7 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc * @param {string} [dirPath] Path of the directory where to store all the content files. @see downloadOrPrefetch. * @return {Promise} Promise resolved when done. */ - protected downloadOrPrefetchAddon(module: any, courseId: number, single?: boolean, siteId?: string, prefetch?: boolean, + protected downloadPrefetchPlugin(module: any, courseId: number, single?: boolean, siteId?: string, prefetch?: boolean, dirPath?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { @@ -80,7 +80,7 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc promises.push(this.downloadOrPrefetchFiles(site.id, module, courseId, prefetch, dirPath)); // Call all the offline functions. - promises.push(this.siteAddonsProvider.prefetchFunctions(this.component, args, this.handlerSchema, courseId, + promises.push(this.sitePluginsProvider.prefetchFunctions(this.component, args, this.handlerSchema, courseId, module, prefetch, dirPath, site)); return Promise.all(promises); @@ -88,7 +88,7 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc } /** - * Download or prefetch the addon files. + * Download or prefetch the plugin files. * * @param {any} module The module object returned by WS. * @param {number} courseId Course ID. @@ -150,10 +150,10 @@ export class CoreSiteAddonsModulePrefetchHandler extends CoreCourseModulePrefetc for (const method in this.handlerSchema.offlinefunctions) { if (currentSite.wsAvailable(method)) { // The method is a WS. - promises.push(currentSite.invalidateWsCacheForKey(this.siteAddonsProvider.getCallWSCacheKey(method, args))); + promises.push(currentSite.invalidateWsCacheForKey(this.sitePluginsProvider.getCallWSCacheKey(method, args))); } else { // It's a method to get content. - promises.push(this.siteAddonsProvider.invalidateContent(this.component, method, args)); + promises.push(this.sitePluginsProvider.invalidateContent(this.component, method, args)); } } diff --git a/src/core/siteaddons/classes/user-handler.ts b/src/core/siteplugins/classes/user-handler.ts similarity index 82% rename from src/core/siteaddons/classes/user-handler.ts rename to src/core/siteplugins/classes/user-handler.ts index 482af10db..25b00c6b2 100644 --- a/src/core/siteaddons/classes/user-handler.ts +++ b/src/core/siteplugins/classes/user-handler.ts @@ -14,13 +14,13 @@ import { NavController } from 'ionic-angular'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsBaseHandler } from './base-handler'; /** - * Handler to display a site addon in the user profile. + * Handler to display a site plugin in the user profile. */ -export class CoreSiteAddonsUserProfileHandler extends CoreSiteAddonsBaseHandler implements CoreUserProfileHandler { +export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandler implements CoreUserProfileHandler { /** * The highest priority is displayed first. * @type {number} @@ -37,8 +37,8 @@ export class CoreSiteAddonsUserProfileHandler extends CoreSiteAddonsBaseHandler */ type: string; - constructor(name: string, protected title: string, protected addon: any, protected handlerSchema: any, - protected bootstrapResult: any, protected siteAddonsProvider: CoreSiteAddonsProvider) { + constructor(name: string, protected title: string, protected plugin: any, protected handlerSchema: any, + protected bootstrapResult: any, protected sitePluginsProvider: CoreSitePluginsProvider) { super(name); this.priority = handlerSchema.priority; @@ -58,14 +58,14 @@ export class CoreSiteAddonsUserProfileHandler extends CoreSiteAddonsBaseHandler */ isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise { // First check if it's enabled for the user. - const enabledForUser = this.siteAddonsProvider.isHandlerEnabledForUser(user.id, this.handlerSchema.restricttocurrentuser, + const enabledForUser = this.sitePluginsProvider.isHandlerEnabledForUser(user.id, this.handlerSchema.restricttocurrentuser, this.bootstrapResult.restrict); if (!enabledForUser) { return false; } // Enabled for user, check if it's enabled for the course. - return this.siteAddonsProvider.isHandlerEnabledForCourse( + return this.sitePluginsProvider.isHandlerEnabledForCourse( courseId, this.handlerSchema.restricttoenrolledcourses, this.bootstrapResult.restrict); } @@ -84,9 +84,9 @@ export class CoreSiteAddonsUserProfileHandler extends CoreSiteAddonsBaseHandler event.preventDefault(); event.stopPropagation(); - navCtrl.push('CoreSiteAddonsAddonPage', { + navCtrl.push('CoreSitePluginsPluginPage', { title: this.title, - component: this.addon.component, + component: this.plugin.component, method: this.handlerSchema.method, args: { courseid: courseId, diff --git a/src/core/siteaddons/classes/user-profile-field-handler.ts b/src/core/siteplugins/classes/user-profile-field-handler.ts similarity index 84% rename from src/core/siteaddons/classes/user-profile-field-handler.ts rename to src/core/siteplugins/classes/user-profile-field-handler.ts index 2ab04e807..6f88c6cc7 100644 --- a/src/core/siteaddons/classes/user-profile-field-handler.ts +++ b/src/core/siteplugins/classes/user-profile-field-handler.ts @@ -14,13 +14,13 @@ import { Injector } from '@angular/core'; import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../user/providers/user-profile-field-delegate'; -import { CoreSiteAddonsBaseHandler } from './base-handler'; -import { CoreSiteAddonsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; +import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CoreSitePluginsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; /** - * Handler to display a site addon in the user profile. + * Handler to display a site plugin in the user profile. */ -export class CoreSiteAddonsUserProfileFieldHandler extends CoreSiteAddonsBaseHandler implements CoreUserProfileFieldHandler { +export class CoreSitePluginsUserProfileFieldHandler extends CoreSitePluginsBaseHandler implements CoreUserProfileFieldHandler { constructor(name: string) { super(name); @@ -34,7 +34,7 @@ export class CoreSiteAddonsUserProfileFieldHandler extends CoreSiteAddonsBaseHan * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ getComponent(injector: Injector): any | Promise { - return CoreSiteAddonsUserProfileFieldComponent; + return CoreSitePluginsUserProfileFieldComponent; } /** diff --git a/src/core/siteaddons/components/components.module.ts b/src/core/siteplugins/components/components.module.ts similarity index 52% rename from src/core/siteaddons/components/components.module.ts rename to src/core/siteplugins/components/components.module.ts index ac5daadeb..f53ae7c01 100644 --- a/src/core/siteaddons/components/components.module.ts +++ b/src/core/siteplugins/components/components.module.ts @@ -18,19 +18,19 @@ import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '../../../components/components.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'; -import { CoreSiteAddonsCourseOptionComponent } from './course-option/course-option'; -import { CoreSiteAddonsCourseFormatComponent } from './course-format/course-format'; -import { CoreSiteAddonsUserProfileFieldComponent } from './user-profile-field/user-profile-field'; +import { CoreSitePluginsPluginContentComponent } from './plugin-content/plugin-content'; +import { CoreSitePluginsModuleIndexComponent } from './module-index/module-index'; +import { CoreSitePluginsCourseOptionComponent } from './course-option/course-option'; +import { CoreSitePluginsCourseFormatComponent } from './course-format/course-format'; +import { CoreSitePluginsUserProfileFieldComponent } from './user-profile-field/user-profile-field'; @NgModule({ declarations: [ - CoreSiteAddonsAddonContentComponent, - CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent, - CoreSiteAddonsUserProfileFieldComponent + CoreSitePluginsPluginContentComponent, + CoreSitePluginsModuleIndexComponent, + CoreSitePluginsCourseOptionComponent, + CoreSitePluginsCourseFormatComponent, + CoreSitePluginsUserProfileFieldComponent ], imports: [ CommonModule, @@ -42,17 +42,17 @@ import { CoreSiteAddonsUserProfileFieldComponent } from './user-profile-field/us providers: [ ], exports: [ - CoreSiteAddonsAddonContentComponent, - CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent, - CoreSiteAddonsUserProfileFieldComponent + CoreSitePluginsPluginContentComponent, + CoreSitePluginsModuleIndexComponent, + CoreSitePluginsCourseOptionComponent, + CoreSitePluginsCourseFormatComponent, + CoreSitePluginsUserProfileFieldComponent ], entryComponents: [ - CoreSiteAddonsModuleIndexComponent, - CoreSiteAddonsCourseOptionComponent, - CoreSiteAddonsCourseFormatComponent, - CoreSiteAddonsUserProfileFieldComponent + CoreSitePluginsModuleIndexComponent, + CoreSitePluginsCourseOptionComponent, + CoreSitePluginsCourseFormatComponent, + CoreSitePluginsUserProfileFieldComponent ] }) -export class CoreSiteAddonsComponentsModule {} +export class CoreSitePluginsComponentsModule {} diff --git a/src/core/siteplugins/components/course-format/course-format.html b/src/core/siteplugins/components/course-format/course-format.html new file mode 100644 index 000000000..b4dd6868b --- /dev/null +++ b/src/core/siteplugins/components/course-format/course-format.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/core/siteaddons/components/course-format/course-format.ts b/src/core/siteplugins/components/course-format/course-format.ts similarity index 72% rename from src/core/siteaddons/components/course-format/course-format.ts rename to src/core/siteplugins/components/course-format/course-format.ts index 5dd0e1148..72a5972f4 100644 --- a/src/core/siteaddons/components/course-format/course-format.ts +++ b/src/core/siteplugins/components/course-format/course-format.ts @@ -13,38 +13,38 @@ // limitations under the License. import { Component, OnInit, Input, ViewChild } from '@angular/core'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; -import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../../providers/siteplugins'; +import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; /** - * Component that displays the index of a course format site addon. + * Component that displays the index of a course format site plugin. */ @Component({ - selector: 'core-site-addons-course-format', + selector: 'core-site-plugins-course-format', templateUrl: 'course-format.html', }) -export class CoreSiteAddonsCourseFormatComponent implements OnInit { +export class CoreSitePluginsCourseFormatComponent implements OnInit { @Input() course: any; // The course to render. @Input() sections: any[]; // List of course sections. @Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled. - @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; + @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; component: string; method: string; args: any; bootstrapResult: any; - constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { } /** * Component being initialized. */ ngOnInit(): void { if (this.course && this.course.format) { - const handler = this.siteAddonsProvider.getSiteAddonHandler(this.course.format); + const handler = this.sitePluginsProvider.getSitePluginHandler(this.course.format); if (handler) { - this.component = handler.addon.component; + this.component = handler.plugin.component; this.method = handler.handlerSchema.method; this.args = { courseid: this.course.id, diff --git a/src/core/siteaddons/components/course-option/course-option.html b/src/core/siteplugins/components/course-option/course-option.html similarity index 53% rename from src/core/siteaddons/components/course-option/course-option.html rename to src/core/siteplugins/components/course-option/course-option.html index ada863f5e..fcda25ccd 100644 --- a/src/core/siteaddons/components/course-option/course-option.html +++ b/src/core/siteplugins/components/course-option/course-option.html @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/src/core/siteaddons/components/course-option/course-option.ts b/src/core/siteplugins/components/course-option/course-option.ts similarity index 68% rename from src/core/siteaddons/components/course-option/course-option.ts rename to src/core/siteplugins/components/course-option/course-option.ts index 99a26249a..b14e67b76 100644 --- a/src/core/siteaddons/components/course-option/course-option.ts +++ b/src/core/siteplugins/components/course-option/course-option.ts @@ -13,37 +13,37 @@ // limitations under the License. import { Component, OnInit, Input, ViewChild } from '@angular/core'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; -import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../../providers/siteplugins'; +import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; /** - * Component that displays the index of a course option site addon. + * Component that displays the index of a course option site plugin. */ @Component({ - selector: 'core-site-addons-course-option', + selector: 'core-site-plugins-course-option', templateUrl: 'course-option.html', }) -export class CoreSiteAddonsCourseOptionComponent implements OnInit { +export class CoreSitePluginsCourseOptionComponent implements OnInit { @Input() courseId: number; @Input() handlerUniqueName: string; - @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; + @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; component: string; method: string; args: any; bootstrapResult: any; - constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { } /** * Component being initialized. */ ngOnInit(): void { if (this.handlerUniqueName) { - const handler = this.siteAddonsProvider.getSiteAddonHandler(this.handlerUniqueName); + const handler = this.sitePluginsProvider.getSitePluginHandler(this.handlerUniqueName); if (handler) { - this.component = handler.addon.component; + this.component = handler.plugin.component; this.method = handler.handlerSchema.method; this.args = { courseid: this.courseId, diff --git a/src/core/siteaddons/components/module-index/module-index.html b/src/core/siteplugins/components/module-index/module-index.html similarity index 80% rename from src/core/siteaddons/components/module-index/module-index.html rename to src/core/siteplugins/components/module-index/module-index.html index d2078263f..bee280179 100644 --- a/src/core/siteaddons/components/module-index/module-index.html +++ b/src/core/siteplugins/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/siteplugins/components/module-index/module-index.ts similarity index 81% rename from src/core/siteaddons/components/module-index/module-index.ts rename to src/core/siteplugins/components/module-index/module-index.ts index 256390380..72322f59f 100644 --- a/src/core/siteaddons/components/module-index/module-index.ts +++ b/src/core/siteplugins/components/module-index/module-index.ts @@ -15,24 +15,24 @@ import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreTextUtilsProvider } from '../../../../providers/utils/text'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSitePluginsProvider } from '../../providers/siteplugins'; import { CoreCourseModuleMainComponent } from '../../../course/providers/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; import { CoreCourseHelperProvider } from '../../../course/providers/helper'; -import { CoreSiteAddonsAddonContentComponent } from '../addon-content/addon-content'; +import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; /** - * Component that displays the index of a module site addon. + * Component that displays the index of a module site plugin. */ @Component({ - selector: 'core-site-addons-module-index', + selector: 'core-site-plugins-module-index', templateUrl: 'module-index.html', }) -export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent { +export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent { @Input() module: any; // The module. @Input() courseId: number; // Course ID the module belongs to. - @ViewChild(CoreSiteAddonsAddonContentComponent) addonContent: CoreSiteAddonsAddonContentComponent; + @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; component: string; method: string; @@ -50,7 +50,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co protected isDestroyed = false; protected statusObserver; - constructor(protected siteAddonsProvider: CoreSiteAddonsProvider, protected courseHelper: CoreCourseHelperProvider, + constructor(protected sitePluginsProvider: CoreSitePluginsProvider, protected courseHelper: CoreCourseHelperProvider, protected prefetchDelegate: CoreCourseModulePrefetchDelegate, protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService) { } @@ -61,9 +61,9 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co this.refreshIcon = 'spinner'; if (this.module) { - const handler = this.siteAddonsProvider.getSiteAddonHandler(this.module.modname); + const handler = this.sitePluginsProvider.getSitePluginHandler(this.module.modname); if (handler) { - this.component = handler.addon.component; + this.component = handler.plugin.component; this.method = handler.handlerSchema.method; this.args = { courseid: this.courseId, @@ -86,10 +86,10 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co * @return {Promise} Promise resolved when done. */ doRefresh(refresher?: any, done?: () => void): Promise { - if (this.addonContent) { + if (this.content) { this.refreshIcon = 'spinner'; - return Promise.resolve(this.addonContent.refreshData()).finally(() => { + return Promise.resolve(this.content.refreshData()).finally(() => { refresher && refresher.complete(); done && done(); }); @@ -102,7 +102,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co } /** - * Function called when the data of the site addon content is loaded. + * Function called when the data of the site plugin content is loaded. */ contentLoaded(refresh: boolean): void { this.refreshIcon = 'refresh'; @@ -114,7 +114,7 @@ export class CoreSiteAddonsModuleIndexComponent implements OnInit, OnDestroy, Co } /** - * Function called when starting to load the data of the site addon content. + * Function called when starting to load the data of the site plugin content. */ contentLoading(refresh: boolean): void { this.refreshIcon = 'spinner'; diff --git a/src/core/siteaddons/components/addon-content/addon-content.html b/src/core/siteplugins/components/plugin-content/plugin-content.html similarity index 100% rename from src/core/siteaddons/components/addon-content/addon-content.html rename to src/core/siteplugins/components/plugin-content/plugin-content.html diff --git a/src/core/siteaddons/components/addon-content/addon-content.ts b/src/core/siteplugins/components/plugin-content/plugin-content.ts similarity index 83% rename from src/core/siteaddons/components/addon-content/addon-content.ts rename to src/core/siteplugins/components/plugin-content/plugin-content.ts index 557ba1258..77636c765 100644 --- a/src/core/siteaddons/components/addon-content/addon-content.ts +++ b/src/core/siteplugins/components/plugin-content/plugin-content.ts @@ -14,17 +14,17 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSitePluginsProvider } from '../../providers/siteplugins'; import { Subject } from 'rxjs'; /** - * Component to render a site addon content. + * Component to render a site plugin content. */ @Component({ - selector: 'core-site-addons-addon-content', - templateUrl: 'addon-content.html', + selector: 'core-site-plugins-plugin-content', + templateUrl: 'plugin-content.html', }) -export class CoreSiteAddonsAddonContentComponent implements OnInit { +export class CoreSitePluginsPluginContentComponent implements OnInit { @Input() component: string; @Input() method: string; @Input() args: any; @@ -39,7 +39,7 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { invalidateObservable: Subject; // An observable to notify observers when to invalidate data. jsData: any; // Data to pass to the component. - constructor(protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) { + constructor(protected domUtils: CoreDomUtilsProvider, protected sitePluginsProvider: CoreSitePluginsProvider) { this.onContentLoaded = new EventEmitter(); this.onLoadingContent = new EventEmitter(); this.invalidateObservable = new Subject(); @@ -61,11 +61,11 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { fetchContent(refresh?: boolean): Promise { this.onLoadingContent.emit(refresh); - return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => { + return this.sitePluginsProvider.getContent(this.component, this.method, this.args).then((result) => { this.content = result.templates.length ? result.templates[0].html : ''; // Load first template. this.javascript = result.javascript; this.otherData = result.otherdata; - this.jsData = this.siteAddonsProvider.createDataForJS(this.bootstrapResult, result); + this.jsData = this.sitePluginsProvider.createDataForJS(this.bootstrapResult, result); this.onContentLoaded.emit(refresh); }).catch((error) => { @@ -87,7 +87,7 @@ export class CoreSiteAddonsAddonContentComponent implements OnInit { this.invalidateObservable.next(); // Notify observers. - return this.siteAddonsProvider.invalidateContent(this.component, this.method, this.args).finally(() => { + return this.sitePluginsProvider.invalidateContent(this.component, this.method, this.args).finally(() => { return this.fetchContent(true); }); } diff --git a/src/core/siteaddons/components/user-profile-field/user-profile-field.html b/src/core/siteplugins/components/user-profile-field/user-profile-field.html similarity index 100% rename from src/core/siteaddons/components/user-profile-field/user-profile-field.html rename to src/core/siteplugins/components/user-profile-field/user-profile-field.html diff --git a/src/core/siteaddons/components/user-profile-field/user-profile-field.ts b/src/core/siteplugins/components/user-profile-field/user-profile-field.ts similarity index 88% rename from src/core/siteaddons/components/user-profile-field/user-profile-field.ts rename to src/core/siteplugins/components/user-profile-field/user-profile-field.ts index 4e0714b36..9d017ce2e 100644 --- a/src/core/siteaddons/components/user-profile-field/user-profile-field.ts +++ b/src/core/siteplugins/components/user-profile-field/user-profile-field.ts @@ -13,18 +13,18 @@ // limitations under the License. import { Component, OnInit, Input, ViewChild, OnDestroy } from '@angular/core'; -import { CoreSiteAddonsProvider } from '../../providers/siteaddons'; +import { CoreSitePluginsProvider } from '../../providers/siteplugins'; import { CoreCompileHtmlComponent } from '../../../compile/components/compile-html/compile-html'; import { Subscription } from 'rxjs'; /** - * Component that displays a user profile field created using a site addon. + * Component that displays a user profile field created using a site plugin. */ @Component({ - selector: 'core-site-addons-user-profile-field', + selector: 'core-site-plugins-user-profile-field', templateUrl: 'user-profile-field.html', }) -export class CoreSiteAddonsUserProfileFieldComponent implements OnInit, OnDestroy { +export class CoreSitePluginsUserProfileFieldComponent implements OnInit, OnDestroy { @Input() field: any; // The profile field to be rendered. @Input() signup = false; // True if editing the field in signup. Defaults to false. @Input() edit = false; // True if editing the field. Defaults to false. @@ -37,7 +37,7 @@ export class CoreSiteAddonsUserProfileFieldComponent implements OnInit, OnDestro jsData; protected componentObserver: Subscription; - constructor(protected siteAddonsProvider: CoreSiteAddonsProvider) { } + constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { } /** * Component being initialized. @@ -55,7 +55,7 @@ export class CoreSiteAddonsUserProfileFieldComponent implements OnInit, OnDestro if (this.field) { // Retrieve the handler data. - const handler = this.siteAddonsProvider.getSiteAddonHandler(this.field.type || this.field.datatype), + const handler = this.sitePluginsProvider.getSitePluginHandler(this.field.type || this.field.datatype), handlerSchema = handler && handler.handlerSchema; if (handlerSchema) { diff --git a/src/core/siteaddons/directives/call-ws-new-content.ts b/src/core/siteplugins/directives/call-ws-new-content.ts similarity index 72% rename from src/core/siteaddons/directives/call-ws-new-content.ts rename to src/core/siteplugins/directives/call-ws-new-content.ts index f4d0666e9..43991db73 100644 --- a/src/core/siteaddons/directives/call-ws-new-content.ts +++ b/src/core/siteplugins/directives/call-ws-new-content.ts @@ -17,53 +17,53 @@ import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; /** * Directive to call a WS when the element is clicked and load a new content passing the WS result as args. This new content - * can be displayed in a new page or in the same page (only if current page is already displaying a site addon content). + * can be displayed in a new page or in the same page (only if current page is already displaying a site plugin content). * - * If you don't need to load some new content when done, @see CoreSiteAddonsCallWSDirective. + * If you don't need to load some new content when done, @see CoreSitePluginsCallWSDirective. * - * @see CoreSiteAddonsCallWSOnClickBaseDirective. + * @see CoreSitePluginsCallWSOnClickBaseDirective. * * Example usages: * * A button to get some data from the server without using cache, showing default confirm and displaying a new page: * - * * * A button to get some data from the server using cache, without confirm, displaying new content in same page and using * userid from otherdata: * - * */ @Directive({ - selector: '[core-site-addons-call-ws-new-content]' + selector: '[core-site-plugins-call-ws-new-content]' }) -export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallWSOnClickBaseDirective { +export class CoreSitePluginsCallWSNewContentDirective extends CoreSitePluginsCallWSOnClickBaseDirective { @Input() component: string; // The component of the new content. @Input() method: string; // The method to get the new content. @Input() args: any; // The params to get the new content. @Input() title: string; // The title to display with the new content. Only if samePage=false. @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. - @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. + @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSitePluginsProvider.loadOtherDataInArgs. constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, - siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, + sitePluginsProvider: CoreSitePluginsProvider, @Optional() parentContent: CoreSitePluginsPluginContentComponent, protected utils: CoreUtilsProvider, @Optional() protected navCtrl: NavController) { - super(element, translate, domUtils, siteAddonsProvider, parentContent); + super(element, translate, domUtils, sitePluginsProvider, parentContent); } /** @@ -75,7 +75,7 @@ export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallW let args = this.args || {}; if (this.parentContent) { - args = this.siteAddonsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + args = this.sitePluginsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); } // Add the properties from the WS call result to the args. @@ -87,7 +87,7 @@ export class CoreSiteAddonsCallWSNewContentDirective extends CoreSiteAddonsCallW this.parentContent.updateContent(this.component, this.method, args); } } else { - this.navCtrl.push('CoreSiteAddonsAddonPage', { + this.navCtrl.push('CoreSitePluginsPluginPage', { title: this.title, component: this.component, method: this.method, diff --git a/src/core/siteaddons/directives/call-ws-on-load.ts b/src/core/siteplugins/directives/call-ws-on-load.ts similarity index 64% rename from src/core/siteaddons/directives/call-ws-on-load.ts rename to src/core/siteplugins/directives/call-ws-on-load.ts index bca0ab323..37b09de1f 100644 --- a/src/core/siteaddons/directives/call-ws-on-load.ts +++ b/src/core/siteplugins/directives/call-ws-on-load.ts @@ -16,31 +16,31 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsCallWSBaseDirective } from '../classes/call-ws-directive'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; /** * Directive to call a WS as soon as its loaded. * This directive is meant for actions to do in the background, like calling logging WebServices. * - * If you want to call a WS when the user clicks on a certain element, @see CoreSiteAddonsCallWSDirective. + * If you want to call a WS when the user clicks on a certain element, @see CoreSitePluginsCallWSDirective. * - * @see CoreSiteAddonsCallWSBaseDirective. + * @see CoreSitePluginsCallWSBaseDirective. * * Example usage: * - * */ @Directive({ - selector: '[core-site-addons-call-ws-on-load]' + selector: '[core-site-plugins-call-ws-on-load]' }) -export class CoreSiteAddonsCallWSOnLoadDirective extends CoreSiteAddonsCallWSBaseDirective implements OnInit { +export class CoreSitePluginsCallWSOnLoadDirective extends CoreSitePluginsCallWSBaseDirective implements OnInit { constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, - siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent) { - super(element, translate, domUtils, siteAddonsProvider, parentContent); + sitePluginsProvider: CoreSitePluginsProvider, @Optional() parentContent: CoreSitePluginsPluginContentComponent) { + super(element, translate, domUtils, sitePluginsProvider, parentContent); } /** diff --git a/src/core/siteaddons/directives/call-ws.ts b/src/core/siteplugins/directives/call-ws.ts similarity index 74% rename from src/core/siteaddons/directives/call-ws.ts rename to src/core/siteplugins/directives/call-ws.ts index 360a7f39d..d41ead26e 100644 --- a/src/core/siteaddons/directives/call-ws.ts +++ b/src/core/siteplugins/directives/call-ws.ts @@ -17,47 +17,47 @@ import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; /** * Directive to call a WS when the element is clicked. The action to do when the WS call is successful depends on the input data: * display a message, go back or refresh current view. * - * If you want to load a new content when the WS call is done, @see CoreSiteAddonsCallWSNewContentDirective. + * If you want to load a new content when the WS call is done, @see CoreSitePluginsCallWSNewContentDirective. * - * @see CoreSiteAddonsCallWSOnClickBaseDirective. + * @see CoreSitePluginsCallWSOnClickBaseDirective. * * Example usages: * * A button to send some data to the server without using cache, displaying default messages and refreshing on success: * - * * * A button to send some data to the server using cache, without confirm, going back on success and using userid from otherdata: * - * */ @Directive({ - selector: '[core-site-addons-call-ws]' + selector: '[core-site-plugins-call-ws]' }) -export class CoreSiteAddonsCallWSDirective extends CoreSiteAddonsCallWSOnClickBaseDirective { +export class CoreSitePluginsCallWSDirective extends CoreSitePluginsCallWSOnClickBaseDirective { @Input() successMessage: string; // Message to show on success. If not supplied, no message. If empty, default message. @Input() goBackOnSuccess: boolean | string; // Whether to go back if the WS call is successful. @Input() refreshOnSuccess: boolean | string; // Whether to refresh the current view if the WS call is successful. constructor(element: ElementRef, translate: TranslateService, domUtils: CoreDomUtilsProvider, - siteAddonsProvider: CoreSiteAddonsProvider, @Optional() parentContent: CoreSiteAddonsAddonContentComponent, + sitePluginsProvider: CoreSitePluginsProvider, @Optional() parentContent: CoreSitePluginsPluginContentComponent, protected utils: CoreUtilsProvider, protected navCtrl: NavController) { - super(element, translate, domUtils, siteAddonsProvider, parentContent); + super(element, translate, domUtils, sitePluginsProvider, parentContent); } /** diff --git a/src/core/siteaddons/directives/directives.module.ts b/src/core/siteplugins/directives/directives.module.ts similarity index 51% rename from src/core/siteaddons/directives/directives.module.ts rename to src/core/siteplugins/directives/directives.module.ts index 973182d91..4197acace 100644 --- a/src/core/siteaddons/directives/directives.module.ts +++ b/src/core/siteplugins/directives/directives.module.ts @@ -13,24 +13,24 @@ // limitations under the License. import { NgModule } from '@angular/core'; -import { CoreSiteAddonsCallWSDirective } from './call-ws'; -import { CoreSiteAddonsCallWSNewContentDirective } from './call-ws-new-content'; -import { CoreSiteAddonsCallWSOnLoadDirective } from './call-ws-on-load'; -import { CoreSiteAddonsNewContentDirective } from './new-content'; +import { CoreSitePluginsCallWSDirective } from './call-ws'; +import { CoreSitePluginsCallWSNewContentDirective } from './call-ws-new-content'; +import { CoreSitePluginsCallWSOnLoadDirective } from './call-ws-on-load'; +import { CoreSitePluginsNewContentDirective } from './new-content'; @NgModule({ declarations: [ - CoreSiteAddonsCallWSDirective, - CoreSiteAddonsCallWSNewContentDirective, - CoreSiteAddonsCallWSOnLoadDirective, - CoreSiteAddonsNewContentDirective + CoreSitePluginsCallWSDirective, + CoreSitePluginsCallWSNewContentDirective, + CoreSitePluginsCallWSOnLoadDirective, + CoreSitePluginsNewContentDirective ], imports: [], exports: [ - CoreSiteAddonsCallWSDirective, - CoreSiteAddonsCallWSNewContentDirective, - CoreSiteAddonsCallWSOnLoadDirective, - CoreSiteAddonsNewContentDirective + CoreSitePluginsCallWSDirective, + CoreSitePluginsCallWSNewContentDirective, + CoreSitePluginsCallWSOnLoadDirective, + CoreSitePluginsNewContentDirective ] }) -export class CoreSiteAddonsDirectivesModule {} +export class CoreSitePluginsDirectivesModule {} diff --git a/src/core/siteaddons/directives/new-content.ts b/src/core/siteplugins/directives/new-content.ts similarity index 72% rename from src/core/siteaddons/directives/new-content.ts rename to src/core/siteplugins/directives/new-content.ts index 6e11a17ac..7e9b38dd2 100644 --- a/src/core/siteaddons/directives/new-content.ts +++ b/src/core/siteplugins/directives/new-content.ts @@ -16,47 +16,47 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreSiteAddonsProvider } from '../providers/siteaddons'; -import { CoreSiteAddonsAddonContentComponent } from '../components/addon-content/addon-content'; +import { CoreSitePluginsProvider } from '../providers/siteplugins'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; /** - * Directive to display a new site addon content when clicked. This new content can be displayed in a new page or in the - * current page (only if the current page is already displaying a site addon content). + * Directive to display a new site plugin content when clicked. This new content can be displayed in a new page or in the + * current page (only if the current page is already displaying a site plugin content). * * Example usages: * * A button to go to a new content page: * - * * * A button to load new content in current page using a param from otherdata: * - * */ @Directive({ - selector: '[core-site-addons-new-content]' + selector: '[core-site-plugins-new-content]' }) -export class CoreSiteAddonsNewContentDirective implements OnInit { +export class CoreSitePluginsNewContentDirective implements OnInit { @Input() component: string; // The component of the new content. @Input() method: string; // The method to get the new content. @Input() args: any; // The params to get the new content. @Input() title: string; // The title to display with the new content. Only if samePage=false. @Input() samePage: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. - @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSiteAddonsProvider.loadOtherDataInArgs. + @Input() useOtherData: any[]; // Whether to include other data in the args. @see CoreSitePluginsProvider.loadOtherDataInArgs. @Input() form: string; // ID or name to identify a form. The form will be obtained from document.forms. // If supplied and form is found, the form data will be retrieved and sent to the new content. protected element: HTMLElement; constructor(element: ElementRef, protected utils: CoreUtilsProvider, @Optional() protected navCtrl: NavController, - @Optional() protected parentContent: CoreSiteAddonsAddonContentComponent, protected domUtils: CoreDomUtilsProvider, - protected siteAddonsProvider: CoreSiteAddonsProvider) { + @Optional() protected parentContent: CoreSitePluginsPluginContentComponent, protected domUtils: CoreDomUtilsProvider, + protected sitePluginsProvider: CoreSitePluginsProvider) { this.element = element.nativeElement || element; } @@ -71,7 +71,7 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { let args = this.args || {}; if (this.parentContent) { - args = this.siteAddonsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + args = this.sitePluginsProvider.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); } if (this.form && document.forms[this.form]) { @@ -84,7 +84,7 @@ export class CoreSiteAddonsNewContentDirective implements OnInit { this.parentContent.updateContent(this.component, this.method, args); } } else { - this.navCtrl.push('CoreSiteAddonsAddonPage', { + this.navCtrl.push('CoreSitePluginsPluginPage', { title: this.title, component: this.component, method: this.method, diff --git a/src/core/siteplugins/pages/module-index/module-index.html b/src/core/siteplugins/pages/module-index/module-index.html new file mode 100644 index 000000000..cf7a5dfad --- /dev/null +++ b/src/core/siteplugins/pages/module-index/module-index.html @@ -0,0 +1,15 @@ + + + {{ title }} + + + + + + + + + + + + diff --git a/src/core/siteaddons/pages/module-index/module-index.module.ts b/src/core/siteplugins/pages/module-index/module-index.module.ts similarity index 71% rename from src/core/siteaddons/pages/module-index/module-index.module.ts rename to src/core/siteplugins/pages/module-index/module-index.module.ts index 9ba33ae12..cd9e5a003 100644 --- a/src/core/siteaddons/pages/module-index/module-index.module.ts +++ b/src/core/siteplugins/pages/module-index/module-index.module.ts @@ -15,20 +15,20 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreSiteAddonsModuleIndexPage } from './module-index'; -import { CoreSiteAddonsComponentsModule } from '../../components/components.module'; +import { CoreSitePluginsModuleIndexPage } from './module-index'; +import { CoreSitePluginsComponentsModule } from '../../components/components.module'; /** * Module to lazy load the page. */ @NgModule({ declarations: [ - CoreSiteAddonsModuleIndexPage + CoreSitePluginsModuleIndexPage ], imports: [ - CoreSiteAddonsComponentsModule, - IonicPageModule.forChild(CoreSiteAddonsModuleIndexPage), + CoreSitePluginsComponentsModule, + IonicPageModule.forChild(CoreSitePluginsModuleIndexPage), TranslateModule.forChild() ] }) -export class CoreSiteAddonsAddonPageModule {} +export class CoreSitePluginsModuleIndexPageModule {} diff --git a/src/core/siteaddons/pages/module-index/module-index.ts b/src/core/siteplugins/pages/module-index/module-index.ts similarity index 75% rename from src/core/siteaddons/pages/module-index/module-index.ts rename to src/core/siteplugins/pages/module-index/module-index.ts index 264a023e3..de4050eb3 100644 --- a/src/core/siteaddons/pages/module-index/module-index.ts +++ b/src/core/siteplugins/pages/module-index/module-index.ts @@ -14,18 +14,18 @@ import { Component, ViewChild } from '@angular/core'; import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreSiteAddonsModuleIndexComponent } from '../../components/module-index/module-index'; +import { CoreSitePluginsModuleIndexComponent } from '../../components/module-index/module-index'; /** - * Page to render the index page of a module site addon. + * Page to render the index page of a module site plugin. */ -@IonicPage({ segment: 'core-site-addons-module-index-page' }) +@IonicPage({ segment: 'core-site-plugins-module-index-page' }) @Component({ - selector: 'page-core-site-addons-module-index', + selector: 'page-core-site-plugins-module-index', templateUrl: 'module-index.html', }) -export class CoreSiteAddonsModuleIndexPage { - @ViewChild(CoreSiteAddonsModuleIndexComponent) content: CoreSiteAddonsModuleIndexComponent; +export class CoreSitePluginsModuleIndexPage { + @ViewChild(CoreSitePluginsModuleIndexComponent) content: CoreSitePluginsModuleIndexComponent; title: string; // Page title. diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteplugins/pages/plugin-page/plugin-page.html similarity index 59% rename from src/core/siteaddons/pages/addon-page/addon-page.html rename to src/core/siteplugins/pages/plugin-page/plugin-page.html index c2a75a760..a7a553fbe 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.html +++ b/src/core/siteplugins/pages/plugin-page/plugin-page.html @@ -3,7 +3,7 @@ {{ title }} - + @@ -11,5 +11,5 @@ - + diff --git a/src/core/siteaddons/pages/addon-page/addon-page.module.ts b/src/core/siteplugins/pages/plugin-page/plugin-page.module.ts similarity index 72% rename from src/core/siteaddons/pages/addon-page/addon-page.module.ts rename to src/core/siteplugins/pages/plugin-page/plugin-page.module.ts index 053a147b4..396df5702 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.module.ts +++ b/src/core/siteplugins/pages/plugin-page/plugin-page.module.ts @@ -15,20 +15,20 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreSiteAddonsAddonPage } from './addon-page'; -import { CoreSiteAddonsComponentsModule } from '../../components/components.module'; +import { CoreSitePluginsPluginPage } from './plugin-page'; +import { CoreSitePluginsComponentsModule } from '../../components/components.module'; /** * Module to lazy load the page. */ @NgModule({ declarations: [ - CoreSiteAddonsAddonPage + CoreSitePluginsPluginPage ], imports: [ - CoreSiteAddonsComponentsModule, - IonicPageModule.forChild(CoreSiteAddonsAddonPage), + CoreSitePluginsComponentsModule, + IonicPageModule.forChild(CoreSitePluginsPluginPage), TranslateModule.forChild() ] }) -export class CoreSiteAddonsAddonPageModule {} +export class CoreSitePluginsPluginPageModule {} diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteplugins/pages/plugin-page/plugin-page.ts similarity index 76% rename from src/core/siteaddons/pages/addon-page/addon-page.ts rename to src/core/siteplugins/pages/plugin-page/plugin-page.ts index 714e50c74..38d516a47 100644 --- a/src/core/siteaddons/pages/addon-page/addon-page.ts +++ b/src/core/siteplugins/pages/plugin-page/plugin-page.ts @@ -14,18 +14,18 @@ import { Component, ViewChild } from '@angular/core'; import { IonicPage, NavParams } from 'ionic-angular'; -import { CoreSiteAddonsAddonContentComponent } from '../../components/addon-content/addon-content'; +import { CoreSitePluginsPluginContentComponent } from '../../components/plugin-content/plugin-content'; /** - * Page to render a site addon page. + * Page to render a site plugin page. */ -@IonicPage({ segment: 'core-site-addons-addon-page' }) +@IonicPage({ segment: 'core-site-plugins-plugin-page' }) @Component({ - selector: 'page-core-site-addons-addon', - templateUrl: 'addon-page.html', + selector: 'page-core-site-plugins-plugin', + templateUrl: 'plugin-page.html', }) -export class CoreSiteAddonsAddonPage { - @ViewChild(CoreSiteAddonsAddonContentComponent) content: CoreSiteAddonsAddonContentComponent; +export class CoreSitePluginsPluginPage { + @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; title: string; // Page title. diff --git a/src/core/siteaddons/providers/helper.ts b/src/core/siteplugins/providers/helper.ts similarity index 54% rename from src/core/siteaddons/providers/helper.ts rename to src/core/siteplugins/providers/helper.ts index 099864fe0..7ab692c38 100644 --- a/src/core/siteaddons/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -19,7 +19,7 @@ import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreSite } from '../../../classes/site'; import { CoreSitesProvider } from '../../../providers/sites'; import { CoreUtilsProvider } from '../../../providers/utils/utils'; -import { CoreSiteAddonsProvider } from './siteaddons'; +import { CoreSitePluginsProvider } from './siteplugins'; import { CoreCompileProvider } from '../../compile/providers/compile'; // Delegates @@ -32,55 +32,55 @@ import { CoreUserDelegate } from '../../user/providers/user-delegate'; import { CoreUserProfileFieldDelegate } from '../../user/providers/user-profile-field-delegate'; // Handler classes. -import { CoreSiteAddonsCourseFormatHandler } from '../classes/course-format-handler'; -import { CoreSiteAddonsCourseOptionHandler } from '../classes/course-option-handler'; -import { CoreSiteAddonsModuleHandler } from '../classes/module-handler'; -import { CoreSiteAddonsModulePrefetchHandler } from '../classes/module-prefetch-handler'; -import { CoreSiteAddonsMainMenuHandler } from '../classes/main-menu-handler'; -import { CoreSiteAddonsUserProfileHandler } from '../classes/user-handler'; -import { CoreSiteAddonsUserProfileFieldHandler } from '../classes/user-profile-field-handler'; +import { CoreSitePluginsCourseFormatHandler } from '../classes/course-format-handler'; +import { CoreSitePluginsCourseOptionHandler } from '../classes/course-option-handler'; +import { CoreSitePluginsModuleHandler } from '../classes/module-handler'; +import { CoreSitePluginsModulePrefetchHandler } from '../classes/module-prefetch-handler'; +import { CoreSitePluginsMainMenuHandler } from '../classes/main-menu-handler'; +import { CoreSitePluginsUserProfileHandler } from '../classes/user-handler'; +import { CoreSitePluginsUserProfileFieldHandler } from '../classes/user-profile-field-handler'; /** - * Helper service to provide functionalities regarding site addons. It basically has the features to load and register site - * addons. + * Helper service to provide functionalities regarding site plugins. It basically has the features to load and register site + * plugin. * - * This code is split from CoreSiteAddonsProvider to prevent circular dependencies. + * This code is split from CoreSitePluginsProvider to prevent circular dependencies. * - * @todo: Support ViewChild and similar in site addons. Possible solution: make components and directives inject the instance + * @todo: Support ViewChild and similar in site plugins. Possible solution: make components and directives inject the instance * inside the host DOM element? */ @Injectable() -export class CoreSiteAddonsHelperProvider { +export class CoreSitePluginsHelperProvider { protected logger; - protected hasSiteAddonsLoaded = false; + protected hasSitePluginsLoaded = false; 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 sitePluginsProvider: CoreSitePluginsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, eventsProvider: CoreEventsProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate) { - this.logger = logger.getInstance('CoreSiteAddonsHelperProvider'); + this.logger = logger.getInstance('CoreSitePluginsHelperProvider'); - // Fetch the addons on login. + // Fetch the plugins on login. eventsProvider.on(CoreEventsProvider.LOGIN, () => { const siteId = this.sitesProvider.getCurrentSiteId(); - this.fetchSiteAddons(siteId).then((addons) => { - // 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).then(() => { - eventsProvider.trigger(CoreEventsProvider.SITE_ADDONS_LOADED, {}, siteId); + this.fetchSitePlugins(siteId).then((plugins) => { + // Plugins fetched, check that site hasn't changed. + if (siteId == this.sitesProvider.getCurrentSiteId() && plugins.length) { + // Site is still the same. Load the plugins and trigger the event. + this.loadSitePlugins(plugins).then(() => { + eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, siteId); }); } }); }); - // Unload addons on logout if any. + // Unload plugins on logout if any. eventsProvider.on(CoreEventsProvider.LOGOUT, () => { - if (this.hasSiteAddonsLoaded) { + if (this.hasSitePluginsLoaded) { // Temporary fix. Reload the page to unload all plugins. window.location.reload(); } @@ -90,32 +90,32 @@ export class CoreSiteAddonsHelperProvider { /** * Bootstrap a handler if it has some bootstrap method. * - * @param {any} addon Data of the addon. + * @param {any} plugin Data of the plugin. * @param {any} handlerSchema Data about the handler. * @return {Promise} Promise resolved when done. It returns the results of the getContent call and the data returned by * the bootstrap JS (if any). */ - protected bootstrapHandler(addon: any, handlerSchema: any): Promise { + protected bootstrapHandler(plugin: any, handlerSchema: any): Promise { if (!handlerSchema.bootstrap) { return Promise.resolve({}); } - return this.executeMethodAndJS(addon, handlerSchema.bootstrap); + return this.executeMethodAndJS(plugin, handlerSchema.bootstrap); } /** * Execute a get_content method and run its javascript (if any). * - * @param {any} addon Data of the addon. + * @param {any} plugin Data of the plugin. * @param {string} method The method to call. * @return {Promise} Promise resolved when done. It returns the results of the getContent call and the data returned by * the JS (if any). */ - protected executeMethodAndJS(addon: any, method: string): Promise { + protected executeMethodAndJS(plugin: any, method: string): Promise { const siteId = this.sitesProvider.getCurrentSiteId(), preSets = {getFromCache: false}; // Try to ignore cache. - return this.siteAddonsProvider.getContent(addon.component, method, {}, preSets).then((result) => { + return this.sitePluginsProvider.getContent(plugin.component, method, {}, preSets).then((result) => { if (!result.javascript || this.sitesProvider.getCurrentSiteId() != siteId) { // No javascript or site has changed, stop. return result; @@ -126,7 +126,7 @@ export class CoreSiteAddonsHelperProvider { this.compileProvider.injectLibraries(instance); // Add some data of the WS call result. - const jsData = this.siteAddonsProvider.createDataForJS(result); + const jsData = this.sitePluginsProvider.createDataForJS(result); for (const name in jsData) { instance[name] = jsData[name]; } @@ -139,30 +139,30 @@ export class CoreSiteAddonsHelperProvider { } /** - * Fetch site addons. + * Fetch site plugins. * * @param {string} [siteId] Site ID. If not defined, current site. - * @return {Promise} Promise resolved when done. Returns the list of addons to load. + * @return {Promise} Promise resolved when done. Returns the list of plugins to load. */ - fetchSiteAddons(siteId?: string): Promise { - const addons = []; + fetchSitePlugins(siteId?: string): Promise { + const plugins = []; return this.sitesProvider.getSite(siteId).then((site) => { - if (!this.siteAddonsProvider.isGetContentAvailable(site)) { - // Cannot load site addons, so there's no point to fetch them. - return addons; + if (!this.sitePluginsProvider.isGetContentAvailable(site)) { + // Cannot load site plugins, so there's no point to fetch them. + return plugins; } - // Get the list of addons. Try not to use cache. + // Get the list of plugins. Try not to use cache. return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => { - data.plugins.forEach((addon: any) => { - // Check if it's a site addon and it's enabled. - if (this.isSiteAddonEnabled(addon, site)) { - addons.push(addon); + data.plugins.forEach((plugin: any) => { + // Check if it's a site plugin and it's enabled. + if (this.isSitePluginEnabled(plugin, site)) { + plugins.push(plugin); } }); - return addons; + return plugins; }); }); } @@ -175,7 +175,7 @@ export class CoreSiteAddonsHelperProvider { */ protected getHandlerPrefixForStrings(handlerName: string): string { if (handlerName) { - return 'addon.' + handlerName + '.'; + return 'plugin.' + handlerName + '.'; } return ''; @@ -193,23 +193,23 @@ export class CoreSiteAddonsHelperProvider { } /** - * Check if a certain addon is a site addon and it's enabled in a certain site. + * Check if a certain plugin is a site plugin and it's enabled in a certain site. * - * @param {any} addon Data of the addon. + * @param {any} plugin Data of the plugin. * @param {CoreSite} site Site affected. - * @return {boolean} Whether it's a site addon and it's enabled. + * @return {boolean} Whether it's a site plugin and it's enabled. */ - isSiteAddonEnabled(addon: any, site: CoreSite): boolean { - if (!site.isFeatureDisabled('siteAddOn_' + addon.component + '_' + addon.addon) && addon.handlers) { - // Site addon not disabled. Check if it has handlers. + isSitePluginEnabled(plugin: any, site: CoreSite): boolean { + if (!site.isFeatureDisabled('sitePlugin_' + plugin.component + '_' + plugin.addon) && plugin.handlers) { + // Site plugin not disabled. Check if it has handlers. try { - if (!addon.parsedHandlers) { - addon.parsedHandlers = JSON.parse(addon.handlers); + if (!plugin.parsedHandlers) { + plugin.parsedHandlers = JSON.parse(plugin.handlers); } - return !!(addon.parsedHandlers && Object.keys(addon.parsedHandlers).length); + return !!(plugin.parsedHandlers && Object.keys(plugin.parsedHandlers).length); } catch (ex) { - this.logger.warn('Error parsing site addon', ex); + this.logger.warn('Error parsing site plugin', ex); } } @@ -219,105 +219,105 @@ export class CoreSiteAddonsHelperProvider { /** * Load the lang strings for a handler. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. */ - loadHandlerLangStrings(addon: any, handlerName: string, handlerSchema: any): void { + loadHandlerLangStrings(plugin: any, handlerName: string, handlerSchema: any): void { if (!handlerSchema.lang) { return; } for (const lang in handlerSchema.lang) { - const prefix = this.getHandlerPrefixForStrings(this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName)); + const prefix = this.getHandlerPrefixForStrings(this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName)); - this.langProvider.addSiteAddonsStrings(lang, handlerSchema.lang[lang], prefix); + this.langProvider.addSitePluginsStrings(lang, handlerSchema.lang[lang], prefix); } } /** - * Load a site addon. + * Load a site plugin. * - * @param {any} addon Data of the addon. + * @param {any} plugin Data of the plugin. * @return {Promise} Promise resolved when loaded. */ - loadSiteAddon(addon: any): Promise { + loadSitePlugin(plugin: any): Promise { const promises = []; - this.logger.debug('Load site addon:', addon); + this.logger.debug('Load site plugin:', plugin); try { - if (!addon.parsedHandlers) { - addon.parsedHandlers = JSON.parse(addon.handlers); + if (!plugin.parsedHandlers) { + plugin.parsedHandlers = JSON.parse(plugin.handlers); } - this.hasSiteAddonsLoaded = true; + this.hasSitePluginsLoaded = true; // Register all the handlers. - for (const name in addon.parsedHandlers) { - promises.push(this.registerHandler(addon, name, addon.parsedHandlers[name])); + for (const name in plugin.parsedHandlers) { + promises.push(this.registerHandler(plugin, name, plugin.parsedHandlers[name])); } } catch (ex) { - this.logger.warn('Error parsing site addon', ex); + this.logger.warn('Error parsing site plugin', ex); } return this.utils.allPromises(promises); } /** - * Load site addons. + * Load site plugins. * - * @param {any[]} addons The addons to load. + * @param {any[]} plugins The plugins to load. * @return {Promise} Promise resolved when loaded. */ - loadSiteAddons(addons: any[]): Promise { + loadSitePlugins(plugins: any[]): Promise { const promises = []; - addons.forEach((addon) => { - promises.push(this.loadSiteAddon(addon)); + plugins.forEach((plugin) => { + promises.push(this.loadSitePlugin(plugin)); }); return this.utils.allPromises(promises); } /** - * Register a site addon handler in the right delegate. + * Register a site plugin handler in the right delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @return {Promise} Promise resolved when done. */ - registerHandler(addon: any, handlerName: string, handlerSchema: any): Promise { - this.loadHandlerLangStrings(addon, handlerName, handlerSchema); + registerHandler(plugin: any, handlerName: string, handlerSchema: any): Promise { + this.loadHandlerLangStrings(plugin, handlerName, handlerSchema); // Wait for the bootstrap JS to be executed. - return this.bootstrapHandler(addon, handlerSchema).then((result) => { + return this.bootstrapHandler(plugin, handlerSchema).then((result) => { let promise; switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': - promise = Promise.resolve(this.registerMainMenuHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerMainMenuHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseModuleDelegate': - promise = Promise.resolve(this.registerModuleHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerModuleHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreUserDelegate': - promise = Promise.resolve(this.registerUserProfileHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerUserProfileHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseOptionsDelegate': - promise = Promise.resolve(this.registerCourseOptionHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerCourseOptionHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseFormatDelegate': - promise = Promise.resolve(this.registerCourseFormatHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerCourseFormatHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreUserProfileFieldDelegate': - promise = Promise.resolve(this.registerUserProfileFieldHandler(addon, handlerName, handlerSchema, result)); + promise = Promise.resolve(this.registerUserProfileFieldHandler(plugin, handlerName, handlerSchema, result)); break; default: @@ -328,8 +328,8 @@ export class CoreSiteAddonsHelperProvider { return promise.then((uniqueName) => { if (uniqueName) { // Store the handler data. - this.siteAddonsProvider.setSiteAddonHandler(uniqueName, { - addon: addon, + this.sitePluginsProvider.setSitePluginHandler(uniqueName, { + plugin: plugin, handlerName: handlerName, handlerSchema: handlerSchema, bootstrapResult: result @@ -342,169 +342,169 @@ export class CoreSiteAddonsHelperProvider { } /** - * Given a handler in an addon, register it in the course format delegate. + * Given a handler in a plugin, register it in the course format delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerCourseFormatHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { - this.logger.debug('Register site addon in course format delegate:', addon, handlerSchema, bootstrapResult); + protected registerCourseFormatHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { + this.logger.debug('Register site plugin in course format delegate:', plugin, handlerSchema, bootstrapResult); // Create and register the handler. - const formatName = addon.component.replace('format_', ''); - this.courseFormatDelegate.registerHandler(new CoreSiteAddonsCourseFormatHandler(formatName, handlerSchema)); + const formatName = plugin.component.replace('format_', ''); + this.courseFormatDelegate.registerHandler(new CoreSitePluginsCourseFormatHandler(formatName, handlerSchema)); return formatName; } /** - * Given a handler in an addon, register it in the course options delegate. + * Given a handler in an plugin, register it in the course options delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerCourseOptionHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { + protected registerCourseOptionHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. - this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } - this.logger.debug('Register site addon in course option delegate:', addon, handlerSchema, bootstrapResult); + this.logger.debug('Register site plugin in course option delegate:', plugin, handlerSchema, bootstrapResult); // Create and register the handler. - const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); - this.courseOptionsDelegate.registerHandler(new CoreSiteAddonsCourseOptionHandler(uniqueName, prefixedTitle, addon, - handlerSchema, bootstrapResult, this.siteAddonsProvider)); + this.courseOptionsDelegate.registerHandler(new CoreSitePluginsCourseOptionHandler(uniqueName, prefixedTitle, plugin, + handlerSchema, bootstrapResult, this.sitePluginsProvider)); return uniqueName; } /** - * Given a handler in an addon, register it in the main menu delegate. + * Given a handler in an plugin, register it in the main menu delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerMainMenuHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { + protected registerMainMenuHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. - this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } - this.logger.debug('Register site addon in main menu delegate:', addon, handlerSchema, bootstrapResult); + this.logger.debug('Register site plugin in main menu delegate:', plugin, handlerSchema, bootstrapResult); // Create and register the handler. - const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); this.mainMenuDelegate.registerHandler( - new CoreSiteAddonsMainMenuHandler(uniqueName, prefixedTitle, addon, handlerSchema, bootstrapResult)); + new CoreSitePluginsMainMenuHandler(uniqueName, prefixedTitle, plugin, handlerSchema, bootstrapResult)); return uniqueName; } /** - * Given a handler in an addon, register it in the module delegate. + * Given a handler in an plugin, register it in the module delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerModuleHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { + protected registerModuleHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. - this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } - this.logger.debug('Register site addon in module delegate:', addon, handlerSchema, bootstrapResult); + this.logger.debug('Register site plugin in module delegate:', plugin, handlerSchema, bootstrapResult); // Create and register the handler. - const modName = addon.component.replace('mod_', ''); + const modName = plugin.component.replace('mod_', ''); - this.moduleDelegate.registerHandler(new CoreSiteAddonsModuleHandler(modName, handlerSchema)); + this.moduleDelegate.registerHandler(new CoreSitePluginsModuleHandler(modName, handlerSchema)); if (handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length) { // Register the prefetch handler. - this.prefetchDelegate.registerHandler(new CoreSiteAddonsModulePrefetchHandler( - this.injector, this.siteAddonsProvider, addon.component, modName, handlerSchema)); + this.prefetchDelegate.registerHandler(new CoreSitePluginsModulePrefetchHandler( + this.injector, this.sitePluginsProvider, plugin.component, modName, handlerSchema)); } return modName; } /** - * Given a handler in an addon, register it in the user profile delegate. + * Given a handler in an plugin, register it in the user profile delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string} A string to identify the handler. */ - protected registerUserProfileHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { + protected registerUserProfileHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. - this.logger.warn('Ignore site addon because it doesn\'t provide displaydata', addon, handlerSchema); + this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } - this.logger.debug('Register site addon in user profile delegate:', addon, handlerSchema, bootstrapResult); + this.logger.debug('Register site plugin in user profile delegate:', plugin, handlerSchema, bootstrapResult); // Create and register the handler. - const uniqueName = this.siteAddonsProvider.getHandlerUniqueName(addon, handlerName), + const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); - this.userDelegate.registerHandler(new CoreSiteAddonsUserProfileHandler(uniqueName, prefixedTitle, addon, handlerSchema, - bootstrapResult, this.siteAddonsProvider)); + this.userDelegate.registerHandler(new CoreSitePluginsUserProfileHandler(uniqueName, prefixedTitle, plugin, handlerSchema, + bootstrapResult, this.sitePluginsProvider)); return uniqueName; } /** - * Given a handler in an addon, register it in the user profile field delegate. + * Given a handler in an plugin, register it in the user profile field delegate. * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler in the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler in the plugin. * @param {any} handlerSchema Data about the handler. * @param {any} bootstrapResult Result of the bootstrap WS call. * @return {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ - protected registerUserProfileFieldHandler(addon: any, handlerName: string, handlerSchema: any, bootstrapResult: any) + protected registerUserProfileFieldHandler(plugin: any, handlerName: string, handlerSchema: any, bootstrapResult: any) : string | Promise { if (!handlerSchema.method) { // Required data not provided, stop. - this.logger.warn('Ignore site addon because it doesn\'t provide method', addon, handlerSchema); + this.logger.warn('Ignore site plugin because it doesn\'t provide method', plugin, handlerSchema); return; } - this.logger.debug('Register site addon in user profile field delegate:', addon, handlerSchema, bootstrapResult); + this.logger.debug('Register site plugin in user profile field delegate:', plugin, handlerSchema, bootstrapResult); // Execute the main method and its JS. The template returned will be used in the profile field component. - return this.executeMethodAndJS(addon, handlerSchema.method).then((result) => { + return this.executeMethodAndJS(plugin, handlerSchema.method).then((result) => { // Create and register the handler. - const fieldType = addon.component.replace('profilefield_', ''), - fieldHandler = new CoreSiteAddonsUserProfileFieldHandler(fieldType); + const fieldType = plugin.component.replace('profilefield_', ''), + fieldHandler = new CoreSitePluginsUserProfileFieldHandler(fieldType); // Store in handlerSchema some data required by the component. handlerSchema.methodTemplates = result.templates; diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteplugins/providers/siteplugins.ts similarity index 94% rename from src/core/siteaddons/providers/siteaddons.ts rename to src/core/siteplugins/providers/siteplugins.ts index 6103c3b09..fc868077e 100644 --- a/src/core/siteaddons/providers/siteaddons.ts +++ b/src/core/siteplugins/providers/siteplugins.ts @@ -25,14 +25,14 @@ import { CoreConfigConstants } from '../../../configconstants'; import { CoreCoursesProvider } from '../../courses/providers/courses'; /** - * Handler of a site addon. + * Handler of a site plugin. */ -export interface CoreSiteAddonsHandler { +export interface CoreSitePluginsHandler { /** - * The site addon data. + * The site plugin data. * @type {any} */ - addon: any; + plugin: any; /** * Name of the handler. @@ -54,14 +54,14 @@ export interface CoreSiteAddonsHandler { } /** - * Service to provide functionalities regarding site addons. + * Service to provide functionalities regarding site plugins. */ @Injectable() -export class CoreSiteAddonsProvider { - protected ROOT_CACHE_KEY = 'CoreSiteAddons:'; +export class CoreSitePluginsProvider { + protected ROOT_CACHE_KEY = 'CoreSitePlugins:'; protected logger; - protected siteAddons: {[name: string]: CoreSiteAddonsHandler} = {}; // Site addons registered. + protected sitePlugins: {[name: string]: CoreSitePluginsHandler} = {}; // Site plugins registered. constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform, @@ -118,7 +118,7 @@ export class CoreSiteAddonsProvider { } /** - * Call a WS for a site addon. + * Call a WS for a site plugin. * * @param {string} method WS method to use. * @param {any} data Data to send to the WS. @@ -185,7 +185,7 @@ export class CoreSiteAddonsProvider { } /** - * Get a certain content for a site addon. + * Get a certain content for a site plugin. * * @param {string} component Component where the class is. E.g. mod_assign. * @param {string} method Method to execute in the class. @@ -265,24 +265,24 @@ export class CoreSiteAddonsProvider { } /** - * Get the unique name of a handler (addon + handler). + * Get the unique name of a handler (plugin + handler). * - * @param {any} addon Data of the addon. - * @param {string} handlerName Name of the handler inside the addon. + * @param {any} plugin Data of the plugin. + * @param {string} handlerName Name of the handler inside the plugin. * @return {string} Unique name. */ - getHandlerUniqueName(addon: any, handlerName: string): string { - return addon.addon + '_' + handlerName; + getHandlerUniqueName(plugin: any, handlerName: string): string { + return plugin.addon + '_' + handlerName; } /** - * Get a site addon handler. + * Get a site plugin handler. * * @param {string} name Unique name of the handler. - * @return {CoreSiteAddonsHandler} Handler. + * @return {CoreSitePluginsHandler} Handler. */ - getSiteAddonHandler(name: string): CoreSiteAddonsHandler { - return this.siteAddons[name]; + getSitePluginHandler(name: string): CoreSitePluginsHandler { + return this.sitePlugins[name]; } /** @@ -426,7 +426,7 @@ export class CoreSiteAddonsProvider { } /** - * Prefetch offline functions for a site addon handler. + * Prefetch offline functions for a site plugin handler. * * @param {string} component The component of the handler. * @param {any} args Params to send to the get_content calls. @@ -491,12 +491,12 @@ export class CoreSiteAddonsProvider { } /** - * Store a site addon handler. + * Store a site plugin handler. * * @param {string} name A unique name to identify the handler. - * @param {CoreSiteAddonsHandler} handler Handler to set. + * @param {CoreSitePluginsHandler} handler Handler to set. */ - setSiteAddonHandler(name: string, handler: CoreSiteAddonsHandler): void { - this.siteAddons[name] = handler; + setSitePluginHandler(name: string, handler: CoreSitePluginsHandler): void { + this.sitePlugins[name] = handler; } } diff --git a/src/core/siteaddons/siteaddons.module.ts b/src/core/siteplugins/siteplugins.module.ts similarity index 62% rename from src/core/siteaddons/siteaddons.module.ts rename to src/core/siteplugins/siteplugins.module.ts index 35493a853..f71eac427 100644 --- a/src/core/siteaddons/siteaddons.module.ts +++ b/src/core/siteplugins/siteplugins.module.ts @@ -13,26 +13,26 @@ // limitations under the License. import { NgModule } from '@angular/core'; -import { CoreSiteAddonsProvider } from './providers/siteaddons'; -import { CoreSiteAddonsHelperProvider } from './providers/helper'; -import { CoreSiteAddonsComponentsModule } from './components/components.module'; +import { CoreSitePluginsProvider } from './providers/siteplugins'; +import { CoreSitePluginsHelperProvider } from './providers/helper'; +import { CoreSitePluginsComponentsModule } from './components/components.module'; // List of providers. -export const CORE_SITEADDONS_PROVIDERS = [ - CoreSiteAddonsProvider, - CoreSiteAddonsHelperProvider +export const CORE_SITEPLUGINS_PROVIDERS = [ + CoreSitePluginsProvider, + CoreSitePluginsHelperProvider ]; @NgModule({ declarations: [ ], imports: [ - CoreSiteAddonsComponentsModule + CoreSitePluginsComponentsModule ], - providers: CORE_SITEADDONS_PROVIDERS + providers: CORE_SITEPLUGINS_PROVIDERS }) -export class CoreSiteAddonsModule { - constructor(helper: CoreSiteAddonsHelperProvider) { +export class CoreSitePluginsModule { + constructor(helper: CoreSitePluginsHelperProvider) { // Inject the helper even if it isn't used so it's instantiated. } } diff --git a/src/core/user/providers/participants-link-handler.ts b/src/core/user/providers/participants-link-handler.ts index 2d6f1a4b7..4d4f93918 100644 --- a/src/core/user/providers/participants-link-handler.ts +++ b/src/core/user/providers/participants-link-handler.ts @@ -47,7 +47,7 @@ export class CoreUserParticipantsLinkHandler extends CoreContentLinksHandlerBase return [{ action: (siteId, navCtrl?): void => { // Always use redirect to make it the new history root (to avoid "loops" in history). - this.loginHelper.redirect('AddonParticipantsListPage', {courseId: courseId}, siteId); + this.loginHelper.redirect('CoreUserParticipantsPage', {courseId: courseId}, siteId); } }]; } diff --git a/src/core/user/providers/user.ts b/src/core/user/providers/user.ts index 052caa938..128f9fc93 100644 --- a/src/core/user/providers/user.ts +++ b/src/core/user/providers/user.ts @@ -310,7 +310,7 @@ export class CoreUserProvider { } /** - * Returns whether or not the participants addon is enabled for a certain course. + * Returns whether or not participants is enabled for a certain course. * * @param {number} courseId Course ID. * @param {string} [siteId] Site Id. If not defined, use current site. diff --git a/src/providers/events.ts b/src/providers/events.ts index 2dff17b08..c9309ac9e 100644 --- a/src/providers/events.ts +++ b/src/providers/events.ts @@ -47,7 +47,7 @@ export class CoreEventsProvider { static PACKAGE_STATUS_CHANGED = 'package_status_changed'; static COURSE_STATUS_CHANGED = 'course_status_changed'; static SECTION_STATUS_CHANGED = 'section_status_changed'; - static SITE_ADDONS_LOADED = 'site_addons_loaded'; + static SITE_PLUGINS_LOADED = 'site_plugins_loaded'; static LOGIN_SITE_CHECKED = 'login_site_checked'; static LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; static IAB_LOAD_START = 'inappbrowser_load_start'; diff --git a/src/providers/init.ts b/src/providers/init.ts index ddb55faf1..ff27eb733 100644 --- a/src/providers/init.ts +++ b/src/providers/init.ts @@ -161,7 +161,7 @@ export class CoreInitDelegate { * * An init process should never change state or prompt user interaction. * - * This delegate cannot be used in remote addons. + * This delegate cannot be used by site plugins. * * @param {CoreInitHandler} instance The instance of the handler. */ diff --git a/src/providers/lang.ts b/src/providers/lang.ts index 71fc34ca0..a83aa8668 100644 --- a/src/providers/lang.ts +++ b/src/providers/lang.ts @@ -30,7 +30,7 @@ export class CoreLangProvider { protected currentLanguage: string; // Save current language in a variable to speed up the get function. protected customStrings = {}; // Strings defined using the admin tool. protected customStringsRaw: string; - protected siteAddonsStrings = {}; // Strings defined by site addons. + protected sitePluginsStrings = {}; // Strings defined by site plugins. constructor(private translate: TranslateService, private configProvider: CoreConfigProvider, platform: Platform, private globalization: Globalization) { @@ -47,16 +47,16 @@ export class CoreLangProvider { } /** - * Add a set of site addons strings for a certain language. + * Add a set of site plugins strings for a certain language. * * @param {string} lang The language where to add the strings. * @param {any} strings Object with the strings to add. * @param {string} [prefix] A prefix to add to all keys. */ - addSiteAddonsStrings(lang: string, strings: any, prefix?: string): void { + addSitePluginsStrings(lang: string, strings: any, prefix?: string): void { // Initialize structures if they don't exist. - if (!this.siteAddonsStrings[lang]) { - this.siteAddonsStrings[lang] = {}; + if (!this.sitePluginsStrings[lang]) { + this.sitePluginsStrings[lang] = {}; } if (!this.translate.translations[lang]) { this.translate.translations[lang] = {}; @@ -71,15 +71,15 @@ export class CoreLangProvider { continue; } - if (!this.siteAddonsStrings[lang][prefixedKey]) { - // It's a new site addon string. Store the original value. - this.siteAddonsStrings[lang][prefixedKey] = { + if (!this.sitePluginsStrings[lang][prefixedKey]) { + // It's a new site plugin string. Store the original value. + this.sitePluginsStrings[lang][prefixedKey] = { original: this.translate.translations[lang][prefixedKey], value: value }; } else { - // Site addon string already defined. Store the new value. - this.siteAddonsStrings[lang][prefixedKey].value = value; + // Site plugin string already defined. Store the new value. + this.sitePluginsStrings[lang][prefixedKey].value = value; } // Store the string in the translations table. @@ -115,11 +115,11 @@ export class CoreLangProvider { } /** - * Clear current site addons strings. + * Clear current site plugins strings. */ - clearSiteAddonsStrings(): void { - this.unloadStrings(this.siteAddonsStrings); - this.siteAddonsStrings = {}; + clearSitePluginsStrings(): void { + this.unloadStrings(this.sitePluginsStrings); + this.sitePluginsStrings = {}; } /** @@ -132,12 +132,12 @@ export class CoreLangProvider { } /** - * Get all current site addons strings. + * Get all current site plugins strings. * - * @return {any} Site addons strings. + * @return {any} Site plugins strings. */ - getAllSiteAddonsStrings(): any { - return this.siteAddonsStrings; + getAllSitePluginsStrings(): any { + return this.sitePluginsStrings; } /** @@ -235,7 +235,7 @@ export class CoreLangProvider { } /** - * Unload custom or site addon strings, removing the to the translations table. + * Unload custom or site plugin strings, removing them from the translations table. * * @param {any} strings Strings to unload. */ From db3fb845472f9ec71b01b91053c79115ce781392 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 9 Mar 2018 14:52:55 +0100 Subject: [PATCH 28/32] MOBILE-2333 redirect: Check if there are site plugins loaded --- src/core/contentlinks/providers/helper.ts | 14 ++++++++------ src/core/login/providers/helper.ts | 18 ++++++++++++------ src/core/siteplugins/providers/helper.ts | 5 ++--- src/core/siteplugins/providers/siteplugins.ts | 1 + 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/core/contentlinks/providers/helper.ts b/src/core/contentlinks/providers/helper.ts index c160a86b1..1ee1ea864 100644 --- a/src/core/contentlinks/providers/helper.ts +++ b/src/core/contentlinks/providers/helper.ts @@ -27,6 +27,7 @@ import { CoreLoginHelperProvider } from '../../login/providers/helper'; import { CoreContentLinksDelegate, CoreContentLinksAction } from './delegate'; import { CoreConstants } from '../../constants'; import { CoreConfigConstants } from '../../../configconstants'; +import { CoreSitePluginsProvider } from '../../siteplugins/providers/siteplugins'; /** * Service that provides some features regarding content links. @@ -38,7 +39,8 @@ export class CoreContentLinksHelperProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, private contentLinksDelegate: CoreContentLinksDelegate, private appProvider: CoreAppProvider, private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private translate: TranslateService, - private initDelegate: CoreInitDelegate, eventsProvider: CoreEventsProvider, private textUtils: CoreTextUtilsProvider) { + private initDelegate: CoreInitDelegate, eventsProvider: CoreEventsProvider, private textUtils: CoreTextUtilsProvider, + private sitePluginsProvider: CoreSitePluginsProvider) { this.logger = logger.getInstance('CoreContentLinksHelperProvider'); // Listen for app launched URLs. If we receive one, check if it's a content link. @@ -144,7 +146,6 @@ export class CoreContentLinksHelperProvider { return this.sitesProvider.checkSite(siteUrl).then((result) => { // Site exists. We'll allow to add it. const ssoNeeded = this.loginHelper.isSSOLoginNeeded(result.code), - hasRemoteAddonsLoaded = false, pageName = 'CoreLoginCredentialsPage', pageParams = { siteUrl: result.siteUrl, @@ -152,7 +153,8 @@ export class CoreContentLinksHelperProvider { urlToOpen: url, siteConfig: result.config }; - let promise; + let promise, + hasSitePluginsLoaded = false; modal.dismiss(); // Dismiss modal so it doesn't collide with confirms. @@ -164,8 +166,8 @@ export class CoreContentLinksHelperProvider { const confirmMsg = this.translate.instant('core.contentlinks.confirmurlothersite'); promise = this.domUtils.showConfirm(confirmMsg).then(() => { if (!ssoNeeded) { - // @todo hasRemoteAddonsLoaded = $mmAddonManager.hasRemoteAddonsLoaded(); @todo - if (hasRemoteAddonsLoaded) { + hasSitePluginsLoaded = this.sitePluginsProvider.hasSitePluginsLoaded; + if (hasSitePluginsLoaded) { // Store the redirect since logout will restart the app. this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams); } @@ -181,7 +183,7 @@ export class CoreContentLinksHelperProvider { if (ssoNeeded) { this.loginHelper.confirmAndOpenBrowserForSSOLogin( result.siteUrl, result.code, result.service, result.config && result.config.launchurl); - } else if (!hasRemoteAddonsLoaded) { + } else if (!hasSitePluginsLoaded) { this.appProvider.getRootNavController().setRoot(pageName, pageParams); } }); diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts index d75c7f3e5..41443cce2 100644 --- a/src/core/login/providers/helper.ts +++ b/src/core/login/providers/helper.ts @@ -26,8 +26,9 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUrlUtilsProvider } from '@providers/utils/url'; import { CoreUtilsProvider } from '@providers/utils/utils'; +import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; import { CoreConfigConstants } from '../../../configconstants'; -import { CoreConstants } from '../../constants'; +import { CoreConstants } from '@core/constants'; import { Md5 } from 'ts-md5/dist/md5'; /** @@ -80,7 +81,7 @@ export class CoreLoginHelperProvider { private wsProvider: CoreWSProvider, private translate: TranslateService, private textUtils: CoreTextUtilsProvider, private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private configProvider: CoreConfigProvider, private platform: Platform, - private initDelegate: CoreInitDelegate) { + private initDelegate: CoreInitDelegate, private sitePluginsProvider: CoreSitePluginsProvider) { this.logger = logger.getInstance('CoreLoginHelper'); } @@ -768,10 +769,15 @@ export class CoreLoginHelperProvider { if (this.sitesProvider.isLoggedIn()) { if (siteId && siteId != this.sitesProvider.getCurrentSiteId()) { // Target page belongs to a different site. Change site. - // @todo Store redirect once we have addon manager. - this.sitesProvider.logout().then(() => { - this.loadSiteAndPage(page, params, siteId); - }); + if (this.sitePluginsProvider.hasSitePluginsLoaded) { + // The site has site plugins so the app will be restarted. Store the data and logout. + this.appProvider.storeRedirect(siteId, page, params); + this.sitesProvider.logout(); + } else { + this.sitesProvider.logout().then(() => { + this.loadSiteAndPage(page, params, siteId); + }); + } } else { this.loadPageInMainMenu(page, params); } diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 7ab692c38..8ecdac53a 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -52,7 +52,6 @@ import { CoreSitePluginsUserProfileFieldHandler } from '../classes/user-profile- @Injectable() export class CoreSitePluginsHelperProvider { protected logger; - protected hasSitePluginsLoaded = false; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private injector: Injector, private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, @@ -80,7 +79,7 @@ export class CoreSitePluginsHelperProvider { // Unload plugins on logout if any. eventsProvider.on(CoreEventsProvider.LOGOUT, () => { - if (this.hasSitePluginsLoaded) { + if (this.sitePluginsProvider.hasSitePluginsLoaded) { // Temporary fix. Reload the page to unload all plugins. window.location.reload(); } @@ -251,7 +250,7 @@ export class CoreSitePluginsHelperProvider { plugin.parsedHandlers = JSON.parse(plugin.handlers); } - this.hasSitePluginsLoaded = true; + this.sitePluginsProvider.hasSitePluginsLoaded = true; // Register all the handlers. for (const name in plugin.parsedHandlers) { diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts index fc868077e..3ca664d7c 100644 --- a/src/core/siteplugins/providers/siteplugins.ts +++ b/src/core/siteplugins/providers/siteplugins.ts @@ -62,6 +62,7 @@ export class CoreSitePluginsProvider { protected logger; protected sitePlugins: {[name: string]: CoreSitePluginsHandler} = {}; // Site plugins registered. + hasSitePluginsLoaded = false; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform, From f209da8ec3efdebbb07467e7c241413e0751c17e Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 12 Mar 2018 12:52:58 +0100 Subject: [PATCH 29/32] MOBILE-2333 siteplugins: Use lang at plugin level and fix {$a} --- src/core/siteplugins/providers/helper.ts | 45 +++++++++++++----------- src/providers/lang.ts | 9 +++-- src/providers/utils/utils.ts | 4 +++ 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 8ecdac53a..435ead192 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -167,28 +167,28 @@ export class CoreSitePluginsHelperProvider { } /** - * Given a handler's unique name, return the prefix to add to its string keys. + * Given an addon name, return the prefix to add to its string keys. * - * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @param {string} addon Name of the addon (plugin.addon). * @return {string} Prefix. */ - protected getHandlerPrefixForStrings(handlerName: string): string { - if (handlerName) { - return 'plugin.' + handlerName + '.'; + protected getPrefixForStrings(addon: string): string { + if (addon) { + return 'plugin.' + addon + '.'; } return ''; } /** - * Given a handler's unique name and the key of a string, return the full string key (prefixed). + * Given an addon name and the key of a string, return the full string key (prefixed). * - * @param {string} handlerName Handler's unique name (result of getHandlerUniqueName). + * @param {string} addon Name of the addon (plugin.addon). * @param {string} key The key of the string. * @return {string} Full string key. */ - protected getHandlerPrefixedString(handlerName: string, key: string): string { - return this.getHandlerPrefixForStrings(handlerName) + key; + protected getPrefixedString(addon: string, key: string): string { + return this.getPrefixForStrings(addon) + key; } /** @@ -216,21 +216,19 @@ export class CoreSitePluginsHelperProvider { } /** - * Load the lang strings for a handler. + * Load the lang strings for a plugin. * * @param {any} plugin Data of the plugin. - * @param {string} handlerName Name of the handler in the plugin. - * @param {any} handlerSchema Data about the handler. */ - loadHandlerLangStrings(plugin: any, handlerName: string, handlerSchema: any): void { - if (!handlerSchema.lang) { + loadLangStrings(plugin: any): void { + if (!plugin.parsedLang) { return; } - for (const lang in handlerSchema.lang) { - const prefix = this.getHandlerPrefixForStrings(this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName)); + for (const lang in plugin.parsedLang) { + const prefix = this.getPrefixForStrings(plugin.addon); - this.langProvider.addSitePluginsStrings(lang, handlerSchema.lang[lang], prefix); + this.langProvider.addSitePluginsStrings(lang, plugin.parsedLang[lang], prefix); } } @@ -249,9 +247,15 @@ export class CoreSitePluginsHelperProvider { if (!plugin.parsedHandlers) { plugin.parsedHandlers = JSON.parse(plugin.handlers); } + if (!plugin.parsedLang && plugin.lang) { + plugin.parsedLang = JSON.parse(plugin.lang); + } this.sitePluginsProvider.hasSitePluginsLoaded = true; + // Register lang strings. + this.loadLangStrings(plugin); + // Register all the handlers. for (const name in plugin.parsedHandlers) { promises.push(this.registerHandler(plugin, name, plugin.parsedHandlers[name])); @@ -288,7 +292,6 @@ export class CoreSitePluginsHelperProvider { * @return {Promise} Promise resolved when done. */ registerHandler(plugin: any, handlerName: string, handlerSchema: any): Promise { - this.loadHandlerLangStrings(plugin, handlerName, handlerSchema); // Wait for the bootstrap JS to be executed. return this.bootstrapHandler(plugin, handlerSchema).then((result) => { @@ -380,7 +383,7 @@ export class CoreSitePluginsHelperProvider { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), - prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); + prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.courseOptionsDelegate.registerHandler(new CoreSitePluginsCourseOptionHandler(uniqueName, prefixedTitle, plugin, handlerSchema, bootstrapResult, this.sitePluginsProvider)); @@ -409,7 +412,7 @@ export class CoreSitePluginsHelperProvider { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), - prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); + prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.mainMenuDelegate.registerHandler( new CoreSitePluginsMainMenuHandler(uniqueName, prefixedTitle, plugin, handlerSchema, bootstrapResult)); @@ -471,7 +474,7 @@ export class CoreSitePluginsHelperProvider { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), - prefixedTitle = this.getHandlerPrefixedString(uniqueName, handlerSchema.displaydata.title); + prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.userDelegate.registerHandler(new CoreSitePluginsUserProfileHandler(uniqueName, prefixedTitle, plugin, handlerSchema, bootstrapResult, this.sitePluginsProvider)); diff --git a/src/providers/lang.ts b/src/providers/lang.ts index a83aa8668..9f4dfd699 100644 --- a/src/providers/lang.ts +++ b/src/providers/lang.ts @@ -63,14 +63,19 @@ export class CoreLangProvider { } for (const key in strings) { - const prefixedKey = prefix + key, - value = strings[key]; + const prefixedKey = prefix + key; + let value = strings[key]; if (this.customStrings[lang] && this.customStrings[lang][prefixedKey]) { // This string is overridden by a custom string, ignore it. continue; } + // Add another curly bracket to string params ({$a} -> {{$a}}). + value = value.replace(/{([^ ]+)}/gm, '{{$1}}'); + // Make sure we didn't add to many brackets in some case. + value = value.replace(/{{{([^ ]+)}}}/gm, '{{$1}}'); + if (!this.sitePluginsStrings[lang][prefixedKey]) { // It's a new site plugin string. Store the original value. this.sitePluginsStrings[lang][prefixedKey] = { diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts index ec57e20d4..f482b279d 100644 --- a/src/providers/utils/utils.ts +++ b/src/providers/utils/utils.ts @@ -836,6 +836,10 @@ export class CoreUtilsProvider { * @return {object} Object. */ objectToKeyValueMap(objects: object[], keyName: string, valueName: string, keyPrefix?: string): object { + if (!objects) { + return; + } + const prefixSubstr = keyPrefix ? keyPrefix.length : 0, mapped = {}; objects.forEach((item) => { From 9684f9789aa49bf48049264020295ae6033f30fd Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 12 Mar 2018 15:44:41 +0100 Subject: [PATCH 30/32] MOBILE-2333 core: Use aliases in imports --- config/webpack.config.js | 5 +- .../calendar/pages/event/event.module.ts | 4 +- src/addon/calendar/pages/list/list.module.ts | 4 +- .../pages/settings/settings.module.ts | 2 +- src/addon/files/pages/list/list.module.ts | 2 +- .../messages/components/components.module.ts | 4 +- .../pages/discussion/discussion.module.ts | 4 +- .../pages/settings/settings.module.ts | 2 +- .../mod/book/components/components.module.ts | 2 +- .../mod/book/pages/index/index.module.ts | 2 +- .../providers/pushnotifications.ts | 2 +- .../checkbox/providers/handler.ts | 3 +- .../datetime/datetime.module.ts | 2 +- .../datetime/providers/handler.ts | 3 +- .../userprofilefield/menu/menu.module.ts | 2 +- .../menu/providers/handler.ts | 3 +- .../text/providers/handler.ts | 3 +- .../userprofilefield/text/text.module.ts | 2 +- .../textarea/providers/handler.ts | 3 +- .../textarea/textarea.module.ts | 2 +- src/components/components.module.ts | 4 +- .../components/compile-html/compile-html.ts | 2 +- src/core/compile/providers/compile.ts | 72 +++++++++---------- .../classes/module-grade-handler.ts | 2 +- .../classes/module-index-handler.ts | 2 +- .../pages/choose-site/choose-site.module.ts | 2 +- src/core/contentlinks/providers/helper.ts | 6 +- .../course/components/components.module.ts | 2 +- src/core/course/components/format/format.ts | 8 +-- .../module-completion/module-completion.ts | 2 +- .../directives/download-module-main-file.ts | 2 +- .../course/pages/section/section.module.ts | 2 +- src/core/course/pages/section/section.ts | 2 +- .../unsupported-module.module.ts | 2 +- src/core/course/providers/default-format.ts | 2 +- src/core/course/providers/helper.ts | 6 +- src/core/course/providers/options-delegate.ts | 2 +- .../courses/components/components.module.ts | 4 +- .../course-progress/course-progress.ts | 6 +- .../overview-events/overview-events.ts | 4 +- src/core/courses/courses.module.ts | 4 +- .../pages/categories/categories.module.ts | 2 +- .../course-preview/course-preview.module.ts | 4 +- .../pages/course-preview/course-preview.ts | 6 +- .../courses/pages/my-courses/my-courses.ts | 4 +- .../pages/my-overview/my-overview.module.ts | 2 +- .../courses/pages/my-overview/my-overview.ts | 6 +- .../self-enrol-password.module.ts | 2 +- .../courses/providers/course-link-handler.ts | 8 +-- .../providers/courses-index-link-handler.ts | 6 +- .../courses/providers/mainmenu-handler.ts | 2 +- .../providers/my-overview-link-handler.ts | 6 +- .../emulator/providers/local-notifications.ts | 2 +- .../grades/components/components.module.ts | 4 +- src/core/grades/grades.module.ts | 10 +-- .../grades/pages/courses/courses.module.ts | 2 +- src/core/grades/pages/grade/grade.module.ts | 2 +- .../grades/providers/course-option-handler.ts | 6 +- src/core/grades/providers/grades.ts | 2 +- src/core/grades/providers/helper.ts | 4 +- src/core/grades/providers/mainmenu-handler.ts | 2 +- .../grades/providers/overview-link-handler.ts | 6 +- src/core/grades/providers/user-handler.ts | 4 +- .../grades/providers/user-link-handler.ts | 6 +- .../pages/credentials/credentials.module.ts | 2 +- .../login/pages/credentials/credentials.ts | 4 +- .../pages/email-signup/email-signup.module.ts | 4 +- .../login/pages/email-signup/email-signup.ts | 2 +- .../login/pages/reconnect/reconnect.module.ts | 2 +- .../pages/site-error/site-error.module.ts | 2 +- .../login/pages/site-help/site-help.module.ts | 2 +- .../pages/site-policy/site-policy.module.ts | 2 +- src/core/login/pages/site/site.module.ts | 2 +- src/core/login/pages/sites/sites.module.ts | 2 +- src/core/mainmenu/pages/more/more.module.ts | 2 +- src/core/settings/pages/list/list.module.ts | 2 +- .../pages/choose-site/choose-site.module.ts | 2 +- .../sharedfiles/pages/list/list.module.ts | 2 +- src/core/sharedfiles/providers/helper.ts | 2 +- .../sharedfiles/providers/upload-handler.ts | 2 +- src/core/sharedfiles/sharedfiles.module.ts | 2 +- .../all-course-list/all-course-list.ts | 2 +- .../components/categories/categories.ts | 2 +- .../sitehome/components/components.module.ts | 4 +- .../components/course-search/course-search.ts | 2 +- .../enrolled-course-list.ts | 2 +- src/core/sitehome/components/index/index.ts | 6 +- src/core/sitehome/pages/index/index.ts | 2 +- .../sitehome/providers/index-link-handler.ts | 6 +- .../sitehome/providers/mainmenu-handler.ts | 4 +- src/core/sitehome/providers/sitehome.ts | 2 +- src/core/sitehome/sitehome.module.ts | 4 +- src/core/siteplugins/classes/base-handler.ts | 2 +- .../classes/call-ws-click-directive.ts | 2 +- .../siteplugins/classes/call-ws-directive.ts | 2 +- .../classes/course-format-handler.ts | 2 +- .../classes/course-option-handler.ts | 2 +- .../siteplugins/classes/main-menu-handler.ts | 2 +- .../siteplugins/classes/module-handler.ts | 2 +- .../classes/module-prefetch-handler.ts | 2 +- src/core/siteplugins/classes/user-handler.ts | 2 +- .../classes/user-profile-field-handler.ts | 2 +- .../components/components.module.ts | 4 +- .../components/module-index/module-index.ts | 8 +-- .../plugin-content/plugin-content.ts | 2 +- .../user-profile-field/user-profile-field.ts | 2 +- .../directives/call-ws-new-content.ts | 4 +- .../siteplugins/directives/call-ws-on-load.ts | 4 +- src/core/siteplugins/directives/call-ws.ts | 4 +- .../siteplugins/directives/new-content.ts | 4 +- src/core/siteplugins/providers/helper.ts | 28 ++++---- src/core/siteplugins/providers/siteplugins.ts | 16 ++--- src/core/user/components/components.module.ts | 4 +- src/core/user/pages/about/about.module.ts | 2 +- src/core/user/pages/profile/profile.module.ts | 2 +- .../user/providers/course-option-handler.ts | 4 +- src/core/user/providers/user-link-handler.ts | 6 +- src/core/viewer/pages/image/image.module.ts | 2 +- src/core/viewer/pages/text/text.module.ts | 2 +- src/directives/download-file.ts | 6 +- src/providers/file-helper.ts | 2 +- tsconfig.json | 5 +- 122 files changed, 247 insertions(+), 250 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 2118f9cb2..203780578 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -6,12 +6,13 @@ const customConfig = { resolve: { alias: { '@addon': resolve('./src/addon'), + '@app': resolve('./src/app'), '@classes': resolve('./src/classes'), '@core': resolve('./src/core'), '@providers': resolve('./src/providers'), '@components': resolve('./src/components'), - '@directives': resolve('./src/directives/directives.module'), - '@pipes': resolve('./src/pipes/pipes.module') + '@directives': resolve('./src/directives'), + '@pipes': resolve('./src/pipes') } } }; diff --git a/src/addon/calendar/pages/event/event.module.ts b/src/addon/calendar/pages/event/event.module.ts index d579114cc..fe5029958 100644 --- a/src/addon/calendar/pages/event/event.module.ts +++ b/src/addon/calendar/pages/event/event.module.ts @@ -16,8 +16,8 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { AddonCalendarEventPage } from './event'; @NgModule({ diff --git a/src/addon/calendar/pages/list/list.module.ts b/src/addon/calendar/pages/list/list.module.ts index b685b6578..bfe0f7ecd 100644 --- a/src/addon/calendar/pages/list/list.module.ts +++ b/src/addon/calendar/pages/list/list.module.ts @@ -16,8 +16,8 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { AddonCalendarListPage } from './list'; @NgModule({ diff --git a/src/addon/calendar/pages/settings/settings.module.ts b/src/addon/calendar/pages/settings/settings.module.ts index 93f443bd0..8b9fbaa23 100644 --- a/src/addon/calendar/pages/settings/settings.module.ts +++ b/src/addon/calendar/pages/settings/settings.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { AddonCalendarSettingsPage } from './settings'; -import { CorePipesModule } from '@pipes'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/addon/files/pages/list/list.module.ts b/src/addon/files/pages/list/list.module.ts index f819f5512..94fd35db4 100644 --- a/src/addon/files/pages/list/list.module.ts +++ b/src/addon/files/pages/list/list.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonFilesListPage } from './list'; @NgModule({ diff --git a/src/addon/messages/components/components.module.ts b/src/addon/messages/components/components.module.ts index 270ed0e3a..929d642f4 100644 --- a/src/addon/messages/components/components.module.ts +++ b/src/addon/messages/components/components.module.ts @@ -17,8 +17,8 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { AddonMessagesDiscussionsComponent } from '../components/discussions/discussions'; import { AddonMessagesContactsComponent } from '../components/contacts/contacts'; diff --git a/src/addon/messages/pages/discussion/discussion.module.ts b/src/addon/messages/pages/discussion/discussion.module.ts index e0f82d74a..edf7ef9cb 100644 --- a/src/addon/messages/pages/discussion/discussion.module.ts +++ b/src/addon/messages/pages/discussion/discussion.module.ts @@ -17,8 +17,8 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { AddonMessagesDiscussionPage } from './discussion'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/addon/messages/pages/settings/settings.module.ts b/src/addon/messages/pages/settings/settings.module.ts index a20e5f382..6e9071710 100644 --- a/src/addon/messages/pages/settings/settings.module.ts +++ b/src/addon/messages/pages/settings/settings.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { AddonMessagesSettingsPage } from './settings'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonMessagesComponentsModule } from '../../components/components.module'; @NgModule({ diff --git a/src/addon/mod/book/components/components.module.ts b/src/addon/mod/book/components/components.module.ts index cf175a12f..b3db11442 100644 --- a/src/addon/mod/book/components/components.module.ts +++ b/src/addon/mod/book/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 { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreCourseComponentsModule } from '@core/course/components/components.module'; import { AddonModBookIndexComponent } from './index/index'; import { AddonModBookTocPopoverComponent } from './toc-popover/toc-popover'; diff --git a/src/addon/mod/book/pages/index/index.module.ts b/src/addon/mod/book/pages/index/index.module.ts index bce50baa7..14d2d6a29 100644 --- a/src/addon/mod/book/pages/index/index.module.ts +++ b/src/addon/mod/book/pages/index/index.module.ts @@ -15,7 +15,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { AddonModBookComponentsModule } from '../../components/components.module'; import { AddonModBookIndexPage } from './index'; diff --git a/src/addon/pushnotifications/providers/pushnotifications.ts b/src/addon/pushnotifications/providers/pushnotifications.ts index 75bbee3d4..9482de788 100644 --- a/src/addon/pushnotifications/providers/pushnotifications.ts +++ b/src/addon/pushnotifications/providers/pushnotifications.ts @@ -25,7 +25,7 @@ import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreConfigProvider } from '@providers/config'; -import { CoreConfigConstants } from '.././../../configconstants'; +import { CoreConfigConstants } from '../../../configconstants'; /** * Service to handle push notifications. diff --git a/src/addon/userprofilefield/checkbox/providers/handler.ts b/src/addon/userprofilefield/checkbox/providers/handler.ts index b160a08bd..4383da420 100644 --- a/src/addon/userprofilefield/checkbox/providers/handler.ts +++ b/src/addon/userprofilefield/checkbox/providers/handler.ts @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from - '../../../../core/user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldCheckboxComponent } from '../component/checkbox'; /** diff --git a/src/addon/userprofilefield/datetime/datetime.module.ts b/src/addon/userprofilefield/datetime/datetime.module.ts index 9814fc7e5..dd2b3f85d 100644 --- a/src/addon/userprofilefield/datetime/datetime.module.ts +++ b/src/addon/userprofilefield/datetime/datetime.module.ts @@ -19,7 +19,7 @@ import { AddonUserProfileFieldDatetimeHandler } from './providers/handler'; import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldDatetimeComponent } from './component/datetime'; import { CoreComponentsModule } from '@components/components.module'; -import { CorePipesModule } from '@pipes'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/addon/userprofilefield/datetime/providers/handler.ts b/src/addon/userprofilefield/datetime/providers/handler.ts index 819d75f4c..2f2ae2513 100644 --- a/src/addon/userprofilefield/datetime/providers/handler.ts +++ b/src/addon/userprofilefield/datetime/providers/handler.ts @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from - '../../../../core/user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldDatetimeComponent } from '../component/datetime'; /** diff --git a/src/addon/userprofilefield/menu/menu.module.ts b/src/addon/userprofilefield/menu/menu.module.ts index 0329b2c78..df81f32b5 100644 --- a/src/addon/userprofilefield/menu/menu.module.ts +++ b/src/addon/userprofilefield/menu/menu.module.ts @@ -19,7 +19,7 @@ import { AddonUserProfileFieldMenuHandler } from './providers/handler'; import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldMenuComponent } from './component/menu'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/addon/userprofilefield/menu/providers/handler.ts b/src/addon/userprofilefield/menu/providers/handler.ts index c02edb812..89d536561 100644 --- a/src/addon/userprofilefield/menu/providers/handler.ts +++ b/src/addon/userprofilefield/menu/providers/handler.ts @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from - '../../../../core/user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldMenuComponent } from '../component/menu'; /** diff --git a/src/addon/userprofilefield/text/providers/handler.ts b/src/addon/userprofilefield/text/providers/handler.ts index 7f9c3df5e..5c4ce7d9f 100644 --- a/src/addon/userprofilefield/text/providers/handler.ts +++ b/src/addon/userprofilefield/text/providers/handler.ts @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from - '../../../../core/user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextComponent } from '../component/text'; import { CoreTextUtilsProvider } from '@providers/utils/text'; diff --git a/src/addon/userprofilefield/text/text.module.ts b/src/addon/userprofilefield/text/text.module.ts index a0cf647a6..8bc58388f 100644 --- a/src/addon/userprofilefield/text/text.module.ts +++ b/src/addon/userprofilefield/text/text.module.ts @@ -19,7 +19,7 @@ import { AddonUserProfileFieldTextHandler } from './providers/handler'; import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextComponent } from './component/text'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/addon/userprofilefield/textarea/providers/handler.ts b/src/addon/userprofilefield/textarea/providers/handler.ts index c945e07aa..b2bffda4e 100644 --- a/src/addon/userprofilefield/textarea/providers/handler.ts +++ b/src/addon/userprofilefield/textarea/providers/handler.ts @@ -13,8 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from - '../../../../core/user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextareaComponent } from '../component/textarea'; import { CoreTextUtilsProvider } from '@providers/utils/text'; diff --git a/src/addon/userprofilefield/textarea/textarea.module.ts b/src/addon/userprofilefield/textarea/textarea.module.ts index 1c2ce58ef..1a307b685 100644 --- a/src/addon/userprofilefield/textarea/textarea.module.ts +++ b/src/addon/userprofilefield/textarea/textarea.module.ts @@ -19,7 +19,7 @@ import { AddonUserProfileFieldTextareaHandler } from './providers/handler'; import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; import { AddonUserProfileFieldTextareaComponent } from './component/textarea'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 0c4a8b7de..ff4ae9592 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -15,8 +15,8 @@ import { NgModule } from '@angular/core'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { CoreLoadingComponent } from './loading/loading'; import { CoreMarkRequiredComponent } from './mark-required/mark-required'; import { CoreInputErrorsComponent } from './input-errors/input-errors'; diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts index 3583a76df..fcbd23171 100644 --- a/src/core/compile/components/compile-html/compile-html.ts +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -17,7 +17,7 @@ import { ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; -import { CoreCompileProvider } from '../../../compile/providers/compile'; +import { CoreCompileProvider } from '../../providers/compile'; import { BehaviorSubject } from 'rxjs'; /** diff --git a/src/core/compile/providers/compile.ts b/src/core/compile/providers/compile.ts index 33b43e5b1..d864d999f 100644 --- a/src/core/compile/providers/compile.ts +++ b/src/core/compile/providers/compile.ts @@ -18,24 +18,24 @@ import { IonicModule } from 'ionic-angular'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { CoreLoggerProvider } from '../../../providers/logger'; +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 { 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_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 only this provider to prevent circular dependencies. -import { CoreSitePluginsProvider } from '../../siteplugins/providers/siteplugins'; +import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; // Import other libraries and providers. import { DomSanitizer } from '@angular/platform-browser'; @@ -43,36 +43,36 @@ 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 { CoreConstants } from '@core/constants'; import * as moment from 'moment'; import { Md5 } from 'ts-md5/dist/md5'; // Import core classes that can be useful for site plugins. -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'; +import { CoreSyncBaseProvider } from '@classes/base-sync'; +import { CoreCache } from '@classes/cache'; +import { CoreDelegate } from '@classes/delegate'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksModuleGradeHandler } from '@core/contentlinks/classes/module-grade-handler'; +import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; +import { CoreCourseModulePrefetchHandlerBase } from '@core/course/classes/module-prefetch-handler'; // 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 { CoreSitePluginsDirectivesModule } from '../../siteplugins/directives/directives.module'; -import { CoreSiteHomeComponentsModule } from '../../sitehome/components/components.module'; -import { CoreUserComponentsModule } from '../../user/components/components.module'; +import { CoreComponentsModule } from '@components/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 { CoreSitePluginsDirectivesModule } from '@core/siteplugins/directives/directives.module'; +import { CoreSiteHomeComponentsModule } from '@core/sitehome/components/components.module'; +import { CoreUserComponentsModule } from '@core/user/components/components.module'; // Import some components listed in entryComponents so they can be injected dynamically. -import { CoreCourseUnsupportedModuleComponent } from '../../course/components/unsupported-module/unsupported-module'; -import { CoreCourseFormatSingleActivityComponent } from '../../course/formats/singleactivity/components/singleactivity'; -import { CoreSitePluginsModuleIndexComponent } from '../../siteplugins/components/module-index/module-index'; -import { CoreSitePluginsCourseOptionComponent } from '../../siteplugins/components/course-option/course-option'; -import { CoreSitePluginsCourseFormatComponent } from '../../siteplugins/components/course-format/course-format'; +import { CoreCourseUnsupportedModuleComponent } from '@core/course/components/unsupported-module/unsupported-module'; +import { CoreCourseFormatSingleActivityComponent } from '@core/course/formats/singleactivity/components/singleactivity'; +import { CoreSitePluginsModuleIndexComponent } from '@core/siteplugins/components/module-index/module-index'; +import { CoreSitePluginsCourseOptionComponent } from '@core/siteplugins/components/course-option/course-option'; +import { CoreSitePluginsCourseFormatComponent } from '@core/siteplugins/components/course-format/course-format'; /** * Service to provide functionalities regarding compiling dynamic HTML and Javascript. diff --git a/src/core/contentlinks/classes/module-grade-handler.ts b/src/core/contentlinks/classes/module-grade-handler.ts index 36296e5b6..68c26f9b6 100644 --- a/src/core/contentlinks/classes/module-grade-handler.ts +++ b/src/core/contentlinks/classes/module-grade-handler.ts @@ -17,7 +17,7 @@ import { CoreContentLinksAction } from '../providers/delegate'; import { CoreContentLinksHandlerBase } from './base-handler'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseHelperProvider } from '../../course/providers/helper'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; /** * Handler to handle URLs pointing to the grade of a module. diff --git a/src/core/contentlinks/classes/module-index-handler.ts b/src/core/contentlinks/classes/module-index-handler.ts index 32fb2007a..28413ed26 100644 --- a/src/core/contentlinks/classes/module-index-handler.ts +++ b/src/core/contentlinks/classes/module-index-handler.ts @@ -14,7 +14,7 @@ import { CoreContentLinksAction } from '../providers/delegate'; import { CoreContentLinksHandlerBase } from './base-handler'; -import { CoreCourseHelperProvider } from '../../course/providers/helper'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; /** * Handler to handle URLs pointing to the index of a module. diff --git a/src/core/contentlinks/pages/choose-site/choose-site.module.ts b/src/core/contentlinks/pages/choose-site/choose-site.module.ts index 10e7ffe90..881bde925 100644 --- a/src/core/contentlinks/pages/choose-site/choose-site.module.ts +++ b/src/core/contentlinks/pages/choose-site/choose-site.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreContentLinksChooseSitePage } from './choose-site'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/contentlinks/providers/helper.ts b/src/core/contentlinks/providers/helper.ts index 1ee1ea864..2eb8be01f 100644 --- a/src/core/contentlinks/providers/helper.ts +++ b/src/core/contentlinks/providers/helper.ts @@ -23,11 +23,11 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUrlUtilsProvider } from '@providers/utils/url'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksDelegate, CoreContentLinksAction } from './delegate'; -import { CoreConstants } from '../../constants'; +import { CoreConstants } from '@core/constants'; import { CoreConfigConstants } from '../../../configconstants'; -import { CoreSitePluginsProvider } from '../../siteplugins/providers/siteplugins'; +import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; /** * Service that provides some features regarding content links. diff --git a/src/core/course/components/components.module.ts b/src/core/course/components/components.module.ts index 021427bf3..a56f920d8 100644 --- a/src/core/course/components/components.module.ts +++ b/src/core/course/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 { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreCourseFormatComponent } from './format/format'; import { CoreCourseModuleComponent } from './module/module'; import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion'; diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index 51cd9555c..504f5d0a3 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -20,10 +20,10 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseProvider } from '../../../course/providers/course'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; -import { CoreCourseFormatDelegate } from '../../../course/providers/format-delegate'; -import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; +import { CoreCourseFormatDelegate } from '@core/course/providers/format-delegate'; +import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; /** diff --git a/src/core/course/components/module-completion/module-completion.ts b/src/core/course/components/module-completion/module-completion.ts index 6290ae798..1389ff1b7 100644 --- a/src/core/course/components/module-completion/module-completion.ts +++ b/src/core/course/components/module-completion/module-completion.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; -import { CoreUserProvider } from '../../../user/providers/user'; +import { CoreUserProvider } from '@core/user/providers/user'; /** * Component to handle activity completion. It shows a checkbox with the current status, and allows manually changing diff --git a/src/core/course/directives/download-module-main-file.ts b/src/core/course/directives/download-module-main-file.ts index 569a215fb..80fe1fb99 100644 --- a/src/core/course/directives/download-module-main-file.ts +++ b/src/core/course/directives/download-module-main-file.ts @@ -15,7 +15,7 @@ import { Directive, Input, OnInit, ElementRef } from '@angular/core'; import { CoreCourseProvider } from '../providers/course'; import { CoreCourseHelperProvider } from '../providers/helper'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; /** * Directive to allow downloading and open the main file of a module. diff --git a/src/core/course/pages/section/section.module.ts b/src/core/course/pages/section/section.module.ts index bfa4d3eb3..db558c4c3 100644 --- a/src/core/course/pages/section/section.module.ts +++ b/src/core/course/pages/section/section.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreCourseSectionPage } from './section'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreCourseComponentsModule } from '../../components/components.module'; @NgModule({ diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index ac47adf2b..b5d5806ca 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -25,7 +25,7 @@ import { CoreCourseFormatDelegate } from '../../providers/format-delegate'; import { CoreCourseModulePrefetchDelegate } from '../../providers/module-prefetch-delegate'; import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from '../../providers/options-delegate'; import { CoreCourseFormatComponent } from '../../components/format/format'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Page that displays the list of courses the user is enrolled in. diff --git a/src/core/course/pages/unsupported-module/unsupported-module.module.ts b/src/core/course/pages/unsupported-module/unsupported-module.module.ts index 5d2ca24a9..d3772a0da 100644 --- a/src/core/course/pages/unsupported-module/unsupported-module.module.ts +++ b/src/core/course/pages/unsupported-module/unsupported-module.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreCourseUnsupportedModulePage } from './unsupported-module'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreCourseComponentsModule } from '../../components/components.module'; @NgModule({ diff --git a/src/core/course/providers/default-format.ts b/src/core/course/providers/default-format.ts index 2bf1991d1..2e9a58a17 100644 --- a/src/core/course/providers/default-format.ts +++ b/src/core/course/providers/default-format.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { NavController } from 'ionic-angular'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreCourseFormatHandler } from './format-delegate'; import { CoreCourseProvider } from './course'; diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index c883ed13b..236b227dd 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -26,12 +26,12 @@ import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from './options-delegate'; -import { CoreSiteHomeProvider } from '../../sitehome/providers/sitehome'; +import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome'; import { CoreCourseProvider } from './course'; import { CoreCourseModuleDelegate } from './module-delegate'; import { CoreCourseModulePrefetchDelegate } from './module-prefetch-delegate'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; -import { CoreConstants } from '../../constants'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; +import { CoreConstants } from '@core/constants'; import { CoreSite } from '@classes/site'; import * as moment from 'moment'; diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts index 93fe43a1e..569476d70 100644 --- a/src/core/course/providers/options-delegate.ts +++ b/src/core/course/providers/options-delegate.ts @@ -18,7 +18,7 @@ import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreCourseProvider } from './course'; /** diff --git a/src/core/courses/components/components.module.ts b/src/core/courses/components/components.module.ts index d8dbe5a35..d873b3d82 100644 --- a/src/core/courses/components/components.module.ts +++ b/src/core/courses/components/components.module.ts @@ -17,8 +17,8 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; import { CoreCoursesCourseProgressComponent } from '../components/course-progress/course-progress'; import { CoreCoursesCourseListItemComponent } from '../components/course-list-item/course-list-item'; import { CoreCoursesOverviewEventsComponent } from '../components/overview-events/overview-events'; diff --git a/src/core/courses/components/course-progress/course-progress.ts b/src/core/courses/components/course-progress/course-progress.ts index 77c81a975..c0690dba1 100644 --- a/src/core/courses/components/course-progress/course-progress.ts +++ b/src/core/courses/components/course-progress/course-progress.ts @@ -17,9 +17,9 @@ import { NavController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseFormatDelegate } from '../../../course/providers/format-delegate'; -import { CoreCourseProvider } from '../../../course/providers/course'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; +import { CoreCourseFormatDelegate } from '@core/course/providers/format-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; /** * This component is meant to display a course for a list of courses with progress. diff --git a/src/core/courses/components/overview-events/overview-events.ts b/src/core/courses/components/overview-events/overview-events.ts index 50b866569..cf59b3f49 100644 --- a/src/core/courses/components/overview-events/overview-events.ts +++ b/src/core/courses/components/overview-events/overview-events.ts @@ -18,8 +18,8 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '../../../course/providers/course'; -import { CoreContentLinksHelperProvider } from '../../../contentlinks/providers/helper'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import * as moment from 'moment'; /** diff --git a/src/core/courses/courses.module.ts b/src/core/courses/courses.module.ts index d38dad816..0156449fb 100644 --- a/src/core/courses/courses.module.ts +++ b/src/core/courses/courses.module.ts @@ -19,8 +19,8 @@ import { CoreCoursesMyOverviewProvider } from './providers/my-overview'; import { CoreCoursesCourseLinkHandler } from './providers/course-link-handler'; import { CoreCoursesIndexLinkHandler } from './providers/courses-index-link-handler'; import { CoreCoursesMyOverviewLinkHandler } from './providers/my-overview-link-handler'; -import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate'; -import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; // List of providers (without handlers). export const CORE_COURSES_PROVIDERS: any[] = [ diff --git a/src/core/courses/pages/categories/categories.module.ts b/src/core/courses/pages/categories/categories.module.ts index 2e27333b6..c60b37cc3 100644 --- a/src/core/courses/pages/categories/categories.module.ts +++ b/src/core/courses/pages/categories/categories.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreCoursesCategoriesPage } from './categories'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreCoursesComponentsModule } from '../../components/components.module'; @NgModule({ diff --git a/src/core/courses/pages/course-preview/course-preview.module.ts b/src/core/courses/pages/course-preview/course-preview.module.ts index 9b65092c2..2a34ddf92 100644 --- a/src/core/courses/pages/course-preview/course-preview.module.ts +++ b/src/core/courses/pages/course-preview/course-preview.module.ts @@ -17,8 +17,8 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreCoursesCoursePreviewPage } from './course-preview'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/core/courses/pages/course-preview/course-preview.ts b/src/core/courses/pages/course-preview/course-preview.ts index a7877ece2..288baabcf 100644 --- a/src/core/courses/pages/course-preview/course-preview.ts +++ b/src/core/courses/pages/course-preview/course-preview.ts @@ -21,9 +21,9 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreCoursesProvider } from '../../providers/courses'; -import { CoreCourseOptionsDelegate } from '../../../course/providers/options-delegate'; -import { CoreCourseProvider } from '../../../course/providers/course'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; /** * Page that allows "previewing" a course and enrolling in it if enabled and not enrolled. diff --git a/src/core/courses/pages/my-courses/my-courses.ts b/src/core/courses/pages/my-courses/my-courses.ts index 9ad191833..1508052d4 100644 --- a/src/core/courses/pages/my-courses/my-courses.ts +++ b/src/core/courses/pages/my-courses/my-courses.ts @@ -18,8 +18,8 @@ import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreCoursesProvider } from '../../providers/courses'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; -import { CoreCourseOptionsDelegate } from '../../../course/providers/options-delegate'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; /** * Page that displays the list of courses the user is enrolled in. diff --git a/src/core/courses/pages/my-overview/my-overview.module.ts b/src/core/courses/pages/my-overview/my-overview.module.ts index 8825f5ab0..9c1611b8e 100644 --- a/src/core/courses/pages/my-overview/my-overview.module.ts +++ b/src/core/courses/pages/my-overview/my-overview.module.ts @@ -18,7 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreCoursesMyOverviewPage } from './my-overview'; import { CoreComponentsModule } from '@components/components.module'; import { CoreCoursesComponentsModule } from '../../components/components.module'; -import { CoreSiteHomeComponentsModule } from '../../../sitehome/components/components.module'; +import { CoreSiteHomeComponentsModule } from '@core/sitehome/components/components.module'; @NgModule({ declarations: [ diff --git a/src/core/courses/pages/my-overview/my-overview.ts b/src/core/courses/pages/my-overview/my-overview.ts index a90972fb1..552208dfa 100644 --- a/src/core/courses/pages/my-overview/my-overview.ts +++ b/src/core/courses/pages/my-overview/my-overview.ts @@ -18,9 +18,9 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreCoursesProvider } from '../../providers/courses'; import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; -import { CoreCourseOptionsDelegate } from '../../../course/providers/options-delegate'; -import { CoreSiteHomeProvider } from '../../../sitehome/providers/sitehome'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; +import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome'; import * as moment from 'moment'; /** diff --git a/src/core/courses/pages/self-enrol-password/self-enrol-password.module.ts b/src/core/courses/pages/self-enrol-password/self-enrol-password.module.ts index d1aaa6da6..3c24736b0 100644 --- a/src/core/courses/pages/self-enrol-password/self-enrol-password.module.ts +++ b/src/core/courses/pages/self-enrol-password/self-enrol-password.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreCoursesSelfEnrolPasswordPage } from './self-enrol-password'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/courses/providers/course-link-handler.ts b/src/core/courses/providers/course-link-handler.ts index 3dfb1fd39..1720cb9b6 100644 --- a/src/core/courses/providers/course-link-handler.ts +++ b/src/core/courses/providers/course-link-handler.ts @@ -16,10 +16,10 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; -import { CoreCourseProvider } from '../../course/providers/course'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; +import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCoursesProvider } from './courses'; /** diff --git a/src/core/courses/providers/courses-index-link-handler.ts b/src/core/courses/providers/courses-index-link-handler.ts index ebf41c4cf..0183689df 100644 --- a/src/core/courses/providers/courses-index-link-handler.ts +++ b/src/core/courses/providers/courses-index-link-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreCoursesProvider } from './courses'; /** diff --git a/src/core/courses/providers/mainmenu-handler.ts b/src/core/courses/providers/mainmenu-handler.ts index 6727ff832..17073d458 100644 --- a/src/core/courses/providers/mainmenu-handler.ts +++ b/src/core/courses/providers/mainmenu-handler.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreCoursesProvider } from './courses'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; import { CoreCoursesMyOverviewProvider } from '../providers/my-overview'; /** diff --git a/src/core/courses/providers/my-overview-link-handler.ts b/src/core/courses/providers/my-overview-link-handler.ts index 1622bc5e7..e35151615 100644 --- a/src/core/courses/providers/my-overview-link-handler.ts +++ b/src/core/courses/providers/my-overview-link-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; /** * Handler to treat links to my overview. diff --git a/src/core/emulator/providers/local-notifications.ts b/src/core/emulator/providers/local-notifications.ts index 6e19d6474..f59120623 100644 --- a/src/core/emulator/providers/local-notifications.ts +++ b/src/core/emulator/providers/local-notifications.ts @@ -17,7 +17,7 @@ import { LocalNotifications, ILocalNotification } from '@ionic-native/local-noti import { CoreAppProvider } from '@providers/app'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { SQLiteDB } from '@classes/sqlitedb'; -import { CoreConstants } from '../../constants'; +import { CoreConstants } from '@core/constants'; import { CoreConfigConstants } from '../../../configconstants'; import * as moment from 'moment'; diff --git a/src/core/grades/components/components.module.ts b/src/core/grades/components/components.module.ts index 872bb100f..8088706e7 100644 --- a/src/core/grades/components/components.module.ts +++ b/src/core/grades/components/components.module.ts @@ -18,8 +18,8 @@ import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreGradesCourseComponent } from './course/course'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/core/grades/grades.module.ts b/src/core/grades/grades.module.ts index 136715c3d..d6f3d9889 100644 --- a/src/core/grades/grades.module.ts +++ b/src/core/grades/grades.module.ts @@ -15,19 +15,19 @@ import { NgModule } from '@angular/core'; import { CoreGradesProvider } from './providers/grades'; import { CoreGradesHelperProvider } from './providers/helper'; -import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate'; +import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; import { CoreGradesMainMenuHandler } from './providers/mainmenu-handler'; import { CoreGradesCourseOptionHandler } from './providers/course-option-handler'; import { CoreGradesComponentsModule } from './components/components.module'; -import { CoreCourseOptionsDelegate } from '../course/providers/options-delegate'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; import { CoreGradesUserLinkHandler } from './providers/user-link-handler'; import { CoreGradesOverviewLinkHandler } from './providers/overview-link-handler'; -import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; import { CoreGradesUserHandler } from './providers/user-handler'; -import { CoreUserDelegate } from '../user/providers/user-delegate'; +import { CoreUserDelegate } from '@core/user/providers/user-delegate'; import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreUserProvider } from '../user/providers/user'; +import { CoreUserProvider } from '@core/user/providers/user'; // List of providers (without handlers). export const CORE_GRADES_PROVIDERS: any[] = [ diff --git a/src/core/grades/pages/courses/courses.module.ts b/src/core/grades/pages/courses/courses.module.ts index 05bb74eab..40339d8ff 100644 --- a/src/core/grades/pages/courses/courses.module.ts +++ b/src/core/grades/pages/courses/courses.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreGradesCoursesPage } from './courses'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/grades/pages/grade/grade.module.ts b/src/core/grades/pages/grade/grade.module.ts index 159a3c28e..6e6c55fa9 100644 --- a/src/core/grades/pages/grade/grade.module.ts +++ b/src/core/grades/pages/grade/grade.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreGradesGradePage } from './grade'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/grades/providers/course-option-handler.ts b/src/core/grades/providers/course-option-handler.ts index 87ac8540f..2a13d0bfe 100644 --- a/src/core/grades/providers/course-option-handler.ts +++ b/src/core/grades/providers/course-option-handler.ts @@ -14,10 +14,10 @@ import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; -import { CoreCourseProvider } from '../../course/providers/course'; +import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreGradesProvider } from './grades'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreGradesCourseComponent } from '../components/course/course'; /** diff --git a/src/core/grades/providers/grades.ts b/src/core/grades/providers/grades.ts index 15f21e1b4..241ca14fb 100644 --- a/src/core/grades/providers/grades.ts +++ b/src/core/grades/providers/grades.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSite } from '@classes/site'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Service to provide grade functionalities. diff --git a/src/core/grades/providers/helper.ts b/src/core/grades/providers/helper.ts index ceffae450..ef98334ed 100644 --- a/src/core/grades/providers/helper.ts +++ b/src/core/grades/providers/helper.ts @@ -16,8 +16,8 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { TranslateService } from '@ngx-translate/core'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; -import { CoreCourseProvider } from '../../course/providers/course'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreGradesProvider } from './grades'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUrlUtilsProvider } from '@providers/utils/url'; diff --git a/src/core/grades/providers/mainmenu-handler.ts b/src/core/grades/providers/mainmenu-handler.ts index 59c2b016e..fe3b96a77 100644 --- a/src/core/grades/providers/mainmenu-handler.ts +++ b/src/core/grades/providers/mainmenu-handler.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreGradesProvider } from './grades'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; /** * Handler to inject an option into main menu. diff --git a/src/core/grades/providers/overview-link-handler.ts b/src/core/grades/providers/overview-link-handler.ts index 915b9e2c2..32ab28ac1 100644 --- a/src/core/grades/providers/overview-link-handler.ts +++ b/src/core/grades/providers/overview-link-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreGradesProvider } from './grades'; /** diff --git a/src/core/grades/providers/user-handler.ts b/src/core/grades/providers/user-handler.ts index ba95dc857..b7f5f42bf 100644 --- a/src/core/grades/providers/user-handler.ts +++ b/src/core/grades/providers/user-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreGradesProvider } from './grades'; /** diff --git a/src/core/grades/providers/user-link-handler.ts b/src/core/grades/providers/user-link-handler.ts index 589f258aa..91e0d4f19 100644 --- a/src/core/grades/providers/user-link-handler.ts +++ b/src/core/grades/providers/user-link-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreGradesProvider } from './grades'; /** diff --git a/src/core/login/pages/credentials/credentials.module.ts b/src/core/login/pages/credentials/credentials.module.ts index a5b767d10..98918503d 100644 --- a/src/core/login/pages/credentials/credentials.module.ts +++ b/src/core/login/pages/credentials/credentials.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreLoginCredentialsPage } from './credentials'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/credentials/credentials.ts b/src/core/login/pages/credentials/credentials.ts index 75ffd2b0b..b80ee030e 100644 --- a/src/core/login/pages/credentials/credentials.ts +++ b/src/core/login/pages/credentials/credentials.ts @@ -21,8 +21,8 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreLoginHelperProvider } from '../../providers/helper'; -import { CoreContentLinksDelegate } from '../../../contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '../../../contentlinks/providers/helper'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; /** diff --git a/src/core/login/pages/email-signup/email-signup.module.ts b/src/core/login/pages/email-signup/email-signup.module.ts index cf128a353..c0db4ac71 100644 --- a/src/core/login/pages/email-signup/email-signup.module.ts +++ b/src/core/login/pages/email-signup/email-signup.module.ts @@ -17,8 +17,8 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreLoginEmailSignupPage } from './email-signup'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CoreUserComponentsModule } from '../../../user/components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreUserComponentsModule } from '@core/user/components/components.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/email-signup/email-signup.ts b/src/core/login/pages/email-signup/email-signup.ts index 606178f13..b713caab5 100644 --- a/src/core/login/pages/email-signup/email-signup.ts +++ b/src/core/login/pages/email-signup/email-signup.ts @@ -22,7 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreWSProvider } from '@providers/ws'; import { CoreLoginHelperProvider } from '../../providers/helper'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { CoreUserProfileFieldDelegate } from '../../../user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; /** * Page to signup using email. diff --git a/src/core/login/pages/reconnect/reconnect.module.ts b/src/core/login/pages/reconnect/reconnect.module.ts index 40f59485f..412de6c45 100644 --- a/src/core/login/pages/reconnect/reconnect.module.ts +++ b/src/core/login/pages/reconnect/reconnect.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreLoginReconnectPage } from './reconnect'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/site-error/site-error.module.ts b/src/core/login/pages/site-error/site-error.module.ts index 60284f65f..a7276f0a7 100644 --- a/src/core/login/pages/site-error/site-error.module.ts +++ b/src/core/login/pages/site-error/site-error.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { CoreLoginSiteErrorPage } from './site-error'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/site-help/site-help.module.ts b/src/core/login/pages/site-help/site-help.module.ts index c9f01d134..8aa07277d 100644 --- a/src/core/login/pages/site-help/site-help.module.ts +++ b/src/core/login/pages/site-help/site-help.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { CoreLoginSiteHelpPage } from './site-help'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/site-policy/site-policy.module.ts b/src/core/login/pages/site-policy/site-policy.module.ts index 65475306b..21e868074 100644 --- a/src/core/login/pages/site-policy/site-policy.module.ts +++ b/src/core/login/pages/site-policy/site-policy.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreLoginSitePolicyPage } from './site-policy'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/site/site.module.ts b/src/core/login/pages/site/site.module.ts index a188fdfc9..230f60cd6 100644 --- a/src/core/login/pages/site/site.module.ts +++ b/src/core/login/pages/site/site.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { CoreLoginSitePage } from './site'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/login/pages/sites/sites.module.ts b/src/core/login/pages/sites/sites.module.ts index 5bf8716fd..72b515f38 100644 --- a/src/core/login/pages/sites/sites.module.ts +++ b/src/core/login/pages/sites/sites.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreLoginSitesPage } from './sites'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/mainmenu/pages/more/more.module.ts b/src/core/mainmenu/pages/more/more.module.ts index 0f2e6b72c..0c5705079 100644 --- a/src/core/mainmenu/pages/more/more.module.ts +++ b/src/core/mainmenu/pages/more/more.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreMainMenuMorePage } from './more'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/settings/pages/list/list.module.ts b/src/core/settings/pages/list/list.module.ts index 5c2fcaf3a..56cc21396 100644 --- a/src/core/settings/pages/list/list.module.ts +++ b/src/core/settings/pages/list/list.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreSettingsListPage } from './list'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/sharedfiles/pages/choose-site/choose-site.module.ts b/src/core/sharedfiles/pages/choose-site/choose-site.module.ts index 4acb401d6..4711cc588 100644 --- a/src/core/sharedfiles/pages/choose-site/choose-site.module.ts +++ b/src/core/sharedfiles/pages/choose-site/choose-site.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreSharedFilesChooseSitePage } from './choose-site'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/sharedfiles/pages/list/list.module.ts b/src/core/sharedfiles/pages/list/list.module.ts index 9fbe43b58..2211d812f 100644 --- a/src/core/sharedfiles/pages/list/list.module.ts +++ b/src/core/sharedfiles/pages/list/list.module.ts @@ -17,7 +17,7 @@ import { IonicPageModule } from 'ionic-angular'; import { CoreSharedFilesListPage } from './list'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/sharedfiles/providers/helper.ts b/src/core/sharedfiles/providers/helper.ts index 36cbcbba7..a3a4341e3 100644 --- a/src/core/sharedfiles/providers/helper.ts +++ b/src/core/sharedfiles/providers/helper.ts @@ -23,7 +23,7 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSharedFilesProvider } from './sharedfiles'; -import { CoreFileUploaderProvider } from '../../fileuploader/providers/fileuploader'; +import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; /** * Helper service to share files with the app. diff --git a/src/core/sharedfiles/providers/upload-handler.ts b/src/core/sharedfiles/providers/upload-handler.ts index abb4331de..da602b2b3 100644 --- a/src/core/sharedfiles/providers/upload-handler.ts +++ b/src/core/sharedfiles/providers/upload-handler.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { Platform } from 'ionic-angular'; -import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from '../../fileuploader/providers/delegate'; +import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from '@core/fileuploader/providers/delegate'; import { CoreSharedFilesHelperProvider } from './helper'; /** * Handler to upload files from the album. diff --git a/src/core/sharedfiles/sharedfiles.module.ts b/src/core/sharedfiles/sharedfiles.module.ts index fabe30c12..dcb47ad1c 100644 --- a/src/core/sharedfiles/sharedfiles.module.ts +++ b/src/core/sharedfiles/sharedfiles.module.ts @@ -17,7 +17,7 @@ import { Platform } from 'ionic-angular'; import { CoreSharedFilesProvider } from './providers/sharedfiles'; import { CoreSharedFilesHelperProvider } from './providers/helper'; import { CoreSharedFilesUploadHandler } from './providers/upload-handler'; -import { CoreFileUploaderDelegate } from '../fileuploader/providers/delegate'; +import { CoreFileUploaderDelegate } from '@core/fileuploader/providers/delegate'; // List of providers (without handlers). export const CORE_SHAREDFILES_PROVIDERS: any[] = [ diff --git a/src/core/sitehome/components/all-course-list/all-course-list.ts b/src/core/sitehome/components/all-course-list/all-course-list.ts index 3e71c3373..4d8785918 100644 --- a/src/core/sitehome/components/all-course-list/all-course-list.ts +++ b/src/core/sitehome/components/all-course-list/all-course-list.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component } from '@angular/core'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Component to open the page to view the list of all courses. diff --git a/src/core/sitehome/components/categories/categories.ts b/src/core/sitehome/components/categories/categories.ts index 3ae52da32..9bd62f2f0 100644 --- a/src/core/sitehome/components/categories/categories.ts +++ b/src/core/sitehome/components/categories/categories.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component } from '@angular/core'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Component to open the page to view the list of categories. diff --git a/src/core/sitehome/components/components.module.ts b/src/core/sitehome/components/components.module.ts index 9a2a04469..d0fd2ddf8 100644 --- a/src/core/sitehome/components/components.module.ts +++ b/src/core/sitehome/components/components.module.ts @@ -17,8 +17,8 @@ import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CoreCourseComponentsModule } from '../../course/components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreCourseComponentsModule } from '@core/course/components/components.module'; import { CoreSiteHomeIndexComponent } from './index/index'; import { CoreSiteHomeAllCourseListComponent } from './all-course-list/all-course-list'; import { CoreSiteHomeCategoriesComponent } from './categories/categories'; diff --git a/src/core/sitehome/components/course-search/course-search.ts b/src/core/sitehome/components/course-search/course-search.ts index 454a3d1f4..558d53ee8 100644 --- a/src/core/sitehome/components/course-search/course-search.ts +++ b/src/core/sitehome/components/course-search/course-search.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component } from '@angular/core'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Component to open the page to search courses. diff --git a/src/core/sitehome/components/enrolled-course-list/enrolled-course-list.ts b/src/core/sitehome/components/enrolled-course-list/enrolled-course-list.ts index 4c53ab4ff..efe66f205 100644 --- a/src/core/sitehome/components/enrolled-course-list/enrolled-course-list.ts +++ b/src/core/sitehome/components/enrolled-course-list/enrolled-course-list.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit } from '@angular/core'; -import { CoreCoursesProvider } from '../../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Component to open the page to view the list of courses the user is enrolled in. diff --git a/src/core/sitehome/components/index/index.ts b/src/core/sitehome/components/index/index.ts index 2c6140efb..27b414e28 100644 --- a/src/core/sitehome/components/index/index.ts +++ b/src/core/sitehome/components/index/index.ts @@ -15,9 +15,9 @@ import { Component, OnInit } from '@angular/core'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreCourseProvider } from '../../../course/providers/course'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; -import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; +import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; /** * Component that displays site home index. diff --git a/src/core/sitehome/pages/index/index.ts b/src/core/sitehome/pages/index/index.ts index 162ca1561..4262af9e1 100644 --- a/src/core/sitehome/pages/index/index.ts +++ b/src/core/sitehome/pages/index/index.ts @@ -15,7 +15,7 @@ import { Component } from '@angular/core'; import { IonicPage, NavParams, NavController } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; /** * Page that displays site home index. diff --git a/src/core/sitehome/providers/index-link-handler.ts b/src/core/sitehome/providers/index-link-handler.ts index 12ec609cb..d8d5c5c64 100644 --- a/src/core/sitehome/providers/index-link-handler.ts +++ b/src/core/sitehome/providers/index-link-handler.ts @@ -14,9 +14,9 @@ import { Injectable } from '@angular/core'; import { CoreSitesProvider } from '@providers/sites'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreLoginHelperProvider } from '../../login/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreSiteHomeProvider } from './sitehome'; /** diff --git a/src/core/sitehome/providers/mainmenu-handler.ts b/src/core/sitehome/providers/mainmenu-handler.ts index c2cb016b2..56cd67156 100644 --- a/src/core/sitehome/providers/mainmenu-handler.ts +++ b/src/core/sitehome/providers/mainmenu-handler.ts @@ -14,8 +14,8 @@ import { Injectable } from '@angular/core'; import { CoreSiteHomeProvider } from './sitehome'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; -import { CoreCoursesMyOverviewProvider } from '../../courses/providers/my-overview'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; +import { CoreCoursesMyOverviewProvider } from '@core/courses/providers/my-overview'; /** * Handler to add Site Home into main menu. diff --git a/src/core/sitehome/providers/sitehome.ts b/src/core/sitehome/providers/sitehome.ts index 036828a8a..de8957226 100644 --- a/src/core/sitehome/providers/sitehome.ts +++ b/src/core/sitehome/providers/sitehome.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreSite } from '@classes/site'; -import { CoreCourseProvider } from '../../course/providers/course'; +import { CoreCourseProvider } from '@core/course/providers/course'; /** * Service that provides some features regarding site home. diff --git a/src/core/sitehome/sitehome.module.ts b/src/core/sitehome/sitehome.module.ts index d24f3c2df..303859c7d 100644 --- a/src/core/sitehome/sitehome.module.ts +++ b/src/core/sitehome/sitehome.module.ts @@ -16,8 +16,8 @@ import { NgModule } from '@angular/core'; import { CoreSiteHomeProvider } from './providers/sitehome'; import { CoreSiteHomeMainMenuHandler } from './providers/mainmenu-handler'; import { CoreSiteHomeIndexLinkHandler } from './providers/index-link-handler'; -import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate'; -import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate'; +import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; // List of providers (without handlers). export const CORE_SITEHOME_PROVIDERS: any[] = [ diff --git a/src/core/siteplugins/classes/base-handler.ts b/src/core/siteplugins/classes/base-handler.ts index 61ee1c8ad..7226a0202 100644 --- a/src/core/siteplugins/classes/base-handler.ts +++ b/src/core/siteplugins/classes/base-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreDelegateHandler } from '../../../classes/delegate'; +import { CoreDelegateHandler } from '@classes/delegate'; /** * Super class for handlers for site plugins. diff --git a/src/core/siteplugins/classes/call-ws-click-directive.ts b/src/core/siteplugins/classes/call-ws-click-directive.ts index 944c6a6b6..7f535eaad 100644 --- a/src/core/siteplugins/classes/call-ws-click-directive.ts +++ b/src/core/siteplugins/classes/call-ws-click-directive.ts @@ -14,7 +14,7 @@ import { Input, OnInit, ElementRef } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; import { CoreSitePluginsCallWSBaseDirective } from './call-ws-directive'; diff --git a/src/core/siteplugins/classes/call-ws-directive.ts b/src/core/siteplugins/classes/call-ws-directive.ts index 72f9f945e..8d7dedc4b 100644 --- a/src/core/siteplugins/classes/call-ws-directive.ts +++ b/src/core/siteplugins/classes/call-ws-directive.ts @@ -14,7 +14,7 @@ import { Input, OnInit, OnDestroy, ElementRef } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; import { Subscription } from 'rxjs'; diff --git a/src/core/siteplugins/classes/course-format-handler.ts b/src/core/siteplugins/classes/course-format-handler.ts index 197c58bec..91e3a5366 100644 --- a/src/core/siteplugins/classes/course-format-handler.ts +++ b/src/core/siteplugins/classes/course-format-handler.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injector } from '@angular/core'; -import { CoreCourseFormatHandler } from '../../course/providers/format-delegate'; +import { CoreCourseFormatHandler } from '@core/course/providers/format-delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsCourseFormatComponent } from '../components/course-format/course-format'; diff --git a/src/core/siteplugins/classes/course-option-handler.ts b/src/core/siteplugins/classes/course-option-handler.ts index fa9509836..c54f049a3 100644 --- a/src/core/siteplugins/classes/course-option-handler.ts +++ b/src/core/siteplugins/classes/course-option-handler.ts @@ -14,7 +14,7 @@ import { Injector } from '@angular/core'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; +import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsCourseOptionComponent } from '../components/course-option/course-option'; diff --git a/src/core/siteplugins/classes/main-menu-handler.ts b/src/core/siteplugins/classes/main-menu-handler.ts index e43b4df8a..7be3245ac 100644 --- a/src/core/siteplugins/classes/main-menu-handler.ts +++ b/src/core/siteplugins/classes/main-menu-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/providers/delegate'; +import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; /** diff --git a/src/core/siteplugins/classes/module-handler.ts b/src/core/siteplugins/classes/module-handler.ts index a9d0eb098..69ec55ba8 100644 --- a/src/core/siteplugins/classes/module-handler.ts +++ b/src/core/siteplugins/classes/module-handler.ts @@ -14,7 +14,7 @@ import { Injector } from '@angular/core'; import { NavController, NavOptions } from 'ionic-angular'; -import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../../course/providers/module-delegate'; +import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsModuleIndexComponent } from '../components/module-index/module-index'; diff --git a/src/core/siteplugins/classes/module-prefetch-handler.ts b/src/core/siteplugins/classes/module-prefetch-handler.ts index a6b83c784..e309067e6 100644 --- a/src/core/siteplugins/classes/module-prefetch-handler.ts +++ b/src/core/siteplugins/classes/module-prefetch-handler.ts @@ -14,7 +14,7 @@ import { Injector } from '@angular/core'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; -import { CoreCourseModulePrefetchHandlerBase } from '../../course/classes/module-prefetch-handler'; +import { CoreCourseModulePrefetchHandlerBase } from '@core/course/classes/module-prefetch-handler'; /** * Handler to prefetch a module site plugin. diff --git a/src/core/siteplugins/classes/user-handler.ts b/src/core/siteplugins/classes/user-handler.ts index 25b00c6b2..0b003e56e 100644 --- a/src/core/siteplugins/classes/user-handler.ts +++ b/src/core/siteplugins/classes/user-handler.ts @@ -13,7 +13,7 @@ // limitations under the License. import { NavController } from 'ionic-angular'; -import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../user/providers/user-delegate'; +import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsBaseHandler } from './base-handler'; diff --git a/src/core/siteplugins/classes/user-profile-field-handler.ts b/src/core/siteplugins/classes/user-profile-field-handler.ts index 6f88c6cc7..9fd8ed73b 100644 --- a/src/core/siteplugins/classes/user-profile-field-handler.ts +++ b/src/core/siteplugins/classes/user-profile-field-handler.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injector } from '@angular/core'; -import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '../../user/providers/user-profile-field-delegate'; +import { CoreUserProfileFieldHandler, CoreUserProfileFieldHandlerData } from '@core/user/providers/user-profile-field-delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsUserProfileFieldComponent } from '../components/user-profile-field/user-profile-field'; diff --git a/src/core/siteplugins/components/components.module.ts b/src/core/siteplugins/components/components.module.ts index f53ae7c01..53a3d912c 100644 --- a/src/core/siteplugins/components/components.module.ts +++ b/src/core/siteplugins/components/components.module.ts @@ -16,8 +16,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { IonicModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; -import { CoreComponentsModule } from '../../../components/components.module'; -import { CoreCompileHtmlComponentModule } from '../../compile/components/compile-html/compile-html.module'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module'; import { CoreSitePluginsPluginContentComponent } from './plugin-content/plugin-content'; import { CoreSitePluginsModuleIndexComponent } from './module-index/module-index'; import { CoreSitePluginsCourseOptionComponent } from './course-option/course-option'; diff --git a/src/core/siteplugins/components/module-index/module-index.ts b/src/core/siteplugins/components/module-index/module-index.ts index 72322f59f..c3cfb5e01 100644 --- a/src/core/siteplugins/components/module-index/module-index.ts +++ b/src/core/siteplugins/components/module-index/module-index.ts @@ -14,11 +14,11 @@ import { Component, OnInit, OnDestroy, Input, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreTextUtilsProvider } from '../../../../providers/utils/text'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreSitePluginsProvider } from '../../providers/siteplugins'; -import { CoreCourseModuleMainComponent } from '../../../course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate'; -import { CoreCourseHelperProvider } from '../../../course/providers/helper'; +import { CoreCourseModuleMainComponent } from '@core/course/providers/module-delegate'; +import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content'; /** diff --git a/src/core/siteplugins/components/plugin-content/plugin-content.ts b/src/core/siteplugins/components/plugin-content/plugin-content.ts index 77636c765..0e31421ea 100644 --- a/src/core/siteplugins/components/plugin-content/plugin-content.ts +++ b/src/core/siteplugins/components/plugin-content/plugin-content.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreSitePluginsProvider } from '../../providers/siteplugins'; import { Subject } from 'rxjs'; diff --git a/src/core/siteplugins/components/user-profile-field/user-profile-field.ts b/src/core/siteplugins/components/user-profile-field/user-profile-field.ts index 9d017ce2e..84030ba62 100644 --- a/src/core/siteplugins/components/user-profile-field/user-profile-field.ts +++ b/src/core/siteplugins/components/user-profile-field/user-profile-field.ts @@ -14,7 +14,7 @@ import { Component, OnInit, Input, ViewChild, OnDestroy } from '@angular/core'; import { CoreSitePluginsProvider } from '../../providers/siteplugins'; -import { CoreCompileHtmlComponent } from '../../../compile/components/compile-html/compile-html'; +import { CoreCompileHtmlComponent } from '@core/compile/components/compile-html/compile-html'; import { Subscription } from 'rxjs'; /** diff --git a/src/core/siteplugins/directives/call-ws-new-content.ts b/src/core/siteplugins/directives/call-ws-new-content.ts index 43991db73..7f1346ef5 100644 --- a/src/core/siteplugins/directives/call-ws-new-content.ts +++ b/src/core/siteplugins/directives/call-ws-new-content.ts @@ -15,8 +15,8 @@ import { Directive, Input, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; diff --git a/src/core/siteplugins/directives/call-ws-on-load.ts b/src/core/siteplugins/directives/call-ws-on-load.ts index 37b09de1f..14290e22a 100644 --- a/src/core/siteplugins/directives/call-ws-on-load.ts +++ b/src/core/siteplugins/directives/call-ws-on-load.ts @@ -14,8 +14,8 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsCallWSBaseDirective } from '../classes/call-ws-directive'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; diff --git a/src/core/siteplugins/directives/call-ws.ts b/src/core/siteplugins/directives/call-ws.ts index d41ead26e..ee6034380 100644 --- a/src/core/siteplugins/directives/call-ws.ts +++ b/src/core/siteplugins/directives/call-ws.ts @@ -15,8 +15,8 @@ import { Directive, Input, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; diff --git a/src/core/siteplugins/directives/new-content.ts b/src/core/siteplugins/directives/new-content.ts index 7e9b38dd2..6503ed5a2 100644 --- a/src/core/siteplugins/directives/new-content.ts +++ b/src/core/siteplugins/directives/new-content.ts @@ -14,8 +14,8 @@ import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; import { NavController } from 'ionic-angular'; -import { CoreDomUtilsProvider } from '../../../providers/utils/dom'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from '../providers/siteplugins'; import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 435ead192..422b7791d 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -13,23 +13,23 @@ // limitations under the License. import { Injectable, Injector } from '@angular/core'; -import { CoreEventsProvider } from '../../../providers/events'; -import { CoreLangProvider } from '../../../providers/lang'; -import { CoreLoggerProvider } from '../../../providers/logger'; -import { CoreSite } from '../../../classes/site'; -import { CoreSitesProvider } from '../../../providers/sites'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreLangProvider } from '@providers/lang'; +import { CoreLoggerProvider } from '@providers/logger'; +import { CoreSite } from '@classes/site'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from './siteplugins'; -import { CoreCompileProvider } from '../../compile/providers/compile'; +import { CoreCompileProvider } from '@core/compile/providers/compile'; // Delegates -import { CoreMainMenuDelegate } from '../../mainmenu/providers/delegate'; -import { CoreCourseModuleDelegate } from '../../course/providers/module-delegate'; -import { CoreCourseModulePrefetchDelegate } from '../../course/providers/module-prefetch-delegate'; -import { CoreCourseOptionsDelegate } from '../../course/providers/options-delegate'; -import { CoreCourseFormatDelegate } from '../../course/providers/format-delegate'; -import { CoreUserDelegate } from '../../user/providers/user-delegate'; -import { CoreUserProfileFieldDelegate } from '../../user/providers/user-profile-field-delegate'; +import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; +import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; +import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; +import { CoreCourseFormatDelegate } from '@core/course/providers/format-delegate'; +import { CoreUserDelegate } from '@core/user/providers/user-delegate'; +import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate'; // Handler classes. import { CoreSitePluginsCourseFormatHandler } from '../classes/course-format-handler'; diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts index 3ca664d7c..42640537d 100644 --- a/src/core/siteplugins/providers/siteplugins.ts +++ b/src/core/siteplugins/providers/siteplugins.ts @@ -14,15 +14,15 @@ import { Injectable } from '@angular/core'; import { Platform } from 'ionic-angular'; -import { CoreAppProvider } from '../../../providers/app'; -import { CoreFilepoolProvider } from '../../../providers/filepool'; -import { CoreLangProvider } from '../../../providers/lang'; -import { CoreLoggerProvider } from '../../../providers/logger'; -import { CoreSite, CoreSiteWSPreSets } from '../../../classes/site'; -import { CoreSitesProvider } from '../../../providers/sites'; -import { CoreUtilsProvider } from '../../../providers/utils/utils'; +import { CoreAppProvider } from '@providers/app'; +import { CoreFilepoolProvider } from '@providers/filepool'; +import { CoreLangProvider } from '@providers/lang'; +import { CoreLoggerProvider } from '@providers/logger'; +import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreConfigConstants } from '../../../configconstants'; -import { CoreCoursesProvider } from '../../courses/providers/courses'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; /** * Handler of a site plugin. diff --git a/src/core/user/components/components.module.ts b/src/core/user/components/components.module.ts index a996fbb8c..7e7427c17 100644 --- a/src/core/user/components/components.module.ts +++ b/src/core/user/components/components.module.ts @@ -19,8 +19,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreUserParticipantsComponent } from './participants/participants'; import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field'; import { CoreComponentsModule } from '@components/components.module'; -import { CoreDirectivesModule } from '@directives'; -import { CorePipesModule } from '@pipes'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; @NgModule({ declarations: [ diff --git a/src/core/user/pages/about/about.module.ts b/src/core/user/pages/about/about.module.ts index 18abe73ea..736e06435 100644 --- a/src/core/user/pages/about/about.module.ts +++ b/src/core/user/pages/about/about.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreUserAboutPage } from './about'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreComponentsModule } from '@components/components.module'; import { CoreUserComponentsModule } from '../../components/components.module'; diff --git a/src/core/user/pages/profile/profile.module.ts b/src/core/user/pages/profile/profile.module.ts index 3b7e17e5c..2511d5573 100644 --- a/src/core/user/pages/profile/profile.module.ts +++ b/src/core/user/pages/profile/profile.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreUserProfilePage } from './profile'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreComponentsModule } from '@components/components.module'; @NgModule({ diff --git a/src/core/user/providers/course-option-handler.ts b/src/core/user/providers/course-option-handler.ts index 8266e95af..7de414c9e 100644 --- a/src/core/user/providers/course-option-handler.ts +++ b/src/core/user/providers/course-option-handler.ts @@ -14,8 +14,8 @@ import { Injectable, Injector } from '@angular/core'; import { NavController } from 'ionic-angular'; -import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '../../course/providers/options-delegate'; -import { CoreCourseProvider } from '../../course/providers/course'; +import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreUserProvider } from './user'; import { CoreUserParticipantsComponent } from '../components/participants/participants'; diff --git a/src/core/user/providers/user-link-handler.ts b/src/core/user/providers/user-link-handler.ts index 5495105d9..faaa55c81 100644 --- a/src/core/user/providers/user-link-handler.ts +++ b/src/core/user/providers/user-link-handler.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler'; -import { CoreContentLinksAction } from '../../contentlinks/providers/delegate'; -import { CoreContentLinksHelperProvider } from '../../contentlinks/providers/helper'; +import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; +import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; /** * Handler to treat links to user profiles. diff --git a/src/core/viewer/pages/image/image.module.ts b/src/core/viewer/pages/image/image.module.ts index 1f1a6f8e6..f3f864171 100644 --- a/src/core/viewer/pages/image/image.module.ts +++ b/src/core/viewer/pages/image/image.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreViewerImagePage } from './image'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; @NgModule({ declarations: [ diff --git a/src/core/viewer/pages/text/text.module.ts b/src/core/viewer/pages/text/text.module.ts index 2a951b7d3..2cfce877b 100644 --- a/src/core/viewer/pages/text/text.module.ts +++ b/src/core/viewer/pages/text/text.module.ts @@ -16,7 +16,7 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { TranslateModule } from '@ngx-translate/core'; import { CoreViewerTextPage } from './text'; -import { CoreDirectivesModule } from '@directives'; +import { CoreDirectivesModule } from '@directives/directives.module'; /** * Module to lazy load the page. diff --git a/src/directives/download-file.ts b/src/directives/download-file.ts index 3418d87d1..d4c4e311a 100644 --- a/src/directives/download-file.ts +++ b/src/directives/download-file.ts @@ -13,9 +13,9 @@ // limitations under the License. import { Directive, Input, OnInit, ElementRef } from '@angular/core'; -import { CoreFileHelperProvider } from '../providers/file-helper'; -import { CoreDomUtilsProvider } from '../providers/utils/dom'; -import { CoreUtilsProvider } from '../providers/utils/utils'; +import { CoreFileHelperProvider } from '@providers/file-helper'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUtilsProvider } from '@providers/utils/utils'; /** * Directive to allow downloading and open a file. When the item with this directive is clicked, the file will be diff --git a/src/providers/file-helper.ts b/src/providers/file-helper.ts index 88feef0dc..94315a513 100644 --- a/src/providers/file-helper.ts +++ b/src/providers/file-helper.ts @@ -19,7 +19,7 @@ import { CoreFileProvider } from './file'; import { CoreFilepoolProvider } from './filepool'; import { CoreSitesProvider } from './sites'; import { CoreUtilsProvider } from './utils/utils'; -import { CoreConstants } from '../core/constants'; +import { CoreConstants } from '@core/constants'; /** * Provider to provide some helper functions regarding files and packages. diff --git a/tsconfig.json b/tsconfig.json index a6feed9c8..95c37d168 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,12 +15,13 @@ "baseUrl": "./src", "paths": { "@addon/*": ["addon/*"], + "@app/*": ["app/*"], "@classes/*": ["classes/*"], "@core/*": ["core/*"], "@providers/*": ["providers/*"], "@components/*": ["components/*"], - "@directives": ["directives/directives.module"], - "@pipes": ["pipes/pipes.module"] + "@directives/*": ["directives/*"], + "@pipes/*": ["pipes/*"] } }, "include": [ From 489de4fc092f35a4400aa3661b001bc0e5f3f623 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 13 Mar 2018 09:12:30 +0100 Subject: [PATCH 31/32] MOBILE-2333 core: Use parseJSON from text utils --- src/addon/messages/providers/sync.ts | 5 +- src/addon/mod/book/providers/book.ts | 2 +- src/classes/base-sync.ts | 10 ++-- src/classes/site.ts | 2 +- src/core/login/providers/helper.ts | 5 +- src/core/siteplugins/providers/helper.ts | 47 +++++++++---------- src/core/siteplugins/providers/siteplugins.ts | 11 ++--- src/directives/format-text.ts | 2 +- src/providers/filepool.ts | 4 +- src/providers/local-notifications.ts | 7 +-- src/providers/sites.ts | 43 ++++++----------- src/providers/utils/text.ts | 14 ++++-- src/providers/ws.ts | 15 ++---- 13 files changed, 74 insertions(+), 93 deletions(-) diff --git a/src/addon/messages/providers/sync.ts b/src/addon/messages/providers/sync.ts index 45dcfbb24..6e2dfcbfe 100644 --- a/src/addon/messages/providers/sync.ts +++ b/src/addon/messages/providers/sync.ts @@ -21,6 +21,7 @@ import { AddonMessagesOfflineProvider } from './messages-offline'; import { AddonMessagesProvider } from './messages'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreEventsProvider } from '@providers/events'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { TranslateService } from '@ngx-translate/core'; import { CoreSyncProvider } from '@providers/sync'; @@ -37,8 +38,8 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider { protected appProvider: CoreAppProvider, private messagesOffline: AddonMessagesOfflineProvider, private eventsProvider: CoreEventsProvider, private messagesProvider: AddonMessagesProvider, private userProvider: CoreUserProvider, private translate: TranslateService, private utils: CoreUtilsProvider, - syncProvider: CoreSyncProvider) { - super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider, syncProvider); + syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider) { + super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider, syncProvider, textUtils); } /** diff --git a/src/addon/mod/book/providers/book.ts b/src/addon/mod/book/providers/book.ts index b6d36e194..808a77128 100644 --- a/src/addon/mod/book/providers/book.ts +++ b/src/addon/mod/book/providers/book.ts @@ -288,7 +288,7 @@ export class AddonModBookProvider { return []; } - return JSON.parse(contents[0].content); + return this.textUtils.parseJSON(contents[0].content, []); } /** diff --git a/src/classes/base-sync.ts b/src/classes/base-sync.ts index e1e70a2c5..654b3bba8 100644 --- a/src/classes/base-sync.ts +++ b/src/classes/base-sync.ts @@ -16,6 +16,7 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreAppProvider } from '@providers/app'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; /** * Base class to create sync providers. It provides some common functions. @@ -44,7 +45,8 @@ export class CoreSyncBaseProvider { protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise } } = {}; constructor(component: string, protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider, - protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider) { + protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider, + protected textUtils: CoreTextUtilsProvider) { this.logger = this.loggerProvider.getInstance(component); this.component = component; } @@ -115,11 +117,7 @@ export class CoreSyncBaseProvider { */ getSyncWarnings(id: string | number, siteId?: string): Promise { return this.syncProvider.getSyncRecord(this.component, id, siteId).then((entry) => { - try { - return JSON.parse(entry.warnings); - } catch (ex) { - return []; - } + return this.textUtils.parseJSON(entry.warnings, []); }).catch(() => { return []; }); diff --git a/src/classes/site.ts b/src/classes/site.ts index 3c3480f25..682561a4e 100644 --- a/src/classes/site.ts +++ b/src/classes/site.ts @@ -734,7 +734,7 @@ export class CoreSite { const expires = (entry.expirationTime - now) / 1000; this.logger.info(`Cached element found, id: ${id} expires in ${expires} seconds`); - return JSON.parse(entry.data); + return this.textUtils.parseJSON(entry.data, {}); } return Promise.reject(null); diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts index 41443cce2..482a7b356 100644 --- a/src/core/login/providers/helper.ts +++ b/src/core/login/providers/helper.ts @@ -942,9 +942,8 @@ export class CoreLoginHelperProvider { const params = url.split(':::'); return this.configProvider.get(CoreConstants.LOGIN_LAUNCH_DATA).then((data): any => { - try { - data = JSON.parse(data); - } catch (ex) { + data = this.textUtils.parseJSON(data, null); + if (data === null) { return Promise.reject(null); } diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 422b7791d..88f1c0013 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -18,6 +18,7 @@ import { CoreLangProvider } from '@providers/lang'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSite } from '@classes/site'; import { CoreSitesProvider } from '@providers/sites'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreSitePluginsProvider } from './siteplugins'; import { CoreCompileProvider } from '@core/compile/providers/compile'; @@ -59,7 +60,8 @@ export class CoreSitePluginsHelperProvider { private sitePluginsProvider: CoreSitePluginsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, eventsProvider: CoreEventsProvider, - private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate) { + private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate, + private textUtils: CoreTextUtilsProvider) { this.logger = logger.getInstance('CoreSitePluginsHelperProvider'); // Fetch the plugins on login. @@ -201,15 +203,12 @@ export class CoreSitePluginsHelperProvider { isSitePluginEnabled(plugin: any, site: CoreSite): boolean { if (!site.isFeatureDisabled('sitePlugin_' + plugin.component + '_' + plugin.addon) && plugin.handlers) { // Site plugin not disabled. Check if it has handlers. - try { - if (!plugin.parsedHandlers) { - plugin.parsedHandlers = JSON.parse(plugin.handlers); - } - - return !!(plugin.parsedHandlers && Object.keys(plugin.parsedHandlers).length); - } catch (ex) { - this.logger.warn('Error parsing site plugin', ex); + if (!plugin.parsedHandlers) { + plugin.parsedHandlers = this.textUtils.parseJSON(plugin.handlers, null, + this.logger.error.bind(this.logger, 'Error parsing site plugin handlers')); } + + return !!(plugin.parsedHandlers && Object.keys(plugin.parsedHandlers).length); } return false; @@ -243,25 +242,23 @@ export class CoreSitePluginsHelperProvider { this.logger.debug('Load site plugin:', plugin); - try { - if (!plugin.parsedHandlers) { - plugin.parsedHandlers = JSON.parse(plugin.handlers); - } - if (!plugin.parsedLang && plugin.lang) { - plugin.parsedLang = JSON.parse(plugin.lang); - } + if (!plugin.parsedHandlers) { + plugin.parsedHandlers = this.textUtils.parseJSON(plugin.handlers, null, + this.logger.error.bind(this.logger, 'Error parsing site plugin handlers')); + } + if (!plugin.parsedLang && plugin.lang) { + plugin.parsedLang = this.textUtils.parseJSON(plugin.lang, null, + this.logger.error.bind(this.logger, 'Error parsing site plugin lang')); + } - this.sitePluginsProvider.hasSitePluginsLoaded = true; + this.sitePluginsProvider.hasSitePluginsLoaded = true; - // Register lang strings. - this.loadLangStrings(plugin); + // Register lang strings. + this.loadLangStrings(plugin); - // Register all the handlers. - for (const name in plugin.parsedHandlers) { - promises.push(this.registerHandler(plugin, name, plugin.parsedHandlers[name])); - } - } catch (ex) { - this.logger.warn('Error parsing site plugin', ex); + // Register all the handlers. + for (const name in plugin.parsedHandlers) { + promises.push(this.registerHandler(plugin, name, plugin.parsedHandlers[name])); } return this.utils.allPromises(promises); diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts index 42640537d..572d7d780 100644 --- a/src/core/siteplugins/providers/siteplugins.ts +++ b/src/core/siteplugins/providers/siteplugins.ts @@ -20,6 +20,7 @@ import { CoreLangProvider } from '@providers/lang'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreSitesProvider } from '@providers/sites'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreConfigConstants } from '../../../configconstants'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; @@ -66,7 +67,8 @@ export class CoreSitePluginsProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private langProvider: CoreLangProvider, private appProvider: CoreAppProvider, private platform: Platform, - private filepoolProvider: CoreFilepoolProvider, private coursesProvider: CoreCoursesProvider) { + private filepoolProvider: CoreFilepoolProvider, private coursesProvider: CoreCoursesProvider, + private textUtils: CoreTextUtilsProvider) { this.logger = logger.getInstance('CoreUserProvider'); } @@ -215,11 +217,8 @@ export class CoreSitePluginsProvider { return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets); }).then((result) => { if (result.otherdata) { - try { - result.otherdata = JSON.parse(result.otherdata) || {}; - } catch (ex) { - // Ignore errors. - } + result.otherdata = this.textUtils.parseJSON(result.otherdata, {}, + this.logger.error.bind(this.logger, 'Error parsing get_content otherdata', method)); } else { result.otherdata = {}; } diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index 3261db59b..330712d1f 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -383,7 +383,7 @@ export class CoreFormatTextDirective implements OnChanges { return; } - const data = JSON.parse(video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'), + const data = this.textUtils.parseJSON(video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'), youtubeId = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' && data.sources && data.sources[0] && data.sources[0].src && this.youtubeGetId(data.sources[0].src); diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index c6fa08632..bb1ded4a5 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -2183,7 +2183,7 @@ export class CoreFilepoolProvider { return Promise.reject(null); } // Convert the links to an object. - entry.links = JSON.parse(entry.links); + entry.links = this.textUtils.parseJSON(entry.links, []); return entry; }); @@ -2421,7 +2421,7 @@ export class CoreFilepoolProvider { return Promise.reject(this.ERR_QUEUE_IS_EMPTY); } // Convert the links to an object. - item.links = JSON.parse(item.links); + item.links = this.textUtils.parseJSON(item.links, []); return this.processQueueItem(item); }, () => { diff --git a/src/providers/local-notifications.ts b/src/providers/local-notifications.ts index 4bd55cfac..c3339dfcf 100644 --- a/src/providers/local-notifications.ts +++ b/src/providers/local-notifications.ts @@ -19,6 +19,7 @@ import { CoreAppProvider } from './app'; import { CoreConfigProvider } from './config'; import { CoreLoggerProvider } from './logger'; import { CoreDomUtilsProvider } from './utils/dom'; +import { CoreTextUtilsProvider } from './utils/text'; import { CoreUtilsProvider } from './utils/utils'; import { SQLiteDB } from '@classes/sqlitedb'; import { CoreConstants } from '@core/constants'; @@ -112,7 +113,7 @@ export class CoreLocalNotificationsProvider { constructor(logger: CoreLoggerProvider, private localNotifications: LocalNotifications, private platform: Platform, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private configProvider: CoreConfigProvider, - private domUtils: CoreDomUtilsProvider) { + private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider) { this.logger = logger.getInstance('CoreLocalNotificationsProvider'); this.appDB = appProvider.getDB(); this.appDB.createTablesFromSchema(this.tablesSchema); @@ -151,7 +152,7 @@ export class CoreLocalNotificationsProvider { scheduled.forEach((notif) => { if (typeof notif.data == 'string') { - notif.data = JSON.parse(notif.data); + notif.data = this.textUtils.parseJSON(notif.data); } if (typeof notif.data == 'object' && notif.data.siteId === siteId) { @@ -403,7 +404,7 @@ export class CoreLocalNotificationsProvider { notifications.forEach((notification) => { // Convert some properties to the needed types. notification.at = new Date(notification.at * 1000); - notification.data = notification.data ? JSON.parse(notification.data) : {}; + notification.data = notification.data ? this.textUtils.parseJSON(notification.data, {}) : {}; promises.push(this.scheduleNotification(notification)); }); diff --git a/src/providers/sites.ts b/src/providers/sites.ts index 435024ee0..c4ecc8268 100644 --- a/src/providers/sites.ts +++ b/src/providers/sites.ts @@ -19,6 +19,7 @@ import { CoreAppProvider } from './app'; import { CoreEventsProvider } from './events'; import { CoreLoggerProvider } from './logger'; import { CoreSitesFactoryProvider } from './sites-factory'; +import { CoreTextUtilsProvider } from './utils/text'; import { CoreUrlUtilsProvider } from './utils/url'; import { CoreUtilsProvider } from './utils/utils'; import { CoreConstants } from '@core/constants'; @@ -211,7 +212,8 @@ export class CoreSitesProvider { constructor(logger: CoreLoggerProvider, private http: HttpClient, private sitesFactory: CoreSitesFactoryProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private translate: TranslateService, - private eventsProvider: CoreEventsProvider, private urlUtils: CoreUrlUtilsProvider) { + private eventsProvider: CoreEventsProvider, private urlUtils: CoreUrlUtilsProvider, + private textUtils: CoreTextUtilsProvider) { this.logger = logger.getInstance('CoreSitesProvider'); this.appDB = appProvider.getDB(); @@ -787,18 +789,9 @@ export class CoreSitesProvider { info = entry.info, config = entry.config; - // Try to parse info and config. - try { - info = info ? JSON.parse(info) : info; - } catch (ex) { - // Ignore errors. - } - - try { - config = config ? JSON.parse(config) : config; - } catch (ex) { - // Ignore errors. - } + // Parse info and config. + info = info ? this.textUtils.parseJSON(info) : info; + config = config ? this.textUtils.parseJSON(config) : config; site = this.sitesFactory.makeSite(entry.id, entry.siteUrl, entry.token, info, entry.privateToken, config, entry.loggedOut == 1); @@ -862,21 +855,15 @@ export class CoreSitesProvider { const formattedSites = []; sites.forEach((site) => { if (!ids || ids.indexOf(site.id) > -1) { - // Try to parse info. - let siteInfo = site.info; - try { - siteInfo = siteInfo ? JSON.parse(siteInfo) : siteInfo; - } catch (ex) { - // Ignore errors. - } - - const basicInfo: CoreSiteBasicInfo = { - id: site.id, - siteUrl: site.siteUrl, - fullName: siteInfo && siteInfo.fullname, - siteName: siteInfo && siteInfo.sitename, - avatar: siteInfo && siteInfo.userpictureurl - }; + // Parse info. + const siteInfo = site.info ? this.textUtils.parseJSON(site.info) : site.info, + basicInfo: CoreSiteBasicInfo = { + id: site.id, + siteUrl: site.siteUrl, + fullName: siteInfo && siteInfo.fullname, + siteName: siteInfo && siteInfo.sitename, + avatar: siteInfo && siteInfo.userpictureurl + }; formattedSites.push(basicInfo); } }); diff --git a/src/providers/utils/text.ts b/src/providers/utils/text.ts index 11191ddaa..bb7d26c17 100644 --- a/src/providers/utils/text.ts +++ b/src/providers/utils/text.ts @@ -383,19 +383,25 @@ export class CoreTextUtilsProvider { } /** - * Same as Javascript's JSON.parse, but if an exception is thrown it will return the original text. + * Same as Javascript's JSON.parse, but it will handle errors. * * @param {string} json JSON text. + * @param {any} [defaultValue] Default value t oreturn if the parse fails. Defaults to the original value. + * @param {Function} [logErrorFn] An error to call with the exception to log the error. If not supplied, no error. * @return {any} JSON parsed as object or what it gets. */ - parseJSON(json: string): any { + parseJSON(json: string, defaultValue?: any, logErrorFn?: Function): any { try { return JSON.parse(json); } catch (ex) { - // Error, use the json text. + // Error, log the error if needed. + if (logErrorFn) { + logErrorFn(ex); + } } - return json; + // Error parsing, return the default value or the original value. + return typeof defaultValue != 'undefined' ? defaultValue : json; } /** diff --git a/src/providers/ws.ts b/src/providers/ws.ts index 01ccd7c71..660887a06 100644 --- a/src/providers/ws.ts +++ b/src/providers/ws.ts @@ -682,11 +682,7 @@ export class CoreWSProvider { } // Treat response. - try { - data = JSON.parse(data); - } catch (ex) { - // Ignore errors. - } + data = this.textUtils.parseJSON(data); // Some moodle web services return null. // If the responseExpected value is set then so long as no data is returned, we create a blank object. @@ -750,12 +746,9 @@ export class CoreWSProvider { }; return transfer.upload(filePath, uploadUrl, options, true).then((success) => { - let data: any = success.response; - try { - data = JSON.parse(data); - } catch (err) { - this.logger.error('Error parsing response from upload:', err, data); - + const data = this.textUtils.parseJSON(success.response, null, + this.logger.error.bind(this.logger, 'Error parsing response from upload')); + if (data === null) { return Promise.reject(this.translate.instant('core.errorinvalidresponse')); } From 30511db13177750fd96e91ef452c9a33a3fbf07b Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 13 Mar 2018 09:55:22 +0100 Subject: [PATCH 32/32] MOBILE-2333 siteplugins: Receive otherdata as array instead of string --- src/core/siteplugins/providers/siteplugins.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts index 572d7d780..b21100ba3 100644 --- a/src/core/siteplugins/providers/siteplugins.ts +++ b/src/core/siteplugins/providers/siteplugins.ts @@ -217,8 +217,7 @@ export class CoreSitePluginsProvider { return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets); }).then((result) => { if (result.otherdata) { - result.otherdata = this.textUtils.parseJSON(result.otherdata, {}, - this.logger.error.bind(this.logger, 'Error parsing get_content otherdata', method)); + result.otherdata = this.utils.objectToKeyValueMap(result.otherdata, 'name', 'value'); } else { result.otherdata = {}; }