forked from CIT/Vmeda.Online
		
	MOBILE-3639 choice: Implement services
This commit is contained in:
		
							parent
							
								
									960f9b01b3
								
							
						
					
					
						commit
						4e7c9eb6e8
					
				
							
								
								
									
										61
									
								
								src/addons/mod/choice/choice.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/addons/mod/choice/choice.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
// (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 { 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 { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
import { AddonModChoiceProvider } from './services/choice';
 | 
			
		||||
import { AddonModChoiceOfflineProvider } from './services/choice-offline';
 | 
			
		||||
import { AddonModChoiceSyncProvider } from './services/choice-sync';
 | 
			
		||||
import { OFFLINE_SITE_SCHEMA } from './services/database/choice';
 | 
			
		||||
import { AddonModChoiceIndexLinkHandler } from './services/handlers/index-link';
 | 
			
		||||
import { AddonModChoiceListLinkHandler } from './services/handlers/list-link';
 | 
			
		||||
import { AddonModChoiceModuleHandler } from './services/handlers/module';
 | 
			
		||||
import { AddonModChoicePrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { AddonModChoiceSyncCronHandler } from './services/handlers/sync-cron';
 | 
			
		||||
 | 
			
		||||
export const ADDON_MOD_CHOICE_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    AddonModChoiceProvider,
 | 
			
		||||
    AddonModChoiceOfflineProvider,
 | 
			
		||||
    AddonModChoiceSyncProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [OFFLINE_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                CoreCourseModuleDelegate.registerHandler(AddonModChoiceModuleHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.registerHandler(AddonModChoicePrefetchHandler.instance);
 | 
			
		||||
                CoreCronDelegate.register(AddonModChoiceSyncCronHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModChoiceIndexLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModChoiceListLinkHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModChoiceModule {}
 | 
			
		||||
							
								
								
									
										150
									
								
								src/addons/mod/choice/services/choice-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/addons/mod/choice/services/choice-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,150 @@
 | 
			
		||||
// (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 { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModChoiceResponsesDBRecord, RESPONSES_TABLE_NAME } from './database/choice';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle offline choices.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceOfflineProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete a response.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID to remove.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param userId User the responses belong to. If not defined, current user in site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteResponse(choiceId: number, siteId?: string, userId?: number): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        userId = userId || site.getUserId();
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(RESPONSES_TABLE_NAME, { choiceid: choiceId, userid: userId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all offline responses.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promi[se resolved with responses.
 | 
			
		||||
     */
 | 
			
		||||
    async getResponses(siteId?: string): Promise<AddonModChoiceOfflineResponses[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const records = await site.getDb().getRecords<AddonModChoiceResponsesDBRecord>(RESPONSES_TABLE_NAME);
 | 
			
		||||
 | 
			
		||||
        return records.map((record) => this.parseResponse(record));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if there are offline responses to send.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param userId User the responses belong to. If not defined, current user in site.
 | 
			
		||||
     * @return Promise resolved with boolean: true if has offline answers, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async hasResponse(choiceId: number, siteId?: string, userId?: number): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            const response = await this.getResponse(choiceId, siteId, userId);
 | 
			
		||||
 | 
			
		||||
            return !!response.choiceid;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // No offline data found, return false.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get response to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID to get.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param userId User the responses belong to. If not defined, current user in site.
 | 
			
		||||
     * @return Promise resolved with the object to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async getResponse(choiceId: number, siteId?: string, userId?: number): Promise<AddonModChoiceOfflineResponses> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
        userId = userId || site.getUserId();
 | 
			
		||||
 | 
			
		||||
        const record = await site.getDb().getRecord<AddonModChoiceResponsesDBRecord>(RESPONSES_TABLE_NAME, {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
            userid: userId,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return this.parseResponse(record);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse responses.
 | 
			
		||||
     *
 | 
			
		||||
     * @param entry Entry to parse.
 | 
			
		||||
     * @return Parsed entry.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseResponse(entry: AddonModChoiceResponsesDBRecord): AddonModChoiceOfflineResponses {
 | 
			
		||||
        return {
 | 
			
		||||
            ...entry,
 | 
			
		||||
            responses: CoreTextUtils.parseJSON(entry.responses, <number[]> []),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Offline version for sending a response to a choice to Moodle.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param name Choice name.
 | 
			
		||||
     * @param courseId Course ID the choice belongs to.
 | 
			
		||||
     * @param responses IDs of selected options.
 | 
			
		||||
     * @param deleting If true, the user is deleting responses, if false, submitting.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @param userId User the responses belong to. If not defined, current user in site.
 | 
			
		||||
     * @return Promise resolved when results are successfully submitted.
 | 
			
		||||
     */
 | 
			
		||||
    async saveResponse(
 | 
			
		||||
        choiceId: number,
 | 
			
		||||
        name: string,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        responses: number[],
 | 
			
		||||
        deleting: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
        userId?: number,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const entry: AddonModChoiceResponsesDBRecord = {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
            name: name,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            userid: userId || site.getUserId(),
 | 
			
		||||
            responses: JSON.stringify(responses),
 | 
			
		||||
            deleting: deleting ? 1 : 0,
 | 
			
		||||
            timecreated: Date.now(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(RESPONSES_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceOffline = makeSingleton(AddonModChoiceOfflineProvider);
 | 
			
		||||
 | 
			
		||||
export type AddonModChoiceOfflineResponses = Omit<AddonModChoiceResponsesDBRecord, 'responses'> & {
 | 
			
		||||
    responses: number[];
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										236
									
								
								src/addons/mod/choice/services/choice-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/addons/mod/choice/services/choice-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,236 @@
 | 
			
		||||
// (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 { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreCourseActivitySyncBaseProvider } from '@features/course/classes/activity-sync';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { AddonModChoice, AddonModChoiceProvider } from './choice';
 | 
			
		||||
import { AddonModChoiceOffline } from './choice-offline';
 | 
			
		||||
import { AddonModChoicePrefetchHandler } from './handlers/prefetch';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to sync choices.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvider<AddonModChoiceSyncResult> {
 | 
			
		||||
 | 
			
		||||
    static readonly AUTO_SYNCED = 'addon_mod_choice_autom_synced';
 | 
			
		||||
 | 
			
		||||
    protected componentTranslatableString = 'choice';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModChoiceSyncProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the ID of a choice sync.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param userId User the responses belong to.
 | 
			
		||||
     * @return Sync ID.
 | 
			
		||||
     */
 | 
			
		||||
    protected getSyncId(choiceId: number, userId: number): string {
 | 
			
		||||
        return choiceId + '#' + userId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize all the choices in a certain site or in all sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @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 if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    syncAllChoices(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return this.syncOnSites('all choices', this.syncAllChoicesFunc.bind(this, !!force), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync all pending choices 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 syncAllChoicesFunc(force: boolean, siteId: string): Promise<void> {
 | 
			
		||||
        const responses = await AddonModChoiceOffline.getResponses(siteId);
 | 
			
		||||
 | 
			
		||||
        // Sync all responses.
 | 
			
		||||
        await Promise.all(responses.map(async (response) => {
 | 
			
		||||
            const result = force ?
 | 
			
		||||
                await this.syncChoice(response.choiceid, response.userid, siteId) :
 | 
			
		||||
                await this.syncChoiceIfNeeded(response.choiceid, response.userid, siteId);
 | 
			
		||||
 | 
			
		||||
            if (result?.updated) {
 | 
			
		||||
                // Sync successful, send event.
 | 
			
		||||
                CoreEvents.trigger(AddonModChoiceSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    choiceId: response.choiceid,
 | 
			
		||||
                    userId: response.userid,
 | 
			
		||||
                    warnings: result.warnings,
 | 
			
		||||
                }, siteId);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync an choice only if a certain time has passed since the last time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID to be synced.
 | 
			
		||||
     * @param userId User the answers belong to.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the choice is synced or it doesn't need to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async syncChoiceIfNeeded(choiceId: number, userId: number, siteId?: string): Promise<AddonModChoiceSyncResult | undefined> {
 | 
			
		||||
        const syncId = this.getSyncId(choiceId, userId);
 | 
			
		||||
 | 
			
		||||
        const needed = await this.isSyncNeeded(syncId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (needed) {
 | 
			
		||||
            return this.syncChoice(choiceId, userId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a choice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID to be synced.
 | 
			
		||||
     * @param userId User the answers belong to.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async syncChoice(choiceId: number, userId?: number, siteId?: string): Promise<AddonModChoiceSyncResult> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        userId = userId || site.getUserId();
 | 
			
		||||
        siteId = site.getId();
 | 
			
		||||
 | 
			
		||||
        const syncId = this.getSyncId(choiceId, userId);
 | 
			
		||||
        if (this.isSyncing(syncId, siteId)) {
 | 
			
		||||
            // There's already a sync ongoing for this discussion, return the promise.
 | 
			
		||||
            return this.getOngoingSync(syncId, siteId)!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Try to sync choice '${choiceId}' for user '${userId}'`);
 | 
			
		||||
 | 
			
		||||
        return this.addOngoingSync(syncId, this.performSync(choiceId, userId, siteId), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize a choice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID to be synced.
 | 
			
		||||
     * @param userId User the answers belong to.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected async performSync(choiceId: number, userId: number, siteId?: string): Promise<AddonModChoiceSyncResult> {
 | 
			
		||||
        const syncId = this.getSyncId(choiceId, userId);
 | 
			
		||||
        const result: AddonModChoiceSyncResult = {
 | 
			
		||||
            warnings: [],
 | 
			
		||||
            updated: false,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Sync offline logs.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(AddonModChoiceProvider.COMPONENT, choiceId, siteId));
 | 
			
		||||
 | 
			
		||||
        const data = await CoreUtils.ignoreErrors(AddonModChoiceOffline.getResponse(choiceId, siteId, userId));
 | 
			
		||||
 | 
			
		||||
        if (!data || !data.choiceid) {
 | 
			
		||||
            // Nothing to sync. Set sync time.
 | 
			
		||||
            await this.setSyncTime(syncId, siteId);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // Cannot sync in offline.
 | 
			
		||||
            throw new CoreNetworkError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const courseId = data.courseid;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // Send the responses.
 | 
			
		||||
            if (data.deleting) {
 | 
			
		||||
                // A user has deleted some responses.
 | 
			
		||||
                await AddonModChoice.deleteResponsesOnline(choiceId, data.responses, siteId);
 | 
			
		||||
            } else {
 | 
			
		||||
                // A user has added some responses.
 | 
			
		||||
                await AddonModChoice.submitResponseOnline(choiceId, data.responses, siteId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
 | 
			
		||||
            await AddonModChoiceOffline.deleteResponse(choiceId, siteId, userId);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (!CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // Couldn't connect to server, reject.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // The WebService has thrown an error, this means that responses cannot be submitted. Delete them.
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
 | 
			
		||||
            await AddonModChoiceOffline.deleteResponse(choiceId, siteId, userId);
 | 
			
		||||
 | 
			
		||||
            // Responses deleted, add a warning.
 | 
			
		||||
            result.warnings.push(Translate.instant('core.warningofflinedatadeleted', {
 | 
			
		||||
                component: this.componentTranslate,
 | 
			
		||||
                name: data.name,
 | 
			
		||||
                error: CoreTextUtils.getErrorMessageFromError(error),
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Data has been sent to server, prefetch choice if needed.
 | 
			
		||||
        try {
 | 
			
		||||
            const module = await CoreCourse.getModuleBasicInfoByInstance(choiceId, 'choice', siteId);
 | 
			
		||||
 | 
			
		||||
            await this.prefetchAfterUpdate(AddonModChoicePrefetchHandler.instance, module, courseId, undefined, siteId);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sync finished, set sync time.
 | 
			
		||||
        await this.setSyncTime(syncId, siteId);
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceSync = makeSingleton(AddonModChoiceSyncProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a choice sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceSyncResult = {
 | 
			
		||||
    warnings: string[]; // List of warnings.
 | 
			
		||||
    updated: boolean; // Whether some data was sent to the server or offline data was updated.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to AUTO_SYNCED event.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceAutoSyncData = {
 | 
			
		||||
    choiceId: number;
 | 
			
		||||
    userId: number;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										603
									
								
								src/addons/mod/choice/services/choice.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										603
									
								
								src/addons/mod/choice/services/choice.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,603 @@
 | 
			
		||||
// (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 { CoreWSError } from '@classes/errors/wserror';
 | 
			
		||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModChoiceOffline } from './choice-offline';
 | 
			
		||||
import { AddonModChoiceAutoSyncData, AddonModChoiceSyncProvider } from './choice-sync';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'mmaModChoice:';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service that provides some features for choices.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly COMPONENT = 'mmaModChoice';
 | 
			
		||||
 | 
			
		||||
    static readonly RESULTS_NOT = 0;
 | 
			
		||||
    static readonly RESULTS_AFTER_ANSWER = 1;
 | 
			
		||||
    static readonly RESULTS_AFTER_CLOSE = 2;
 | 
			
		||||
    static readonly RESULTS_ALWAYS = 3;
 | 
			
		||||
 | 
			
		||||
    static readonly PUBLISH_ANONYMOUS = false;
 | 
			
		||||
    static readonly PUBLISH_NAMES = true;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if results can be seen by a student. The student can see the results if:
 | 
			
		||||
     *     - they're always published, OR
 | 
			
		||||
     *     - they're published after the choice is closed and it's closed, OR
 | 
			
		||||
     *     - they're published after answering and the user has answered.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choice Choice to check.
 | 
			
		||||
     * @param hasAnswered True if user has answered the choice, false otherwise.
 | 
			
		||||
     * @return True if the students can see the results.
 | 
			
		||||
     */
 | 
			
		||||
    canStudentSeeResults(choice: AddonModChoiceChoice, hasAnswered: boolean): boolean {
 | 
			
		||||
        const now = Date.now();
 | 
			
		||||
 | 
			
		||||
        return choice.showresults === AddonModChoiceProvider.RESULTS_ALWAYS ||
 | 
			
		||||
            choice.showresults === AddonModChoiceProvider.RESULTS_AFTER_CLOSE && choice.timeclose && choice.timeclose <= now ||
 | 
			
		||||
            choice.showresults === AddonModChoiceProvider.RESULTS_AFTER_ANSWER && hasAnswered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete responses from a choice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param name Choice name.
 | 
			
		||||
     * @param courseId Course ID the choice belongs to.
 | 
			
		||||
     * @param responses IDs of the answers. If not defined, delete all the answers of the current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with boolean: true if response was sent to server, false if stored in device.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteResponses(
 | 
			
		||||
        choiceId: number,
 | 
			
		||||
        name: string,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        responses?: number[],
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
        responses = responses || [];
 | 
			
		||||
 | 
			
		||||
        // Convenience function to store a message to be synchronized later.
 | 
			
		||||
        const storeOffline = async (): Promise<boolean> => {
 | 
			
		||||
            await AddonModChoiceOffline.saveResponse(choiceId, name, courseId, responses!, true, siteId);
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // App is offline, store the action.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If there's already a response to be sent to the server, discard it first.
 | 
			
		||||
        await AddonModChoiceOffline.deleteResponse(choiceId, siteId);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.deleteResponsesOnline(choiceId, responses, siteId);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error, this means that responses cannot be submitted.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Couldn't connect to server, store in offline.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete responses from a choice. It will fail if offline or cannot connect.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param responses IDs of the answers. If not defined, delete all the answers of the current user.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when responses are successfully deleted.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteResponsesOnline(choiceId: number, responses?: number[], siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const params: AddonModChoiceDeleteChoiceResponsesWSParams = {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
            responses: responses,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const response = await site.write<CoreStatusWithWarningsWSResponse>('mod_choice_delete_choice_responses', params);
 | 
			
		||||
 | 
			
		||||
        // Other errors ocurring.
 | 
			
		||||
        if (response.status === false) {
 | 
			
		||||
            if (response.warnings?.[0]) {
 | 
			
		||||
                throw new CoreWSError(response.warnings[0]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new CoreError('Cannot delete responses.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Invalidate related data.
 | 
			
		||||
        await CoreUtils.ignoreErrors(Promise.all([
 | 
			
		||||
            this.invalidateOptions(choiceId, site.id),
 | 
			
		||||
            this.invalidateResults(choiceId, site.id),
 | 
			
		||||
        ]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for choice data WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getChoiceDataCacheKey(courseId: number): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'choice:' + courseId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for choice options WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getChoiceOptionsCacheKey(choiceId: number): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'options:' + choiceId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get cache key for choice results WS calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    protected getChoiceResultsCacheKey(choiceId: number): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + 'results:' + choiceId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a choice with key=value. If more than one is found, only the first will be returned.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param key Name of the property to check.
 | 
			
		||||
     * @param value Value to search.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the choice is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    protected async getChoiceByDataKey(
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        key: string,
 | 
			
		||||
        value: unknown,
 | 
			
		||||
        options: CoreSitesCommonWSOptions = {},
 | 
			
		||||
    ): Promise<AddonModChoiceChoice> {
 | 
			
		||||
        const site = await CoreSites.getSite(options.siteId);
 | 
			
		||||
 | 
			
		||||
        const params: AddonModChoiceGetChoicesByCoursesWSParams = {
 | 
			
		||||
            courseids: [courseId],
 | 
			
		||||
        };
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getChoiceDataCacheKey(courseId),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_RARELY,
 | 
			
		||||
            component: AddonModChoiceProvider.COMPONENT,
 | 
			
		||||
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const response = await site.read<AddonModChoiceGetChoicesByCoursesWSResponse>(
 | 
			
		||||
            'mod_choice_get_choices_by_courses',
 | 
			
		||||
            params,
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const currentChoice = response.choices.find((choice) => choice[key] == value);
 | 
			
		||||
        if (currentChoice) {
 | 
			
		||||
            return currentChoice;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new CoreError('Choice not found.');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a choice by course module ID.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param cmId Course module ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the choice is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    getChoice(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModChoiceChoice> {
 | 
			
		||||
        return this.getChoiceByDataKey(courseId, 'coursemodule', cmId, options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a choice by ID.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the choice is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    getChoiceById(courseId: number, choiceId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModChoiceChoice> {
 | 
			
		||||
        return this.getChoiceByDataKey(courseId, 'id', choiceId, options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get choice options.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with choice options.
 | 
			
		||||
     */
 | 
			
		||||
    async getOptions(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChoiceOption[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(options.siteId);
 | 
			
		||||
 | 
			
		||||
        const params: AddonModChoiceGetChoiceOptionsWSParams = {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getChoiceOptionsCacheKey(choiceId),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_RARELY,
 | 
			
		||||
            component: AddonModChoiceProvider.COMPONENT,
 | 
			
		||||
            componentId: options.cmId,
 | 
			
		||||
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const response = await site.read<AddonModChoiceGetChoiceOptionsWSResponse>(
 | 
			
		||||
            'mod_choice_get_choice_options',
 | 
			
		||||
            params,
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return response.options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get choice results.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with choice results.
 | 
			
		||||
     */
 | 
			
		||||
    async getResults(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChoiceResult[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(options.siteId);
 | 
			
		||||
 | 
			
		||||
        const params: AddonModChoiceGetChoiceResultsWSParams = {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
        };
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getChoiceOptionsCacheKey(choiceId),
 | 
			
		||||
            component: AddonModChoiceProvider.COMPONENT,
 | 
			
		||||
            componentId: options.cmId,
 | 
			
		||||
            ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const response = await site.read<AddonModChoiceGetChoiceResultsWSResponse>(
 | 
			
		||||
            'mod_choice_get_choice_results',
 | 
			
		||||
            params,
 | 
			
		||||
            preSets,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return response.options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate choice data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateChoiceData(courseId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.invalidateWsCacheForKey(this.getChoiceDataCacheKey(courseId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate the prefetched content.
 | 
			
		||||
     *
 | 
			
		||||
     * @param moduleId The module ID.
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const choice = await this.getChoice(courseId, moduleId);
 | 
			
		||||
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            this.invalidateChoiceData(courseId),
 | 
			
		||||
            this.invalidateOptions(choice.id),
 | 
			
		||||
            this.invalidateResults(choice.id),
 | 
			
		||||
            CoreFilepool.invalidateFilesByComponent(siteId, AddonModChoiceProvider.COMPONENT, moduleId),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate choice options.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateOptions(choiceId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await  site.invalidateWsCacheForKey(this.getChoiceOptionsCacheKey(choiceId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate choice results.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateResults(choiceId: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await  site.invalidateWsCacheForKey(this.getChoiceResultsCacheKey(choiceId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Report the choice as being viewed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id Choice ID.
 | 
			
		||||
     * @param name Name of the choice.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the WS call is successful.
 | 
			
		||||
     */
 | 
			
		||||
    logView(id: number, name?: string, siteId?: string): Promise<void> {
 | 
			
		||||
        const params: AddonModChoiceViewChoiceWSParams = {
 | 
			
		||||
            choiceid: id,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return CoreCourseLogHelper.logSingle(
 | 
			
		||||
            'mod_choice_view_choice',
 | 
			
		||||
            params,
 | 
			
		||||
            AddonModChoiceProvider.COMPONENT,
 | 
			
		||||
            id,
 | 
			
		||||
            name,
 | 
			
		||||
            'choice',
 | 
			
		||||
            {},
 | 
			
		||||
            siteId,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send a response to a choice to Moodle.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param name Choice name.
 | 
			
		||||
     * @param courseId Course ID the choice belongs to.
 | 
			
		||||
     * @param responses IDs of selected options.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with boolean: true if response was sent to server, false if stored in device.
 | 
			
		||||
     */
 | 
			
		||||
    async submitResponse(choiceId: number, name: string, courseId: number, responses: number[], siteId?: string): Promise<boolean> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        // Convenience function to store a message to be synchronized later.
 | 
			
		||||
        const storeOffline = async (): Promise<boolean> => {
 | 
			
		||||
            await AddonModChoiceOffline.saveResponse(choiceId, name, courseId, responses, false, siteId);
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // App is offline, store the action.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If there's already a response to be sent to the server, discard it first.
 | 
			
		||||
        await AddonModChoiceOffline.deleteResponse(choiceId, siteId);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await this.submitResponseOnline(choiceId, responses, siteId);
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (CoreUtils.isWebServiceError(error)) {
 | 
			
		||||
                // The WebService has thrown an error, this means that responses cannot be submitted.
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Couldn't connect to server, store it offline.
 | 
			
		||||
            return storeOffline();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Send a response to a choice to Moodle. It will fail if offline or cannot connect.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param responses IDs of selected options.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when responses are successfully submitted.
 | 
			
		||||
     */
 | 
			
		||||
    async submitResponseOnline(choiceId: number, responses: number[], siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const params: AddonModChoiceSubmitChoiceResponseWSParams = {
 | 
			
		||||
            choiceid: choiceId,
 | 
			
		||||
            responses: responses,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.write('mod_choice_submit_choice_response', params);
 | 
			
		||||
 | 
			
		||||
        // Invalidate related data.
 | 
			
		||||
        await CoreUtils.ignoreErrors(Promise.all([
 | 
			
		||||
            this.invalidateOptions(choiceId, siteId),
 | 
			
		||||
            this.invalidateResults(choiceId, siteId),
 | 
			
		||||
        ]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoice = makeSingleton(AddonModChoiceProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_get_choices_by_courses WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoicesByCoursesWSParams = {
 | 
			
		||||
    courseids?: number[]; // Array of course ids.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by mod_choice_get_choices_by_courses WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoicesByCoursesWSResponse = {
 | 
			
		||||
    choices: AddonModChoiceChoice[];
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Choice returned by mod_choice_get_choices_by_courses.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceChoice = {
 | 
			
		||||
    id: number; // Choice instance id.
 | 
			
		||||
    coursemodule: number; // Course module id.
 | 
			
		||||
    course: number; // Course id.
 | 
			
		||||
    name: string; // Choice name.
 | 
			
		||||
    intro: string; // The choice intro.
 | 
			
		||||
    introformat: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | 
			
		||||
    introfiles?: CoreWSExternalFile[]; // @since 3.2.
 | 
			
		||||
    publish?: boolean; // If choice is published.
 | 
			
		||||
    showresults?: number; // 0 never, 1 after answer, 2 after close, 3 always.
 | 
			
		||||
    display?: number; // Display mode (vertical, horizontal).
 | 
			
		||||
    allowupdate?: boolean; // Allow update.
 | 
			
		||||
    allowmultiple?: boolean; // Allow multiple choices.
 | 
			
		||||
    showunanswered?: boolean; // Show users who not answered yet.
 | 
			
		||||
    includeinactive?: boolean; // Include inactive users.
 | 
			
		||||
    limitanswers?: boolean; // Limit unswers.
 | 
			
		||||
    timeopen?: number; // Date of opening validity.
 | 
			
		||||
    timeclose?: number; // Date of closing validity.
 | 
			
		||||
    showpreview?: boolean; // Show preview before timeopen.
 | 
			
		||||
    timemodified?: number; // Time of last modification.
 | 
			
		||||
    completionsubmit?: boolean; // Completion on user submission.
 | 
			
		||||
    showavailable?: boolean; // Show available spaces. @since 3.10
 | 
			
		||||
    section?: number; // Course section id.
 | 
			
		||||
    visible?: boolean; // Visible.
 | 
			
		||||
    groupmode?: number; // Group mode.
 | 
			
		||||
    groupingid?: number; // Group id.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_delete_choice_responses WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceDeleteChoiceResponsesWSParams = {
 | 
			
		||||
    choiceid: number; // Choice instance id.
 | 
			
		||||
    responses?: number[]; // Array of response ids, empty for deleting all the current user responses.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_get_choice_options WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoiceOptionsWSParams = {
 | 
			
		||||
    choiceid: number; // Choice instance id.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by mod_choice_get_choice_options WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoiceOptionsWSResponse = {
 | 
			
		||||
    options: AddonModChoiceOption[]; // Options.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Option returned by mod_choice_get_choice_options.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceOption = {
 | 
			
		||||
    id: number; // Option id.
 | 
			
		||||
    text: string; // Text of the choice.
 | 
			
		||||
    maxanswers: number; // Maximum number of answers.
 | 
			
		||||
    displaylayout: boolean; // True for orizontal, otherwise vertical.
 | 
			
		||||
    countanswers: number; // Number of answers.
 | 
			
		||||
    checked: boolean; // We already answered.
 | 
			
		||||
    disabled: boolean; // Option disabled.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_get_choice_results WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoiceResultsWSParams = {
 | 
			
		||||
    choiceid: number; // Choice instance id.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by mod_choice_get_choice_results WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceGetChoiceResultsWSResponse = {
 | 
			
		||||
    options: AddonModChoiceResult[];
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Result returned by mod_choice_get_choice_results.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceResult = {
 | 
			
		||||
    id: number; // Choice instance id.
 | 
			
		||||
    text: string; // Text of the choice.
 | 
			
		||||
    maxanswer: number; // Maximum number of answers.
 | 
			
		||||
    userresponses: {
 | 
			
		||||
        userid: number; // User id.
 | 
			
		||||
        fullname: string; // User full name.
 | 
			
		||||
        profileimageurl: string; // Profile user image url.
 | 
			
		||||
        answerid?: number; // Answer id.
 | 
			
		||||
        timemodified?: number; // Time of modification.
 | 
			
		||||
    }[];
 | 
			
		||||
    numberofuser: number; // Number of users answers.
 | 
			
		||||
    percentageamount: number; // Percentage of users answers.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_view_choice WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceViewChoiceWSParams = {
 | 
			
		||||
    choiceid: number; // Choice instance id.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of mod_choice_submit_choice_response WS.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceSubmitChoiceResponseWSParams = {
 | 
			
		||||
    choiceid: number; // Choice instance id.
 | 
			
		||||
    responses: number[]; // Array of response ids.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
declare module '@singletons/events' {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Augment CoreEventsData interface with events specific to this service.
 | 
			
		||||
     *
 | 
			
		||||
     * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 | 
			
		||||
     */
 | 
			
		||||
    export interface CoreEventsData {
 | 
			
		||||
        [AddonModChoiceSyncProvider.AUTO_SYNCED]: AddonModChoiceAutoSyncData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								src/addons/mod/choice/services/database/choice.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/addons/mod/choice/services/database/choice.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Database variables for AddonModChoiceOfflineProvider.
 | 
			
		||||
 */
 | 
			
		||||
export const RESPONSES_TABLE_NAME = 'addon_mod_choice_responses';
 | 
			
		||||
export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'AddonModChoiceOfflineProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: RESPONSES_TABLE_NAME,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'choiceid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'name',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'userid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'responses',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'deleting',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timecreated',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['choiceid', 'userid'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Response data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModChoiceResponsesDBRecord = {
 | 
			
		||||
    choiceid: number;
 | 
			
		||||
    userid: number;
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    responses: string;
 | 
			
		||||
    deleting: number;
 | 
			
		||||
    timecreated: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										33
									
								
								src/addons/mod/choice/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addons/mod/choice/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to choice index.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModChoiceIndexLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModChoice', 'choice');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceIndexLinkHandler = makeSingleton(AddonModChoiceIndexLinkHandlerService);
 | 
			
		||||
							
								
								
									
										33
									
								
								src/addons/mod/choice/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/addons/mod/choice/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// (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';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to choice list page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceListLinkHandlerService extends CoreContentLinksModuleListHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModChoiceListLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModChoice', 'choice');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceListLinkHandler = makeSingleton(AddonModChoiceListLinkHandlerService);
 | 
			
		||||
							
								
								
									
										83
									
								
								src/addons/mod/choice/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/addons/mod/choice/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
			
		||||
// (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 { AddonModChoiceIndexComponent } from '../../components/index';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support choice modules.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceModuleHandlerService implements CoreCourseModuleHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'mod_choice';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModChoice';
 | 
			
		||||
    modName = 'choice';
 | 
			
		||||
 | 
			
		||||
    supportedFeatures = {
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_GROUPINGS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_MOD_INTRO]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_GRADE_HAS_GRADE]: false,
 | 
			
		||||
        [CoreConstants.FEATURE_GRADE_OUTCOMES]: false,
 | 
			
		||||
        [CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
 | 
			
		||||
        [CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
 | 
			
		||||
            title: module.name,
 | 
			
		||||
            class: 'addon-mod_choice-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(AddonModChoiceModuleHandlerService.PAGE_NAME + routeParams, options);
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getMainComponent(): Promise<Type<unknown>> {
 | 
			
		||||
        return AddonModChoiceIndexComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceModuleHandler = makeSingleton(AddonModChoiceModuleHandlerService);
 | 
			
		||||
							
								
								
									
										157
									
								
								src/addons/mod/choice/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/addons/mod/choice/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,157 @@
 | 
			
		||||
// (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 { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
 | 
			
		||||
import { CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModChoice, AddonModChoiceProvider } from '../choice';
 | 
			
		||||
import { AddonModChoiceSync, AddonModChoiceSyncResult } from '../choice-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to prefetch choices.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoicePrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModChoice';
 | 
			
		||||
    modName = 'choice';
 | 
			
		||||
    component = AddonModChoiceProvider.COMPONENT;
 | 
			
		||||
    updatesNames = /^configuration$|^.*files$|^answers$/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    prefetch(module: CoreCourseAnyModuleData, courseId?: number, single?: boolean): Promise<void> {
 | 
			
		||||
        return this.prefetchPackage(module, courseId, this.prefetchChoice.bind(this, module, courseId, !!single));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a choice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @param single True if we're downloading a single module, false if we're downloading a whole section.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchChoice(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        single: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const commonOptions = {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
        const modOptions = {
 | 
			
		||||
            cmId: module.id,
 | 
			
		||||
            ...commonOptions, // Include all common options.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const choice = await AddonModChoice.getChoice(courseId, module.id, commonOptions);
 | 
			
		||||
 | 
			
		||||
        // Get the intro files.
 | 
			
		||||
        const introFiles = this.getIntroFilesFromInstance(module, choice);
 | 
			
		||||
 | 
			
		||||
        await Promise.all([
 | 
			
		||||
            AddonModChoice.getOptions(choice.id, modOptions),
 | 
			
		||||
            this.prefetchResults(choice.id, courseId, modOptions),
 | 
			
		||||
            CoreFilepool.addFilesToQueue(siteId, introFiles, AddonModChoiceProvider.COMPONENT, module.id),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch choice results.
 | 
			
		||||
     *
 | 
			
		||||
     * @param choiceId Choice ID.
 | 
			
		||||
     * @param modOptions Options.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchResults(
 | 
			
		||||
        choiceId: number,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        modOptions: CoreCourseCommonModWSOptions,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const options = await AddonModChoice.getResults(choiceId, modOptions);
 | 
			
		||||
 | 
			
		||||
        // If we can see the users that answered, prefetch their profile and avatar.
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        options.forEach((option) => {
 | 
			
		||||
            option.userresponses.forEach((response) => {
 | 
			
		||||
                if (response.userid) {
 | 
			
		||||
                    promises.push(CoreUser.getProfile(response.userid, courseId, false, modOptions.siteId));
 | 
			
		||||
                }
 | 
			
		||||
                if (response.profileimageurl) {
 | 
			
		||||
                    promises.push(CoreFilepool.addToQueueByUrl(modOptions.siteId!, response.profileimageurl).catch(() => {
 | 
			
		||||
                        // Ignore failures.
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSExternalFile[]> {
 | 
			
		||||
        const choice = await CoreUtils.ignoreErrors(AddonModChoice.getChoice(courseId, module.id));
 | 
			
		||||
 | 
			
		||||
        return this.getIntroFilesFromInstance(module, choice);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModChoice.invalidateContent(moduleId, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate WS calls needed to determine module status.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @return Promise resolved when invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModChoice.invalidateChoiceData(courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync a module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModChoiceSyncResult> {
 | 
			
		||||
        return AddonModChoiceSync.syncChoice(module.instance!, undefined, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoicePrefetchHandler = makeSingleton(AddonModChoicePrefetchHandlerService);
 | 
			
		||||
							
								
								
									
										51
									
								
								src/addons/mod/choice/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/addons/mod/choice/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
// (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 { AddonModChoiceSync } from '../choice-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Synchronization cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModChoiceSyncCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModChoiceSyncCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the process.
 | 
			
		||||
     * Receives the ID of the site affected, undefined for all sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId ID of the site affected, undefined for all sites.
 | 
			
		||||
     * @param force Wether the execution is forced (manual sync).
 | 
			
		||||
     * @return Promise resolved when done, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    execute(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return AddonModChoiceSync.syncAllChoices(siteId, force);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the time between consecutive executions.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Time between consecutive executions (in ms).
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return AddonModChoiceSync.syncInterval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModChoiceSyncCronHandler = makeSingleton(AddonModChoiceSyncCronHandlerService);
 | 
			
		||||
@ -29,6 +29,7 @@ import { AddonModLtiModule } from './lti/lti.module';
 | 
			
		||||
import { AddonModH5PActivityModule } from './h5pactivity/h5pactivity.module';
 | 
			
		||||
import { AddonModSurveyModule } from './survey/survey.module';
 | 
			
		||||
import { AddonModScormModule } from './scorm/scorm.module';
 | 
			
		||||
import { AddonModChoiceModule } from './choice/choice.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    declarations: [],
 | 
			
		||||
@ -48,6 +49,7 @@ import { AddonModScormModule } from './scorm/scorm.module';
 | 
			
		||||
        AddonModH5PActivityModule,
 | 
			
		||||
        AddonModSurveyModule,
 | 
			
		||||
        AddonModScormModule,
 | 
			
		||||
        AddonModChoiceModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [],
 | 
			
		||||
    exports: [],
 | 
			
		||||
 | 
			
		||||
@ -124,7 +124,7 @@ import { ADDON_MESSAGES_SERVICES } from '@addons/messages/messages.module';
 | 
			
		||||
import { ADDON_MOD_ASSIGN_SERVICES } from '@addons/mod/assign/assign.module';
 | 
			
		||||
import { ADDON_MOD_BOOK_SERVICES } from '@addons/mod/book/book.module';
 | 
			
		||||
// @todo import { ADDON_MOD_CHAT_SERVICES } from '@addons/mod/chat/chat.module';
 | 
			
		||||
// @todo import { ADDON_MOD_CHOICE_SERVICES } from '@addons/mod/choice/choice.module';
 | 
			
		||||
import { ADDON_MOD_CHOICE_SERVICES } from '@addons/mod/choice/choice.module';
 | 
			
		||||
// @todo import { ADDON_MOD_FEEDBACK_SERVICES } from '@addons/mod/feedback/feedback.module';
 | 
			
		||||
import { ADDON_MOD_FOLDER_SERVICES } from '@addons/mod/folder/folder.module';
 | 
			
		||||
import { ADDON_MOD_FORUM_SERVICES } from '@addons/mod/forum/forum.module';
 | 
			
		||||
@ -289,7 +289,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...ADDON_MOD_ASSIGN_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_BOOK_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_CHAT_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_CHOICE_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_CHOICE_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_FEEDBACK_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_FOLDER_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_FORUM_SERVICES,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user