forked from CIT/Vmeda.Online
		
	MOBILE-3657 workshop: Add workshop activity services
This commit is contained in:
		
							parent
							
								
									a6574c5b11
								
							
						
					
					
						commit
						8e7b148205
					
				@ -792,7 +792,7 @@ export type AddonModH5PActivityWSResultAnswer = {
 | 
			
		||||
/**
 | 
			
		||||
 * User attempts data with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModH5PActivityUserAttempts = Omit<AddonModH5PActivityWSUserAttempts, 'attempts|scored'> & {
 | 
			
		||||
export type AddonModH5PActivityUserAttempts = Omit<AddonModH5PActivityWSUserAttempts, 'attempts'|'scored'> & {
 | 
			
		||||
    attempts: AddonModH5PActivityAttempt[]; // The complete attempts list.
 | 
			
		||||
    scored?: { // Attempts used to grade the activity.
 | 
			
		||||
        title: string; // Scored attempts title.
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,7 @@ import { AddonModScormModule } from './scorm/scorm.module';
 | 
			
		||||
import { AddonModSurveyModule } from './survey/survey.module';
 | 
			
		||||
import { AddonModUrlModule } from './url/url.module';
 | 
			
		||||
import { AddonModWikiModule } from './wiki/wiki.module';
 | 
			
		||||
import { AddonModWorkshopModule } from './workshop/workshop.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -59,6 +60,7 @@ import { AddonModWikiModule } from './wiki/wiki.module';
 | 
			
		||||
        AddonModSurveyModule,
 | 
			
		||||
        AddonModUrlModule,
 | 
			
		||||
        AddonModWikiModule,
 | 
			
		||||
        AddonModWorkshopModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModModule { }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										214
									
								
								src/addons/mod/workshop/services/database/workshop.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/addons/mod/workshop/services/database/workshop.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,214 @@
 | 
			
		||||
// (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 { CoreSiteSchema } from '@services/sites';
 | 
			
		||||
import { AddonModWorkshopAction } from '../workshop';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for AddonModWorkshopOfflineProvider.
 | 
			
		||||
 */
 | 
			
		||||
export const SUBMISSIONS_TABLE = 'addon_mod_workshop_submissions';
 | 
			
		||||
export const ASSESSMENTS_TABLE = 'addon_mod_workshop_assessments';
 | 
			
		||||
export const EVALUATE_SUBMISSIONS_TABLE = 'addon_mod_workshop_evaluate_submissions';
 | 
			
		||||
export const EVALUATE_ASSESSMENTS_TABLE = 'addon_mod_workshop_evaluate_assessments';
 | 
			
		||||
 | 
			
		||||
export const ADDON_MOD_WORKSHOP_OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'AddonModWorkshopOfflineProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: SUBMISSIONS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'workshopid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'action',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'submissionid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'title',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'content',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'attachmentsid',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['workshopid', 'action'],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: ASSESSMENTS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'workshopid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'assessmentid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'inputdata',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['workshopid', 'assessmentid'],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: EVALUATE_SUBMISSIONS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'workshopid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'submissionid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'feedbacktext',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'published',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'gradeover',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['workshopid', 'submissionid'],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            name: EVALUATE_ASSESSMENTS_TABLE,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'workshopid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'assessmentid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'feedbacktext',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'weight',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'gradinggradeover',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['workshopid', 'assessmentid'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data about workshop submissions to sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWorkshopSubmissionDBRecord = {
 | 
			
		||||
    workshopid: number; // Primary key.
 | 
			
		||||
    action: AddonModWorkshopAction; // Primary key.
 | 
			
		||||
    submissionid: number;
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    title: string;
 | 
			
		||||
    content: string;
 | 
			
		||||
    attachmentsid: string;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data about workshop assessments to sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWorkshopAssessmentDBRecord = {
 | 
			
		||||
    workshopid: number; // Primary key.
 | 
			
		||||
    assessmentid: number; // Primary key.
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    inputdata: string;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data about workshop evaluate submissions to sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWorkshopEvaluateSubmissionDBRecord = {
 | 
			
		||||
    workshopid: number; // Primary key.
 | 
			
		||||
    submissionid: number; // Primary key.
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
    feedbacktext: string;
 | 
			
		||||
    published: number;
 | 
			
		||||
    gradeover: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data about workshop evaluate assessments to sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWorkshopEvaluateAssessmentDBRecord = {
 | 
			
		||||
    workshopid: number; // Primary key.
 | 
			
		||||
    assessmentid: number; // Primary key.
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
    feedbacktext: string;
 | 
			
		||||
    weight: number;
 | 
			
		||||
    gradinggradeover: string;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										39
									
								
								src/addons/mod/workshop/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/addons/mod/workshop/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -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 { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWorkshopProvider, AddonModWorkshop } from '../workshop';
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to workshop.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWorkshopLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super(AddonModWorkshopProvider.COMPONENT, 'workshop', 'w');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(siteId: string): Promise<boolean> {
 | 
			
		||||
        return AddonModWorkshop.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopIndexLinkHandler = makeSingleton(AddonModWorkshopIndexLinkHandlerService);
 | 
			
		||||
							
								
								
									
										42
									
								
								src/addons/mod/workshop/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/addons/mod/workshop/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// (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 { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWorkshop } from '../workshop';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to workshop list page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopListLinkHandlerService extends CoreContentLinksModuleListHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWorkshopListLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModWorkshop', 'workshop');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the handler is enabled on a site level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Whether or not the handler is enabled on a site level.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModWorkshop.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopListLinkHandler = makeSingleton(AddonModWorkshopListLinkHandlerService);
 | 
			
		||||
							
								
								
									
										79
									
								
								src/addons/mod/workshop/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/addons/mod/workshop/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
// (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 { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { Injectable, Type } from '@angular/core';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseModule } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
 | 
			
		||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModWorkshopIndexComponent } from '../../components/index';
 | 
			
		||||
import { AddonModWorkshop } from '../workshop';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support workshop modules.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopModuleHandlerService implements CoreCourseModuleHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'mod_workshop';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWorkshop';
 | 
			
		||||
    modName = 'workshop';
 | 
			
		||||
 | 
			
		||||
    supportedFeatures = {
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPINGS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_MOD_INTRO]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_GRADE_HAS_GRADE]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_PLAGIARISM]: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModWorkshop.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
 | 
			
		||||
            title: module.name,
 | 
			
		||||
            class: 'addon-mod_workshop-handler',
 | 
			
		||||
            showDownloadButton: true,
 | 
			
		||||
            action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void {
 | 
			
		||||
                options = options || {};
 | 
			
		||||
                options.params = options.params || {};
 | 
			
		||||
                Object.assign(options.params, { module });
 | 
			
		||||
                const routeParams = '/' + courseId + '/' + module.id;
 | 
			
		||||
 | 
			
		||||
                CoreNavigator.navigateToSitePath(AddonModWorkshopModuleHandlerService.PAGE_NAME + routeParams, options);
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async getMainComponent(): Promise<Type<unknown>> {
 | 
			
		||||
        return AddonModWorkshopIndexComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopModuleHandler = makeSingleton(AddonModWorkshopModuleHandlerService);
 | 
			
		||||
							
								
								
									
										399
									
								
								src/addons/mod/workshop/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								src/addons/mod/workshop/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,399 @@
 | 
			
		||||
// (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 { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
			
		||||
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 {
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshop,
 | 
			
		||||
    AddonModWorkshopPhase,
 | 
			
		||||
    AddonModWorkshopGradesData,
 | 
			
		||||
    AddonModWorkshopData,
 | 
			
		||||
    AddonModWorkshopGetWorkshopAccessInformationWSResponse,
 | 
			
		||||
} from '../workshop';
 | 
			
		||||
import { AddonModWorkshopHelper } from '../workshop-helper';
 | 
			
		||||
import { AddonModWorkshopSync } from '../workshop-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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<CoreWSFile[]> {
 | 
			
		||||
        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.
 | 
			
		||||
     * @return 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 | undefined;
 | 
			
		||||
        let access: AddonModWorkshopGetWorkshopAccessInformationWSResponse | undefined;
 | 
			
		||||
 | 
			
		||||
        const modOptions = {
 | 
			
		||||
            cmId: module.id,
 | 
			
		||||
            ...options, // Include all options.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const site = await CoreSites.getSite(options.siteId);
 | 
			
		||||
            const userId = site.getUserId();
 | 
			
		||||
            const workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, options);
 | 
			
		||||
 | 
			
		||||
            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<void>[] = [];
 | 
			
		||||
 | 
			
		||||
            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(<CoreWSExternalFile[]>assessment.feedbackattachmentfiles)
 | 
			
		||||
                            .concat(assessment.feedbackcontentfiles);
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
                workshop,
 | 
			
		||||
                groups,
 | 
			
		||||
                files: files.filter((file) => typeof file !== 'undefined'),
 | 
			
		||||
            };
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (options.omitFail) {
 | 
			
		||||
                // Any error, return the info we have.
 | 
			
		||||
                return {
 | 
			
		||||
                    workshop,
 | 
			
		||||
                    groups,
 | 
			
		||||
                    files: files.filter((file) => typeof file !== 'undefined'),
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
			
		||||
        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.
 | 
			
		||||
     * @return Whether the module can be downloaded. The promise should never be rejected.
 | 
			
		||||
     */
 | 
			
		||||
    async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
 | 
			
		||||
        const workshop = await AddonModWorkshop.getWorkshop(courseId, module.id, {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.PreferCache,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModWorkshop.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    prefetch(module: CoreCourseAnyModuleData, courseId?: number): Promise<void> {
 | 
			
		||||
        return this.prefetchPackage(module, courseId, this.prefetchWorkshop.bind(this, module, courseId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.
 | 
			
		||||
     * @return All unique entries.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getAllGradesReport(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        groups: CoreGroup[],
 | 
			
		||||
        cmId: number,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<AddonModWorkshopGradesData[]> {
 | 
			
		||||
        const promises: Promise<AddonModWorkshopGradesData[]>[] = [];
 | 
			
		||||
 | 
			
		||||
        groups.forEach((group) => {
 | 
			
		||||
            promises.push(AddonModWorkshop.fetchAllGradeReports(workshopId, { groupId: group.id, cmId, siteId }));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const grades = await Promise.all(promises);
 | 
			
		||||
        const uniqueGrades: Record<number, AddonModWorkshopGradesData> = {};
 | 
			
		||||
 | 
			
		||||
        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.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchWorkshop(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const userIds: number[] = [];
 | 
			
		||||
        const commonOptions = {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            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);
 | 
			
		||||
        const workshop = info.workshop!;
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
        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<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
            if (canSubmit) {
 | 
			
		||||
                promises2.push(AddonModWorkshop.getSubmissions(workshop.id, modOptions));
 | 
			
		||||
                // Add userId to the profiles to prefetch.
 | 
			
		||||
                userIds.push(currentUserId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let reportPromise: Promise<unknown> = 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));
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        // Prefetch user profiles.
 | 
			
		||||
        await CoreUser.prefetchProfiles(userIds, courseId, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModDataSyncResult> {
 | 
			
		||||
        return AddonModWorkshopSync.syncWorkshop(module.instance!, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopPrefetchHandler = makeSingleton(AddonModWorkshopPrefetchHandlerService);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Options to pass to getWorkshopInfoHelper.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModWorkshopGetInfoOptions = CoreSitesCommonWSOptions & {
 | 
			
		||||
    omitFail?: boolean; // True to always return even if fails.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										43
									
								
								src/addons/mod/workshop/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/addons/mod/workshop/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Synchronization cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopSyncCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModWorkshopSyncCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    execute(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return AddonModWorkshopSync.syncAllWorkshops(siteId, force);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return AddonModWorkshopSync.syncInterval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopSyncCronHandler = makeSingleton(AddonModWorkshopSyncCronHandlerService);
 | 
			
		||||
							
								
								
									
										638
									
								
								src/addons/mod/workshop/services/workshop-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										638
									
								
								src/addons/mod/workshop/services/workshop-helper.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,638 @@
 | 
			
		||||
// (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 { CoreError } from '@classes/errors/error';
 | 
			
		||||
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreFileEntry } from '@services/file-helper';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreFormFields } from '@singletons/form';
 | 
			
		||||
import { AddonModWorkshopAssessmentStrategyFieldErrors } from '../components/assessment-strategy/assessment-strategy';
 | 
			
		||||
import { AddonWorkshopAssessmentStrategyDelegate } from './assessment-strategy-delegate';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopExampleMode,
 | 
			
		||||
    AddonModWorkshopPhase,
 | 
			
		||||
    AddonModWorkshopUserOptions,
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshopData,
 | 
			
		||||
    AddonModWorkshop,
 | 
			
		||||
    AddonModWorkshopSubmissionData,
 | 
			
		||||
    AddonModWorkshopGetWorkshopAccessInformationWSResponse,
 | 
			
		||||
    AddonModWorkshopPhaseTaskData,
 | 
			
		||||
    AddonModWorkshopSubmissionAssessmentData,
 | 
			
		||||
    AddonModWorkshopGetAssessmentFormDefinitionData,
 | 
			
		||||
    AddonModWorkshopAction,
 | 
			
		||||
    AddonModWorkshopOverallFeedbackMode,
 | 
			
		||||
    AddonModWorkshopGetAssessmentFormFieldsParsedData,
 | 
			
		||||
} from './workshop';
 | 
			
		||||
import { AddonModWorkshopOffline, AddonModWorkshopOfflineSubmission } from './workshop-offline';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Helper to gather some common functions for workshop.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopHelperProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a task by code.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tasks Array of tasks.
 | 
			
		||||
     * @param taskCode Unique task code.
 | 
			
		||||
     * @return Task requested
 | 
			
		||||
     */
 | 
			
		||||
    getTask(tasks: AddonModWorkshopPhaseTaskData[], taskCode: string): AddonModWorkshopPhaseTaskData | undefined {
 | 
			
		||||
        return tasks.find((task) => task.code == taskCode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check is task code is done.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tasks Array of tasks.
 | 
			
		||||
     * @param taskCode Unique task code.
 | 
			
		||||
     * @return True if task is completed.
 | 
			
		||||
     */
 | 
			
		||||
    isTaskDone(tasks: AddonModWorkshopPhaseTaskData[], taskCode: string): boolean {
 | 
			
		||||
        const task = this.getTask(tasks, taskCode);
 | 
			
		||||
 | 
			
		||||
        if (task) {
 | 
			
		||||
            return !!task.completed;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Task not found, assume true.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return if a user can submit a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop info.
 | 
			
		||||
     * @param access Access information.
 | 
			
		||||
     * @param tasks Array of tasks.
 | 
			
		||||
     * @return True if the user can submit the workshop.
 | 
			
		||||
     */
 | 
			
		||||
    canSubmit(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        access: AddonModWorkshopGetWorkshopAccessInformationWSResponse,
 | 
			
		||||
        tasks: AddonModWorkshopPhaseTaskData[],
 | 
			
		||||
    ): boolean {
 | 
			
		||||
        const examplesMust = workshop.useexamples &&
 | 
			
		||||
            workshop.examplesmode == AddonModWorkshopExampleMode.EXAMPLES_BEFORE_SUBMISSION;
 | 
			
		||||
        const examplesDone = access.canmanageexamples ||
 | 
			
		||||
            workshop.examplesmode == AddonModWorkshopExampleMode.EXAMPLES_VOLUNTARY ||
 | 
			
		||||
            this.isTaskDone(tasks, 'examples');
 | 
			
		||||
 | 
			
		||||
        return workshop.phase > AddonModWorkshopPhase.PHASE_SETUP && access.cansubmit && (!examplesMust || examplesDone);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return if a user can assess a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop info.
 | 
			
		||||
     * @param access Access information.
 | 
			
		||||
     * @return True if the user can assess the workshop.
 | 
			
		||||
     */
 | 
			
		||||
    canAssess(workshop: AddonModWorkshopData, access: AddonModWorkshopGetWorkshopAccessInformationWSResponse): boolean {
 | 
			
		||||
        const examplesMust = workshop.useexamples &&
 | 
			
		||||
            workshop.examplesmode == AddonModWorkshopExampleMode.EXAMPLES_BEFORE_ASSESSMENT;
 | 
			
		||||
 | 
			
		||||
        const examplesDone = access.canmanageexamples;
 | 
			
		||||
 | 
			
		||||
        return !examplesMust || examplesDone;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return a particular user submission from the submission list.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Resolved with the submission, resolved with false if not found.
 | 
			
		||||
     */
 | 
			
		||||
    async getUserSubmission(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        options: AddonModWorkshopUserOptions = {},
 | 
			
		||||
    ): Promise<AddonModWorkshopSubmissionData | undefined> {
 | 
			
		||||
        const userId = options.userId || CoreSites.getCurrentSiteUserId();
 | 
			
		||||
 | 
			
		||||
        const submissions = await AddonModWorkshop.getSubmissions(workshopId, options);
 | 
			
		||||
 | 
			
		||||
        return submissions.find((submission) => submission.authorid == userId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return a particular submission. It will use prefetched data if fetch fails.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId Submission ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Resolved with the submission, resolved with false if not found.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubmissionById(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        submissionId: number,
 | 
			
		||||
        options: AddonModWorkshopUserOptions = {},
 | 
			
		||||
    ): Promise<AddonModWorkshopSubmissionData> {
 | 
			
		||||
        try {
 | 
			
		||||
            return await AddonModWorkshop.getSubmission(workshopId, submissionId, options);
 | 
			
		||||
        } catch {
 | 
			
		||||
            const submissions = await AddonModWorkshop.getSubmissions(workshopId, options);
 | 
			
		||||
 | 
			
		||||
            const submission = submissions.find((submission) => submission.id == submissionId);
 | 
			
		||||
 | 
			
		||||
            if (!submission) {
 | 
			
		||||
                throw new CoreError('Submission not found');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return submission;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return a particular assesment. It will use prefetched data if fetch fails. It will add assessment form data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Resolved with the assessment.
 | 
			
		||||
     */
 | 
			
		||||
    async getReviewerAssessmentById(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        options: AddonModWorkshopUserOptions = {},
 | 
			
		||||
    ): Promise<AddonModWorkshopSubmissionAssessmentWithFormData> {
 | 
			
		||||
        let assessment: AddonModWorkshopSubmissionAssessmentWithFormData | undefined;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            assessment = await AddonModWorkshop.getAssessment(workshopId, assessmentId, options);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            const assessments = await AddonModWorkshop.getReviewerAssessments(workshopId, options);
 | 
			
		||||
            assessment = assessments.find((assessment_1) => assessment_1.id == assessmentId);
 | 
			
		||||
 | 
			
		||||
            if (!assessment) {
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assessment.form = await AddonModWorkshop.getAssessmentForm(workshopId, assessmentId, options);
 | 
			
		||||
 | 
			
		||||
        return assessment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the assessment of the given user and all the related data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the workshop data is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getReviewerAssessments(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        options: AddonModWorkshopUserOptions = {},
 | 
			
		||||
    ): Promise<AddonModWorkshopSubmissionAssessmentWithFormData[]> {
 | 
			
		||||
        options.siteId = options.siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const assessments: AddonModWorkshopSubmissionAssessmentWithFormData[] =
 | 
			
		||||
            await AddonModWorkshop.getReviewerAssessments(workshopId, options);
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
        assessments.forEach((assessment) => {
 | 
			
		||||
            promises.push(this.getSubmissionById(workshopId, assessment.submissionid, options).then((submission) => {
 | 
			
		||||
                assessment.submission = submission;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
            promises.push(AddonModWorkshop.getAssessmentForm(workshopId, assessment.id, options).then((assessmentForm) => {
 | 
			
		||||
                assessment.form = assessmentForm;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        return assessments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete stored attachment files for a submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when deleted.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteSubmissionStoredFiles(workshopId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a list of files (either online files or local files), store the local files in a local folder
 | 
			
		||||
     * to be submitted later.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param files List of files.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async storeSubmissionFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult> {
 | 
			
		||||
        // Get the folder where to store the files.
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.storeFilesToUpload(folderPath, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Upload or store some files for a submission, depending if the user is offline or not.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId If not editing, it will refer to timecreated.
 | 
			
		||||
     * @param files List of files.
 | 
			
		||||
     * @param editing If the submission is being edited or added otherwise.
 | 
			
		||||
     * @param offline True if files sould be stored for offline, false to upload them.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if success.
 | 
			
		||||
     */
 | 
			
		||||
    uploadOrStoreSubmissionFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: true,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult>;
 | 
			
		||||
    uploadOrStoreSubmissionFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: false,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<number>;
 | 
			
		||||
    uploadOrStoreSubmissionFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult | number> {
 | 
			
		||||
        if (offline) {
 | 
			
		||||
            return this.storeSubmissionFiles(workshopId, files, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of stored attachment files for a submission. See AddonModWorkshopHelperProvider#storeFiles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId If not editing, it will refer to timecreated.
 | 
			
		||||
     * @param editing If the submission is being edited or added otherwise.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async getStoredSubmissionFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<FileEntry[]> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        // Ignore not found files.
 | 
			
		||||
        return CoreUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of stored attachment files for a submission and online files also. See AddonModWorkshopHelperProvider#storeFiles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filesObject Files object combining offline and online information.
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubmissionFilesFromOfflineFilesObject(
 | 
			
		||||
        filesObject: CoreFileUploaderStoreFilesResult,
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileEntry[]> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getSubmissionFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.getStoredFilesFromOfflineFilesObject(filesObject, folderPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete stored attachment files for an assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when deleted.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteAssessmentStoredFiles(workshopId: number, assessmentId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        // Ignore any errors, CoreFileProvider.removeDir fails if folder doesn't exists.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreFile.removeDir(folderPath));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a list of files (either online files or local files), store the local files in a local folder
 | 
			
		||||
     * to be submitted later.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param files List of files.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async storeAssessmentFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult> {
 | 
			
		||||
        // Get the folder where to store the files.
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.storeFilesToUpload(folderPath, files);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Upload or store some files for an assessment, depending if the user is offline or not.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId ID.
 | 
			
		||||
     * @param files List of files.
 | 
			
		||||
     * @param offline True if files sould be stored for offline, false to upload them.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if success.
 | 
			
		||||
     */
 | 
			
		||||
    uploadOrStoreAssessmentFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: true,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult>;
 | 
			
		||||
    uploadOrStoreAssessmentFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: false,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<number>
 | 
			
		||||
    uploadOrStoreAssessmentFiles(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        files: CoreFileEntry[],
 | 
			
		||||
        offline: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileUploaderStoreFilesResult | number> {
 | 
			
		||||
        if (offline) {
 | 
			
		||||
            return this.storeAssessmentFiles(workshopId, assessmentId, files, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.uploadOrReuploadFiles(files, AddonModWorkshopProvider.COMPONENT, workshopId, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of stored attachment files for an assessment. See AddonModWorkshopHelperProvider#storeFiles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async getStoredAssessmentFiles(workshopId: number, assessmentId: number, siteId?: string): Promise<FileEntry[]> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        // Ignore not found files.
 | 
			
		||||
        return CoreUtils.ignoreErrors(CoreFileUploader.getStoredFiles(folderPath), []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of stored attachment files for an assessment and online files also. See AddonModWorkshopHelperProvider#storeFiles.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filesObject Files object combining offline and online information.
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async getAssessmentFilesFromOfflineFilesObject(
 | 
			
		||||
        filesObject: CoreFileUploaderStoreFilesResult,
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<CoreFileEntry[]> {
 | 
			
		||||
        const folderPath = await AddonModWorkshopOffline.getAssessmentFolder(workshopId, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        return CoreFileUploader.getStoredFilesFromOfflineFilesObject(filesObject, folderPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Applies offline data to submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param submission Submission object to be modified.
 | 
			
		||||
     * @param actions Offline actions to be applied to the given submission.
 | 
			
		||||
     * @return Promise resolved with the files.
 | 
			
		||||
     */
 | 
			
		||||
    async applyOfflineData(
 | 
			
		||||
        submission?: AddonModWorkshopSubmissionDataWithOfflineData,
 | 
			
		||||
        actions: AddonModWorkshopOfflineSubmission[] = [],
 | 
			
		||||
    ): Promise<AddonModWorkshopSubmissionDataWithOfflineData | undefined> {
 | 
			
		||||
        if (actions.length == 0) {
 | 
			
		||||
            return submission;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (typeof submission == 'undefined') {
 | 
			
		||||
            submission = {
 | 
			
		||||
                id: 0,
 | 
			
		||||
                workshopid: 0,
 | 
			
		||||
                title: '',
 | 
			
		||||
                content: '',
 | 
			
		||||
                timemodified: 0,
 | 
			
		||||
                example: false,
 | 
			
		||||
                authorid: 0,
 | 
			
		||||
                timecreated: 0,
 | 
			
		||||
                contenttrust: 0,
 | 
			
		||||
                attachment: 0,
 | 
			
		||||
                published: false,
 | 
			
		||||
                late: 0,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let attachmentsId: CoreFileUploaderStoreFilesResult | undefined;
 | 
			
		||||
        const workshopId = actions[0].workshopid;
 | 
			
		||||
 | 
			
		||||
        actions.forEach((action) => {
 | 
			
		||||
            switch (action.action) {
 | 
			
		||||
                case AddonModWorkshopAction.ADD:
 | 
			
		||||
                case  AddonModWorkshopAction.UPDATE:
 | 
			
		||||
                    submission!.title = action.title;
 | 
			
		||||
                    submission!.content = action.content;
 | 
			
		||||
                    submission!.title = action.title;
 | 
			
		||||
                    submission!.courseid = action.courseid;
 | 
			
		||||
                    submission!.submissionmodified = action.timemodified / 1000;
 | 
			
		||||
                    submission!.offline = true;
 | 
			
		||||
                    attachmentsId = action.attachmentsid as CoreFileUploaderStoreFilesResult;
 | 
			
		||||
                    break;
 | 
			
		||||
                case  AddonModWorkshopAction.DELETE:
 | 
			
		||||
                    submission!.deleted = true;
 | 
			
		||||
                    submission!.submissionmodified = action.timemodified / 1000;
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Check offline files for latest attachmentsid.
 | 
			
		||||
        if (attachmentsId) {
 | 
			
		||||
            submission.attachmentfiles =
 | 
			
		||||
                await this.getSubmissionFilesFromOfflineFilesObject(attachmentsId, workshopId);
 | 
			
		||||
        } else {
 | 
			
		||||
            submission.attachmentfiles = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return submission;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare assessment data to be sent to the server.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop object.
 | 
			
		||||
     * @param selectedValues Assessment current values
 | 
			
		||||
     * @param feedbackText Feedback text.
 | 
			
		||||
     * @param feedbackFiles Feedback attachments.
 | 
			
		||||
     * @param form Assessment form original data.
 | 
			
		||||
     * @param attachmentsId The draft file area id for attachments.
 | 
			
		||||
     * @return Promise resolved with the data to be sent. Or rejected with the input errors object.
 | 
			
		||||
     */
 | 
			
		||||
    async prepareAssessmentData(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        selectedValues: AddonModWorkshopGetAssessmentFormFieldsParsedData[],
 | 
			
		||||
        feedbackText: string,
 | 
			
		||||
        form: AddonModWorkshopGetAssessmentFormDefinitionData,
 | 
			
		||||
        attachmentsId: CoreFileUploaderStoreFilesResult | number = 0,
 | 
			
		||||
    ): Promise<CoreFormFields<unknown>> {
 | 
			
		||||
 | 
			
		||||
        if (workshop.overallfeedbackmode == AddonModWorkshopOverallFeedbackMode.ENABLED_REQUIRED && !feedbackText) {
 | 
			
		||||
            const errors: AddonModWorkshopAssessmentStrategyFieldErrors =
 | 
			
		||||
                { feedbackauthor: Translate.instant('core.err_required') };
 | 
			
		||||
            throw errors;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data =
 | 
			
		||||
            (await AddonWorkshopAssessmentStrategyDelegate.prepareAssessmentData(workshop.strategy!, selectedValues, form)) || {};
 | 
			
		||||
        data.feedbackauthor = feedbackText;
 | 
			
		||||
        data.feedbackauthorattachmentsid = attachmentsId;
 | 
			
		||||
        data.nodims = form.dimenssionscount;
 | 
			
		||||
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the real value of a grade based on real_grade_value.
 | 
			
		||||
     *
 | 
			
		||||
     * @param value Percentual value from 0 to 100.
 | 
			
		||||
     * @param max The maximal grade.
 | 
			
		||||
     * @param decimals Decimals to show in the formatted grade.
 | 
			
		||||
     * @return Real grade formatted.
 | 
			
		||||
     */
 | 
			
		||||
    protected realGradeValueHelper(value?: number | string, max = 0, decimals = 0): string | undefined {
 | 
			
		||||
        if (typeof value == 'string') {
 | 
			
		||||
            // Already treated.
 | 
			
		||||
            return value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (value == null || typeof value == 'undefined') {
 | 
			
		||||
            return undefined;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (max == 0) {
 | 
			
		||||
            return '0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        value = CoreTextUtils.roundToDecimals(max * value / 100, decimals);
 | 
			
		||||
 | 
			
		||||
        return CoreUtils.formatFloat(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the real value of a grades of an assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop object.
 | 
			
		||||
     * @param assessment Assessment data.
 | 
			
		||||
     * @return Assessment with real grades.
 | 
			
		||||
     */
 | 
			
		||||
    realGradeValue(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        assessment: AddonModWorkshopSubmissionAssessmentWithFormData,
 | 
			
		||||
    ): AddonModWorkshopSubmissionAssessmentWithFormData {
 | 
			
		||||
        assessment.grade = this.realGradeValueHelper(assessment.grade, workshop.grade, workshop.gradedecimals);
 | 
			
		||||
        assessment.gradinggrade = this.realGradeValueHelper(assessment.gradinggrade, workshop.gradinggrade, workshop.gradedecimals);
 | 
			
		||||
 | 
			
		||||
        assessment.gradinggradeover = this.realGradeValueHelper(
 | 
			
		||||
            assessment.gradinggradeover,
 | 
			
		||||
            workshop.gradinggrade,
 | 
			
		||||
            workshop.gradedecimals,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return assessment;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check grade should be shown
 | 
			
		||||
     *
 | 
			
		||||
     * @param grade Grade to be shown
 | 
			
		||||
     * @return If grade should be shown or not.
 | 
			
		||||
     */
 | 
			
		||||
    showGrade(grade?: number|string): boolean {
 | 
			
		||||
        return typeof grade !== 'undefined' && grade !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopHelper = makeSingleton(AddonModWorkshopHelperProvider);
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopSubmissionAssessmentWithFormData =
 | 
			
		||||
    Omit<AddonModWorkshopSubmissionAssessmentData, 'grade'|'gradinggrade'|'gradinggradeover'|'feedbackattachmentfiles'> & {
 | 
			
		||||
        form?: AddonModWorkshopGetAssessmentFormDefinitionData;
 | 
			
		||||
        submission?: AddonModWorkshopSubmissionData;
 | 
			
		||||
        offline?: boolean;
 | 
			
		||||
        strategy?: string;
 | 
			
		||||
        grade?: string | number;
 | 
			
		||||
        gradinggrade?: string | number;
 | 
			
		||||
        gradinggradeover?: string | number;
 | 
			
		||||
        ownAssessment?: boolean;
 | 
			
		||||
        feedbackauthor?: string;
 | 
			
		||||
        feedbackattachmentfiles: CoreFileEntry[]; // Feedbackattachmentfiles.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopSubmissionDataWithOfflineData = Omit<AddonModWorkshopSubmissionData, 'attachmentfiles'> & {
 | 
			
		||||
    courseid?: number;
 | 
			
		||||
    submissionmodified?: number;
 | 
			
		||||
    offline?: boolean;
 | 
			
		||||
    deleted?: boolean;
 | 
			
		||||
    attachmentfiles?: CoreFileEntry[];
 | 
			
		||||
    reviewedby?: AddonModWorkshopSubmissionAssessmentWithFormData[];
 | 
			
		||||
    reviewerof?: AddonModWorkshopSubmissionAssessmentWithFormData[];
 | 
			
		||||
    gradinggrade?: number;
 | 
			
		||||
    reviewedbydone?: number;
 | 
			
		||||
    reviewerofdone?: number;
 | 
			
		||||
    reviewedbycount?: number;
 | 
			
		||||
    reviewerofcount?: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										684
									
								
								src/addons/mod/workshop/services/workshop-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										684
									
								
								src/addons/mod/workshop/services/workshop-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,684 @@
 | 
			
		||||
// (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 { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreFormFields } from '@singletons/form';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModWorkshopAssessmentDBRecord,
 | 
			
		||||
    AddonModWorkshopEvaluateAssessmentDBRecord,
 | 
			
		||||
    AddonModWorkshopEvaluateSubmissionDBRecord,
 | 
			
		||||
    AddonModWorkshopSubmissionDBRecord,
 | 
			
		||||
    ASSESSMENTS_TABLE,
 | 
			
		||||
    EVALUATE_ASSESSMENTS_TABLE,
 | 
			
		||||
    EVALUATE_SUBMISSIONS_TABLE,
 | 
			
		||||
    SUBMISSIONS_TABLE,
 | 
			
		||||
} from './database/workshop';
 | 
			
		||||
import { AddonModWorkshopAction } from './workshop';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle offline workshop.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopOfflineProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the workshops ids that have something to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with workshops id that have something to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllWorkshops(siteId?: string): Promise<number[]> {
 | 
			
		||||
        const promiseResults = await Promise.all([
 | 
			
		||||
            this.getAllSubmissions(siteId),
 | 
			
		||||
            this.getAllAssessments(siteId),
 | 
			
		||||
            this.getAllEvaluateSubmissions(siteId),
 | 
			
		||||
            this.getAllEvaluateAssessments(siteId),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        const workshopIds: Record<number, number> = {};
 | 
			
		||||
 | 
			
		||||
        // Get workshops from any offline object all should have workshopid.
 | 
			
		||||
        promiseResults.forEach((offlineObjects) => {
 | 
			
		||||
            offlineObjects.forEach((offlineObject: AddonModWorkshopOfflineSubmission | AddonModWorkshopOfflineAssessment |
 | 
			
		||||
            AddonModWorkshopOfflineEvaluateSubmission | AddonModWorkshopOfflineEvaluateAssessment) => {
 | 
			
		||||
                workshopIds[offlineObject.workshopid] = offlineObject.workshopid;
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return Object.values(workshopIds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if there is an offline data to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID to remove.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with boolean: true if has offline data, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async hasWorkshopOfflineData(workshopId: number, siteId?: string): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            const results = await Promise.all([
 | 
			
		||||
                this.getSubmissions(workshopId, siteId),
 | 
			
		||||
                this.getAssessments(workshopId, siteId),
 | 
			
		||||
                this.getEvaluateSubmissions(workshopId, siteId),
 | 
			
		||||
                this.getEvaluateAssessments(workshopId, siteId),
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            return results.some((result) => result && result.length);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // No offline data found.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete workshop submission action.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId Submission ID.
 | 
			
		||||
     * @param action Action to be done.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteSubmissionAction(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        action: AddonModWorkshopAction,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            action: action,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete all workshop submission actions.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteAllSubmissionActions(workshopId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the all the submissions to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the objects to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllSubmissions(siteId?: string): Promise<AddonModWorkshopOfflineSubmission[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopSubmissionDBRecord>(SUBMISSIONS_TABLE);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseSubmissionRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the submissions of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubmissions(workshopId: number, siteId?: string): Promise<AddonModWorkshopOfflineSubmission[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopSubmissionDBRecord>(SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseSubmissionRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an specific action of a submission of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param action Action to be done.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubmissionAction(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        action: AddonModWorkshopAction,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWorkshopOfflineSubmission> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            action: action,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const record = await site.getDb().getRecord<AddonModWorkshopSubmissionDBRecord>(SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return this.parseSubmissionRecord(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Offline version for adding a submission action to a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param courseId Course ID the workshop belongs to.
 | 
			
		||||
     * @param title The submission title.
 | 
			
		||||
     * @param content The submission text content.
 | 
			
		||||
     * @param attachmentsId Stored attachments.
 | 
			
		||||
     * @param submissionId Submission Id, if action is add, the time the submission was created.
 | 
			
		||||
     *                     If set to 0, current time is used.
 | 
			
		||||
     * @param action Action to be done. ['add', 'update', 'delete']
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when submission action is successfully saved.
 | 
			
		||||
     */
 | 
			
		||||
    async saveSubmission(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        title: string,
 | 
			
		||||
        content: string,
 | 
			
		||||
        attachmentsId: CoreFileUploaderStoreFilesResult | undefined,
 | 
			
		||||
        submissionId = 0,
 | 
			
		||||
        action: AddonModWorkshopAction,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const timemodified = CoreTimeUtils.timestamp();
 | 
			
		||||
 | 
			
		||||
        const submission: AddonModWorkshopSubmissionDBRecord = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            title: title,
 | 
			
		||||
            content: content,
 | 
			
		||||
            attachmentsid: JSON.stringify(attachmentsId),
 | 
			
		||||
            action: action,
 | 
			
		||||
            submissionid: submissionId,
 | 
			
		||||
            timemodified: timemodified,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(SUBMISSIONS_TABLE, submission);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse "attachments" column of a submission record.
 | 
			
		||||
     *
 | 
			
		||||
     * @param record Submission record, modified in place.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseSubmissionRecord(record: AddonModWorkshopSubmissionDBRecord): AddonModWorkshopOfflineSubmission {
 | 
			
		||||
        return {
 | 
			
		||||
            ...record,
 | 
			
		||||
            attachmentsid: CoreTextUtils.parseJSON(record.attachmentsid),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete workshop assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the all the assessments to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the objects to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllAssessments(siteId?: string): Promise<AddonModWorkshopOfflineAssessment[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopAssessmentDBRecord>(ASSESSMENTS_TABLE);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseAssessmentRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the assessments of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAssessments(workshopId: number, siteId?: string): Promise<AddonModWorkshopOfflineAssessment[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopAssessmentDBRecord>(ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseAssessmentRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an specific assessment of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise<AddonModWorkshopOfflineAssessment> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const record = await site.getDb().getRecord<AddonModWorkshopAssessmentDBRecord>(ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return this.parseAssessmentRecord(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Offline version for adding an assessment to a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param courseId Course ID the workshop belongs to.
 | 
			
		||||
     * @param inputData Assessment data.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when assessment is successfully saved.
 | 
			
		||||
     */
 | 
			
		||||
    async saveAssessment(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        inputData: CoreFormFields,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const assessment: AddonModWorkshopAssessmentDBRecord = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            inputdata: JSON.stringify(inputData),
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
            timemodified: CoreTimeUtils.timestamp(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(ASSESSMENTS_TABLE, assessment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse "inpudata" column of an assessment record.
 | 
			
		||||
     *
 | 
			
		||||
     * @param record Assessnent record, modified in place.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseAssessmentRecord(record: AddonModWorkshopAssessmentDBRecord): AddonModWorkshopOfflineAssessment {
 | 
			
		||||
        return {
 | 
			
		||||
            ...record,
 | 
			
		||||
            inputdata: CoreTextUtils.parseJSON(record.inputdata),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete workshop evaluate submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId Submission ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteEvaluateSubmission(workshopId: number, submissionId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            submissionid: submissionId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(EVALUATE_SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the all the evaluate submissions to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the objects to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllEvaluateSubmissions(siteId?: string): Promise<AddonModWorkshopOfflineEvaluateSubmission[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopEvaluateSubmissionDBRecord>(EVALUATE_SUBMISSIONS_TABLE);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseEvaluateSubmissionRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the evaluate submissions of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getEvaluateSubmissions(workshopId: number, siteId?: string): Promise<AddonModWorkshopOfflineEvaluateSubmission[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const records =
 | 
			
		||||
            await site.getDb().getRecords<AddonModWorkshopEvaluateSubmissionDBRecord>(EVALUATE_SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseEvaluateSubmissionRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an specific evaluate submission of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param submissionId Submission ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getEvaluateSubmission(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        submissionId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWorkshopOfflineEvaluateSubmission> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateSubmissionDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            submissionid: submissionId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const record =
 | 
			
		||||
            await site.getDb().getRecord<AddonModWorkshopEvaluateSubmissionDBRecord>(EVALUATE_SUBMISSIONS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return this.parseEvaluateSubmissionRecord(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Offline version for evaluation a submission to a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param submissionId Submission ID.
 | 
			
		||||
     * @param courseId Course ID the workshop belongs to.
 | 
			
		||||
     * @param feedbackText The feedback for the author.
 | 
			
		||||
     * @param published Whether to publish the submission for other users.
 | 
			
		||||
     * @param gradeOver The new submission grade (empty for no overriding the grade).
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when submission evaluation is successfully saved.
 | 
			
		||||
     */
 | 
			
		||||
    async saveEvaluateSubmission(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        submissionId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        feedbackText = '',
 | 
			
		||||
        published?: boolean,
 | 
			
		||||
        gradeOver?: string,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const submission: AddonModWorkshopEvaluateSubmissionDBRecord = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            submissionid: submissionId,
 | 
			
		||||
            timemodified: CoreTimeUtils.timestamp(),
 | 
			
		||||
            feedbacktext: feedbackText,
 | 
			
		||||
            published: Number(published),
 | 
			
		||||
            gradeover: JSON.stringify(gradeOver),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(EVALUATE_SUBMISSIONS_TABLE, submission);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse "published" and "gradeover" columns of an evaluate submission record.
 | 
			
		||||
     *
 | 
			
		||||
     * @param record Evaluate submission record, modified in place.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseEvaluateSubmissionRecord(
 | 
			
		||||
        record: AddonModWorkshopEvaluateSubmissionDBRecord,
 | 
			
		||||
    ): AddonModWorkshopOfflineEvaluateSubmission {
 | 
			
		||||
        return {
 | 
			
		||||
            ...record,
 | 
			
		||||
            published: Boolean(record.published),
 | 
			
		||||
            gradeover: CoreTextUtils.parseJSON(record.gradeover),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete workshop evaluate assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteEvaluateAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(EVALUATE_ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the all the evaluate assessments to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the objects to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllEvaluateAssessments(siteId?: string): Promise<AddonModWorkshopOfflineEvaluateAssessment[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModWorkshopEvaluateAssessmentDBRecord>(EVALUATE_ASSESSMENTS_TABLE);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseEvaluateAssessmentRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the evaluate assessments of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getEvaluateAssessments(workshopId: number, siteId?: string): Promise<AddonModWorkshopOfflineEvaluateAssessment[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const records =
 | 
			
		||||
            await site.getDb().getRecords<AddonModWorkshopEvaluateAssessmentDBRecord>(EVALUATE_ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return records.map(this.parseEvaluateAssessmentRecord.bind(this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an specific evaluate assessment of a workshop to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId ID of the workshop.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getEvaluateAssessment(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModWorkshopOfflineEvaluateAssessment> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModWorkshopEvaluateAssessmentDBRecord> = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const record =
 | 
			
		||||
            await site.getDb().getRecord<AddonModWorkshopEvaluateAssessmentDBRecord>(EVALUATE_ASSESSMENTS_TABLE, conditions);
 | 
			
		||||
 | 
			
		||||
        return this.parseEvaluateAssessmentRecord(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Offline version for evaluating an assessment to a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param courseId Course ID the workshop belongs to.
 | 
			
		||||
     * @param feedbackText The feedback for the reviewer.
 | 
			
		||||
     * @param weight The new weight for the assessment.
 | 
			
		||||
     * @param gradingGradeOver The new grading grade (empty for no overriding the grade).
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when assessment evaluation is successfully saved.
 | 
			
		||||
     */
 | 
			
		||||
    async saveEvaluateAssessment(
 | 
			
		||||
        workshopId: number,
 | 
			
		||||
        assessmentId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        feedbackText?: string,
 | 
			
		||||
        weight = 0,
 | 
			
		||||
        gradingGradeOver?: string,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const assessment: AddonModWorkshopEvaluateAssessmentDBRecord = {
 | 
			
		||||
            workshopid: workshopId,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            assessmentid: assessmentId,
 | 
			
		||||
            timemodified: CoreTimeUtils.timestamp(),
 | 
			
		||||
            feedbacktext: feedbackText || '',
 | 
			
		||||
            weight: weight,
 | 
			
		||||
            gradinggradeover: JSON.stringify(gradingGradeOver),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(EVALUATE_ASSESSMENTS_TABLE, assessment);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse "gradinggradeover" column of an evaluate assessment record.
 | 
			
		||||
     *
 | 
			
		||||
     * @param record Evaluate assessment record, modified in place.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseEvaluateAssessmentRecord(
 | 
			
		||||
        record: AddonModWorkshopEvaluateAssessmentDBRecord,
 | 
			
		||||
    ): AddonModWorkshopOfflineEvaluateAssessment {
 | 
			
		||||
        return {
 | 
			
		||||
            ...record,
 | 
			
		||||
            gradinggradeover: CoreTextUtils.parseJSON(record.gradinggradeover),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the path to the folder where to store files for offline attachments in a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the path.
 | 
			
		||||
     */
 | 
			
		||||
    async getWorkshopFolder(workshopId: number, siteId?: string): Promise<string> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const siteFolderPath = CoreFile.getSiteFolder(site.getId());
 | 
			
		||||
        const workshopFolderPath = 'offlineworkshop/' + workshopId + '/';
 | 
			
		||||
 | 
			
		||||
        return CoreTextUtils.concatenatePaths(siteFolderPath, workshopFolderPath);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the path to the folder where to store files for offline submissions.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the path.
 | 
			
		||||
     */
 | 
			
		||||
    async getSubmissionFolder(workshopId: number, siteId?: string): Promise<string> {
 | 
			
		||||
        const folderPath = await this.getWorkshopFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        return CoreTextUtils.concatenatePaths(folderPath, 'submission');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the path to the folder where to store files for offline assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param assessmentId Assessment ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with the path.
 | 
			
		||||
     */
 | 
			
		||||
    async getAssessmentFolder(workshopId: number, assessmentId: number, siteId?: string): Promise<string> {
 | 
			
		||||
        let folderPath = await this.getWorkshopFolder(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        folderPath += 'assessment/';
 | 
			
		||||
 | 
			
		||||
        return CoreTextUtils.concatenatePaths(folderPath, String(assessmentId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopOffline = makeSingleton(AddonModWorkshopOfflineProvider);
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopOfflineSubmission = Omit<AddonModWorkshopSubmissionDBRecord, 'attachmentsid'> & {
 | 
			
		||||
    attachmentsid?: CoreFileUploaderStoreFilesResult;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopOfflineAssessment = Omit<AddonModWorkshopAssessmentDBRecord, 'inputdata'> & {
 | 
			
		||||
    inputdata: CoreFormFields;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopOfflineEvaluateSubmission =
 | 
			
		||||
    Omit<AddonModWorkshopEvaluateSubmissionDBRecord, 'published' | 'gradeover'> & {
 | 
			
		||||
        published: boolean;
 | 
			
		||||
        gradeover: string;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopOfflineEvaluateAssessment =
 | 
			
		||||
    Omit<AddonModWorkshopEvaluateAssessmentDBRecord, 'gradinggradeover'> & {
 | 
			
		||||
        gradinggradeover: string;
 | 
			
		||||
    };
 | 
			
		||||
							
								
								
									
										631
									
								
								src/addons/mod/workshop/services/workshop-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										631
									
								
								src/addons/mod/workshop/services/workshop-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,631 @@
 | 
			
		||||
// (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 { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync';
 | 
			
		||||
import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFileEntry } from '@services/file-helper';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreSync } from '@services/sync';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { Translate, makeSingleton } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { AddonModWorkshop,
 | 
			
		||||
    AddonModWorkshopAction,
 | 
			
		||||
    AddonModWorkshopData,
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshopSubmissionType,
 | 
			
		||||
} from './workshop';
 | 
			
		||||
import { AddonModWorkshopHelper } from './workshop-helper';
 | 
			
		||||
import { AddonModWorkshopOffline,
 | 
			
		||||
    AddonModWorkshopOfflineAssessment,
 | 
			
		||||
    AddonModWorkshopOfflineEvaluateAssessment,
 | 
			
		||||
    AddonModWorkshopOfflineEvaluateSubmission,
 | 
			
		||||
    AddonModWorkshopOfflineSubmission,
 | 
			
		||||
} from './workshop-offline';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to sync workshops.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider<AddonModWorkshopSyncResult> {
 | 
			
		||||
 | 
			
		||||
    static readonly AUTO_SYNCED = 'addon_mod_workshop_autom_synced';
 | 
			
		||||
    static readonly MANUAL_SYNCED = 'addon_mod_workshop_manual_synced';
 | 
			
		||||
 | 
			
		||||
    protected componentTranslatableString = 'workshop';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModWorkshopSyncProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if an workshop has data to synchronize.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with boolean: true if has data to sync, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    hasDataToSync(workshopId: number, siteId?: string): Promise<boolean> {
 | 
			
		||||
        return AddonModWorkshopOffline.hasWorkshopOfflineData(workshopId, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize all workshops that need it and haven't been synchronized in a while.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID to sync. If not defined, sync all sites.
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @return Promise resolved when the sync is done.
 | 
			
		||||
     */
 | 
			
		||||
    syncAllWorkshops(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return this.syncOnSites('all workshops', this.syncAllWorkshopsFunc.bind(this, !!force), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync all workshops on a site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @param siteId Site ID to sync.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncAllWorkshopsFunc(force: boolean, siteId: string): Promise<void> {
 | 
			
		||||
        const workshopIds = await AddonModWorkshopOffline.getAllWorkshops(siteId);
 | 
			
		||||
 | 
			
		||||
        // Sync all workshops that haven't been synced for a while.
 | 
			
		||||
        const promises = workshopIds.map(async (workshopId) => {
 | 
			
		||||
            const data = force
 | 
			
		||||
                ? await this.syncWorkshop(workshopId, siteId)
 | 
			
		||||
                : await this.syncWorkshopIfNeeded(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
            if (data && data.updated) {
 | 
			
		||||
                // Sync done. Send event.
 | 
			
		||||
                CoreEvents.trigger(AddonModWorkshopSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    workshopId: workshopId,
 | 
			
		||||
                    warnings: data.warnings,
 | 
			
		||||
                }, siteId);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync a workshop only if a certain time has passed since the last time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the workshop is synced or if it doesn't need to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async syncWorkshopIfNeeded(workshopId: number, siteId?: string): Promise<AddonModWorkshopSyncResult | undefined> {
 | 
			
		||||
        const needed = await this.isSyncNeeded(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (needed) {
 | 
			
		||||
            return this.syncWorkshop(workshopId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize a workshop.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    syncWorkshop(workshopId: number, siteId?: string): Promise<AddonModWorkshopSyncResult> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        if (this.isSyncing(workshopId, siteId)) {
 | 
			
		||||
            // There's already a sync ongoing for this discussion, return the promise.
 | 
			
		||||
            return this.getOngoingSync(workshopId, siteId)!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Verify that workshop isn't blocked.
 | 
			
		||||
        if (CoreSync.isBlocked(AddonModWorkshopProvider.COMPONENT, workshopId, siteId)) {
 | 
			
		||||
            this.logger.debug(`Cannot sync workshop '${workshopId}' because it is blocked.`);
 | 
			
		||||
 | 
			
		||||
            throw new CoreSyncBlockedError(Translate.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Try to sync workshop '${workshopId}' in site ${siteId}'`);
 | 
			
		||||
 | 
			
		||||
        const syncPromise = this.performSyncWorkshop(workshopId, siteId);
 | 
			
		||||
 | 
			
		||||
        return this.addOngoingSync(workshopId, syncPromise, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Perform the workshop sync.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshopId Workshop ID.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async performSyncWorkshop(workshopId: number, siteId: string): Promise<AddonModWorkshopSyncResult> {
 | 
			
		||||
        const result: AddonModWorkshopSyncResult = {
 | 
			
		||||
            warnings: [],
 | 
			
		||||
            updated: false,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Sync offline logs.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(AddonModWorkshopProvider.COMPONENT, workshopId, siteId));
 | 
			
		||||
 | 
			
		||||
        // Get offline submissions to be sent.
 | 
			
		||||
        const syncs = await Promise.all([
 | 
			
		||||
            // Get offline submissions to be sent.
 | 
			
		||||
            CoreUtils.ignoreErrors(AddonModWorkshopOffline.getSubmissions(workshopId, siteId), []),
 | 
			
		||||
            // Get offline submission assessments to be sent.
 | 
			
		||||
            CoreUtils.ignoreErrors(AddonModWorkshopOffline.getAssessments(workshopId, siteId), []),
 | 
			
		||||
            // Get offline submission evaluations to be sent.
 | 
			
		||||
            CoreUtils.ignoreErrors(AddonModWorkshopOffline.getEvaluateSubmissions(workshopId, siteId), []),
 | 
			
		||||
            // Get offline assessment evaluations to be sent.
 | 
			
		||||
            CoreUtils.ignoreErrors(AddonModWorkshopOffline.getEvaluateAssessments(workshopId, siteId), []),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        let courseId: number | undefined;
 | 
			
		||||
 | 
			
		||||
        // Get courseId from the first object
 | 
			
		||||
        for (const x in syncs) {
 | 
			
		||||
            if (syncs[x].length > 0 && syncs[x][0].courseid) {
 | 
			
		||||
                courseId = syncs[x][0].courseid;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!courseId) {
 | 
			
		||||
            // Sync finished, set sync time.
 | 
			
		||||
            await CoreUtils.ignoreErrors(this.setSyncTime(workshopId, siteId));
 | 
			
		||||
 | 
			
		||||
            // Nothing to sync.
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // Cannot sync in offline.
 | 
			
		||||
            throw new CoreNetworkError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const workshop = await AddonModWorkshop.getWorkshopById(courseId, workshopId, { siteId });
 | 
			
		||||
 | 
			
		||||
        const submissionsActions: AddonModWorkshopOfflineSubmission[] = syncs[0];
 | 
			
		||||
        const assessments: AddonModWorkshopOfflineAssessment[] = syncs[1];
 | 
			
		||||
        const submissionEvaluations: AddonModWorkshopOfflineEvaluateSubmission[] = syncs[2];
 | 
			
		||||
        const assessmentEvaluations: AddonModWorkshopOfflineEvaluateAssessment[] = syncs[3];
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(this.syncSubmission(workshop, submissionsActions, result, siteId).then(() => {
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        assessments.forEach((assessment) => {
 | 
			
		||||
            promises.push(this.syncAssessment(workshop, assessment, result, siteId).then(() => {
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        submissionEvaluations.forEach((evaluation) => {
 | 
			
		||||
            promises.push(this.syncEvaluateSubmission(workshop, evaluation, result, siteId).then(() => {
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        assessmentEvaluations.forEach((evaluation) => {
 | 
			
		||||
            promises.push(this.syncEvaluateAssessment(workshop, evaluation, result, siteId).then(() => {
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        if (result.updated) {
 | 
			
		||||
            // Data has been sent to server. Now invalidate the WS calls.
 | 
			
		||||
            await CoreUtils.ignoreErrors(AddonModWorkshop.invalidateContentById(workshopId, courseId, siteId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sync finished, set sync time.
 | 
			
		||||
        await CoreUtils.ignoreErrors(this.setSyncTime(workshopId, siteId));
 | 
			
		||||
 | 
			
		||||
        // All done, return the warnings.
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a submission.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop.
 | 
			
		||||
     * @param submissionActions Submission actions offline data.
 | 
			
		||||
     * @param result Object with the result of the sync.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncSubmission(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        submissionActions: AddonModWorkshopOfflineSubmission[],
 | 
			
		||||
        result: AddonModWorkshopSyncResult,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        let discardError: string | undefined;
 | 
			
		||||
 | 
			
		||||
        // Sort entries by timemodified.
 | 
			
		||||
        submissionActions = submissionActions.sort((a, b) => a.timemodified - b.timemodified);
 | 
			
		||||
 | 
			
		||||
        let timemodified = 0;
 | 
			
		||||
        let submissionId = submissionActions[0].submissionid;
 | 
			
		||||
 | 
			
		||||
        if (submissionId > 0) {
 | 
			
		||||
            // Is editing.
 | 
			
		||||
            try {
 | 
			
		||||
                const submission = await AddonModWorkshop.getSubmission(workshop.id, submissionId, {
 | 
			
		||||
                    cmId: workshop.coursemodule,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                timemodified = submission.timemodified;
 | 
			
		||||
            } catch {
 | 
			
		||||
                timemodified = -1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (timemodified < 0 || timemodified >= submissionActions[0].timemodified) {
 | 
			
		||||
            // The entry was not found in Moodle or the entry has been modified, discard the action.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
            discardError = Translate.instant('addon.mod_workshop.warningsubmissionmodified');
 | 
			
		||||
 | 
			
		||||
            await AddonModWorkshopOffline.deleteAllSubmissionActions(workshop.id, siteId);
 | 
			
		||||
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        submissionActions.forEach(async (action) => {
 | 
			
		||||
            submissionId = action.submissionid > 0 ? action.submissionid : submissionId;
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                let attachmentsId: number | undefined;
 | 
			
		||||
 | 
			
		||||
                // Upload attachments first if any.
 | 
			
		||||
                if (action.attachmentsid) {
 | 
			
		||||
                    const files = await AddonModWorkshopHelper.getSubmissionFilesFromOfflineFilesObject(
 | 
			
		||||
                        action.attachmentsid,
 | 
			
		||||
                        workshop.id,
 | 
			
		||||
                        siteId,
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    attachmentsId = await AddonModWorkshopHelper.uploadOrStoreSubmissionFiles(
 | 
			
		||||
                        workshop.id,
 | 
			
		||||
                        files,
 | 
			
		||||
                        false,
 | 
			
		||||
                        siteId,
 | 
			
		||||
                    );
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Remove all files.
 | 
			
		||||
                    attachmentsId = await AddonModWorkshopHelper.uploadOrStoreSubmissionFiles(
 | 
			
		||||
                        workshop.id,
 | 
			
		||||
                        [],
 | 
			
		||||
                        false,
 | 
			
		||||
                        siteId,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (workshop.submissiontypefile == AddonModWorkshopSubmissionType.SUBMISSION_TYPE_DISABLED) {
 | 
			
		||||
                    attachmentsId = undefined;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Perform the action.
 | 
			
		||||
                switch (action.action) {
 | 
			
		||||
                    case AddonModWorkshopAction.ADD:
 | 
			
		||||
                        submissionId = await AddonModWorkshop.addSubmissionOnline(
 | 
			
		||||
                            workshop.id,
 | 
			
		||||
                            action.title,
 | 
			
		||||
                            action.content,
 | 
			
		||||
                            attachmentsId,
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        );
 | 
			
		||||
                    case AddonModWorkshopAction.UPDATE:
 | 
			
		||||
                        await AddonModWorkshop.updateSubmissionOnline(
 | 
			
		||||
                            submissionId,
 | 
			
		||||
                            action.title,
 | 
			
		||||
                            action.content,
 | 
			
		||||
                            attachmentsId,
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        );
 | 
			
		||||
                    case AddonModWorkshopAction.DELETE:
 | 
			
		||||
                        await AddonModWorkshop.deleteSubmissionOnline(submissionId, siteId);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                if (error && CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                    // The WebService has thrown an error, this means it cannot be performed. Discard.
 | 
			
		||||
                    discardError = CoreTextUtils.getErrorMessageFromError(error);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Couldn't connect to server, reject.
 | 
			
		||||
                throw error;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            // Delete the offline data.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
 | 
			
		||||
            await AddonModWorkshopOffline.deleteSubmissionAction(
 | 
			
		||||
                action.workshopid,
 | 
			
		||||
                action.action,
 | 
			
		||||
                siteId,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Delete stored files.
 | 
			
		||||
            if (action.action == AddonModWorkshopAction.ADD || action.action == AddonModWorkshopAction.UPDATE) {
 | 
			
		||||
 | 
			
		||||
                return AddonModWorkshopHelper.deleteSubmissionStoredFiles(
 | 
			
		||||
                    action.workshopid,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (discardError) {
 | 
			
		||||
            // Submission was discarded, add a warning.
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize an assessment.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop.
 | 
			
		||||
     * @param assessment Assessment offline data.
 | 
			
		||||
     * @param result Object with the result of the sync.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncAssessment(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        assessmentData: AddonModWorkshopOfflineAssessment,
 | 
			
		||||
        result: AddonModWorkshopSyncResult,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        let discardError: string | undefined;
 | 
			
		||||
        const assessmentId = assessmentData.assessmentid;
 | 
			
		||||
 | 
			
		||||
        let timemodified = 0;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const assessment = await AddonModWorkshop.getAssessment(workshop.id, assessmentId, {
 | 
			
		||||
                cmId: workshop.coursemodule,
 | 
			
		||||
                siteId,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            timemodified = assessment.timemodified;
 | 
			
		||||
        } catch {
 | 
			
		||||
            timemodified = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (timemodified < 0 || timemodified >= assessmentData.timemodified) {
 | 
			
		||||
            // The entry was not found in Moodle or the entry has been modified, discard the action.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
            discardError = Translate.instant('addon.mod_workshop.warningassessmentmodified');
 | 
			
		||||
 | 
			
		||||
            await AddonModWorkshopOffline.deleteAssessment(workshop.id, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let attachmentsId = 0;
 | 
			
		||||
        const inputData = assessmentData.inputdata;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            let files: CoreFileEntry[] = [];
 | 
			
		||||
            // Upload attachments first if any.
 | 
			
		||||
            if (inputData.feedbackauthorattachmentsid) {
 | 
			
		||||
                files = await AddonModWorkshopHelper.getAssessmentFilesFromOfflineFilesObject(
 | 
			
		||||
                    <CoreFileUploaderStoreFilesResult>inputData.feedbackauthorattachmentsid,
 | 
			
		||||
                    workshop.id,
 | 
			
		||||
                    assessmentId,
 | 
			
		||||
                    siteId,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            attachmentsId =
 | 
			
		||||
                await AddonModWorkshopHelper.uploadOrStoreAssessmentFiles(workshop.id, assessmentId, files, false, siteId);
 | 
			
		||||
 | 
			
		||||
            inputData.feedbackauthorattachmentsid = attachmentsId || 0;
 | 
			
		||||
 | 
			
		||||
            await AddonModWorkshop.updateAssessmentOnline(assessmentId, inputData, siteId);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error && CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error, this means it cannot be performed. Discard.
 | 
			
		||||
                discardError = CoreTextUtils.getErrorMessageFromError(error);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Couldn't connect to server, reject.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Delete the offline data.
 | 
			
		||||
        result.updated = true;
 | 
			
		||||
 | 
			
		||||
        await AddonModWorkshopOffline.deleteAssessment(workshop.id, assessmentId, siteId);
 | 
			
		||||
        await AddonModWorkshopHelper.deleteAssessmentStoredFiles(workshop.id, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (discardError) {
 | 
			
		||||
            // Assessment was discarded, add a warning.
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a submission evaluation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop.
 | 
			
		||||
     * @param evaluate Submission evaluation offline data.
 | 
			
		||||
     * @param result Object with the result of the sync.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncEvaluateSubmission(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        evaluate: AddonModWorkshopOfflineEvaluateSubmission,
 | 
			
		||||
        result: AddonModWorkshopSyncResult,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        let discardError: string | undefined;
 | 
			
		||||
        const submissionId = evaluate.submissionid;
 | 
			
		||||
 | 
			
		||||
        let timemodified = 0;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const submission = await AddonModWorkshop.getSubmission(workshop.id, submissionId, {
 | 
			
		||||
                cmId: workshop.coursemodule,
 | 
			
		||||
                siteId,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            timemodified = submission.timemodified;
 | 
			
		||||
        } catch {
 | 
			
		||||
            timemodified = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (timemodified < 0 || timemodified >= evaluate.timemodified) {
 | 
			
		||||
            // The entry was not found in Moodle or the entry has been modified, discard the action.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
            discardError = Translate.instant('addon.mod_workshop.warningsubmissionmodified');
 | 
			
		||||
 | 
			
		||||
            await AddonModWorkshopOffline.deleteEvaluateSubmission(workshop.id, submissionId, siteId);
 | 
			
		||||
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await AddonModWorkshop.evaluateSubmissionOnline(
 | 
			
		||||
                submissionId,
 | 
			
		||||
                evaluate.feedbacktext,
 | 
			
		||||
                evaluate.published,
 | 
			
		||||
                evaluate.gradeover,
 | 
			
		||||
                siteId,
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error && CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error, this means it cannot be performed. Discard.
 | 
			
		||||
                discardError = CoreTextUtils.getErrorMessageFromError(error);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Couldn't connect to server, reject.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Delete the offline data.
 | 
			
		||||
        result.updated = true;
 | 
			
		||||
 | 
			
		||||
        await AddonModWorkshopOffline.deleteEvaluateSubmission(workshop.id, submissionId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (discardError) {
 | 
			
		||||
            // Assessment was discarded, add a warning.
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a assessment evaluation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param workshop Workshop.
 | 
			
		||||
     * @param evaluate Assessment evaluation offline data.
 | 
			
		||||
     * @param result Object with the result of the sync.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved if success, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncEvaluateAssessment(
 | 
			
		||||
        workshop: AddonModWorkshopData,
 | 
			
		||||
        evaluate: AddonModWorkshopOfflineEvaluateAssessment,
 | 
			
		||||
        result: AddonModWorkshopSyncResult,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        let discardError: string | undefined;
 | 
			
		||||
        const assessmentId = evaluate.assessmentid;
 | 
			
		||||
 | 
			
		||||
        let timemodified = 0;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const assessment = await AddonModWorkshop.getAssessment(workshop.id, assessmentId, {
 | 
			
		||||
                cmId: workshop.coursemodule,
 | 
			
		||||
                siteId,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            timemodified = assessment.timemodified;
 | 
			
		||||
        } catch {
 | 
			
		||||
            timemodified = -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (timemodified < 0 || timemodified >= evaluate.timemodified) {
 | 
			
		||||
            // The entry was not found in Moodle or the entry has been modified, discard the action.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
            discardError = Translate.instant('addon.mod_workshop.warningassessmentmodified');
 | 
			
		||||
 | 
			
		||||
            return AddonModWorkshopOffline.deleteEvaluateAssessment(workshop.id, assessmentId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await AddonModWorkshop.evaluateAssessmentOnline(
 | 
			
		||||
                assessmentId,
 | 
			
		||||
                evaluate.feedbacktext,
 | 
			
		||||
                evaluate.weight,
 | 
			
		||||
                evaluate.gradinggradeover,
 | 
			
		||||
                siteId,
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error && CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error, this means it cannot be performed. Discard.
 | 
			
		||||
                discardError = CoreTextUtils.getErrorMessageFromError(error);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Couldn't connect to server, reject.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Delete the offline data.
 | 
			
		||||
        result.updated = true;
 | 
			
		||||
 | 
			
		||||
        await AddonModWorkshopOffline.deleteEvaluateAssessment(workshop.id, assessmentId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (discardError) {
 | 
			
		||||
            // Assessment was discarded, add a warning.
 | 
			
		||||
            this.addOfflineDataDeletedWarning(result.warnings, workshop.name, discardError);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonModWorkshopSync = makeSingleton(AddonModWorkshopSyncProvider);
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopAutoSyncData = {
 | 
			
		||||
    workshopId: number;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonModWorkshopSyncResult = {
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
    updated: boolean;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										2065
									
								
								src/addons/mod/workshop/services/workshop.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2065
									
								
								src/addons/mod/workshop/services/workshop.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										79
									
								
								src/addons/mod/workshop/workshop.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/addons/mod/workshop/workshop.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
// (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 { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
			
		||||
import { Routes } from '@angular/router';
 | 
			
		||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
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 { 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';
 | 
			
		||||
 | 
			
		||||
// List of providers (without handlers).
 | 
			
		||||
export const ADDON_MOD_WORKSHOP_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    AddonModWorkshopProvider,
 | 
			
		||||
    AddonModWorkshopOfflineProvider,
 | 
			
		||||
    AddonModWorkshopSyncProvider,
 | 
			
		||||
    AddonModWorkshopHelperProvider,
 | 
			
		||||
    AddonWorkshopAssessmentStrategyDelegateService,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonModWorkshopModuleHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('./workshop-lazy.module').then(m => m.AddonModWorkshopLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        AddonModWorkshopComponentsModule,
 | 
			
		||||
        AddonModWorkshopAssessmentStrategyModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [ADDON_MOD_WORKSHOP_OFFLINE_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                CoreCourseModuleDelegate.registerHandler(AddonModWorkshopModuleHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.registerHandler(AddonModWorkshopPrefetchHandler.instance);
 | 
			
		||||
                CoreCronDelegate.register(AddonModWorkshopSyncCronHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWorkshopIndexLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModWorkshopListLinkHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModWorkshopModule {}
 | 
			
		||||
@ -142,7 +142,7 @@ 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';
 | 
			
		||||
// @todo import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module';
 | 
			
		||||
import { ADDON_MOD_WORKSHOP_SERVICES } 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';
 | 
			
		||||
@ -150,7 +150,7 @@ import { ADDON_REMOTETHEMES_SERVICES } from '@addons/remotethemes/remotethemes.m
 | 
			
		||||
 | 
			
		||||
// Import some addon modules that define components, directives and pipes. Only import the important ones.
 | 
			
		||||
import { AddonModAssignComponentsModule } from '@addons/mod/assign/components/components.module';
 | 
			
		||||
// @todo import { AddonModWorkshopComponentsModule } from '@addons/mod/workshop/components/components.module';
 | 
			
		||||
import { AddonModWorkshopComponentsModule } from '@addons/mod/workshop/components/components.module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to provide functionalities regarding compiling dynamic HTML and Javascript.
 | 
			
		||||
@ -172,7 +172,7 @@ export class CoreCompileProvider {
 | 
			
		||||
        CoreSharedModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreUserComponentsModule,
 | 
			
		||||
        CoreCourseDirectivesModule, CoreQuestionComponentsModule, AddonModAssignComponentsModule,
 | 
			
		||||
        CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, CoreSitePluginsDirectivesModule,
 | 
			
		||||
        // @todo AddonModWorkshopComponentsModule,
 | 
			
		||||
        AddonModWorkshopComponentsModule,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    constructor(protected injector: Injector, compilerFactory: JitCompilerFactory) {
 | 
			
		||||
@ -308,7 +308,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...ADDON_MOD_SURVEY_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_URL_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_WIKI_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_WORKSHOP_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_WORKSHOP_SERVICES,
 | 
			
		||||
            ...ADDON_NOTES_SERVICES,
 | 
			
		||||
            ...ADDON_NOTIFICATIONS_SERVICES,
 | 
			
		||||
            ...ADDON_PRIVATEFILES_SERVICES,
 | 
			
		||||
 | 
			
		||||
@ -1158,12 +1158,12 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param keyPrefix Key prefix if neededs to delete it.
 | 
			
		||||
     * @return Object.
 | 
			
		||||
     */
 | 
			
		||||
    objectToKeyValueMap(
 | 
			
		||||
    objectToKeyValueMap<T = unknown>(
 | 
			
		||||
        objects: Record<string, unknown>[],
 | 
			
		||||
        keyName: string,
 | 
			
		||||
        valueName: string,
 | 
			
		||||
        keyPrefix?: string,
 | 
			
		||||
    ): {[name: string]: unknown} | undefined {
 | 
			
		||||
    ): {[name: string]: T} | undefined {
 | 
			
		||||
        if (!objects) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user