forked from CIT/Vmeda.Online
		
	MOBILE-3641 feedback: Migrate services
This commit is contained in:
		
							parent
							
								
									163647b165
								
							
						
					
					
						commit
						d73b5d6160
					
				
							
								
								
									
										87
									
								
								src/addons/mod/feedback/feedback.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/addons/mod/feedback/feedback.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
// (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 { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
			
		||||
import { AddonModFeedbackComponentsModule } from './components/components.module';
 | 
			
		||||
import { OFFLINE_SITE_SCHEMA } from './services/database/feedback';
 | 
			
		||||
import { AddonModFeedbackProvider } from './services/feedback';
 | 
			
		||||
import { AddonModFeedbackHelperProvider } from './services/feedback-helper';
 | 
			
		||||
import { AddonModFeedbackOfflineProvider } from './services/feedback-offline';
 | 
			
		||||
import { AddonModFeedbackSyncProvider } from './services/feedback-sync';
 | 
			
		||||
import { AddonModFeedbackAnalysisLinkHandler } from './services/handlers/analysis-link';
 | 
			
		||||
import { AddonModFeedbackCompleteLinkHandler } from './services/handlers/complete-link';
 | 
			
		||||
import { AddonModFeedbackIndexLinkHandler } from './services/handlers/index-link';
 | 
			
		||||
import { AddonModFeedbackListLinkHandler } from './services/handlers/list-link';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService, AddonModFeedbackModuleHandler } from './services/handlers/module';
 | 
			
		||||
import { AddonModFeedbackPrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { AddonModFeedbackPrintLinkHandler } from './services/handlers/print-link';
 | 
			
		||||
import { AddonModFeedbackPushClickHandler } from './services/handlers/push-click';
 | 
			
		||||
import { AddonModFeedbackShowEntriesLinkHandler } from './services/handlers/show-entries-link';
 | 
			
		||||
import { AddonModFeedbackShowNonRespondentsLinkHandler } from './services/handlers/show-non-respondents-link';
 | 
			
		||||
import { AddonModFeedbackSyncCronHandler } from './services/handlers/sync-cron';
 | 
			
		||||
 | 
			
		||||
export const ADDON_MOD_FEEDBACK_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    AddonModFeedbackProvider,
 | 
			
		||||
    AddonModFeedbackOfflineProvider,
 | 
			
		||||
    AddonModFeedbackHelperProvider,
 | 
			
		||||
    AddonModFeedbackSyncProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonModFeedbackModuleHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('./feedback-lazy.module').then(m => m.AddonModFeedbackLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        AddonModFeedbackComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: CORE_SITE_SCHEMAS,
 | 
			
		||||
            useValue: [OFFLINE_SITE_SCHEMA],
 | 
			
		||||
            multi: true,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => () => {
 | 
			
		||||
                CoreCourseModuleDelegate.registerHandler(AddonModFeedbackModuleHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.registerHandler(AddonModFeedbackPrefetchHandler.instance);
 | 
			
		||||
                CoreCronDelegate.register(AddonModFeedbackSyncCronHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackIndexLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackListLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackAnalysisLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackCompleteLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackPrintLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackShowEntriesLinkHandler.instance);
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonModFeedbackShowNonRespondentsLinkHandler.instance);
 | 
			
		||||
                CorePushNotificationsDelegate.registerClickHandler(AddonModFeedbackPushClickHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModFeedbackModule {}
 | 
			
		||||
							
								
								
									
										38
									
								
								src/addons/mod/feedback/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/addons/mod/feedback/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
{
 | 
			
		||||
    "analysis": "Analysis",
 | 
			
		||||
    "anonymous": "Anonymous",
 | 
			
		||||
    "anonymous_entries": "Anonymous entries ({{$a}})",
 | 
			
		||||
    "average": "Average",
 | 
			
		||||
    "captchaofflinewarning": "Feedback with CAPTCHA cannot be completed offline, or if not configured, or if the server is down.",
 | 
			
		||||
    "complete_the_form": "Answer the questions",
 | 
			
		||||
    "completed_feedbacks": "Submitted answers",
 | 
			
		||||
    "continue_the_form": "Continue answering the questions",
 | 
			
		||||
    "feedback_is_not_open": "The feedback is not open",
 | 
			
		||||
    "feedback_submitted_offline": "This feedback has been saved to be submitted later.",
 | 
			
		||||
    "feedbackclose": "Allow answers to",
 | 
			
		||||
    "feedbackopen": "Allow answers from",
 | 
			
		||||
    "mapcourses": "Map feedback to courses",
 | 
			
		||||
    "maximal": "Maximum",
 | 
			
		||||
    "minimal": "Minimum",
 | 
			
		||||
    "mode": "Mode",
 | 
			
		||||
    "modulenameplural": "Feedback",
 | 
			
		||||
    "next_page": "Next page",
 | 
			
		||||
    "non_anonymous": "User's name will be logged and shown with answers",
 | 
			
		||||
    "non_anonymous_entries": "Non anonymous entries ({{$a}})",
 | 
			
		||||
    "non_respondents_students": "Non-respondent students ({{$a}})",
 | 
			
		||||
    "not_selected": "Not selected",
 | 
			
		||||
    "not_started": "Not started",
 | 
			
		||||
    "numberoutofrange": "Number out of range",
 | 
			
		||||
    "overview": "Overview",
 | 
			
		||||
    "page_after_submit": "Completion message",
 | 
			
		||||
    "preview": "Preview",
 | 
			
		||||
    "previous_page": "Previous page",
 | 
			
		||||
    "questions": "Questions",
 | 
			
		||||
    "response_nr": "Response number",
 | 
			
		||||
    "responses": "Responses",
 | 
			
		||||
    "save_entries": "Submit your answers",
 | 
			
		||||
    "show_entries": "Show responses",
 | 
			
		||||
    "show_nonrespondents": "Show non-respondents",
 | 
			
		||||
    "started": "Started",
 | 
			
		||||
    "this_feedback_is_already_submitted": "You've already completed this activity."
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								src/addons/mod/feedback/services/database/feedback.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/addons/mod/feedback/services/database/feedback.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
// (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 AddonModFeedbackOfflineProvider.
 | 
			
		||||
 */
 | 
			
		||||
export const FEEDBACK_TABLE_NAME = 'addon_mod_feedback_answers';
 | 
			
		||||
export const OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
 | 
			
		||||
    name: 'AddonModFeedbackOfflineProvider',
 | 
			
		||||
    version: 1,
 | 
			
		||||
    tables: [
 | 
			
		||||
        {
 | 
			
		||||
            name: FEEDBACK_TABLE_NAME,
 | 
			
		||||
            columns: [
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'feedbackid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'page',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'courseid',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'responses',
 | 
			
		||||
                    type: 'TEXT',
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    name: 'timemodified',
 | 
			
		||||
                    type: 'INTEGER',
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            primaryKeys: ['feedbackid', 'page'],
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Response data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackResponseDBRecord = {
 | 
			
		||||
    feedbackid: number;
 | 
			
		||||
    page: number;
 | 
			
		||||
    courseid: number;
 | 
			
		||||
    responses: string;
 | 
			
		||||
    timemodified: number;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										585
									
								
								src/addons/mod/feedback/services/feedback-helper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										585
									
								
								src/addons/mod/feedback/services/feedback-helper.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,585 @@
 | 
			
		||||
// (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 { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModFeedback,
 | 
			
		||||
    AddonModFeedbackGetNonRespondentsWSResponse,
 | 
			
		||||
    AddonModFeedbackGetResponsesAnalysisWSResponse,
 | 
			
		||||
    AddonModFeedbackGroupPaginatedOptions,
 | 
			
		||||
    AddonModFeedbackItem,
 | 
			
		||||
    AddonModFeedbackProvider,
 | 
			
		||||
    AddonModFeedbackResponseValue,
 | 
			
		||||
    AddonModFeedbackWSAttempt,
 | 
			
		||||
    AddonModFeedbackWSNonRespondent,
 | 
			
		||||
} from './feedback';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService } from './handlers/module';
 | 
			
		||||
 | 
			
		||||
const MODE_RESPONSETIME = 1;
 | 
			
		||||
const MODE_COURSE = 2;
 | 
			
		||||
const MODE_CATEGORY = 3;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service that provides helper functions for feedbacks.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackHelperProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves a list of students who didn't submit the feedback with extra info.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the info is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getNonRespondents(
 | 
			
		||||
        feedbackId: number,
 | 
			
		||||
        options: AddonModFeedbackGroupPaginatedOptions = {},
 | 
			
		||||
    ): Promise<AddonModFeedbackGetNonRespondents> {
 | 
			
		||||
        const responses: AddonModFeedbackGetNonRespondents = await AddonModFeedback.getNonRespondents(feedbackId, options);
 | 
			
		||||
 | 
			
		||||
        responses.users = await this.addImageProfile(responses.users);
 | 
			
		||||
 | 
			
		||||
        return responses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get page items responses to be sent.
 | 
			
		||||
     *
 | 
			
		||||
     * @param items Items where the values are.
 | 
			
		||||
     * @return Responses object to be sent.
 | 
			
		||||
     */
 | 
			
		||||
    getPageItemsResponses(items: AddonModFeedbackFormItem[]): Record<string, AddonModFeedbackResponseValue> {
 | 
			
		||||
        const responses: Record<string, AddonModFeedbackResponseValue> = {};
 | 
			
		||||
 | 
			
		||||
        items.forEach((itemData) => {
 | 
			
		||||
            let answered = false;
 | 
			
		||||
            itemData.hasError = false;
 | 
			
		||||
 | 
			
		||||
            if (itemData.typ == 'captcha') {
 | 
			
		||||
                const value = itemData.value || '';
 | 
			
		||||
                const name = itemData.typ + '_' + itemData.id;
 | 
			
		||||
 | 
			
		||||
                answered = !!value;
 | 
			
		||||
                responses[name] = 1;
 | 
			
		||||
                responses['g-recaptcha-response'] = value;
 | 
			
		||||
                responses['recaptcha_element'] = 'dummyvalue';
 | 
			
		||||
 | 
			
		||||
                if (itemData.required && !answered) {
 | 
			
		||||
                    // Check if it has any value.
 | 
			
		||||
                    itemData.isEmpty = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    itemData.isEmpty = false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (itemData.hasvalue) {
 | 
			
		||||
                let name: string;
 | 
			
		||||
                let value: AddonModFeedbackResponseValue;
 | 
			
		||||
                const nameTemp = itemData.typ + '_' + itemData.id;
 | 
			
		||||
 | 
			
		||||
                if (this.isMultiChoiceItem(itemData) && itemData.subtype == 'c') {
 | 
			
		||||
                    name = nameTemp + '[0]';
 | 
			
		||||
                    responses[name] = 0;
 | 
			
		||||
                    itemData.choices.forEach((choice, index) => {
 | 
			
		||||
                        name = nameTemp + '[' + (index + 1) + ']';
 | 
			
		||||
                        value = choice.checked ? choice.value : 0;
 | 
			
		||||
                        if (!answered && value) {
 | 
			
		||||
                            answered = true;
 | 
			
		||||
                        }
 | 
			
		||||
                        responses[name] = value;
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.isMultiChoiceItem(itemData) && itemData.subtype != 'r') {
 | 
			
		||||
                        name = nameTemp + '[0]';
 | 
			
		||||
                    } else {
 | 
			
		||||
                        name = nameTemp;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (itemData.typ == 'multichoice' || itemData.typ == 'multichoicerated') {
 | 
			
		||||
                        value = itemData.value || 0;
 | 
			
		||||
                    } else if (this.isNumericItem(itemData)) {
 | 
			
		||||
                        value = itemData.value || itemData.value  == 0 ? itemData.value : '';
 | 
			
		||||
 | 
			
		||||
                        if (value != '') {
 | 
			
		||||
                            if ((itemData.rangefrom != '' && value < itemData.rangefrom) ||
 | 
			
		||||
                                    (itemData.rangeto != '' && value > itemData.rangeto)) {
 | 
			
		||||
                                itemData.hasError = true;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        value = itemData.value || itemData.value  == 0 ? itemData.value : '';
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    answered = !!value;
 | 
			
		||||
                    responses[name] = value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (itemData.required && !answered) {
 | 
			
		||||
                    // Check if it has any value.
 | 
			
		||||
                    itemData.isEmpty = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    itemData.isEmpty = false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return responses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the feedback user responses with extra info.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved when the info is retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getResponsesAnalysis(
 | 
			
		||||
        feedbackId: number,
 | 
			
		||||
        options: AddonModFeedbackGroupPaginatedOptions = {},
 | 
			
		||||
    ): Promise<AddonModFeedbackResponsesAnalysis> {
 | 
			
		||||
        const responses: AddonModFeedbackResponsesAnalysis = await AddonModFeedback.getResponsesAnalysis(feedbackId, options);
 | 
			
		||||
 | 
			
		||||
        responses.attempts = await this.addImageProfile(responses.attempts);
 | 
			
		||||
 | 
			
		||||
        return responses;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a show entries link.
 | 
			
		||||
     *
 | 
			
		||||
     * @param params URL params.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async handleShowEntriesLink(params: Record<string, string>, siteId?: string): Promise<void> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const module = await CoreCourse.getModuleBasicInfo(Number(params.id), siteId);
 | 
			
		||||
 | 
			
		||||
            if (typeof params.showcompleted == 'undefined') {
 | 
			
		||||
                // Param showcompleted not defined. Show entry list.
 | 
			
		||||
                await CoreNavigator.navigateToSitePath(
 | 
			
		||||
                    AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/respondents`,
 | 
			
		||||
                    { siteId },
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const attempt = await AddonModFeedback.getAttempt(module.instance, Number(params.showcompleted), {
 | 
			
		||||
                cmId: module.id,
 | 
			
		||||
                readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
                siteId,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            await CoreNavigator.navigateToSitePath(
 | 
			
		||||
                AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/attempt/${attempt.id}`,
 | 
			
		||||
                {
 | 
			
		||||
                    params: {
 | 
			
		||||
                        feedbackId: module.instance,
 | 
			
		||||
                        attempt: attempt,
 | 
			
		||||
                    },
 | 
			
		||||
                    siteId,
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'Error opening link.');
 | 
			
		||||
        } finally {
 | 
			
		||||
            modal.dismiss();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add Image profile url field on some entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param entries Entries array to get profile from.
 | 
			
		||||
     * @return Returns the same array with the profileimageurl added if found.
 | 
			
		||||
     */
 | 
			
		||||
    protected async addImageProfile(entries: AddonModFeedbackWSAttempt[]): Promise<AddonModFeedbackAttempt[]>;
 | 
			
		||||
    protected async addImageProfile(entries: AddonModFeedbackWSNonRespondent[]): Promise<AddonModFeedbackNonRespondent[]>;
 | 
			
		||||
    protected async addImageProfile(
 | 
			
		||||
        entries: (AddonModFeedbackWSAttempt | AddonModFeedbackWSNonRespondent)[],
 | 
			
		||||
    ): Promise<(AddonModFeedbackAttempt | AddonModFeedbackNonRespondent)[]> {
 | 
			
		||||
        return await Promise.all(entries.map(async (entry: AddonModFeedbackAttempt | AddonModFeedbackNonRespondent) => {
 | 
			
		||||
            try {
 | 
			
		||||
                const user = await CoreUser.getProfile(entry.userid, entry.courseid, true);
 | 
			
		||||
 | 
			
		||||
                entry.profileimageurl = user.profileimageurl;
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Error getting profile, resolve promise without adding any extra data.
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return entry;
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Label.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormLabel(item: AddonModFeedbackItem): AddonModFeedbackFormBasicItem {
 | 
			
		||||
        item.name = '';
 | 
			
		||||
        item.presentation = CoreTextUtils.replacePluginfileUrls(item.presentation, item.itemfiles);
 | 
			
		||||
 | 
			
		||||
        return Object.assign(item, {
 | 
			
		||||
            templateName: 'label',
 | 
			
		||||
            value: '',
 | 
			
		||||
            hasTextInput: false,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Info.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormInfo(item: AddonModFeedbackItem): AddonModFeedbackFormBasicItem | undefined {
 | 
			
		||||
        const formItem: AddonModFeedbackFormBasicItem = Object.assign(item, {
 | 
			
		||||
            templateName: 'label',
 | 
			
		||||
            value: '',
 | 
			
		||||
            hasTextInput: false,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const type = parseInt(formItem.presentation, 10);
 | 
			
		||||
 | 
			
		||||
        if (type == MODE_COURSE || type == MODE_CATEGORY) {
 | 
			
		||||
            formItem.presentation = formItem.otherdata;
 | 
			
		||||
            formItem.value = typeof formItem.rawValue != 'undefined' ? formItem.rawValue : formItem.otherdata;
 | 
			
		||||
        } else if (type == MODE_RESPONSETIME) {
 | 
			
		||||
            formItem.value = '__CURRENT__TIMESTAMP__';
 | 
			
		||||
 | 
			
		||||
            const rawValue = Number(formItem.rawValue);
 | 
			
		||||
            const tempValue = isNaN(rawValue) ? Date.now() : rawValue * 1000;
 | 
			
		||||
            formItem.presentation = CoreTimeUtils.userDate(tempValue);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Errors on item, return false.
 | 
			
		||||
            return undefined;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return formItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Numeric.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormNumeric(item: AddonModFeedbackItem): AddonModFeedbackNumericItem {
 | 
			
		||||
 | 
			
		||||
        const range = item.presentation.split(AddonModFeedbackProvider.LINE_SEP) || [];
 | 
			
		||||
        const rangeFrom = range.length > 0 ? parseInt(range[0], 10) : undefined;
 | 
			
		||||
        const rangeTo = range.length > 1 ? parseInt(range[1], 10) : undefined;
 | 
			
		||||
 | 
			
		||||
        const formItem: AddonModFeedbackNumericItem = Object.assign(item, {
 | 
			
		||||
            templateName: 'numeric',
 | 
			
		||||
            value: typeof item.rawValue != 'undefined' ? Number(item.rawValue) : '',
 | 
			
		||||
            rangefrom: typeof rangeFrom == 'number' && !isNaN(rangeFrom) ? range[0] : '',
 | 
			
		||||
            rangeto: typeof rangeTo == 'number' && !isNaN(rangeTo) ? rangeTo : '',
 | 
			
		||||
            hasTextInput: true,
 | 
			
		||||
        });
 | 
			
		||||
        formItem.postfix = this.getNumericBoundariesForDisplay(formItem.rangefrom, formItem.rangeto);
 | 
			
		||||
 | 
			
		||||
        return formItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Text field.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormTextfield(item: AddonModFeedbackItem): AddonModFeedbackTextItem {
 | 
			
		||||
        return Object.assign(item, {
 | 
			
		||||
            templateName: 'textfield',
 | 
			
		||||
            length: Number(item.presentation.split(AddonModFeedbackProvider.LINE_SEP)[1]) || 255,
 | 
			
		||||
            value: typeof item.rawValue != 'undefined' ? item.rawValue : '',
 | 
			
		||||
            hasTextInput: true,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Textarea.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormTextarea(item: AddonModFeedbackItem): AddonModFeedbackFormBasicItem {
 | 
			
		||||
        return Object.assign(item, {
 | 
			
		||||
            templateName: 'textarea',
 | 
			
		||||
            value: typeof item.rawValue != 'undefined' ? item.rawValue : '',
 | 
			
		||||
            hasTextInput: true,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Multichoice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormMultichoice(item: AddonModFeedbackItem): AddonModFeedbackMultichoiceItem {
 | 
			
		||||
 | 
			
		||||
        let parts = item.presentation.split(AddonModFeedbackProvider.MULTICHOICE_TYPE_SEP) || [];
 | 
			
		||||
        const subType = parts.length > 0 && parts[0] ? parts[0] : 'r';
 | 
			
		||||
 | 
			
		||||
        const formItem: AddonModFeedbackMultichoiceItem = Object.assign(item, {
 | 
			
		||||
            templateName: 'multichoice-' + subType,
 | 
			
		||||
            subtype: subType,
 | 
			
		||||
            value: '',
 | 
			
		||||
            choices: [],
 | 
			
		||||
            hasTextInput: false,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        formItem.presentation = parts.length > 1 ? parts[1] : '';
 | 
			
		||||
        if (formItem.subtype != 'd') {
 | 
			
		||||
            parts = formItem.presentation.split(AddonModFeedbackProvider.MULTICHOICE_ADJUST_SEP) || [];
 | 
			
		||||
            formItem.presentation = parts.length > 0 ? parts[0] : '';
 | 
			
		||||
            // Horizontal are not supported right now. item.horizontal = parts.length > 1 && !!parts[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const choices = formItem.presentation.split(AddonModFeedbackProvider.LINE_SEP) || [];
 | 
			
		||||
        formItem.choices = choices.map((choice, index) => {
 | 
			
		||||
            const weightValue = choice.split(AddonModFeedbackProvider.MULTICHOICERATED_VALUE_SEP) || [''];
 | 
			
		||||
            choice = weightValue.length == 1 ? weightValue[0] : '(' + weightValue[0] + ') ' + weightValue[1];
 | 
			
		||||
 | 
			
		||||
            return { value: index + 1, label: choice };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (formItem.subtype === 'r' && formItem.options.search(AddonModFeedbackProvider.MULTICHOICE_HIDENOSELECT) == -1) {
 | 
			
		||||
            formItem.choices.unshift({ value: 0, label: Translate.instant('addon.mod_feedback.not_selected') });
 | 
			
		||||
            formItem.value = typeof formItem.rawValue != 'undefined' ? Number(formItem.rawValue) : 0;
 | 
			
		||||
        } else if (formItem.subtype === 'd') {
 | 
			
		||||
            formItem.choices.unshift({ value: 0, label: '' });
 | 
			
		||||
            formItem.value = typeof formItem.rawValue != 'undefined' ? Number(formItem.rawValue) : 0;
 | 
			
		||||
        } else if (formItem.subtype === 'c') {
 | 
			
		||||
            if (typeof formItem.rawValue != 'undefined') {
 | 
			
		||||
                formItem.rawValue = String(formItem.rawValue);
 | 
			
		||||
                const values = formItem.rawValue.split(AddonModFeedbackProvider.LINE_SEP);
 | 
			
		||||
                formItem.choices.forEach((choice) => {
 | 
			
		||||
                    for (const x in values) {
 | 
			
		||||
                        if (choice.value == Number(values[x])) {
 | 
			
		||||
                            choice.checked = true;
 | 
			
		||||
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            formItem.value = typeof formItem.rawValue != 'undefined' ? Number(formItem.rawValue) : '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return formItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Helper funtion for item type Captcha.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    protected getItemFormCaptcha(item: AddonModFeedbackItem): AddonModFeedbackCaptchaItem {
 | 
			
		||||
        const formItem: AddonModFeedbackCaptchaItem = Object.assign(item, {
 | 
			
		||||
            templateName: 'captcha',
 | 
			
		||||
            value: '',
 | 
			
		||||
            hasTextInput: false,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const data = <string[]> CoreTextUtils.parseJSON(item.otherdata);
 | 
			
		||||
        if (data && data.length > 3) {
 | 
			
		||||
            formItem.captcha = {
 | 
			
		||||
                recaptchapublickey: data[3],
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return formItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Process and returns item to print form.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item to process.
 | 
			
		||||
     * @param preview Previewing options.
 | 
			
		||||
     * @return Item processed to show form.
 | 
			
		||||
     */
 | 
			
		||||
    getItemForm(item: AddonModFeedbackItem, preview: boolean): AddonModFeedbackFormItem | undefined {
 | 
			
		||||
        switch (item.typ) {
 | 
			
		||||
            case 'label':
 | 
			
		||||
                return this.getItemFormLabel(item);
 | 
			
		||||
            case 'info':
 | 
			
		||||
                return this.getItemFormInfo(item);
 | 
			
		||||
            case 'numeric':
 | 
			
		||||
                return this.getItemFormNumeric(item);
 | 
			
		||||
            case 'textfield':
 | 
			
		||||
                return this.getItemFormTextfield(item);
 | 
			
		||||
            case 'textarea':
 | 
			
		||||
                return this.getItemFormTextarea(item);
 | 
			
		||||
            case 'multichoice':
 | 
			
		||||
                return this.getItemFormMultichoice(item);
 | 
			
		||||
            case 'multichoicerated':
 | 
			
		||||
                return this.getItemFormMultichoice(item);
 | 
			
		||||
            case 'pagebreak':
 | 
			
		||||
                if (!preview) {
 | 
			
		||||
                    // Pagebreaks are only used on preview.
 | 
			
		||||
                    return undefined;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            case 'captcha':
 | 
			
		||||
                // Captcha is not supported right now. However label will be shown.
 | 
			
		||||
                return this.getItemFormCaptcha(item);
 | 
			
		||||
            default:
 | 
			
		||||
                return undefined;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns human-readable boundaries (min - max).
 | 
			
		||||
     * Based on Moodle's get_boundaries_for_display.
 | 
			
		||||
     *
 | 
			
		||||
     * @param rangeFrom Range from.
 | 
			
		||||
     * @param rangeTo Range to.
 | 
			
		||||
     * @return Human-readable boundaries.
 | 
			
		||||
     */
 | 
			
		||||
    protected getNumericBoundariesForDisplay(rangeFrom: number | string, rangeTo: number | string): string {
 | 
			
		||||
        const rangeFromSet = typeof rangeFrom == 'number';
 | 
			
		||||
        const rangeToSet = typeof rangeTo == 'number';
 | 
			
		||||
 | 
			
		||||
        if (!rangeFromSet && rangeToSet) {
 | 
			
		||||
            return ' (' + Translate.instant('addon.mod_feedback.maximal') + ': ' + CoreUtils.formatFloat(rangeTo) + ')';
 | 
			
		||||
        } else if (rangeFromSet && !rangeToSet) {
 | 
			
		||||
            return ' (' + Translate.instant('addon.mod_feedback.minimal') + ': ' + CoreUtils.formatFloat(rangeFrom) + ')';
 | 
			
		||||
        } else if (!rangeFromSet && !rangeToSet) {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ' (' + CoreUtils.formatFloat(rangeFrom) + ' - ' + CoreUtils.formatFloat(rangeTo) + ')';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a form item is multichoice.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item.
 | 
			
		||||
     * @return Whether item is multichoice.
 | 
			
		||||
     */
 | 
			
		||||
    protected isMultiChoiceItem(item: AddonModFeedbackFormItem): item is AddonModFeedbackMultichoiceItem {
 | 
			
		||||
        return item.typ == 'multichoice';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a form item is numeric.
 | 
			
		||||
     *
 | 
			
		||||
     * @param item Item.
 | 
			
		||||
     * @return Whether item is numeric.
 | 
			
		||||
     */
 | 
			
		||||
    protected isNumericItem(item: AddonModFeedbackFormItem): item is AddonModFeedbackNumericItem {
 | 
			
		||||
        return item.typ == 'numeric';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackHelper = makeSingleton(AddonModFeedbackHelperProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Attempt with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackAttempt = AddonModFeedbackWSAttempt & {
 | 
			
		||||
    profileimageurl?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Non respondent with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackNonRespondent = AddonModFeedbackWSNonRespondent & {
 | 
			
		||||
    profileimageurl?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Non respondents with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackResponsesAnalysis = Omit<AddonModFeedbackGetResponsesAnalysisWSResponse, 'attempts'> & {
 | 
			
		||||
    attempts: AddonModFeedbackAttempt[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Non respondents with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackGetNonRespondents = Omit<AddonModFeedbackGetNonRespondentsWSResponse, 'users'> & {
 | 
			
		||||
    users: AddonModFeedbackNonRespondent[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Item with form data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackFormItem =
 | 
			
		||||
    AddonModFeedbackFormBasicItem | AddonModFeedbackNumericItem | AddonModFeedbackTextItem | AddonModFeedbackMultichoiceItem |
 | 
			
		||||
    AddonModFeedbackCaptchaItem;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Common calculated data for all form items.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackFormBasicItem = AddonModFeedbackItem & {
 | 
			
		||||
    templateName: string;
 | 
			
		||||
    value: AddonModFeedbackResponseValue;
 | 
			
		||||
    hasTextInput: boolean;
 | 
			
		||||
    isEmpty?: boolean;
 | 
			
		||||
    hasError?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Numeric item.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackNumericItem = AddonModFeedbackFormBasicItem & {
 | 
			
		||||
    rangefrom: number | string;
 | 
			
		||||
    rangeto: number | string;
 | 
			
		||||
    postfix?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Text item.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackTextItem = AddonModFeedbackFormBasicItem & {
 | 
			
		||||
    length: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Multichoice item.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackMultichoiceItem = AddonModFeedbackFormBasicItem & {
 | 
			
		||||
    subtype: string;
 | 
			
		||||
    choices: { value: number; label: string; checked?: boolean }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Captcha item.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackCaptchaItem = AddonModFeedbackFormBasicItem & {
 | 
			
		||||
    captcha?: {
 | 
			
		||||
        recaptchapublickey: string;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										162
									
								
								src/addons/mod/feedback/services/feedback-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/addons/mod/feedback/services/feedback-offline.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,162 @@
 | 
			
		||||
// (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 { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedbackResponseDBRecord, FEEDBACK_TABLE_NAME } from './database/feedback';
 | 
			
		||||
import { AddonModFeedbackResponseValue } from './feedback';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle offline feedback.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackOfflineProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete the stored for a certain feedback page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param page Page of the form to delete responses from.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if deleted, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async deleteFeedbackPageResponses(feedbackId: number, page: number, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.getDb().deleteRecords(FEEDBACK_TABLE_NAME, <Partial<AddonModFeedbackResponseDBRecord>> {
 | 
			
		||||
            feedbackid: feedbackId,
 | 
			
		||||
            page: page,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the stored feedback responses data from all the feedback.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with entries.
 | 
			
		||||
     */
 | 
			
		||||
    async getAllFeedbackResponses(siteId?: string): Promise<AddonModFeedbackOfflineResponse[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const entries = await site.getDb().getAllRecords<AddonModFeedbackResponseDBRecord>(FEEDBACK_TABLE_NAME);
 | 
			
		||||
 | 
			
		||||
        return entries.map(entry => this.parseResponse(entry));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all the stored responses from a certain feedback.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with responses.
 | 
			
		||||
     */
 | 
			
		||||
    async getFeedbackResponses(feedbackId: number, siteId?: string): Promise<AddonModFeedbackOfflineResponse[]> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const entries = await site.getDb().getRecords<AddonModFeedbackResponseDBRecord>(FEEDBACK_TABLE_NAME, {
 | 
			
		||||
            feedbackid: feedbackId,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return entries.map(entry => this.parseResponse(entry));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the stored responses for a certain feedback page.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param page Page of the form to get responses from.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with responses.
 | 
			
		||||
     */
 | 
			
		||||
    async getFeedbackPageResponses(feedbackId: number, page: number, siteId?: string): Promise<AddonModFeedbackOfflineResponse> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const conditions: Partial<AddonModFeedbackResponseDBRecord> = {
 | 
			
		||||
            feedbackid: feedbackId,
 | 
			
		||||
            page: page,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const entry = await site.getDb().getRecord<AddonModFeedbackResponseDBRecord>(FEEDBACK_TABLE_NAME, conditions);
 | 
			
		||||
 | 
			
		||||
        return this.parseResponse(entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get if the feedback have something to be synced.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with true if the feedback have something to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async hasFeedbackOfflineData(feedbackId: number, siteId?: string): Promise<boolean> {
 | 
			
		||||
        const responses = await this.getFeedbackResponses(feedbackId, siteId);
 | 
			
		||||
 | 
			
		||||
        return !!responses.length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse "options" and "attachments" columns of a fetched record.
 | 
			
		||||
     *
 | 
			
		||||
     * @param records Record object
 | 
			
		||||
     * @return Record object with columns parsed.
 | 
			
		||||
     */
 | 
			
		||||
    protected parseResponse(record: AddonModFeedbackResponseDBRecord): AddonModFeedbackOfflineResponse {
 | 
			
		||||
        return Object.assign(record, {
 | 
			
		||||
            responses: <Record<string, AddonModFeedbackResponseValue>> CoreTextUtils.parseJSON(record.responses),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Save page responses to be sent later.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param page The page being processed.
 | 
			
		||||
     * @param responses The data to be processed the key is the field name (usually type[index]_id)
 | 
			
		||||
     * @param courseId Course ID the feedback belongs to.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if stored, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    async saveResponses(
 | 
			
		||||
        feedbackId: number,
 | 
			
		||||
        page: number,
 | 
			
		||||
        responses: Record<string, AddonModFeedbackResponseValue>,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const entry: AddonModFeedbackResponseDBRecord = {
 | 
			
		||||
            feedbackid: feedbackId,
 | 
			
		||||
            page: page,
 | 
			
		||||
            courseid: courseId,
 | 
			
		||||
            responses: JSON.stringify(responses),
 | 
			
		||||
            timemodified: CoreTimeUtils.timestamp(),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await site.getDb().insertRecord(FEEDBACK_TABLE_NAME, entry);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackOffline = makeSingleton(AddonModFeedbackOfflineProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Feedback offline response with parsed data.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackOfflineResponse = Omit<AddonModFeedbackResponseDBRecord, 'responses'> & {
 | 
			
		||||
    responses: Record<string, AddonModFeedbackResponseValue>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										305
									
								
								src/addons/mod/feedback/services/feedback-sync.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								src/addons/mod/feedback/services/feedback-sync.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,305 @@
 | 
			
		||||
// (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 { CoreSyncBlockedError } from '@classes/base-sync';
 | 
			
		||||
import { CoreNetworkError } from '@classes/errors/network-error';
 | 
			
		||||
import { CoreCourseActivitySyncBaseProvider } from '@features/course/classes/activity-sync';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreSync } from '@services/sync';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { AddonModFeedback, AddonModFeedbackProvider, AddonModFeedbackWSFeedback } from './feedback';
 | 
			
		||||
import { AddonModFeedbackOffline, AddonModFeedbackOfflineResponse } from './feedback-offline';
 | 
			
		||||
import { AddonModFeedbackPrefetchHandler, AddonModFeedbackPrefetchHandlerService } from './handlers/prefetch';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to sync feedbacks.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProvider<AddonModFeedbackSyncResult> {
 | 
			
		||||
 | 
			
		||||
    static readonly AUTO_SYNCED = 'addon_mod_feedback_autom_synced';
 | 
			
		||||
 | 
			
		||||
    protected componentTranslatableString = 'feedback';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModFeedbackSyncProvider');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    prefetchAfterUpdate(
 | 
			
		||||
        prefetchHandler: AddonModFeedbackPrefetchHandlerService,
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        regex?: RegExp,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        regex = regex || /^.*files$|^timers/;
 | 
			
		||||
 | 
			
		||||
        return super.prefetchAfterUpdate(prefetchHandler, module, courseId, regex, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Try to synchronize all the feedbacks 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.
 | 
			
		||||
     */
 | 
			
		||||
    syncAllFeedbacks(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return this.syncOnSites('all feedbacks', this.syncAllFeedbacksFunc.bind(this, !!force), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync all pending feedbacks on a site.
 | 
			
		||||
     *
 | 
			
		||||
     * @param force Wether to force sync not depending on last execution.
 | 
			
		||||
     * @param siteId Site ID to sync. If not defined, sync all sites.
 | 
			
		||||
     * @param Promise resolved if sync is successful, rejected if sync fails.
 | 
			
		||||
     */
 | 
			
		||||
    protected async syncAllFeedbacksFunc(force: boolean, siteId?: string): Promise<void> {
 | 
			
		||||
        // Sync all new responses.
 | 
			
		||||
        const responses = await AddonModFeedbackOffline.getAllFeedbackResponses(siteId);
 | 
			
		||||
 | 
			
		||||
        // Do not sync same feedback twice.
 | 
			
		||||
        const treated: Record<number, boolean> = {};
 | 
			
		||||
 | 
			
		||||
        await Promise.all(responses.map(async (response) => {
 | 
			
		||||
            if (treated[response.feedbackid]) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            treated[response.feedbackid] = true;
 | 
			
		||||
 | 
			
		||||
            const result = force ?
 | 
			
		||||
                await this.syncFeedback(response.feedbackid, siteId) :
 | 
			
		||||
                await this.syncFeedbackIfNeeded(response.feedbackid, siteId);
 | 
			
		||||
 | 
			
		||||
            if (result?.updated) {
 | 
			
		||||
                // Sync successful, send event.
 | 
			
		||||
                CoreEvents.trigger(AddonModFeedbackSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    feedbackId: response.feedbackid,
 | 
			
		||||
                    warnings: result.warnings,
 | 
			
		||||
                }, siteId);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync a feedback only if a certain time has passed since the last time.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when the feedback is synced or if it doesn't need to be synced.
 | 
			
		||||
     */
 | 
			
		||||
    async syncFeedbackIfNeeded(feedbackId: number, siteId?: string): Promise<AddonModFeedbackSyncResult | undefined> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const needed = await this.isSyncNeeded(feedbackId, siteId);
 | 
			
		||||
 | 
			
		||||
        if (needed) {
 | 
			
		||||
            return this.syncFeedback(feedbackId, siteId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Synchronize all offline responses of a feedback.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID to be synced.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    syncFeedback(feedbackId: number, siteId?: string): Promise<AddonModFeedbackSyncResult> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        if (this.isSyncing(feedbackId, siteId)) {
 | 
			
		||||
            // There's already a sync ongoing for this feedback, return the promise.
 | 
			
		||||
            return this.getOngoingSync(feedbackId, siteId)!;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Verify that feedback isn't blocked.
 | 
			
		||||
        if (CoreSync.isBlocked(AddonModFeedbackProvider.COMPONENT, feedbackId, siteId)) {
 | 
			
		||||
            this.logger.debug(`Cannot sync feedback '${feedbackId}' because it is blocked.`);
 | 
			
		||||
 | 
			
		||||
            throw new CoreSyncBlockedError(Translate.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.logger.debug(`Try to sync feedback '${feedbackId}' in site ${siteId}'`);
 | 
			
		||||
 | 
			
		||||
        return this.addOngoingSync(feedbackId, this.performSyncFeedback(feedbackId, siteId), siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Perform the feedback sync.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedbackId Feedback ID.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved in success.
 | 
			
		||||
     */
 | 
			
		||||
    protected async performSyncFeedback(feedbackId: number, siteId: string): Promise<AddonModFeedbackSyncResult> {
 | 
			
		||||
        const result: AddonModFeedbackSyncResult = {
 | 
			
		||||
            warnings: [],
 | 
			
		||||
            updated: false,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Sync offline logs.
 | 
			
		||||
        await CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(AddonModFeedbackProvider.COMPONENT, feedbackId, siteId));
 | 
			
		||||
 | 
			
		||||
        // Get offline responses to be sent.
 | 
			
		||||
        const responses = await CoreUtils.ignoreErrors(AddonModFeedbackOffline.getFeedbackResponses(feedbackId, siteId));
 | 
			
		||||
 | 
			
		||||
        if (!responses || !responses.length) {
 | 
			
		||||
            // Nothing to sync.
 | 
			
		||||
            await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId));
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!CoreApp.isOnline()) {
 | 
			
		||||
            // Cannot sync in offline.
 | 
			
		||||
            throw new CoreNetworkError();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const courseId = responses[0].courseid;
 | 
			
		||||
 | 
			
		||||
        const feedback = await AddonModFeedback.getFeedbackById(courseId, feedbackId, { siteId });
 | 
			
		||||
 | 
			
		||||
        if (!feedback.multiple_submit) {
 | 
			
		||||
            // If it does not admit multiple submits, check if it is completed to know if we can submit.
 | 
			
		||||
            const isCompleted = await AddonModFeedback.isCompleted(feedbackId, { cmId: feedback.coursemodule, siteId });
 | 
			
		||||
 | 
			
		||||
            if (isCompleted) {
 | 
			
		||||
                // Cannot submit again, delete resposes.
 | 
			
		||||
                await Promise.all(responses.map((data) =>
 | 
			
		||||
                    AddonModFeedbackOffline.deleteFeedbackPageResponses(feedbackId, data.page, siteId)));
 | 
			
		||||
 | 
			
		||||
                result.updated = true;
 | 
			
		||||
                this.addOfflineDataDeletedWarning(
 | 
			
		||||
                    result.warnings,
 | 
			
		||||
                    feedback.name,
 | 
			
		||||
                    Translate.instant('addon.mod_feedback.this_feedback_is_already_submitted'),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId));
 | 
			
		||||
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const timemodified = await AddonModFeedback.getCurrentCompletedTimeModified(feedbackId, {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        });
 | 
			
		||||
        // Sort by page.
 | 
			
		||||
        responses.sort((a, b) => a.page - b.page);
 | 
			
		||||
 | 
			
		||||
        const orderedData = responses.map((data) => ({
 | 
			
		||||
            function: this.processPage.bind(this, feedback, data, siteId, timemodified, result),
 | 
			
		||||
            blocking: true,
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Execute all the processes in order to solve dependencies.
 | 
			
		||||
        await CoreUtils.executeOrderedPromises(orderedData);
 | 
			
		||||
 | 
			
		||||
        if (result.updated) {
 | 
			
		||||
            // Data has been sent to server, update data.
 | 
			
		||||
            try {
 | 
			
		||||
                const module = await CoreCourse.getModuleBasicInfoByInstance(feedbackId, 'feedback', siteId);
 | 
			
		||||
 | 
			
		||||
                await this.prefetchAfterUpdate(AddonModFeedbackPrefetchHandler.instance, module, courseId, undefined, siteId);
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Ignore errors.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sync finished, set sync time.
 | 
			
		||||
        await CoreUtils.ignoreErrors(this.setSyncTime(feedbackId, siteId));
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convenience function to sync process page calls.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedback Feedback object.
 | 
			
		||||
     * @param data Response data.
 | 
			
		||||
     * @param siteId Site Id.
 | 
			
		||||
     * @param timemodified Current completed modification time.
 | 
			
		||||
     * @param result Result object to be modified.
 | 
			
		||||
     * @return Resolve when done or rejected with error.
 | 
			
		||||
     */
 | 
			
		||||
    protected async processPage(
 | 
			
		||||
        feedback: AddonModFeedbackWSFeedback,
 | 
			
		||||
        data: AddonModFeedbackOfflineResponse,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
        timemodified: number,
 | 
			
		||||
        result: AddonModFeedbackSyncResult,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        // Delete all pages that are submitted before changing website.
 | 
			
		||||
        if (timemodified > data.timemodified) {
 | 
			
		||||
            return AddonModFeedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await AddonModFeedback.processPageOnline(feedback.id, data.page, data.responses, false, siteId);
 | 
			
		||||
 | 
			
		||||
            result.updated = true;
 | 
			
		||||
 | 
			
		||||
            await AddonModFeedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId);
 | 
			
		||||
        } 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 AddonModFeedbackOffline.deleteFeedbackPageResponses(feedback.id, data.page, siteId);
 | 
			
		||||
 | 
			
		||||
            // Responses deleted, add a warning.
 | 
			
		||||
            this.addOfflineDataDeletedWarning(
 | 
			
		||||
                result.warnings,
 | 
			
		||||
                feedback.name,
 | 
			
		||||
                error,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackSync = makeSingleton(AddonModFeedbackSyncProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a feedback sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModFeedbackSyncResult = {
 | 
			
		||||
    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 AddonModFeedbackAutoSyncData = {
 | 
			
		||||
    feedbackId: number;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1762
									
								
								src/addons/mod/feedback/services/feedback.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1762
									
								
								src/addons/mod/feedback/services/feedback.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										92
									
								
								src/addons/mod/feedback/services/handlers/analysis-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/addons/mod/feedback/services/handlers/analysis-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Content links handler for a feedback analysis.
 | 
			
		||||
 * Match mod/feedback/analysis.php with a valid feedback id.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackAnalysisLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackAnalysisLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
    pattern = /\/mod\/feedback\/analysis\.php.*([&?]id=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                const moduleId = Number(params.id);
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const moduleBasicInfo = await CoreCourse.getModuleBasicInfo(moduleId, siteId);
 | 
			
		||||
 | 
			
		||||
                    // Get the module.
 | 
			
		||||
                    const module = await CoreCourse.getModule(
 | 
			
		||||
                        moduleId,
 | 
			
		||||
                        moduleBasicInfo.course,
 | 
			
		||||
                        moduleBasicInfo.section,
 | 
			
		||||
                        false,
 | 
			
		||||
                        false,
 | 
			
		||||
                        siteId,
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                module,
 | 
			
		||||
                                tab: 'analysis',
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'Error opening link.');
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        if (typeof params.id == 'undefined') {
 | 
			
		||||
            // Cannot treat the URL.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackAnalysisLinkHandler = makeSingleton(AddonModFeedbackAnalysisLinkHandlerService);
 | 
			
		||||
							
								
								
									
										80
									
								
								src/addons/mod/feedback/services/handlers/complete-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/addons/mod/feedback/services/handlers/complete-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Content links handler for feedback complete questions.
 | 
			
		||||
 * Match mod/feedback/complete.php with a valid feedback id.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackCompleteLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackCompleteLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
    pattern = /\/mod\/feedback\/complete\.php.*([?&](id|gopage)=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                const moduleId = Number(params.id);
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const module = await CoreCourse.getModuleBasicInfo(moduleId, siteId);
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/form`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                page: typeof params.gopage != 'undefined' ? Number(params.gopage) : undefined,
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'Error opening link.');
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        if (typeof params.id == 'undefined') {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackCompleteLinkHandler = makeSingleton(AddonModFeedbackCompleteLinkHandlerService);
 | 
			
		||||
							
								
								
									
										47
									
								
								src/addons/mod/feedback/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/addons/mod/feedback/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
// (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 { AddonModFeedback } from '../feedback';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to feedback.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModFeedback', 'feedback');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if the handler is enabled for a certain site (site + user) and a URL.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId The site ID.
 | 
			
		||||
     * @param url The URL to treat.
 | 
			
		||||
     * @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
 | 
			
		||||
     * @param courseId Course ID related to the URL. Optional but recommended.
 | 
			
		||||
     * @return Whether the handler is enabled for the URL and site.
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackIndexLinkHandler = makeSingleton(AddonModFeedbackIndexLinkHandlerService);
 | 
			
		||||
							
								
								
									
										41
									
								
								src/addons/mod/feedback/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/addons/mod/feedback/services/handlers/list-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to feedback list page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackListLinkHandlerService extends CoreContentLinksModuleListHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackListLinkHandler';
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('AddonModFeedback', 'feedback');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackListLinkHandler = makeSingleton(AddonModFeedbackListLinkHandlerService);
 | 
			
		||||
							
								
								
									
										84
									
								
								src/addons/mod/feedback/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/addons/mod/feedback/services/handlers/module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
// (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 { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackIndexComponent } from '../../components/index';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support feedback modules.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackModuleHandlerService implements CoreCourseModuleHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'mod_feedback';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedback';
 | 
			
		||||
    modName = 'feedback';
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getData(module: CoreCourseAnyModuleData): CoreCourseModuleHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
 | 
			
		||||
            title: module.name,
 | 
			
		||||
            class: 'addon-mod_feedback-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(AddonModFeedbackModuleHandlerService.PAGE_NAME + routeParams, options);
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getMainComponent(): Promise<Type<unknown>> {
 | 
			
		||||
        return AddonModFeedbackIndexComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackModuleHandler = makeSingleton(AddonModFeedbackModuleHandlerService);
 | 
			
		||||
							
								
								
									
										228
									
								
								src/addons/mod/feedback/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/addons/mod/feedback/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,228 @@
 | 
			
		||||
// (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 { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreGroups } from '@services/groups';
 | 
			
		||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreTimeUtils } from '@services/utils/time';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreWSFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModFeedback,
 | 
			
		||||
    AddonModFeedbackGetFeedbackAccessInformationWSResponse,
 | 
			
		||||
    AddonModFeedbackProvider,
 | 
			
		||||
    AddonModFeedbackWSFeedback,
 | 
			
		||||
} from '../feedback';
 | 
			
		||||
import { AddonModFeedbackSync, AddonModFeedbackSyncResult } from '../feedback-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to prefetch feedbacks.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedback';
 | 
			
		||||
    modName = 'feedback';
 | 
			
		||||
    component = AddonModFeedbackProvider.COMPONENT;
 | 
			
		||||
    updatesNames = /^configuration$|^.*files$|^attemptsfinished|^attemptsunfinished$/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> {
 | 
			
		||||
        let files: CoreWSFile[] = [];
 | 
			
		||||
 | 
			
		||||
        const feedback = await AddonModFeedback.getFeedback(courseId, module.id);
 | 
			
		||||
 | 
			
		||||
        // Get intro files and page after submit files.
 | 
			
		||||
        files = feedback.pageaftersubmitfiles || [];
 | 
			
		||||
        files = files.concat(this.getIntroFilesFromInstance(module, feedback));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const response = await AddonModFeedback.getItems(feedback.id);
 | 
			
		||||
 | 
			
		||||
            response.items.forEach((item) => {
 | 
			
		||||
                files = files.concat(item.itemfiles);
 | 
			
		||||
            });
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return files;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSFile[]> {
 | 
			
		||||
        const feedback = await CoreUtils.ignoreErrors(AddonModFeedback.getFeedback(courseId, module.id));
 | 
			
		||||
 | 
			
		||||
        return this.getIntroFilesFromInstance(module, feedback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModFeedback.invalidateContent(moduleId, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModFeedback.invalidateFeedbackData(courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
 | 
			
		||||
        const feedback = await AddonModFeedback.getFeedback(courseId, module.id, {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.PreferCache,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        const now = CoreTimeUtils.timestamp();
 | 
			
		||||
 | 
			
		||||
        // Check time first if available.
 | 
			
		||||
        if (feedback.timeopen && feedback.timeopen > now) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (feedback.timeclose && feedback.timeclose < now) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const accessData = await AddonModFeedback.getFeedbackAccessInformation(feedback.id, { cmId: module.id });
 | 
			
		||||
 | 
			
		||||
        return accessData.isopen;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    prefetch(module: CoreCourseAnyModuleData, courseId?: number): Promise<void> {
 | 
			
		||||
        return this.prefetchPackage(module, courseId, this.prefetchFeedback.bind(this, module, courseId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a feedback.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchFeedback(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
 | 
			
		||||
        const siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
        const commonOptions = {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
        const modOptions = {
 | 
			
		||||
            cmId: module.id,
 | 
			
		||||
            ...commonOptions, // Include all common options.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Prefetch the feedback data.
 | 
			
		||||
        const feedback = await AddonModFeedback.getFeedback(courseId, module.id, commonOptions);
 | 
			
		||||
 | 
			
		||||
        let files: CoreWSFile[] = feedback.pageaftersubmitfiles || [];
 | 
			
		||||
        files = files.concat(this.getIntroFilesFromInstance(module, feedback));
 | 
			
		||||
 | 
			
		||||
        const accessData = await AddonModFeedback.getFeedbackAccessInformation(feedback.id, modOptions);
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        if (accessData.canedititems || accessData.canviewreports) {
 | 
			
		||||
            // Get all groups analysis.
 | 
			
		||||
            promises.push(AddonModFeedback.getAnalysis(feedback.id, modOptions));
 | 
			
		||||
            promises.push(this.prefetchAllGroupsAnalysis(feedback, accessData, modOptions));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonModFeedback.getItems(feedback.id, commonOptions).then((response) => {
 | 
			
		||||
            response.items.forEach((item) => {
 | 
			
		||||
                files = files.concat(item.itemfiles);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return CoreFilepool.addFilesToQueue(siteId, files, this.component, module.id);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (accessData.cancomplete && accessData.cansubmit && !accessData.isempty) {
 | 
			
		||||
            // Send empty data, so it will recover last completed feedback attempt values.
 | 
			
		||||
            promises.push(AddonModFeedback.processPageOnline(feedback.id, 0, {}, false, siteId).then(() => Promise.all([
 | 
			
		||||
                AddonModFeedback.getCurrentValues(feedback.id, modOptions),
 | 
			
		||||
                AddonModFeedback.getResumePage(feedback.id, modOptions),
 | 
			
		||||
            ])));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch all groups analysis.
 | 
			
		||||
     *
 | 
			
		||||
     * @param feedback Feedback.
 | 
			
		||||
     * @param accessData Access info.
 | 
			
		||||
     * @param modOptions Options.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchAllGroupsAnalysis(
 | 
			
		||||
        feedback: AddonModFeedbackWSFeedback,
 | 
			
		||||
        accessData: AddonModFeedbackGetFeedbackAccessInformationWSResponse,
 | 
			
		||||
        modOptions: CoreCourseCommonModWSOptions,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const groupInfo = await CoreGroups.getActivityGroupInfo(feedback.coursemodule, true, undefined, modOptions.siteId, true);
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        if (!groupInfo.groups || groupInfo.groups.length == 0) {
 | 
			
		||||
            groupInfo.groups = [{ id: 0, name: '' }];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        groupInfo.groups.forEach((group) => {
 | 
			
		||||
            const groupOptions = {
 | 
			
		||||
                groupId: group.id,
 | 
			
		||||
                ...modOptions, // Include all mod options.
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            promises.push(AddonModFeedback.getAnalysis(feedback.id, groupOptions));
 | 
			
		||||
            promises.push(AddonModFeedback.getAllResponsesAnalysis(feedback.id, groupOptions));
 | 
			
		||||
 | 
			
		||||
            if (!accessData.isanonymous) {
 | 
			
		||||
                promises.push(AddonModFeedback.getAllNonRespondents(feedback.id, groupOptions));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModFeedbackSyncResult> {
 | 
			
		||||
        return AddonModFeedbackSync.syncFeedback(module.instance!, siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackPrefetchHandler = makeSingleton(AddonModFeedbackPrefetchHandlerService);
 | 
			
		||||
							
								
								
									
										80
									
								
								src/addons/mod/feedback/services/handlers/print-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/addons/mod/feedback/services/handlers/print-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService } from './module';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Content links handler for feedback print questions.
 | 
			
		||||
 * Match mod/feedback/print.php with a valid feedback id.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackPrintLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackPrintLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
    pattern = /\/mod\/feedback\/print\.php.*([?&](id)=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                const moduleId = Number(params.id);
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const module = await CoreCourse.getModuleBasicInfo(moduleId, siteId);
 | 
			
		||||
 | 
			
		||||
                    CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/form`,
 | 
			
		||||
                        {
 | 
			
		||||
                            params: {
 | 
			
		||||
                                preview: true,
 | 
			
		||||
                            },
 | 
			
		||||
                            siteId,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'Error opening link.');
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        if (typeof params.id == 'undefined') {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackPrintLinkHandler = makeSingleton(AddonModFeedbackPrintLinkHandlerService);
 | 
			
		||||
							
								
								
									
										70
									
								
								src/addons/mod/feedback/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/addons/mod/feedback/services/handlers/push-click.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
// (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 { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
			
		||||
import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate';
 | 
			
		||||
import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
 | 
			
		||||
import { CoreUrlUtils } from '@services/utils/url';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackHelper } from '../feedback-helper';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler for feedback push notifications clicks.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackPushClickHandlerService implements CorePushNotificationsClickHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackPushClickHandler';
 | 
			
		||||
    priority = 200;
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async handles(notification: CorePushNotificationsNotificationBasicData): Promise<boolean> {
 | 
			
		||||
        if (CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_feedback' &&
 | 
			
		||||
                (notification.name == 'submission' || notification.name == 'message')) {
 | 
			
		||||
 | 
			
		||||
            return AddonModFeedback.isPluginEnabled(notification.site);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    handleClick(notification: AddonModFeedbackPushNotificationData): Promise<void> {
 | 
			
		||||
        const contextUrlParams = CoreUrlUtils.extractUrlParams(notification.contexturl!);
 | 
			
		||||
        const courseId = Number(notification.courseid);
 | 
			
		||||
        const moduleId = Number(contextUrlParams.id);
 | 
			
		||||
 | 
			
		||||
        if (notification.name == 'submission') {
 | 
			
		||||
            return AddonModFeedbackHelper.handleShowEntriesLink(contextUrlParams, notification.site);
 | 
			
		||||
        } else {
 | 
			
		||||
            return CoreCourseHelper.navigateToModule(moduleId, notification.site, courseId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackPushClickHandler = makeSingleton(AddonModFeedbackPushClickHandlerService);
 | 
			
		||||
 | 
			
		||||
type AddonModFeedbackPushNotificationData = CorePushNotificationsNotificationBasicData & {
 | 
			
		||||
    contexturl?: string;
 | 
			
		||||
    courseid?: number | string;
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,58 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackHelper } from '../feedback-helper';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Content links handler for feedback show entries questions.
 | 
			
		||||
 * Match mod/feedback/show_entries.php with a valid feedback id.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackShowEntriesLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackShowEntriesLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
    pattern = /\/mod\/feedback\/show_entries\.php.*([?&](id|showcompleted)=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            action: (siteId: string) => {
 | 
			
		||||
                AddonModFeedbackHelper.handleShowEntriesLink(params, siteId);
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        if (typeof params.id == 'undefined') {
 | 
			
		||||
            // Cannot treat the URL.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackShowEntriesLinkHandler = makeSingleton(AddonModFeedbackShowEntriesLinkHandlerService);
 | 
			
		||||
@ -0,0 +1,75 @@
 | 
			
		||||
// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModFeedback } from '../feedback';
 | 
			
		||||
import { AddonModFeedbackModuleHandlerService } from './module';
 | 
			
		||||
/**
 | 
			
		||||
 * Content links handler for feedback show non respondents.
 | 
			
		||||
 * Match mod/feedback/show_nonrespondents.php with a valid feedback id.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackShowNonRespondentsLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackShowNonRespondentsLinkHandler';
 | 
			
		||||
    featureName = 'CoreCourseModuleDelegate_AddonModFeedback';
 | 
			
		||||
    pattern = /\/mod\/feedback\/show_nonrespondents\.php.*([?&](id)=\d+)/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        return [{
 | 
			
		||||
            action: async (siteId: string) => {
 | 
			
		||||
                const modal = await CoreDomUtils.showModalLoading();
 | 
			
		||||
 | 
			
		||||
                const moduleId = Number(params.id);
 | 
			
		||||
 | 
			
		||||
                try {
 | 
			
		||||
                    const module = await CoreCourse.getModuleBasicInfo(moduleId, siteId);
 | 
			
		||||
 | 
			
		||||
                    await CoreNavigator.navigateToSitePath(
 | 
			
		||||
                        AddonModFeedbackModuleHandlerService.PAGE_NAME + `/${module.course}/${module.id}/nonrespondents`,
 | 
			
		||||
                        { siteId },
 | 
			
		||||
                    );
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    CoreDomUtils.showErrorModalDefault(error, 'Error opening link.');
 | 
			
		||||
                } finally {
 | 
			
		||||
                    modal.dismiss();
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
 | 
			
		||||
        if (typeof params.id == 'undefined') {
 | 
			
		||||
            // Cannot treat the URL.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return AddonModFeedback.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackShowNonRespondentsLinkHandler = makeSingleton(AddonModFeedbackShowNonRespondentsLinkHandlerService);
 | 
			
		||||
							
								
								
									
										51
									
								
								src/addons/mod/feedback/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/addons/mod/feedback/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 { AddonModFeedbackSync } from '../feedback-sync';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Synchronization cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModFeedbackSyncCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModFeedbackSyncCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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 AddonModFeedbackSync.syncAllFeedbacks(siteId, force);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the time between consecutive executions.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Time between consecutive executions (in ms).
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return AddonModFeedbackSync.syncInterval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AddonModFeedbackSyncCronHandler = makeSingleton(AddonModFeedbackSyncCronHandlerService);
 | 
			
		||||
@ -34,6 +34,7 @@ import { AddonModChoiceModule } from './choice/choice.module';
 | 
			
		||||
import { AddonModWikiModule } from './wiki/wiki.module';
 | 
			
		||||
import { AddonModGlossaryModule } from './glossary/glossary.module';
 | 
			
		||||
import { AddonModChatModule } from './chat/chat.module';
 | 
			
		||||
import { AddonModFeedbackModule } from './feedback/feedback.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
@ -57,6 +58,7 @@ import { AddonModChatModule } from './chat/chat.module';
 | 
			
		||||
        AddonModWikiModule,
 | 
			
		||||
        AddonModGlossaryModule,
 | 
			
		||||
        AddonModChatModule,
 | 
			
		||||
        AddonModFeedbackModule,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonModModule { }
 | 
			
		||||
 | 
			
		||||
@ -127,7 +127,7 @@ import { ADDON_MOD_BOOK_SERVICES } from '@addons/mod/book/book.module';
 | 
			
		||||
import { ADDON_MOD_CHAT_SERVICES } from '@addons/mod/chat/chat.module';
 | 
			
		||||
import { ADDON_MOD_CHOICE_SERVICES } from '@addons/mod/choice/choice.module';
 | 
			
		||||
import { ADDON_MOD_DATA_SERVICES } from '@addons/mod/data/data.module';
 | 
			
		||||
// @todo import { ADDON_MOD_FEEDBACK_SERVICES } from '@addons/mod/feedback/feedback.module';
 | 
			
		||||
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';
 | 
			
		||||
import { ADDON_MOD_GLOSSARY_SERVICES } from '@addons/mod/glossary/glossary.module';
 | 
			
		||||
@ -293,7 +293,7 @@ export class CoreCompileProvider {
 | 
			
		||||
            ...ADDON_MOD_CHAT_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_CHOICE_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_DATA_SERVICES,
 | 
			
		||||
            // @todo ...ADDON_MOD_FEEDBACK_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_FEEDBACK_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_FOLDER_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_FORUM_SERVICES,
 | 
			
		||||
            ...ADDON_MOD_GLOSSARY_SERVICES,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user