// (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 { Http } from '@angular/http'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreLangProvider } from '@providers/lang'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSite } from '@classes/site'; import { CoreSitesProvider } from '@providers/sites'; 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 './siteplugins'; import { CoreCompileProvider } from '@core/compile/providers/compile'; import { CoreQuestionProvider } from '@core/question/providers/question'; import { CoreCourseProvider } from '@core/course/providers/course'; // Delegates 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'; import { CoreSettingsDelegate } from '@core/settings/providers/delegate'; import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate'; import { AddonMessageOutputDelegate } from '@addon/messageoutput/providers/delegate'; import { AddonModQuizAccessRuleDelegate } from '@addon/mod/quiz/providers/access-rules-delegate'; import { AddonModAssignFeedbackDelegate } from '@addon/mod/assign/providers/feedback-delegate'; import { AddonModAssignSubmissionDelegate } from '@addon/mod/assign/providers/submission-delegate'; import { AddonWorkshopAssessmentStrategyDelegate } from '@addon/mod/workshop/providers/assessment-strategy-delegate'; // Handler classes. import { CoreSitePluginsCourseFormatHandler } from '../classes/handlers/course-format-handler'; import { CoreSitePluginsCourseOptionHandler } from '../classes/handlers/course-option-handler'; import { CoreSitePluginsModuleHandler } from '../classes/handlers/module-handler'; import { CoreSitePluginsModulePrefetchHandler } from '../classes/handlers/module-prefetch-handler'; import { CoreSitePluginsMainMenuHandler } from '../classes/handlers/main-menu-handler'; import { CoreSitePluginsUserProfileHandler } from '../classes/handlers/user-handler'; import { CoreSitePluginsUserProfileFieldHandler } from '../classes/handlers/user-profile-field-handler'; import { CoreSitePluginsSettingsHandler } from '../classes/handlers/settings-handler'; import { CoreSitePluginsQuestionHandler } from '../classes/handlers/question-handler'; import { CoreSitePluginsQuestionBehaviourHandler } from '../classes/handlers/question-behaviour-handler'; import { CoreSitePluginsMessageOutputHandler } from '../classes/handlers/message-output-handler'; import { CoreSitePluginsQuizAccessRuleHandler } from '../classes/handlers/quiz-access-rule-handler'; import { CoreSitePluginsAssignFeedbackHandler } from '../classes/handlers/assign-feedback-handler'; import { CoreSitePluginsAssignSubmissionHandler } from '../classes/handlers/assign-submission-handler'; import { CoreSitePluginsWorkshopAssessmentStrategyHandler } from '../classes/handlers/workshop-assessment-strategy-handler'; import { CoreBlockDelegate } from '@core/block/providers/delegate'; import { CoreSitePluginsBlockHandler } from '@core/siteplugins/classes/handlers/block-handler'; /** * Helper service to provide functionalities regarding site plugins. It basically has the features to load and register site * plugin. * * This code is split from CoreSitePluginsProvider to prevent circular dependencies. * * @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 CoreSitePluginsHelperProvider { protected logger; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private http: Http, private sitePluginsProvider: CoreSitePluginsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, eventsProvider: CoreEventsProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate, private textUtils: CoreTextUtilsProvider, private filepoolProvider: CoreFilepoolProvider, private settingsDelegate: CoreSettingsDelegate, private questionDelegate: CoreQuestionDelegate, private questionBehaviourDelegate: CoreQuestionBehaviourDelegate, private questionProvider: CoreQuestionProvider, private messageOutputDelegate: AddonMessageOutputDelegate, private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private assignSubmissionDelegate: AddonModAssignSubmissionDelegate, private translate: TranslateService, private assignFeedbackDelegate: AddonModAssignFeedbackDelegate, private appProvider: CoreAppProvider, private workshopAssessmentStrategyDelegate: AddonWorkshopAssessmentStrategyDelegate, private courseProvider: CoreCourseProvider, private blockDelegate: CoreBlockDelegate) { this.logger = logger.getInstance('CoreSitePluginsHelperProvider'); // Fetch the plugins on login. eventsProvider.on(CoreEventsProvider.LOGIN, (data) => { this.fetchSitePlugins(data.siteId).then((plugins) => { // Plugins fetched, check that site hasn't changed. if (data.siteId == this.sitesProvider.getCurrentSiteId() && plugins.length) { // Site is still the same. Load the plugins and trigger the event. this.loadSitePlugins(plugins).catch((error) => { this.logger.error(error); }).finally(() => { eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId); }); } }).finally(() => { this.sitePluginsProvider.setPluginsFetched(); }); }); // Unload plugins on logout if any. eventsProvider.on(CoreEventsProvider.LOGOUT, () => { if (this.sitePluginsProvider.hasSitePluginsLoaded) { // Temporary fix. Reload the page to unload all plugins. window.location.reload(); } }); } /** * Download the styles for a handler (if any). * * @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 {string} [siteId] Site ID. If not provided, current site. * @return {Promise} Promise resolved with the CSS code. */ downloadStyles(plugin: any, handlerName: string, handlerSchema: any, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { // Get the absolute URL. If it's a relative URL, add the site URL to it. let url = handlerSchema.styles && handlerSchema.styles.url; if (url && !this.urlUtils.isAbsoluteURL(url)) { url = this.textUtils.concatenatePaths(site.getURL(), url); } const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), componentId = uniqueName + '#main'; // Remove the CSS files for this handler that aren't used anymore. Don't block the call for this. this.filepoolProvider.getFilesByComponent(site.id, CoreSitePluginsProvider.COMPONENT, componentId).then((files) => { files.forEach((file) => { if (file.url != url) { // It's not the current file, delete it. this.filepoolProvider.removeFileByUrl(site.id, file.url).catch(() => { // Ignore errors. }); } }); }).catch(() => { // Ignore errors. }); if (!url) { // No styles. return ''; } // Download the file if not downloaded or the version changed. return this.filepoolProvider.downloadUrl(site.id, url, false, CoreSitePluginsProvider.COMPONENT, componentId, 0, undefined, undefined, undefined, handlerSchema.styles.version).then((url) => { // File is downloaded, get the contents. return this.http.get(url).toPromise(); }).then((response): any => { const text = response && response.text(); if (typeof text == 'string') { return text; } else { return Promise.reject(null); } }); }); } /** * Execute a handler's init method if it has any. * * @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 init JS (if any). */ protected executeHandlerInit(plugin: any, handlerSchema: any): Promise { if (!handlerSchema.init) { return Promise.resolve({}); } return this.executeMethodAndJS(plugin, handlerSchema.init, true); } /** * Execute a get_content method and run its javascript (if any). * * @param {any} plugin Data of the plugin. * @param {string} method The method to call. * @param {boolean} [isInit] Whether it's the init method. * @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(plugin: any, method: string, isInit?: boolean): Promise { const siteId = this.sitesProvider.getCurrentSiteId(), preSets = { getFromCache: false, // Try to ignore cache. deleteCacheIfWSError: isInit // If the init WS call returns an exception we won't use cached data. }; 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; } // Create a "fake" instance to hold all the libraries. const instance = {}; this.compileProvider.injectLibraries(instance); // Add some data of the WS call result. const jsData = this.sitePluginsProvider.createDataForJS(result); for (const name in jsData) { instance[name] = jsData[name]; } // Now execute the javascript using this instance. result.jsResult = this.compileProvider.executeJavascript(instance, result.javascript); return result; }); } /** * Fetch site plugins. * * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when done. Returns the list of plugins to load. */ fetchSitePlugins(siteId?: string): Promise { const plugins = []; return this.sitesProvider.getSite(siteId).then((site) => { if (!this.sitePluginsProvider.isGetContentAvailable(site)) { // Cannot load site plugins, so there's no point to fetch them. return plugins; } // 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((plugin: any) => { // Check if it's a site plugin and it's enabled. if (this.isSitePluginEnabled(plugin, site)) { plugins.push(plugin); } }); return plugins; }); }); } /** * Given an addon name, return the prefix to add to its string keys. * * @param {string} addon Name of the addon (plugin.addon). * @return {string} Prefix. */ protected getPrefixForStrings(addon: string): string { if (addon) { return 'plugin.' + addon + '.'; } return ''; } /** * Given an addon name and the key of a string, return the full string key (prefixed). * * @param {string} addon Name of the addon (plugin.addon). * @param {string} key The key of the string. * @return {string} Full string key. */ protected getPrefixedString(addon: string, key: string): string { return this.getPrefixForStrings(addon) + key; } /** * Check if a certain plugin is a site plugin and it's enabled in a certain site. * * @param {any} plugin Data of the plugin. * @param {CoreSite} site Site affected. * @return {boolean} Whether it's a site plugin and it's enabled. */ 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. 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; } /** * Load the lang strings for a plugin. * * @param {any} plugin Data of the plugin. */ loadLangStrings(plugin: any): void { if (!plugin.parsedLang) { return; } for (const lang in plugin.parsedLang) { const prefix = this.getPrefixForStrings(plugin.addon); this.langProvider.addSitePluginsStrings(lang, plugin.parsedLang[lang], prefix); } } /** * Load a site plugin. * * @param {any} plugin Data of the plugin. * @return {Promise} Promise resolved when loaded. */ loadSitePlugin(plugin: any): Promise { const promises = []; this.logger.debug('Load site plugin:', plugin); 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; // 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])); } return this.utils.allPromises(promises); } /** * Load site plugins. * * @param {any[]} plugins The plugins to load. * @return {Promise} Promise resolved when loaded. */ loadSitePlugins(plugins: any[]): Promise { const promises = []; plugins.forEach((plugin) => { const pluginPromise = this.loadSitePlugin(plugin); promises.push(pluginPromise); this.sitePluginsProvider.registerSitePluginPromise(plugin.component, pluginPromise); }); return this.utils.allPromises(promises); } /** * Load the styles for a handler. * * @param {any} plugin Data of the plugin. * @param {string} handlerName Name of the handler in the plugin. * @param {string} fileUrl CSS file URL. * @param {string} cssCode CSS code. * @param {number} [version] Styles version. * @param {string} [siteId] Site ID. If not provided, current site. */ loadStyles(plugin: any, handlerName: string, fileUrl: string, cssCode: string, version?: number, siteId?: string): void { siteId = siteId || this.sitesProvider.getCurrentSiteId(); // Create the style and add it to the header. const styleEl = document.createElement('style'), uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName); styleEl.setAttribute('id', 'siteplugin-' + uniqueName); styleEl.innerHTML = cssCode; document.head.appendChild(styleEl); // Styles have been loaded, now treat the CSS. this.filepoolProvider.treatCSSCode(siteId, fileUrl, cssCode, CoreSitePluginsProvider.COMPONENT, uniqueName, version) .catch(() => { // Ignore errors. }); } /** * Register a site plugin handler in the right delegate. * * @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(plugin: any, handlerName: string, handlerSchema: any): Promise { // Wait for the init JS to be executed and for the styles to be downloaded. const promises = [], siteId = this.sitesProvider.getCurrentSiteId(); let result, cssCode; promises.push(this.downloadStyles(plugin, handlerName, handlerSchema, siteId).then((code) => { cssCode = code; }).catch((error) => { this.logger.error('Error getting styles for plugin', handlerName, handlerSchema, error); })); promises.push(this.executeHandlerInit(plugin, handlerSchema).then((initResult) => { result = initResult; })); return Promise.all(promises).then(() => { if (cssCode) { // Load the styles. this.loadStyles(plugin, handlerName, handlerSchema.styles.url, cssCode, handlerSchema.styles.version, siteId); } let promise; switch (handlerSchema.delegate) { case 'CoreMainMenuDelegate': promise = Promise.resolve(this.registerMainMenuHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseModuleDelegate': promise = Promise.resolve(this.registerModuleHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreUserDelegate': promise = Promise.resolve(this.registerUserProfileHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseOptionsDelegate': promise = Promise.resolve(this.registerCourseOptionHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreCourseFormatDelegate': promise = Promise.resolve(this.registerCourseFormatHandler(plugin, handlerName, handlerSchema)); break; case 'CoreUserProfileFieldDelegate': promise = Promise.resolve(this.registerUserProfileFieldHandler(plugin, handlerName, handlerSchema)); break; case 'CoreSettingsDelegate': promise = Promise.resolve(this.registerSettingsHandler(plugin, handlerName, handlerSchema, result)); break; case 'CoreQuestionDelegate': promise = Promise.resolve(this.registerQuestionHandler(plugin, handlerName, handlerSchema)); break; case 'CoreQuestionBehaviourDelegate': promise = Promise.resolve(this.registerQuestionBehaviourHandler(plugin, handlerName, handlerSchema)); break; case 'CoreBlockDelegate': promise = Promise.resolve(this.registerBlockHandler(plugin, handlerName, handlerSchema, result)); break; case 'AddonMessageOutputDelegate': promise = Promise.resolve(this.registerMessageOutputHandler(plugin, handlerName, handlerSchema, result)); break; case 'AddonModQuizAccessRuleDelegate': promise = Promise.resolve(this.registerQuizAccessRuleHandler(plugin, handlerName, handlerSchema)); break; case 'AddonModAssignFeedbackDelegate': promise = Promise.resolve(this.registerAssignFeedbackHandler(plugin, handlerName, handlerSchema)); break; case 'AddonModAssignSubmissionDelegate': promise = Promise.resolve(this.registerAssignSubmissionHandler(plugin, handlerName, handlerSchema)); break; case 'AddonWorkshopAssessmentStrategyDelegate': promise = Promise.resolve(this.registerWorkshopAssessmentStrategyHandler(plugin, handlerName, handlerSchema)); break; default: // Nothing to do. promise = Promise.resolve(); } return promise.then((uniqueName) => { if (uniqueName) { // Store the handler data. this.sitePluginsProvider.setSitePluginHandler(uniqueName, { plugin: plugin, handlerName: handlerName, handlerSchema: handlerSchema, initResult: result }); } }); }).catch((err) => { return Promise.reject('Error executing init method ' + handlerSchema.init + ': ' + err.message); }); } /** * Register a handler that relies in a "componentInit" function in a certain delegate. * These type of handlers will return a generic template and its JS in the main method, so it will be called * before registering the handler. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerComponentInitHandler(plugin: any, handlerName: string, handlerSchema: any, delegate: any, createHandlerFn: (uniqueName: string, result: any) => any): string | Promise { if (!handlerSchema.method) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide method', plugin, handlerSchema); return; } this.logger.debug('Register site plugin', plugin, handlerSchema); // Execute the main method and its JS. The template returned will be used in the right component. return this.executeMethodAndJS(plugin, handlerSchema.method).then((result): any => { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), handler = createHandlerFn(uniqueName, result); // Store in handlerSchema some data required by the component. handlerSchema.methodTemplates = result.templates; handlerSchema.methodJSResult = result.jsResult; handlerSchema.methodOtherdata = result.otherdata; if (result && result.jsResult) { // Override default handler functions with the result of the method JS. for (const property in handler) { if (property != 'constructor' && typeof handler[property] == 'function' && typeof result.jsResult[property] == 'function') { handler[property] = result.jsResult[property].bind(handler); } } } delegate.registerHandler(handler); return uniqueName; }).catch((err) => { this.logger.error('Error executing main method', plugin.component, handlerSchema.method, err); }); } /** * Given a handler in a plugin, register it in the assign feedback delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerAssignFeedbackHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.assignFeedbackDelegate, (uniqueName: string, result: any) => { const type = (handlerSchema.moodlecomponent || plugin.component).replace('assignfeedback_', ''), prefix = this.getPrefixForStrings(plugin.addon); return new CoreSitePluginsAssignFeedbackHandler(this.translate, uniqueName, type, prefix); }); } /** * Given a handler in a plugin, register it in the assign submission delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerAssignSubmissionHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.assignSubmissionDelegate, (uniqueName: string, result: any) => { const type = (handlerSchema.moodlecomponent || plugin.component).replace('assignsubmission_', ''), prefix = this.getPrefixForStrings(plugin.addon); return new CoreSitePluginsAssignSubmissionHandler(this.translate, uniqueName, type, prefix); }); } /** * Given a handler in a plugin, register it in the block delegate. * * @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} initResult Result of init function. * @return {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerBlockHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string | Promise { const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), blockName = (handlerSchema.moodlecomponent || plugin.component).replace('block_', ''); this.blockDelegate.registerHandler( new CoreSitePluginsBlockHandler(uniqueName, blockName, handlerSchema, initResult)); return uniqueName; } /** * Given a handler in a plugin, register it in the course format delegate. * * @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 {string} A string to identify the handler. */ protected registerCourseFormatHandler(plugin: any, handlerName: string, handlerSchema: any): string { this.logger.debug('Register site plugin in course format delegate:', plugin, handlerSchema); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), formatName = (handlerSchema.moodlecomponent || plugin.component).replace('format_', ''); this.courseFormatDelegate.registerHandler(new CoreSitePluginsCourseFormatHandler(uniqueName, formatName, handlerSchema)); return uniqueName; } /** * Given a handler in a plugin, register it in the course options delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerCourseOptionHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in course option delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.courseOptionsDelegate.registerHandler(new CoreSitePluginsCourseOptionHandler(uniqueName, prefixedTitle, plugin, handlerSchema, initResult, this.sitePluginsProvider)); return uniqueName; } /** * Given a handler in a plugin, register it in the main menu delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerMainMenuHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in main menu delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.mainMenuDelegate.registerHandler( new CoreSitePluginsMainMenuHandler(uniqueName, prefixedTitle, plugin, handlerSchema, initResult)); return uniqueName; } /** * Given a handler in a plugin, register it in the message output delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerMessageOutputHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in message output delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title), processorName = (handlerSchema.moodlecomponent || plugin.component).replace('message_', ''); this.messageOutputDelegate.registerHandler(new CoreSitePluginsMessageOutputHandler(uniqueName, processorName, prefixedTitle, plugin, handlerSchema, initResult)); return uniqueName; } /** * Given a handler in a plugin, register it in the module delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerModuleHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in module delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), modName = (handlerSchema.moodlecomponent || plugin.component).replace('mod_', ''); this.moduleDelegate.registerHandler(new CoreSitePluginsModuleHandler(uniqueName, modName, handlerSchema, initResult)); if (handlerSchema.offlinefunctions && Object.keys(handlerSchema.offlinefunctions).length) { // Register the prefetch handler. this.prefetchDelegate.registerHandler(new CoreSitePluginsModulePrefetchHandler(this.translate, this.appProvider, this.utils, this.courseProvider, this.filepoolProvider, this.sitesProvider, this.domUtils, this.sitePluginsProvider, plugin.component, uniqueName, modName, handlerSchema)); } return uniqueName; } /** * Given a handler in a plugin, register it in the question delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerQuestionHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.questionDelegate, (uniqueName: string, result: any) => { return new CoreSitePluginsQuestionHandler(uniqueName, handlerSchema.moodlecomponent || plugin.component); }); } /** * Given a handler in a plugin, register it in the question behaviour delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerQuestionBehaviourHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.questionBehaviourDelegate, (uniqueName: string, result: any) => { const type = (handlerSchema.moodlecomponent || plugin.component).replace('qbehaviour_', ''); return new CoreSitePluginsQuestionBehaviourHandler(this.questionProvider, uniqueName, type, result.templates.length); }); } /** * Given a handler in a plugin, register it in the quiz access rule delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerQuizAccessRuleHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.accessRulesDelegate, (uniqueName: string, result: any) => { return new CoreSitePluginsQuizAccessRuleHandler(uniqueName, handlerSchema.moodlecomponent || plugin.component, result.templates.length); }); } /** * Given a handler in a plugin, register it in the settings delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerSettingsHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in settings delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.settingsDelegate.registerHandler( new CoreSitePluginsSettingsHandler(uniqueName, prefixedTitle, plugin, handlerSchema, initResult)); return uniqueName; } /** * Given a handler in a plugin, register it in the user profile delegate. * * @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} initResult Result of the init WS call. * @return {string} A string to identify the handler. */ protected registerUserProfileHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any): string { if (!handlerSchema.displaydata) { // Required data not provided, stop. this.logger.warn('Ignore site plugin because it doesn\'t provide displaydata', plugin, handlerSchema); return; } this.logger.debug('Register site plugin in user profile delegate:', plugin, handlerSchema, initResult); // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); this.userDelegate.registerHandler(new CoreSitePluginsUserProfileHandler(uniqueName, prefixedTitle, plugin, handlerSchema, initResult, this.sitePluginsProvider)); return uniqueName; } /** * Given a handler in a plugin, register it in the user profile field delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerUserProfileFieldHandler(plugin: any, handlerName: string, handlerSchema: any): string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.profileFieldDelegate, (uniqueName: string, result: any) => { const fieldType = (handlerSchema.moodlecomponent || plugin.component).replace('profilefield_', ''); return new CoreSitePluginsUserProfileFieldHandler(uniqueName, fieldType); }); } /** * Given a handler in a plugin, register it in the workshop assessment strategy delegate. * * @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 {string|Promise} A string (or a promise resolved with a string) to identify the handler. */ protected registerWorkshopAssessmentStrategyHandler(plugin: any, handlerName: string, handlerSchema: any) : string | Promise { return this.registerComponentInitHandler(plugin, handlerName, handlerSchema, this.workshopAssessmentStrategyDelegate, (uniqueName: string, result: any) => { const strategyName = (handlerSchema.moodlecomponent || plugin.component).replace('workshopform_', ''); return new CoreSitePluginsWorkshopAssessmentStrategyHandler(uniqueName, strategyName); }); } }