From c3ca95e838350c3a5336cf52c5b9a12bde643743 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 15 Feb 2018 14:57:18 +0100 Subject: [PATCH] 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); }); } }