From 1ebe8ba57bc341c8cdd00cf3c53aaeb7d086a163 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 23 Mar 2018 14:36:18 +0100 Subject: [PATCH] MOBILE-2391 core: Improve delegate subclasses --- src/core/course/course.module.ts | 4 +- src/core/course/providers/default-module.ts | 92 +++++++++++++++++++ src/core/course/providers/format-delegate.ts | 52 ++++------- src/core/course/providers/module-delegate.ts | 52 +++-------- src/core/course/providers/options-delegate.ts | 4 +- src/core/fileuploader/providers/delegate.ts | 5 +- src/core/mainmenu/providers/delegate.ts | 4 +- src/core/settings/providers/delegate.ts | 4 +- src/core/user/providers/user-delegate.ts | 4 +- .../providers/user-profile-field-delegate.ts | 2 - 10 files changed, 127 insertions(+), 96 deletions(-) create mode 100644 src/core/course/providers/default-module.ts diff --git a/src/core/course/course.module.ts b/src/core/course/course.module.ts index d26fbd730..eccf47d56 100644 --- a/src/core/course/course.module.ts +++ b/src/core/course/course.module.ts @@ -20,6 +20,7 @@ import { CoreCourseModuleDelegate } from './providers/module-delegate'; import { CoreCourseModulePrefetchDelegate } from './providers/module-prefetch-delegate'; import { CoreCourseOptionsDelegate } from './providers/options-delegate'; import { CoreCourseFormatDefaultHandler } from './providers/default-format'; +import { CoreCourseModuleDefaultHandler } from './providers/default-module'; import { CoreCourseFormatSingleActivityModule } from './formats/singleactivity/singleactivity.module'; import { CoreCourseFormatSocialModule } from './formats/social/social.module'; import { CoreCourseFormatTopicsModule } from './formats/topics/topics.module'; @@ -44,7 +45,8 @@ export const CORE_COURSE_PROVIDERS: any[] = [ CoreCourseFormatSocialModule ], providers: CORE_COURSE_PROVIDERS.concat([ - CoreCourseFormatDefaultHandler + CoreCourseFormatDefaultHandler, + CoreCourseModuleDefaultHandler ]), exports: [] }) diff --git a/src/core/course/providers/default-module.ts b/src/core/course/providers/default-module.ts new file mode 100644 index 000000000..65b52a9df --- /dev/null +++ b/src/core/course/providers/default-module.ts @@ -0,0 +1,92 @@ +// (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 { NavController, NavOptions } from 'ionic-angular'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from './module-delegate'; +import { CoreCourseProvider } from './course'; + +/** + * Default handler used when the module doesn't have a specific implementation. + */ +@Injectable() +export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler { + name = 'CoreCourseModuleDefault'; + modName = 'default'; + + constructor(private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider) { } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } + + /** + * 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 { + // Return the default data. + const defaultData: CoreCourseModuleHandlerData = { + icon: this.courseProvider.getModuleIconSrc(module.modname), + title: module.name, + class: 'core-course-default-handler core-course-module-' + module.modname + '-handler', + action: (event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions): void => { + event.preventDefault(); + event.stopPropagation(); + + navCtrl.push('CoreCourseUnsupportedModulePage', { module: module }, options); + } + }; + + if (module.url) { + defaultData.buttons = [{ + icon: 'open', + label: 'core.openinbrowser', + action: (e: Event): void => { + e.preventDefault(); + e.stopPropagation(); + + this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(module.url); + } + }]; + } + + return defaultData; + } + + /** + * 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 { + // We can't inject CoreCourseUnsupportedModuleComponent here due to circular dependencies. + // Don't return anything, by default it will use CoreCourseUnsupportedModuleComponent. + } +} diff --git a/src/core/course/providers/format-delegate.ts b/src/core/course/providers/format-delegate.ts index 7154e3290..e79841ace 100644 --- a/src/core/course/providers/format-delegate.ts +++ b/src/core/course/providers/format-delegate.ts @@ -155,8 +155,6 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler { */ @Injectable() export class CoreCourseFormatDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreCourseFormatHandler } = {}; // All registered handlers. - protected enabledHandlers: { [s: string]: CoreCourseFormatHandler } = {}; // Handlers enabled for the current site. protected featurePrefix = 'CoreCourseFormatDelegate_'; protected handlerNameProperty = 'format'; @@ -172,7 +170,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {boolean} Whether it allows seeing all sections at the same time. */ canViewAllSections(course: any): boolean { - return this.executeFunction(course.format, 'canViewAllSections', [course]); + return this.executeFunctionOnEnabled(course.format, 'canViewAllSections', [course]); } /** @@ -182,7 +180,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {boolean} Whether the option to enable section/module download should be displayed */ displayEnableDownload(course: any): boolean { - return this.executeFunction(course.format, 'displayEnableDownload', [course]); + return this.executeFunctionOnEnabled(course.format, 'displayEnableDownload', [course]); } /** @@ -192,25 +190,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {boolean} Whether the section selector should be displayed. */ displaySectionSelector(course: any): boolean { - return this.executeFunction(course.format, 'displaySectionSelector', [course]); - } - - /** - * Execute a certain function in a course format handler. - * If the handler isn't found or function isn't defined, call the same function in the default handler. - * - * @param {string} format The format name. - * @param {string} fnName Name of the function to execute. - * @param {any[]} params Parameters to pass to the function. - * @return {any} Function returned value or default value. - */ - protected executeFunction(format: string, fnName: string, params?: any[]): any { - const handler = this.enabledHandlers[format]; - if (handler && handler[fnName]) { - return handler[fnName].apply(handler, params); - } else if (this.defaultHandler[fnName]) { - return this.defaultHandler[fnName].apply(this.defaultHandler, params); - } + return this.executeFunctionOnEnabled(course.format, 'displaySectionSelector', [course]); } /** @@ -221,7 +201,8 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getAllSectionsComponent(injector: Injector, course: any): Promise { - return Promise.resolve(this.executeFunction(course.format, 'getAllSectionsComponent', [injector, course])).catch((e) => { + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getAllSectionsComponent', [injector, course])) + .catch((e) => { this.logger.error('Error getting all sections component', e); }); } @@ -234,7 +215,8 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getCourseFormatComponent(injector: Injector, course: any): Promise { - return Promise.resolve(this.executeFunction(course.format, 'getCourseFormatComponent', [injector, course])).catch((e) => { + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCourseFormatComponent', [injector, course])) + .catch((e) => { this.logger.error('Error getting course format component', e); }); } @@ -247,7 +229,8 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getCourseSummaryComponent(injector: Injector, course: any): Promise { - return Promise.resolve(this.executeFunction(course.format, 'getCourseSummaryComponent', [injector, course])).catch((e) => { + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCourseSummaryComponent', [injector, course])) + .catch((e) => { this.logger.error('Error getting course summary component', e); }); } @@ -260,7 +243,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {string} Course title. */ getCourseTitle(course: any, sections?: any[]): string { - return this.executeFunction(course.format, 'getCourseTitle', [course, sections]); + return this.executeFunctionOnEnabled(course.format, 'getCourseTitle', [course, sections]); } /** @@ -272,7 +255,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { */ getCurrentSection(course: any, sections: any[]): Promise { // Convert the result to a Promise if it isn't. - return Promise.resolve(this.executeFunction(course.format, 'getCurrentSection', [course, sections])).catch(() => { + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getCurrentSection', [course, sections])).catch(() => { // This function should never fail. Just return the first section. if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) { return sections[0]; @@ -290,7 +273,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getSectionSelectorComponent(injector: Injector, course: any): Promise { - return Promise.resolve(this.executeFunction(course.format, 'getSectionSelectorComponent', [injector, course])) + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getSectionSelectorComponent', [injector, course])) .catch((e) => { this.logger.error('Error getting section selector component', e); }); @@ -305,7 +288,8 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getSingleSectionComponent(injector: Injector, course: any): Promise { - return Promise.resolve(this.executeFunction(course.format, 'getSingleSectionComponent', [injector, course])).catch((e) => { + return Promise.resolve(this.executeFunctionOnEnabled(course.format, 'getSingleSectionComponent', [injector, course])) + .catch((e) => { this.logger.error('Error getting single section component', e); }); } @@ -318,7 +302,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved when the data is invalidated. */ invalidateData(course: any, sections: any[]): Promise { - return this.executeFunction(course.format, 'invalidateData', [course, sections]); + return this.executeFunctionOnEnabled(course.format, 'invalidateData', [course, sections]); } /** @@ -329,10 +313,6 @@ export class CoreCourseFormatDelegate extends CoreDelegate { * @return {Promise} Promise resolved when done. */ openCourse(navCtrl: NavController, course: any): Promise { - if (this.enabledHandlers[course.format] && this.enabledHandlers[course.format].openCourse) { - return this.enabledHandlers[course.format].openCourse(navCtrl, course); - } - - return navCtrl.push('CoreCourseSectionPage', { course: course }); + return this.executeFunctionOnEnabled(course.format, 'openCourse', [navCtrl, course]); } } diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts index f6bbdc669..3d46aadae 100644 --- a/src/core/course/providers/module-delegate.ts +++ b/src/core/course/providers/module-delegate.ts @@ -19,6 +19,7 @@ import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreCourseProvider } from './course'; import { CoreSite } from '@classes/site'; +import { CoreCourseModuleDefaultHandler } from './default-module'; import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; /** @@ -172,13 +173,11 @@ export interface CoreCourseModuleHandlerButton { */ @Injectable() export class CoreCourseModuleDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreCourseModuleHandler } = {}; // All registered handlers. - protected enabledHandlers: { [s: string]: CoreCourseModuleHandler } = {}; // Handlers enabled for the current site. protected featurePrefix = 'CoreCourseModuleDelegate_'; protected handlerNameProperty = 'modName'; constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, - protected courseProvider: CoreCourseProvider) { + protected courseProvider: CoreCourseProvider, protected defaultHandler: CoreCourseModuleDefaultHandler) { super('CoreCourseModuleDelegate', loggerProvider, sitesProvider, eventsProvider); } @@ -191,12 +190,10 @@ export class CoreCourseModuleDelegate extends CoreDelegate { * @return {Promise} Promise resolved with component to use, undefined if not found. */ getMainComponent(injector: Injector, course: any, module: any): Promise { - const handler = this.enabledHandlers[module.modname]; - if (handler && handler.getMainComponent) { - return Promise.resolve(handler.getMainComponent(injector, course, module)).catch((err) => { - this.logger.error('Error getting main component', err); - }); - } + return Promise.resolve(this.executeFunctionOnEnabled(module.modname, 'getMainComponent', [injector, course, module])) + .catch((err) => { + this.logger.error('Error getting main component', err); + }); } /** @@ -209,36 +206,7 @@ export class CoreCourseModuleDelegate extends CoreDelegate { * @return {CoreCourseModuleHandlerData} Data to render the module. */ getModuleDataFor(modname: string, module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { - if (typeof this.enabledHandlers[modname] != 'undefined') { - return this.enabledHandlers[modname].getData(module, courseId, sectionId); - } - - // Return the default data. - const defaultData: CoreCourseModuleHandlerData = { - icon: this.courseProvider.getModuleIconSrc(module.modname), - title: module.name, - class: 'core-course-default-handler core-course-module-' + module.modname + '-handler', - action: (event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions): void => { - event.preventDefault(); - event.stopPropagation(); - - navCtrl.push('CoreCourseUnsupportedModulePage', { module: module }, options); - } - }; - - if (module.url) { - defaultData.buttons = [{ - icon: 'open', - label: 'core.openinbrowser', - action: (e: Event): void => { - e.preventDefault(); - e.stopPropagation(); - this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(module.url); - } - }]; - } - - return defaultData; + return this.executeFunctionOnEnabled(modname, 'getData', [module, courseId, sectionId]); } /** @@ -262,10 +230,12 @@ export class CoreCourseModuleDelegate extends CoreDelegate { * @return {boolean} Whether module is disabled. */ isModuleDisabledInSite(modname: string, site?: CoreSite): boolean { - if (typeof this.handlers[modname] != 'undefined') { + const handler = this.getHandler(modname, true); + + if (handler) { site = site || this.sitesProvider.getCurrentSite(); - return this.isFeatureDisabled(this.handlers[modname], site); + return this.isFeatureDisabled(handler, site); } return false; diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts index 440ab2cc4..32357a8da 100644 --- a/src/core/course/providers/options-delegate.ts +++ b/src/core/course/providers/options-delegate.ts @@ -129,8 +129,6 @@ export interface CoreCourseOptionsHandlerToDisplay { */ @Injectable() export class CoreCourseOptionsDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreCourseOptionsHandler } = {}; // All registered handlers. - protected enabledHandlers: { [s: string]: CoreCourseOptionsHandler } = {}; // Handlers enabled for the current site. protected loaded: { [courseId: number]: boolean } = {}; protected lastUpdateHandlersForCoursesStart: any = {}; protected coursesHandlers: { @@ -440,7 +438,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate { this.lastUpdateHandlersForCoursesStart[courseId] = now; for (const name in this.enabledHandlers) { - const handler = this.enabledHandlers[name]; + const handler = this.enabledHandlers[name]; // Checks if the handler is enabled for the user. promises.push(Promise.resolve(handler.isEnabledForCourse(courseId, accessData, navOptions, admOptions)) diff --git a/src/core/fileuploader/providers/delegate.ts b/src/core/fileuploader/providers/delegate.ts index e0b149c81..b8e7371a7 100644 --- a/src/core/fileuploader/providers/delegate.ts +++ b/src/core/fileuploader/providers/delegate.ts @@ -146,9 +146,6 @@ export interface CoreFileUploaderHandlerDataToReturn extends CoreFileUploaderHan */ @Injectable() export class CoreFileUploaderDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreFileUploaderHandler } = {}; // All registered handlers. - protected enabledHandlers: { [s: string]: CoreFileUploaderHandler } = {}; // Handlers enabled for the current site. - constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, protected eventsProvider: CoreEventsProvider) { super('CoreFileUploaderDelegate', loggerProvider, sitesProvider, eventsProvider); @@ -173,7 +170,7 @@ export class CoreFileUploaderDelegate extends CoreDelegate { const handlers = []; for (const name in this.enabledHandlers) { - const handler = this.enabledHandlers[name]; + const handler = this.enabledHandlers[name]; let supportedMimetypes; if (mimetypes) { diff --git a/src/core/mainmenu/providers/delegate.ts b/src/core/mainmenu/providers/delegate.ts index 1ffa81b5f..ea23cad25 100644 --- a/src/core/mainmenu/providers/delegate.ts +++ b/src/core/mainmenu/providers/delegate.ts @@ -107,8 +107,6 @@ export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData { */ @Injectable() export class CoreMainMenuDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreMainMenuHandler } = {}; - protected enabledHandlers: { [s: string]: CoreMainMenuHandler } = {}; protected loaded = false; protected siteHandlers: Subject = new BehaviorSubject([]); protected featurePrefix = 'CoreMainMenuDelegate_'; @@ -153,7 +151,7 @@ export class CoreMainMenuDelegate extends CoreDelegate { const handlersData: any[] = []; for (const name in this.enabledHandlers) { - const handler = this.enabledHandlers[name], + const handler = this.enabledHandlers[name], data = handler.getDisplayData(); handlersData.push({ diff --git a/src/core/settings/providers/delegate.ts b/src/core/settings/providers/delegate.ts index df8d2e346..a7fca19cd 100644 --- a/src/core/settings/providers/delegate.ts +++ b/src/core/settings/providers/delegate.ts @@ -79,8 +79,6 @@ export interface CoreSettingsHandlerData { @Injectable() export class CoreSettingsDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreSettingsHandler } = {}; - protected enabledHandlers: { [s: string]: CoreSettingsHandler } = {}; protected siteHandlers: CoreSettingsHandlerData[] = []; // Handlers to return. constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, @@ -113,7 +111,7 @@ export class CoreSettingsDelegate extends CoreDelegate { const handlersData: any[] = []; for (const name in this.enabledHandlers) { - const handler = this.enabledHandlers[name], + const handler = this.enabledHandlers[name], data = handler.getDisplayData(); handlersData.push({ diff --git a/src/core/user/providers/user-delegate.ts b/src/core/user/providers/user-delegate.ts index cb8f9de98..9fd4632d4 100644 --- a/src/core/user/providers/user-delegate.ts +++ b/src/core/user/providers/user-delegate.ts @@ -162,8 +162,6 @@ export class CoreUserDelegate extends CoreDelegate { */ static UPDATE_HANDLER_EVENT = 'CoreUserDelegate_update_handler_event'; - protected handlers: { [s: string]: CoreUserProfileHandler } = {}; - protected enabledHandlers: { [s: string]: CoreUserProfileHandler } = {}; protected observableHandlers: Subject = new BehaviorSubject([]); protected userHandlers: CoreUserProfileHandlerToDisplay[] = []; @@ -233,7 +231,7 @@ export class CoreUserDelegate extends CoreDelegate { for (const name in this.enabledHandlers) { // Checks if the handler is enabled for the user. - const handler = this.handlers[name], + const handler = this.handlers[name], isEnabledForUser = handler.isEnabledForUser(user, courseId, navOptions, admOptions), promise = Promise.resolve(isEnabledForUser).then((enabled) => { if (enabled) { diff --git a/src/core/user/providers/user-profile-field-delegate.ts b/src/core/user/providers/user-profile-field-delegate.ts index 2cc3a5f4a..0e48f4a0c 100644 --- a/src/core/user/providers/user-profile-field-delegate.ts +++ b/src/core/user/providers/user-profile-field-delegate.ts @@ -71,8 +71,6 @@ export interface CoreUserProfileFieldHandlerData { */ @Injectable() export class CoreUserProfileFieldDelegate extends CoreDelegate { - protected handlers: { [s: string]: CoreUserProfileFieldHandler } = {}; - protected enabledHandlers: { [s: string]: CoreUserProfileFieldHandler } = {}; protected handlerNameProperty = 'type'; constructor(protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,