diff --git a/src/addons/mod/workshop/assessment/accumulative/accumulative.module.ts b/src/addons/mod/workshop/assessment/accumulative/accumulative.module.ts index be3c10dc1c1..ae072947fa1 100644 --- a/src/addons/mod/workshop/assessment/accumulative/accumulative.module.ts +++ b/src/addons/mod/workshop/assessment/accumulative/accumulative.module.ts @@ -13,15 +13,11 @@ // limitations under the License. import { APP_INITIALIZER, NgModule } from '@angular/core'; -import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from './component/accumulative'; -import { AddonModWorkshopAssessmentStrategyAccumulativeHandler } from './services/handler'; import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate'; import { CoreSharedModule } from '@/core/shared.module'; +import { getAssessmentStrategyHandlerInstance } from '@addons/mod/workshop/assessment/accumulative/services/handler'; @NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyAccumulativeComponent, - ], imports: [ CoreSharedModule, ], @@ -30,14 +26,9 @@ import { CoreSharedModule } from '@/core/shared.module'; provide: APP_INITIALIZER, multi: true, useValue: () => { - AddonWorkshopAssessmentStrategyDelegate.registerHandler( - AddonModWorkshopAssessmentStrategyAccumulativeHandler.instance, - ); + AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance()); }, }, ], - exports: [ - AddonModWorkshopAssessmentStrategyAccumulativeComponent, - ], }) export class AddonModWorkshopAssessmentStrategyAccumulativeModule {} diff --git a/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts new file mode 100644 index 00000000000..c9f8bcd8e1b --- /dev/null +++ b/src/addons/mod/workshop/assessment/accumulative/services/handler-lazy.ts @@ -0,0 +1,149 @@ +// (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. + +import { + AddonModWorkshopAssessmentStrategyFieldErrors, +} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { + AddonModWorkshopGetAssessmentFormDefinitionData, + AddonModWorkshopGetAssessmentFormFieldsParsedData, +} from '@addons/mod/workshop/services/workshop'; +import { Injectable, Type } from '@angular/core'; +import { CoreGradesHelper } from '@features/grades/services/grades-helper'; +import { makeSingleton, Translate } from '@singletons'; +import { CoreFormFields } from '@singletons/form'; +import { AddonWorkshopAssessmentStrategyHandler } from '../../../services/assessment-strategy-delegate'; +import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from '../component/accumulative'; +import { AddonModWorkshopAssessmentStrategyAccumulativeHandlerService } from './handler'; + +/** + * Handler for accumulative assessment strategy plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerLazyService + extends AddonModWorkshopAssessmentStrategyAccumulativeHandlerService + implements AddonWorkshopAssessmentStrategyHandler { + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + + /** + * @inheritdoc + */ + getComponent(): Type { + return AddonModWorkshopAssessmentStrategyAccumulativeComponent; + } + + /** + * @inheritdoc + */ + async getOriginalValues( + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const defaultGrade = Translate.instant('core.choosedots'); + const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + const promises: Promise[] = []; + + form.fields.forEach((field, n) => { + field.dimtitle = Translate.instant('addon.mod_workshop_assessment_accumulative.dimensionnumber', { $a: field.number }); + + if (!form.current[n]) { + form.current[n] = {}; + } + + originalValues[n] = {}; + originalValues[n].peercomment = form.current[n].peercomment || ''; + originalValues[n].number = field.number; // eslint-disable-line id-blacklist + + form.current[n].grade = form.current[n].grade ? parseInt(String(form.current[n].grade), 10) : -1; + + const gradingType = parseInt(String(field.grade), 10); + const dimension = form.dimensionsinfo.find((dimension) => dimension.id == parseInt(field.dimensionid, 10)); + const scale = dimension && gradingType < 0 ? dimension.scale : undefined; + + promises.push(CoreGradesHelper.makeGradesMenu(gradingType, undefined, defaultGrade, -1, scale).then((grades) => { + field.grades = grades; + originalValues[n].grade = form.current[n].grade; + + return; + })); + }); + + await Promise.all(promises); + + return originalValues; + } + + /** + * @inheritdoc + */ + hasDataChanged( + originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + ): boolean { + for (const x in originalValues) { + if (originalValues[x].grade != currentValues[x].grade) { + return true; + } + if (originalValues[x].peercomment != currentValues[x].peercomment) { + return true; + } + } + + return false; + } + + /** + * @inheritdoc + */ + async prepareAssessmentData( + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const data: CoreFormFields = {}; + const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; + let hasErrors = false; + + form.fields.forEach((field, idx) => { + if (idx < form.dimenssionscount) { + const grade = parseInt(String(currentValues[idx].grade), 10); + if (!isNaN(grade) && grade >= 0) { + data['grade__idx_' + idx] = grade; + } else { + errors['grade_' + idx] = Translate.instant('addon.mod_workshop_assessment_accumulative.mustchoosegrade'); + hasErrors = true; + } + + data['peercomment__idx_' + idx] = currentValues[idx].peercomment ?? ''; + + data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; + data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); + data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; + } + }); + + if (hasErrors) { + throw errors; + } + + return data; + } + +} +export const AddonModWorkshopAssessmentStrategyAccumulativeHandler = + makeSingleton(AddonModWorkshopAssessmentStrategyAccumulativeHandlerLazyService); diff --git a/src/addons/mod/workshop/assessment/accumulative/services/handler.ts b/src/addons/mod/workshop/assessment/accumulative/services/handler.ts index 563be19b885..869652eee69 100644 --- a/src/addons/mod/workshop/assessment/accumulative/services/handler.ts +++ b/src/addons/mod/workshop/assessment/accumulative/services/handler.ts @@ -12,138 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { asyncInstance } from '@/core/utils/async-instance'; +import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { - AddonModWorkshopAssessmentStrategyFieldErrors, -} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; -import { - AddonModWorkshopGetAssessmentFormDefinitionData, - AddonModWorkshopGetAssessmentFormFieldsParsedData, -} from '@addons/mod/workshop/services/workshop'; -import { Injectable, Type } from '@angular/core'; -import { CoreGradesHelper } from '@features/grades/services/grades-helper'; -import { makeSingleton, Translate } from '@singletons'; -import { CoreFormFields } from '@singletons/form'; -import { AddonWorkshopAssessmentStrategyHandler } from '../../../services/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from '../component/accumulative'; - -/** - * Handler for accumulative assessment strategy plugin. - */ -@Injectable({ providedIn: 'root' }) -export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerService implements AddonWorkshopAssessmentStrategyHandler { - - name = 'AddonModWorkshopAssessmentStrategyAccumulative'; - strategyName = 'accumulative'; - - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getComponent(): Type { - return AddonModWorkshopAssessmentStrategyAccumulativeComponent; - } - - /** - * @inheritdoc - */ - async getOriginalValues( - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const defaultGrade = Translate.instant('core.choosedots'); - const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; - const promises: Promise[] = []; - - form.fields.forEach((field, n) => { - field.dimtitle = Translate.instant('addon.mod_workshop_assessment_accumulative.dimensionnumber', { $a: field.number }); - - if (!form.current[n]) { - form.current[n] = {}; - } - - originalValues[n] = {}; - originalValues[n].peercomment = form.current[n].peercomment || ''; - originalValues[n].number = field.number; // eslint-disable-line id-blacklist + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_NAME, + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_STRATEGY_NAME, +} from '@addons/mod/workshop/assessment/constants'; - form.current[n].grade = form.current[n].grade ? parseInt(String(form.current[n].grade), 10) : -1; +export class AddonModWorkshopAssessmentStrategyAccumulativeHandlerService { - const gradingType = parseInt(String(field.grade), 10); - const dimension = form.dimensionsinfo.find((dimension) => dimension.id == parseInt(field.dimensionid, 10)); - const scale = dimension && gradingType < 0 ? dimension.scale : undefined; + name = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_NAME; + strategyName = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_STRATEGY_NAME; - promises.push(CoreGradesHelper.makeGradesMenu(gradingType, undefined, defaultGrade, -1, scale).then((grades) => { - field.grades = grades; - originalValues[n].grade = form.current[n].grade; - - return; - })); - }); - - await Promise.all(promises); - - return originalValues; - } - - /** - * @inheritdoc - */ - hasDataChanged( - originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - ): boolean { - for (const x in originalValues) { - if (originalValues[x].grade != currentValues[x].grade) { - return true; - } - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * @inheritdoc - */ - async prepareAssessmentData( - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const data: CoreFormFields = {}; - const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const grade = parseInt(String(currentValues[idx].grade), 10); - if (!isNaN(grade) && grade >= 0) { - data['grade__idx_' + idx] = grade; - } else { - errors['grade_' + idx] = Translate.instant('addon.mod_workshop_assessment_accumulative.mustchoosegrade'); - hasErrors = true; - } - - data['peercomment__idx_' + idx] = currentValues[idx].peercomment ?? ''; +} - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; - } - }); +/** + * Get assessment strategy handler instance. + * + * @returns Assessment strategy handler. + */ +export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopAssessmentStrategyAccumulativeHandler } = await import('./handler-lazy'); - if (hasErrors) { - throw errors; - } + return AddonModWorkshopAssessmentStrategyAccumulativeHandler.instance; + }); - return data; - } + lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyAccumulativeHandlerService()); + return lazyHandler; } -export const AddonModWorkshopAssessmentStrategyAccumulativeHandler = - makeSingleton(AddonModWorkshopAssessmentStrategyAccumulativeHandlerService); diff --git a/src/addons/mod/workshop/assessment/assesment-components.module.ts b/src/addons/mod/workshop/assessment/assesment-components.module.ts new file mode 100644 index 00000000000..f702edacde9 --- /dev/null +++ b/src/addons/mod/workshop/assessment/assesment-components.module.ts @@ -0,0 +1,39 @@ +// (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. + +import { AddonModWorkshopAssessmentStrategyCommentsComponent } from './comments/component/comments'; +import { AddonModWorkshopAssessmentStrategyAccumulativeComponent } from './accumulative/component/accumulative'; +import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from './numerrors/component/numerrors'; +import { AddonModWorkshopAssessmentStrategyRubricComponent } from './rubric/component/rubric'; +import { NgModule } from '@angular/core'; +import { CoreSharedModule } from '@/core/shared.module'; + +@NgModule({ + imports: [ + CoreSharedModule, + ], + declarations: [ + AddonModWorkshopAssessmentStrategyAccumulativeComponent, + AddonModWorkshopAssessmentStrategyCommentsComponent, + AddonModWorkshopAssessmentStrategyNumErrorsComponent, + AddonModWorkshopAssessmentStrategyRubricComponent, + ], + exports: [ + AddonModWorkshopAssessmentStrategyAccumulativeComponent, + AddonModWorkshopAssessmentStrategyCommentsComponent, + AddonModWorkshopAssessmentStrategyNumErrorsComponent, + AddonModWorkshopAssessmentStrategyRubricComponent, + ], +}) +export class AddonModWorkshopAssessmentComponentsModule {} diff --git a/src/addons/mod/workshop/assessment/comments/comments.module.ts b/src/addons/mod/workshop/assessment/comments/comments.module.ts index cb56fc9cc67..33d72d4b505 100644 --- a/src/addons/mod/workshop/assessment/comments/comments.module.ts +++ b/src/addons/mod/workshop/assessment/comments/comments.module.ts @@ -15,13 +15,9 @@ import { CoreSharedModule } from '@/core/shared.module'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyCommentsComponent } from './component/comments'; -import { AddonModWorkshopAssessmentStrategyCommentsHandler } from './services/handler'; +import { getAssessmentStrategyHandlerInstance } from './services/handler'; @NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyCommentsComponent, - ], imports: [ CoreSharedModule, ], @@ -30,14 +26,9 @@ import { AddonModWorkshopAssessmentStrategyCommentsHandler } from './services/ha provide: APP_INITIALIZER, multi: true, useValue: () => { - AddonWorkshopAssessmentStrategyDelegate.registerHandler( - AddonModWorkshopAssessmentStrategyCommentsHandler.instance, - ); + AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance()); }, }, ], - exports: [ - AddonModWorkshopAssessmentStrategyCommentsComponent, - ], }) export class AddonModWorkshopAssessmentStrategyCommentsModule {} diff --git a/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts new file mode 100644 index 00000000000..702c83393d9 --- /dev/null +++ b/src/addons/mod/workshop/assessment/comments/services/handler-lazy.ts @@ -0,0 +1,124 @@ +// (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. + +import { + AddonModWorkshopAssessmentStrategyFieldErrors, +} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; +import { + AddonModWorkshopGetAssessmentFormDefinitionData, + AddonModWorkshopGetAssessmentFormFieldsParsedData, +} from '@addons/mod/workshop/services/workshop'; +import { Injectable, Type } from '@angular/core'; +import { makeSingleton, Translate } from '@singletons'; +import { CoreFormFields } from '@singletons/form'; +import { AddonModWorkshopAssessmentStrategyCommentsComponent } from '../component/comments'; +import { AddonModWorkshopAssessmentStrategyCommentsHandlerService } from './handler'; + +/** + * Handler for comments assessment strategy plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopAssessmentStrategyCommentsHandlerLazyService + extends AddonModWorkshopAssessmentStrategyCommentsHandlerService + implements AddonWorkshopAssessmentStrategyHandler { + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + + /** + * @inheritdoc + */ + getComponent(): Type { + return AddonModWorkshopAssessmentStrategyCommentsComponent; + } + + /** + * @inheritdoc + */ + async getOriginalValues( + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + + form.fields.forEach((field, n) => { + field.dimtitle = Translate.instant('addon.mod_workshop_assessment_comments.dimensionnumber', { $a: field.number }); + + if (!form.current[n]) { + form.current[n] = {}; + } + + originalValues[n] = {}; + originalValues[n].peercomment = form.current[n].peercomment || ''; + originalValues[n].number = field.number; // eslint-disable-line id-blacklist + }); + + return originalValues; + } + + /** + * @inheritdoc + */ + hasDataChanged( + originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + ): boolean { + for (const x in originalValues) { + if (originalValues[x].peercomment != currentValues[x].peercomment) { + return true; + } + } + + return false; + } + + /** + * @inheritdoc + */ + async prepareAssessmentData( + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const data: CoreFormFields = {}; + const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; + let hasErrors = false; + + form.fields.forEach((field, idx) => { + if (idx < form.dimenssionscount) { + if (currentValues[idx].peercomment) { + data['peercomment__idx_' + idx] = currentValues[idx].peercomment; + } else { + errors['peercomment_' + idx] = Translate.instant('core.err_required'); + hasErrors = true; + } + + data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; + data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); + } + }); + + if (hasErrors) { + throw errors; + } + + return data; + } + +} +export const AddonModWorkshopAssessmentStrategyCommentsHandler = + makeSingleton(AddonModWorkshopAssessmentStrategyCommentsHandlerLazyService); diff --git a/src/addons/mod/workshop/assessment/comments/services/handler.ts b/src/addons/mod/workshop/assessment/comments/services/handler.ts index cca1ac768a1..a8b08cc8f19 100644 --- a/src/addons/mod/workshop/assessment/comments/services/handler.ts +++ b/src/addons/mod/workshop/assessment/comments/services/handler.ts @@ -12,113 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - AddonModWorkshopAssessmentStrategyFieldErrors, -} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { asyncInstance } from '@/core/utils/async-instance'; import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { - AddonModWorkshopGetAssessmentFormDefinitionData, - AddonModWorkshopGetAssessmentFormFieldsParsedData, -} from '@addons/mod/workshop/services/workshop'; -import { Injectable, Type } from '@angular/core'; -import { makeSingleton, Translate } from '@singletons'; -import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyCommentsComponent } from '../component/comments'; - -/** - * Handler for comments assessment strategy plugin. - */ -@Injectable({ providedIn: 'root' }) -export class AddonModWorkshopAssessmentStrategyCommentsHandlerService implements AddonWorkshopAssessmentStrategyHandler { - - name = 'AddonModWorkshopAssessmentStrategyComments'; - strategyName = 'comments'; - - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getComponent(): Type { - return AddonModWorkshopAssessmentStrategyCommentsComponent; - } - - /** - * @inheritdoc - */ - async getOriginalValues( - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_NAME, + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_STRATEGY_NAME, +} from '@addons/mod/workshop/assessment/constants'; - form.fields.forEach((field, n) => { - field.dimtitle = Translate.instant('addon.mod_workshop_assessment_comments.dimensionnumber', { $a: field.number }); +export class AddonModWorkshopAssessmentStrategyCommentsHandlerService { - if (!form.current[n]) { - form.current[n] = {}; - } + name = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_NAME; + strategyName = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_STRATEGY_NAME; - originalValues[n] = {}; - originalValues[n].peercomment = form.current[n].peercomment || ''; - originalValues[n].number = field.number; // eslint-disable-line id-blacklist - }); - - return originalValues; - } - - /** - * @inheritdoc - */ - hasDataChanged( - originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - ): boolean { - for (const x in originalValues) { - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * @inheritdoc - */ - async prepareAssessmentData( - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const data: CoreFormFields = {}; - const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - if (currentValues[idx].peercomment) { - data['peercomment__idx_' + idx] = currentValues[idx].peercomment; - } else { - errors['peercomment_' + idx] = Translate.instant('core.err_required'); - hasErrors = true; - } +} - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - } - }); +/** + * Get assessment strategy handler instance. + * + * @returns Assessment strategy handler. + */ +export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopAssessmentStrategyCommentsHandler } = await import('./handler-lazy'); - if (hasErrors) { - throw errors; - } + return AddonModWorkshopAssessmentStrategyCommentsHandler.instance; + }); - return data; - } + lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyCommentsHandlerService()); + return lazyHandler; } -export const AddonModWorkshopAssessmentStrategyCommentsHandler = - makeSingleton(AddonModWorkshopAssessmentStrategyCommentsHandlerService); diff --git a/src/addons/mod/workshop/assessment/constants.ts b/src/addons/mod/workshop/assessment/constants.ts new file mode 100644 index 00000000000..5fcbbed5597 --- /dev/null +++ b/src/addons/mod/workshop/assessment/constants.ts @@ -0,0 +1,25 @@ +// (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 ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_NAME = 'AddonModWorkshopAssessmentStrategyAccumulative'; +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_ACCUMULATIVE_STRATEGY_NAME = 'accumulative'; + +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_NAME = 'AddonModWorkshopAssessmentStrategyComments'; +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_COMMENTS_STRATEGY_NAME = 'comments'; + +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_NAME = 'AddonModWorkshopAssessmentStrategyNumErrors'; +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_STRATEGY_NAME = 'numerrors'; + +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_NAME = 'AddonModWorkshopAssessmentStrategyRubric'; +export const ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_STRATEGY_NAME = 'rubric'; diff --git a/src/addons/mod/workshop/assessment/numerrors/numerrors.module.ts b/src/addons/mod/workshop/assessment/numerrors/numerrors.module.ts index 7d2591c2a04..3fdcc9df2f6 100644 --- a/src/addons/mod/workshop/assessment/numerrors/numerrors.module.ts +++ b/src/addons/mod/workshop/assessment/numerrors/numerrors.module.ts @@ -15,13 +15,9 @@ import { CoreSharedModule } from '@/core/shared.module'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from './component/numerrors'; -import { AddonModWorkshopAssessmentStrategyNumErrorsHandler } from './services/handler'; +import { getAssessmentStrategyHandlerInstance } from './services/handler'; @NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyNumErrorsComponent, - ], imports: [ CoreSharedModule, ], @@ -30,14 +26,9 @@ import { AddonModWorkshopAssessmentStrategyNumErrorsHandler } from './services/h provide: APP_INITIALIZER, multi: true, useValue: () => { - AddonWorkshopAssessmentStrategyDelegate.registerHandler( - AddonModWorkshopAssessmentStrategyNumErrorsHandler.instance, - ); + AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance()); }, }, ], - exports: [ - AddonModWorkshopAssessmentStrategyNumErrorsComponent, - ], }) export class AddonModWorkshopAssessmentStrategyNumErrorsModule {} diff --git a/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts new file mode 100644 index 00000000000..6ec33f06542 --- /dev/null +++ b/src/addons/mod/workshop/assessment/numerrors/services/handler-lazy.ts @@ -0,0 +1,132 @@ +// (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. + +import { + AddonModWorkshopAssessmentStrategyFieldErrors, +} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; +import { + AddonModWorkshopGetAssessmentFormDefinitionData, + AddonModWorkshopGetAssessmentFormFieldsParsedData, +} from '@addons/mod/workshop/services/workshop'; +import { Injectable, Type } from '@angular/core'; +import { Translate, makeSingleton } from '@singletons'; +import { CoreFormFields } from '@singletons/form'; +import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from '../component/numerrors'; +import { AddonModWorkshopAssessmentStrategyNumErrorsHandlerService } from './handler'; + +/** + * Handler for numerrors assessment strategy plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerLazyService + extends AddonModWorkshopAssessmentStrategyNumErrorsHandlerService + implements AddonWorkshopAssessmentStrategyHandler { + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + + /** + * @inheritdoc + */ + getComponent(): Type { + return AddonModWorkshopAssessmentStrategyNumErrorsComponent; + } + + /** + * @inheritdoc + */ + async getOriginalValues( + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + + form.fields.forEach((field, n) => { + field.dimtitle = Translate.instant('addon.mod_workshop_assessment_numerrors.dimensionnumber', { $a: field.number }); + + if (!form.current[n]) { + form.current[n] = {}; + } + + originalValues[n] = {}; + originalValues[n].peercomment = form.current[n].peercomment || ''; + originalValues[n].number = field.number; // eslint-disable-line id-blacklist + originalValues[n].grade = form.current[n].grade || ''; + }); + + return originalValues; + } + + /** + * @inheritdoc + */ + hasDataChanged( + originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + ): boolean { + for (const x in originalValues) { + if (originalValues[x].grade != currentValues[x].grade) { + return true; + } + if (originalValues[x].peercomment != currentValues[x].peercomment) { + return true; + } + } + + return false; + } + + /** + * @inheritdoc + */ + async prepareAssessmentData( + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const data: CoreFormFields = {}; + const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; + let hasErrors = false; + + form.fields.forEach((field, idx) => { + if (idx < form.dimenssionscount) { + const grade = parseInt(String(currentValues[idx].grade), 10); + if (!isNaN(grade) && (grade == 1 || grade == -1)) { + data['grade__idx_' + idx] = grade; + } else { + errors['grade_' + idx] = Translate.instant('core.required'); + hasErrors = true; + } + + data['peercomment__idx_' + idx] = currentValues[idx].peercomment ?? ''; + + data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; + data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); + data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; + } + }); + + if (hasErrors) { + throw errors; + } + + return data; + } + +} +export const AddonModWorkshopAssessmentStrategyNumErrorsHandler = + makeSingleton(AddonModWorkshopAssessmentStrategyNumErrorsHandlerLazyService); diff --git a/src/addons/mod/workshop/assessment/numerrors/services/handler.ts b/src/addons/mod/workshop/assessment/numerrors/services/handler.ts index ce7e5dc3911..7e946a41f1b 100644 --- a/src/addons/mod/workshop/assessment/numerrors/services/handler.ts +++ b/src/addons/mod/workshop/assessment/numerrors/services/handler.ts @@ -12,121 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - AddonModWorkshopAssessmentStrategyFieldErrors, -} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { asyncInstance } from '@/core/utils/async-instance'; import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { - AddonModWorkshopGetAssessmentFormDefinitionData, - AddonModWorkshopGetAssessmentFormFieldsParsedData, -} from '@addons/mod/workshop/services/workshop'; -import { Injectable, Type } from '@angular/core'; -import { Translate, makeSingleton } from '@singletons'; -import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyNumErrorsComponent } from '../component/numerrors'; - -/** - * Handler for numerrors assessment strategy plugin. - */ -@Injectable({ providedIn: 'root' }) -export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerService implements AddonWorkshopAssessmentStrategyHandler { - - name = 'AddonModWorkshopAssessmentStrategyNumErrors'; - strategyName = 'numerrors'; - - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getComponent(): Type { - return AddonModWorkshopAssessmentStrategyNumErrorsComponent; - } - - /** - * @inheritdoc - */ - async getOriginalValues( - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; - - form.fields.forEach((field, n) => { - field.dimtitle = Translate.instant('addon.mod_workshop_assessment_numerrors.dimensionnumber', { $a: field.number }); + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_NAME, + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_STRATEGY_NAME, +} from '@addons/mod/workshop/assessment/constants'; - if (!form.current[n]) { - form.current[n] = {}; - } +export class AddonModWorkshopAssessmentStrategyNumErrorsHandlerService { - originalValues[n] = {}; - originalValues[n].peercomment = form.current[n].peercomment || ''; - originalValues[n].number = field.number; // eslint-disable-line id-blacklist - originalValues[n].grade = form.current[n].grade || ''; - }); + name = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_NAME; + strategyName = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_NUMERRORS_STRATEGY_NAME; - return originalValues; - } - - /** - * @inheritdoc - */ - hasDataChanged( - originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - ): boolean { - for (const x in originalValues) { - if (originalValues[x].grade != currentValues[x].grade) { - return true; - } - if (originalValues[x].peercomment != currentValues[x].peercomment) { - return true; - } - } - - return false; - } - - /** - * @inheritdoc - */ - async prepareAssessmentData( - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const data: CoreFormFields = {}; - const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const grade = parseInt(String(currentValues[idx].grade), 10); - if (!isNaN(grade) && (grade == 1 || grade == -1)) { - data['grade__idx_' + idx] = grade; - } else { - errors['grade_' + idx] = Translate.instant('core.required'); - hasErrors = true; - } - - data['peercomment__idx_' + idx] = currentValues[idx].peercomment ?? ''; +} - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - data['weight__idx_' + idx] = parseInt(field.weight, 10) || 0; - } - }); +/** + * Get assessment strategy handler instance. + * + * @returns Assessment strategy handler. + */ +export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopAssessmentStrategyNumErrorsHandler } = await import('./handler-lazy'); - if (hasErrors) { - throw errors; - } + return AddonModWorkshopAssessmentStrategyNumErrorsHandler.instance; + }); - return data; - } + lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyNumErrorsHandlerService()); + return lazyHandler; } -export const AddonModWorkshopAssessmentStrategyNumErrorsHandler = - makeSingleton(AddonModWorkshopAssessmentStrategyNumErrorsHandlerService); diff --git a/src/addons/mod/workshop/assessment/rubric/rubric.module.ts b/src/addons/mod/workshop/assessment/rubric/rubric.module.ts index 26ef017117c..62328560e70 100644 --- a/src/addons/mod/workshop/assessment/rubric/rubric.module.ts +++ b/src/addons/mod/workshop/assessment/rubric/rubric.module.ts @@ -15,13 +15,9 @@ import { CoreSharedModule } from '@/core/shared.module'; import { APP_INITIALIZER, NgModule } from '@angular/core'; import { AddonWorkshopAssessmentStrategyDelegate } from '../../services/assessment-strategy-delegate'; -import { AddonModWorkshopAssessmentStrategyRubricComponent } from './component/rubric'; -import { AddonModWorkshopAssessmentStrategyRubricHandler } from './services/handler'; +import { getAssessmentStrategyHandlerInstance } from './services/handler'; @NgModule({ - declarations: [ - AddonModWorkshopAssessmentStrategyRubricComponent, - ], imports: [ CoreSharedModule, ], @@ -30,14 +26,9 @@ import { AddonModWorkshopAssessmentStrategyRubricHandler } from './services/hand provide: APP_INITIALIZER, multi: true, useValue: () => { - AddonWorkshopAssessmentStrategyDelegate.registerHandler( - AddonModWorkshopAssessmentStrategyRubricHandler.instance, - ); + AddonWorkshopAssessmentStrategyDelegate.registerHandler(getAssessmentStrategyHandlerInstance()); }, }, ], - exports: [ - AddonModWorkshopAssessmentStrategyRubricComponent, - ], }) export class AddonModWorkshopAssessmentStrategyRubricModule {} diff --git a/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts b/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts new file mode 100644 index 00000000000..c001975f1da --- /dev/null +++ b/src/addons/mod/workshop/assessment/rubric/services/handler-lazy.ts @@ -0,0 +1,125 @@ +// (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. + +import { + AddonModWorkshopAssessmentStrategyFieldErrors, +} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; +import { + AddonModWorkshopGetAssessmentFormDefinitionData, + AddonModWorkshopGetAssessmentFormFieldsParsedData, +} from '@addons/mod/workshop/services/workshop'; +import { Injectable, Type } from '@angular/core'; +import { Translate, makeSingleton } from '@singletons'; +import { CoreFormFields } from '@singletons/form'; +import { AddonModWorkshopAssessmentStrategyRubricComponent } from '../component/rubric'; +import { AddonModWorkshopAssessmentStrategyRubricHandlerService } from './handler'; + +/** + * Handler for rubric assessment strategy plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopAssessmentStrategyRubricHandlerLazyService + extends AddonModWorkshopAssessmentStrategyRubricHandlerService + implements AddonWorkshopAssessmentStrategyHandler { + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + + /** + * @inheritdoc + */ + getComponent(): Type { + return AddonModWorkshopAssessmentStrategyRubricComponent; + } + + /** + * @inheritdoc + */ + async getOriginalValues( + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + + form.fields.forEach((field, n) => { + field.dimtitle = Translate.instant('addon.mod_workshop_assessment_rubric.dimensionnumber', { $a: field.number }); + + if (!form.current[n]) { + form.current[n] = {}; + } + + originalValues[n] = {}; + originalValues[n].chosenlevelid = form.current[n].chosenlevelid || ''; + originalValues[n].number = field.number; // eslint-disable-line id-blacklist + }); + + return originalValues; + } + + /** + * @inheritdoc + */ + hasDataChanged( + originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + ): boolean { + for (const x in originalValues) { + if (originalValues[x].chosenlevelid != (currentValues[x].chosenlevelid || '')) { + return true; + } + } + + return false; + } + + /** + * @inheritdoc + */ + async prepareAssessmentData( + currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], + form: AddonModWorkshopGetAssessmentFormDefinitionData, + ): Promise { + const data: CoreFormFields = {}; + const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; + let hasErrors = false; + + form.fields.forEach((field, idx) => { + if (idx < form.dimenssionscount) { + const id = parseInt(currentValues[idx].chosenlevelid, 10); + if (!isNaN(id) && id >= 0) { + data['chosenlevelid__idx_' + idx] = id; + } else { + errors['chosenlevelid_' + idx] = Translate.instant('addon.mod_workshop_assessment_rubric.mustchooseone'); + hasErrors = true; + } + + data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; + data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); + } + }); + + if (hasErrors) { + throw errors; + } + + return data; + } + +} +export const AddonModWorkshopAssessmentStrategyRubricHandler = + makeSingleton(AddonModWorkshopAssessmentStrategyRubricHandlerLazyService); diff --git a/src/addons/mod/workshop/assessment/rubric/services/handler.ts b/src/addons/mod/workshop/assessment/rubric/services/handler.ts index e824862b0c6..aa45828f84c 100644 --- a/src/addons/mod/workshop/assessment/rubric/services/handler.ts +++ b/src/addons/mod/workshop/assessment/rubric/services/handler.ts @@ -12,114 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - AddonModWorkshopAssessmentStrategyFieldErrors, -} from '@addons/mod/workshop/components/assessment-strategy/assessment-strategy'; +import { asyncInstance } from '@/core/utils/async-instance'; import { AddonWorkshopAssessmentStrategyHandler } from '@addons/mod/workshop/services/assessment-strategy-delegate'; import { - AddonModWorkshopGetAssessmentFormDefinitionData, - AddonModWorkshopGetAssessmentFormFieldsParsedData, -} from '@addons/mod/workshop/services/workshop'; -import { Injectable, Type } from '@angular/core'; -import { Translate, makeSingleton } from '@singletons'; -import { CoreFormFields } from '@singletons/form'; -import { AddonModWorkshopAssessmentStrategyRubricComponent } from '../component/rubric'; - -/** - * Handler for rubric assessment strategy plugin. - */ -@Injectable({ providedIn: 'root' }) -export class AddonModWorkshopAssessmentStrategyRubricHandlerService implements AddonWorkshopAssessmentStrategyHandler { - - name = 'AddonModWorkshopAssessmentStrategyRubric'; - strategyName = 'rubric'; - - /** - * @inheritdoc - */ - async isEnabled(): Promise { - return true; - } - - /** - * @inheritdoc - */ - getComponent(): Type { - return AddonModWorkshopAssessmentStrategyRubricComponent; - } - - /** - * @inheritdoc - */ - async getOriginalValues( - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[] = []; + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_NAME, + ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_STRATEGY_NAME, +} from '@addons/mod/workshop/assessment/constants'; - form.fields.forEach((field, n) => { - field.dimtitle = Translate.instant('addon.mod_workshop_assessment_rubric.dimensionnumber', { $a: field.number }); +export class AddonModWorkshopAssessmentStrategyRubricHandlerService { - if (!form.current[n]) { - form.current[n] = {}; - } + name = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_NAME; + strategyName = ADDON_MOD_WORKSHOP_ASSESSMENT_STRATEGY_RUBRIC_STRATEGY_NAME; - originalValues[n] = {}; - originalValues[n].chosenlevelid = form.current[n].chosenlevelid || ''; - originalValues[n].number = field.number; // eslint-disable-line id-blacklist - }); - - return originalValues; - } - - /** - * @inheritdoc - */ - hasDataChanged( - originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - ): boolean { - for (const x in originalValues) { - if (originalValues[x].chosenlevelid != (currentValues[x].chosenlevelid || '')) { - return true; - } - } - - return false; - } - - /** - * @inheritdoc - */ - async prepareAssessmentData( - currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - form: AddonModWorkshopGetAssessmentFormDefinitionData, - ): Promise { - const data: CoreFormFields = {}; - const errors: AddonModWorkshopAssessmentStrategyFieldErrors = {}; - let hasErrors = false; - - form.fields.forEach((field, idx) => { - if (idx < form.dimenssionscount) { - const id = parseInt(currentValues[idx].chosenlevelid, 10); - if (!isNaN(id) && id >= 0) { - data['chosenlevelid__idx_' + idx] = id; - } else { - errors['chosenlevelid_' + idx] = Translate.instant('addon.mod_workshop_assessment_rubric.mustchooseone'); - hasErrors = true; - } +} - data['gradeid__idx_' + idx] = parseInt(form.current[idx].gradeid, 10) || 0; - data['dimensionid__idx_' + idx] = parseInt(field.dimensionid, 10); - } - }); +/** + * Get assessment strategy handler instance. + * + * @returns Assessment strategy handler. + */ +export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentStrategyHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopAssessmentStrategyRubricHandler } = await import('./handler-lazy'); - if (hasErrors) { - throw errors; - } + return AddonModWorkshopAssessmentStrategyRubricHandler.instance; + }); - return data; - } + lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyRubricHandlerService()); + return lazyHandler; } -export const AddonModWorkshopAssessmentStrategyRubricHandler = - makeSingleton(AddonModWorkshopAssessmentStrategyRubricHandlerService); diff --git a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts index 90afcbac221..2c53eea33d4 100644 --- a/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addons/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -38,6 +38,7 @@ import { } from '../../services/workshop'; import { AddonModWorkshopHelper, AddonModWorkshopSubmissionAssessmentWithFormData } from '../../services/workshop-helper'; import { AddonModWorkshopOffline } from '../../services/workshop-offline'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Component that displays workshop assessment strategy form. @@ -75,7 +76,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe feedbackControl = new FormControl(); overallFeedkback = false; overallFeedkbackRequired = false; - component = AddonModWorkshopProvider.COMPONENT; + component = ADDON_MOD_WORKSHOP_COMPONENT; componentId?: number; weights: number[] = []; weight?: number; @@ -110,7 +111,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe this.data.moduleId = this.workshop.coursemodule; this.data.courseId = this.workshop.course; - this.componentClass = AddonWorkshopAssessmentStrategyDelegate.getComponentForPlugin(this.strategy); + this.componentClass = await AddonWorkshopAssessmentStrategyDelegate.getComponentForPlugin(this.strategy); if (this.componentClass) { this.overallFeedkback = this.workshop.overallfeedbackmode != AddonModWorkshopOverallFeedbackMode.DISABLED; this.overallFeedkbackRequired = @@ -128,7 +129,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe // Check if rich text editor is enabled. if (this.edit) { // Block the workshop. - CoreSync.blockOperation(AddonModWorkshopProvider.COMPONENT, this.workshop.id); + CoreSync.blockOperation(ADDON_MOD_WORKSHOP_COMPONENT, this.workshop.id); } try { @@ -232,7 +233,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe this.originalData.selectedValues = CoreUtils.clone(this.data.selectedValues); if (this.edit) { CoreFileSession.setFiles( - AddonModWorkshopProvider.COMPONENT, + ADDON_MOD_WORKSHOP_COMPONENT, this.workshop.id + '_' + this.assessmentId, this.data.assessment.feedbackattachmentfiles, ); @@ -265,7 +266,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe // Compare feedback files. const files = CoreFileSession.getFiles( - AddonModWorkshopProvider.COMPONENT, + ADDON_MOD_WORKSHOP_COMPONENT, this.workshop.id + '_' + this.assessmentId, ) || []; if (CoreFileUploader.areFileListDifferent(files, this.originalData.files)) { @@ -290,7 +291,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe } const files = CoreFileSession.getFiles( - AddonModWorkshopProvider.COMPONENT, + ADDON_MOD_WORKSHOP_COMPONENT, this.workshop.id + '_' + this.assessmentId, ) || []; diff --git a/src/addons/mod/workshop/components/components.module.ts b/src/addons/mod/workshop/components/components.module.ts index cc3cb1961b1..46f93d91fde 100644 --- a/src/addons/mod/workshop/components/components.module.ts +++ b/src/addons/mod/workshop/components/components.module.ts @@ -18,6 +18,7 @@ import { AddonModWorkshopSubmissionComponent } from './submission/submission'; import { CoreCourseComponentsModule } from '@features/course/components/components.module'; import { CoreEditorComponentsModule } from '@features/editor/components/components.module'; import { CoreSharedModule } from '@/core/shared.module'; +import { AddonModWorkshopAssessmentComponentsModule } from '@addons/mod/workshop/assessment/assesment-components.module'; import { AddonModWorkshopPhaseInfoComponent } from './phase/phase'; import { AddonModWorkshopAssessmentComponent } from './assessment/assessment'; import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strategy/assessment-strategy'; @@ -34,6 +35,7 @@ import { AddonModWorkshopAssessmentStrategyComponent } from './assessment-strate CoreSharedModule, CoreCourseComponentsModule, CoreEditorComponentsModule, + AddonModWorkshopAssessmentComponentsModule, ], exports: [ AddonModWorkshopIndexComponent, diff --git a/src/addons/mod/workshop/components/index/index.ts b/src/addons/mod/workshop/components/index/index.ts index 8b7c54776f9..c8a255e7a69 100644 --- a/src/addons/mod/workshop/components/index/index.ts +++ b/src/addons/mod/workshop/components/index/index.ts @@ -25,7 +25,6 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { Subscription } from 'rxjs'; -import { AddonModWorkshopModuleHandlerService } from '../../services/handlers/module'; import { AddonModWorkshopProvider, AddonModWorkshopPhase, @@ -53,6 +52,7 @@ import { AddonModWorkshopSyncResult, } from '../../services/workshop-sync'; import { AddonModWorkshopPhaseInfoComponent } from '../phase/phase'; +import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME } from '@addons/mod/workshop/constants'; /** * Component that displays a workshop index page. @@ -65,7 +65,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity @Input() group = 0; - component = AddonModWorkshopProvider.COMPONENT; + component = ADDON_MOD_WORKSHOP_COMPONENT; pluginName = 'workshop'; workshop?: AddonModWorkshopData; @@ -379,7 +379,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity const submissionId = this.submission?.id || 0; CoreNavigator.navigateToSitePath( - AddonModWorkshopModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.module.id}/${submissionId}/edit`, + `${ADDON_MOD_WORKSHOP_PAGE_NAME}/${this.courseId}/${this.module.id}/${submissionId}/edit`, { params }, ); diff --git a/src/addons/mod/workshop/components/submission/submission.ts b/src/addons/mod/workshop/components/submission/submission.ts index 1e3d0c766a0..8d49a967c5d 100644 --- a/src/addons/mod/workshop/components/submission/submission.ts +++ b/src/addons/mod/workshop/components/submission/submission.ts @@ -19,9 +19,7 @@ import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; import { AddonModWorkshopSubmissionPage } from '../../pages/submission/submission'; -import { AddonModWorkshopModuleHandlerService } from '../../services/handlers/module'; import { - AddonModWorkshopProvider, AddonModWorkshopPhase, AddonModWorkshopData, AddonModWorkshopGetWorkshopAccessInformationWSResponse, @@ -32,6 +30,7 @@ import { AddonModWorkshopSubmissionDataWithOfflineData, } from '../../services/workshop-helper'; import { AddonModWorkshopOffline } from '../../services/workshop-offline'; +import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME } from '@addons/mod/workshop/constants'; /** * Component that displays workshop submission. @@ -51,7 +50,7 @@ export class AddonModWorkshopSubmissionComponent implements OnInit { @Input() assessment?: AddonModWorkshopSubmissionAssessmentWithFormData; @Input() summary = false; - component = AddonModWorkshopProvider.COMPONENT; + component = ADDON_MOD_WORKSHOP_COMPONENT; componentId?: number; userId: number; loaded = false; @@ -128,7 +127,7 @@ export class AddonModWorkshopSubmissionComponent implements OnInit { }; CoreNavigator.navigateToSitePath( - AddonModWorkshopModuleHandlerService.PAGE_NAME + `/${this.courseId}/${this.module.id}/${this.submission.id}`, + `${ADDON_MOD_WORKSHOP_PAGE_NAME}/${this.courseId}/${this.module.id}/${this.submission.id}`, { params }, ); diff --git a/src/addons/mod/workshop/constants.ts b/src/addons/mod/workshop/constants.ts new file mode 100644 index 00000000000..a1de919915d --- /dev/null +++ b/src/addons/mod/workshop/constants.ts @@ -0,0 +1,41 @@ +// (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 ADDON_MOD_WORKSHOP_COMPONENT = 'mmaModWorkshop'; + +// Routing. +export const ADDON_MOD_WORKSHOP_PAGE_NAME = 'mod_workshop'; + +// Handlers. +export const ADDON_MOD_WORKSHOP_PREFETCH_NAME = 'AddonModWorkshop'; +export const ADDON_MOD_WORKSHOP_PREFETCH_MODNAME = 'workshop'; +export const ADDON_MOD_WORKSHOP_PREFETCH_COMPONENT = ADDON_MOD_WORKSHOP_COMPONENT; +export const ADDON_MOD_WORKSHOP_PREFETCH_UPDATE_NAMES = new RegExp( + [ + '^configuration$', + '^.*files$', + '^completion', + '^gradeitems$', + '^outcomes$', + '^submissions$', + '^assessments$' + + '^assessmentgrades$', + '^usersubmissions$', + '^userassessments$', + '^userassessmentgrades$', + '^userassessmentgrades$', + ].join('|'), +); + +export const ADDON_MOD_WORKSHOP_SYNC_CRON_NAME = 'AddonModWorkshopSyncCronHandler'; diff --git a/src/addons/mod/workshop/pages/assessment/assessment.ts b/src/addons/mod/workshop/pages/assessment/assessment.ts index 973a16a0317..82592509b15 100644 --- a/src/addons/mod/workshop/pages/assessment/assessment.ts +++ b/src/addons/mod/workshop/pages/assessment/assessment.ts @@ -41,6 +41,7 @@ import { AddonModWorkshopOffline } from '../../services/workshop-offline'; import { AddonModWorkshopSyncProvider } from '../../services/workshop-sync'; import { CoreTime } from '@singletons/time'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Page that displays a workshop assessment. @@ -198,7 +199,7 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy, CanLea if (this.assessmentId && (this.access.canallocate || this.access.canoverridegrades)) { if (!this.isDestroyed) { // Block the workshop. - CoreSync.blockOperation(AddonModWorkshopProvider.COMPONENT, this.workshopId); + CoreSync.blockOperation(ADDON_MOD_WORKSHOP_COMPONENT, this.workshopId); } this.evaluating = true; @@ -413,7 +414,7 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy, CanLea this.syncObserver?.off(); // Restore original back functions. - CoreSync.unblockOperation(AddonModWorkshopProvider.COMPONENT, this.workshopId); + CoreSync.unblockOperation(ADDON_MOD_WORKSHOP_COMPONENT, this.workshopId); } } diff --git a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts index 4c7ea2d08d3..5d7b7373408 100644 --- a/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addons/mod/workshop/pages/edit-submission/edit-submission.ts @@ -41,6 +41,7 @@ import { import { AddonModWorkshopHelper, AddonModWorkshopSubmissionDataWithOfflineData } from '../../services/workshop-helper'; import { AddonModWorkshopOffline } from '../../services/workshop-offline'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Page that displays the workshop edit submission. @@ -59,7 +60,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy, Ca submission?: AddonModWorkshopSubmissionDataWithOfflineData; loaded = false; - component = AddonModWorkshopProvider.COMPONENT; + component = ADDON_MOD_WORKSHOP_COMPONENT; componentId!: number; editForm: FormGroup; // The form group. editorExtraParams: Record = {}; // Extra params to identify the draft. diff --git a/src/addons/mod/workshop/pages/submission/submission.ts b/src/addons/mod/workshop/pages/submission/submission.ts index faf95933b7a..bfb7c87b906 100644 --- a/src/addons/mod/workshop/pages/submission/submission.ts +++ b/src/addons/mod/workshop/pages/submission/submission.ts @@ -49,6 +49,7 @@ import { AddonModWorkshopOffline } from '../../services/workshop-offline'; import { AddonModWorkshopSyncProvider, AddonModWorkshopAutoSyncData } from '../../services/workshop-sync'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreTime } from '@singletons/time'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Page that displays a workshop submission. @@ -99,7 +100,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy, CanLea }; protected hasOffline = false; - protected component = AddonModWorkshopProvider.COMPONENT; + protected component = ADDON_MOD_WORKSHOP_COMPONENT; protected forceLeave = false; protected obsAssessmentSaved: CoreEventObserver; protected syncObserver: CoreEventObserver; diff --git a/src/addons/mod/workshop/services/assessment-strategy-delegate.ts b/src/addons/mod/workshop/services/assessment-strategy-delegate.ts index 913154672e6..f28f8556d0b 100644 --- a/src/addons/mod/workshop/services/assessment-strategy-delegate.ts +++ b/src/addons/mod/workshop/services/assessment-strategy-delegate.ts @@ -34,7 +34,7 @@ export interface AddonWorkshopAssessmentStrategyHandler extends CoreDelegateHand * @param injector Injector. * @returns The component (or promise resolved with component) to use, undefined if not found. */ - getComponent?(): Type; + getComponent?(): Promise> | Type; /** * Prepare original values to be shown and compared. @@ -58,7 +58,7 @@ export interface AddonWorkshopAssessmentStrategyHandler extends CoreDelegateHand hasDataChanged?( originalValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], currentValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[], - ): boolean; + ): Promise | boolean; /** * Prepare assessment data to be sent to the server depending on the strategy selected. @@ -102,7 +102,7 @@ export class AddonWorkshopAssessmentStrategyDelegateService extends CoreDelegate * @param workshopStrategy Assessment strategy name. * @returns The component, undefined if not found. */ - getComponentForPlugin(workshopStrategy: string): Type | undefined { + getComponentForPlugin(workshopStrategy: string): Promise> | Type | undefined { return this.executeFunctionOnEnabled(workshopStrategy, 'getComponent'); } diff --git a/src/addons/mod/workshop/services/handlers/index-link.ts b/src/addons/mod/workshop/services/handlers/index-link.ts index 3ad62aba97f..98469d0cbc5 100644 --- a/src/addons/mod/workshop/services/handlers/index-link.ts +++ b/src/addons/mod/workshop/services/handlers/index-link.ts @@ -15,7 +15,8 @@ import { Injectable } from '@angular/core'; import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler'; import { makeSingleton } from '@singletons'; -import { AddonModWorkshopProvider } from '../workshop'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; + /** * Handler to treat links to workshop. */ @@ -25,7 +26,7 @@ export class AddonModWorkshopIndexLinkHandlerService extends CoreContentLinksMod name = 'AddonModWorkshopLinkHandler'; constructor() { - super(AddonModWorkshopProvider.COMPONENT, 'workshop', 'w'); + super(ADDON_MOD_WORKSHOP_COMPONENT, 'workshop', 'w'); } } diff --git a/src/addons/mod/workshop/services/handlers/module.ts b/src/addons/mod/workshop/services/handlers/module.ts index 869c347cb9d..4ec4d181c02 100644 --- a/src/addons/mod/workshop/services/handlers/module.ts +++ b/src/addons/mod/workshop/services/handlers/module.ts @@ -13,11 +13,11 @@ // limitations under the License. import { CoreConstants, ModPurpose } from '@/core/constants'; +import { ADDON_MOD_WORKSHOP_PAGE_NAME } from '@addons/mod/workshop/constants'; import { Injectable, Type } from '@angular/core'; import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler'; import { CoreCourseModuleHandler } from '@features/course/services/module-delegate'; import { makeSingleton } from '@singletons'; -import { AddonModWorkshopIndexComponent } from '../../components/index'; /** * Handler to support workshop modules. @@ -25,11 +25,9 @@ import { AddonModWorkshopIndexComponent } from '../../components/index'; @Injectable({ providedIn: 'root' }) export class AddonModWorkshopModuleHandlerService extends CoreModuleHandlerBase implements CoreCourseModuleHandler { - static readonly PAGE_NAME = 'mod_workshop'; - name = 'AddonModWorkshop'; modName = 'workshop'; - protected pageName = AddonModWorkshopModuleHandlerService.PAGE_NAME; + protected pageName = ADDON_MOD_WORKSHOP_PAGE_NAME; supportedFeatures = { [CoreConstants.FEATURE_GROUPS]: true, @@ -47,6 +45,8 @@ export class AddonModWorkshopModuleHandlerService extends CoreModuleHandlerBase * @inheritdoc */ async getMainComponent(): Promise> { + const { AddonModWorkshopIndexComponent } = await import('@addons/mod/workshop/components/index'); + return AddonModWorkshopIndexComponent; } diff --git a/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts b/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts new file mode 100644 index 00000000000..b482275637e --- /dev/null +++ b/src/addons/mod/workshop/services/handlers/prefetch-lazy.ts @@ -0,0 +1,405 @@ +// (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. + +import { AddonModDataSyncResult } from '@addons/mod/data/services/data-sync'; +import { Injectable } from '@angular/core'; +import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; +import { CoreCourses } from '@features/courses/services/courses'; +import { CoreUser } from '@features/user/services/user'; +import { CoreFilepool } from '@services/filepool'; +import { CoreGroup, CoreGroups } from '@services/groups'; +import { CoreSites, CoreSitesReadingStrategy, CoreSitesCommonWSOptions } from '@services/sites'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreWSExternalFile, CoreWSFile } from '@services/ws'; +import { makeSingleton } from '@singletons'; +import { + AddonModWorkshop, + AddonModWorkshopPhase, + AddonModWorkshopGradesData, + AddonModWorkshopData, + AddonModWorkshopGetWorkshopAccessInformationWSResponse, +} from '../workshop'; +import { AddonModWorkshopHelper } from '../workshop-helper'; +import { AddonModWorkshopSync } from '../workshop-sync'; +import { AddonModWorkshopPrefetchHandlerService } from '@addons/mod/workshop/services/handlers/prefetch'; + +/** + * Handler to prefetch workshops. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopPrefetchHandlerLazyService extends AddonModWorkshopPrefetchHandlerService { + + /** + * @inheritdoc + */ + async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { + const info = await this.getWorkshopInfoHelper(module, courseId, { omitFail: true }); + + return info.files; + } + + /** + * Helper function to get all workshop info just once. + * + * @param module Module to get the files. + * @param courseId Course ID the module belongs to. + * @param options Other options. + * @returns Promise resolved with the info fetched. + */ + protected async getWorkshopInfoHelper( + module: CoreCourseAnyModuleData, + courseId: number, + options: AddonModWorkshopGetInfoOptions = {}, + ): Promise<{ workshop?: AddonModWorkshopData; groups: CoreGroup[]; files: CoreWSFile[]}> { + let groups: CoreGroup[] = []; + let files: CoreWSFile[] = []; + let workshop: AddonModWorkshopData; + let access: AddonModWorkshopGetWorkshopAccessInformationWSResponse | undefined; + + const modOptions = { + cmId: module.id, + ...options, // Include all options. + }; + + const site = await CoreSites.getSite(options.siteId); + options.siteId = options.siteId ?? site.getId(); + const userId = site.getUserId(); + + try { + workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, options); + } catch (error) { + if (options.omitFail) { + // Any error, return the info we have. + return { + groups: [], + files: [], + }; + } + + throw error; + } + + try { + files = this.getIntroFilesFromInstance(module, workshop); + files = files.concat(workshop.instructauthorsfiles || []).concat(workshop.instructreviewersfiles || []); + + access = await AddonModWorkshop.getWorkshopAccessInformation(workshop.id, modOptions); + if (access.canviewallsubmissions) { + const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); + if (!groupInfo.groups || groupInfo.groups.length == 0) { + groupInfo.groups = [{ id: 0, name: '' }]; + } + groups = groupInfo.groups; + } + + const phases = await AddonModWorkshop.getUserPlanPhases(workshop.id, modOptions); + + // Get submission phase info. + const submissionPhase = phases[AddonModWorkshopPhase.PHASE_SUBMISSION]; + const canSubmit = AddonModWorkshopHelper.canSubmit(workshop, access, submissionPhase.tasks); + const canAssess = AddonModWorkshopHelper.canAssess(workshop, access); + + const promises: Promise[] = []; + + if (canSubmit) { + promises.push(AddonModWorkshopHelper.getUserSubmission(workshop.id, { + userId, + cmId: module.id, + }).then((submission) => { + if (submission) { + files = files.concat(submission.contentfiles || []).concat(submission.attachmentfiles || []); + } + + return; + })); + } + + if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopPhase.PHASE_SUBMISSION) { + promises.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions).then(async (submissions) => { + + await Promise.all(submissions.map(async (submission) => { + files = files.concat(submission.contentfiles || []).concat(submission.attachmentfiles || []); + + const assessments = await AddonModWorkshop.getSubmissionAssessments(workshop.id, submission.id, { + cmId: module.id, + }); + + assessments.forEach((assessment) => { + files = files.concat(assessment.feedbackattachmentfiles) + .concat(assessment.feedbackcontentfiles); + }); + + if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { + await Promise.all(assessments.map((assessment) => + AddonModWorkshopHelper.getReviewerAssessmentById(workshop.id, assessment.id))); + } + })); + + return; + })); + } + + // Get assessment files. + if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { + promises.push(AddonModWorkshopHelper.getReviewerAssessments(workshop.id, modOptions).then((assessments) => { + assessments.forEach((assessment) => { + files = files.concat(assessment.feedbackattachmentfiles) + .concat(assessment.feedbackcontentfiles); + }); + + return; + })); + } + + await Promise.all(promises); + + return { + workshop, + groups, + files: files.filter((file) => file !== undefined), + }; + } catch (error) { + if (options.omitFail) { + // Any error, return the info we have. + return { + workshop, + groups, + files: files.filter((file) => file !== undefined), + }; + } + + throw error; + } + } + + /** + * @inheritdoc + */ + async invalidateContent(moduleId: number, courseId: number): Promise { + await AddonModWorkshop.invalidateContent(moduleId, courseId); + } + + /** + * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. + * + * @param module Module. + * @param courseId Course ID the module belongs to. + * @returns Whether the module can be downloaded. The promise should never be rejected. + */ + async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise { + const workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE, + }); + + const accessData = await AddonModWorkshop.getWorkshopAccessInformation(workshop.id, { cmId: module.id }); + + // Check if workshop is setup by phase. + return accessData.canswitchphase || workshop.phase > AddonModWorkshopPhase.PHASE_SETUP; + } + + /** + * @inheritdoc + */ + prefetch(module: CoreCourseAnyModuleData, courseId: number): Promise { + return this.prefetchPackage(module, courseId, (siteId) => this.prefetchWorkshop(module, courseId, siteId)); + } + + /** + * Retrieves all the grades reports for all the groups and then returns only unique grades. + * + * @param workshopId Workshop ID. + * @param groups Array of groups in the activity. + * @param cmId Module ID. + * @param siteId Site ID. If not defined, current site. + * @returns All unique entries. + */ + protected async getAllGradesReport( + workshopId: number, + groups: CoreGroup[], + cmId: number, + siteId: string, + ): Promise { + const promises: Promise[] = []; + + groups.forEach((group) => { + promises.push(AddonModWorkshop.fetchAllGradeReports(workshopId, { groupId: group.id, cmId, siteId })); + }); + + const grades = await Promise.all(promises); + const uniqueGrades: Record = {}; + + grades.forEach((groupGrades) => { + groupGrades.forEach((grade) => { + if (grade.submissionid) { + uniqueGrades[grade.submissionid] = grade; + } + }); + }); + + return CoreUtils.objectToArray(uniqueGrades); + } + + /** + * Prefetch a workshop. + * + * @param module The module object returned by WS. + * @param courseId Course ID the module belongs to. + * @param siteId Site ID. + * @returns Promise resolved when done. + */ + protected async prefetchWorkshop(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise { + const userIds: number[] = []; + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + + const site = await CoreSites.getSite(siteId); + const currentUserId = site.getUserId(); + + // Prefetch the workshop data. + const info = await this.getWorkshopInfoHelper(module, courseId, commonOptions); + if (!info.workshop) { + // It would throw an exception so it would not happen. + return; + } + + const workshop = info.workshop; + const promises: Promise[] = []; + const assessmentIds: number[] = []; + + promises.push(CoreFilepool.addFilesToQueue(siteId, info.files, this.component, module.id)); + + promises.push(AddonModWorkshop.getWorkshopAccessInformation(workshop.id, modOptions).then(async (access) => { + const phases = await AddonModWorkshop.getUserPlanPhases(workshop.id, modOptions); + + // Get submission phase info. + const submissionPhase = phases[AddonModWorkshopPhase.PHASE_SUBMISSION]; + const canSubmit = AddonModWorkshopHelper.canSubmit(workshop, access, submissionPhase.tasks); + const canAssess = AddonModWorkshopHelper.canAssess(workshop, access); + const promises2: Promise[] = []; + + if (canSubmit) { + promises2.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions)); + // Add userId to the profiles to prefetch. + userIds.push(currentUserId); + } + + let reportPromise: Promise = Promise.resolve(); + if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopPhase.PHASE_SUBMISSION) { + // eslint-disable-next-line promise/no-nesting + reportPromise = this.getAllGradesReport(workshop.id, info.groups, module.id, siteId).then((grades) => { + grades.forEach((grade) => { + userIds.push(grade.userid); + grade.submissiongradeoverby && userIds.push(grade.submissiongradeoverby); + + grade.reviewedby && grade.reviewedby.forEach((assessment) => { + userIds.push(assessment.userid); + assessmentIds[assessment.assessmentid] = assessment.assessmentid; + }); + + grade.reviewerof && grade.reviewerof.forEach((assessment) => { + userIds.push(assessment.userid); + assessmentIds[assessment.assessmentid] = assessment.assessmentid; + }); + }); + + return; + }); + } + + if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { + // Wait the report promise to finish to override assessments array if needed. + reportPromise = reportPromise.finally(async () => { + const revAssessments = await AddonModWorkshopHelper.getReviewerAssessments(workshop.id, { + userId: currentUserId, + cmId: module.id, + siteId, + }); + + let files: CoreWSExternalFile[] = []; // Files in each submission. + + revAssessments.forEach((assessment) => { + if (assessment.submission?.authorid == currentUserId) { + promises.push(AddonModWorkshop.getAssessment( + workshop.id, + assessment.id, + modOptions, + )); + } + userIds.push(assessment.reviewerid); + userIds.push(assessment.gradinggradeoverby); + assessmentIds[assessment.id] = assessment.id; + + files = files.concat(assessment.submission?.attachmentfiles || []) + .concat(assessment.submission?.contentfiles || []); + }); + + await CoreFilepool.addFilesToQueue(siteId, files, this.component, module.id); + }); + } + + reportPromise = reportPromise.finally(() => { + if (assessmentIds.length > 0) { + return Promise.all(assessmentIds.map((assessmentId) => + AddonModWorkshop.getAssessmentForm(workshop.id, assessmentId, modOptions))); + } + }); + promises2.push(reportPromise); + + if (workshop.phase == AddonModWorkshopPhase.PHASE_CLOSED) { + promises2.push(AddonModWorkshop.getGrades(workshop.id, modOptions)); + if (access.canviewpublishedsubmissions) { + promises2.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions)); + } + } + + await Promise.all(promises2); + + return; + })); + + // Add Basic Info to manage links. + promises.push(CoreCourse.getModuleBasicInfoByInstance(workshop.id, 'workshop', { siteId })); + promises.push(CoreCourse.getModuleBasicGradeInfo(module.id, siteId)); + + // Get course data, needed to determine upload max size if it's configured to be course limit. + promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); + + await Promise.all(promises); + + // Prefetch user profiles. + await CoreUser.prefetchProfiles(userIds, courseId, siteId); + } + + /** + * @inheritdoc + */ + async sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise { + return AddonModWorkshopSync.syncWorkshop(module.instance, siteId); + } + +} +export const AddonModWorkshopPrefetchHandler = makeSingleton(AddonModWorkshopPrefetchHandlerLazyService); + +/** + * Options to pass to getWorkshopInfoHelper. + */ +export type AddonModWorkshopGetInfoOptions = CoreSitesCommonWSOptions & { + omitFail?: boolean; // True to always return even if fails. +}; diff --git a/src/addons/mod/workshop/services/handlers/prefetch.ts b/src/addons/mod/workshop/services/handlers/prefetch.ts index 2da580e757b..c4c8ef95a2f 100644 --- a/src/addons/mod/workshop/services/handlers/prefetch.ts +++ b/src/addons/mod/workshop/services/handlers/prefetch.ts @@ -12,401 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { AddonModDataSyncResult } from '@addons/mod/data/services/data-sync'; -import { Injectable } from '@angular/core'; -import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; -import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course'; -import { CoreCourses } from '@features/courses/services/courses'; -import { CoreUser } from '@features/user/services/user'; -import { CoreFilepool } from '@services/filepool'; -import { CoreGroup, CoreGroups } from '@services/groups'; -import { CoreSites, CoreSitesReadingStrategy, CoreSitesCommonWSOptions } from '@services/sites'; -import { CoreUtils } from '@services/utils/utils'; -import { CoreWSExternalFile, CoreWSFile } from '@services/ws'; -import { makeSingleton } from '@singletons'; +import { asyncInstance } from '@/core/utils/async-instance'; import { - AddonModWorkshopProvider, - AddonModWorkshop, - AddonModWorkshopPhase, - AddonModWorkshopGradesData, - AddonModWorkshopData, - AddonModWorkshopGetWorkshopAccessInformationWSResponse, -} from '../workshop'; -import { AddonModWorkshopHelper } from '../workshop-helper'; -import { AddonModWorkshopSync } from '../workshop-sync'; + ADDON_MOD_WORKSHOP_PREFETCH_COMPONENT, + ADDON_MOD_WORKSHOP_PREFETCH_MODNAME, + ADDON_MOD_WORKSHOP_PREFETCH_NAME, + ADDON_MOD_WORKSHOP_PREFETCH_UPDATE_NAMES, +} from '@addons/mod/workshop/constants'; +import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; +import { CoreCourseModulePrefetchHandler } from '@features/course/services/module-prefetch-delegate'; -/** - * Handler to prefetch workshops. - */ -@Injectable({ providedIn: 'root' }) export class AddonModWorkshopPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase { - name = 'AddonModWorkshop'; - modName = 'workshop'; - component = AddonModWorkshopProvider.COMPONENT; - updatesNames = new RegExp('^configuration$|^.*files$|^completion|^gradeitems$|^outcomes$|^submissions$|^assessments$' + - '|^assessmentgrades$|^usersubmissions$|^userassessments$|^userassessmentgrades$|^userassessmentgrades$'); - - /** - * @inheritdoc - */ - async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise { - const info = await this.getWorkshopInfoHelper(module, courseId, { omitFail: true }); - - return info.files; - } - - /** - * Helper function to get all workshop info just once. - * - * @param module Module to get the files. - * @param courseId Course ID the module belongs to. - * @param options Other options. - * @returns Promise resolved with the info fetched. - */ - protected async getWorkshopInfoHelper( - module: CoreCourseAnyModuleData, - courseId: number, - options: AddonModWorkshopGetInfoOptions = {}, - ): Promise<{ workshop?: AddonModWorkshopData; groups: CoreGroup[]; files: CoreWSFile[]}> { - let groups: CoreGroup[] = []; - let files: CoreWSFile[] = []; - let workshop: AddonModWorkshopData; - let access: AddonModWorkshopGetWorkshopAccessInformationWSResponse | undefined; - - const modOptions = { - cmId: module.id, - ...options, // Include all options. - }; - - const site = await CoreSites.getSite(options.siteId); - options.siteId = options.siteId ?? site.getId(); - const userId = site.getUserId(); - - try { - workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, options); - } catch (error) { - if (options.omitFail) { - // Any error, return the info we have. - return { - groups: [], - files: [], - }; - } - - throw error; - } - - try { - files = this.getIntroFilesFromInstance(module, workshop); - files = files.concat(workshop.instructauthorsfiles || []).concat(workshop.instructreviewersfiles || []); - - access = await AddonModWorkshop.getWorkshopAccessInformation(workshop.id, modOptions); - if (access.canviewallsubmissions) { - const groupInfo = await CoreGroups.getActivityGroupInfo(module.id, false, undefined, options.siteId); - if (!groupInfo.groups || groupInfo.groups.length == 0) { - groupInfo.groups = [{ id: 0, name: '' }]; - } - groups = groupInfo.groups; - } - - const phases = await AddonModWorkshop.getUserPlanPhases(workshop.id, modOptions); - - // Get submission phase info. - const submissionPhase = phases[AddonModWorkshopPhase.PHASE_SUBMISSION]; - const canSubmit = AddonModWorkshopHelper.canSubmit(workshop, access, submissionPhase.tasks); - const canAssess = AddonModWorkshopHelper.canAssess(workshop, access); - - const promises: Promise[] = []; - - if (canSubmit) { - promises.push(AddonModWorkshopHelper.getUserSubmission(workshop.id, { - userId, - cmId: module.id, - }).then((submission) => { - if (submission) { - files = files.concat(submission.contentfiles || []).concat(submission.attachmentfiles || []); - } - - return; - })); - } - - if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopPhase.PHASE_SUBMISSION) { - promises.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions).then(async (submissions) => { - - await Promise.all(submissions.map(async (submission) => { - files = files.concat(submission.contentfiles || []).concat(submission.attachmentfiles || []); - - const assessments = await AddonModWorkshop.getSubmissionAssessments(workshop.id, submission.id, { - cmId: module.id, - }); - - assessments.forEach((assessment) => { - files = files.concat(assessment.feedbackattachmentfiles) - .concat(assessment.feedbackcontentfiles); - }); - - if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { - await Promise.all(assessments.map((assessment) => - AddonModWorkshopHelper.getReviewerAssessmentById(workshop.id, assessment.id))); - } - })); - - return; - })); - } - - // Get assessment files. - if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { - promises.push(AddonModWorkshopHelper.getReviewerAssessments(workshop.id, modOptions).then((assessments) => { - assessments.forEach((assessment) => { - files = files.concat(assessment.feedbackattachmentfiles) - .concat(assessment.feedbackcontentfiles); - }); - - return; - })); - } - - await Promise.all(promises); - - return { - workshop, - groups, - files: files.filter((file) => file !== undefined), - }; - } catch (error) { - if (options.omitFail) { - // Any error, return the info we have. - return { - workshop, - groups, - files: files.filter((file) => file !== undefined), - }; - } - - throw error; - } - } - - /** - * @inheritdoc - */ - async invalidateContent(moduleId: number, courseId: number): Promise { - await AddonModWorkshop.invalidateContent(moduleId, courseId); - } - - /** - * Check if a module can be downloaded. If the function is not defined, we assume that all modules are downloadable. - * - * @param module Module. - * @param courseId Course ID the module belongs to. - * @returns Whether the module can be downloaded. The promise should never be rejected. - */ - async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise { - const workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, { - readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE, - }); - - const accessData = await AddonModWorkshop.getWorkshopAccessInformation(workshop.id, { cmId: module.id }); - - // Check if workshop is setup by phase. - return accessData.canswitchphase || workshop.phase > AddonModWorkshopPhase.PHASE_SETUP; - } - - /** - * @inheritdoc - */ - prefetch(module: CoreCourseAnyModuleData, courseId: number): Promise { - return this.prefetchPackage(module, courseId, (siteId) => this.prefetchWorkshop(module, courseId, siteId)); - } + name = ADDON_MOD_WORKSHOP_PREFETCH_NAME; + modName = ADDON_MOD_WORKSHOP_PREFETCH_MODNAME; + component = ADDON_MOD_WORKSHOP_PREFETCH_COMPONENT; + updatesNames = ADDON_MOD_WORKSHOP_PREFETCH_UPDATE_NAMES; - /** - * Retrieves all the grades reports for all the groups and then returns only unique grades. - * - * @param workshopId Workshop ID. - * @param groups Array of groups in the activity. - * @param cmId Module ID. - * @param siteId Site ID. If not defined, current site. - * @returns All unique entries. - */ - protected async getAllGradesReport( - workshopId: number, - groups: CoreGroup[], - cmId: number, - siteId: string, - ): Promise { - const promises: Promise[] = []; - - groups.forEach((group) => { - promises.push(AddonModWorkshop.fetchAllGradeReports(workshopId, { groupId: group.id, cmId, siteId })); - }); - - const grades = await Promise.all(promises); - const uniqueGrades: Record = {}; - - grades.forEach((groupGrades) => { - groupGrades.forEach((grade) => { - if (grade.submissionid) { - uniqueGrades[grade.submissionid] = grade; - } - }); - }); - - return CoreUtils.objectToArray(uniqueGrades); - } - - /** - * Prefetch a workshop. - * - * @param module The module object returned by WS. - * @param courseId Course ID the module belongs to. - * @param siteId Site ID. - * @returns Promise resolved when done. - */ - protected async prefetchWorkshop(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise { - const userIds: number[] = []; - const commonOptions = { - readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK, - siteId, - }; - const modOptions = { - cmId: module.id, - ...commonOptions, // Include all common options. - }; - - const site = await CoreSites.getSite(siteId); - const currentUserId = site.getUserId(); - - // Prefetch the workshop data. - const info = await this.getWorkshopInfoHelper(module, courseId, commonOptions); - if (!info.workshop) { - // It would throw an exception so it would not happen. - return; - } - - const workshop = info.workshop; - const promises: Promise[] = []; - const assessmentIds: number[] = []; - - promises.push(CoreFilepool.addFilesToQueue(siteId, info.files, this.component, module.id)); - - promises.push(AddonModWorkshop.getWorkshopAccessInformation(workshop.id, modOptions).then(async (access) => { - const phases = await AddonModWorkshop.getUserPlanPhases(workshop.id, modOptions); - - // Get submission phase info. - const submissionPhase = phases[AddonModWorkshopPhase.PHASE_SUBMISSION]; - const canSubmit = AddonModWorkshopHelper.canSubmit(workshop, access, submissionPhase.tasks); - const canAssess = AddonModWorkshopHelper.canAssess(workshop, access); - const promises2: Promise[] = []; - - if (canSubmit) { - promises2.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions)); - // Add userId to the profiles to prefetch. - userIds.push(currentUserId); - } - - let reportPromise: Promise = Promise.resolve(); - if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopPhase.PHASE_SUBMISSION) { - // eslint-disable-next-line promise/no-nesting - reportPromise = this.getAllGradesReport(workshop.id, info.groups, module.id, siteId).then((grades) => { - grades.forEach((grade) => { - userIds.push(grade.userid); - grade.submissiongradeoverby && userIds.push(grade.submissiongradeoverby); - - grade.reviewedby && grade.reviewedby.forEach((assessment) => { - userIds.push(assessment.userid); - assessmentIds[assessment.assessmentid] = assessment.assessmentid; - }); - - grade.reviewerof && grade.reviewerof.forEach((assessment) => { - userIds.push(assessment.userid); - assessmentIds[assessment.assessmentid] = assessment.assessmentid; - }); - }); - - return; - }); - } - - if (workshop.phase >= AddonModWorkshopPhase.PHASE_ASSESSMENT && canAssess) { - // Wait the report promise to finish to override assessments array if needed. - reportPromise = reportPromise.finally(async () => { - const revAssessments = await AddonModWorkshopHelper.getReviewerAssessments(workshop.id, { - userId: currentUserId, - cmId: module.id, - siteId, - }); - - let files: CoreWSExternalFile[] = []; // Files in each submission. - - revAssessments.forEach((assessment) => { - if (assessment.submission?.authorid == currentUserId) { - promises.push(AddonModWorkshop.getAssessment( - workshop.id, - assessment.id, - modOptions, - )); - } - userIds.push(assessment.reviewerid); - userIds.push(assessment.gradinggradeoverby); - assessmentIds[assessment.id] = assessment.id; - - files = files.concat(assessment.submission?.attachmentfiles || []) - .concat(assessment.submission?.contentfiles || []); - }); - - await CoreFilepool.addFilesToQueue(siteId, files, this.component, module.id); - }); - } - - reportPromise = reportPromise.finally(() => { - if (assessmentIds.length > 0) { - return Promise.all(assessmentIds.map((assessmentId) => - AddonModWorkshop.getAssessmentForm(workshop.id, assessmentId, modOptions))); - } - }); - promises2.push(reportPromise); - - if (workshop.phase == AddonModWorkshopPhase.PHASE_CLOSED) { - promises2.push(AddonModWorkshop.getGrades(workshop.id, modOptions)); - if (access.canviewpublishedsubmissions) { - promises2.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions)); - } - } - - await Promise.all(promises2); - - return; - })); - - // Add Basic Info to manage links. - promises.push(CoreCourse.getModuleBasicInfoByInstance(workshop.id, 'workshop', { siteId })); - promises.push(CoreCourse.getModuleBasicGradeInfo(module.id, siteId)); - - // Get course data, needed to determine upload max size if it's configured to be course limit. - promises.push(CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', courseId, siteId))); +} - await Promise.all(promises); +/** + * Get prefetch handler instance. + * + * @returns Prefetch handler. + */ +export function getPrefetchHandlerInstance(): CoreCourseModulePrefetchHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopPrefetchHandler } = await import('./prefetch-lazy'); - // Prefetch user profiles. - await CoreUser.prefetchProfiles(userIds, courseId, siteId); - } + return AddonModWorkshopPrefetchHandler.instance; + }); - /** - * @inheritdoc - */ - async sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise { - return AddonModWorkshopSync.syncWorkshop(module.instance, siteId); - } + lazyHandler.setEagerInstance(new AddonModWorkshopPrefetchHandlerService()); + return lazyHandler; } -export const AddonModWorkshopPrefetchHandler = makeSingleton(AddonModWorkshopPrefetchHandlerService); - -/** - * Options to pass to getWorkshopInfoHelper. - */ -export type AddonModWorkshopGetInfoOptions = CoreSitesCommonWSOptions & { - omitFail?: boolean; // True to always return even if fails. -}; diff --git a/src/addons/mod/workshop/services/handlers/sync-cron-lazy.ts b/src/addons/mod/workshop/services/handlers/sync-cron-lazy.ts new file mode 100644 index 00000000000..cbe5eb693c4 --- /dev/null +++ b/src/addons/mod/workshop/services/handlers/sync-cron-lazy.ts @@ -0,0 +1,44 @@ +// (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. + +import { Injectable } from '@angular/core'; +import { CoreCronHandler } from '@services/cron'; +import { makeSingleton } from '@singletons'; +import { AddonModWorkshopSync } from '../workshop-sync'; +import { AddonModWorkshopSyncCronHandlerService } from './sync-cron'; + +/** + * Synchronization cron handler. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModWorkshopSyncCronHandlerLazyService + extends AddonModWorkshopSyncCronHandlerService + implements CoreCronHandler { + + /** + * @inheritdoc + */ + execute(siteId?: string, force?: boolean): Promise { + return AddonModWorkshopSync.syncAllWorkshops(siteId, force); + } + + /** + * @inheritdoc + */ + getInterval(): number { + return AddonModWorkshopSync.syncInterval; + } + +} +export const AddonModWorkshopSyncCronHandler = makeSingleton(AddonModWorkshopSyncCronHandlerLazyService); diff --git a/src/addons/mod/workshop/services/handlers/sync-cron.ts b/src/addons/mod/workshop/services/handlers/sync-cron.ts index d23811ab316..11cf1cc1a12 100644 --- a/src/addons/mod/workshop/services/handlers/sync-cron.ts +++ b/src/addons/mod/workshop/services/handlers/sync-cron.ts @@ -12,32 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { asyncInstance } from '@/core/utils/async-instance'; +import { ADDON_MOD_WORKSHOP_SYNC_CRON_NAME } from '@addons/mod/workshop/constants'; import { CoreCronHandler } from '@services/cron'; -import { makeSingleton } from '@singletons'; -import { AddonModWorkshopSync } from '../workshop-sync'; + +export class AddonModWorkshopSyncCronHandlerService { + + name = ADDON_MOD_WORKSHOP_SYNC_CRON_NAME; + +} /** - * Synchronization cron handler. + * Get cron handler instance. + * + * @returns Cron handler. */ -@Injectable({ providedIn: 'root' }) -export class AddonModWorkshopSyncCronHandlerService implements CoreCronHandler { - - name = 'AddonModWorkshopSyncCronHandler'; +export function getCronHandlerInstance(): CoreCronHandler { + const lazyHandler = asyncInstance(async () => { + const { AddonModWorkshopSyncCronHandler } = await import('./sync-cron-lazy'); - /** - * @inheritdoc - */ - execute(siteId?: string, force?: boolean): Promise { - return AddonModWorkshopSync.syncAllWorkshops(siteId, force); - } + return AddonModWorkshopSyncCronHandler.instance; + }); - /** - * @inheritdoc - */ - getInterval(): number { - return AddonModWorkshopSync.syncInterval; - } + lazyHandler.setEagerInstance(new AddonModWorkshopSyncCronHandlerService()); + return lazyHandler; } -export const AddonModWorkshopSyncCronHandler = makeSingleton(AddonModWorkshopSyncCronHandlerService); diff --git a/src/addons/mod/workshop/services/workshop-helper.ts b/src/addons/mod/workshop/services/workshop-helper.ts index 3fc6f60114c..c868bfd6e46 100644 --- a/src/addons/mod/workshop/services/workshop-helper.ts +++ b/src/addons/mod/workshop/services/workshop-helper.ts @@ -29,7 +29,6 @@ import { AddonModWorkshopExampleMode, AddonModWorkshopPhase, AddonModWorkshopUserOptions, - AddonModWorkshopProvider, AddonModWorkshopData, AddonModWorkshop, AddonModWorkshopSubmissionData, @@ -42,6 +41,7 @@ import { AddonModWorkshopGetAssessmentFormFieldsParsedData, } from './workshop'; import { AddonModWorkshopOffline, AddonModWorkshopOfflineSubmission } from './workshop-offline'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Helper to gather some common functions for workshop. @@ -293,7 +293,7 @@ export class AddonModWorkshopHelperProvider { return this.storeSubmissionFiles(workshopId, files, siteId); } - return CoreFileUploader.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId); + return CoreFileUploader.uploadOrReuploadFiles(files, ADDON_MOD_WORKSHOP_COMPONENT, workshopId, siteId); } /** @@ -403,7 +403,7 @@ export class AddonModWorkshopHelperProvider { return this.storeAssessmentFiles(workshopId, assessmentId, files, siteId); } - return CoreFileUploader.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId); + return CoreFileUploader.uploadOrReuploadFiles(files, ADDON_MOD_WORKSHOP_COMPONENT, workshopId, siteId); } /** diff --git a/src/addons/mod/workshop/services/workshop-sync.ts b/src/addons/mod/workshop/services/workshop-sync.ts index 3e345020beb..06b46dfa4da 100644 --- a/src/addons/mod/workshop/services/workshop-sync.ts +++ b/src/addons/mod/workshop/services/workshop-sync.ts @@ -28,7 +28,6 @@ import { CoreEvents } from '@singletons/events'; import { AddonModWorkshop, AddonModWorkshopAction, AddonModWorkshopData, - AddonModWorkshopProvider, AddonModWorkshopSubmissionType, } from './workshop'; import { AddonModWorkshopHelper } from './workshop-helper'; @@ -38,6 +37,7 @@ import { AddonModWorkshopOffline, AddonModWorkshopOfflineEvaluateSubmission, AddonModWorkshopOfflineSubmission, } from './workshop-offline'; +import { ADDON_MOD_WORKSHOP_COMPONENT } from '@addons/mod/workshop/constants'; /** * Service to sync workshops. @@ -136,7 +136,7 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider( @@ -345,7 +345,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getWorkshopAccessInformationDataCacheKey(workshopId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -390,7 +390,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getUserPlanDataCacheKey(workshopId), updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -437,7 +437,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSubmissionsDataCacheKey(workshopId, userId, groupId), updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -483,7 +483,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSubmissionDataCacheKey(workshopId, submissionId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -523,7 +523,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getGradesDataCacheKey(workshopId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -567,7 +567,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getGradesReportDataCacheKey(workshopId, options.groupId), updateFrequency: CoreSite.FREQUENCY_OFTEN, - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -663,7 +663,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getSubmissionAssessmentsDataCacheKey(workshopId, submissionId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -967,7 +967,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getReviewerAssessmentsDataCacheKey(workshopId, options.userId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -1017,7 +1017,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAssessmentDataCacheKey(workshopId, assessmentId), - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -1065,7 +1065,7 @@ export class AddonModWorkshopProvider { const preSets: CoreSiteWSPreSets = { cacheKey: this.getAssessmentFormDataCacheKey(workshopId, assessmentId, mode), updateFrequency: CoreSite.FREQUENCY_RARELY, - component: AddonModWorkshopProvider.COMPONENT, + component: ADDON_MOD_WORKSHOP_COMPONENT, componentId: options.cmId, ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; @@ -1454,7 +1454,7 @@ export class AddonModWorkshopProvider { await CoreCourseLogHelper.log( 'mod_workshop_view_workshop', params, - AddonModWorkshopProvider.COMPONENT, + ADDON_MOD_WORKSHOP_COMPONENT, id, siteId, ); @@ -1476,7 +1476,7 @@ export class AddonModWorkshopProvider { await CoreCourseLogHelper.log( 'mod_workshop_view_submission', params, - AddonModWorkshopProvider.COMPONENT, + ADDON_MOD_WORKSHOP_COMPONENT, workshopId, siteId, ); diff --git a/src/addons/mod/workshop/workshop.module.ts b/src/addons/mod/workshop/workshop.module.ts index 92ec3d3f93d..930ba20883a 100644 --- a/src/addons/mod/workshop/workshop.module.ts +++ b/src/addons/mod/workshop/workshop.module.ts @@ -21,32 +21,51 @@ import { CoreCourseModulePrefetchDelegate } from '@features/course/services/modu import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreCronDelegate } from '@services/cron'; import { CORE_SITE_SCHEMAS } from '@services/sites'; -import { AddonModWorkshopAssessmentStrategyModule } from './assessment/assessment.module'; -import { AddonModWorkshopComponentsModule } from './components/components.module'; -import { AddonWorkshopAssessmentStrategyDelegateService } from './services/assessment-strategy-delegate'; +import { AddonModWorkshopAssessmentStrategyModule } from '@addons/mod/workshop/assessment/assessment.module'; import { ADDON_MOD_WORKSHOP_OFFLINE_SITE_SCHEMA } from './services/database/workshop'; import { AddonModWorkshopIndexLinkHandler } from './services/handlers/index-link'; import { AddonModWorkshopListLinkHandler } from './services/handlers/list-link'; -import { AddonModWorkshopModuleHandler, AddonModWorkshopModuleHandlerService } from './services/handlers/module'; -import { AddonModWorkshopPrefetchHandler } from './services/handlers/prefetch'; -import { AddonModWorkshopSyncCronHandler } from './services/handlers/sync-cron'; -import { AddonModWorkshopProvider } from './services/workshop'; -import { AddonModWorkshopHelperProvider } from './services/workshop-helper'; -import { AddonModWorkshopOfflineProvider } from './services/workshop-offline'; -import { AddonModWorkshopSyncProvider } from './services/workshop-sync'; +import { AddonModWorkshopModuleHandler } from './services/handlers/module'; +import { ADDON_MOD_WORKSHOP_COMPONENT, ADDON_MOD_WORKSHOP_PAGE_NAME } from '@addons/mod/workshop/constants'; +import { getCronHandlerInstance } from '@addons/mod/workshop/services/handlers/sync-cron'; +import { getPrefetchHandlerInstance } from '@addons/mod/workshop/services/handlers/prefetch'; -// List of providers (without handlers). -export const ADDON_MOD_WORKSHOP_SERVICES: Type[] = [ - AddonModWorkshopProvider, - AddonModWorkshopOfflineProvider, - AddonModWorkshopSyncProvider, - AddonModWorkshopHelperProvider, - AddonWorkshopAssessmentStrategyDelegateService, -]; +/** + * Get workshop services. + * + * @returns Workshop services. + */ +export async function getWorkshopServices(): Promise[]> { + const { AddonModWorkshopProvider } = await import('@addons/mod/workshop/services/workshop'); + const { AddonModWorkshopOfflineProvider } = await import('@addons/mod/workshop/services/workshop-offline'); + const { AddonModWorkshopSyncProvider } = await import('@addons/mod/workshop/services/workshop-sync'); + const { AddonModWorkshopHelperProvider } = await import('@addons/mod/workshop/services/workshop-helper'); + const { AddonWorkshopAssessmentStrategyDelegateService } = + await import('@addons/mod/workshop/services/assessment-strategy-delegate'); + + return [ + AddonModWorkshopProvider, + AddonModWorkshopOfflineProvider, + AddonModWorkshopSyncProvider, + AddonModWorkshopHelperProvider, + AddonWorkshopAssessmentStrategyDelegateService, + ]; +} + +/** + * Get workshop component modules. + * + * @returns Workshop component modules. + */ +export async function getWorkshopComponentModules(): Promise { + const { AddonModWorkshopComponentsModule } = await import('@addons/mod/workshop/components/components.module'); + + return [AddonModWorkshopComponentsModule]; +} const routes: Routes = [ { - path: AddonModWorkshopModuleHandlerService.PAGE_NAME, + path: ADDON_MOD_WORKSHOP_PAGE_NAME, loadChildren: () => import('./workshop-lazy.module').then(m => m.AddonModWorkshopLazyModule), }, ]; @@ -54,7 +73,6 @@ const routes: Routes = [ @NgModule({ imports: [ CoreMainMenuTabRoutingModule.forChild(routes), - AddonModWorkshopComponentsModule, AddonModWorkshopAssessmentStrategyModule, ], providers: [ @@ -68,12 +86,12 @@ const routes: Routes = [ multi: true, useValue: () => { CoreCourseModuleDelegate.registerHandler(AddonModWorkshopModuleHandler.instance); - CoreCourseModulePrefetchDelegate.registerHandler(AddonModWorkshopPrefetchHandler.instance); - CoreCronDelegate.register(AddonModWorkshopSyncCronHandler.instance); + CoreCourseModulePrefetchDelegate.registerHandler(getPrefetchHandlerInstance()); + CoreCronDelegate.register(getCronHandlerInstance()); CoreContentLinksDelegate.registerHandler(AddonModWorkshopIndexLinkHandler.instance); CoreContentLinksDelegate.registerHandler(AddonModWorkshopListLinkHandler.instance); - CoreCourseHelper.registerModuleReminderClick(AddonModWorkshopProvider.COMPONENT); + CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_WORKSHOP_COMPONENT); }, }, ], diff --git a/src/core/features/compile/components/compile-html/compile-html.ts b/src/core/features/compile/components/compile-html/compile-html.ts index ecb833a3221..25a937676eb 100644 --- a/src/core/features/compile/components/compile-html/compile-html.ts +++ b/src/core/features/compile/components/compile-html/compile-html.ts @@ -126,11 +126,8 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { this.compiling.emit(true); try { - const factory = await CoreCompile.createAndCompileComponent( - this.text, - this.getComponentClass(), - this.extraImports, - ); + const componentClass = await this.getComponentClass(); + const factory = await CoreCompile.createAndCompileComponent(this.text, componentClass, this.extraImports); // Destroy previous components. this.componentRef?.destroy(); @@ -166,9 +163,10 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { * * @returns The component class. */ - protected getComponentClass(): Type { + protected async getComponentClass(): Promise> { // eslint-disable-next-line @typescript-eslint/no-this-alias const compileInstance = this; + const lazyLibraries = await CoreCompile.getLazyLibraries(); // Create the component, using the text as the template. return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy { @@ -184,7 +182,10 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { this['dataArray'] = []; // Inject the libraries. - CoreCompile.injectLibraries(this, compileInstance.extraProviders); + CoreCompile.injectLibraries(this, [ + ...lazyLibraries, + ...compileInstance.extraProviders, + ]); // Always add these elements, they could be needed on component init (componentObservable). this['ChangeDetectorRef'] = compileInstance.changeDetector; diff --git a/src/core/features/compile/services/compile.ts b/src/core/features/compile/services/compile.ts index 30eb5d31b83..b9c5ae548b2 100644 --- a/src/core/features/compile/services/compile.ts +++ b/src/core/features/compile/services/compile.ts @@ -149,14 +149,13 @@ import { ADDON_MOD_SCORM_SERVICES } from '@addons/mod/scorm/scorm.module'; import { ADDON_MOD_SURVEY_SERVICES } from '@addons/mod/survey/survey.module'; import { ADDON_MOD_URL_SERVICES } from '@addons/mod/url/url.module'; import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module'; -import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module'; +import { getWorkshopComponentModules, getWorkshopServices } from '@addons/mod/workshop/workshop.module'; import { ADDON_NOTES_SERVICES } from '@addons/notes/notes.module'; import { ADDON_NOTIFICATIONS_SERVICES } from '@addons/notifications/notifications.module'; import { ADDON_PRIVATEFILES_SERVICES } from '@addons/privatefiles/privatefiles.module'; // Import some addon modules that define components, directives and pipes. Only import the important ones. import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module'; -import { AddonModWorkshopComponentsModule } from '@addons/mod/workshop/components/components.module'; import { CorePromisedValue } from '@classes/promised-value'; import { CorePlatform } from '@services/platform'; @@ -180,7 +179,10 @@ export class CoreCompileProvider { CoreSharedModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreUserComponentsModule, CoreCourseDirectivesModule, CoreQuestionComponentsModule, AddonModAssignComponentsModule, CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, CoreSitePluginsDirectivesModule, - AddonModWorkshopComponentsModule, + ]; + + protected readonly LAZY_IMPORTS = [ + getWorkshopComponentModules, ]; constructor(protected injector: Injector, compilerFactory: JitCompilerFactory) { @@ -205,7 +207,9 @@ export class CoreCompileProvider { // Create the component using the template and the class. const component = Component({ template })(componentClass); + const lazyImports = await Promise.all(this.LAZY_IMPORTS.map(getModules => getModules())); const imports = [ + ...CoreArray.flatten(lazyImports), ...this.IMPORTS, ...extraImports, ]; @@ -317,7 +321,6 @@ export class CoreCompileProvider { ...ADDON_MOD_SURVEY_SERVICES, ...ADDON_MOD_URL_SERVICES, ...ADDON_MOD_WIKI_SERVICES, - ...ADDON_MOD_WORKSHOP_SERVICES, ...ADDON_NOTES_SERVICES, ...ADDON_NOTIFICATIONS_SERVICES, ...ADDON_PRIVATEFILES_SERVICES, @@ -388,6 +391,19 @@ export class CoreCompileProvider { }); } + /** + * Get lazy libraries to inject. + * + * @returns Lazy libraries. + */ + async getLazyLibraries(): Promise[]> { + const ADDON_MOD_WORKSHOP_SERVICES = await getWorkshopServices(); + + return [ + ...ADDON_MOD_WORKSHOP_SERVICES, + ]; + } + /** * Instantiate a dynamic component. * diff --git a/src/core/features/native/services/native.ts b/src/core/features/native/services/native.ts index 9b98c0a6204..2220a621d94 100644 --- a/src/core/features/native/services/native.ts +++ b/src/core/features/native/services/native.ts @@ -23,7 +23,7 @@ import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance'; @Injectable({ providedIn: 'root' }) export class CoreNativeService { - private plugins: Partial>> = {}; + private plugins: Partial> = {}; /** * Get a native plugin instance. diff --git a/src/core/features/siteplugins/services/siteplugins-helper.ts b/src/core/features/siteplugins/services/siteplugins-helper.ts index 93e96eddabb..37759ab9116 100644 --- a/src/core/features/siteplugins/services/siteplugins-helper.ts +++ b/src/core/features/siteplugins/services/siteplugins-helper.ts @@ -268,11 +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); + + CoreCompile.injectLibraries(instance, lazyLibraries); // Add some data of the WS call result. const jsData = CoreSitePlugins.createDataForJS(result); diff --git a/src/core/services/cron.ts b/src/core/services/cron.ts index 811eeaf4065..01c3505b918 100644 --- a/src/core/services/cron.ts +++ b/src/core/services/cron.ts @@ -212,14 +212,14 @@ export class CoreCronDelegateService { * @param name Handler's name. * @returns Handler's interval. */ - protected getHandlerInterval(name: string): number { + protected async getHandlerInterval(name: string): Promise { if (this.handlers[name] === undefined) { // Invalid, return default. return CoreCronDelegateService.DEFAULT_INTERVAL; } // Don't allow intervals lower than the minimum. - const handlerInterval = this.handlers[name].getInterval?.(); + const handlerInterval = await this.handlers[name].getInterval?.(); if (!handlerInterval) { return CoreCronDelegateService.DEFAULT_INTERVAL; @@ -365,8 +365,7 @@ export class CoreCronDelegateService { if (!timeToNextExecution) { // Get last execution time to check when do we need to execute it. const lastExecution = await this.getHandlerLastExecutionTime(name); - - const interval = this.getHandlerInterval(name); + const interval = await this.getHandlerInterval(name); timeToNextExecution = lastExecution + interval - Date.now(); } @@ -486,7 +485,7 @@ export interface CoreCronHandler { * * @returns Interval time (in milliseconds). */ - getInterval?(): number; + getInterval?(): number | Promise; /** * Check whether the process uses network or not. True if not defined. diff --git a/src/core/utils/async-instance.ts b/src/core/utils/async-instance.ts index 977a8b83e96..40352cb1cba 100644 --- a/src/core/utils/async-instance.ts +++ b/src/core/utils/async-instance.ts @@ -14,19 +14,28 @@ import { CorePromisedValue } from '@classes/promised-value'; +// eslint-disable-next-line @typescript-eslint/ban-types +type AsyncObject = object; + /** * Create a wrapper to hold an asynchronous instance. * * @param lazyConstructor Constructor to use the first time the instance is needed. * @returns Asynchronous instance wrapper. */ -function createAsyncInstanceWrapper(lazyConstructor?: () => T | Promise): AsyncInstanceWrapper { - let promisedInstance: CorePromisedValue | null = null; +function createAsyncInstanceWrapper( + lazyConstructor?: () => TInstance | Promise, +): AsyncInstanceWrapper { + let promisedInstance: CorePromisedValue | null = null; + let eagerInstance: TEagerInstance; return { get instance() { return promisedInstance?.value ?? undefined; }, + get eagerInstance() { + return eagerInstance; + }, async getInstance() { if (!promisedInstance) { promisedInstance = new CorePromisedValue(); @@ -54,6 +63,9 @@ function createAsyncInstanceWrapper(lazyConstructor?: () => T | Promise): promisedInstance.resolve(instance); }, + setEagerInstance(instance) { + eagerInstance = instance; + }, setLazyConstructor(constructor) { if (!promisedInstance) { lazyConstructor = constructor; @@ -81,12 +93,14 @@ function createAsyncInstanceWrapper(lazyConstructor?: () => T | Promise): /** * Asynchronous instance wrapper. */ -export interface AsyncInstanceWrapper { - instance?: T; - getInstance(): Promise; - getProperty

(property: P): Promise; - setInstance(instance: T): void; - setLazyConstructor(lazyConstructor: () => T | Promise): void; +export interface AsyncInstanceWrapper { + instance?: TInstance; + eagerInstance?: TEagerInstance; + getInstance(): Promise; + getProperty

(property: P): Promise; + setInstance(instance: TInstance): void; + setEagerInstance(eagerInstance: TEagerInstance): void; + setLazyConstructor(lazyConstructor: () => TInstance | Promise): void; resetInstance(): void; } @@ -107,9 +121,10 @@ export type AsyncMethod = * All methods are converted to their asynchronous version, and properties are available asynchronously using * the getProperty method. */ -export type AsyncInstance = AsyncInstanceWrapper & { - [k in keyof T]: AsyncMethod; -}; +export type AsyncInstance = + AsyncInstanceWrapper & TEagerInstance & { + [k in keyof TInstance]: AsyncMethod; + }; /** * Create an asynchronous instance proxy, where all methods will be callable directly but will become asynchronous. If the @@ -118,8 +133,10 @@ export type AsyncInstance = AsyncInstanceWrapper & { * @param lazyConstructor Constructor to use the first time the instance is needed. * @returns Asynchronous instance. */ -export function asyncInstance(lazyConstructor?: () => T | Promise): AsyncInstance { - const wrapper = createAsyncInstanceWrapper(lazyConstructor); +export function asyncInstance( + lazyConstructor?: () => TInstance | Promise, +): AsyncInstance { + const wrapper = createAsyncInstanceWrapper(lazyConstructor); return new Proxy(wrapper, { get: (target, property, receiver) => { @@ -127,11 +144,19 @@ export function asyncInstance(lazyConstructor?: () => T | Promise): AsyncI return Reflect.get(target, property, receiver); } + if (wrapper.instance && property in wrapper.instance) { + return Reflect.get(wrapper.instance, property, receiver); + } + + if (wrapper.eagerInstance && property in wrapper.eagerInstance) { + return Reflect.get(wrapper.eagerInstance, property, receiver); + } + return async (...args: unknown[]) => { const instance = await wrapper.getInstance(); return instance[property](...args); }; }, - }) as AsyncInstance; + }) as AsyncInstance; }