MOBILE-4543 siteplugins: Import libraries once

main
Pau Ferrer Ocaña 2024-04-03 12:53:01 +02:00
parent 9038e883e7
commit 3fed4943d9
11 changed files with 210 additions and 94 deletions

View File

@ -12,32 +12,40 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Type } from '@angular/core';
/**
* Get core errors exported objects.
*
* @returns Core errors exported objects.
*/
export async function getCoreErrorsExportedObjects(): Promise<Record<string, unknown>> {
import { CoreError } from './error';
import { CoreWSError } from './wserror';
import { CoreCanceledError } from './cancelederror';
import { CoreSilentError } from './silenterror';
import { CoreAjaxError } from './ajaxerror';
import { CoreAjaxWSError } from './ajaxwserror';
import { CoreCaptureError } from './captureerror';
import { CoreNetworkError } from './network-error';
import { CoreSiteError } from './siteerror';
import { CoreLoginError } from './loginerror';
import { CoreErrorWithOptions } from './errorwithoptions';
import { CoreHttpError } from './httperror';
const { CoreError } = await import('./error');
const { CoreWSError } = await import('./wserror');
const { CoreCanceledError } = await import('./cancelederror');
const { CoreSilentError } = await import('./silenterror');
const { CoreAjaxError } = await import('./ajaxerror');
const { CoreAjaxWSError } = await import('./ajaxwserror');
const { CoreCaptureError } = await import('./captureerror');
const { CoreNetworkError } = await import('./network-error');
const { CoreSiteError } = await import('./siteerror');
const { CoreLoginError } = await import('./loginerror');
const { CoreErrorWithOptions } = await import('./errorwithoptions');
const { CoreHttpError } = await import('./httperror');
export const CORE_ERRORS_CLASSES: Type<unknown>[] = [
CoreAjaxError,
CoreAjaxWSError,
CoreCanceledError,
CoreCaptureError,
CoreError,
CoreNetworkError,
CoreSilentError,
CoreSiteError,
CoreLoginError,
CoreWSError,
CoreErrorWithOptions,
CoreHttpError,
];
/* eslint-disable @typescript-eslint/naming-convention */
return {
CoreError,
CoreWSError,
CoreCanceledError,
CoreSilentError,
CoreAjaxError,
CoreAjaxWSError,
CoreCaptureError,
CoreNetworkError,
CoreSiteError,
CoreLoginError,
CoreErrorWithOptions,
CoreHttpError,
};
/* eslint-enable @typescript-eslint/naming-convention */
}

View File

@ -171,7 +171,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
protected async getComponentClass(): Promise<Type<unknown>> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const compileInstance = this;
const lazyLibraries = await CoreCompile.getLazyLibraries();
await CoreCompile.loadLibraries();
// Create the component, using the text as the template.
return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
@ -187,10 +187,10 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck {
this['dataArray'] = [];
// Inject the libraries.
CoreCompile.injectLibraries(this, [
...lazyLibraries,
...compileInstance.extraProviders,
]);
CoreCompile.injectLibraries(
this,
compileInstance.extraProviders,
);
// Always add these elements, they could be needed on component init (componentObservable).
this['ChangeDetectorRef'] = compileInstance.changeDetector;

View File

@ -43,8 +43,8 @@ import { makeSingleton } from '@singletons';
import { getCoreServices } from '@/core/core.module';
import { getBlockServices } from '@features/block/block.module';
import { getCommentsServices } from '@features/comments/comments.module';
import { getContentLinksServices } from '@features/contentlinks/contentlinks.module';
import { getCourseServices } from '@features/course/course.module';
import { getContentLinksExportedObjects, getContentLinksServices } from '@features/contentlinks/contentlinks.module';
import { getCourseExportedObjects, getCourseServices } from '@features/course/course.module';
import { getCoursesServices } from '@features/courses/courses.module';
import { getEditorServices } from '@features/editor/editor.module';
import { getEnrolServices } from '@features/enrol/enrol.module';
@ -88,13 +88,8 @@ import { CoreUrl } from '@singletons/url';
import { CoreWindow } from '@singletons/window';
import { CoreCache } from '@classes/cache';
import { CoreDelegate } from '@classes/delegate';
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
import { CoreContentLinksModuleGradeHandler } from '@features/contentlinks/classes/module-grade-handler';
import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
import { CoreCourseResourcePrefetchHandlerBase } from '@features/course/classes/resource-prefetch-handler';
import { CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation';
import { CORE_ERRORS_CLASSES } from '@classes/errors/errors';
import { getCoreErrorsExportedObjects } from '@classes/errors/errors';
import { CoreNetwork } from '@services/network';
// Import all core modules that define components, directives and pipes.
@ -109,19 +104,6 @@ import { CoreBlockComponentsModule } from '@features/block/components/components
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
import { CoreSearchComponentsModule } from '@features/search/components/components.module';
// Import some components so they can be injected dynamically.
import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module';
import { CoreCourseFormatSingleActivityComponent } from '@features/course/format/singleactivity/components/singleactivity';
import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index';
import { CoreSitePluginsBlockComponent } from '@features/siteplugins/components/block/block';
import { CoreSitePluginsCourseFormatComponent } from '@features/siteplugins/components/course-format/course-format';
import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question';
import { CoreSitePluginsQuestionBehaviourComponent } from '@features/siteplugins/components/question-behaviour/question-behaviour';
import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field';
import { CoreSitePluginsQuizAccessRuleComponent } from '@features/siteplugins/components/quiz-access-rule/quiz-access-rule';
import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback';
import { CoreSitePluginsAssignSubmissionComponent } from '@features/siteplugins/components/assign-submission/assign-submission';
// Import addon providers. Do not import database module because it causes circular dependencies.
import { getBadgesServices } from '@addons/badges/badges.module';
import { getCalendarServices } from '@addons/calendar/calendar.module';
@ -142,6 +124,8 @@ import { CorePlatform } from '@services/platform';
import { CoreAutoLogoutService } from '@features/autologout/services/autologout';
import { CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins';
import { getSitePluginsExportedObjects } from '@features/siteplugins/siteplugins.module';
import { CoreError } from '@classes/errors/error';
/**
* Service to provide functionalities regarding compiling dynamic HTML and Javascript.
@ -177,6 +161,9 @@ export class CoreCompileProvider {
getModWorkshopComponentModules,
];
protected libraries?: unknown[];
protected exportedObjects?: Record<string, unknown>;
constructor(protected injector: Injector) {
this.logger = CoreLogger.getInstance('CoreCompileProvider');
}
@ -264,26 +251,28 @@ export class CoreCompileProvider {
* Inject all the core libraries in a certain object.
*
* @param instance The instance where to inject the libraries.
* @param extraProviders Extra imported providers if needed and not imported by this class.
* @param extraLibraries Extra imported providers if needed and not imported by this class.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
injectLibraries(instance: any, extraProviders: Type<unknown>[] = []): void {
const providers = [
...extraProviders,
CoreAutoLogoutService,
CoreSitePluginsProvider,
...this.OTHER_SERVICES,
injectLibraries(instance: any, extraLibraries: Type<unknown>[] = []): void {
if (!this.libraries || !this.exportedObjects) {
throw new CoreError('Libraries not loaded. You need to call loadLibraries before calling injectLibraries.');
}
const libraries = [
...this.libraries,
...extraLibraries,
];
// We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance.
for (const i in providers) {
const providerDef = providers[i];
if (typeof providerDef === 'function' && providerDef.name) {
for (const i in libraries) {
const libraryDef = libraries[i];
if (typeof libraryDef === 'function' && libraryDef.name) {
try {
// Inject the provider to the instance. We use the class name as the property name.
instance[providerDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get<Provider>(providerDef);
instance[libraryDef.name.replace(/DelegateService$/, 'Delegate')] = this.injector.get<Provider>(libraryDef);
} catch (ex) {
this.logger.error('Error injecting provider', providerDef.name, ex);
this.logger.error('Error injecting provider', libraryDef.name, ex);
}
}
}
@ -319,27 +308,26 @@ export class CoreCompileProvider {
instance['CoreCache'] = CoreCache; // @deprecated since 4.4, plugins should use plain objects instead.
instance['CoreDelegate'] = CoreDelegate;
instance['CorePromisedValue'] = CorePromisedValue;
instance['CoreContentLinksHandlerBase'] = CoreContentLinksHandlerBase;
instance['CoreContentLinksModuleGradeHandler'] = CoreContentLinksModuleGradeHandler;
instance['CoreContentLinksModuleIndexHandler'] = CoreContentLinksModuleIndexHandler;
instance['CoreCourseActivityPrefetchHandlerBase'] = CoreCourseActivityPrefetchHandlerBase;
instance['CoreCourseResourcePrefetchHandlerBase'] = CoreCourseResourcePrefetchHandlerBase;
instance['CoreCourseUnsupportedModuleComponent'] = CoreCourseUnsupportedModuleComponent;
instance['CoreCourseFormatSingleActivityComponent'] = CoreCourseFormatSingleActivityComponent;
instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent;
instance['CoreSitePluginsBlockComponent'] = CoreSitePluginsBlockComponent;
instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent;
instance['CoreSitePluginsQuestionComponent'] = CoreSitePluginsQuestionComponent;
instance['CoreSitePluginsQuestionBehaviourComponent'] = CoreSitePluginsQuestionBehaviourComponent;
instance['CoreSitePluginsUserProfileFieldComponent'] = CoreSitePluginsUserProfileFieldComponent;
instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent;
instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent;
instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent;
instance['CoreGeolocationError'] = CoreGeolocationError;
instance['CoreGeolocationErrorReason'] = CoreGeolocationErrorReason;
CORE_ERRORS_CLASSES.forEach((classDef) => {
instance[classDef.name] = classDef;
});
// Inject exported objects.
for (const name in this.exportedObjects) {
instance[name] = this.exportedObjects[name];
}
}
/**
* Load all the libraries needed for the compile service.
*/
async loadLibraries(): Promise<void> {
if (!this.libraries) {
this.libraries = await this.getLibraries();
}
if (!this.exportedObjects) {
this.exportedObjects = await this.getExportedObjects();
}
}
/**
@ -347,7 +335,7 @@ export class CoreCompileProvider {
*
* @returns Lazy libraries.
*/
async getLazyLibraries(): Promise<Type<unknown>[]> {
protected async getLibraries(): Promise<unknown[]> {
const services = await Promise.all([
getCoreServices(),
getBlockServices(),
@ -389,7 +377,30 @@ export class CoreCompileProvider {
getPrivateFilesServices(),
]);
return services.flat();
const lazyLibraries = services.flat();
return [
...lazyLibraries,
CoreAutoLogoutService,
CoreSitePluginsProvider,
...this.OTHER_SERVICES,
];
}
/**
* Get lazy exported objects to inject.
*
* @returns Lazy exported objects.
*/
protected async getExportedObjects(): Promise<Record<string, unknown>> {
const objects = await Promise.all([
getCoreErrorsExportedObjects(),
getCourseExportedObjects(),
getContentLinksExportedObjects(),
getSitePluginsExportedObjects(),
]);
return Object.assign({}, ...objects);
}
}

View File

@ -30,6 +30,25 @@ export async function getContentLinksServices(): Promise<Type<unknown>[]> {
];
}
/**
* Get content links exported objects.
*
* @returns Content links exported objects.
*/
export async function getContentLinksExportedObjects(): Promise<Record<string, unknown>> {
const { CoreContentLinksHandlerBase } = await import ('@features/contentlinks/classes/base-handler');
const { CoreContentLinksModuleGradeHandler } = await import ('@features/contentlinks/classes/module-grade-handler');
const { CoreContentLinksModuleIndexHandler } = await import ('@features/contentlinks/classes/module-index-handler');
/* eslint-disable @typescript-eslint/naming-convention */
return {
CoreContentLinksHandlerBase,
CoreContentLinksModuleGradeHandler,
CoreContentLinksModuleIndexHandler,
};
/* eslint-enable @typescript-eslint/naming-convention */
}
@NgModule({
imports: [
CoreContentLinksComponentsModule,

View File

@ -62,6 +62,31 @@ export async function getCourseServices(): Promise<Type<unknown>[]> {
];
}
/**
* Get course exported objects.
*
* @returns Course exported objects.
*/
export async function getCourseExportedObjects(): Promise<Record<string, unknown>> {
const { CoreCourseActivityPrefetchHandlerBase } = await import('@features/course/classes/activity-prefetch-handler');
const { CoreCourseResourcePrefetchHandlerBase } = await import('@features/course/classes/resource-prefetch-handler');
const { CoreCourseAccessDataType } = await import('@features/course/services/course');
const { CoreCourseUnsupportedModuleComponent } =
await import ('@features/course/components/unsupported-module/unsupported-module');
const { CoreCourseFormatSingleActivityComponent } =
await import ('@features/course/format/singleactivity/components/singleactivity');
/* eslint-disable @typescript-eslint/naming-convention */
return {
CoreCourseActivityPrefetchHandlerBase,
CoreCourseResourcePrefetchHandlerBase,
CoreCourseUnsupportedModuleComponent,
CoreCourseFormatSingleActivityComponent,
CoreCourseAccessDataType,
};
/* eslint-enable @typescript-eslint/naming-convention */
}
export const COURSE_PAGE_NAME = 'course';
export const CONTENTS_PAGE_NAME = 'contents';
export const COURSE_CONTENTS_PATH = `${COURSE_PAGE_NAME}/${COURSE_INDEX_PATH}/${CONTENTS_PAGE_NAME}`;

View File

@ -24,13 +24,13 @@ import {
CoreSitePluginsContent,
CoreSitePluginsCourseModuleHandlerData,
CoreSitePluginsPlugin,
CoreSitePluginsProvider,
} from '@features/siteplugins/services/siteplugins';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
import { CoreLogger } from '@singletons/logger';
import { CoreSitePluginsBaseHandler } from './base-handler';
import { CoreEvents } from '@singletons/events';
import { CoreUtils } from '@services/utils/utils';
import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '@features/siteplugins/constants';
/**
* Handler to support a module using a site plugin.
@ -114,7 +114,7 @@ export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler imp
this.loadCoursePageTemplate(module, courseId, handlerData, method);
// Allow updating the data via event.
CoreEvents.on(CoreSitePluginsProvider.UPDATE_COURSE_CONTENT, (data) => {
CoreEvents.on(CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT, (data) => {
if (data.cmId === module.id) {
this.loadCoursePageTemplate(module, courseId, handlerData, method, !data.alreadyFetched);
}

View File

@ -29,11 +29,12 @@ import { Md5 } from 'ts-md5';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { CoreCompileHtmlComponent } from '@features/compile/components/compile-html/compile-html';
import { CoreSitePlugins, CoreSitePluginsContent, CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins';
import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreEvents } from '@singletons/events';
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '@features/siteplugins/constants';
/**
* Component to render a site plugin content.
@ -261,7 +262,7 @@ export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
* @param alreadyFetched Whether course data has already been fetched (no need to fetch it again).
*/
updateModuleCourseContent(cmId: number, alreadyFetched?: boolean): void {
CoreEvents.trigger(CoreSitePluginsProvider.UPDATE_COURSE_CONTENT, { cmId, alreadyFetched });
CoreEvents.trigger(CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT, { cmId, alreadyFetched });
}
/**

View File

@ -0,0 +1,15 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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.
export const CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT = 'siteplugins_update_course_content';

View File

@ -268,13 +268,13 @@ export class CoreSitePluginsHelperProvider {
}
// Create a "fake" instance to hold all the libraries.
const lazyLibraries = await CoreCompile.getLazyLibraries();
const instance = {
// eslint-disable-next-line @typescript-eslint/naming-convention
HANDLER_DISABLED: HANDLER_DISABLED,
};
CoreCompile.injectLibraries(instance, lazyLibraries);
await CoreCompile.loadLibraries();
CoreCompile.injectLibraries(instance);
// Add some data of the WS call result.
const jsData = CoreSitePlugins.createDataForJS(result);

View File

@ -34,6 +34,7 @@ import { CorePlatform } from '@services/platform';
import { CoreEnrolAction, CoreEnrolInfoIcon } from '@features/enrol/services/enrol-delegate';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { CoreUserProfileHandlerType } from '@features/user/services/user-delegate';
import { CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT } from '../constants';
const ROOT_CACHE_KEY = 'CoreSitePlugins:';
@ -44,7 +45,7 @@ const ROOT_CACHE_KEY = 'CoreSitePlugins:';
export class CoreSitePluginsProvider {
static readonly COMPONENT = 'CoreSitePlugins';
static readonly UPDATE_COURSE_CONTENT = 'siteplugins_update_course_content';
static readonly UPDATE_COURSE_CONTENT = CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT;
protected logger: CoreLogger;
protected sitePlugins: {[name: string]: CoreSitePluginsHandler} = {}; // Site plugins registered.
@ -995,7 +996,7 @@ declare module '@singletons/events' {
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
*/
export interface CoreEventsData {
[CoreSitePluginsProvider.UPDATE_COURSE_CONTENT]: CoreSitePluginsUpdateCourseContentEvent;
[CORE_SITE_PLUGINS_UPDATE_COURSE_CONTENT]: CoreSitePluginsUpdateCourseContentEvent;
}
}

View File

@ -27,6 +27,42 @@ import { canLeaveGuard } from '@guards/can-leave';
import { CoreSitePluginsCourseOptionPage } from '@features/siteplugins/pages/course-option/course-option';
import { CoreSitePluginsModuleIndexPage } from '@features/siteplugins/pages/module-index/module-index';
/**
* Get site plugins exported objects.
*
* @returns Site plugins exported objects.
*/
export async function getSitePluginsExportedObjects(): Promise<Record<string, unknown>> {
const { CoreSitePluginsModuleIndexComponent } = await import ('@features/siteplugins/components/module-index/module-index');
const { CoreSitePluginsBlockComponent } = await import ('@features/siteplugins/components/block/block');
const { CoreSitePluginsCourseFormatComponent } = await import ('@features/siteplugins/components/course-format/course-format');
const { CoreSitePluginsQuestionComponent } = await import ('@features/siteplugins/components/question/question');
const { CoreSitePluginsQuestionBehaviourComponent }
= await import ('@features/siteplugins/components/question-behaviour/question-behaviour');
const { CoreSitePluginsUserProfileFieldComponent }
= await import ('@features/siteplugins/components/user-profile-field/user-profile-field');
const { CoreSitePluginsQuizAccessRuleComponent }
= await import ('@features/siteplugins/components/quiz-access-rule/quiz-access-rule');
const { CoreSitePluginsAssignFeedbackComponent }
= await import ('@features/siteplugins/components/assign-feedback/assign-feedback');
const { CoreSitePluginsAssignSubmissionComponent }
= await import ('@features/siteplugins/components/assign-submission/assign-submission');
/* eslint-disable @typescript-eslint/naming-convention */
return {
CoreSitePluginsModuleIndexComponent,
CoreSitePluginsBlockComponent,
CoreSitePluginsCourseFormatComponent,
CoreSitePluginsQuestionComponent,
CoreSitePluginsQuestionBehaviourComponent,
CoreSitePluginsUserProfileFieldComponent,
CoreSitePluginsQuizAccessRuleComponent,
CoreSitePluginsAssignFeedbackComponent,
CoreSitePluginsAssignSubmissionComponent,
};
/* eslint-enable @typescript-eslint/naming-convention */
}
const routes: Routes = [
{
path: 'siteplugins/content/:component/:method/:hash',