MOBILE-3654 survey: Add survey activity module
parent
4cf3d3d80d
commit
1e039b0b0f
|
@ -27,6 +27,7 @@ import { AddonModResourceModule } from './resource/resource.module';
|
||||||
import { AddonModUrlModule } from './url/url.module';
|
import { AddonModUrlModule } from './url/url.module';
|
||||||
import { AddonModLtiModule } from './lti/lti.module';
|
import { AddonModLtiModule } from './lti/lti.module';
|
||||||
import { AddonModH5PActivityModule } from './h5pactivity/h5pactivity.module';
|
import { AddonModH5PActivityModule } from './h5pactivity/h5pactivity.module';
|
||||||
|
import { AddonModSurveyModule } from './survey/survey.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
|
@ -44,6 +45,7 @@ import { AddonModH5PActivityModule } from './h5pactivity/h5pactivity.module';
|
||||||
AddonModImscpModule,
|
AddonModImscpModule,
|
||||||
AddonModLtiModule,
|
AddonModLtiModule,
|
||||||
AddonModH5PActivityModule,
|
AddonModH5PActivityModule,
|
||||||
|
AddonModSurveyModule,
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
exports: [],
|
exports: [],
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// (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 { NgModule } from '@angular/core';
|
||||||
|
import { AddonModSurveyIndexComponent } from './index/index';
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModSurveyIndexComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
CoreCourseComponentsModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonModSurveyIndexComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModSurveyComponentsModule {}
|
|
@ -0,0 +1,152 @@
|
||||||
|
<!-- Buttons to add to the header. -->
|
||||||
|
<core-navbar-buttons slot="end">
|
||||||
|
<core-context-menu>
|
||||||
|
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
|
||||||
|
[href]="externalUrl" iconAction="fas-external-link-alt">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
|
||||||
|
(action)="expandDescription()" iconAction="fas-arrow-right">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"
|
||||||
|
iconAction="far-newspaper" (action)="gotoBlog()">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="loaded && !hasOffline && isOnline" [priority]="700" [content]="'core.refresh' | translate"
|
||||||
|
(action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="loaded && hasOffline && isOnline" [priority]="600"
|
||||||
|
[content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)"
|
||||||
|
[iconAction]="syncIcon" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)"
|
||||||
|
[iconAction]="prefetchStatusIcon" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="size" [priority]="400" [content]="'core.clearstoreddata' | translate:{$a: size}"
|
||||||
|
iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
|
||||||
|
</core-context-menu-item>
|
||||||
|
</core-context-menu>
|
||||||
|
</core-navbar-buttons>
|
||||||
|
|
||||||
|
<!-- Content. -->
|
||||||
|
<core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page">
|
||||||
|
|
||||||
|
<core-course-module-description *ngIf="survey && !survey.surveydone && !hasOffline" [description]="description"
|
||||||
|
[component]="component" [componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id"
|
||||||
|
[courseId]="courseId">
|
||||||
|
</core-course-module-description>
|
||||||
|
|
||||||
|
<!-- Survey already done -->
|
||||||
|
<ion-card class="ion-padding" *ngIf="survey && survey.surveydone">
|
||||||
|
<p class="ion-padding">{{ 'addon.mod_survey.surveycompletednograph' | translate }}</p>
|
||||||
|
<ion-button expand="block" [href]="externalUrl" core-link>
|
||||||
|
<ion-icon name="fas-external-link-alt" slot="start"></ion-icon>
|
||||||
|
{{ 'addon.mod_survey.results' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<!-- Survey done in offline but not synchronized -->
|
||||||
|
<ion-card class="core-warning-card" *ngIf="hasOffline">
|
||||||
|
<ion-item>
|
||||||
|
<ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
|
||||||
|
<ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-card>
|
||||||
|
|
||||||
|
<!-- Survey questions -->
|
||||||
|
<form *ngIf="survey && !survey.surveydone && !hasOffline && questions && questions.length">
|
||||||
|
|
||||||
|
<ion-grid class="ion-no-padding ion-text-wrap">
|
||||||
|
<ng-container *ngFor="let question of questions; let questionIndex=index; let isEven=even;" class="ion-no-padding ion-text-wrap">
|
||||||
|
<!-- Parent question (Category header) -->
|
||||||
|
<ng-container *ngIf="question.multiArray?.length" >
|
||||||
|
<h3 class="ion-padding-horizontal" [class.ion-padding-top]="questionIndex == 1">{{ question.text }}</h3>
|
||||||
|
<ion-row class="ion-align-items-center ion-hide-md-down ion-padding">
|
||||||
|
<ion-col size="7" class="ion-padding">{{ 'addon.mod_survey.responses' | translate }}</ion-col>
|
||||||
|
<ion-col size="1" class="ion-text-center option-name"
|
||||||
|
*ngFor="let option of question.optionsArray; let indexOption=index;">
|
||||||
|
{{ option }}
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
<ion-item class="ion-text-wrap addon-mod_survey-question" [class.even]="isEven" lines="full">
|
||||||
|
<ion-label><p>{{ question.intro }}</p></ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Subquestion -->
|
||||||
|
<ion-radio-group [(ngModel)]="answers[question.name]" [required]="question.required" [name]="question.name">
|
||||||
|
<ion-row *ngIf="question.parent !== 0" class="ion-align-items-center ion-padding" [class.even]="isEven">
|
||||||
|
|
||||||
|
<ion-col size="7">
|
||||||
|
<ion-label id="addon-mod_survey-{{question.id}}">
|
||||||
|
<span [core-mark-required]="question.required">
|
||||||
|
<strong>{{question.num}}.</strong> {{ question.text }}
|
||||||
|
</span>
|
||||||
|
</ion-label>
|
||||||
|
</ion-col>
|
||||||
|
|
||||||
|
<!-- Tablet view: radio buttons -->
|
||||||
|
<ion-col class="ion-hide-md-down ion-text-center" size="1"
|
||||||
|
*ngFor="let option of question.optionsArray; let value=index;">
|
||||||
|
<!-- Empty slot to avoid errors on migration tslint checks -->
|
||||||
|
<ion-radio [value]="value + 1" [attr.aria-labelledby]="'addon-mod_survey-'+question.id" slot="">
|
||||||
|
</ion-radio>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col class="ion-hide-md-up" size="5">
|
||||||
|
<ion-select class="ion-padding" [(ngModel)]="answers[question.name]" [required]="question.required"
|
||||||
|
[attr.aria-labelledby]="'addon-mod_survey-'+question.id" interface="action-sheet"
|
||||||
|
[name]="question.name">
|
||||||
|
<ion-select-option value="-1" selected disabled>{{ 'core.choose' | translate }}</ion-select-option>
|
||||||
|
<ion-select-option *ngFor="let option of question.optionsArray; let value=index;"
|
||||||
|
[value]="value +1">
|
||||||
|
{{option}}
|
||||||
|
</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-radio-group>
|
||||||
|
|
||||||
|
<!-- Single question (don't belong to a category) -->
|
||||||
|
<ng-container *ngIf="(!question.multiArray || question.multiArray.length == 0) && question.parent === 0">
|
||||||
|
<ion-row class="ion-align-items-center ion-padding" *ngIf="question.type > 0" [class.even]="isEven">
|
||||||
|
<ion-col size="7">
|
||||||
|
<ion-label id="addon-mod_survey-{{question.id}}">
|
||||||
|
<span [core-mark-required]="question.required">
|
||||||
|
<strong>{{question.num}}.</strong> {{ question.text }}
|
||||||
|
</span>
|
||||||
|
</ion-label>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col size="5">
|
||||||
|
<ion-select class="ion-padding" [(ngModel)]="answers[question.name]" [required]="question.required"
|
||||||
|
[attr.aria-labelledby]="'addon-mod_survey-'+question.id" interface="action-sheet"
|
||||||
|
[name]="question.name">
|
||||||
|
<ion-select-option *ngFor="let option of question.optionsArray; let value=index;" [value]="value">
|
||||||
|
{{option}}
|
||||||
|
</ion-select-option>
|
||||||
|
</ion-select>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
|
||||||
|
<ion-item *ngIf="question.type === 0" class="ion-text-wrap" [class.even]="isEven">
|
||||||
|
<ion-label position="floating" id="addon-mod_survey-{{question.id}}">
|
||||||
|
<span [core-mark-required]="question.required">
|
||||||
|
<strong>{{question.num}}.</strong> {{ question.text }}
|
||||||
|
</span>
|
||||||
|
</ion-label>
|
||||||
|
<ion-textarea [(ngModel)]="answers[question.name]" [name]="question.name"
|
||||||
|
[attr.aria-labelledby]="'addon-mod_survey-'+question.id" [required]="question.required">
|
||||||
|
</ion-textarea>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
</ion-grid>
|
||||||
|
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>
|
||||||
|
<ion-button expand="block" (click)="submit()" [disabled]="!isValidResponse()">
|
||||||
|
{{ 'core.submit' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</core-loading>
|
|
@ -0,0 +1,25 @@
|
||||||
|
:host {
|
||||||
|
--grid-background: var(--white);
|
||||||
|
--even-background: var(--gray-light);
|
||||||
|
|
||||||
|
.option-name {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.addon-mod_survey-question {
|
||||||
|
border-top: 1px solid var(--gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-row {
|
||||||
|
background-color: var(--grid-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.even {
|
||||||
|
background-color: var(--even-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(body.dark) {
|
||||||
|
--grid-background: var(--black);
|
||||||
|
--even-background: var(--gray-darker);
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
// (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 { Component, OnInit, Optional } from '@angular/core';
|
||||||
|
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
||||||
|
import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component';
|
||||||
|
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
||||||
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
|
import { IonContent } from '@ionic/angular';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { Translate } from '@singletons';
|
||||||
|
import { CoreEvents } from '@singletons/events';
|
||||||
|
import { AddonModSurveyPrefetchHandler } from '../../services/handlers/prefetch';
|
||||||
|
import {
|
||||||
|
AddonModSurveyProvider,
|
||||||
|
AddonModSurveySurvey,
|
||||||
|
AddonModSurvey,
|
||||||
|
AddonModSurveySubmitAnswerData,
|
||||||
|
} from '../../services/survey';
|
||||||
|
import { AddonModSurveyHelper, AddonModSurveyQuestionFormatted } from '../../services/survey-helper';
|
||||||
|
import { AddonModSurveyOffline } from '../../services/survey-offline';
|
||||||
|
import { AddonModSurveyAutoSyncData, AddonModSurveySync, AddonModSurveySyncResult } from '../../services/survey-sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays a survey.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-mod-survey-index',
|
||||||
|
templateUrl: 'addon-mod-survey-index.html',
|
||||||
|
styleUrls: ['index.scss'],
|
||||||
|
})
|
||||||
|
export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit {
|
||||||
|
|
||||||
|
component = AddonModSurveyProvider.COMPONENT;
|
||||||
|
moduleName = 'survey';
|
||||||
|
|
||||||
|
survey?: AddonModSurveySurvey;
|
||||||
|
questions: AddonModSurveyQuestionFormatted[] = [];
|
||||||
|
answers: Record<string, string> = {};
|
||||||
|
|
||||||
|
protected currentUserId?: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected content?: IonContent,
|
||||||
|
@Optional() courseContentsPage?: CoreCourseContentsPage,
|
||||||
|
) {
|
||||||
|
super('AddonModSurveyIndexComponent', content, courseContentsPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
super.ngOnInit();
|
||||||
|
|
||||||
|
this.currentUserId = CoreSites.getCurrentSiteUserId();
|
||||||
|
|
||||||
|
await this.loadContent(false, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await AddonModSurvey.logView(this.survey!.id, this.survey!.name);
|
||||||
|
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||||
|
} catch {
|
||||||
|
// Ignore errors. Just don't check Module completion.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the invalidate content function.
|
||||||
|
*
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected async invalidateContent(): Promise<void> {
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
promises.push(AddonModSurvey.invalidateSurveyData(this.courseId));
|
||||||
|
if (this.survey) {
|
||||||
|
promises.push(AddonModSurvey.invalidateQuestions(this.survey.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares sync event data with current data to check if refresh content is needed.
|
||||||
|
*
|
||||||
|
* @param syncEventData Data receiven on sync observer.
|
||||||
|
* @return True if refresh is needed, false otherwise.
|
||||||
|
*/
|
||||||
|
protected isRefreshSyncNeeded(syncEventData: AddonModSurveyAutoSyncData): boolean {
|
||||||
|
if (this.survey && syncEventData.surveyId == this.survey.id && syncEventData.userId == this.currentUserId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download survey contents.
|
||||||
|
*
|
||||||
|
* @param refresh If it's refreshing content.
|
||||||
|
* @param sync If it should try to sync.
|
||||||
|
* @param showErrors If show errors to the user of hide them.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id);
|
||||||
|
|
||||||
|
this.description = this.survey.intro;
|
||||||
|
this.dataRetrieved.emit(this.survey);
|
||||||
|
|
||||||
|
if (sync) {
|
||||||
|
// Try to synchronize the survey.
|
||||||
|
const answersSent = await this.syncActivity(showErrors);
|
||||||
|
if (answersSent) {
|
||||||
|
// Answers were sent, update the survey.
|
||||||
|
this.survey = await AddonModSurvey.getSurvey(this.courseId, this.module.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are answers stored in offline.
|
||||||
|
this.hasOffline = this.survey.surveydone
|
||||||
|
? false
|
||||||
|
: await AddonModSurveyOffline.hasAnswers(this.survey.id);
|
||||||
|
|
||||||
|
if (!this.survey.surveydone && !this.hasOffline) {
|
||||||
|
await this.fetchQuestions();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.fillContextMenu(refresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to get survey questions.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchQuestions(): Promise<void> {
|
||||||
|
const questions = await AddonModSurvey.getQuestions(this.survey!.id, { cmId: this.module.id });
|
||||||
|
|
||||||
|
this.questions = AddonModSurveyHelper.formatQuestions(questions);
|
||||||
|
|
||||||
|
// Init answers object.
|
||||||
|
this.questions.forEach((question) => {
|
||||||
|
if (question.name) {
|
||||||
|
const isTextArea = question.multiArray && question.multiArray.length === 0 && question.type === 0;
|
||||||
|
this.answers[question.name] = question.required ? '-1' : (isTextArea ? '' : '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (question.multiArray && !question.multiArray.length && question.parent === 0 && question.type > 0) {
|
||||||
|
// Options shown in a select. Remove all HTML.
|
||||||
|
question.optionsArray = question.optionsArray?.map((option) => CoreTextUtils.cleanTags(option));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if answers are valid to be submitted.
|
||||||
|
*
|
||||||
|
* @return If answers are valid
|
||||||
|
*/
|
||||||
|
isValidResponse(): boolean {
|
||||||
|
return !this.questions.some((question) => question.required && question.name &&
|
||||||
|
(question.type === 0 ? this.answers[question.name] == '' : parseInt(this.answers[question.name], 10) === -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save options selected.
|
||||||
|
*/
|
||||||
|
async submit(): Promise<void> {
|
||||||
|
let modal: CoreIonLoadingElement | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CoreDomUtils.showConfirm(Translate.instant('core.areyousure'));
|
||||||
|
|
||||||
|
const answers: AddonModSurveySubmitAnswerData[] = [];
|
||||||
|
modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
||||||
|
|
||||||
|
for (const x in this.answers) {
|
||||||
|
answers.push({
|
||||||
|
key: x,
|
||||||
|
value: this.answers[x],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const online = await AddonModSurvey.submitAnswers(this.survey!.id, this.survey!.name, this.courseId, answers);
|
||||||
|
|
||||||
|
CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: this.moduleName });
|
||||||
|
|
||||||
|
if (online && this.isPrefetched()) {
|
||||||
|
// The survey is downloaded, update the data.
|
||||||
|
try {
|
||||||
|
await AddonModSurveySync.prefetchAfterUpdate(
|
||||||
|
AddonModSurveyPrefetchHandler.instance,
|
||||||
|
this.module,
|
||||||
|
this.courseId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the view.
|
||||||
|
this.showLoadingAndFetch(false, false);
|
||||||
|
} catch {
|
||||||
|
// Prefetch failed, refresh the data.
|
||||||
|
await this.showLoadingAndRefresh(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not downloaded, refresh the data.
|
||||||
|
await this.showLoadingAndRefresh(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_survey.cannotsubmitsurvey', true);
|
||||||
|
} finally {
|
||||||
|
modal?.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the sync of the activity.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async sync(): Promise<void> {
|
||||||
|
await AddonModSurveySync.syncSurvey(this.survey!.id, this.currentUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if sync has succeed from result sync data.
|
||||||
|
*
|
||||||
|
* @param result Data returned on the sync function.
|
||||||
|
* @return If suceed or not.
|
||||||
|
*/
|
||||||
|
protected hasSyncSucceed(result: AddonModSurveySyncResult): boolean {
|
||||||
|
return result.answersSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"cannotsubmitsurvey": "Sorry, there was a problem submitting your survey. Please try again.",
|
||||||
|
"errorgetsurvey": "Error getting survey data.",
|
||||||
|
"ifoundthat": "I found that",
|
||||||
|
"ipreferthat": "I prefer that",
|
||||||
|
"modulenameplural": "Surveys",
|
||||||
|
"responses": "Responses",
|
||||||
|
"results": "Results",
|
||||||
|
"surveycompletednograph": "You have completed this survey."
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title><core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id"></core-format-text></ion-title>
|
||||||
|
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<!-- The buttons defined by the component will be added in here. -->
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
|
||||||
|
<addon-mod-survey-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-survey-index>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (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 { Component, ViewChild } from '@angular/core';
|
||||||
|
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
|
||||||
|
import { AddonModSurveyIndexComponent } from '../../components/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays a survey.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-mod-survey-index',
|
||||||
|
templateUrl: 'index.html',
|
||||||
|
})
|
||||||
|
export class AddonModSurveyIndexPage extends CoreCourseModuleMainActivityPage<AddonModSurveyIndexComponent> {
|
||||||
|
|
||||||
|
@ViewChild(AddonModSurveyIndexComponent) activityComponent?: AddonModSurveyIndexComponent;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// (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 AddonModSurveyOfflineProvider.
|
||||||
|
*/
|
||||||
|
export const SURVEY_TABLE = 'addon_mod_survey_answers';
|
||||||
|
export const ADDON_MOD_SURVEY_OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
|
||||||
|
name: 'AddonModSurveyOfflineProvider',
|
||||||
|
version: 1,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
name: SURVEY_TABLE,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'surveyid',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'courseid',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'userid',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'answers',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'timecreated',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primaryKeys: ['surveyid', 'userid'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Survey offline answers.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyAnswersDBRecord = {
|
||||||
|
surveyid: number;
|
||||||
|
userid: number;
|
||||||
|
name: string;
|
||||||
|
courseid: number;
|
||||||
|
answers: string;
|
||||||
|
timecreated: number;
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to survey.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
|
||||||
|
|
||||||
|
name = 'AddonModSurveyLinkHandler';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('AddonModSurvey', 'survey');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyIndexLinkHandler = makeSingleton(AddonModSurveyIndexLinkHandlerService);
|
|
@ -0,0 +1,32 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to survey list page.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyListLinkHandlerService extends CoreContentLinksModuleListHandler {
|
||||||
|
|
||||||
|
name = 'AddonModSurveyListLinkHandler';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('AddonModSurvey', 'survey');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyListLinkHandler = makeSingleton(AddonModSurveyListLinkHandlerService);
|
|
@ -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 { AddonModSurveyIndexComponent } from '../../components/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support survey modules.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyModuleHandlerService implements CoreCourseModuleHandler {
|
||||||
|
|
||||||
|
static readonly PAGE_NAME = 'mod_survey';
|
||||||
|
|
||||||
|
name = 'AddonModSurvey';
|
||||||
|
modName = 'survey';
|
||||||
|
|
||||||
|
supportedFeatures = {
|
||||||
|
[CoreConstants.FEATURE_GROUPS]: true,
|
||||||
|
[CoreConstants.FEATURE_GROUPINGS]: true,
|
||||||
|
[CoreConstants.FEATURE_MOD_INTRO]: true,
|
||||||
|
[CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
|
||||||
|
[CoreConstants.FEATURE_COMPLETION_HAS_RULES]: true,
|
||||||
|
[CoreConstants.FEATURE_GRADE_HAS_GRADE]: false,
|
||||||
|
[CoreConstants.FEATURE_GRADE_OUTCOMES]: false,
|
||||||
|
[CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
|
||||||
|
[CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getData(
|
||||||
|
module: CoreCourseAnyModuleData,
|
||||||
|
): CoreCourseModuleHandlerData {
|
||||||
|
return {
|
||||||
|
icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
|
||||||
|
title: module.name,
|
||||||
|
class: 'addon-mod_survey-handler',
|
||||||
|
showDownloadButton: true,
|
||||||
|
action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => {
|
||||||
|
options = options || {};
|
||||||
|
options.params = options.params || {};
|
||||||
|
Object.assign(options.params, { module });
|
||||||
|
const routeParams = '/' + courseId + '/' + module.id;
|
||||||
|
|
||||||
|
CoreNavigator.navigateToSitePath(AddonModSurveyModuleHandlerService.PAGE_NAME + routeParams, options);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async getMainComponent(): Promise<Type<unknown>> {
|
||||||
|
return AddonModSurveyIndexComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyModuleHandler = makeSingleton(AddonModSurveyModuleHandlerService);
|
|
@ -0,0 +1,114 @@
|
||||||
|
// (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 } from '@features/course/services/course';
|
||||||
|
import { CoreFilepool } from '@services/filepool';
|
||||||
|
import { CoreSitesReadingStrategy } from '@services/sites';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreWSExternalFile } from '@services/ws';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModSurvey, AddonModSurveyProvider } from '../survey';
|
||||||
|
import { AddonModSurveySync, AddonModSurveySyncResult } from '../survey-sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to prefetch surveys.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
|
||||||
|
|
||||||
|
name = 'AddonModSurvey';
|
||||||
|
modName = 'survey';
|
||||||
|
component = AddonModSurveyProvider.COMPONENT;
|
||||||
|
updatesNames = /^configuration$|^.*files$|^answers$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async getIntroFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSExternalFile[]> {
|
||||||
|
const survey = await CoreUtils.ignoreErrors(AddonModSurvey.getSurvey(courseId, module.id));
|
||||||
|
|
||||||
|
return this.getIntroFilesFromInstance(module, survey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async invalidateContent(moduleId: number, courseId: number): Promise<void> {
|
||||||
|
return AddonModSurvey.invalidateContent(moduleId, courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
|
||||||
|
await AddonModSurvey.invalidateSurveyData(courseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
prefetch(module: CoreCourseAnyModuleData, courseId?: number): Promise<void> {
|
||||||
|
return this.prefetchPackage(module, courseId, this.prefetchSurvey.bind(this, module, courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch a survey.
|
||||||
|
*
|
||||||
|
* @param module Module.
|
||||||
|
* @param courseId Course ID the module belongs to.
|
||||||
|
* @param siteId SiteId or current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async prefetchSurvey(module: CoreCourseAnyModuleData, courseId: number, siteId: string): Promise<void> {
|
||||||
|
const survey = await AddonModSurvey.getSurvey(courseId, module.id, {
|
||||||
|
readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
|
||||||
|
siteId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const promises: Promise<unknown>[] = [];
|
||||||
|
const files = this.getIntroFilesFromInstance(module, survey);
|
||||||
|
|
||||||
|
// Prefetch files.
|
||||||
|
promises.push(CoreFilepool.addFilesToQueue(siteId, files, AddonModSurveyProvider.COMPONENT, module.id));
|
||||||
|
|
||||||
|
// If survey isn't answered, prefetch the questions.
|
||||||
|
if (!survey.surveydone) {
|
||||||
|
promises.push(AddonModSurvey.getQuestions(survey.id, {
|
||||||
|
cmId: module.id,
|
||||||
|
readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
|
||||||
|
siteId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
sync(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModSurveySyncResult> {
|
||||||
|
return AddonModSurveySync.syncSurvey(module.instance!, undefined, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyPrefetchHandler = makeSingleton(AddonModSurveyPrefetchHandlerService);
|
|
@ -0,0 +1,43 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreCronHandler } from '@services/cron';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModSurveySync } from '../survey-sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronization cron handler.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveySyncCronHandlerService implements CoreCronHandler {
|
||||||
|
|
||||||
|
name = 'AddonModSurveySyncCronHandler';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async execute(siteId?: string, force?: boolean): Promise<void> {
|
||||||
|
await AddonModSurveySync.syncAllSurveys(siteId, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getInterval(): number {
|
||||||
|
return AddonModSurveySync.syncInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveySyncCronHandler = makeSingleton(AddonModSurveySyncCronHandlerService);
|
|
@ -0,0 +1,138 @@
|
||||||
|
// (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 { makeSingleton, Translate } from '@singletons';
|
||||||
|
import { AddonModSurveyQuestion } from './survey';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides helper functions for surveys.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a string with values separated by commas into an array.
|
||||||
|
*
|
||||||
|
* @param value Value to convert.
|
||||||
|
* @return Array.
|
||||||
|
*/
|
||||||
|
protected commaStringToArray(value: string | string[]): string[] {
|
||||||
|
if (typeof value == 'string') {
|
||||||
|
if (value.length > 0) {
|
||||||
|
return value.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the parent questions and puts them in an object: ID -> question.
|
||||||
|
*
|
||||||
|
* @param questions Questions.
|
||||||
|
* @return Object with parent questions.
|
||||||
|
*/
|
||||||
|
protected getParentQuestions(questions: AddonModSurveyQuestion[]): {[id: number]: AddonModSurveyQuestion} {
|
||||||
|
const parents: { [id: number]: AddonModSurveyQuestion } = {};
|
||||||
|
|
||||||
|
questions.forEach((question) => {
|
||||||
|
if (question.parent === 0) {
|
||||||
|
parents[question.id] = question;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a questions list, turning "multi" and "options" strings into arrays and adding the properties
|
||||||
|
* 'num' and 'name'.
|
||||||
|
*
|
||||||
|
* @param questions Questions.
|
||||||
|
* @return Promise resolved with the formatted questions.
|
||||||
|
*/
|
||||||
|
formatQuestions(questions: AddonModSurveyQuestion[]): AddonModSurveyQuestionFormatted[] {
|
||||||
|
const strIPreferThat = Translate.instant('addon.mod_survey.ipreferthat');
|
||||||
|
const strIFoundThat = Translate.instant('addon.mod_survey.ifoundthat');
|
||||||
|
const strChoose = Translate.instant('core.choose');
|
||||||
|
|
||||||
|
const formatted: AddonModSurveyQuestionFormatted[] = [];
|
||||||
|
const parents = this.getParentQuestions(questions);
|
||||||
|
|
||||||
|
let num = 1;
|
||||||
|
|
||||||
|
questions.forEach((question) => {
|
||||||
|
// Copy the object to prevent modifying the original.
|
||||||
|
const q1: AddonModSurveyQuestionFormatted = Object.assign({}, question);
|
||||||
|
const parent = parents[q1.parent];
|
||||||
|
|
||||||
|
// Turn multi and options into arrays.
|
||||||
|
q1.multiArray = this.commaStringToArray(q1.multi);
|
||||||
|
q1.optionsArray = this.commaStringToArray(q1.options);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
// It's a sub-question.
|
||||||
|
q1.required = true;
|
||||||
|
|
||||||
|
if (parent.type === 1 || parent.type === 2) {
|
||||||
|
// One answer question. Set its name and add it to the returned array.
|
||||||
|
q1.name = 'q' + (parent.type == 2 ? 'P' : '') + q1.id;
|
||||||
|
q1.num = num++;
|
||||||
|
} else {
|
||||||
|
// Two answers per question (COLLES P&A). We'll add two questions.
|
||||||
|
const q2 = Object.assign({}, q1);
|
||||||
|
|
||||||
|
q1.text = strIPreferThat + ' ' + q1.text;
|
||||||
|
q1.name = 'qP' + q1.id;
|
||||||
|
q1.num = num++;
|
||||||
|
formatted.push(q1);
|
||||||
|
|
||||||
|
q2.text = strIFoundThat + ' ' + q2.text;
|
||||||
|
q2.name = 'q' + q1.id;
|
||||||
|
q2.num = num++;
|
||||||
|
formatted.push(q2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (q1.multiArray && q1.multiArray.length === 0) {
|
||||||
|
// It's a single question.
|
||||||
|
q1.name = 'q' + q1.id;
|
||||||
|
q1.num = num++;
|
||||||
|
if (q1.type > 0) { // Add "choose" option since this question is not required.
|
||||||
|
q1.optionsArray.unshift(strChoose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formatted.push(q1);
|
||||||
|
});
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyHelper = makeSingleton(AddonModSurveyHelperProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Survey question with some calculated data.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyQuestionFormatted = AddonModSurveyQuestion & {
|
||||||
|
required?: boolean; // Calculated in the app. Whether the question is required.
|
||||||
|
name?: string; // Calculated in the app. The name of the question.
|
||||||
|
num?: number; // Calculated in the app. Number of the question.
|
||||||
|
multiArray?: string[]; // Subquestions ids, converted to an array.
|
||||||
|
optionsArray?: string[]; // Question options, converted to an array.
|
||||||
|
};
|
|
@ -0,0 +1,151 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModSurveyAnswersDBRecord, SURVEY_TABLE } from './database/survey';
|
||||||
|
import { AddonModSurveySubmitAnswerData } from './survey';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle Offline survey.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyOfflineProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a survey answers.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user in site.
|
||||||
|
* @return Promise resolved if deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
async deleteSurveyAnswers(surveyId: number, siteId?: string, userId?: number): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
await site.getDb().deleteRecords(SURVEY_TABLE, { surveyid: surveyId, userid: userId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the stored data from all the surveys.
|
||||||
|
*
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with answers.
|
||||||
|
*/
|
||||||
|
async getAllData(siteId?: string): Promise<AddonModSurveyAnswersDBRecordFormatted[]> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
const entries = await site.getDb().getAllRecords<AddonModSurveyAnswersDBRecord>(SURVEY_TABLE);
|
||||||
|
|
||||||
|
return entries.map((entry) => Object.assign(entry, {
|
||||||
|
answers: CoreTextUtils.parseJSON<AddonModSurveySubmitAnswerData[]>(entry.answers),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey stored answers.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user in site.
|
||||||
|
* @return Promise resolved with the answers.
|
||||||
|
*/
|
||||||
|
async getSurveyAnswers(surveyId: number, siteId?: string, userId?: number): Promise<AddonModSurveySubmitAnswerData[]> {
|
||||||
|
try {
|
||||||
|
const entry = await this.getSurveyData(surveyId, siteId, userId);
|
||||||
|
|
||||||
|
return entry.answers || [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey stored data.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user in site.
|
||||||
|
* @return Promise resolved with the data.
|
||||||
|
*/
|
||||||
|
async getSurveyData(surveyId: number, siteId?: string, userId?: number): Promise<AddonModSurveyAnswersDBRecordFormatted> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const entry = await site.getDb().getRecord<AddonModSurveyAnswersDBRecord>(
|
||||||
|
SURVEY_TABLE,
|
||||||
|
{ surveyid: surveyId, userid: userId },
|
||||||
|
);
|
||||||
|
|
||||||
|
return Object.assign(entry, {
|
||||||
|
answers: CoreTextUtils.parseJSON<AddonModSurveySubmitAnswerData[]>(entry.answers),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are offline answers to send.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user in site.
|
||||||
|
* @return Promise resolved with boolean: true if has offline answers, false otherwise.
|
||||||
|
*/
|
||||||
|
async hasAnswers(surveyId: number, siteId?: string, userId?: number): Promise<boolean> {
|
||||||
|
const answers = await this.getSurveyAnswers(surveyId, siteId, userId);
|
||||||
|
|
||||||
|
return !!answers.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save answers to be sent later.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param name Survey name.
|
||||||
|
* @param courseId Course ID the survey belongs to.
|
||||||
|
* @param answers Answers.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user in site.
|
||||||
|
* @return Promise resolved if stored, rejected if failure.
|
||||||
|
*/
|
||||||
|
async saveAnswers(
|
||||||
|
surveyId: number,
|
||||||
|
name: string,
|
||||||
|
courseId: number,
|
||||||
|
answers: AddonModSurveySubmitAnswerData[],
|
||||||
|
siteId?: string,
|
||||||
|
userId?: number,
|
||||||
|
): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const entry: AddonModSurveyAnswersDBRecord = {
|
||||||
|
surveyid: surveyId,
|
||||||
|
name: name,
|
||||||
|
courseid: courseId,
|
||||||
|
userid: userId,
|
||||||
|
answers: JSON.stringify(answers),
|
||||||
|
timecreated: new Date().getTime(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().insertRecord(SURVEY_TABLE, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveyOffline = makeSingleton(AddonModSurveyOfflineProvider);
|
||||||
|
|
||||||
|
export type AddonModSurveyAnswersDBRecordFormatted = Omit<AddonModSurveyAnswersDBRecord, 'answers'> & {
|
||||||
|
answers: AddonModSurveySubmitAnswerData[];
|
||||||
|
};
|
|
@ -0,0 +1,257 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreNetworkError } from '@classes/errors/network-error';
|
||||||
|
import { CoreCourseActivitySyncBaseProvider } from '@features/course/classes/activity-sync';
|
||||||
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
|
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
|
||||||
|
import { CoreApp } from '@services/app';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
|
import { CoreEvents } from '@singletons/events';
|
||||||
|
import { AddonModSurveyPrefetchHandler } from './handlers/prefetch';
|
||||||
|
import { AddonModSurvey, AddonModSurveyProvider } from './survey';
|
||||||
|
import { AddonModSurveyAnswersDBRecordFormatted, AddonModSurveyOffline } from './survey-offline';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to sync surveys.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvider<AddonModSurveySyncResult> {
|
||||||
|
|
||||||
|
static readonly AUTO_SYNCED = 'addon_mod_survey_autom_synced';
|
||||||
|
|
||||||
|
protected componentTranslate: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('AddonModSurveySyncProvider');
|
||||||
|
this.componentTranslate = CoreCourse.translateModuleName('survey');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID of a survey sync.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param userId User the answers belong to.
|
||||||
|
* @return Sync ID.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
getSyncId(surveyId: number, userId: number): string {
|
||||||
|
return surveyId + '#' + userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to synchronize all the surveys 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.
|
||||||
|
*/
|
||||||
|
syncAllSurveys(siteId?: string, force?: boolean): Promise<void> {
|
||||||
|
return this.syncOnSites('all surveys', this.syncAllSurveysFunc.bind(this, !!force), siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync all pending surveys on a site.
|
||||||
|
*
|
||||||
|
* @param force Wether to force sync not depending on last execution.
|
||||||
|
* @param siteId Site ID to sync.
|
||||||
|
* @param Promise resolved if sync is successful, rejected if sync fails.
|
||||||
|
*/
|
||||||
|
protected async syncAllSurveysFunc(force: boolean, siteId: string): Promise<void> {
|
||||||
|
// Get all survey answers pending to be sent in the site.
|
||||||
|
const entries = await AddonModSurveyOffline.getAllData(siteId);
|
||||||
|
|
||||||
|
// Sync all surveys.
|
||||||
|
const promises = entries.map(async (entry) => {
|
||||||
|
const result = await (force
|
||||||
|
? this.syncSurvey(entry.surveyid, entry.userid, siteId)
|
||||||
|
: this.syncSurveyIfNeeded(entry.surveyid, entry.userid, siteId));
|
||||||
|
|
||||||
|
if (result && result.answersSent) {
|
||||||
|
// Sync successful, send event.
|
||||||
|
CoreEvents.trigger(AddonModSurveySyncProvider.AUTO_SYNCED, {
|
||||||
|
surveyId: entry.surveyid,
|
||||||
|
userId: entry.userid,
|
||||||
|
warnings: result.warnings,
|
||||||
|
}, siteId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync a survey only if a certain time has passed since the last time.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param userId User the answers belong to.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the survey is synced or if it doesn't need to be synced.
|
||||||
|
*/
|
||||||
|
async syncSurveyIfNeeded(surveyId: number, userId: number, siteId?: string): Promise<AddonModSurveySyncResult | undefined> {
|
||||||
|
const syncId = this.getSyncId(surveyId, userId);
|
||||||
|
|
||||||
|
const needed = await this.isSyncNeeded(syncId, siteId);
|
||||||
|
if (needed) {
|
||||||
|
return this.syncSurvey(surveyId, userId, siteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize a survey.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved if sync is successful, rejected otherwise.
|
||||||
|
*/
|
||||||
|
async syncSurvey(surveyId: number, userId?: number, siteId?: string): Promise<AddonModSurveySyncResult> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
siteId = site.getId();
|
||||||
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
const syncId = this.getSyncId(surveyId, userId);
|
||||||
|
|
||||||
|
if (this.isSyncing(syncId, siteId)) {
|
||||||
|
// There's already a sync ongoing for this site, return the promise.
|
||||||
|
return this.getOngoingSync(syncId, siteId)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(`Try to sync survey '${surveyId}' for user '${userId}'`);
|
||||||
|
|
||||||
|
// Get offline events.
|
||||||
|
const syncPromise = this.performSyncSurvey(surveyId, userId, siteId);
|
||||||
|
|
||||||
|
return this.addOngoingSync(syncId, syncPromise, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the survey sync.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param userId User the answers belong to. If not defined, current user.
|
||||||
|
* @param siteId Site ID.
|
||||||
|
* @return Promise resolved if sync is successful, rejected otherwise.
|
||||||
|
*/
|
||||||
|
protected async performSyncSurvey(surveyId: number, userId: number, siteId: string): Promise<AddonModSurveySyncResult> {
|
||||||
|
const result: AddonModSurveySyncResult = {
|
||||||
|
warnings: [],
|
||||||
|
answersSent: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sync offline logs.
|
||||||
|
CoreUtils.ignoreErrors(CoreCourseLogHelper.syncActivity(AddonModSurveyProvider.COMPONENT, surveyId, siteId));
|
||||||
|
|
||||||
|
let answersNumber = 0;
|
||||||
|
let data: AddonModSurveyAnswersDBRecordFormatted | undefined;
|
||||||
|
try {
|
||||||
|
// Get answers to be sent.
|
||||||
|
data = await AddonModSurveyOffline.getSurveyData(surveyId, siteId, userId);
|
||||||
|
|
||||||
|
answersNumber = data.answers.length;
|
||||||
|
} catch {
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (answersNumber > 0 && data) {
|
||||||
|
if (!CoreApp.isOnline()) {
|
||||||
|
// Cannot sync in offline.
|
||||||
|
throw new CoreNetworkError();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.courseId = data.courseid;
|
||||||
|
|
||||||
|
// Send the answers.
|
||||||
|
try {
|
||||||
|
await AddonModSurvey.submitAnswersOnline(surveyId, data.answers, siteId);
|
||||||
|
|
||||||
|
result.answersSent = true;
|
||||||
|
|
||||||
|
// Answers sent, delete them.
|
||||||
|
await AddonModSurveyOffline.deleteSurveyAnswers(surveyId, siteId, userId);
|
||||||
|
} catch (error) {
|
||||||
|
if (!CoreUtils.isWebServiceError(error)) {
|
||||||
|
// Local error, reject.
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The WebService has thrown an error, this means that answers cannot be submitted. Delete them.
|
||||||
|
result.answersSent = true;
|
||||||
|
|
||||||
|
await AddonModSurveyOffline.deleteSurveyAnswers(surveyId, siteId, userId);
|
||||||
|
|
||||||
|
// Answers deleted, add a warning.
|
||||||
|
result.warnings.push(Translate.instant('core.warningofflinedatadeleted', {
|
||||||
|
component: this.componentTranslate,
|
||||||
|
name: data.name,
|
||||||
|
error: CoreTextUtils.getErrorMessageFromError(error),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.courseId) {
|
||||||
|
await AddonModSurvey.invalidateSurveyData(result.courseId, siteId);
|
||||||
|
|
||||||
|
// Data has been sent to server, update survey data.
|
||||||
|
const module = await CoreCourse.getModuleBasicInfoByInstance(surveyId, 'survey', siteId);
|
||||||
|
|
||||||
|
CoreUtils.ignoreErrors(
|
||||||
|
this.prefetchAfterUpdate(AddonModSurveyPrefetchHandler.instance, module, result.courseId, undefined, siteId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncId = this.getSyncId(surveyId, userId);
|
||||||
|
// Sync finished, set sync time.
|
||||||
|
CoreUtils.ignoreErrors(this.setSyncTime(syncId, siteId));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurveySync = makeSingleton(AddonModSurveySyncProvider);
|
||||||
|
|
||||||
|
declare module '@singletons/events' {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Augment CoreEventsData interface with events specific to this service.
|
||||||
|
*
|
||||||
|
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||||
|
*/
|
||||||
|
export interface CoreEventsData {
|
||||||
|
[AddonModSurveySyncProvider.AUTO_SYNCED]: AddonModSurveyAutoSyncData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by a assign sync.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveySyncResult = {
|
||||||
|
warnings: string[]; // List of warnings.
|
||||||
|
answersSent: boolean; // Whether some data was sent to the server or offline data was updated.
|
||||||
|
courseId?: number; // Course the survey belongs to (if known).
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data passed to AUTO_SYNCED event.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyAutoSyncData = {
|
||||||
|
surveyId: number;
|
||||||
|
warnings: string[];
|
||||||
|
userId: number;
|
||||||
|
};
|
|
@ -0,0 +1,393 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
import { CoreCourseCommonModWSOptions } from '@features/course/services/course';
|
||||||
|
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
|
||||||
|
import { CoreApp } from '@services/app';
|
||||||
|
import { CoreFilepool } from '@services/filepool';
|
||||||
|
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModSurveyOffline } from './survey-offline';
|
||||||
|
|
||||||
|
const ROOT_CACHE_KEY = 'mmaModSurvey:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features for surveys.
|
||||||
|
*/
|
||||||
|
@Injectable( { providedIn: 'root' })
|
||||||
|
export class AddonModSurveyProvider {
|
||||||
|
|
||||||
|
static readonly COMPONENT = 'mmaModSurvey';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey's questions.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the questions are retrieved.
|
||||||
|
*/
|
||||||
|
async getQuestions(surveyId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModSurveyQuestion[]> {
|
||||||
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
|
|
||||||
|
const params: AddonModSurveyGetQuestionsWSParams = {
|
||||||
|
surveyid: surveyId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getQuestionsCacheKey(surveyId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
component: AddonModSurveyProvider.COMPONENT,
|
||||||
|
componentId: options.cmId,
|
||||||
|
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await site.read<AddonModSurveyGetQuestionsWSResponse>('mod_survey_get_questions', params, preSets);
|
||||||
|
if (response.questions) {
|
||||||
|
return response.questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CoreError('No questions were found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for survey questions WS calls.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @return Cache key.
|
||||||
|
*/
|
||||||
|
protected getQuestionsCacheKey(surveyId: number): string {
|
||||||
|
return ROOT_CACHE_KEY + 'questions:' + surveyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for survey data WS calls.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @return Cache key.
|
||||||
|
*/
|
||||||
|
protected getSurveyCacheKey(courseId: number): string {
|
||||||
|
return ROOT_CACHE_KEY + 'survey:' + courseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey data.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param key Name of the property to check.
|
||||||
|
* @param value Value to search.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the survey is retrieved.
|
||||||
|
*/
|
||||||
|
protected async getSurveyDataByKey(
|
||||||
|
courseId: number,
|
||||||
|
key: string,
|
||||||
|
value: number,
|
||||||
|
options: CoreSitesCommonWSOptions = {},
|
||||||
|
): Promise<AddonModSurveySurvey> {
|
||||||
|
const site = await CoreSites.getSite(options.siteId);
|
||||||
|
|
||||||
|
const params: AddonModSurveyGetSurveysByCoursesWSParams = {
|
||||||
|
courseids: [courseId],
|
||||||
|
};
|
||||||
|
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getSurveyCacheKey(courseId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
component: AddonModSurveyProvider.COMPONENT,
|
||||||
|
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
|
||||||
|
};
|
||||||
|
|
||||||
|
const response =
|
||||||
|
await site.read<AddonModSurveyGetSurveysByCoursesWSResponse>('mod_survey_get_surveys_by_courses', params, preSets);
|
||||||
|
|
||||||
|
const currentSurvey = response.surveys.find((survey) => survey[key] == value);
|
||||||
|
if (currentSurvey) {
|
||||||
|
return currentSurvey;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CoreError('Activity not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey by course module ID.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param cmId Course module ID.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the survey is retrieved.
|
||||||
|
*/
|
||||||
|
getSurvey(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModSurveySurvey> {
|
||||||
|
return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a survey by ID.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param id Survey ID.
|
||||||
|
* @param options Other options.
|
||||||
|
* @return Promise resolved when the survey is retrieved.
|
||||||
|
*/
|
||||||
|
getSurveyById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModSurveySurvey> {
|
||||||
|
return this.getSurveyDataByKey(courseId, 'id', id, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate the prefetched content.
|
||||||
|
*
|
||||||
|
* @param moduleId The module ID.
|
||||||
|
* @param courseId Course ID of the module.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise<void> {
|
||||||
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
||||||
|
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
|
promises.push(this.getSurvey(courseId, moduleId).then(async (survey) => {
|
||||||
|
const ps: Promise<void>[] = [];
|
||||||
|
|
||||||
|
// Do not invalidate activity data before getting activity info, we need it!
|
||||||
|
ps.push(this.invalidateSurveyData(courseId, siteId));
|
||||||
|
ps.push(this.invalidateQuestions(survey.id, siteId));
|
||||||
|
|
||||||
|
await Promise.all(ps);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}));
|
||||||
|
|
||||||
|
promises.push(CoreFilepool.invalidateFilesByComponent(siteId, AddonModSurveyProvider.COMPONENT, moduleId));
|
||||||
|
|
||||||
|
await CoreUtils.allPromises(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates survey questions.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateQuestions(surveyId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await site.invalidateWsCacheForKey(this.getQuestionsCacheKey(surveyId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates survey data.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateSurveyData(courseId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await site.invalidateWsCacheForKey(this.getSurveyCacheKey(courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report the survey as being viewed.
|
||||||
|
*
|
||||||
|
* @param id Module ID.
|
||||||
|
* @param name Name of the assign.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when the WS call is successful.
|
||||||
|
*/
|
||||||
|
async logView(id: number, name?: string, siteId?: string): Promise<void> {
|
||||||
|
const params: AddonModSurveyViewSurveyWSParams = {
|
||||||
|
surveyid: id,
|
||||||
|
};
|
||||||
|
|
||||||
|
await CoreCourseLogHelper.logSingle(
|
||||||
|
'mod_survey_view_survey',
|
||||||
|
params,
|
||||||
|
AddonModSurveyProvider.COMPONENT,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
'survey',
|
||||||
|
{},
|
||||||
|
siteId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send survey answers. If cannot send them to Moodle, they'll be stored in offline to be sent later.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param name Survey name.
|
||||||
|
* @param courseId Course ID the survey belongs to.
|
||||||
|
* @param answers Answers.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with boolean if success: true if answers were sent to server,
|
||||||
|
* false if stored in device.
|
||||||
|
*/
|
||||||
|
async submitAnswers(
|
||||||
|
surveyId: number,
|
||||||
|
name: string,
|
||||||
|
courseId: number,
|
||||||
|
answers: AddonModSurveySubmitAnswerData[],
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
|
||||||
|
// Convenience function to store a survey to be synchronized later.
|
||||||
|
const storeOffline = async (): Promise<boolean> => {
|
||||||
|
await AddonModSurveyOffline.saveAnswers(surveyId, name, courseId, answers, siteId);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
||||||
|
|
||||||
|
if (!CoreApp.isOnline()) {
|
||||||
|
// App is offline, store the message.
|
||||||
|
return storeOffline();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If there's already answers to be sent to the server, discard it first.
|
||||||
|
await AddonModSurveyOffline.deleteSurveyAnswers(surveyId, siteId);
|
||||||
|
|
||||||
|
// Device is online, try to send them to server.
|
||||||
|
await this.submitAnswersOnline(surveyId, answers, siteId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
if (CoreUtils.isWebServiceError(error)) {
|
||||||
|
// It's a WebService error, the user cannot send the message so don't store it.
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return storeOffline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send survey answers to Moodle.
|
||||||
|
*
|
||||||
|
* @param surveyId Survey ID.
|
||||||
|
* @param answers Answers.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when answers are successfully submitted.
|
||||||
|
*/
|
||||||
|
async submitAnswersOnline(surveyId: number, answers: AddonModSurveySubmitAnswerData[], siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: AddonModSurveySubmitAnswersWSParams = {
|
||||||
|
surveyid: surveyId,
|
||||||
|
answers: answers,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await site.write<CoreStatusWithWarningsWSResponse>('mod_survey_submit_answers', params);
|
||||||
|
if (!response.status) {
|
||||||
|
throw new CoreError('Error submitting answers.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonModSurvey = makeSingleton(AddonModSurveyProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_survey_view_survey WS.
|
||||||
|
*/
|
||||||
|
type AddonModSurveyViewSurveyWSParams = {
|
||||||
|
surveyid: number; // Survey instance id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Survey returned by WS mod_survey_get_surveys_by_courses.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveySurvey = {
|
||||||
|
id: number; // Survey id.
|
||||||
|
coursemodule: number; // Course module id.
|
||||||
|
course: number; // Course id.
|
||||||
|
name: string; // Survey name.
|
||||||
|
intro?: string; // The Survey intro.
|
||||||
|
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||||
|
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||||
|
template?: number; // Survey type.
|
||||||
|
days?: number; // Days.
|
||||||
|
questions?: string; // Question ids.
|
||||||
|
surveydone?: number; // Did I finish the survey?.
|
||||||
|
timecreated?: number; // Time of creation.
|
||||||
|
timemodified?: number; // Time of last modification.
|
||||||
|
section?: number; // Course section id.
|
||||||
|
visible?: number; // Visible.
|
||||||
|
groupmode?: number; // Group mode.
|
||||||
|
groupingid?: number; // Group id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Survey question.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyQuestion = {
|
||||||
|
id: number; // Question id.
|
||||||
|
text: string; // Question text.
|
||||||
|
shorttext: string; // Question short text.
|
||||||
|
multi: string; // Subquestions ids.
|
||||||
|
intro: string; // The question intro.
|
||||||
|
type: number; // Question type.
|
||||||
|
options: string; // Question options.
|
||||||
|
parent: number; // Parent question (for subquestions).
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_survey_get_questions WS.
|
||||||
|
*/
|
||||||
|
type AddonModSurveyGetQuestionsWSParams = {
|
||||||
|
surveyid: number; // Survey instance id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by mod_survey_get_questions WS.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyGetQuestionsWSResponse = {
|
||||||
|
questions: AddonModSurveyQuestion[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_survey_get_surveys_by_courses WS.
|
||||||
|
*/
|
||||||
|
type AddonModSurveyGetSurveysByCoursesWSParams = {
|
||||||
|
courseids?: number[]; // Array of course ids.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by mod_survey_get_surveys_by_courses WS.
|
||||||
|
*/
|
||||||
|
export type AddonModSurveyGetSurveysByCoursesWSResponse = {
|
||||||
|
surveys: AddonModSurveySurvey[];
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddonModSurveySubmitAnswerData = {
|
||||||
|
key: string; // Answer key.
|
||||||
|
value: string; // Answer value.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of mod_survey_submit_answers WS.
|
||||||
|
*/
|
||||||
|
type AddonModSurveySubmitAnswersWSParams = {
|
||||||
|
surveyid: number; // Survey id.
|
||||||
|
answers: AddonModSurveySubmitAnswerData[];
|
||||||
|
};
|
|
@ -0,0 +1,39 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonModSurveyIndexPage } from './pages/index';
|
||||||
|
import { AddonModSurveyComponentsModule } from './components/components.module';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: ':courseId/:cmId',
|
||||||
|
component: AddonModSurveyIndexPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CoreSharedModule,
|
||||||
|
AddonModSurveyComponentsModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AddonModSurveyIndexPage,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModSurveyLazyModule {}
|
|
@ -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 { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
||||||
|
import { Routes } from '@angular/router';
|
||||||
|
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||||
|
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||||
|
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||||
|
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||||
|
import { CoreCronDelegate } from '@services/cron';
|
||||||
|
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||||
|
import { AddonModSurveyComponentsModule } from './components/components.module';
|
||||||
|
import { ADDON_MOD_SURVEY_OFFLINE_SITE_SCHEMA } from './services/database/survey';
|
||||||
|
import { AddonModSurveyIndexLinkHandler } from './services/handlers/index-link';
|
||||||
|
import { AddonModSurveyListLinkHandler } from './services/handlers/list-link';
|
||||||
|
import { AddonModSurveyModuleHandler, AddonModSurveyModuleHandlerService } from './services/handlers/module';
|
||||||
|
import { AddonModSurveyPrefetchHandler } from './services/handlers/prefetch';
|
||||||
|
import { AddonModSurveySyncCronHandler } from './services/handlers/sync-cron';
|
||||||
|
import { AddonModSurveyProvider } from './services/survey';
|
||||||
|
import { AddonModSurveyHelperProvider } from './services/survey-helper';
|
||||||
|
import { AddonModSurveyOfflineProvider } from './services/survey-offline';
|
||||||
|
import { AddonModSurveySyncProvider } from './services/survey-sync';
|
||||||
|
|
||||||
|
// List of providers (without handlers).
|
||||||
|
export const ADDON_MOD_SURVEY_SERVICES: Type<unknown>[] = [
|
||||||
|
AddonModSurveyProvider,
|
||||||
|
AddonModSurveyHelperProvider,
|
||||||
|
AddonModSurveySyncProvider,
|
||||||
|
AddonModSurveyOfflineProvider,
|
||||||
|
];
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: AddonModSurveyModuleHandlerService.PAGE_NAME,
|
||||||
|
loadChildren: () => import('./survey-lazy.module').then(m => m.AddonModSurveyLazyModule),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CoreMainMenuTabRoutingModule.forChild(routes),
|
||||||
|
AddonModSurveyComponentsModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: CORE_SITE_SCHEMAS,
|
||||||
|
useValue: [ADDON_MOD_SURVEY_OFFLINE_SITE_SCHEMA],
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
CoreCourseModuleDelegate.registerHandler(AddonModSurveyModuleHandler.instance);
|
||||||
|
CoreCourseModulePrefetchDelegate.registerHandler(AddonModSurveyPrefetchHandler.instance);
|
||||||
|
CoreCronDelegate.register(AddonModSurveySyncCronHandler.instance);
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonModSurveyIndexLinkHandler.instance);
|
||||||
|
CoreContentLinksDelegate.registerHandler(AddonModSurveyListLinkHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModSurveyModule {}
|
|
@ -137,7 +137,7 @@ import { ADDON_MOD_PAGE_SERVICES } from '@addons/mod/page/page.module';
|
||||||
import { ADDON_MOD_QUIZ_SERVICES } from '@addons/mod/quiz/quiz.module';
|
import { ADDON_MOD_QUIZ_SERVICES } from '@addons/mod/quiz/quiz.module';
|
||||||
import { ADDON_MOD_RESOURCE_SERVICES } from '@addons/mod/resource/resource.module';
|
import { ADDON_MOD_RESOURCE_SERVICES } from '@addons/mod/resource/resource.module';
|
||||||
// @todo import { ADDON_MOD_SCORM_SERVICES } from '@addons/mod/scorm/scorm.module';
|
// @todo import { ADDON_MOD_SCORM_SERVICES } from '@addons/mod/scorm/scorm.module';
|
||||||
// @todo import { ADDON_MOD_SURVEY_SERVICES } from '@addons/mod/survey/survey.module';
|
import { ADDON_MOD_SURVEY_SERVICES } from '@addons/mod/survey/survey.module';
|
||||||
import { ADDON_MOD_URL_SERVICES } from '@addons/mod/url/url.module';
|
import { ADDON_MOD_URL_SERVICES } from '@addons/mod/url/url.module';
|
||||||
// @todo import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module';
|
// @todo import { ADDON_MOD_WIKI_SERVICES } from '@addons/mod/wiki/wiki.module';
|
||||||
// @todo import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module';
|
// @todo import { ADDON_MOD_WORKSHOP_SERVICES } from '@addons/mod/workshop/workshop.module';
|
||||||
|
@ -302,7 +302,7 @@ export class CoreCompileProvider {
|
||||||
...ADDON_MOD_QUIZ_SERVICES,
|
...ADDON_MOD_QUIZ_SERVICES,
|
||||||
...ADDON_MOD_RESOURCE_SERVICES,
|
...ADDON_MOD_RESOURCE_SERVICES,
|
||||||
// @todo ...ADDON_MOD_SCORM_SERVICES,
|
// @todo ...ADDON_MOD_SCORM_SERVICES,
|
||||||
// @todo ...ADDON_MOD_SURVEY_SERVICES,
|
...ADDON_MOD_SURVEY_SERVICES,
|
||||||
...ADDON_MOD_URL_SERVICES,
|
...ADDON_MOD_URL_SERVICES,
|
||||||
// @todo ...ADDON_MOD_WIKI_SERVICES,
|
// @todo ...ADDON_MOD_WIKI_SERVICES,
|
||||||
// @todo ...ADDON_MOD_WORKSHOP_SERVICES,
|
// @todo ...ADDON_MOD_WORKSHOP_SERVICES,
|
||||||
|
|
Loading…
Reference in New Issue