From 421634dff7594a79a0f65e2b03739236e7e00d1e Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 2 Sep 2020 16:00:18 +0200 Subject: [PATCH 1/2] MOBILE-3338 mod: Apply component + id to mod WS calls --- .../mod/assign/components/index/index.ts | 9 +- .../components/submission/submission.ts | 6 +- src/addon/mod/assign/pages/edit/edit.ts | 19 +- .../pages/submission-list/submission-list.ts | 5 +- src/addon/mod/assign/providers/assign-sync.ts | 24 +- src/addon/mod/assign/providers/assign.ts | 251 +++-- src/addon/mod/assign/providers/helper.ts | 31 +- .../mod/assign/providers/prefetch-handler.ts | 79 +- src/addon/mod/book/providers/book.ts | 28 +- src/addon/mod/chat/pages/chat/chat.ts | 5 +- .../session-messages/session-messages.ts | 4 +- src/addon/mod/chat/pages/sessions/sessions.ts | 2 +- src/addon/mod/chat/pages/users/users.ts | 3 +- src/addon/mod/chat/providers/chat.ts | 83 +- .../mod/chat/providers/prefetch-handler.ts | 26 +- .../mod/choice/components/index/index.ts | 4 +- src/addon/mod/choice/providers/choice.ts | 85 +- .../mod/choice/providers/prefetch-handler.ts | 17 +- src/addon/mod/data/components/index/index.ts | 19 +- src/addon/mod/data/pages/edit/edit.ts | 4 +- src/addon/mod/data/pages/entry/entry.ts | 22 +- src/addon/mod/data/providers/data.ts | 327 ++++--- src/addon/mod/data/providers/helper.ts | 44 +- .../mod/data/providers/prefetch-handler.ts | 55 +- src/addon/mod/data/providers/sync.ts | 23 +- .../mod/feedback/components/index/index.ts | 6 +- .../mod/feedback/pages/attempt/attempt.ts | 2 +- src/addon/mod/feedback/pages/form/form.ts | 41 +- .../pages/nonrespondents/nonrespondents.ts | 6 +- .../feedback/pages/respondents/respondents.ts | 6 +- src/addon/mod/feedback/providers/feedback.ts | 495 +++++----- src/addon/mod/feedback/providers/helper.ts | 24 +- .../feedback/providers/prefetch-handler.ts | 40 +- src/addon/mod/feedback/providers/sync.ts | 11 +- src/addon/mod/folder/providers/folder.ts | 29 +- .../discussion-options-menu.ts | 2 +- src/addon/mod/forum/components/index/index.ts | 11 +- .../post-options-menu/post-options-menu.ts | 10 +- src/addon/mod/forum/components/post/post.ts | 3 +- .../mod/forum/pages/discussion/discussion.ts | 6 +- .../pages/new-discussion/new-discussion.ts | 10 +- src/addon/mod/forum/providers/forum.ts | 180 ++-- src/addon/mod/forum/providers/helper.ts | 6 +- .../mod/forum/providers/module-handler.ts | 2 +- .../mod/forum/providers/prefetch-handler.ts | 49 +- src/addon/mod/forum/providers/sync.ts | 4 +- .../mod/glossary/components/index/index.ts | 8 +- src/addon/mod/glossary/pages/edit/edit.ts | 17 +- .../glossary/providers/entry-link-handler.ts | 2 +- src/addon/mod/glossary/providers/glossary.ts | 333 ++++--- .../glossary/providers/prefetch-handler.ts | 29 +- src/addon/mod/glossary/providers/sync.ts | 2 +- .../mod/h5pactivity/components/index/index.ts | 9 +- .../pages/attempt-results/attempt-results.ts | 27 +- .../pages/user-attempts/user-attempts.ts | 17 +- .../mod/h5pactivity/providers/h5pactivity.ts | 107 +-- .../h5pactivity/providers/prefetch-handler.ts | 16 +- src/addon/mod/h5pactivity/providers/sync.ts | 2 +- src/addon/mod/imscp/providers/imscp.ts | 22 +- .../mod/imscp/providers/prefetch-handler.ts | 7 +- src/addon/mod/label/providers/label.ts | 53 +- .../mod/label/providers/prefetch-handler.ts | 6 +- .../mod/lesson/components/index/index.ts | 18 +- src/addon/mod/lesson/pages/player/player.ts | 102 ++- .../lesson/pages/user-retake/user-retake.ts | 9 +- .../lesson/providers/grade-link-handler.ts | 2 +- src/addon/mod/lesson/providers/lesson-sync.ts | 25 +- src/addon/mod/lesson/providers/lesson.ts | 867 ++++++++++-------- .../mod/lesson/providers/prefetch-handler.ts | 130 ++- src/addon/mod/lti/providers/lti.ts | 29 +- src/addon/mod/page/providers/page.ts | 29 +- src/addon/mod/quiz/components/index/index.ts | 18 +- src/addon/mod/quiz/pages/attempt/attempt.ts | 13 +- src/addon/mod/quiz/pages/player/player.ts | 41 +- src/addon/mod/quiz/pages/review/review.ts | 6 +- src/addon/mod/quiz/providers/helper.ts | 19 +- .../mod/quiz/providers/prefetch-handler.ts | 139 ++- src/addon/mod/quiz/providers/quiz-sync.ts | 36 +- src/addon/mod/quiz/providers/quiz.ts | 444 +++++---- src/addon/mod/resource/providers/resource.ts | 30 +- src/addon/mod/scorm/components/index/index.ts | 20 +- src/addon/mod/scorm/pages/player/player.ts | 53 +- src/addon/mod/scorm/providers/helper.ts | 48 +- .../mod/scorm/providers/prefetch-handler.ts | 35 +- src/addon/mod/scorm/providers/scorm-sync.ts | 84 +- src/addon/mod/scorm/providers/scorm.ts | 302 +++--- .../mod/survey/components/index/index.ts | 2 +- .../mod/survey/providers/prefetch-handler.ts | 13 +- src/addon/mod/survey/providers/survey.ts | 72 +- src/addon/mod/url/providers/url.ts | 30 +- src/addon/mod/wiki/components/index/index.ts | 10 +- src/addon/mod/wiki/pages/edit/edit.ts | 18 +- .../mod/wiki/providers/create-link-handler.ts | 11 +- .../providers/page-or-map-link-handler.ts | 2 +- .../mod/wiki/providers/prefetch-handler.ts | 39 +- src/addon/mod/wiki/providers/wiki-sync.ts | 11 +- src/addon/mod/wiki/providers/wiki.ts | 360 ++++---- .../assessment-strategy.ts | 6 +- .../components/assessment/assessment.ts | 4 +- .../mod/workshop/components/index/index.ts | 20 +- .../workshop/pages/assessment/assessment.ts | 8 +- .../pages/edit-submission/edit-submission.ts | 3 +- .../workshop/pages/submission/submission.ts | 17 +- src/addon/mod/workshop/providers/helper.ts | 48 +- .../workshop/providers/prefetch-handler.ts | 109 ++- src/addon/mod/workshop/providers/sync.ts | 22 +- src/addon/mod/workshop/providers/workshop.ts | 356 ++++--- src/core/course/providers/course.ts | 9 +- src/providers/sites.ts | 20 + 109 files changed, 3535 insertions(+), 2929 deletions(-) diff --git a/src/addon/mod/assign/components/index/index.ts b/src/addon/mod/assign/components/index/index.ts index d7370fea3..6f872d3c6 100644 --- a/src/addon/mod/assign/components/index/index.ts +++ b/src/addon/mod/assign/components/index/index.ts @@ -175,7 +175,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo this.hasOffline = hasOffline; // Get assignment submissions. - return this.assignProvider.getSubmissions(this.assign.id).then((data) => { + return this.assignProvider.getSubmissions(this.assign.id, {cmId: this.module.id}).then((data) => { const time = this.timeUtils.timestamp(); this.canViewAllSubmissions = data.canviewsubmissions; @@ -217,7 +217,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo } // Check if the user can view their own submission. - return this.assignProvider.getSubmissionStatus(this.assign.id).then(() => { + return this.assignProvider.getSubmissionStatus(this.assign.id, {cmId: this.module.id}).then(() => { this.canViewOwnSubmission = true; }).catch((error) => { this.canViewOwnSubmission = false; @@ -241,7 +241,10 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo setGroup(groupId: number): Promise { this.group = groupId; - return this.assignProvider.getSubmissionStatus(this.assign.id, undefined, this.group).then((response) => { + return this.assignProvider.getSubmissionStatus(this.assign.id, { + groupId: this.group, + cmId: this.module.id, + }).then((response) => { this.summary = response.gradingsummary; if (typeof this.summary.warnofungroupedusers == 'boolean' && this.summary.warnofungroupedusers) { this.summary.warnofungroupedusers = 'ungroupedusers'; diff --git a/src/addon/mod/assign/components/submission/submission.ts b/src/addon/mod/assign/components/submission/submission.ts index e3fc1cb28..92cf4167b 100644 --- a/src/addon/mod/assign/components/submission/submission.ts +++ b/src/addon/mod/assign/components/submission/submission.ts @@ -460,7 +460,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy { await Promise.all(promises); // Get submission status. - const response = await this.assignProvider.getSubmissionStatusWithRetry(this.assign, this.submitId, undefined, isBlind); + const response = await this.assignProvider.getSubmissionStatusWithRetry(this.assign, {userId: this.submitId, isBlind}); promises = []; @@ -996,7 +996,9 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy { response.lastattempt.submissiongroupmemberswhoneedtosubmit.forEach((member) => { if (this.blindMarking) { // Users not blinded! (Moodle < 3.1.1, 3.2). - promises.push(this.assignProvider.getAssignmentUserMappings(this.assign.id, member).then((blindId) => { + promises.push(this.assignProvider.getAssignmentUserMappings(this.assign.id, member, { + cmId: this.moduleId, + }).then((blindId) => { this.membersToSubmit.push(blindId); })); } else { diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts index 33fcf85b8..d080ffd23 100644 --- a/src/addon/mod/assign/pages/edit/edit.ts +++ b/src/addon/mod/assign/pages/edit/edit.ts @@ -16,7 +16,7 @@ import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/co import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreFileUploaderHelperProvider } from '@core/fileuploader/providers/helper'; @@ -125,11 +125,20 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy { }).then(() => { // Get submission status. Ignore cache to get the latest data. - return this.assignProvider.getSubmissionStatus(this.assign.id, this.userId, undefined, this.isBlind, false, true) - .catch((err) => { + const options = { + userId: this.userId, + isBlind: this.isBlind, + cmId: this.assign.cmid, + filter: false, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }; + + return this.assignProvider.getSubmissionStatus(this.assign.id, options).catch((err) => { // Cannot connect. Get cached data. - return this.assignProvider.getSubmissionStatus(this.assign.id, this.userId, undefined, this.isBlind) - .then((response) => { + options.filter = true; + options.readingStrategy = CoreSitesReadingStrategy.PreferCache; + + return this.assignProvider.getSubmissionStatus(this.assign.id, options).then((response) => { const userSubmission = this.assignProvider.getSubmissionObjectFromAttempt(this.assign, response.lastattempt); // Check if the user can edit it in offline. diff --git a/src/addon/mod/assign/pages/submission-list/submission-list.ts b/src/addon/mod/assign/pages/submission-list/submission-list.ts index 3ca5c7674..b0d6d2eca 100644 --- a/src/addon/mod/assign/pages/submission-list/submission-list.ts +++ b/src/addon/mod/assign/pages/submission-list/submission-list.ts @@ -153,7 +153,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy { } // Get assignment submissions. - this.submissionsData = await this.assignProvider.getSubmissions(this.assign.id); + this.submissionsData = await this.assignProvider.getSubmissions(this.assign.id, {cmId: this.assign.cmid}); if (!this.submissionsData.canviewsubmissions) { // User shouldn't be able to reach here. @@ -192,7 +192,8 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy { const promises = [ this.assignHelper.getSubmissionsUserData(this.assign, this.submissionsData.submissions, this.groupId), // Get assignment grades only if workflow is not enabled to check grading date. - !this.assign.markingworkflow ? this.assignProvider.getAssignmentGrades(this.assign.id) : Promise.resolve(null), + !this.assign.markingworkflow ? this.assignProvider.getAssignmentGrades(this.assign.id, {cmId: this.assign.cmid}) : + Promise.resolve(null), ]; return Promise.all(promises).then(([submissions, grades]: [AddonModAssignSubmissionFormatted[], AddonModAssignGrade[]]) => { diff --git a/src/addon/mod/assign/providers/assign-sync.ts b/src/addon/mod/assign/providers/assign-sync.ts index de6ee611c..3cc10f7f6 100644 --- a/src/addon/mod/assign/providers/assign-sync.ts +++ b/src/addon/mod/assign/providers/assign-sync.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -251,7 +251,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { const courseId = submissions.length > 0 ? submissions[0].courseid : grades[0].courseid; - const assign = await this.assignProvider.getAssignmentById(courseId, assignId, false, siteId); + const assign = await this.assignProvider.getAssignmentById(courseId, assignId, {siteId}); let promises = []; @@ -340,8 +340,14 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { const userId = offlineData.userid; const pluginData = {}; + const options = { + userId, + cmId: assign.cmid, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; - const status = await this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId); + const status = await this.assignProvider.getSubmissionStatus(assign.id, options); const submission = this.assignProvider.getSubmissionObjectFromAttempt(assign, status.lastattempt); @@ -370,7 +376,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { } // Submission data sent, update cached data. No need to block the user for this. - this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId); + this.assignProvider.getSubmissionStatus(assign.id, options); } catch (error) { if (!error || !this.utils.isWebServiceError(error)) { // Local error, reject. @@ -422,6 +428,12 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { const userId = offlineData.userid; const syncId = this.getGradeSyncId(assign.id, userId); + const options = { + userId, + cmId: assign.cmid, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; // Check if this grade sync is blocked. if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, syncId, siteId)) { @@ -431,7 +443,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { {$a: this.translate.instant('addon.mod_assign.syncblockedusercomponent')})); } - const status = await this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId); + const status = await this.assignProvider.getSubmissionStatus(assign.id, options); const timemodified = status.feedback && (status.feedback.gradeddate || status.feedback.grade.timemodified); @@ -483,7 +495,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { } // Update cached data. - promises.push(this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId)); + promises.push(this.assignProvider.getSubmissionStatus(assign.id, options)); await Promise.all(promises); } catch (error) { diff --git a/src/addon/mod/assign/providers/assign.ts b/src/addon/mod/assign/providers/assign.ts index 2ab77ae3a..cf570beb7 100644 --- a/src/addon/mod/assign/providers/assign.ts +++ b/src/addon/mod/assign/providers/assign.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -25,9 +25,10 @@ import { CoreGradesProvider } from '@core/grades/providers/grades'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModAssignSubmissionDelegate } from './submission-delegate'; import { AddonModAssignOfflineProvider } from './assign-offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreInterceptor } from '@classes/interceptor'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some functions for assign. @@ -143,12 +144,11 @@ export class AddonModAssignProvider { * * @param courseId Course ID the assignment belongs to. * @param cmId Assignment module ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the assignment. */ - getAssignment(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.getAssignmentByField(courseId, 'cmid', cmId, ignoreCache, siteId); + getAssignment(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getAssignmentByField(courseId, 'cmid', cmId, options); } /** @@ -157,27 +157,23 @@ export class AddonModAssignProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the assignment is retrieved. */ - protected getAssignmentByField(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string) + protected getAssignmentByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId], - includenotenrolledcourses: 1 - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAssignmentCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + includenotenrolledcourses: 1, + }; + const preSets = { + cacheKey: this.getAssignmentCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModAssignProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_get_assignments', params, preSets).catch(() => { // In 3.6 we added a new parameter includenotenrolledcourses that could cause offline data not to be found. @@ -206,13 +202,12 @@ export class AddonModAssignProvider { * Get an assignment by instance ID. * * @param courseId Course ID the assignment belongs to. - * @param cmId Assignment instance ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param id Assignment instance ID. + * @param options Other options. * @return Promise resolved with the assignment. */ - getAssignmentById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.getAssignmentByField(courseId, 'id', id, ignoreCache, siteId); + getAssignmentById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getAssignmentByField(courseId, 'id', id, options); } /** @@ -230,24 +225,22 @@ export class AddonModAssignProvider { * * @param assignId Assignment Id. * @param userId User Id to be blinded. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the user blind id. */ - getAssignmentUserMappings(assignId: number, userId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assignmentids: [assignId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAssignmentUserMappingsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; + getAssignmentUserMappings(assignId: number, userId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + return this.sitesProvider.getSite(options.siteId).then((site) => { + const params = { + assignmentids: [assignId], + }; + const preSets = { + cacheKey: this.getAssignmentUserMappingsCacheKey(assignId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModAssignProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_get_user_mappings', params, preSets) .then((response: AddonModAssignGetUserMappingsResult): any => { @@ -293,23 +286,21 @@ export class AddonModAssignProvider { * Returns grade information from assign_grades for the requested assignment id * * @param assignId Assignment Id. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with requested info when done. */ - getAssignmentGrades(assignId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - assignmentids: [assignId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAssignmentGradesCacheKey(assignId) - }; + getAssignmentGrades(assignId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + return this.sitesProvider.getSite(options.siteId).then((site) => { + const params = { + assignmentids: [assignId], + }; + const preSets = { + cacheKey: this.getAssignmentGradesCacheKey(assignId), + component: AddonModAssignProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_get_grades', params, preSets).then((response: AddonModAssignGetGradesResult): any => { // Search the assignment. @@ -455,26 +446,23 @@ export class AddonModAssignProvider { * Get an assignment submissions. * * @param assignId Assignment id. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - getSubmissions(assignId: number, ignoreCache?: boolean, siteId?: string) + getSubmissions(assignId: number, options: CoreCourseCommonModWSOptions = {}) : Promise<{canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]}> { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - assignmentids: [assignId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSubmissionsCacheKey(assignId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + assignmentids: [assignId], + }; + const preSets = { + cacheKey: this.getSubmissionsCacheKey(assignId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModAssignProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_get_submissions', params, preSets) .then((response: AddonModAssignGetSubmissionsResult): any => { @@ -510,46 +498,40 @@ export class AddonModAssignProvider { * Get information about an assignment submission status for a given user. * * @param assignId Assignment instance id. - * @param userId User Id (empty for current user). - * @param groupId Group Id (empty for all participants). - * @param isBlind If blind marking is enabled or not. - * @param filter True to filter WS response and rewrite URLs, false otherwise. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site id (empty for current site). + * @param options Other options. * @return Promise always resolved with the user submission status. */ - getSubmissionStatus(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true, - ignoreCache?: boolean, siteId?: string): Promise { + getSubmissionStatus(assignId: number, options: AddonModAssignSubmissionStatusOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const fixedParams = this.fixSubmissionStatusParams(site, userId, groupId, isBlind); + if (options.filter === undefined || options.filter === null) { + options.filter = true; + } + + return this.sitesProvider.getSite(options.siteId).then((site) => { + const fixedParams = this.fixSubmissionStatusParams(site, options.userId, options.groupId, options.isBlind); const params = { - assignid: assignId, - userid: fixedParams.userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSubmissionStatusCacheKey(assignId, fixedParams.userId, fixedParams.groupId, - fixedParams.isBlind), - getCacheUsingCacheKey: true, // We use the cache key to take isBlind into account. - filter: filter, - rewriteurls: filter - }; - + assignid: assignId, + userid: fixedParams.userId, + }; if (fixedParams.groupId) { params['groupid'] = fixedParams.groupId; } - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - if (!filter) { + const preSets = { + cacheKey: this.getSubmissionStatusCacheKey(assignId, fixedParams.userId, fixedParams.groupId, + fixedParams.isBlind), + getCacheUsingCacheKey: true, // We use the cache key to take isBlind into account. + filter: options.filter, + rewriteurls: options.filter, + component: AddonModAssignProvider.COMPONENT, + componentId: options.cmId, // Don't cache when getting text without filters. // @todo Change this to support offline editing. - preSets.saveToCache = false; - } + saveToCache: options.filter, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_get_submission_status', params, preSets); }); @@ -560,23 +542,24 @@ export class AddonModAssignProvider { * If the data doesn't include the user submission, retry ignoring cache. * * @param assign Assignment. - * @param userId User id (empty for current user). - * @param groupId Group Id (empty for all participants). - * @param isBlind If blind marking is enabled or not. - * @param filter True to filter WS response and rewrite URLs, false otherwise. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site id (empty for current site). + * @param options Other options. * @return Promise always resolved with the user submission status. */ - getSubmissionStatusWithRetry(assign: any, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true, - ignoreCache?: boolean, siteId?: string): Promise { + getSubmissionStatusWithRetry(assign: any, options: AddonModAssignSubmissionStatusOptions = {}) + : Promise { + options.cmId = options.cmId || assign.cmid; - return this.getSubmissionStatus(assign.id, userId, groupId, isBlind, filter, ignoreCache, siteId).then((response) => { + return this.getSubmissionStatus(assign.id, options).then((response) => { const userSubmission = this.getSubmissionObjectFromAttempt(assign, response.lastattempt); if (!userSubmission) { // Try again, ignoring cache. - return this.getSubmissionStatus(assign.id, userId, groupId, isBlind, filter, true, siteId).catch(() => { + const newOptions = { + ...options, // Include all the original options. + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }; + + return this.getSubmissionStatus(assign.id, newOptions).catch(() => { // Error, return the first result even if it doesn't have the user submission. return response; }); @@ -650,35 +633,32 @@ export class AddonModAssignProvider { * * @param assignId Assignment id. * @param groupId Group id. If not defined, 0. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of participants and summary of submissions. */ - listParticipants(assignId: number, groupId?: number, ignoreCache?: boolean, siteId?: string) + listParticipants(assignId: number, groupId?: number, options: CoreCourseCommonModWSOptions = {}) : Promise { groupId = groupId || 0; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { if (!site.wsAvailable('mod_assign_list_participants')) { // Silently fail if is not available. (needs Moodle version >= 3.2) return Promise.reject(null); } const params = { - assignid: assignId, - groupid: groupId, - filter: '' - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.listParticipantsCacheKey(assignId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + assignid: assignId, + groupid: groupId, + filter: '', + }; + const preSets = { + cacheKey: this.listParticipantsCacheKey(assignId, groupId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModAssignProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_assign_list_participants', params, preSets); }); @@ -769,7 +749,7 @@ export class AddonModAssignProvider { invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.getAssignment(courseId, moduleId, false, siteId).then((assign) => { + return this.getAssignment(courseId, moduleId, {siteId}).then((assign) => { const promises = []; // Do not invalidate assignment data before getting assignment info, we need it! @@ -1014,7 +994,10 @@ export class AddonModAssignProvider { } // We need more data to decide that. - return this.getSubmissionStatus(assignId, submission.submitid, undefined, submission.blindid).then((response) => { + return this.getSubmissionStatus(assignId, { + userId: submission.submitid, + isBlind: !!submission.blindid, + }).then((response) => { if (!response.feedback || !response.feedback.gradeddate) { // Not graded. return true; @@ -1304,6 +1287,16 @@ export class AddonModAssignProvider { } } +/** + * Options to pass to get submission status. + */ +export type AddonModAssignSubmissionStatusOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User Id (empty for current user). + groupId?: number; // Group Id (empty for all participants). + isBlind?: boolean; // If blind marking is enabled or not. + filter?: boolean; // True to filter WS response and rewrite URLs, false otherwise. Defaults to true. +}; + /** * Assign data returned by mod_assign_get_assignments. */ diff --git a/src/addon/mod/assign/providers/helper.ts b/src/addon/mod/assign/providers/helper.ts index 7cea16eef..4e7cc288e 100644 --- a/src/addon/mod/assign/providers/helper.ts +++ b/src/addon/mod/assign/providers/helper.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreFileProvider } from '@providers/file'; import { CoreGroupsProvider } from '@providers/groups'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; @@ -209,29 +209,29 @@ export class AddonModAssignHelperProvider { * * @param assign Assignment object. * @param groupId Group Id. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of participants and summary of submissions. */ - getParticipants(assign: AddonModAssignAssign, groupId?: number, ignoreCache?: boolean, siteId?: string) + getParticipants(assign: AddonModAssignAssign, groupId?: number, options: CoreSitesCommonWSOptions = {}) : Promise { groupId = groupId || 0; - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.assignProvider.listParticipants(assign.id, groupId, ignoreCache, siteId).then((participants) => { + const modOptions = {cmId: assign.cmid, ...options}; // Create new options including all existing ones. + + return this.assignProvider.listParticipants(assign.id, groupId, modOptions).then((participants) => { if (groupId || participants && participants.length > 0) { return participants; } // If no participants returned and all groups specified, get participants by groups. - return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((info) => { + return this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, modOptions.siteId).then((info) => { const promises = [], participants: {[id: number]: AddonModAssignParticipant} = {}; info.groups.forEach((userGroup) => { - promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, ignoreCache, siteId) - .then((parts) => { + promises.push(this.assignProvider.listParticipants(assign.id, userGroup.id, modOptions).then((parts) => { // Do not get repeated users. parts.forEach((participant) => { participants[participant.id] = participant; @@ -355,14 +355,15 @@ export class AddonModAssignHelperProvider { * @param assign Assignment object. * @param submissions Submissions to get the data for. * @param groupId Group Id. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site id (empty for current site). + * @param options Other options. * @return Promise always resolved. Resolve param is the formatted submissions. */ getSubmissionsUserData(assign: AddonModAssignAssign, submissions: AddonModAssignSubmissionFormatted[], groupId?: number, - ignoreCache?: boolean, siteId?: string): Promise { + options: CoreSitesCommonWSOptions = {}): Promise { - return this.getParticipants(assign, groupId).then((parts) => { + const modOptions = {cmId: assign.cmid, ...options}; // Create new options including all existing ones. + + return this.getParticipants(assign, groupId, modOptions).then((parts) => { const blind = assign.blindmarking && !assign.revealidentities; const promises = []; const result: AddonModAssignSubmissionFormatted[] = []; @@ -399,8 +400,8 @@ export class AddonModAssignHelperProvider { // Blind but not blinded! (Moodle < 3.1.1, 3.2). delete submission.userid; - promise = this.assignProvider.getAssignmentUserMappings(assign.id, submission.submitid, ignoreCache, siteId). - then((blindId) => { + promise = this.assignProvider.getAssignmentUserMappings(assign.id, submission.submitid, modOptions) + .then((blindId) => { submission.blindid = blindId; }); } diff --git a/src/addon/mod/assign/providers/prefetch-handler.ts b/src/addon/mod/assign/providers/prefetch-handler.ts index e67846720..26e2a11c4 100644 --- a/src/addon/mod/assign/providers/prefetch-handler.ts +++ b/src/addon/mod/assign/providers/prefetch-handler.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -80,13 +80,13 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan canUseCheckUpdates(module: any, courseId: number): boolean | Promise { // Teachers cannot use the WS because it doesn't check student submissions. return this.assignProvider.getAssignment(courseId, module.id).then((assign) => { - return this.assignProvider.getSubmissions(assign.id).then((data) => { + return this.assignProvider.getSubmissions(assign.id, {cmId: module.id}).then((data) => { if (data.canviewsubmissions) { return false; } // Check if the user can view their own submission. - return this.assignProvider.getSubmissionStatus(assign.id).then(() => { + return this.assignProvider.getSubmissionStatus(assign.id, {cmId: module.id}).then(() => { return true; }); }); @@ -108,18 +108,18 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.assignProvider.getAssignment(courseId, module.id, false, siteId).then((assign) => { + return this.assignProvider.getAssignment(courseId, module.id, {siteId}).then((assign) => { // Get intro files and attachments. let files = assign.introattachments || []; files = files.concat(this.getIntroFilesFromInstance(module, assign)); // Now get the files in the submissions. - return this.assignProvider.getSubmissions(assign.id, false, siteId).then((data) => { + return this.assignProvider.getSubmissions(assign.id, {cmId: module.id, siteId}).then((data) => { const blindMarking = assign.blindmarking && !assign.revealidentities; if (data.canviewsubmissions) { // Teacher, get all submissions. - return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, false, siteId) + return this.assignHelper.getSubmissionsUserData(assign, data.submissions, 0, {siteId}) .then((submissions: AddonModAssignSubmissionFormatted[]) => { const promises = []; @@ -172,8 +172,11 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan protected getSubmissionFiles(assign: any, submitId: number, blindMarking: boolean, siteId?: string) : Promise { - return this.assignProvider.getSubmissionStatusWithRetry(assign, submitId, undefined, blindMarking, true, false, siteId) - .then((response) => { + return this.assignProvider.getSubmissionStatusWithRetry(assign, { + userId: submitId, + isBlind: blindMarking, + siteId, + }).then((response) => { const promises = []; let userSubmission: AddonModAssignSubmission; @@ -261,20 +264,24 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan * @return Promise resolved when done. */ protected prefetchAssign(module: any, courseId: number, single: boolean, siteId: string): Promise { - const userId = this.sitesProvider.getCurrentSiteUserId(), - promises = []; + const userId = this.sitesProvider.getCurrentSiteUserId(); + const promises = []; siteId = siteId || this.sitesProvider.getCurrentSiteId(); + const options = { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + // Get assignment to retrieve all its submissions. - promises.push(this.assignProvider.getAssignment(courseId, module.id, true, siteId).then((assign) => { + promises.push(this.assignProvider.getAssignment(courseId, module.id, options).then((assign) => { const subPromises = [], blindMarking = assign.blindmarking && !assign.revealidentities; if (blindMarking) { - subPromises.push(this.assignProvider.getAssignmentUserMappings(assign.id, undefined, true, siteId).catch(() => { - // Ignore errors. - })); + subPromises.push(this.utils.ignoreErrors(this.assignProvider.getAssignmentUserMappings(assign.id, -1, options))); } subPromises.push(this.prefetchSubmissions(assign, courseId, module.id, userId, siteId)); @@ -304,8 +311,14 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan * @return Promise resolved when prefetched, rejected otherwise. */ protected prefetchSubmissions(assign: any, courseId: number, moduleId: number, userId: number, siteId: string): Promise { + const options = { + cmId: moduleId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + // Get submissions. - return this.assignProvider.getSubmissions(assign.id, true, siteId).then((data) => { + return this.assignProvider.getSubmissions(assign.id, options).then((data) => { const promises = []; promises.push(this.groupsProvider.getActivityGroupInfo(assign.cmid, false, undefined, siteId).then((groupInfo) => { @@ -317,14 +330,22 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan } groupInfo.groups.forEach((group) => { - groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, true, siteId) + groupProms.push(this.assignHelper.getSubmissionsUserData(assign, data.submissions, group.id, options) .then((submissions: AddonModAssignSubmissionFormatted[]) => { const subPromises = []; submissions.forEach((submission) => { - subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, submission.submitid, - group.id, !!submission.blindid, true, true, siteId).then((subm) => { + const submissionOptions = { + userId: submission.submitid, + groupId: group.id, + isBlind: !!submission.blindid, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + + subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, submissionOptions) + .then((subm) => { return this.prefetchSubmission(assign, courseId, moduleId, subm, submission.submitid, siteId); }).catch((error) => { if (error && error.errorcode == 'nopermission') { @@ -338,14 +359,21 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan if (!assign.markingworkflow) { // Get assignment grades only if workflow is not enabled to check grading date. - subPromises.push(this.assignProvider.getAssignmentGrades(assign.id, true, siteId)); + subPromises.push(this.assignProvider.getAssignmentGrades(assign.id, options)); } // Prefetch the submission of the current user even if it does not exist, this will be create it. if (!data.submissions || !data.submissions.find((subm: AddonModAssignSubmissionFormatted) => subm.submitid == userId)) { - subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, userId, group.id, - false, true, true, siteId).then((subm) => { + const submissionOptions = { + userId, + groupId: group.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + + subPromises.push(this.assignProvider.getSubmissionStatusWithRetry(assign, submissionOptions) + .then((subm) => { return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId); })); } @@ -353,7 +381,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan return Promise.all(subPromises); }).then(() => { // Participiants already fetched, we don't need to ignore cache now. - return this.assignHelper.getParticipants(assign, group.id, false, siteId).then((participants) => { + return this.assignHelper.getParticipants(assign, group.id, {siteId}).then((participants) => { return this.userProvider.prefetchUserAvatars(participants, 'profileimageurl', siteId); }).catch(() => { // Fail silently (Moodle < 3.2). @@ -367,8 +395,11 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan // Prefetch own submission, we need to do this for teachers too so the response with error is cached. promises.push( - this.assignProvider.getSubmissionStatusWithRetry(assign, userId, undefined, false, true, true, siteId) - .then((subm) => { + this.assignProvider.getSubmissionStatusWithRetry(assign, { + userId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((subm) => { return this.prefetchSubmission(assign, courseId, moduleId, subm, userId, siteId); }).catch((error) => { // Ignore if the user can't view their own submission. diff --git a/src/addon/mod/book/providers/book.ts b/src/addon/mod/book/providers/book.ts index 95d3264fd..a9e4f143b 100644 --- a/src/addon/mod/book/providers/book.ts +++ b/src/addon/mod/book/providers/book.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreFileProvider } from '@providers/file'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -73,11 +73,11 @@ export class AddonModBookProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the book is retrieved. */ - getBook(courseId: number, cmId: number, siteId?: string): Promise { - return this.getBookByField(courseId, 'coursemodule', cmId, siteId); + getBook(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getBookByField(courseId, 'coursemodule', cmId, options); } /** @@ -89,15 +89,19 @@ export class AddonModBookProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the book is retrieved. */ - protected getBookByField(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getBookByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getBookDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId] + }; + const preSets = { + cacheKey: this.getBookDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModBookProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_book_get_books_by_courses', params, preSets) .then((response: AddonModBookGetBooksByCoursesResult): any => { diff --git a/src/addon/mod/chat/pages/chat/chat.ts b/src/addon/mod/chat/pages/chat/chat.ts index 3130f397c..f5f50d7ba 100644 --- a/src/addon/mod/chat/pages/chat/chat.ts +++ b/src/addon/mod/chat/pages/chat/chat.ts @@ -127,7 +127,8 @@ export class AddonModChatChatPage { showChatUsers(): void { // Create the toc modal. const modal = this.modalCtrl.create('AddonModChatUsersPage', { - sessionId: this.sessionId + sessionId: this.sessionId, + cmId: this.cmId, }, { cssClass: 'core-modal-lateral', showBackdrop: true, enableBackdropDismiss: true, @@ -168,7 +169,7 @@ export class AddonModChatChatPage { return Promise.resolve(user.fullname); } - return this.chatProvider.getChatUsers(this.sessionId).then((data) => { + return this.chatProvider.getChatUsers(this.sessionId, {cmId: this.cmId}).then((data) => { this.users = data.users; const user = this.users.find((user) => user.id == id); diff --git a/src/addon/mod/chat/pages/session-messages/session-messages.ts b/src/addon/mod/chat/pages/session-messages/session-messages.ts index 39e15433e..aa894c218 100644 --- a/src/addon/mod/chat/pages/session-messages/session-messages.ts +++ b/src/addon/mod/chat/pages/session-messages/session-messages.ts @@ -60,8 +60,8 @@ export class AddonModChatSessionMessagesPage { * @return Promise resolved when done. */ protected fetchMessages(): Promise { - return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId) - .then((messages) => { + return this.chatProvider.getSessionMessages(this.chatId, this.sessionStart, this.sessionEnd, this.groupId, + {cmId: this.cmId}).then((messages) => { return this.chatProvider.getMessagesUserData(messages, this.courseId).then((messages) => { this.messages = messages; diff --git a/src/addon/mod/chat/pages/sessions/sessions.ts b/src/addon/mod/chat/pages/sessions/sessions.ts index 997341b4c..2b62d4ad9 100644 --- a/src/addon/mod/chat/pages/sessions/sessions.ts +++ b/src/addon/mod/chat/pages/sessions/sessions.ts @@ -72,7 +72,7 @@ export class AddonModChatSessionsPage { this.groupInfo = groupInfo; this.groupId = this.groupsProvider.validateGroupId(this.groupId, groupInfo); - return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll); + return this.chatProvider.getSessions(this.chatId, this.groupId, this.showAll, {cmId: this.cmId}); }).then((sessions: AddonModChatSessionFormatted[]) => { // Fetch user profiles. const promises = []; diff --git a/src/addon/mod/chat/pages/users/users.ts b/src/addon/mod/chat/pages/users/users.ts index e3e3c4028..6879416e9 100644 --- a/src/addon/mod/chat/pages/users/users.ts +++ b/src/addon/mod/chat/pages/users/users.ts @@ -36,6 +36,7 @@ export class AddonModChatUsersPage { isOnline: boolean; protected sessionId: string; + protected cmId: number; protected onlineObserver: any; constructor(navParams: NavParams, network: Network, zone: NgZone, private appProvider: CoreAppProvider, @@ -56,7 +57,7 @@ export class AddonModChatUsersPage { * View loaded. */ ionViewDidLoad(): void { - this.chatProvider.getChatUsers(this.sessionId).then((data) => { + this.chatProvider.getChatUsers(this.sessionId, {cmId: this.cmId}).then((data) => { this.users = data.users; }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilegettingchatusers', true); diff --git a/src/addon/mod/chat/providers/chat.ts b/src/addon/mod/chat/providers/chat.ts index 8fa311954..688c051fe 100644 --- a/src/addon/mod/chat/providers/chat.ts +++ b/src/addon/mod/chat/providers/chat.ts @@ -14,13 +14,14 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; import { AddonModChatMessageForView, AddonModChatSessionMessageForView } from './helper'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for chats. @@ -40,17 +41,19 @@ export class AddonModChatProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the chat is retrieved. */ - getChat(courseId: number, cmId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getChat(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { courseids: [courseId] }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getChatsCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModChatProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_chat_get_chats_by_courses', params, preSets) @@ -179,17 +182,25 @@ export class AddonModChatProvider { * Get the actives users of a current chat. * * @param sessionId Chat sessiond ID. + * @param options Other options. * @return Promise resolved when the WS is executed. */ - getChatUsers(sessionId: string): Promise { - const params = { - chatsid: sessionId - }; - const preSets = { - getFromCache: false - }; + getChatUsers(sessionId: string, options: CoreCourseCommonModWSOptions = {}): Promise { + // By default, always try to get the latest data. + options.readingStrategy = options.readingStrategy || CoreSitesReadingStrategy.PreferNetwork; - return this.sitesProvider.getCurrentSite().read('mod_chat_get_chat_users', params, preSets); + return this.sitesProvider.getSite(options.siteId).then((site) => { + const params = { + chatsid: sessionId, + }; + const preSets = { + component: AddonModChatProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; + + return site.read('mod_chat_get_chat_users', params, preSets); + }); } /** @@ -210,28 +221,26 @@ export class AddonModChatProvider { * @param chatId Chat ID. * @param groupId Group ID, 0 means that the function will determine the user group. * @param showAll Whether to include incomplete sessions or not. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of sessions. * @since 3.5 */ - getSessions(chatId: number, groupId: number = 0, showAll: boolean = false, ignoreCache: boolean = false, siteId?: string): + getSessions(chatId: number, groupId: number = 0, showAll: boolean = false, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { chatid: chatId, groupid: groupId, - showall: showAll ? 1 : 0 + showall: showAll ? 1 : 0, }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getSessionsCacheKey(chatId, groupId, showAll), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModChatProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } return site.read('mod_chat_get_sessions', params, preSets).then((response: AddonModChatGetSessionsResult): any => { if (!response || !response.sessions) { @@ -250,29 +259,27 @@ export class AddonModChatProvider { * @param sessionStart Session start time. * @param sessionEnd Session end time. * @param groupId Group ID, 0 means that the function will determine the user group. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of messages. * @since 3.5 */ - getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0, ignoreCache: boolean = false, - siteId?: string): Promise { + getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0, + options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { chatid: chatId, sessionstart: sessionStart, sessionend: sessionEnd, - groupid: groupId + groupid: groupId, }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getSessionMessagesCacheKey(chatId, sessionStart, groupId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModChatProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } return site.read('mod_chat_get_session_messages', params, preSets) .then((response: AddonModChatGetSessionMessagesResult): any => { diff --git a/src/addon/mod/chat/providers/prefetch-handler.ts b/src/addon/mod/chat/providers/prefetch-handler.ts index f5c5900b1..62821a577 100644 --- a/src/addon/mod/chat/providers/prefetch-handler.ts +++ b/src/addon/mod/chat/providers/prefetch-handler.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -122,9 +122,14 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl protected prefetchChat(module: any, courseId: number, single: boolean, siteId: string): Promise { // Prefetch chat and group info. const promises: Promise[] = [ - this.chatProvider.getChat(courseId, module.id, siteId), + this.chatProvider.getChat(courseId, module.id, {readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, siteId}), this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId) ]; + const options = { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; return Promise.all(promises).then(([chat, groupInfo]: [AddonModChatChat, CoreGroupInfo]) => { const promises = []; @@ -136,7 +141,7 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl groupIds.forEach((groupId) => { // Prefetch complete sessions. - promises.push(this.chatProvider.getSessions(chat.id, groupId, false, true, siteId).catch((error) => { + promises.push(this.chatProvider.getSessions(chat.id, groupId, false, options).catch((error) => { // Ignore group error. if (error.errorcode != 'notingroup') { return Promise.reject(error); @@ -144,8 +149,9 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl })); // Prefetch all sessions. - promises.push(this.chatProvider.getSessions(chat.id, groupId, true, true, siteId).then((sessions) => { - const promises = sessions.map((session) => this.prefetchSession(chat.id, session, 0, courseId, siteId)); + promises.push(this.chatProvider.getSessions(chat.id, groupId, true, options).then((sessions) => { + const promises = sessions.map((session) => this.prefetchSession(chat.id, session, 0, courseId, module.id, + siteId)); return Promise.all(promises); }).catch((error) => { @@ -170,9 +176,13 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl * @param siteId Site ID. * @return Promise resolved when done. */ - protected prefetchSession(chatId: number, session: any, groupId: number, courseId: number, siteId: string): Promise { - return this.chatProvider.getSessionMessages(chatId, session.sessionstart, session.sessionend, groupId, true, siteId) - .then((messages) => { + protected prefetchSession(chatId: number, session: any, groupId: number, courseId: number, cmId: number, siteId: string) + : Promise { + return this.chatProvider.getSessionMessages(chatId, session.sessionstart, session.sessionend, groupId, { + cmId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((messages) => { const users = {}; session.sessionusers.forEach((user) => { users[user.userid] = true; diff --git a/src/addon/mod/choice/components/index/index.ts b/src/addon/mod/choice/components/index/index.ts index 2ce8f309b..89dbcc0dc 100644 --- a/src/addon/mod/choice/components/index/index.ts +++ b/src/addon/mod/choice/components/index/index.ts @@ -174,7 +174,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo * @return Promise resolved when done. */ protected fetchOptions(hasOffline: boolean): Promise { - return this.choiceProvider.getOptions(this.choice.id).then((options) => { + return this.choiceProvider.getOptions(this.choice.id, {cmId: this.module.id}).then((options) => { let promise; // Check if the user has answered (synced) to allow show results. @@ -294,7 +294,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo return Promise.resolve(); } - return this.choiceProvider.getResults(this.choice.id).then((results) => { + return this.choiceProvider.getResults(this.choice.id, {cmId: this.module.id}).then((results) => { let hasVotes = false; this.data = []; this.labels = []; diff --git a/src/addon/mod/choice/providers/choice.ts b/src/addon/mod/choice/providers/choice.ts index 88808a6ba..3e74cfb66 100644 --- a/src/addon/mod/choice/providers/choice.ts +++ b/src/addon/mod/choice/providers/choice.ts @@ -13,14 +13,15 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModChoiceOfflineProvider } from './offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for choices. @@ -173,34 +174,26 @@ export class AddonModChoiceProvider { /** * Get a choice with key=value. If more than one is found, only the first will be returned. * - * @param siteId Site ID. * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the choice is retrieved. */ - protected getChoiceByDataKey(siteId: string, courseId: number, key: string, value: any, forceCache?: boolean, - ignoreCache?: boolean): Promise { + protected getChoiceByDataKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { courseids: [courseId] }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getChoiceDataCacheKey(courseId), - omitExpires: forceCache, - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModChoiceProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - return site.read('mod_choice_get_choices_by_courses', params, preSets) .then((response: AddonModChoiceGetChoicesByCoursesResult): any => { @@ -221,14 +214,11 @@ export class AddonModChoiceProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the choice is retrieved. */ - getChoice(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean) - : Promise { - return this.getChoiceByDataKey(siteId, courseId, 'coursemodule', cmId, forceCache, ignoreCache); + getChoice(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getChoiceByDataKey(courseId, 'coursemodule', cmId, options); } /** @@ -236,39 +226,33 @@ export class AddonModChoiceProvider { * * @param courseId Course ID. * @param choiceId Choice ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the choice is retrieved. */ - getChoiceById(courseId: number, choiceId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean) - : Promise { - return this.getChoiceByDataKey(siteId, courseId, 'id', choiceId, forceCache, ignoreCache); + getChoiceById(courseId: number, choiceId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getChoiceByDataKey(courseId, 'id', choiceId, options); } /** * Get choice options. * * @param choiceId Choice ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with choice options. */ - getOptions(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getOptions(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { choiceid: choiceId }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getChoiceOptionsCacheKey(choiceId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModChoiceProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - return site.read('mod_choice_get_choice_options', params, preSets) .then((response: AddonModChoiceGetChoiceOptionsResult): any => { @@ -285,24 +269,21 @@ export class AddonModChoiceProvider { * Get choice results. * * @param choiceId Choice ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with choice results. */ - getResults(choiceId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getResults(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { choiceid: choiceId }; - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getChoiceResultsCacheKey(choiceId) + const preSets = { + cacheKey: this.getChoiceOptionsCacheKey(choiceId), + component: AddonModChoiceProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - return site.read('mod_choice_get_choice_results', params, preSets) .then((response: AddonModChoiceGetChoiceResults): any => { diff --git a/src/addon/mod/choice/providers/prefetch-handler.ts b/src/addon/mod/choice/providers/prefetch-handler.ts index 656ab5efc..f319c6044 100644 --- a/src/addon/mod/choice/providers/prefetch-handler.ts +++ b/src/addon/mod/choice/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -79,12 +79,21 @@ export class AddonModChoicePrefetchHandler extends CoreCourseActivityPrefetchHan * @return Promise resolved when done. */ protected prefetchChoice(module: any, courseId: number, single: boolean, siteId: string): Promise { - return this.choiceProvider.getChoice(courseId, module.id, siteId, false, true).then((choice) => { + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + + return this.choiceProvider.getChoice(courseId, module.id, commonOptions).then((choice) => { const promises = []; // Get the options and results. - promises.push(this.choiceProvider.getOptions(choice.id, true, siteId)); - promises.push(this.choiceProvider.getResults(choice.id, true, siteId).then((options) => { + promises.push(this.choiceProvider.getOptions(choice.id, modOptions)); + promises.push(this.choiceProvider.getResults(choice.id, modOptions).then((options) => { // If we can see the users that answered, prefetch their profile and avatar. const subPromises = []; options.forEach((option) => { diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts index b63f00ce0..34feeef8f 100644 --- a/src/addon/mod/data/components/index/index.ts +++ b/src/addon/mod/data/components/index/index.ts @@ -198,7 +198,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp }); } }).then(() => { - return this.dataProvider.getDatabaseAccessInformation(this.data.id); + return this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id}); }).then((accessData) => { this.access = accessData; @@ -226,7 +226,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp this.selectedGroup = this.groupsProvider.validateGroupId(this.selectedGroup, groupInfo); }); }).then(() => { - return this.dataProvider.getFields(this.data.id).then((fields) => { + return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}).then((fields) => { if (fields.length == 0) { canSearch = false; canAdd = false; @@ -252,15 +252,24 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp */ protected fetchEntriesData(): Promise { - return this.dataProvider.getDatabaseAccessInformation(this.data.id, this.selectedGroup).then((accessData) => { + return this.dataProvider.getDatabaseAccessInformation(this.data.id, { + groupId: this.selectedGroup, + cmId: this.module.id, + }).then((accessData) => { // Update values for current group. this.access.canaddentry = accessData.canaddentry; const search = this.search.searching && !this.search.searchingAdvanced ? this.search.text : undefined; const advSearch = this.search.searching && this.search.searchingAdvanced ? this.search.advanced : undefined; - return this.dataHelper.fetchEntries(this.data, this.fieldsArray, this.selectedGroup, search, advSearch, - this.search.sortBy, this.search.sortDirection, this.search.page); + return this.dataHelper.fetchEntries(this.data, this.fieldsArray, { + groupId: this.selectedGroup, + search, + advSearch, + sort: Number(this.search.sortBy), + order: this.search.sortDirection, + page: this.search.page, + }); }).then((entries) => { const numEntries = entries.entries.length; const numOfflineEntries = entries.offlineEntries.length; diff --git a/src/addon/mod/data/pages/edit/edit.ts b/src/addon/mod/data/pages/edit/edit.ts index 877785bbd..b3542eebf 100644 --- a/src/addon/mod/data/pages/edit/edit.ts +++ b/src/addon/mod/data/pages/edit/edit.ts @@ -128,7 +128,7 @@ export class AddonModDataEditPage { this.data = data; this.cssClass = 'addon-data-entries-' + data.id; - return this.dataProvider.getDatabaseAccessInformation(data.id); + return this.dataProvider.getDatabaseAccessInformation(data.id, {cmId: this.module.id}); }).then((accessData) => { if (this.entryId) { return this.groupsProvider.getActivityGroupInfo(this.data.coursemodule).then((groupInfo) => { @@ -137,7 +137,7 @@ export class AddonModDataEditPage { }); } }).then(() => { - return this.dataProvider.getFields(this.data.id); + return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}); }).then((fieldsData) => { this.fieldsArray = fieldsData; this.fields = this.utils.arrayToObject(fieldsData, 'id'); diff --git a/src/addon/mod/data/pages/entry/entry.ts b/src/addon/mod/data/pages/entry/entry.ts index ed8e2a84e..7a10b347a 100644 --- a/src/addon/mod/data/pages/entry/entry.ts +++ b/src/addon/mod/data/pages/entry/entry.ts @@ -142,13 +142,13 @@ export class AddonModDataEntryPage implements OnDestroy { this.title = data.name || this.title; this.data = data; - return this.dataProvider.getFields(this.data.id).then((fieldsData) => { + return this.dataProvider.getFields(this.data.id, {cmId: this.module.id}).then((fieldsData) => { this.fields = this.utils.arrayToObject(fieldsData, 'id'); this.fieldsArray = fieldsData; }); }).then(() => { return this.setEntryFromOffset().then(() => { - return this.dataProvider.getDatabaseAccessInformation(this.data.id); + return this.dataProvider.getDatabaseAccessInformation(this.data.id, {cmId: this.module.id}); }); }).then((accessData) => { this.access = accessData; @@ -290,8 +290,13 @@ export class AddonModDataEntryPage implements OnDestroy { const perPage = AddonModDataProvider.PER_PAGE; const page = !emptyOffset && this.offset >= 0 ? Math.floor(this.offset / perPage) : 0; - return this.dataHelper.fetchEntries(this.data, this.fieldsArray, this.selectedGroup, undefined, undefined, '0', 'DESC', - page, perPage).then((entries) => { + return this.dataHelper.fetchEntries(this.data, this.fieldsArray, { + groupId: this.selectedGroup, + sort: 0, + order: 'DESC', + page, + perPage, + }).then((entries) => { const pageEntries = entries.offlineEntries.concat(entries.entries); let pageIndex; // Index of the entry when concatenating offline and online page entries. @@ -321,8 +326,11 @@ export class AddonModDataEntryPage implements OnDestroy { this.nextOffset = null; } else { // Last entry of the page, check if there are more pages. - promise = this.dataProvider.getEntries(this.data.id, this.selectedGroup, '0', 'DESC', page + 1, perPage) - .then((entries) => { + promise = this.dataProvider.getEntries(this.data.id, { + groupId: this.selectedGroup, + page: page + 1, + perPage: perPage, + }).then((entries) => { this.nextOffset = entries && entries.entries && entries.entries.length > 0 ? this.offset + 1 : null; }); } @@ -330,7 +338,7 @@ export class AddonModDataEntryPage implements OnDestroy { return Promise.resolve(promise).then(() => { if (this.entryId > 0) { // Online entry, we need to fetch the the rating info. - return this.dataProvider.getEntry(this.data.id, this.entryId).then((entry) => { + return this.dataProvider.getEntry(this.data.id, this.entryId, {cmId: this.module.id}).then((entry) => { this.ratingInfo = entry.ratinginfo; }); } diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts index 481896b28..cde86d294 100644 --- a/src/addon/mod/data/providers/data.ts +++ b/src/addon/mod/data/providers/data.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreAppProvider } from '@providers/app'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; @@ -23,6 +23,7 @@ import { AddonModDataOfflineProvider } from './offline'; import { AddonModDataFieldsDelegate } from './fields-delegate'; import { CoreRatingInfo } from '@core/rating/providers/rating'; import { CoreSite } from '@classes/site'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Database entry (online or offline). @@ -482,49 +483,34 @@ export class AddonModDataProvider { * Performs the whole fetch of the entries in the database. * * @param dataId Data ID. - * @param groupId Group ID. - * @param sort Sort the records by this field id. See AddonModDataProvider#getEntries for more info. - * @param order The direction of the sorting. See AddonModDataProvider#getEntries for more info. - * @param perPage Records per page to fetch. It has to match with the prefetch. - * Default on AddonModDataProvider.PER_PAGE. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - fetchAllEntries(dataId: number, groupId: number = 0, sort: string = '0', order: string = 'DESC', - perPage: number = AddonModDataProvider.PER_PAGE, forceCache: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + fetchAllEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); + options.page = 0; - return this.fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, [], 0, siteId); + return this.fetchEntriesRecursive(dataId, [], options); } /** * Recursive call on fetch all entries. * * @param dataId Data ID. - * @param groupId Group ID. - * @param sort Sort the records by this field id. See AddonModDataProvider#getEntries for more info. - * @param order The direction of the sorting. See AddonModDataProvider#getEntries for more info. - * @param perPage Records per page to fetch. It has to match with the prefetch. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @param entries Entries already fetch (just to concatenate them). - * @param page Page of records to return. - * @param siteId Site ID. + * @param options Other options. * @return Promise resolved when done. */ - protected fetchEntriesRecursive(dataId: number, groupId: number, sort: string, order: string, perPage: number, - forceCache: boolean, ignoreCache: boolean, entries: any, page: number, siteId: string): Promise { - return this.getEntries(dataId, groupId, sort, order, page, perPage, forceCache, ignoreCache, siteId) - .then((result) => { + protected fetchEntriesRecursive(dataId: number, entries: any, options: AddonModDataGetEntriesOptions = {}) + : Promise { + return this.getEntries(dataId, options).then((result) => { entries = entries.concat(result.entries); - const canLoadMore = perPage > 0 && ((page + 1) * perPage) < result.totalcount; + const canLoadMore = options.perPage > 0 && ((options.page + 1) * options.perPage) < result.totalcount; if (canLoadMore) { - return this.fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, entries, page + 1, - siteId); + options.page++; + + return this.fetchEntriesRecursive(dataId, entries, options); } return entries; @@ -557,23 +543,21 @@ export class AddonModDataProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the data is retrieved. */ - protected getDatabaseByKey(courseId: number, key: string, value: any, siteId?: string, forceCache: boolean = false): + protected getDatabaseByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getDatabaseDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - if (forceCache) { - preSets['omitExpires'] = true; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getDatabaseDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModDataProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_data_get_databases_by_courses', params, preSets).then((response) => { if (response && response.databases) { @@ -593,12 +577,11 @@ export class AddonModDataProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the data is retrieved. */ - getDatabase(courseId: number, cmId: number, siteId?: string, forceCache: boolean = false): Promise { - return this.getDatabaseByKey(courseId, 'coursemodule', cmId, siteId, forceCache); + getDatabase(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getDatabaseByKey(courseId, 'coursemodule', cmId, options); } /** @@ -606,12 +589,11 @@ export class AddonModDataProvider { * * @param courseId Course ID. * @param id Data ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the data is retrieved. */ - getDatabaseById(courseId: number, id: number, siteId?: string, forceCache: boolean = false): Promise { - return this.getDatabaseByKey(courseId, 'id', id, siteId, forceCache); + getDatabaseById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getDatabaseByKey(courseId, 'id', id, options); } /** @@ -639,31 +621,23 @@ export class AddonModDataProvider { * Get access information for a given database. * * @param dataId Data ID. - * @param groupId Group ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it'll always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the database is retrieved. */ - getDatabaseAccessInformation(dataId: number, groupId?: number, offline: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getDatabaseAccessInformation(dataId: number, options: AddonModDataAccessInfoOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - databaseid: dataId - }, - preSets = { - cacheKey: this.getDatabaseAccessInformationDataCacheKey(dataId, groupId) - }; + databaseid: dataId, + }; + const preSets = { + cacheKey: this.getDatabaseAccessInformationDataCacheKey(dataId, options.groupId), + component: AddonModDataProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (typeof groupId !== 'undefined') { - params['groupid'] = groupId; - } - - if (offline) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; + if (typeof options.groupId !== 'undefined') { + params['groupid'] = options.groupId; } return site.read('mod_data_get_data_access_information', params, preSets); @@ -674,48 +648,34 @@ export class AddonModDataProvider { * Get entries for a specific database and group. * * @param dataId Data ID. - * @param groupId Group ID. - * @param sort Sort the records by this field id, reserved ids are: - * 0: timeadded - * -1: firstname - * -2: lastname - * -3: approved - * -4: timemodified. - * Empty for using the default database setting. - * @param order The direction of the sorting: 'ASC' or 'DESC'. - * Empty for using the default database setting. - * @param page Page of records to return. - * @param perPage Records per page to return. Default on PER_PAGE. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it'll always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the database is retrieved. */ - getEntries(dataId: number, groupId: number = 0, sort: string = '0', order: string = 'DESC', page: number = 0, - perPage: number = AddonModDataProvider.PER_PAGE, forceCache: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise { + options.groupId = options.groupId || 0; + options.sort = options.sort || 0; + options.order || options.order || 'DESC'; + options.page = options.page || 0; + options.perPage = options.perPage || AddonModDataProvider.PER_PAGE; + + return this.sitesProvider.getSite(options.siteId).then((site) => { // Always use sort and order params to improve cache usage (entries are identified by params). const params = { - databaseid: dataId, - returncontents: 1, - page: page, - perpage: perPage, - groupid: groupId, - sort: sort, - order: order - }, - preSets = { - cacheKey: this.getEntriesCacheKey(dataId, groupId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (forceCache) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + databaseid: dataId, + returncontents: 1, + page: options.page, + perpage: options.perPage, + groupid: options.groupId, + sort: options.sort, + order: options.order, + }; + const preSets = { + cacheKey: this.getEntriesCacheKey(dataId, options.groupId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModDataProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_data_get_entries', params, preSets).then((response) => { response.entries.forEach((entry) => { @@ -753,26 +713,23 @@ export class AddonModDataProvider { * * @param dataId Data ID for caching purposes. * @param entryId Entry ID. - * @param ignoreCache True if it should ignore cached data (it'll always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the entry is retrieved. */ - getEntry(dataId: number, entryId: number, ignoreCache: boolean = false, siteId?: string): + getEntry(dataId: number, entryId: number, options: CoreCourseCommonModWSOptions = {}): Promise<{entry: AddonModDataEntry, ratinginfo: CoreRatingInfo}> { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - entryid: entryId, - returncontents: 1 - }, - preSets = { - cacheKey: this.getEntryCacheKey(dataId, entryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + entryid: entryId, + returncontents: 1, + }; + const preSets = { + cacheKey: this.getEntryCacheKey(dataId, entryId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModDataProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_data_get_entry', params, preSets).then((response) => { response.entry.contents = this.utils.arrayToObject(response.entry.contents, 'fieldid'); @@ -797,27 +754,21 @@ export class AddonModDataProvider { * Get the list of configured fields for the given database. * * @param dataId Data ID. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the fields are retrieved. */ - getFields(dataId: number, forceCache: boolean = false, ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getFields(dataId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - databaseid: dataId - }, - preSets = { - cacheKey: this.getFieldsCacheKey(dataId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + databaseid: dataId, + }; + const preSets = { + cacheKey: this.getFieldsCacheKey(dataId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModDataProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_data_get_fields', params, preSets).then((response) => { if (response && response.fields) { @@ -993,46 +944,45 @@ export class AddonModDataProvider { * Performs search over a database. * * @param dataId The data instance id. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param search Search text. It will be used if advSearch is not defined. - * @param advSearch Advanced search data. - * @param sort Sort by this field. - * @param order The direction of the sorting. - * @param page Page of records to return. - * @param perPage Records per page to return. Default on AddonModDataProvider.PER_PAGE. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the action is done. */ - searchEntries(dataId: number, groupId: number = 0, search?: string, advSearch?: any, sort?: string, order?: string, - page: number = 0, perPage: number = AddonModDataProvider.PER_PAGE, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + searchEntries(dataId: number, options?: AddonModDataSearchEntriesOptions): Promise { + options.groupId = options.groupId || 0; + options.sort = options.sort || 0; + options.order || options.order || 'DESC'; + options.page = options.page || 0; + options.perPage = options.perPage || AddonModDataProvider.PER_PAGE; + options.readingStrategy = options.readingStrategy || CoreSitesReadingStrategy.PreferNetwork; + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - databaseid: dataId, - groupid: groupId, - returncontents: 1, - page: page, - perpage: perPage - }, - preSets = { - getFromCache: false, - saveToCache: true, - emergencyCache: true - }; + databaseid: dataId, + groupid: options.groupId, + returncontents: 1, + page: options.page, + perpage: options.perPage, + }; + const preSets = { + component: AddonModDataProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (typeof sort != 'undefined') { - params['sort'] = sort; + if (typeof options.sort != 'undefined') { + params['sort'] = options.sort; } - if (typeof order !== 'undefined') { - params['order'] = order; + if (typeof options.order !== 'undefined') { + params['order'] = options.order; } - if (typeof search !== 'undefined') { - params['search'] = search; + if (typeof options.search !== 'undefined') { + params['search'] = options.search; } - if (typeof advSearch !== 'undefined') { - params['advsearch'] = advSearch; + if (typeof options.advSearch !== 'undefined') { + params['advsearch'] = options.advSearch; } return site.read('mod_data_search_entries', params, preSets).then((response) => { @@ -1045,3 +995,34 @@ export class AddonModDataProvider { }); } } + +/** + * Options to pass to get access info. + */ +export type AddonModDataAccessInfoOptions = CoreCourseCommonModWSOptions & { + groupId?: number; // Group Id. +}; + +/** + * Options to pass to get entries. + */ +export type AddonModDataGetEntriesOptions = CoreCourseCommonModWSOptions & { + groupId?: number; // Group Id. + sort?: number; // Sort the records by this field id, defaults to 0. Reserved ids are: + // 0: timeadded + // -1: firstname + // -2: lastname + // -3: approved + // -4: timemodified + order?: string; // The direction of the sorting: 'ASC' or 'DESC'. Defaults to 'DESC'. + page?: number; // Page of records to return. Defaults to 0. + perPage?: number; // Records per page to return. Defaults to AddonModDataProvider.PER_PAGE. +}; + +/** + * Options to pass to search entries. + */ +export type AddonModDataSearchEntriesOptions = AddonModDataGetEntriesOptions & { + search?: string; // Search text. It will be used if advSearch is not defined. + advSearch?: any; // Advanced search data. +}; diff --git a/src/addon/mod/data/providers/helper.ts b/src/addon/mod/data/providers/helper.ts index 2d48ef390..ea686e422 100644 --- a/src/addon/mod/data/providers/helper.ts +++ b/src/addon/mod/data/providers/helper.ts @@ -23,7 +23,9 @@ import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; import { AddonModDataFieldsDelegate } from './fields-delegate'; import { AddonModDataOfflineProvider, AddonModDataOfflineAction } from './offline'; -import { AddonModDataProvider, AddonModDataEntry, AddonModDataEntryFields, AddonModDataEntries } from './data'; +import { + AddonModDataProvider, AddonModDataEntry, AddonModDataEntryFields, AddonModDataEntries, AddonModDataSearchEntriesOptions +} from './data'; import { CoreRatingInfo } from '@core/rating/providers/rating'; import { CoreRatingOfflineProvider } from '@core/rating/providers/offline'; @@ -210,33 +212,21 @@ export class AddonModDataHelperProvider { * * @param data Database object. * @param fields The fields that define the contents. - * @param groupId Group ID. - * @param search Search text. It will be used if advSearch is not defined. - * @param advSearch Advanced search data. - * @param sort Sort the records by this field id, reserved ids are: - * 0: timeadded - * -1: firstname - * -2: lastname - * -3: approved - * -4: timemodified. - * Empty for using the default database setting. - * @param order The direction of the sorting: 'ASC' or 'DESC'. - * Empty for using the default database setting. - * @param page Page of records to return. - * @param perPage Records per page to return. Default on PER_PAGE. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the database is retrieved. */ - fetchEntries(data: any, fields: any[], groupId: number = 0, search?: string, advSearch?: any[], sort: string = '0', - order: string = 'DESC', page: number = 0, perPage: number = AddonModDataProvider.PER_PAGE, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + fetchEntries(data: any, fields: any[], options: AddonModDataSearchEntriesOptions = {}): Promise { + options.groupId = options.groupId || 0; + options.page = options.page || 0; + + return this.sitesProvider.getSite(options.siteId).then((site) => { const offlineActions = {}; const result: AddonModDataEntries = { entries: [], totalcount: 0, offlineEntries: [] }; + options.siteId = site.id; const offlinePromise = this.dataOffline.getDatabaseEntries(data.id, site.id).then((actions) => { result.hasOfflineActions = !!actions.length; @@ -248,8 +238,8 @@ export class AddonModDataHelperProvider { offlineActions[action.entryid].push(action); // We only display new entries in the first page when not searching. - if (action.action == 'add' && page == 0 && !search && !advSearch && - (!action.groupid || !groupId || action.groupid == groupId)) { + if (action.action == 'add' && options.page == 0 && !options.search && !options.advSearch && + (!action.groupid || !options.groupId || action.groupid == options.groupId)) { result.offlineEntries.push({ id: action.entryid, canmanageentry: true, @@ -275,16 +265,14 @@ export class AddonModDataHelperProvider { }); let fetchPromise: Promise; - if (search || advSearch) { - fetchPromise = this.dataProvider.searchEntries(data.id, groupId, search, advSearch, sort, order, page, perPage, - site.id).then((fetchResult) => { + if (options.search || options.advSearch) { + fetchPromise = this.dataProvider.searchEntries(data.id, options).then((fetchResult) => { result.entries = fetchResult.entries; result.totalcount = fetchResult.totalcount; result.maxcount = fetchResult.maxcount; }); } else { - fetchPromise = this.dataProvider.getEntries(data.id, groupId, sort, order, page, perPage, false, false, site.id) - .then((fetchResult) => { + fetchPromise = this.dataProvider.getEntries(data.id, options).then((fetchResult) => { result.entries = fetchResult.entries; result.totalcount = fetchResult.totalcount; }); @@ -324,7 +312,7 @@ export class AddonModDataHelperProvider { if (entryId > 0) { // Online entry. - promise = this.dataProvider.getEntry(data.id, entryId, false, site.id); + promise = this.dataProvider.getEntry(data.id, entryId, {cmId: data.coursemodule, siteId: site.id}); } else { // Offline entry or new entry. promise = Promise.resolve({ diff --git a/src/addon/mod/data/providers/prefetch-handler.ts b/src/addon/mod/data/providers/prefetch-handler.ts index ecdf8927a..4355a8fe6 100644 --- a/src/addon/mod/data/providers/prefetch-handler.ts +++ b/src/addon/mod/data/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreGroupsProvider } from '@providers/groups'; @@ -65,16 +65,17 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl * * @param dataId Database Id. * @param groups Array of groups in the activity. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. + * @param options Other options. * @return All unique entries. */ - protected getAllUniqueEntries(dataId: number, groups: any[], forceCache: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { + protected getAllUniqueEntries(dataId: number, groups: any[], options: CoreSitesCommonWSOptions = {}) + : Promise { + const promises = groups.map((group) => { - return this.dataProvider.fetchAllEntries(dataId, group.id, undefined, undefined, undefined, forceCache, ignoreCache, - siteId); + return this.dataProvider.fetchAllEntries(dataId, { + groupId: group.id, + ...options, // Include all options. + }); }); return Promise.all(promises).then((responses) => { @@ -96,31 +97,29 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl * @param module Module to get the files. * @param courseId Course ID the module belongs to. * @param omitFail True to always return even if fails. Default false. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. + * @param options Other options. * @return Promise resolved with the info fetched. */ - protected getDatabaseInfoHelper(module: any, courseId: number, omitFail: boolean = false, forceCache: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { + protected getDatabaseInfoHelper(module: any, courseId: number, omitFail: boolean, options: CoreSitesCommonWSOptions = {}) + : Promise { let database, groups = [], entries = [], files = []; - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.dataProvider.getDatabase(courseId, module.id, siteId, forceCache).then((data) => { + return this.dataProvider.getDatabase(courseId, module.id, options).then((data) => { files = this.getIntroFilesFromInstance(module, data); database = data; - return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId).then((groupInfo) => { + return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, options.siteId).then((groupInfo) => { if (!groupInfo.groups || groupInfo.groups.length == 0) { groupInfo.groups = [{id: 0}]; } groups = groupInfo.groups; - return this.getAllUniqueEntries(database.id, groups, forceCache, ignoreCache, siteId); + return this.getAllUniqueEntries(database.id, groups, options); }); }).then((uniqueEntries) => { entries = uniqueEntries; @@ -229,8 +228,10 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl * @return Promise resolved with true if downloadable, resolved with false otherwise. */ isDownloadable(module: any, courseId: number): boolean | Promise { - return this.dataProvider.getDatabase(courseId, module.id, undefined, true).then((database) => { - return this.dataProvider.getDatabaseAccessInformation(database.id).then((accessData) => { + return this.dataProvider.getDatabase(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((database) => { + return this.dataProvider.getDatabaseAccessInformation(database.id, {cmId: module.id}).then((accessData) => { // Check if database is restricted by time. if (!accessData.timeavailable) { const time = this.timeUtils.timestamp(); @@ -281,23 +282,31 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl * @return Promise resolved when done. */ protected prefetchDatabase(module: any, courseId: number, single: boolean, siteId: string): Promise { + const options = { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; - return this.getDatabaseInfoHelper(module, courseId, false, false, true, siteId).then((info) => { + return this.getDatabaseInfoHelper(module, courseId, false, options).then((info) => { // Prefetch the database data. const database = info.database, commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(), promises = []; - promises.push(this.dataProvider.getFields(database.id, false, true, siteId)); + promises.push(this.dataProvider.getFields(database.id, options)); promises.push(this.filepoolProvider.addFilesToQueue(siteId, info.files, this.component, module.id)); info.groups.forEach((group) => { - promises.push(this.dataProvider.getDatabaseAccessInformation(database.id, group.id, false, true, siteId)); + promises.push(this.dataProvider.getDatabaseAccessInformation(database.id, { + groupId: group.id, + ...options, // Include all options. + })); }); info.entries.forEach((entry) => { - promises.push(this.dataProvider.getEntry(database.id, entry.id, true, siteId)); + promises.push(this.dataProvider.getEntry(database.id, entry.id, options)); if (commentsEnabled && database.comments) { promises.push(this.commentsProvider.getComments('module', database.coursemodule, 'mod_data', entry.id, diff --git a/src/addon/mod/data/providers/sync.ts b/src/addon/mod/data/providers/sync.ts index 4cf76575d..dbc3792dd 100644 --- a/src/addon/mod/data/providers/sync.ts +++ b/src/addon/mod/data/providers/sync.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncBaseProvider } from '@classes/base-sync'; import { CoreAppProvider } from '@providers/app'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -188,7 +188,7 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { courseId = offlineActions[0].courseid; // Send the answers. - return this.dataProvider.getDatabaseById(courseId, dataId, siteId).then((database) => { + return this.dataProvider.getDatabaseById(courseId, dataId, {siteId}).then((database) => { data = database; const offlineEntries = {}; @@ -233,18 +233,23 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { * @return Promise resolved if success, rejected otherwise. */ protected syncEntry(data: any, entryActions: AddonModDataOfflineAction[], result: any, siteId?: string): Promise { - let discardError, - timePromise, - entryId = entryActions[0].entryid, - offlineId, - deleted = false; + let discardError; + let timePromise; + let entryId = entryActions[0].entryid; + let offlineId; + let deleted = false; const editAction = entryActions.find((action) => action.action == 'add' || action.action == 'edit'); const approveAction = entryActions.find((action) => action.action == 'approve' || action.action == 'disapprove'); const deleteAction = entryActions.find((action) => action.action == 'delete'); + const options = { + cmId: data.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; if (entryId > 0) { - timePromise = this.dataProvider.getEntry(data.id, entryId, true, siteId).then((entry) => { + timePromise = this.dataProvider.getEntry(data.id, entryId, options).then((entry) => { return entry.entry.timemodified; }).catch((error) => { if (error && this.utils.isWebServiceError(error)) { @@ -402,7 +407,7 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { const promises = []; results.forEach((result) => { - promises.push(this.dataProvider.getDatabase(result.itemSet.courseId, result.itemSet.instanceId, siteId) + promises.push(this.dataProvider.getDatabase(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) .then((data) => { const promises = []; diff --git a/src/addon/mod/feedback/components/index/index.ts b/src/addon/mod/feedback/components/index/index.ts index daa067482..0f58c27d7 100644 --- a/src/addon/mod/feedback/components/index/index.ts +++ b/src/addon/mod/feedback/components/index/index.ts @@ -184,7 +184,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity } }).then(() => { // Check if there are answers stored in offline. - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id); + return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, {cmId: this.module.id}); }).then((accessData) => { this.access = accessData; this.showTabs = (accessData.canviewreports || accessData.canviewanalysis) && !accessData.isempty; @@ -220,7 +220,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity const promises = []; if (accessData.cancomplete && accessData.cansubmit && accessData.isopen) { - promises.push(this.feedbackProvider.getResumePage(this.feedback.id).then((goPage) => { + promises.push(this.feedbackProvider.getResumePage(this.feedback.id, {cmId: this.module.id}).then((goPage) => { this.goPage = goPage > 0 ? goPage : false; })); } @@ -421,7 +421,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity setGroup(groupId: number): Promise { this.group = groupId; - return this.feedbackProvider.getAnalysis(this.feedback.id, groupId).then((analysis) => { + return this.feedbackProvider.getAnalysis(this.feedback.id, {groupId, cmId: this.module.id}).then((analysis) => { this.feedback.completedCount = analysis.completedcount; this.feedback.itemsCount = analysis.itemscount; diff --git a/src/addon/mod/feedback/pages/attempt/attempt.ts b/src/addon/mod/feedback/pages/attempt/attempt.ts index 4b17573e1..d6ba346e9 100644 --- a/src/addon/mod/feedback/pages/attempt/attempt.ts +++ b/src/addon/mod/feedback/pages/attempt/attempt.ts @@ -65,7 +65,7 @@ export class AddonModFeedbackAttemptPage { return this.feedbackProvider.getFeedbackById(this.courseId, this.feedbackId).then((feedback) => { this.feedback = feedback; - return this.feedbackProvider.getItems(this.feedbackId); + return this.feedbackProvider.getItems(this.feedbackId, {cmId: this.feedback.coursemodule}); }).then((items) => { // Add responses and format items. this.items = items.items.map((item) => { diff --git a/src/addon/mod/feedback/pages/form/form.ts b/src/addon/mod/feedback/pages/form/form.ts index 8e136401a..601b1a416 100644 --- a/src/addon/mod/feedback/pages/form/form.ts +++ b/src/addon/mod/feedback/pages/form/form.ts @@ -27,7 +27,7 @@ import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; /** * Page that displays feedback form. @@ -141,6 +141,10 @@ export class AddonModFeedbackFormPage implements OnDestroy { */ protected fetchData(): Promise { this.offline = !this.appProvider.isOnline(); + const options = { + cmId: this.module.id, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; return this.feedbackProvider.getFeedback(this.courseId, this.module.id).then((feedbackData) => { this.feedback = feedbackData; @@ -151,8 +155,7 @@ export class AddonModFeedbackFormPage implements OnDestroy { }).then((accessData) => { if (!this.preview && accessData.cansubmit && !accessData.isempty) { return typeof this.currentPage == 'undefined' ? - this.feedbackProvider.getResumePage(this.feedback.id, this.offline, true) : - Promise.resolve(this.currentPage); + this.feedbackProvider.getResumePage(this.feedback.id, options) : Promise.resolve(this.currentPage); } else { this.preview = true; @@ -162,8 +165,9 @@ export class AddonModFeedbackFormPage implements OnDestroy { if (!this.offline && !this.utils.isWebServiceError(error)) { // If it fails, go offline. this.offline = true; + options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - return this.feedbackProvider.getResumePage(this.feedback.id, true); + return this.feedbackProvider.getResumePage(this.feedback.id, options); } return Promise.reject(error); @@ -186,12 +190,18 @@ export class AddonModFeedbackFormPage implements OnDestroy { * @return Promise resolved when done. */ protected fetchAccessData(): Promise { - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, this.offline, true).catch((error) => { + const options = { + cmId: this.module.id, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; + + return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, options).catch((error) => { if (!this.offline && !this.utils.isWebServiceError(error)) { // If it fails, go offline. this.offline = true; + options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, true); + return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, options); } return Promise.reject(error); @@ -203,20 +213,25 @@ export class AddonModFeedbackFormPage implements OnDestroy { } protected fetchFeedbackPageData(page: number = 0): Promise { + const options = { + cmId: this.module.id, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; let promise; this.items = []; if (this.preview) { - promise = this.feedbackProvider.getItems(this.feedback.id); + promise = this.feedbackProvider.getItems(this.feedback.id, {cmId: this.module.id}); } else { this.currentPage = page; - promise = this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, this.offline, true).catch((error) => { + promise = this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, options).catch((error) => { if (!this.offline && !this.utils.isWebServiceError(error)) { // If it fails, go offline. this.offline = true; + options.readingStrategy = CoreSitesReadingStrategy.PreferCache; - return this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, true); + return this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, options); } return Promise.reject(error); @@ -262,8 +277,12 @@ export class AddonModFeedbackFormPage implements OnDestroy { return this.feedbackSync.syncFeedback(this.feedback.id).catch(() => { // Ignore errors. }).then(() => { - return this.feedbackProvider.processPage(this.feedback.id, this.currentPage, responses, goPrevious, formHasErrors, - this.courseId).then((response) => { + return this.feedbackProvider.processPage(this.feedback.id, this.currentPage, responses, { + goPrevious, + formHasErrors, + courseId: this.courseId, + cmId: this.module.id, + }).then((response) => { const jumpTo = parseInt(response.jumpto, 10); if (response.completed) { diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts index aab3cdac3..35425ea36 100644 --- a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts +++ b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.ts @@ -111,7 +111,11 @@ export class AddonModFeedbackNonRespondentsPage { this.feedbackLoaded = false; } - return this.feedbackHelper.getNonRespondents(this.feedbackId, this.selectedGroup, this.page).then((response) => { + return this.feedbackHelper.getNonRespondents(this.feedbackId, { + groupId: this.selectedGroup, + page: this.page, + cmId: this.moduleId, + }).then((response) => { this.total = response.total; if (this.users.length < response.total) { diff --git a/src/addon/mod/feedback/pages/respondents/respondents.ts b/src/addon/mod/feedback/pages/respondents/respondents.ts index e05ce6c70..ca21c2325 100644 --- a/src/addon/mod/feedback/pages/respondents/respondents.ts +++ b/src/addon/mod/feedback/pages/respondents/respondents.ts @@ -134,7 +134,11 @@ export class AddonModFeedbackRespondentsPage { this.feedbackLoaded = false; } - return this.feedbackHelper.getResponsesAnalysis(this.feedbackId, this.selectedGroup, this.page).then((responses) => { + return this.feedbackHelper.getResponsesAnalysis(this.feedbackId, { + groupId: this.selectedGroup, + page: this.page, + cmId: this.moduleId, + }).then((responses) => { this.responses.total = responses.totalattempts; this.anonResponses.total = responses.totalanonattempts; diff --git a/src/addon/mod/feedback/providers/feedback.ts b/src/addon/mod/feedback/providers/feedback.ts index cdb16569e..19e39fab9 100644 --- a/src/addon/mod/feedback/providers/feedback.ts +++ b/src/addon/mod/feedback/providers/feedback.ts @@ -14,13 +14,14 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreAppProvider } from '@providers/app'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModFeedbackOfflineProvider } from './offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for feedbacks. @@ -35,7 +36,7 @@ export class AddonModFeedbackProvider { static MULTICHOICE_HIDENOSELECT = 'h'; static MULTICHOICERATED_VALUE_SEP = '####'; - protected ROOT_CACHE_KEY = this.ROOT_CACHE_KEY + ''; + protected ROOT_CACHE_KEY = ''; protected logger; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, @@ -130,13 +131,11 @@ export class AddonModFeedbackProvider { * * @param feedbackId Feedback ID. * @param items Item to fill the value. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. + * @param options Other options. * @return Resolved with values when done. */ - protected fillValues(feedbackId: number, items: any[], offline: boolean, ignoreCache: boolean, siteId: string): Promise { - return this.getCurrentValues(feedbackId, offline, ignoreCache, siteId).then((valuesArray) => { + protected fillValues(feedbackId: number, items: any[], options: CoreCourseCommonModWSOptions = {}): Promise { + return this.getCurrentValues(feedbackId, options).then((valuesArray) => { const values = {}; valuesArray.forEach((value) => { @@ -152,7 +151,7 @@ export class AddonModFeedbackProvider { // Ignore errors. }).then(() => { // Merge with offline data. - return this.feedbackOffline.getFeedbackResponses(feedbackId, siteId).then((offlineValuesArray) => { + return this.feedbackOffline.getFeedbackResponses(feedbackId, options.siteId).then((offlineValuesArray) => { const offlineValues = {}; // Merge all values into one array. @@ -203,24 +202,22 @@ export class AddonModFeedbackProvider { * Returns all the feedback non respondents users. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @param previous Only for recurrent use. Object with the previous fetched info. * @return Promise resolved when the info is retrieved. */ - getAllNonRespondents(feedbackId: number, groupId: number, ignoreCache?: boolean, siteId?: string, previous?: any) - : Promise { + getAllNonRespondents(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (typeof previous == 'undefined') { - previous = { - page: 0, - users: [] - }; - } + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); + previous = previous || { + page: 0, + users: [] + }; - return this.getNonRespondents(feedbackId, groupId, previous.page, ignoreCache, siteId).then((response) => { + return this.getNonRespondents(feedbackId, { + page: previous.page, + ...options, // Include all options. + }).then((response) => { if (previous.users.length < response.total) { previous.users = previous.users.concat(response.users); } @@ -229,7 +226,7 @@ export class AddonModFeedbackProvider { // Can load more. previous.page++; - return this.getAllNonRespondents(feedbackId, groupId, ignoreCache, siteId, previous); + return this.getAllNonRespondents(feedbackId, options, previous); } previous.total = response.total; @@ -241,25 +238,23 @@ export class AddonModFeedbackProvider { * Returns all the feedback user responses. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @param previous Only for recurrent use. Object with the previous fetched info. * @return Promise resolved when the info is retrieved. */ - getAllResponsesAnalysis(feedbackId: number, groupId: number, ignoreCache?: boolean, siteId?: string, previous?: any) - : Promise { + getAllResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (typeof previous == 'undefined') { - previous = { - page: 0, - attempts: [], - anonattempts: [] - }; - } + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); + previous = previous || { + page: 0, + attempts: [], + anonattempts: [] + }; - return this.getResponsesAnalysis(feedbackId, groupId, previous.page, ignoreCache, siteId).then((responses) => { + return this.getResponsesAnalysis(feedbackId, { + page: previous.page, + ...options, // Include all options. + }).then((responses) => { if (previous.anonattempts.length < responses.totalanonattempts) { previous.anonattempts = previous.anonattempts.concat(responses.anonattempts); } @@ -272,7 +267,7 @@ export class AddonModFeedbackProvider { // Can load more. previous.page++; - return this.getAllResponsesAnalysis(feedbackId, groupId, ignoreCache, siteId, previous); + return this.getAllResponsesAnalysis(feedbackId, options, previous); } previous.totalattempts = responses.totalattempts; @@ -286,27 +281,23 @@ export class AddonModFeedbackProvider { * Get analysis information for a given feedback. * * @param feedbackId Feedback ID. - * @param groupId Group ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the feedback is retrieved. */ - getAnalysis(feedbackId: number, groupId?: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAnalysisDataCacheKey(feedbackId, groupId) - }; + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getAnalysisDataCacheKey(feedbackId, options.groupId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (groupId) { - params['groupid'] = groupId; - } - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; + if (options.groupId) { + params['groupid'] = options.groupId; } return site.read('mod_feedback_get_analysis', params, preSets); @@ -339,22 +330,23 @@ export class AddonModFeedbackProvider { * * @param feedbackId Feedback ID. * @param attemptId Attempt id to find. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @param previous Only for recurrent use. Object with the previous fetched info. * @return Promise resolved when the info is retrieved. */ - getAttempt(feedbackId: number, attemptId: number, ignoreCache?: boolean, siteId?: string, previous?: any): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - if (typeof previous == 'undefined') { - previous = { - page: 0, - attemptsLoaded: 0, - anonAttemptsLoaded: 0 - }; - } + getAttempt(feedbackId: number, attemptId: number, options: CoreCourseCommonModWSOptions = {}, previous?: any): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); + previous = previous || { + page: 0, + attemptsLoaded: 0, + anonAttemptsLoaded: 0 + }; - return this.getResponsesAnalysis(feedbackId, 0, previous.page, ignoreCache, siteId).then((responses) => { + return this.getResponsesAnalysis(feedbackId, { + page: previous.page, + groupId: 0, + ...options, // Include all options. + }).then((responses) => { let attempt; attempt = responses.attempts.find((attempt) => { @@ -385,7 +377,7 @@ export class AddonModFeedbackProvider { // Can load more. Check there. previous.page++; - return this.getAttempt(feedbackId, attemptId, ignoreCache, siteId, previous); + return this.getAttempt(feedbackId, attemptId, options, previous); } // Not found and all loaded. Reject. @@ -407,23 +399,20 @@ export class AddonModFeedbackProvider { * Returns the temporary completion timemodified for the current user. * * @param feedbackId Feedback ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getCurrentCompletedTimeModified(feedbackId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getCurrentCompletedTimeModified(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getCurrentCompletedTimeModifiedDataCacheKey(feedbackId) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getCurrentCompletedTimeModifiedDataCacheKey(feedbackId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_current_completed_tmp', params, preSets).then((response) => { if (response && typeof response.feedback != 'undefined' && typeof response.feedback.timemodified != 'undefined') { @@ -452,26 +441,20 @@ export class AddonModFeedbackProvider { * Returns the temporary responses or responses of the last submission for the current user. * * @param feedbackId Feedback ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getCurrentValues(feedbackId: number, offline: boolean = false, ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getCurrentValues(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets = { - cacheKey: this.getCurrentValuesDataCacheKey(feedbackId) - }; - - if (offline) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getCurrentValuesDataCacheKey(feedbackId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_unfinished_responses', params, preSets).then((response) => { if (!response || typeof response.responses == 'undefined') { @@ -508,27 +491,20 @@ export class AddonModFeedbackProvider { * Get access information for a given feedback. * * @param feedbackId Feedback ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the feedback is retrieved. */ - getFeedbackAccessInformation(feedbackId: number, offline: boolean = false, ignoreCache: boolean = false, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getFeedbackAccessInformation(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets = { - cacheKey: this.getFeedbackAccessInformationDataCacheKey(feedbackId) - }; - - if (offline) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getFeedbackAccessInformationDataCacheKey(feedbackId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_feedback_access_information', params, preSets); }); @@ -570,29 +546,22 @@ export class AddonModFeedbackProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the feedback is retrieved. */ - protected getFeedbackDataByKey(courseId: number, key: string, value: any, siteId?: string, forceCache?: boolean, - ignoreCache?: boolean): Promise { + protected getFeedbackDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getFeedbackCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getFeedbackCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModFeedbackProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_feedbacks_by_courses', params, preSets).then((response) => { if (response && response.feedbacks) { @@ -614,13 +583,11 @@ export class AddonModFeedbackProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the feedback is retrieved. */ - getFeedback(courseId: number, cmId: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise { - return this.getFeedbackDataByKey(courseId, 'coursemodule', cmId, siteId, forceCache, ignoreCache); + getFeedback(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getFeedbackDataByKey(courseId, 'coursemodule', cmId, options); } /** @@ -628,37 +595,32 @@ export class AddonModFeedbackProvider { * * @param courseId Course ID. * @param id Feedback ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param options Other options. * @return Promise resolved when the feedback is retrieved. */ - getFeedbackById(courseId: number, id: number, siteId?: string, forceCache?: boolean, ignoreCache?: boolean): Promise { - return this.getFeedbackDataByKey(courseId, 'id', id, siteId, forceCache, ignoreCache); + getFeedbackById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getFeedbackDataByKey(courseId, 'id', id, options); } /** * Returns the items (questions) in the given feedback. * * @param feedbackId Feedback ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getItems(feedbackId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getItems(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getItemsDataCacheKey(feedbackId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getItemsDataCacheKey(feedbackId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_items', params, preSets); }); @@ -678,29 +640,25 @@ export class AddonModFeedbackProvider { * Retrieves a list of students who didn't submit the feedback. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param page The page of records to return. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getNonRespondents(feedbackId: number, groupId: number = 0, page: number = 0, ignoreCache?: boolean, siteId?: string) - : Promise { + getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { + options.groupId = options.groupId || 0; + options.page = options.page || 0; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId, - groupid: groupId, - page: page - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getNonRespondentsDataCacheKey(feedbackId, groupId) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + feedbackid: feedbackId, + groupid: options.groupId, + page: options.page, + }; + const preSets = { + cacheKey: this.getNonRespondentsDataCacheKey(feedbackId, options.groupId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_non_respondents', params, preSets); }); @@ -751,25 +709,22 @@ export class AddonModFeedbackProvider { * * @param feedbackId Feedback ID. * @param page The page to get. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getPageItemsWithValues(feedbackId: number, page: number, offline: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getPageItemsWithValues(feedbackId: number, page: number, options: CoreCourseCommonModWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.getPageItems(feedbackId, page, siteId).then((response) => { - return this.fillValues(feedbackId, response.items, offline, ignoreCache, siteId).then((items) => { + return this.getPageItems(feedbackId, page, options.siteId).then((response) => { + return this.fillValues(feedbackId, response.items, options).then((items) => { response.items = items; return response; }); }).catch(() => { // If getPageItems fail we should calculate it using getItems. - return this.getItems(feedbackId, false, siteId).then((response) => { - return this.fillValues(feedbackId, response.items, offline, ignoreCache, siteId).then((items) => { + return this.getItems(feedbackId, options).then((response) => { + return this.fillValues(feedbackId, response.items, options).then((items) => { // Separate items by pages. let currentPage = 0; const previousPageItems = []; @@ -819,11 +774,17 @@ export class AddonModFeedbackProvider { * @param feedbackId Feedback ID. * @param page Page where we want to jump. * @param changePage If page change is forward (1) or backward (-1). - * @param siteId Site ID. + * @param options Other options. * @return Page number where to jump. Or false if completed or first page. */ - protected getPageJumpTo(feedbackId: number, page: number, changePage: number, siteId: string): Promise { - return this.getPageItemsWithValues(feedbackId, page, true, false, siteId).then((resp) => { + protected getPageJumpTo(feedbackId: number, page: number, changePage: number, options: {cmId?: number, siteId?: string}) + : Promise { + + return this.getPageItemsWithValues(feedbackId, page, { + cmId: options.cmId, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((resp) => { // The page we are going has items. if (resp.items.length > 0) { return page; @@ -831,7 +792,7 @@ export class AddonModFeedbackProvider { // Check we can jump futher. if ((changePage == 1 && resp.hasnextpage) || (changePage == -1 && resp.hasprevpage)) { - return this.getPageJumpTo(feedbackId, page + changePage, changePage, siteId); + return this.getPageJumpTo(feedbackId, page + changePage, changePage, options); } // Completed or first page. @@ -843,27 +804,25 @@ export class AddonModFeedbackProvider { * Returns the feedback user responses. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param page The page of records to return. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getResponsesAnalysis(feedbackId: number, groupId: number, page: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - feedbackid: feedbackId, - groupid: groupId || 0, - page: page || 0 - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getResponsesAnalysisDataCacheKey(feedbackId, groupId) - }; + getResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { + options.groupId = options.groupId || 0; + options.page = options.page || 0; - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + return this.sitesProvider.getSite(options.siteId).then((site) => { + const params = { + feedbackid: feedbackId, + groupid: options.groupId, + page: options.page, + }; + const preSets = { + cacheKey: this.getResponsesAnalysisDataCacheKey(feedbackId, options.groupId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_get_responses_analysis', params, preSets); }); @@ -894,26 +853,20 @@ export class AddonModFeedbackProvider { * Gets the resume page information. * * @param feedbackId Feedback ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getResumePage(feedbackId: number, offline: boolean = false, ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getResumePage(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets = { - cacheKey: this.getResumePageDataCacheKey(feedbackId) - }; - - if (offline) { - preSets['omitExpires'] = true; - } else if (ignoreCache) { - preSets['getFromCache'] = false; - preSets['emergencyCache'] = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getResumePageDataCacheKey(feedbackId), + component: AddonModFeedbackProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_feedback_launch_feedback', params, preSets).then((response) => { if (response && typeof response.gopage != 'undefined') { @@ -964,7 +917,7 @@ export class AddonModFeedbackProvider { /** * Invalidate the prefetched content. - * To invalidate files, use AddonFeedbackProvider#invalidateFiles. + * To invalidate files, use AddonModFeedbackProvider#invalidateFiles. * * @param moduleId The module ID. * @param courseId Course ID of the module. @@ -976,7 +929,7 @@ export class AddonModFeedbackProvider { const promises = []; - promises.push(this.getFeedback(courseId, moduleId, siteId).then((feedback) => { + promises.push(this.getFeedback(courseId, moduleId, {siteId}).then((feedback) => { const ps = []; // Do not invalidate module data before getting module info, we need it! @@ -1086,23 +1039,20 @@ export class AddonModFeedbackProvider { * Returns if feedback has been completed * * @param feedbackId Feedback ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - isCompleted(feedbackId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + isCompleted(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - feedbackid: feedbackId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getCompletedDataCacheKey(feedbackId) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + feedbackid: feedbackId, + }; + const preSets = { + cacheKey: this.getCompletedDataCacheKey(feedbackId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModFeedbackProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return this.utils.promiseWorks(site.read('mod_feedback_get_last_completed', params, preSets)); }); @@ -1147,19 +1097,15 @@ export class AddonModFeedbackProvider { * @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 goPrevious Whether we want to jump to previous page. - * @param formHasErrors Whether the form we sent has required but empty fields (only used in offline). - * @param courseId Course ID the feedback belongs to. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - processPage(feedbackId: number, page: number, responses: any, goPrevious: boolean, formHasErrors: boolean, courseId: number, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + processPage(feedbackId: number, page: number, responses: any, options: AddonModFeedbackProcessPageOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); // Convenience function to store a message to be synchronized later. const storeOffline = (): Promise => { - return this.feedbackOffline.saveResponses(feedbackId, page, responses, courseId, siteId).then(() => { + return this.feedbackOffline.saveResponses(feedbackId, page, responses, options.courseId, options.siteId).then(() => { // Simulate process_page response. const response = { jumpto: page, @@ -1168,11 +1114,11 @@ export class AddonModFeedbackProvider { }; let changePage = 0; - if (goPrevious) { + if (options.goPrevious) { if (page > 0) { changePage = -1; } - } else if (!formHasErrors) { + } else if (!options.formHasErrors) { // We can only go next if it has no errors. changePage = 1; } @@ -1181,7 +1127,11 @@ export class AddonModFeedbackProvider { return response; } - return this.getPageItemsWithValues(feedbackId, page, true, false, siteId).then((resp) => { + return this.getPageItemsWithValues(feedbackId, page, { + cmId: options.cmId, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((resp) => { // Check completion. if (changePage == 1 && !resp.hasnextpage) { response.completed = true; @@ -1189,7 +1139,7 @@ export class AddonModFeedbackProvider { return response; } - return this.getPageJumpTo(feedbackId, page + changePage, changePage, siteId).then((loadPage) => { + return this.getPageJumpTo(feedbackId, page + changePage, changePage, options).then((loadPage) => { if (loadPage === false) { // Completed or first page. if (changePage == -1) { @@ -1215,8 +1165,8 @@ export class AddonModFeedbackProvider { } // If there's already a response to be sent to the server, discard it first. - return this.feedbackOffline.deleteFeedbackPageResponses(feedbackId, page, siteId).then(() => { - return this.processPageOnline(feedbackId, page, responses, goPrevious, siteId).catch((error) => { + return this.feedbackOffline.deleteFeedbackPageResponses(feedbackId, page, options.siteId).then(() => { + return this.processPageOnline(feedbackId, page, responses, options.goPrevious, options.siteId).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -1252,7 +1202,7 @@ export class AddonModFeedbackProvider { }).then((response) => { // Invalidate and update current values because they will change. return this.invalidateCurrentValuesData(feedbackId, site.getId()).then(() => { - return this.getCurrentValues(feedbackId, false, false, site.getId()); + return this.getCurrentValues(feedbackId, {siteId: site.getId()}); }).catch(() => { // Ignore errors. }).then(() => { @@ -1262,3 +1212,28 @@ export class AddonModFeedbackProvider { }); } } + +/** + * Common options with a group ID. + */ +export type AddonModFeedbackGroupOptions = CoreCourseCommonModWSOptions & { + groupId?: number; // Group id, 0 means that the function will determine the user group. Defaults to 0. +}; + +/** + * Common options with a group ID and page. + */ +export type AddonModFeedbackGroupPaginatedOptions = AddonModFeedbackGroupOptions & { + page?: number; // The page of records to return. The page of records to return. +}; + +/** + * Common options with a group ID and page. + */ +export type AddonModFeedbackProcessPageOptions = { + goPrevious?: boolean; // Whether we want to jump to previous page. + formHasErrors?: boolean; // Whether the form we sent has required but empty fields (only used in offline). + cmId?: number; // Module ID. + courseId?: number; // Course ID the feedback belongs to. + siteId?: string; // Site ID. If not defined, current site.; +}; diff --git a/src/addon/mod/feedback/providers/helper.ts b/src/addon/mod/feedback/providers/helper.ts index 2639cb5c6..f136c2456 100644 --- a/src/addon/mod/feedback/providers/helper.ts +++ b/src/addon/mod/feedback/providers/helper.ts @@ -14,11 +14,11 @@ import { Injectable } from '@angular/core'; import { NavController, ViewController } from 'ionic-angular'; -import { AddonModFeedbackProvider } from './feedback'; +import { AddonModFeedbackProvider, AddonModFeedbackGroupPaginatedOptions } from './feedback'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -86,12 +86,11 @@ export class AddonModFeedbackHelperProvider { * Retrieves a list of students who didn't submit the feedback with extra info. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param page The page of records to return. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getNonRespondents(feedbackId: number, groupId: number, page: number): Promise { - return this.feedbackProvider.getNonRespondents(feedbackId, groupId, page).then((responses) => { + getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { + return this.feedbackProvider.getNonRespondents(feedbackId, options).then((responses) => { return this.addImageProfileToAttempts(responses.users).then((users) => { responses.users = users; @@ -186,12 +185,11 @@ export class AddonModFeedbackHelperProvider { * Returns the feedback user responses with extra info. * * @param feedbackId Feedback ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param page The page of records to return. + * @param options Other options. * @return Promise resolved when the info is retrieved. */ - getResponsesAnalysis(feedbackId: number, groupId: number, page: number): Promise { - return this.feedbackProvider.getResponsesAnalysis(feedbackId, groupId, page).then((responses) => { + getResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise { + return this.feedbackProvider.getResponsesAnalysis(feedbackId, options).then((responses) => { return this.addImageProfileToAttempts(responses.attempts).then((attempts) => { responses.attempts = attempts; @@ -227,7 +225,11 @@ export class AddonModFeedbackHelperProvider { return this.linkHelper.goInSite(navCtrl, 'AddonModFeedbackRespondentsPage', stateParams, siteId); } - return this.feedbackProvider.getAttempt(module.instance, params.showcompleted, true, siteId).then((attempt) => { + return this.feedbackProvider.getAttempt(module.instance, params.showcompleted, { + cmId: moduleId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((attempt) => { stateParams = { moduleId: module.id, attempt: attempt, diff --git a/src/addon/mod/feedback/providers/prefetch-handler.ts b/src/addon/mod/feedback/providers/prefetch-handler.ts index 0c0562638..375e6a049 100644 --- a/src/addon/mod/feedback/providers/prefetch-handler.ts +++ b/src/addon/mod/feedback/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -143,7 +143,9 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH * @return Promise resolved with true if downloadable, resolved with false otherwise. */ isDownloadable(module: any, courseId: number): boolean | Promise { - return this.feedbackProvider.getFeedback(courseId, module.id, undefined, true).then((feedback) => { + return this.feedbackProvider.getFeedback(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((feedback) => { const now = this.timeUtils.timestamp(); // Check time first if available. @@ -154,7 +156,7 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH return false; } - return this.feedbackProvider.getFeedbackAccessInformation(feedback.id).then((accessData) => { + return this.feedbackProvider.getFeedbackAccessInformation(feedback.id, {cmId: module.id}).then((accessData) => { return accessData.isopen; }); }); @@ -192,15 +194,24 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH * @return Promise resolved when done. */ protected prefetchFeedback(module: any, courseId: number, single: boolean, siteId: string): Promise { + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + // Prefetch the feedback data. - return this.feedbackProvider.getFeedback(courseId, module.id, siteId, false, true).then((feedback) => { + return this.feedbackProvider.getFeedback(courseId, module.id, commonOptions).then((feedback) => { let files = (feedback.pageaftersubmitfiles || []).concat(this.getIntroFilesFromInstance(module, feedback)); - return this.feedbackProvider.getFeedbackAccessInformation(feedback.id, false, true, siteId).then((accessData) => { + return this.feedbackProvider.getFeedbackAccessInformation(feedback.id, modOptions).then((accessData) => { const p2 = []; if (accessData.canedititems || accessData.canviewreports) { // Get all groups analysis. - p2.push(this.feedbackProvider.getAnalysis(feedback.id, undefined, true, siteId)); + p2.push(this.feedbackProvider.getAnalysis(feedback.id, modOptions)); p2.push(this.groupsProvider.getActivityGroupInfo(feedback.coursemodule, true, undefined, siteId, true) .then((groupInfo) => { const p3 = []; @@ -209,11 +220,16 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH groupInfo.groups = [{id: 0}]; } groupInfo.groups.forEach((group) => { - p3.push(this.feedbackProvider.getAnalysis(feedback.id, group.id, true, siteId)); - p3.push(this.feedbackProvider.getAllResponsesAnalysis(feedback.id, group.id, true, siteId)); + const groupOptions = { + groupId: group.id, + ...modOptions, // Include all mod options. + }; + + p3.push(this.feedbackProvider.getAnalysis(feedback.id, groupOptions)); + p3.push(this.feedbackProvider.getAllResponsesAnalysis(feedback.id, groupOptions)); if (!accessData.isanonymous) { - p3.push(this.feedbackProvider.getAllNonRespondents(feedback.id, group.id, true, siteId)); + p3.push(this.feedbackProvider.getAllNonRespondents(feedback.id, groupOptions)); } }); @@ -221,7 +237,7 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH })); } - p2.push(this.feedbackProvider.getItems(feedback.id, true, siteId).then((response) => { + p2.push(this.feedbackProvider.getItems(feedback.id, commonOptions).then((response) => { response.items.forEach((item) => { files = files.concat(item.itemfiles); }); @@ -234,8 +250,8 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH p2.push(this.feedbackProvider.processPageOnline(feedback.id, 0, {}, undefined, siteId).finally(() => { const p4 = []; - p4.push(this.feedbackProvider.getCurrentValues(feedback.id, false, true, siteId)); - p4.push(this.feedbackProvider.getResumePage(feedback.id, false, true, siteId)); + p4.push(this.feedbackProvider.getCurrentValues(feedback.id, modOptions)); + p4.push(this.feedbackProvider.getResumePage(feedback.id, modOptions)); return Promise.all(p4); })); diff --git a/src/addon/mod/feedback/providers/sync.ts b/src/addon/mod/feedback/providers/sync.ts index f3643f219..f4716e7b5 100644 --- a/src/addon/mod/feedback/providers/sync.ts +++ b/src/addon/mod/feedback/providers/sync.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreAppProvider } from '@providers/app'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -192,12 +192,12 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv courseId = responses[0].courseid; - return this.feedbackProvider.getFeedbackById(courseId, feedbackId, siteId).then((feedbackData) => { + return this.feedbackProvider.getFeedbackById(courseId, feedbackId, {siteId}).then((feedbackData) => { feedback = feedbackData; if (!feedback.multiple_submit) { // If it does not admit multiple submits, check if it is completed to know if we can submit. - return this.feedbackProvider.isCompleted(feedbackId); + return this.feedbackProvider.isCompleted(feedbackId, {cmId: feedback.coursemodule, siteId}); } else { return false; } @@ -220,7 +220,10 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv return Promise.all(promises); } - return this.feedbackProvider.getCurrentCompletedTimeModified(feedbackId, true, siteId).then((timemodified) => { + return this.feedbackProvider.getCurrentCompletedTimeModified(feedbackId, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((timemodified) => { // Sort by page. responses.sort((a, b) => { return a.page - b.page; diff --git a/src/addon/mod/folder/providers/folder.ts b/src/addon/mod/folder/providers/folder.ts index 1f1281e57..a75520c16 100644 --- a/src/addon/mod/folder/providers/folder.ts +++ b/src/addon/mod/folder/providers/folder.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; @@ -41,11 +41,11 @@ export class AddonModFolderProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the book is retrieved. */ - getFolder(courseId: number, cmId: number, siteId?: string): Promise { - return this.getFolderByKey(courseId, 'coursemodule', cmId, siteId); + getFolder(courseId: number, cmId: number, options?: CoreSitesCommonWSOptions): Promise { + return this.getFolderByKey(courseId, 'coursemodule', cmId, options); } /** @@ -54,18 +54,21 @@ export class AddonModFolderProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the book is retrieved. */ - protected getFolderByKey(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getFolderByKey(courseId: number, key: string, value: any, options?: CoreSitesCommonWSOptions) + : Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getFolderCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getFolderCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModFolderProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_folder_get_folders_by_courses', params, preSets) .then((response: AddonModFolderGetFoldersByCoursesResult): any => { diff --git a/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts b/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts index 142c25706..754ecff79 100644 --- a/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts +++ b/src/addon/mod/forum/components/discussion-options-menu/discussion-options-menu.ts @@ -49,7 +49,7 @@ export class AddonForumDiscussionOptionsMenuComponent implements OnInit { ngOnInit(): void { if (this.forumProvider.isSetPinStateAvailableForSite()) { // Use the canAddDiscussion WS to check if the user can pin discussions. - this.forumProvider.canAddDiscussionToAll(this.forumId).then((response) => { + this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { this.canPin = !!response.canpindiscussions; }).catch(() => { this.canPin = false; diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts index 5c3147867..9642aaddf 100644 --- a/src/addon/mod/forum/components/index/index.ts +++ b/src/addon/mod/forum/components/index/index.ts @@ -250,7 +250,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom promises.push(this.groupsProvider.getActivityGroupMode(this.forum.cmid).then((mode) => { this.usesGroups = (mode === CoreGroupsProvider.SEPARATEGROUPS || mode === CoreGroupsProvider.VISIBLEGROUPS); })); - promises.push(this.forumProvider.getAccessInformation(this.forum.id).then((accessInfo) => { + promises.push(this.forumProvider.getAccessInformation(this.forum.id, {cmId: this.module.id}).then((accessInfo) => { // Disallow adding discussions if cut-off date is reached and the user has not the capability to override it. // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now. const cutoffDateReached = this.forumHelper.isCutoffDateReached(this.forum) && !accessInfo.cancanoverridecutoff; @@ -259,7 +259,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom if (this.forumProvider.isSetPinStateAvailableForSite()) { // Use the canAddDiscussion WS to check if the user can pin discussions. - promises.push(this.forumProvider.canAddDiscussionToAll(this.forum.id).then((response) => { + promises.push(this.forumProvider.canAddDiscussionToAll(this.forum.id, {cmId: this.module.id}).then((response) => { this.canPin = !!response.canpindiscussions; }).catch(() => { this.canPin = false; @@ -354,8 +354,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom this.page = 0; } - return this.forumProvider.getDiscussions(this.forum.id, this.forum.cmid, - this.selectedSortOrder.value, this.page).then((response) => { + return this.forumProvider.getDiscussions(this.forum.id, { + cmId: this.forum.cmid, + sortOrder: this.selectedSortOrder.value, + page: this.page, + }).then((response) => { let promise; if (this.usesGroups) { promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions); diff --git a/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts b/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts index 3479d7154..7ddaf862f 100644 --- a/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts +++ b/src/addon/mod/forum/components/post-options-menu/post-options-menu.ts @@ -15,7 +15,7 @@ import { Component, OnInit } from '@angular/core'; import { NavParams, ViewController } from 'ionic-angular'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSite } from '@classes/site'; import { AddonModForumProvider } from '../../providers/forum'; @@ -35,6 +35,8 @@ export class AddonForumPostOptionsMenuComponent implements OnInit { loaded = false; url: string; + protected cmId: number; + constructor(navParams: NavParams, protected viewCtrl: ViewController, protected domUtils: CoreDomUtilsProvider, @@ -42,6 +44,7 @@ export class AddonForumPostOptionsMenuComponent implements OnInit { protected sitesProvider: CoreSitesProvider) { this.post = navParams.get('post'); this.forumId = navParams.get('forumId'); + this.cmId = navParams.get('cmId'); } /** @@ -64,7 +67,10 @@ export class AddonForumPostOptionsMenuComponent implements OnInit { if (this.forumId) { try { this.post = - await this.forumProvider.getDiscussionPost(this.forumId, this.post.discussionid, this.post.id, true); + await this.forumProvider.getDiscussionPost(this.forumId, this.post.discussionid, this.post.id, { + cmId: this.cmId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }); } catch (error) { this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.'); } diff --git a/src/addon/mod/forum/components/post/post.ts b/src/addon/mod/forum/components/post/post.ts index b55449ef5..48d0fc8e4 100644 --- a/src/addon/mod/forum/components/post/post.ts +++ b/src/addon/mod/forum/components/post/post.ts @@ -193,7 +193,8 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges const popover = this.popoverCtrl.create(AddonForumPostOptionsMenuComponent, { post: this.post, - forumId: this.forum.id + forumId: this.forum.id, + cmId: this.forum.cmid, }); popover.onDidDismiss((data) => { if (data && data.action) { diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts index 65fac0147..cee6fa087 100644 --- a/src/addon/mod/forum/pages/discussion/discussion.ts +++ b/src/addon/mod/forum/pages/discussion/discussion.ts @@ -329,7 +329,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { let ratingInfo; return syncPromise.then(() => { - return this.forumProvider.getDiscussionPosts(this.discussionId, this.cmId).then((response) => { + return this.forumProvider.getDiscussionPosts(this.discussionId, {cmId: this.cmId}).then((response) => { onlinePosts = response.posts; ratingInfo = response.ratinginfo; this.courseId = response.courseid || this.courseId; @@ -403,7 +403,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { const promises = []; - promises.push(this.forumProvider.getAccessInformation(this.forumId).then((accessInfo) => { + promises.push(this.forumProvider.getAccessInformation(this.forumId, {cmId: this.cmId}).then((accessInfo) => { this.accessInfo = accessInfo; // Disallow replying if cut-off date is reached and the user has not the capability to override it. @@ -448,7 +448,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { }).then(() => { if (this.forumProvider.isSetPinStateAvailableForSite()) { // Use the canAddDiscussion WS to check if the user can pin discussions. - return this.forumProvider.canAddDiscussionToAll(this.forumId).then((response) => { + return this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { this.canPin = !!response.canpindiscussions; }).catch(() => { this.canPin = false; diff --git a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts b/src/addon/mod/forum/pages/new-discussion/new-discussion.ts index b4aea7323..bde109ba0 100644 --- a/src/addon/mod/forum/pages/new-discussion/new-discussion.ts +++ b/src/addon/mod/forum/pages/new-discussion/new-discussion.ts @@ -176,7 +176,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy { this.newDiscussion.postToAllGroups = false; // Use the canAddDiscussion WS to check if the user can add attachments and pin discussions. - promises.push(this.forumProvider.canAddDiscussionToAll(this.forumId).then((response) => { + promises.push(this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { this.canPin = !!response.canpindiscussions; this.canCreateAttachments = !!response.cancreateattachment; }).catch(() => { @@ -190,7 +190,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy { })); // Get access information. - promises.push(this.forumProvider.getAccessInformation(this.forumId).then((accessInfo) => { + promises.push(this.forumProvider.getAccessInformation(this.forumId, {cmId: this.cmId}).then((accessInfo) => { this.accessInfo = accessInfo; })); @@ -265,7 +265,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy { */ protected validateVisibleGroups(forumGroups: any[]): Promise { // We first check if the user can post to all the groups. - return this.forumProvider.canAddDiscussionToAll(this.forumId).catch(() => { + return this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).catch(() => { // The call failed, let's assume he can't. return { status: false, @@ -285,7 +285,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy { const filtered = []; forumGroups.forEach((group) => { - promises.push(this.forumProvider.canAddDiscussion(this.forumId, group.id).catch(() => { + promises.push(this.forumProvider.canAddDiscussion(this.forumId, group.id, {cmId: this.cmId}).catch(() => { /* The call failed, let's return true so the group is shown. If the user can't post to it an error will be shown when he tries to add the discussion. */ return { @@ -342,7 +342,7 @@ export class AddonModForumNewDiscussionPage implements OnDestroy { if (check) { // We need to check if the user can add a discussion to all participants. - promise = this.forumProvider.canAddDiscussionToAll(this.forumId).then((response) => { + promise = this.forumProvider.canAddDiscussionToAll(this.forumId, {cmId: this.cmId}).then((response) => { this.canPin = !!response.canpindiscussions; this.canCreateAttachments = !!response.cancreateattachment; diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts index 1ac3fdaf7..cdb8b6b5b 100644 --- a/src/addon/mod/forum/providers/forum.ts +++ b/src/addon/mod/forum/providers/forum.ts @@ -14,16 +14,17 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreGroupsProvider } from '@providers/groups'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModForumOfflineProvider } from './offline'; import { CoreRatingInfo } from '@core/rating/providers/rating'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for forums. @@ -206,26 +207,29 @@ export class AddonModForumProvider { * * @param forumId Forum ID. * @param groupId Group ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with an object with the following properties: * - status (boolean) * - canpindiscussions (boolean) * - cancreateattachment (boolean) */ - canAddDiscussion(forumId: number, groupId: number, siteId?: string): Promise { + canAddDiscussion(forumId: number, groupId: number, options: CoreCourseCommonModWSOptions = {}): Promise { const params = { forumid: forumId, - groupid: groupId + groupid: groupId, }; const preSets = { - cacheKey: this.getCanAddDiscussionCacheKey(forumId, groupId) + cacheKey: this.getCanAddDiscussionCacheKey(forumId, groupId), + component: AddonModForumProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { return site.read('mod_forum_can_add_discussion', params, preSets).then((result) => { if (result) { if (typeof result.canpindiscussions == 'undefined') { - // WS doesn't support it yet, default it to false to prevent students from seing the option. + // WS doesn't support it yet, default it to false to prevent students from seeing the option. result.canpindiscussions = false; } if (typeof result.cancreateattachment == 'undefined') { @@ -245,14 +249,14 @@ export class AddonModForumProvider { * Check if a user can post to all groups. * * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with an object with the following properties: * - status (boolean) * - canpindiscussions (boolean) * - cancreateattachment (boolean) */ - canAddDiscussionToAll(forumId: number, siteId?: string): Promise { - return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, siteId); + canAddDiscussionToAll(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, options); } /** @@ -382,17 +386,19 @@ export class AddonModForumProvider { * Get all course forums. * * @param courseId Course ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the forums are retrieved. */ - getCourseForums(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getCourseForums(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] + courseids: [courseId], }; const preSets = { cacheKey: this.getForumDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModForumProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_forum_get_forums_by_courses', params, preSets); @@ -405,24 +411,23 @@ export class AddonModForumProvider { * @param forumId Forum ID. * @param discussionId Discussion ID. * @param postId Post ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the post is retrieved. */ - getDiscussionPost(forumId: number, discussionId: number, postId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - const params = { - postid: postId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId), - updateFrequency: CoreSite.FREQUENCY_USUALLY - }; + getDiscussionPost(forumId: number, discussionId: number, postId: number, options: CoreCourseCommonModWSOptions = {}) + : Promise { - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + return this.sitesProvider.getSite(options.siteId).then((site) => { + const params = { + postid: postId, + }; + const preSets = { + cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId), + updateFrequency: CoreSite.FREQUENCY_USUALLY, + component: AddonModForumProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_forum_get_discussion_post', params, preSets).then((response) => { if (response.post) { @@ -439,11 +444,11 @@ export class AddonModForumProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the forum is retrieved. */ - getForum(courseId: number, cmId: number, siteId?: string): Promise { - return this.getCourseForums(courseId, siteId).then((forums) => { + getForum(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getCourseForums(courseId, options).then((forums) => { const forum = forums.find((forum) => forum.cmid == cmId); if (forum) { return forum; @@ -458,11 +463,11 @@ export class AddonModForumProvider { * * @param courseId Course ID. * @param forumId Forum ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the forum is retrieved. */ - getForumById(courseId: number, forumId: number, siteId?: string): Promise { - return this.getCourseForums(courseId, siteId).then((forums) => { + getForumById(courseId: number, forumId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getCourseForums(courseId, options).then((forums) => { const forum = forums.find((forum) => forum.id == forumId); if (forum) { return forum; @@ -476,24 +481,25 @@ export class AddonModForumProvider { * Get access information for a given forum. * * @param forumId Forum ID. - * @param forceCache True to always get the value from cache. false otherwise. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Object with access information. * @since 3.7 */ - getAccessInformation(forumId: number, forceCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAccessInformation(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { if (!site.wsAvailable('mod_forum_get_forum_access_information')) { // Access information not available for 3.6 or older sites. return Promise.resolve({}); } const params = { - forumid: forumId + forumid: forumId, }; const preSets = { cacheKey: this.getAccessInformationCacheKey(forumId), - omitExpires: forceCache + component: AddonModForumProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_forum_get_forum_access_information', params, preSets); @@ -504,11 +510,10 @@ export class AddonModForumProvider { * Get forum discussion posts. * * @param discussionId Discussion ID. - * @param cmId Forum cmid. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with forum posts and rating info. */ - getDiscussionPosts(discussionId: number, cmId: number, siteId?: string): Promise<{posts: any[], courseid?: number, + getDiscussionPosts(discussionId: number, options: CoreCourseCommonModWSOptions = {}): Promise<{posts: any[], courseid?: number, forumid?: number, ratinginfo?: CoreRatingInfo}> { // Convenience function to translate legacy data to new format. @@ -546,15 +551,16 @@ export class AddonModForumProvider { }; const params = { - discussionid: discussionId + discussionid: discussionId, }; const preSets = { cacheKey: this.getDiscussionPostsCacheKey(discussionId), component: AddonModForumProvider.COMPONENT, - componentId: cmId + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const wsName = this.isGetDiscussionPostsAvailable(site) ? 'mod_forum_get_discussion_posts' : 'mod_forum_get_forum_discussion_posts'; @@ -650,34 +656,30 @@ export class AddonModForumProvider { * Get forum discussions. * * @param forumId Forum ID. - * @param cmId Forum cmid - * @param sortOrder Sort order. - * @param page Page. - * @param forceCache True to always get the value from cache. false otherwise. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with an object with: * - discussions: List of discussions. Note that for every discussion in the list discussion.id is the main post ID but * discussion ID is discussion.discussion. * - canLoadMore: True if there may be more discussions to load. */ - getDiscussions(forumId: number, cmId: number, sortOrder?: number, page: number = 0, - forceCache?: boolean, siteId?: string): Promise { - sortOrder = sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC; + getDiscussions(forumId: number, options: AddonModForumGetDiscussionsOptions = {}): Promise { + options.sortOrder = options.sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC; + options.page = options.page || 0; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { let method = 'mod_forum_get_forum_discussions_paginated'; const params: any = { forumid: forumId, - page: page, - perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE + page: options.page, + perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE, }; if (site.wsAvailable('mod_forum_get_forum_discussions')) { // Since Moodle 3.7. method = 'mod_forum_get_forum_discussions'; - params.sortorder = sortOrder; + params.sortorder = options.sortOrder; } else { - if (sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { + if (options.sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { params.sortby = 'timemodified'; params.sortdirection = 'DESC'; } else { @@ -685,31 +687,27 @@ export class AddonModForumProvider { return Promise.reject(null); } } - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder), + + const preSets = { + cacheKey: this.getDiscussionsListCacheKey(forumId, options.sortOrder), component: AddonModForumProvider.COMPONENT, - componentId: cmId + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (forceCache) { - preSets.omitExpires = true; - } return site.read(method, params, preSets).catch((error) => { // Try to get the data from cache stored with the old WS method. if (!this.appProvider.isOnline() && method == 'mod_forum_get_forum_discussion' && - sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { + options.sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) { const params = { forumid: forumId, - page: page, + page: options.page, perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE, sortby: 'timemodified', sortdirection: 'DESC' }; - const preSets: CoreSiteWSPreSets = { - cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder), - omitExpires: true - }; + Object.assign(preSets, this.sitesProvider.getReadingStrategyPreSets(CoreSitesReadingStrategy.PreferCache)); return site.read('mod_forum_get_forum_discussions_paginated', params, preSets); } @@ -745,17 +743,14 @@ export class AddonModForumProvider { * - discussions: List of discussions. * - error: True if an error occurred, false otherwise. */ - getDiscussionsInPages(forumId: number, cmId: number, sortOrder?: number, forceCache?: boolean, - numPages?: number, startPage?: number, siteId?: string): Promise { - if (typeof numPages == 'undefined') { - numPages = -1; - } - startPage = startPage || 0; + getDiscussionsInPages(forumId: number, options: AddonModForumGetDiscussionsInPagesOptions = {}): Promise { + options.page = options.page || 0; const result = { discussions: [], error: false }; + let numPages = typeof options.numPages == 'undefined' ? -1 : options.numPages; if (!numPages) { return Promise.resolve(result); @@ -763,7 +758,7 @@ export class AddonModForumProvider { const getPage = (page: number): Promise => { // Get page discussions. - return this.getDiscussions(forumId, cmId, sortOrder, page, forceCache, siteId).then((response) => { + return this.getDiscussions(forumId, options).then((response) => { result.discussions = result.discussions.concat(response.discussions); numPages--; @@ -780,7 +775,7 @@ export class AddonModForumProvider { }); }; - return getPage(startPage); + return getPage(options.page); } /** @@ -816,7 +811,11 @@ export class AddonModForumProvider { this.getAvailableSortOrders().forEach((sortOrder) => { // We need to get the list of discussions to be able to invalidate their posts. - promises.push(this.getDiscussionsInPages(forum.id, forum.cmid, sortOrder.value, true).then((response) => { + promises.push(this.getDiscussionsInPages(forum.id, { + cmId: forum.cmid, + sortOrder: sortOrder.value, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((response) => { // Now invalidate the WS calls. const promises = []; @@ -1164,3 +1163,18 @@ export class AddonModForumProvider { }); } } + +/** + * Options to pass to get discussions. + */ +export type AddonModForumGetDiscussionsOptions = CoreCourseCommonModWSOptions & { + sortOrder?: number; // Sort order. + page?: number; // Page. Defaults to 0. +}; + +/** + * Options to pass to get discussions in pages. + */ +export type AddonModForumGetDiscussionsInPagesOptions = AddonModForumGetDiscussionsOptions & { + numPages?: number; // Number of pages to get. If not defined, all pages. +}; diff --git a/src/addon/mod/forum/providers/helper.ts b/src/addon/mod/forum/providers/helper.ts index 7b3db2efa..a3d72dccd 100644 --- a/src/addon/mod/forum/providers/helper.ts +++ b/src/addon/mod/forum/providers/helper.ts @@ -279,7 +279,11 @@ export class AddonModForumHelperProvider { siteId = siteId || this.sitesProvider.getCurrentSiteId(); const findDiscussion = (page: number): Promise => { - return this.forumProvider.getDiscussions(forumId, cmId, undefined, page, false, siteId).then((response) => { + return this.forumProvider.getDiscussions(forumId, { + cmId, + page, + siteId, + }).then((response) => { if (response.discussions && response.discussions.length > 0) { // Note that discussion.id is the main post ID but discussion ID is discussion.discussion. const discussion = response.discussions.find((discussion) => discussion.discussion == discussionId); diff --git a/src/addon/mod/forum/providers/module-handler.ts b/src/addon/mod/forum/providers/module-handler.ts index 67839c7bc..f79aa5a75 100644 --- a/src/addon/mod/forum/providers/module-handler.ts +++ b/src/addon/mod/forum/providers/module-handler.ts @@ -143,7 +143,7 @@ export class AddonModForumModuleHandler implements CoreCourseModuleHandler { this.forumProvider.invalidateForumData(courseId).finally(() => { // Handle unread posts. - this.forumProvider.getForum(courseId, moduleId, siteId).then((forumData) => { + this.forumProvider.getForum(courseId, moduleId, {siteId}).then((forumData) => { data.extraBadgeColor = ''; data.extraBadge = forumData.unreadpostscount ? this.translate.instant('addon.mod_forum.unreadpostsnumber', {$a : forumData.unreadpostscount }) : ''; diff --git a/src/addon/mod/forum/providers/prefetch-handler.ts b/src/addon/mod/forum/providers/prefetch-handler.ts index 5a75af10a..fd09775d1 100644 --- a/src/addon/mod/forum/providers/prefetch-handler.ts +++ b/src/addon/mod/forum/providers/prefetch-handler.ts @@ -16,10 +16,10 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; import { CoreUserProvider } from '@core/user/providers/user'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { CoreGroupsProvider } from '@providers/groups'; @@ -69,7 +69,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand const files = this.getIntroFilesFromInstance(module, forum); // Get posts. - return this.getPostsForPrefetch(forum).then((posts) => { + return this.getPostsForPrefetch(forum, {cmId: module.id}).then((posts) => { // Add posts attachments and embedded files. return files.concat(this.getPostsFiles(posts)); }); @@ -108,14 +108,19 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand * Get the posts to be prefetched. * * @param forum Forum instance. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with array of posts. */ - protected getPostsForPrefetch(forum: any, siteId?: string): Promise { + protected getPostsForPrefetch(forum: any, options: CoreCourseCommonModWSOptions = {}): Promise { const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => { // Get discussions in first 2 pages. - return this.forumProvider.getDiscussionsInPages(forum.id, forum.cmid, - sortOrder.value, false, 2, 0, siteId).then((response) => { + const discussionsOptions = { + sortOrder: sortOrder.value, + numPages: 2, + ...options, // Include all options. + }; + + return this.forumProvider.getDiscussionsInPages(forum.id, discussionsOptions).then((response) => { if (response.error) { return Promise.reject(null); } @@ -123,7 +128,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand const promises = []; response.discussions.forEach((discussion) => { - promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, forum.cmid, siteId)); + promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, options)); }); return Promise.all(promises); @@ -202,12 +207,21 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand * @return Promise resolved when done. */ protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise { + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + // Get the forum data. - return this.forumProvider.getForum(courseId, module.id, siteId).then((forum) => { + return this.forumProvider.getForum(courseId, module.id, commonOptions).then((forum) => { const promises = []; // Prefetch the posts. - promises.push(this.getPostsForPrefetch(forum, siteId).then((posts) => { + promises.push(this.getPostsForPrefetch(forum, modOptions).then((posts) => { const promises = []; const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts)); @@ -223,7 +237,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand })); // Prefetch access information. - promises.push(this.forumProvider.getAccessInformation(forum.id, false, siteId)); + promises.push(this.forumProvider.getAccessInformation(forum.id, modOptions)); // Prefetch sort order preference. if (this.forumProvider.isDiscussionListSortingAvailable()) { @@ -244,11 +258,16 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand * @return Promise resolved when group data has been prefetched. */ protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean, siteId?: string): any { + const options = { + cmId: forum.cmid, + siteId, + }; + // Check group mode. return this.groupsProvider.getActivityGroupMode(forum.cmid, siteId).then((mode) => { if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) { // Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach. - return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => { + return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { // Ignore errors. }); } @@ -257,14 +276,14 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand return this.groupsProvider.getActivityAllowedGroups(forum.cmid, undefined, siteId).then((result) => { if (mode === CoreGroupsProvider.SEPARATEGROUPS) { // Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach. - return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => { + return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { // Ignore errors. }); } if (canCreateDiscussions) { // Prefetch data to check the visible groups when creating discussions. - return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => { + return this.forumProvider.canAddDiscussionToAll(forum.id, options).catch(() => { // The call failed, let's assume he can't. return { status: false @@ -278,7 +297,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand // The user can't post to all groups, let's check which groups he can post to. const groupPromises = []; result.groups.forEach((group) => { - groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id, siteId).catch(() => { + groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id, options).catch(() => { // Ignore errors. })); }); diff --git a/src/addon/mod/forum/providers/sync.ts b/src/addon/mod/forum/providers/sync.ts index 2b204df72..9ca5c5a18 100644 --- a/src/addon/mod/forum/providers/sync.ts +++ b/src/addon/mod/forum/providers/sync.ts @@ -227,7 +227,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { let groupsPromise; if (data.groupid == AddonModForumProvider.ALL_GROUPS) { // Fetch all group ids. - groupsPromise = this.forumProvider.getForumById(data.courseid, data.forumid, siteId).then((forum) => { + groupsPromise = this.forumProvider.getForumById(data.courseid, data.forumid, {siteId}).then((forum) => { return this.groupsProvider.getActivityAllowedGroups(forum.cmid).then((result) => { return result.groups.map((group) => group.id); }); @@ -330,7 +330,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { } if (result.warnings.length) { // Fetch forum to construct the warning message. - promises.push(this.forumProvider.getForum(result.itemSet.courseId, result.itemSet.instanceId, siteId) + promises.push(this.forumProvider.getForum(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) .then((forum) => { result.warnings.forEach((warning) => { warnings.push(this.translate.instant('core.warningofflinedatadeleted', { diff --git a/src/addon/mod/glossary/components/index/index.ts b/src/addon/mod/glossary/components/index/index.ts index 41bfbd760..3b759ea5e 100644 --- a/src/addon/mod/glossary/components/index/index.ts +++ b/src/addon/mod/glossary/components/index/index.ts @@ -181,10 +181,10 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity return Promise.resolve({entries: [], count: 0}); } - const limitFrom = append ? this.entries.length : 0; - const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES; - - return this.glossaryProvider.fetchEntries(this.fetchFunction, this.fetchArguments, limitFrom, limitNum).then((result) => { + return this.glossaryProvider.fetchEntries(this.fetchFunction, this.fetchArguments, { + from: append ? this.entries.length : 0, + cmId: this.module.id, + }).then((result) => { if (append) { Array.prototype.push.apply(this.entries, result.entries); } else { diff --git a/src/addon/mod/glossary/pages/edit/edit.ts b/src/addon/mod/glossary/pages/edit/edit.ts index 9bc2ee645..498568084 100644 --- a/src/addon/mod/glossary/pages/edit/edit.ts +++ b/src/addon/mod/glossary/pages/edit/edit.ts @@ -125,7 +125,9 @@ export class AddonModGlossaryEditPage implements OnInit { this.definitionControl.setValue(this.entry.definition); Promise.resolve(promise).then(() => { - this.glossaryProvider.getAllCategories(this.glossary.id).then((categories) => { + this.glossaryProvider.getAllCategories(this.glossary.id, { + cmId: this.module.id, + }).then((categories) => { this.categories = categories; }).finally(() => { this.loaded = true; @@ -215,8 +217,10 @@ export class AddonModGlossaryEditPage implements OnInit { let promise; if (this.entry && !this.glossary.allowduplicatedentries) { // Check if the entry is duplicated in online or offline mode. - promise = this.glossaryProvider.isConceptUsed(this.glossary.id, this.entry.concept, this.entry.timecreated) - .then((used) => { + promise = this.glossaryProvider.isConceptUsed(this.glossary.id, this.entry.concept, { + timeCreated: this.entry.timecreated, + cmId: this.module.id, + }).then((used) => { if (used) { // There's a entry with same name, reject with error message. return Promise.reject(this.translate.instant('addon.mod_glossary.errconceptalreadyexists')); @@ -237,7 +241,12 @@ export class AddonModGlossaryEditPage implements OnInit { // Try to send it to server. // Don't allow offline if there are attachments since they were uploaded fine. return this.glossaryProvider.addEntry(this.glossary.id, this.entry.concept, definition, this.courseId, options, - attach, timecreated, undefined, this.entry, !this.attachments.length, !this.glossary.allowduplicatedentries); + attach, { + timeCreated: timecreated, + discardEntry: this.entry, + allowOffline: !this.attachments.length, + checkDuplicates: !this.glossary.allowduplicatedentries, + }); } }).then((entryId) => { // Delete the local files from the tmp folder. diff --git a/src/addon/mod/glossary/providers/entry-link-handler.ts b/src/addon/mod/glossary/providers/entry-link-handler.ts index 98ec8a6e3..4c7d5a6fd 100644 --- a/src/addon/mod/glossary/providers/entry-link-handler.ts +++ b/src/addon/mod/glossary/providers/entry-link-handler.ts @@ -63,7 +63,7 @@ export class AddonModGlossaryEntryLinkHandler extends CoreContentLinksHandlerBas if (courseId) { promise = Promise.resolve(courseId); } else { - promise = this.glossaryProvider.getEntry(entryId, siteId).catch((error) => { + promise = this.glossaryProvider.getEntry(entryId, {siteId}).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.mod_glossary.errorloadingentry', true); return Promise.reject(null); diff --git a/src/addon/mod/glossary/providers/glossary.ts b/src/addon/mod/glossary/providers/glossary.ts index d80820756..79f9887ad 100644 --- a/src/addon/mod/glossary/providers/glossary.ts +++ b/src/addon/mod/glossary/providers/glossary.ts @@ -17,12 +17,13 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreSite } from '@classes/site'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreSitesProvider, CoreSiteSchema, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreRatingInfo } from '@core/rating/providers/rating'; import { AddonModGlossaryOfflineProvider } from './offline'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for glossaries. @@ -92,17 +93,19 @@ export class AddonModGlossaryProvider { * Get all the glossaries in a course. * * @param courseId Course Id. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the glossaries. */ - getCourseGlossaries(courseId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getCourseGlossaries(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] + courseids: [courseId], }; const preSets = { cacheKey: this.getCourseGlossariesCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModGlossaryProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_glossaries_by_courses', params, preSets).then((result) => { @@ -146,29 +149,27 @@ export class AddonModGlossaryProvider { * @param letter First letter of firstname or lastname, or either keywords: ALL or SPECIAL. * @param field Search and order using: FIRSTNAME or LASTNAME * @param sort The direction of the order: ASC or DESC - * @param from Start returning records from here. - * @param limit Number of records to return. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the entries. */ - getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, from: number, limit: number, - omitExpires: boolean, forceOffline: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, + options: AddonModGlossaryGetEntriesOptions = {}): Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { id: glossaryId, letter: letter, field: field, sort: sort, - from: from, - limit: limit + from: options.from || 0, + limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, }; const preSets = { cacheKey: this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort), - omitExpires: omitExpires, - forceOffline: forceOffline, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entries_by_author', params, preSets); @@ -199,28 +200,24 @@ export class AddonModGlossaryProvider { * @param glossaryId Glossary Id. * @param categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or * constant SHOW_NOT_CATEGORISED for uncategorised entries. - * @param from Start returning records from here. - * @param limit Number of records to return. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the entries. */ - getEntriesByCategory(glossaryId: number, categoryId: number, from: number, limit: number, omitExpires: boolean, - forceOffline: boolean, siteId?: string): Promise { + getEntriesByCategory(glossaryId: number, categoryId: number, options: AddonModGlossaryGetEntriesOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { id: glossaryId, categoryid: categoryId, - from: from, - limit: limit + from: options.from || 0, + limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, }; const preSets = { cacheKey: this.getEntriesByCategoryCacheKey(glossaryId, categoryId), - omitExpires: omitExpires, - forceOffline: forceOffline, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entries_by_category', params, preSets); @@ -274,29 +271,26 @@ export class AddonModGlossaryProvider { * @param glossaryId Glossary Id. * @param order The way to order the records. * @param sort The direction of the order. - * @param from Start returning records from here. - * @param limit Number of records to return. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the entries. */ - getEntriesByDate(glossaryId: number, order: string, sort: string, from: number, limit: number, omitExpires: boolean, - forceOffline: boolean, siteId?: string): Promise { + getEntriesByDate(glossaryId: number, order: string, sort: string, options: AddonModGlossaryGetEntriesOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { id: glossaryId, order: order, sort: sort, - from: from, - limit: limit + from: options.from || 0, + limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, }; const preSets = { cacheKey: this.getEntriesByDateCacheKey(glossaryId, order, sort), - omitExpires: omitExpires, - forceOffline: forceOffline, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entries_by_date', params, preSets); @@ -336,35 +330,33 @@ export class AddonModGlossaryProvider { * * @param glossaryId Glossary Id. * @param letter A letter, or a special keyword. - * @param from Start returning records from here. - * @param limit Number of records to return. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the entries. */ - getEntriesByLetter(glossaryId: number, letter: string, from: number, limit: number, omitExpires: boolean, forceOffline: boolean, - siteId?: string): Promise { + getEntriesByLetter(glossaryId: number, letter: string, options: AddonModGlossaryGetEntriesOptions = {}): Promise { + options.from = options.from || 0; + options.limit = options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { id: glossaryId, letter: letter, - from: from, - limit: limit + from: options.from, + limit: options.limit, }; const preSets = { cacheKey: this.getEntriesByLetterCacheKey(glossaryId, letter), - omitExpires: omitExpires, - forceOffline: forceOffline, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entries_by_letter', params, preSets).then((result) => { - if (limit == AddonModGlossaryProvider.LIMIT_ENTRIES) { + if (options.limit == AddonModGlossaryProvider.LIMIT_ENTRIES) { // Store entries in background, don't block the user for this. - this.storeEntries(glossaryId, result.entries, from, site.getId()).catch(() => { + this.storeEntries(glossaryId, result.entries, options.from, site.getId()).catch(() => { // Ignore errors. }); } @@ -420,23 +412,25 @@ export class AddonModGlossaryProvider { * @param siteId Site ID. If not defined, current site. * @return Resolved with the entries. */ - getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, from: number, - limit: number, omitExpires: boolean, forceOffline: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, + options: AddonModGlossaryGetEntriesOptions = {}): Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { id: glossaryId, query: query, fullsearch: fullSearch, order: order, sort: sort, - from: from, - limit: limit + from: options.from || 0, + limit: options.limit || AddonModGlossaryProvider.LIMIT_ENTRIES, }; const preSets = { cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort), - omitExpires: omitExpires, - forceOffline: forceOffline, - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entries_by_search', params, preSets); @@ -477,12 +471,12 @@ export class AddonModGlossaryProvider { * Get all the categories related to the glossary. * * @param glossaryId Glossary Id. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the categories if supported or empty array if not. */ - getAllCategories(glossaryId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - return this.getCategories(glossaryId, 0, AddonModGlossaryProvider.LIMIT_CATEGORIES, [], site); + getAllCategories(glossaryId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { + return this.getCategories(glossaryId, [], site, options); }); } @@ -490,30 +484,37 @@ export class AddonModGlossaryProvider { * Get the categories related to the glossary by sections. It's a recursive function see initial call values. * * @param glossaryId Glossary Id. - * @param from Number of categories already fetched, so fetch will be done from this number. Initial value 0. - * @param limit Number of categories to fetch. Initial value LIMIT_CATEGORIES. - * @param categories Already fetched categories where to append the fetch. Initial value []. + * @param categories Already fetched categories where to append the fetch. * @param site Site object. + * @param options Other options. * @return Promise resolved with the categories. */ - protected getCategories(glossaryId: number, from: number, limit: number, categories: any[], site: CoreSite): Promise { + protected getCategories(glossaryId: number, categories: any[], site: CoreSite, + options: AddonModGlossaryGetCategoriesOptions = {}): Promise { + + options.from = options.from || 0; + options.limit = options.limit || AddonModGlossaryProvider.LIMIT_CATEGORIES; + const params = { id: glossaryId, - from: from, - limit: limit + from: options.from, + limit: options.limit, }; const preSets = { cacheKey: this.getCategoriesCacheKey(glossaryId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_categories', params, preSets).then((response) => { categories = categories.concat(response.categories); - const canLoadMore = (from + limit) < response.count; + const canLoadMore = (options.from + options.limit) < response.count; if (canLoadMore) { - from += limit; + options.from += options.limit; - return this.getCategories(glossaryId, from, limit, categories, site); + return this.getCategories(glossaryId, categories, site, options); } return categories; @@ -547,17 +548,22 @@ export class AddonModGlossaryProvider { * Get one entry by ID. * * @param entryId Entry ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the entry. */ - getEntry(entryId: number, siteId?: string): Promise<{entry: any, ratinginfo: CoreRatingInfo, from?: number}> { - return this.sitesProvider.getSite(siteId).then((site) => { + getEntry(entryId: number, options: CoreCourseCommonModWSOptions = {}) + : Promise<{entry: any, ratinginfo: CoreRatingInfo, from?: number}> { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - id: entryId + id: entryId, }; const preSets = { cacheKey: this.getEntryCacheKey(entryId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModGlossaryProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_glossary_get_entry_by_id', params, preSets).then((response) => { @@ -572,8 +578,12 @@ export class AddonModGlossaryProvider { const searchEntry = (from: number, loadNext: boolean): Promise => { // Get the entries from this "page" and check if the entry we're looking for is in it. - return this.getEntriesByLetter(glossaryId, 'ALL', from, AddonModGlossaryProvider.LIMIT_ENTRIES, false, true, - siteId).then((result) => { + return this.getEntriesByLetter(glossaryId, 'ALL', { + from: from, + readingStrategy: CoreSitesReadingStrategy.OnlyCache, + cmId: options.cmId, + siteId: options.siteId, + }).then((result) => { for (let i = 0; i < result.entries.length; i++) { const entry = result.entries[i]; @@ -643,48 +653,34 @@ export class AddonModGlossaryProvider { * * @param fetchFunction Function to fetch. * @param fetchArguments Arguments to call the fetching. - * @param limitFrom Number of entries already fetched, so fetch will be done from this number. - * @param limitNum Number of records to return. Defaults to LIMIT_ENTRIES. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the response. */ - fetchEntries(fetchFunction: Function, fetchArguments: any[], limitFrom: number = 0, limitNum?: number, - omitExpires: boolean = false, forceOffline: boolean = false, siteId?: string): Promise { - limitNum = limitNum || AddonModGlossaryProvider.LIMIT_ENTRIES; - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - + fetchEntries(fetchFunction: Function, fetchArguments: any[], options: AddonModGlossaryGetEntriesOptions = {}): Promise { const args = fetchArguments.slice(); - args.push(limitFrom); - args.push(limitNum); - args.push(omitExpires); - args.push(forceOffline); - args.push(siteId); + args.push(options); return fetchFunction.apply(this, args); } /** - * Performs the whole fetch of the entries using the propper function and arguments. + * Performs the whole fetch of the entries using the proper function and arguments. * * @param fetchFunction Function to fetch. * @param fetchArguments Arguments to call the fetching. - * @param omitExpires True to always get the value from cache. If data isn't cached, it will call the WS. - * @param forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with all entrries. */ - fetchAllEntries(fetchFunction: Function, fetchArguments: any[], omitExpires: boolean = false, forceOffline: boolean = false, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + fetchAllEntries(fetchFunction: Function, fetchArguments: any[], options: CoreCourseCommonModWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); const entries = []; - const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES; const fetchMoreEntries = (): Promise => { - return this.fetchEntries(fetchFunction, fetchArguments, entries.length, limitNum, omitExpires, forceOffline, siteId) - .then((result) => { + return this.fetchEntries(fetchFunction, fetchArguments, { + from: entries.length, + ...options, // Include all options. + }).then((result) => { Array.prototype.push.apply(entries, result.entries); return entries.length < result.count ? fetchMoreEntries() : entries; @@ -759,8 +755,11 @@ export class AddonModGlossaryProvider { const promises = []; if (!onlyEntriesList) { - promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], true, false, siteId) - .then((entries) => { + promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], { + cmId: glossary.coursemodule, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }).then((entries) => { return this.invalidateEntries(entries, siteId); })); } @@ -804,11 +803,11 @@ export class AddonModGlossaryProvider { * * @param courseId Course Id. * @param cmId Course Module Id. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the glossary. */ - getGlossary(courseId: number, cmId: number, siteId?: string): Promise { - return this.getCourseGlossaries(courseId, siteId).then((glossaries) => { + getGlossary(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getCourseGlossaries(courseId, options).then((glossaries) => { const glossary = glossaries.find((glossary) => glossary.coursemodule == cmId); if (glossary) { @@ -824,11 +823,11 @@ export class AddonModGlossaryProvider { * * @param courseId Course Id. * @param glossaryId Glossary Id. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the glossary. */ - getGlossaryById(courseId: number, glossaryId: number, siteId?: string): Promise { - return this.getCourseGlossaries(courseId, siteId).then((glossaries) => { + getGlossaryById(courseId: number, glossaryId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getCourseGlossaries(courseId, options).then((glossaries) => { const glossary = glossaries.find((glossary) => glossary.id == glossaryId); if (glossary) { @@ -846,28 +845,27 @@ export class AddonModGlossaryProvider { * @param concept Glossary entry concept. * @param definition Glossary entry concept definition. * @param courseId Course ID of the glossary. - * @param options Array of options for the entry. + * @param entryOptions Array of options for the entry. * @param attach Attachments ID if sending online, result of CoreFileUploaderProvider#storeFilesToUpload * otherwise. - * @param timeCreated The time the entry was created. If not defined, current time. - * @param siteId Site ID. If not defined, current site. - * @param discardEntry The entry provided will be discarded if found. - * @param allowOffline True if it can be stored in offline, false otherwise. - * @param checkDuplicates Check for duplicates before storing offline. Only used if allowOffline is true. + * @param otherOptions Other options. * @return Promise resolved with entry ID if entry was created in server, false if stored in device. */ - addEntry(glossaryId: number, concept: string, definition: string, courseId: number, options: any, attach: any, - timeCreated: number, siteId?: string, discardEntry?: any, allowOffline?: boolean, checkDuplicates?: boolean): - Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + addEntry(glossaryId: number, concept: string, definition: string, courseId: number, entryOptions: any, attach: any, + otherOptions: AddonModGlossaryAddEntryOptions = {}): Promise { + otherOptions.siteId = otherOptions.siteId || this.sitesProvider.getCurrentSiteId(); // Convenience function to store a new entry to be synchronized later. const storeOffline = (): Promise => { - const discardTime = discardEntry && discardEntry.timecreated; + const discardTime = otherOptions.discardEntry && otherOptions.discardEntry.timecreated; let duplicatesPromise; - if (checkDuplicates) { - duplicatesPromise = this.isConceptUsed(glossaryId, concept, discardTime, siteId); + if (otherOptions.checkDuplicates) { + duplicatesPromise = this.isConceptUsed(glossaryId, concept, { + cmId: otherOptions.cmId, + timeCreated: discardTime, + siteId: otherOptions.siteId, + }); } else { duplicatesPromise = Promise.resolve(false); } @@ -878,33 +876,34 @@ export class AddonModGlossaryProvider { return Promise.reject(this.translate.instant('addon.mod_glossary.errconceptalreadyexists')); } - return this.glossaryOffline.addNewEntry(glossaryId, concept, definition, courseId, attach, options, timeCreated, - siteId, undefined, discardEntry).then(() => { + return this.glossaryOffline.addNewEntry(glossaryId, concept, definition, courseId, attach, entryOptions, + otherOptions.timeCreated, otherOptions.siteId, undefined, otherOptions.discardEntry).then(() => { return false; }); }); }; - if (!this.appProvider.isOnline() && allowOffline) { + if (!this.appProvider.isOnline() && otherOptions.allowOffline) { // App is offline, store the action. return storeOffline(); } // If we are editing an offline entry, discard previous first. let discardPromise; - if (discardEntry) { + if (otherOptions.discardEntry) { discardPromise = this.glossaryOffline.deleteNewEntry( - glossaryId, discardEntry.concept, discardEntry.timecreated, siteId); + glossaryId, otherOptions.discardEntry.concept, otherOptions.discardEntry.timecreated, otherOptions.siteId); } else { discardPromise = Promise.resolve(); } return discardPromise.then(() => { // Try to add it in online. - return this.addEntryOnline(glossaryId, concept, definition, options, attach, siteId).then((entryId) => { + return this.addEntryOnline(glossaryId, concept, definition, entryOptions, attach, otherOptions.siteId) + .then((entryId) => { return entryId; }).catch((error) => { - if (allowOffline && !this.utils.isWebServiceError(error)) { + if (otherOptions.allowOffline && !this.utils.isWebServiceError(error)) { // Couldn't connect to server, store in offline. return storeOffline(); } else { @@ -964,20 +963,23 @@ export class AddonModGlossaryProvider { * * @param glossaryId Glossary ID. * @param concept Concept to check. - * @param timeCreated Timecreated to check that is not the timecreated we are editing. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with true if used, resolved with false if not used or error. */ - isConceptUsed(glossaryId: number, concept: string, timeCreated?: number, siteId?: string): Promise { + isConceptUsed(glossaryId: number, concept: string, options: AddonModGlossaryIsConceptUsedOptions = {}): Promise { // Check offline first. - return this.glossaryOffline.isConceptUsed(glossaryId, concept, timeCreated, siteId).then((exists) => { + return this.glossaryOffline.isConceptUsed(glossaryId, concept, options.timeCreated, options.siteId).then((exists) => { if (exists) { return true; } // If we get here, there's no offline entry with this name, check online. // Get entries from the cache. - return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], true, false, siteId).then((entries) => { + return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], { + cmId: options.cmId, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((entries) => { // Check if there's any entry with the same concept. return entries.some((entry) => entry.concept == concept); }); @@ -1074,3 +1076,40 @@ export class AddonModGlossaryProvider { }); } } + +/** + * Options to pass to add entry. + */ +export type AddonModGlossaryAddEntryOptions = { + timeCreated?: number; // The time the entry was created. If not defined, current time. + discardEntry?: any; // The entry provided will be discarded if found. + allowOffline?: boolean; // True if it can be stored in offline, false otherwise. + checkDuplicates?: boolean; // Check for duplicates before storing offline. Only used if allowOffline is true. + cmId?: number; // Module ID. + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to the different get entries functions. + */ +export type AddonModGlossaryGetEntriesOptions = CoreCourseCommonModWSOptions & { + from?: number; // Start returning records from here. Defaults to 0. + limit?: number; // Number of records to return. Defaults to AddonModGlossaryProvider.LIMIT_ENTRIES. +}; + +/** + * Options to pass to get categories. + */ +export type AddonModGlossaryGetCategoriesOptions = CoreCourseCommonModWSOptions & { + from?: number; // Start returning records from here. Defaults to 0. + limit?: number; // Number of records to return. Defaults to AddonModGlossaryProvider.LIMIT_CATEGORIES. +}; + +/** + * Options to pass to is concept used. + */ +export type AddonModGlossaryIsConceptUsedOptions = { + cmId?: number; // Module ID. + timeCreated?: number; // Timecreated to check that is not the timecreated we are editing. + siteId?: string; // Site ID. If not defined, current site. +}; diff --git a/src/addon/mod/glossary/providers/prefetch-handler.ts b/src/addon/mod/glossary/providers/prefetch-handler.ts index c202a88e6..cfb5c4785 100644 --- a/src/addon/mod/glossary/providers/prefetch-handler.ts +++ b/src/addon/mod/glossary/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -66,8 +66,9 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH */ getFiles(module: any, courseId: number, single?: boolean): Promise { return this.glossaryProvider.getGlossary(courseId, module.id).then((glossary) => { - return this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, [glossary.id, 'ALL']) - .then((entries) => { + return this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, [glossary.id, 'ALL'], { + cmId: module.id, + }).then((entries) => { return this.getFilesFromGlossaryAndEntries(module, glossary, entries); }); }).catch(() => { @@ -139,8 +140,14 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH protected prefetchGlossary(module: any, courseId: number, single: boolean, siteId: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); + const options = { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + // Prefetch the glossary data. - return this.glossaryProvider.getGlossary(courseId, module.id, siteId).then((glossary) => { + return this.glossaryProvider.getGlossary(courseId, module.id, {siteId}).then((glossary) => { const promises = []; glossary.browsemodes.forEach((mode) => { @@ -149,25 +156,25 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH break; case 'cat': // Not implemented. promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByCategory, - [glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES], false, false, siteId)); + [glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES], options)); break; case 'date': promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, - [glossary.id, 'CREATION', 'DESC'], false, false, siteId)); + [glossary.id, 'CREATION', 'DESC'], options)); promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, - [glossary.id, 'UPDATE', 'DESC'], false, false, siteId)); + [glossary.id, 'UPDATE', 'DESC'], options)); break; case 'author': promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByAuthor, - [glossary.id, 'ALL', 'LASTNAME', 'ASC'], false, false, siteId)); + [glossary.id, 'ALL', 'LASTNAME', 'ASC'], options)); break; default: } }); // Fetch all entries to get information from. - promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, - [glossary.id, 'ALL'], false, false, siteId).then((entries) => { + promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, [glossary.id, 'ALL'], + options).then((entries) => { const promises = []; const commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(); @@ -190,7 +197,7 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH })); // Get all categories. - promises.push(this.glossaryProvider.getAllCategories(glossary.id, siteId)); + promises.push(this.glossaryProvider.getAllCategories(glossary.id, options)); // Prefetch data for link handlers. promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId)); diff --git a/src/addon/mod/glossary/providers/sync.ts b/src/addon/mod/glossary/providers/sync.ts index 5ad4034c5..87ea9ba5c 100644 --- a/src/addon/mod/glossary/providers/sync.ts +++ b/src/addon/mod/glossary/providers/sync.ts @@ -281,7 +281,7 @@ export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { }); } if (result.warnings.length) { - promises.push(this.glossaryProvider.getGlossary(result.itemSet.courseId, result.itemSet.instanceId, siteId) + promises.push(this.glossaryProvider.getGlossary(result.itemSet.courseId, result.itemSet.instanceId, {siteId}) .then((glossary) => { result.warnings.forEach((warning) => { warnings.push(this.translate.instant('core.warningofflinedatadeleted', { diff --git a/src/addon/mod/h5pactivity/components/index/index.ts b/src/addon/mod/h5pactivity/components/index/index.ts index 2dc01a2d5..c0f1077e7 100644 --- a/src/addon/mod/h5pactivity/components/index/index.ts +++ b/src/addon/mod/h5pactivity/components/index/index.ts @@ -108,7 +108,9 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv */ protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { try { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id, false, this.siteId); + this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(this.courseId, this.module.id, { + siteId: this.siteId, + }); this.dataRetrieved.emit(this.h5pActivity); this.description = this.h5pActivity.intro; @@ -161,7 +163,10 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv * @return Promise resolved when done. */ protected async fetchAccessInfo(): Promise { - this.accessInfo = await AddonModH5PActivity.instance.getAccessInformation(this.h5pActivity.id, false, this.siteId); + this.accessInfo = await AddonModH5PActivity.instance.getAccessInformation(this.h5pActivity.id, { + cmId: this.module.id, + siteId: this.siteId, + }); } /** diff --git a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts index 96b99fc16..996ddb0cf 100644 --- a/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts +++ b/src/addon/mod/h5pactivity/pages/attempt-results/attempt-results.ts @@ -77,32 +77,15 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit { * @return Promise resolved when done. */ protected async fetchData(): Promise { - await Promise.all([ - this.fetchActivity(), - this.fetchAttempt(), - ]); + this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); + + this.attempt = await AddonModH5PActivity.instance.getAttemptResults(this.h5pActivityId, this.attemptId, { + cmId: this.h5pActivity.coursemodule, + }); await this.fetchUserProfile(); } - /** - * Get activity data. - * - * @return Promise resolved when done. - */ - protected async fetchActivity(): Promise { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); - } - - /** - * Get attempts. - * - * @return Promise resolved when done. - */ - protected async fetchAttempt(): Promise { - this.attempt = await AddonModH5PActivity.instance.getAttemptResults(this.h5pActivityId, this.attemptId); - } - /** * Get user profile. * diff --git a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts index cc8499405..bffee1f21 100644 --- a/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts +++ b/src/addon/mod/h5pactivity/pages/user-attempts/user-attempts.ts @@ -79,29 +79,24 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit { * @return Promise resolved when done. */ protected async fetchData(): Promise { + this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); + await Promise.all([ - this.fetchActivity(), this.fetchAttempts(), this.fetchUserProfile(), ]); } - /** - * Get activity data. - * - * @return Promise resolved when done. - */ - protected async fetchActivity(): Promise { - this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId); - } - /** * Get attempts. * * @return Promise resolved when done. */ protected async fetchAttempts(): Promise { - this.attemptsData = await AddonModH5PActivity.instance.getUserAttempts(this.h5pActivityId, { userId: this.userId }); + this.attemptsData = await AddonModH5PActivity.instance.getUserAttempts(this.h5pActivityId, { + cmId: this.h5pActivity.coursemodule, + userId: this.userId, + }); } /** diff --git a/src/addon/mod/h5pactivity/providers/h5pactivity.ts b/src/addon/mod/h5pactivity/providers/h5pactivity.ts index 5e1fabc42..1cf09d1c8 100644 --- a/src/addon/mod/h5pactivity/providers/h5pactivity.ts +++ b/src/addon/mod/h5pactivity/providers/h5pactivity.ts @@ -14,14 +14,15 @@ import { Injectable } from '@angular/core'; -import { CoreSites } from '@providers/sites'; +import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; import { CoreTimeUtils } from '@providers/utils/time'; import { CoreUtils } from '@providers/utils/utils'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreCourseLogHelper } from '@core/course/providers/log-helper'; import { CoreH5P } from '@core/h5p/providers/h5p'; import { CoreH5PDisplayOptions } from '@core/h5p/classes/core'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; import { makeSingleton, Translate } from '@singletons/core.singletons'; @@ -121,20 +122,22 @@ export class AddonModH5PActivityProvider { * Get access information for a given H5P activity. * * @param id H5P activity ID. - * @param forceCache True to always get the value from cache. false otherwise. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the data. */ - async getAccessInformation(id: number, forceCache?: boolean, siteId?: string): Promise { + async getAccessInformation(id: number, options: CoreCourseCommonModWSOptions = {}): Promise { - const site = await CoreSites.instance.getSite(siteId); + const site = await CoreSites.instance.getSite(options.siteId); const params = { h5pactivityid: id, }; const preSets = { cacheKey: this.getAccessInformationCacheKey(id), - omitExpires: forceCache, + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModH5PActivityProvider.COMPONENT, + componentId: options.cmId, + ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_h5pactivity_get_h5pactivity_access_information', params, preSets); @@ -209,18 +212,14 @@ export class AddonModH5PActivityProvider { h5pactivityid: id, attemptids: [attemptId], }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getAttemptResultsCacheKey(id, params.attemptids), updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModH5PActivityProvider.COMPONENT, + componentId: options.cmId, + ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (options.forceCache) { - preSets.omitExpires = true; - } else if (options.ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - try { const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets); @@ -235,9 +234,12 @@ export class AddonModH5PActivityProvider { } // Check if the full list of results is cached. If so, get the results from there. - options.forceCache = true; + const cacheOptions = { + ...options, // Include all the original options. + readingStrategy: CoreSitesReadingStrategy.OnlyCache, + }; - const attemptsResults = await AddonModH5PActivity.instance.getAllAttemptsResults(id, options); + const attemptsResults = await AddonModH5PActivity.instance.getAllAttemptsResults(id, cacheOptions); const attempt = attemptsResults.attempts.find((attempt) => { return attempt.id == attemptId; @@ -270,18 +272,14 @@ export class AddonModH5PActivityProvider { h5pactivityid: id, attemptids: attemptsIds, }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getAttemptResultsCommonCacheKey(id), updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModH5PActivityProvider.COMPONENT, + componentId: options.cmId, + ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (options.forceCache) { - preSets.omitExpires = true; - } else if (options.ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - const response: AddonModH5PActivityGetResultsResult = await site.read('mod_h5pactivity_get_results', params, preSets); response.attempts = response.attempts.map((attempt) => { @@ -334,28 +332,24 @@ export class AddonModH5PActivityProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param moduleUrl Module URL. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the activity data. */ - protected async getH5PActivityByField(courseId: number, key: string, value: any, forceCache?: boolean, siteId?: string) + protected async getH5PActivityByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) : Promise { - const site = await CoreSites.instance.getSite(siteId); + const site = await CoreSites.instance.getSite(options.siteId); const params = { courseids: [courseId], }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getH5PActivityDataCacheKey(courseId), updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModH5PActivityProvider.COMPONENT, + ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (forceCache) { - preSets.omitExpires = true; - } - const response: AddonModH5PActivityGetByCoursesResult = await site.read('mod_h5pactivity_get_h5pactivities_by_courses', params, preSets); @@ -377,12 +371,11 @@ export class AddonModH5PActivityProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the activity data. */ - getH5PActivity(courseId: number, cmId: number, forceCache?: boolean, siteId?: string): Promise { - return this.getH5PActivityByField(courseId, 'coursemodule', cmId, forceCache, siteId); + getH5PActivity(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getH5PActivityByField(courseId, 'coursemodule', cmId, options); } /** @@ -390,13 +383,12 @@ export class AddonModH5PActivityProvider { * * @param courseId Course ID. * @param contextId Context ID. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the activity data. */ - getH5PActivityByContextId(courseId: number, contextId: number, forceCache?: boolean, siteId?: string) + getH5PActivityByContextId(courseId: number, contextId: number, options: CoreSitesCommonWSOptions = {}) : Promise { - return this.getH5PActivityByField(courseId, 'context', contextId, forceCache, siteId); + return this.getH5PActivityByField(courseId, 'context', contextId, options); } /** @@ -404,12 +396,11 @@ export class AddonModH5PActivityProvider { * * @param courseId Course ID. * @param id Instance ID. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the activity data. */ - getH5PActivityById(courseId: number, id: number, forceCache?: boolean, siteId?: string): Promise { - return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId); + getH5PActivityById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getH5PActivityByField(courseId, 'id', id, options); } /** @@ -440,9 +431,8 @@ export class AddonModH5PActivityProvider { * @param options Other options. * @return Promise resolved with the attempts of the user. */ - async getUserAttempts(id: number, options?: AddonModH5PActivityGetAttemptsOptions): Promise { - - options = options || {}; + async getUserAttempts(id: number, options: AddonModH5PActivityGetAttemptsOptions = {}) + : Promise { const site = await CoreSites.instance.getSite(options.siteId); @@ -450,18 +440,14 @@ export class AddonModH5PActivityProvider { h5pactivityid: id, userids: [options.userId || site.getUserId()], }; - const preSets: CoreSiteWSPreSets = { + const preSets = { cacheKey: this.getUserAttemptsCacheKey(id, params.userids), updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModH5PActivityProvider.COMPONENT, + componentId: options.cmId, + ...CoreSites.instance.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (options.forceCache) { - preSets.omitExpires = true; - } else if (options.ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - const response: AddonModH5PActivityGetAttemptsResult = await site.read('mod_h5pactivity_get_attempts', params, preSets); if (response.warnings[0]) { @@ -789,10 +775,7 @@ export type AddonModH5PActivityGetDeployedFileOptions = { /** * Options to pass to getAttemptResults function. */ -export type AddonModH5PActivityGetAttemptResultsOptions = { - forceCache?: boolean; // Whether to force cache. If not cached, it will call the WS. - ignoreCache?: boolean; // Whether to ignore cache. Will fail if offline or server down. - siteId?: string; // Site ID. If not defined, current site. +export type AddonModH5PActivityGetAttemptResultsOptions = CoreCourseCommonModWSOptions & { userId?: number; // User ID. If not defined, user of the site. }; diff --git a/src/addon/mod/h5pactivity/providers/prefetch-handler.ts b/src/addon/mod/h5pactivity/providers/prefetch-handler.ts index 85a804baf..fd000532a 100644 --- a/src/addon/mod/h5pactivity/providers/prefetch-handler.ts +++ b/src/addon/mod/h5pactivity/providers/prefetch-handler.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreWSExternalFile } from '@providers/ws'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -130,7 +130,10 @@ export class AddonModH5PActivityPrefetchHandler extends CoreCourseActivityPrefet */ protected async prefetchActivity(module: any, courseId: number, single: boolean, siteId: string): Promise { - const h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(courseId, module.id, true, siteId); + const h5pActivity = await AddonModH5PActivity.instance.getH5PActivity(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }); const introFiles = this.getIntroFilesFromInstance(module, h5pActivity); @@ -171,14 +174,19 @@ export class AddonModH5PActivityPrefetchHandler extends CoreCourseActivityPrefet */ protected async prefetchWSData(h5pActivity: AddonModH5PActivityData, siteId: string): Promise { - const accessInfo = await AddonModH5PActivity.instance.getAccessInformation(h5pActivity.id, true, siteId); + const accessInfo = await AddonModH5PActivity.instance.getAccessInformation(h5pActivity.id, { + cmId: h5pActivity.coursemodule, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }); if (!accessInfo.canreviewattempts) { // Not a teacher, prefetch user attempts and the current user profile. const site = await this.sitesProvider.getSite(siteId); const options = { - ignoreCache: true, + cmId: h5pActivity.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, siteId: siteId, }; diff --git a/src/addon/mod/h5pactivity/providers/sync.ts b/src/addon/mod/h5pactivity/providers/sync.ts index dafa44842..407f86d91 100644 --- a/src/addon/mod/h5pactivity/providers/sync.ts +++ b/src/addon/mod/h5pactivity/providers/sync.ts @@ -165,7 +165,7 @@ export class AddonModH5PActivitySyncProvider extends CoreCourseActivitySyncBaseP // Get the activity instance. const courseId = entries[0].courseid; - const h5pActivity = await AddonModH5PActivity.instance.getH5PActivityByContextId(courseId, contextId, false, siteId); + const h5pActivity = await AddonModH5PActivity.instance.getH5PActivityByContextId(courseId, contextId, {siteId}); // Sync offline logs. try { diff --git a/src/addon/mod/imscp/providers/imscp.ts b/src/addon/mod/imscp/providers/imscp.ts index bd7eb8cf4..3a0bd3c52 100644 --- a/src/addon/mod/imscp/providers/imscp.ts +++ b/src/addon/mod/imscp/providers/imscp.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -155,17 +155,21 @@ export class AddonModImscpProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the imscp is retrieved. */ - protected getImscpByKey(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getImscpByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] + courseids: [courseId], }; const preSets = { cacheKey: this.getImscpDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModImscpProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_imscp_get_imscps_by_courses', params, preSets) @@ -188,11 +192,11 @@ export class AddonModImscpProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the imscp is retrieved. */ - getImscp(courseId: number, cmId: number, siteId?: string): Promise { - return this.getImscpByKey(courseId, 'coursemodule', cmId, siteId); + getImscp(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getImscpByKey(courseId, 'coursemodule', cmId, options); } /** diff --git a/src/addon/mod/imscp/providers/prefetch-handler.ts b/src/addon/mod/imscp/providers/prefetch-handler.ts index 6fe5ab2b9..899fdbe09 100644 --- a/src/addon/mod/imscp/providers/prefetch-handler.ts +++ b/src/addon/mod/imscp/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -67,7 +67,10 @@ export class AddonModImscpPrefetchHandler extends CoreCourseResourcePrefetchHand const promises = []; promises.push(super.downloadOrPrefetch(module, courseId, prefetch, dirPath)); - promises.push(this.imscpProvider.getImscp(courseId, module.id, siteId)); + promises.push(this.imscpProvider.getImscp(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + })); return Promise.all(promises); }); diff --git a/src/addon/mod/label/providers/label.ts b/src/addon/mod/label/providers/label.ts index ec924ebd0..b16a304fe 100644 --- a/src/addon/mod/label/providers/label.ts +++ b/src/addon/mod/label/providers/label.ts @@ -13,10 +13,10 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; /** @@ -47,29 +47,22 @@ export class AddonModLabelProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param forceCache True to always get the value from cache, false otherwise. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not provided, current site. + * @param options Other options. * @return Promise resolved when the label is retrieved. */ - protected getLabelByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + protected getLabelByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getLabelDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getLabelDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModLabelProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_label_get_labels_by_courses', params, preSets) .then((response: AddonModLabelGetLabelsByCoursesResult): any => { @@ -91,14 +84,11 @@ export class AddonModLabelProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param forceCache True to always get the value from cache, false otherwise. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the label is retrieved. */ - getLabel(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { - return this.getLabelByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId); + getLabel(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getLabelByField(courseId, 'coursemodule', cmId, options); } /** @@ -106,14 +96,11 @@ export class AddonModLabelProvider { * * @param courseId Course ID. * @param labelId Label ID. - * @param forceCache True to always get the value from cache, false otherwise. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the label is retrieved. */ - getLabelById(courseId: number, labelId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { - return this.getLabelByField(courseId, 'id', labelId, forceCache, ignoreCache, siteId); + getLabelById(courseId: number, labelId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getLabelByField(courseId, 'id', labelId, options); } /** diff --git a/src/addon/mod/label/providers/prefetch-handler.ts b/src/addon/mod/label/providers/prefetch-handler.ts index e05f9ac0c..b920ac7ba 100644 --- a/src/addon/mod/label/providers/prefetch-handler.ts +++ b/src/addon/mod/label/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -63,7 +63,9 @@ export class AddonModLabelPrefetchHandler extends CoreCourseResourcePrefetchHand let promise; if (this.labelProvider.isGetLabelAvailableForSite()) { - promise = this.labelProvider.getLabel(courseId, module.id, false, ignoreCache); + promise = this.labelProvider.getLabel(courseId, module.id, { + readingStrategy: ignoreCache ? CoreSitesReadingStrategy.OnlyNetwork : undefined + }); } else { promise = Promise.resolve(); } diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts index c8c4fb1ed..4df72774b 100644 --- a/src/addon/mod/lesson/components/index/index.ts +++ b/src/addon/mod/lesson/components/index/index.ts @@ -118,6 +118,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo let lessonReady = true; this.askPassword = false; + const options = {cmId: this.module.id}; return this.lessonProvider.getLesson(this.courseId, this.module.id).then((lessonData) => { this.lesson = lessonData; @@ -130,7 +131,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo return this.syncActivity(showErrors); } }).then(() => { - return this.lessonProvider.getAccessInformation(this.lesson.id); + return this.lessonProvider.getAccessInformation(this.lesson.id, options); }).then((info) => { const promises = []; @@ -167,8 +168,8 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo })); // Update the list of content pages viewed and question attempts. - promises.push(this.lessonProvider.getContentPagesViewedOnline(this.lesson.id, info.attemptscount)); - promises.push(this.lessonProvider.getQuestionsAttemptsOnline(this.lesson.id, info.attemptscount)); + promises.push(this.lessonProvider.getContentPagesViewedOnline(this.lesson.id, info.attemptscount, options)); + promises.push(this.lessonProvider.getQuestionsAttemptsOnline(this.lesson.id, info.attemptscount, options)); } if (info.preventaccessreasons && info.preventaccessreasons.length) { @@ -364,7 +365,9 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo if (this.hasOffline) { if (continueLast) { - promise = this.lessonProvider.getLastPageSeen(this.lesson.id, this.accessInfo.attemptscount); + promise = this.lessonProvider.getLastPageSeen(this.lesson.id, this.accessInfo.attemptscount, { + cmId: this.module.id, + }); } else { promise = Promise.resolve(this.accessInfo.firstpageid); } @@ -445,7 +448,10 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } // Get the overview of retakes for the group. - return this.lessonProvider.getRetakesOverview(this.lesson.id, groupId).then((data) => { + return this.lessonProvider.getRetakesOverview(this.lesson.id, { + groupId, + cmId: this.lesson.coursemodule, + }).then((data) => { const promises = []; // Format times and grades. @@ -617,7 +623,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo * @return Promise resolved when done. */ protected validatePassword(password: string): Promise { - return this.lessonProvider.getLessonWithPassword(this.lesson.id, password).then((lessonData) => { + return this.lessonProvider.getLessonWithPassword(this.lesson.id, {password, cmId: this.module.id}).then((lessonData) => { this.lesson = lessonData; this.password = password; }).catch((error) => { diff --git a/src/addon/mod/lesson/pages/player/player.ts b/src/addon/mod/lesson/pages/player/player.ts index 2b2c1753b..b2a69cd5e 100644 --- a/src/addon/mod/lesson/pages/player/player.ts +++ b/src/addon/mod/lesson/pages/player/player.ts @@ -18,7 +18,7 @@ import { IonicPage, NavParams, Content, PopoverController, ModalController, Moda import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -172,11 +172,10 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { * * @param func Function to call. * @param args Arguments to pass to the function. - * @param offlineParamPos Position of the offline parameter in the args. - * @param jumpsParamPos Position of the jumps parameter in the args. + * @param options Options passed to the function (also included in args). * @return Promise resolved in success, rejected otherwise. */ - protected callFunction(func: Function, args: any[], offlineParamPos: number, jumpsParamPos?: number): Promise { + protected callFunction(func: Function, args: any[], options: any): Promise { return func.apply(func, args).catch((error) => { if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson) && !this.utils.isWebServiceError(error)) { @@ -184,14 +183,16 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { this.offline = true; // Get the possible jumps now. - return this.lessonProvider.getPagesPossibleJumps(this.lesson.id, true).then((jumpList) => { + return this.lessonProvider.getPagesPossibleJumps(this.lesson.id, { + cmId: this.lesson.coursemodule, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((jumpList) => { this.jumps = jumpList; - // Call the function again with offline set to true and the new jumps. - args[offlineParamPos] = true; - if (typeof jumpsParamPos != 'undefined') { - args[jumpsParamPos] = this.jumps; - } + // Call the function again with offline mode and the new jumps. + options.readingStrategy = CoreSitesReadingStrategy.PreferCache; + options.jumps = this.jumps; + options.offline = true; return func.apply(func, args); }); @@ -246,8 +247,13 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { this.offline = true; } + const options = { + cmId: this.lesson.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; + return this.callFunction(this.lessonProvider.getAccessInformation.bind(this.lessonProvider), - [this.lesson.id, this.offline, true], 1); + [this.lesson.id, options], options); }).then((info) => { const promises = []; @@ -272,15 +278,23 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { if (this.password) { // Lesson uses password, get the whole lesson object. + const options = { + password: this.password, + cmId: this.lesson.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; promises.push(this.callFunction(this.lessonProvider.getLessonWithPassword.bind(this.lessonProvider), - [this.lesson.id, this.password, true, this.offline, true], 3).then((lesson) => { + [this.lesson.id, options], options).then((lesson) => { this.lesson = lesson; })); } if (this.offline) { // Offline mode, get the list of possible jumps to allow navigation. - promises.push(this.lessonProvider.getPagesPossibleJumps(this.lesson.id, true).then((jumpList) => { + promises.push(this.lessonProvider.getPagesPossibleJumps(this.lesson.id, { + cmId: this.lesson.coursemodule, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((jumpList) => { this.jumps = jumpList; })); } @@ -334,7 +348,9 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { const error = result.warnings[0]; // Some data was deleted. Check if the retake has changed. - return this.lessonProvider.getAccessInformation(this.lesson.id).then((info) => { + return this.lessonProvider.getAccessInformation(this.lesson.id, { + cmId: this.lesson.coursemodule, + }).then((info) => { if (info.attemptscount != this.accessInfo.attemptscount) { // The retake has changed. Leave the view and show the error. this.forceLeave = true; @@ -359,9 +375,16 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { return promise.then(() => { // Now finish the retake. - const args = [this.lesson, this.courseId, this.password, outOfTime, this.review, this.offline, this.accessInfo]; + const options = { + password: this.password, + outOfTime, + review: this.review, + offline: this.offline, + accessInfo: this.accessInfo, + }; + const args = [this.lesson, this.courseId, options]; - return this.callFunction(this.lessonProvider.finishRetake.bind(this.lessonProvider), args, 5); + return this.callFunction(this.lessonProvider.finishRetake.bind(this.lessonProvider), args, options); }).then((data) => { this.title = this.lesson.name; this.eolData = data.data; @@ -447,7 +470,10 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { if (this.lesson.timelimit && !this.accessInfo.canmanage) { // Get the last lesson timer. - return this.lessonProvider.getTimers(this.lesson.id, false, true).then((timers) => { + return this.lessonProvider.getTimers(this.lesson.id, { + cmId: this.lesson.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }).then((timers) => { this.endTime = timers[timers.length - 1].starttime + this.lesson.timelimit; }); } @@ -469,9 +495,14 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { this.loadingMenu = true; - const args = [this.lessonId, this.password, this.offline, true]; + const options = { + password: this.password, + cmId: this.lesson.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }; + const args = [this.lessonId, options]; - return this.callFunction(this.lessonProvider.getPages.bind(this.lessonProvider), args, 2).then((pages) => { + return this.callFunction(this.lessonProvider.getPages.bind(this.lessonProvider), args, options).then((pages) => { this.lessonPages = pages.map((entry) => { return entry.page; }); @@ -494,9 +525,18 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { return this.finishRetake(); } - const args = [this.lesson, pageId, this.password, this.review, true, this.offline, true, this.accessInfo, this.jumps]; + const options = { + password: this.password, + review: this.review, + inludeContents: true, + cmId: this.lesson.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + accessInfo: this.accessInfo, + jumps: this.jumps, + }; + const args = [this.lesson, pageId, options]; - return this.callFunction(this.lessonProvider.getPageData.bind(this.lessonProvider), args, 5, 8).then((data) => { + return this.callFunction(this.lessonProvider.getPageData.bind(this.lessonProvider), args, options).then((data) => { if (data.newpageid == AddonModLessonProvider.LESSON_EOL) { // End of lesson reached. return this.finishRetake(); @@ -548,10 +588,16 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { protected processPage(data: any, formSubmitted?: boolean): Promise { this.loaded = false; - const args = [this.lesson, this.courseId, this.pageData, data, this.password, this.review, this.offline, this.accessInfo, - this.jumps]; + const options = { + password: this.password, + review: this.review, + offline: this.offline, + accessInfo: this.accessInfo, + jumps: this.jumps, + }; + const args = [this.lesson, this.courseId, this.pageData, data, options]; - return this.callFunction(this.lessonProvider.processPage.bind(this.lessonProvider), args, 6, 8).then((result) => { + return this.callFunction(this.lessonProvider.processPage.bind(this.lessonProvider), args, options).then((result) => { if (formSubmitted) { this.domUtils.triggerFormSubmittedEvent(this.formElement, result.sent, this.sitesProvider.getCurrentSiteId()); } @@ -559,11 +605,15 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson)) { // Lesson allows offline and the user changed some data in server. Update cached data. const retake = this.accessInfo.attemptscount; + const options = { + cmId: this.lesson.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }; if (this.lessonProvider.isQuestionPage(this.pageData.page.type)) { - this.lessonProvider.getQuestionsAttemptsOnline(this.lessonId, retake, false, undefined, false, true); + this.lessonProvider.getQuestionsAttemptsOnline(this.lessonId, retake, options); } else { - this.lessonProvider.getContentPagesViewedOnline(this.lessonId, retake, false, true); + this.lessonProvider.getContentPagesViewedOnline(this.lessonId, retake, options); } } diff --git a/src/addon/mod/lesson/pages/user-retake/user-retake.ts b/src/addon/mod/lesson/pages/user-retake/user-retake.ts index bbbb9e6c8..3d6418495 100644 --- a/src/addon/mod/lesson/pages/user-retake/user-retake.ts +++ b/src/addon/mod/lesson/pages/user-retake/user-retake.ts @@ -106,7 +106,9 @@ export class AddonModLessonUserRetakePage implements OnInit { this.lesson = lessonData; // Get the retakes overview for all participants. - return this.lessonProvider.getRetakesOverview(this.lesson.id); + return this.lessonProvider.getRetakesOverview(this.lesson.id, { + cmId: this.lesson.coursemodule, + }); }).then((data) => { // Search the student. let student; @@ -193,7 +195,10 @@ export class AddonModLessonUserRetakePage implements OnInit { protected setRetake(retakeNumber: number): Promise { this.selectedRetake = retakeNumber; - return this.lessonProvider.getUserRetake(this.lessonId, retakeNumber, this.userId).then((data) => { + return this.lessonProvider.getUserRetake(this.lessonId, retakeNumber, { + cmId: this.lesson.coursemodule, + userId: this.userId, + }).then((data) => { if (data && data.completed != -1) { // Completed. diff --git a/src/addon/mod/lesson/providers/grade-link-handler.ts b/src/addon/mod/lesson/providers/grade-link-handler.ts index c608c692e..7c0cc6c34 100644 --- a/src/addon/mod/lesson/providers/grade-link-handler.ts +++ b/src/addon/mod/lesson/providers/grade-link-handler.ts @@ -57,7 +57,7 @@ export class AddonModLessonGradeLinkHandler extends CoreContentLinksModuleGradeH courseId = module.course || courseId || params.courseid || params.cid; // Check if the user can see the user reports in the lesson. - return this.lessonProvider.getAccessInformation(module.instance); + return this.lessonProvider.getAccessInformation(module.instance, {cmId: module.id, siteId}); }).then((info) => { if (info.canviewreports) { // User can view reports, go to view the report. diff --git a/src/addon/mod/lesson/providers/lesson-sync.ts b/src/addon/mod/lesson/providers/lesson-sync.ts index f3c2d4303..a5375d352 100644 --- a/src/addon/mod/lesson/providers/lesson-sync.ts +++ b/src/addon/mod/lesson/providers/lesson-sync.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreSitesProvider, CoreSiteSchema, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -292,10 +292,14 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid courseId = attempts[0].courseid; // Get the info, access info and the lesson password if needed. - return this.lessonProvider.getLessonById(courseId, lessonId, false, false, siteId).then((lessonData) => { + return this.lessonProvider.getLessonById(courseId, lessonId, {siteId}).then((lessonData) => { lesson = lessonData; - return this.prefetchHandler.getLessonPassword(lessonId, false, true, askPassword, siteId); + return this.prefetchHandler.getLessonPassword(lessonId, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + askPassword, + siteId, + }); }).then((data) => { const attemptsLength = attempts.length, promises = []; @@ -368,10 +372,14 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid // Data already retrieved when syncing attempts. promise = Promise.resolve(); } else { - promise = this.lessonProvider.getLessonById(courseId, lessonId, false, false, siteId).then((lessonData) => { + promise = this.lessonProvider.getLessonById(courseId, lessonId, {siteId}).then((lessonData) => { lesson = lessonData; - return this.prefetchHandler.getLessonPassword(lessonId, false, true, askPassword, siteId); + return this.prefetchHandler.getLessonPassword(lessonId, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + askPassword, + siteId, + }); }).then((data) => { accessInfo = data.accessInfo; password = data.password; @@ -394,7 +402,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid } // All good, finish the retake. - return this.lessonProvider.finishRetakeOnline(lessonId, password, false, false, siteId).then((response) => { + return this.lessonProvider.finishRetakeOnline(lessonId, {password, siteId}).then((response) => { result.updated = true; if (!ignoreBlock) { @@ -466,7 +474,10 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid protected sendAttempt(lesson: any, password: string, attempt: any, result: AddonModLessonSyncResult, siteId?: string) : Promise { - return this.lessonProvider.processPageOnline(lesson.id, attempt.pageid, attempt.data, password, false, siteId).then(() => { + return this.lessonProvider.processPageOnline(lesson.id, attempt.pageid, attempt.data, { + password, + siteId, + }).then(() => { result.updated = true; return this.lessonOfflineProvider.deleteAttempt(lesson.id, attempt.retake, attempt.pageid, attempt.timemodified, diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts index a4db30c6f..148ad611c 100644 --- a/src/addon/mod/lesson/providers/lesson.ts +++ b/src/addon/mod/lesson/providers/lesson.ts @@ -16,14 +16,15 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreSitesProvider, CoreSiteSchema, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreGradesProvider } from '@core/grades/providers/grades'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { AddonModLessonOfflineProvider } from './lesson-offline'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Result of check answer. @@ -314,32 +315,31 @@ export class AddonModLessonProvider { * Calculate some offline data like progress and ongoingscore. * * @param lesson Lesson. - * @param accessInfo Result of get access info. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param pageIndex Object containing all the pages indexed by ID. If not defined, it will be calculated. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the data. */ - protected calculateOfflineData(lesson: any, accessInfo?: any, password?: string, review?: boolean, pageIndex?: any, - siteId?: string): Promise<{reviewmode: boolean, progress: number, ongoingscore: string}> { + protected calculateOfflineData(lesson: any, options: AddonModLessonCalculateOfflineDataOptions = {}) + : Promise<{reviewmode: boolean, progress: number, ongoingscore: string}> { - accessInfo = accessInfo || {}; - - const reviewMode = review || accessInfo.reviewmode, + const accessInfo = options.accessInfo || {}; + const reviewMode = options.review || accessInfo.reviewmode, promises = []; let ongoingMessage = '', progress: number; if (!accessInfo.canmanage) { if (lesson.ongoing && !reviewMode) { - promises.push(this.getOngoingScoreMessage(lesson, accessInfo, password, review, pageIndex, siteId) - .then((message) => { + promises.push(this.getOngoingScoreMessage(lesson, accessInfo, options).then((message) => { ongoingMessage = message; })); } if (lesson.progressbar) { - promises.push(this.calculateProgress(lesson.id, accessInfo, password, review, pageIndex, siteId).then((p) => { + const modOptions = { + cmId: lesson.coursemodule, + ...options, // Include all options. + }; + + promises.push(this.calculateProgress(lesson.id, accessInfo, modOptions).then((p) => { progress = p; })); } @@ -366,37 +366,45 @@ export class AddonModLessonProvider { * @param siteId Site ID. If not defined, current site. * @return Promise resolved with a number: the progress (scale 0-100). */ - calculateProgress(lessonId: number, accessInfo: any, password?: string, review?: boolean, pageIndex?: any, siteId?: string) - : Promise { + calculateProgress(lessonId: number, accessInfo: any, options: AddonModLessonCalculateProgressOptions = {}): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); // Check if the user is reviewing the attempt. - if (review) { + if (options.review) { return Promise.resolve(100); } const retake = accessInfo.attemptscount; - let viewedPagesIds, - promise; + const commonOptions = { + cmId: options.cmId, + siteId: options.siteId, + }; + let viewedPagesIds; + let promise; - if (pageIndex) { + if (options.pageIndex) { promise = Promise.resolve(); } else { // Retrieve the index. - promise = this.getPages(lessonId, password, true, false, siteId).then((pages) => { - pageIndex = this.createPagesIndex(pages); + promise = this.getPages(lessonId, { + cmId: options.cmId, + password: options.password, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((pages) => { + options.pageIndex = this.createPagesIndex(pages); }); } return promise.then(() => { // Get the list of question pages attempted. - return this.getPagesIdsWithQuestionAttempts(lessonId, retake, false, siteId); + return this.getPagesIdsWithQuestionAttempts(lessonId, retake, commonOptions); }).then((ids) => { viewedPagesIds = ids; // Get the list of viewed content pages. - return this.getContentPagesViewedIds(lessonId, retake, siteId); + return this.getContentPagesViewedIds(lessonId, retake, commonOptions); }).then((viewedContentPagesIds) => { const validPages = {}; let pageId = accessInfo.firstpageid; @@ -410,7 +418,7 @@ export class AddonModLessonProvider { // Do not filter out Cluster Page(s) because we count a cluster as one. // By keeping the cluster page, we get our 1. while (pageId) { - pageId = this.validPageAndView(pageIndex, pageIndex[pageId], validPages, viewedPagesIds); + pageId = this.validPageAndView(options.pageIndex, options.pageIndex[pageId], validPages, viewedPagesIds); } // Progress calculation as a percent. @@ -998,23 +1006,24 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param courseId Course ID the lesson belongs to. - * @param password Lesson password (if any). - * @param outOfTime If the user ran out of time. - * @param review If the user wants to review just after finishing (1 hour margin). - * @param offline Whether it's offline mode. - * @param accessInfo Result of get access info. Required if offline is true. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved in success, rejected otherwise. */ - finishRetake(lesson: any, courseId: number, password?: string, outOfTime?: boolean, review?: boolean, offline?: boolean, - accessInfo?: any, siteId?: string): Promise { + finishRetake(lesson: any, courseId: number, options: AddonModLessonFinishRetakeOptions = {}): Promise { - if (offline) { - const retake = accessInfo.attemptscount; + if (options.offline) { + const retake = options.accessInfo.attemptscount; + const newOptions = { + cmId: lesson.coursemodule, + password: options.password, + review: options.review, + siteId: options.siteId, + }; - return this.lessonOfflineProvider.finishRetake(lesson.id, courseId, retake, true, outOfTime, siteId).then(() => { + return this.lessonOfflineProvider.finishRetake(lesson.id, courseId, retake, true, options.outOfTime, options.siteId) + .then(() => { // Get the lesson grade. - return this.lessonGrade(lesson, retake, password, review, undefined, siteId).catch(() => { + return this.lessonGrade(lesson, retake, newOptions).catch(() => { // Ignore errors. return {}; }); @@ -1034,7 +1043,7 @@ export class AddonModLessonProvider { this.addResultValueEolPage(result, 'offline', true); // Mark the result as offline. this.addResultValueEolPage(result, 'gradeinfo', gradeInfo); - if (lesson.custom && !accessInfo.canmanage) { + if (lesson.custom && !options.accessInfo.canmanage) { /* Before we calculate the custom score make sure they answered the minimum number of questions. We only need to do this for custom scoring as we can not get the miniumum score the user should achieve. If we are not using custom scoring (so all questions are valued as 1) then we simply check if they @@ -1052,10 +1061,9 @@ export class AddonModLessonProvider { } } - if (!accessInfo.canmanage) { + if (!options.accessInfo.canmanage) { if (gradeLesson) { - promises.push(this.calculateProgress(lesson.id, accessInfo, password, review, undefined, siteId) - .then((progress) => { + promises.push(this.calculateProgress(lesson.id, options.accessInfo, newOptions).then((progress) => { this.addResultValueEolPage(result, 'progresscompleted', progress); })); @@ -1094,7 +1102,7 @@ export class AddonModLessonProvider { } else { // User hasn't answered any question, only content pages. if (lesson.timelimit) { - if (outOfTime) { + if (options.outOfTime) { this.addResultValueEolPage(result, 'eolstudentoutoftimenoanswers', true, true); } } else { @@ -1109,7 +1117,7 @@ export class AddonModLessonProvider { } } - if (lesson.modattempts && accessInfo.canmanage) { + if (lesson.modattempts && options.accessInfo.canmanage) { this.addResultValueEolPage(result, 'modattemptsnoteacher', true, true); } @@ -1121,13 +1129,13 @@ export class AddonModLessonProvider { }); } - return this.finishRetakeOnline(lesson.id, password, outOfTime, review, siteId).then((response) => { + return this.finishRetakeOnline(lesson.id, options).then((response) => { this.eventsProvider.trigger(AddonModLessonProvider.DATA_SENT_EVENT, { lessonId: lesson.id, type: 'finish', courseId: courseId, - outOfTime: outOfTime, - review: review + outOfTime: options.outOfTime, + review: options.review, }, this.sitesProvider.getCurrentSiteId()); return response; @@ -1138,23 +1146,20 @@ export class AddonModLessonProvider { * Finishes a retake. It will fail if offline or cannot connect. * * @param lessonId Lesson ID. - * @param password Lesson password (if any). - * @param outOfTime If the user ran out of time. - * @param review If the user wants to review just after finishing (1 hour margin). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved in success, rejected otherwise. */ - finishRetakeOnline(lessonId: number, password?: string, outOfTime?: boolean, review?: boolean, siteId?: string): Promise { + finishRetakeOnline(lessonId: number, options: AddonModLessonFinishRetakeOnlineOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { lessonid: lessonId, - outoftime: outOfTime ? 1 : 0, - review: review ? 1 : 0 + outoftime: options.outOfTime ? 1 : 0, + review: options.review ? 1 : 0, }; - if (typeof password == 'string') { - params.password = password; + if (typeof options.password == 'string') { + params.password = options.password; } return site.write('mod_lesson_finish_attempt', params).then((response) => { @@ -1180,26 +1185,21 @@ export class AddonModLessonProvider { * Get the access information of a certain lesson. * * @param lessonId Lesson ID. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the access information. */ - getAccessInformation(lessonId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAccessInformation(lessonId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - lessonid: lessonId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAccessInformationCacheKey(lessonId) - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + }; + const preSets = { + cacheKey: this.getAccessInformationCacheKey(lessonId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_lesson_access_information', params, preSets); }); @@ -1220,10 +1220,11 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with an object with the online and offline viewed pages. */ - getContentPagesViewed(lessonId: number, retake: number, siteId?: string): Promise<{online: any[], offline: any[]}> { + getContentPagesViewed(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}) + : Promise<{online: any[], offline: any[]}> { const promises = [], type = AddonModLessonProvider.TYPE_STRUCTURE, result = { @@ -1232,12 +1233,12 @@ export class AddonModLessonProvider { }; // Get the online pages. - promises.push(this.getContentPagesViewedOnline(lessonId, retake, false, false, siteId).then((pages) => { + promises.push(this.getContentPagesViewedOnline(lessonId, retake, options).then((pages) => { result.online = pages; })); // Get the offline pages. - promises.push(this.lessonOfflineProvider.getRetakeAttemptsForType(lessonId, retake, type, siteId).catch(() => { + promises.push(this.lessonOfflineProvider.getRetakeAttemptsForType(lessonId, retake, type, options.siteId).catch(() => { return []; }).then((pages) => { result.offline = pages; @@ -1274,11 +1275,11 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with list of IDs. */ - getContentPagesViewedIds(lessonId: number, retake: number, siteId?: string): Promise { - return this.getContentPagesViewed(lessonId, retake, siteId).then((result) => { + getContentPagesViewedIds(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.getContentPagesViewed(lessonId, retake, options).then((result) => { const ids = {}, pages = result.online.concat(result.offline); @@ -1299,29 +1300,22 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the viewed pages. */ - getContentPagesViewedOnline(lessonId: number, retake: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getContentPagesViewedOnline(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - lessonid: lessonId, - lessonattempt: retake - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getContentPagesViewedCacheKey(lessonId, retake) - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + lessonattempt: retake, + }; + const preSets = { + cacheKey: this.getContentPagesViewedCacheKey(lessonId, retake), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_content_pages_viewed', params, preSets).then((result) => { return result.pages; @@ -1334,11 +1328,11 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the last content page viewed. */ - getLastContentPageViewed(lessonId: number, retake: number, siteId?: string): Promise { - return this.getContentPagesViewed(lessonId, retake, siteId).then((data) => { + getLastContentPageViewed(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.getContentPagesViewed(lessonId, retake, options).then((data) => { let lastPage, maxTime = 0; @@ -1368,22 +1362,22 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the last page seen. */ - getLastPageSeen(lessonId: number, retake: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getLastPageSeen(lessonId: number, retake: number, options: CoreCourseCommonModWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); let lastPageSeen: number; // Get the last question answered. - return this.lessonOfflineProvider.getLastQuestionPageAttempt(lessonId, retake, siteId).then((answer) => { + return this.lessonOfflineProvider.getLastQuestionPageAttempt(lessonId, retake, options.siteId).then((answer) => { if (answer) { lastPageSeen = answer.newpageid; } // Now get the last content page viewed. - return this.getLastContentPageViewed(lessonId, retake, siteId).then((page) => { + return this.getLastContentPageViewed(lessonId, retake, options).then((page) => { if (page) { if (answer) { if (page.timemodified > answer.timemodified) { @@ -1406,13 +1400,11 @@ export class AddonModLessonProvider { * * @param courseId Course ID. * @param cmid Course module ID. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the lesson is retrieved. */ - getLesson(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getLessonByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId); + getLesson(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getLessonByField(courseId, 'coursemodule', cmId, options); } /** @@ -1421,29 +1413,21 @@ export class AddonModLessonProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the lesson is retrieved. */ - protected getLessonByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + protected getLessonByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getLessonDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getLessonDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModLessonProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_lessons_by_courses', params, preSets).then((response) => { if (response && response.lessons) { @@ -1466,13 +1450,11 @@ export class AddonModLessonProvider { * * @param courseId Course ID. * @param id Lesson ID. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the lesson is retrieved. */ - getLessonById(courseId: number, id: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getLessonByField(courseId, 'id', id, forceCache, ignoreCache, siteId); + getLessonById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getLessonByField(courseId, 'id', id, options); } /** @@ -1489,34 +1471,25 @@ export class AddonModLessonProvider { * Get a lesson protected with password. * * @param lessonId Lesson ID. - * @param password Password. - * @param validatePassword If true, the function will fail if the password is wrong. - * If false, it will return a lesson with the basic data if password is wrong. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the lesson. */ - getLessonWithPassword(lessonId: number, password?: string, validatePassword: boolean = true, forceCache?: boolean, - ignoreCache?: boolean, siteId?: string): Promise { + getLessonWithPassword(lessonId: number, options: AddonModLessonGetWithPasswordOptions = {}): Promise { + const validatePassword = typeof options.validatePassword == 'undefined' ? true : options.validatePassword; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { - lessonid: lessonId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getLessonWithPasswordCacheKey(lessonId) - }; + lessonid: lessonId, + }; + const preSets = { + cacheKey: this.getLessonWithPasswordCacheKey(lessonId), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (typeof password == 'string') { - params.password = password; - } - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; + if (typeof options.password == 'string') { + params.password = options.password; } return site.read('mod_lesson_get_lesson', params, preSets).then((response) => { @@ -1574,24 +1547,20 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param accessInfo Result of get access info. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param pageIndex Object containing all the pages indexed by ID. If not provided, it will be calculated. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the ongoing score message. */ - getOngoingScoreMessage(lesson: any, accessInfo: any, password?: string, review?: boolean, pageIndex?: any, siteId?: string) - : Promise { + getOngoingScoreMessage(lesson: any, accessInfo: any, options: AddonModLessonGradeOptions = {}): Promise { if (accessInfo.canmanage) { return Promise.resolve(this.translate.instant('addon.mod_lesson.teacherongoingwarning')); } else { let retake = accessInfo.attemptscount; - if (review) { + if (options.review) { retake--; } - return this.lessonGrade(lesson, retake, password, review, pageIndex, siteId).then((gradeInfo) => { + return this.lessonGrade(lesson, retake, options).then((gradeInfo) => { const data: any = {}; if (lesson.custom) { @@ -1614,13 +1583,15 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param pageId Page ID. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of possible answers. */ - protected getPageAnswers(lesson: any, pageId: number, password?: string, review?: boolean, siteId?: string): Promise { - return this.getPageData(lesson, pageId, password, review, true, true, false, undefined, undefined, siteId).then((data) => { + protected getPageAnswers(lesson: any, pageId: number, options: AddonModLessonPwdReviewOptions = {}): Promise { + return this.getPageData(lesson, pageId, { + includeContents: true, + ...options, // Include all options. + readingStrategy: options.readingStrategy || CoreSitesReadingStrategy.PreferCache, + }).then((data) => { return data.answers; }); } @@ -1630,19 +1601,16 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param pageIds List of page IDs. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with an object containing the answers. */ - protected getPagesAnswers(lesson: any, pageIds: number[], password?: string, review?: boolean, siteId?: string) - : Promise { + protected getPagesAnswers(lesson: any, pageIds: number[], options: AddonModLessonPwdReviewOptions = {}): Promise { const answers = {}, promises = []; pageIds.forEach((pageId) => { - promises.push(this.getPageAnswers(lesson, pageId, password, review, siteId).then((pageAnswers) => { + promises.push(this.getPageAnswers(lesson, pageId, options).then((pageAnswers) => { pageAnswers.forEach((answer) => { // Include the pageid in each answer and add them to the final list. answer.pageid = pageId; @@ -1661,42 +1629,30 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param pageId Page ID. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param includeContents Include the page rendered contents. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param accessInfo Result of get access info. Required if offline is true. - * @param jumps Result of get pages possible jumps. Required if offline is true. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the page data. */ - getPageData(lesson: any, pageId: number, password?: string, review?: boolean, includeContents?: boolean, forceCache?: boolean, - ignoreCache?: boolean, accessInfo?: any, jumps?: any, siteId?: string): Promise { + getPageData(lesson: any, pageId: number, options: AddonModLessonGetPageDataOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { - lessonid: lesson.id, - pageid: Number(pageId), - review: review ? 1 : 0, - returncontents: includeContents ? 1 : 0 - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getPageDataCacheKey(lesson.id, pageId) - }; + lessonid: lesson.id, + pageid: Number(pageId), + review: options.review ? 1 : 0, + returncontents: options.includeContents ? 1 : 0, + }; + const preSets = { + cacheKey: this.getPageDataCacheKey(lesson.id, pageId), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (typeof password == 'string') { - params.password = password; + if (typeof options.password == 'string') { + params.password = options.password; } - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } - - if (review) { + if (options.review) { // Force online mode in review. preSets.getFromCache = false; preSets.saveToCache = false; @@ -1704,12 +1660,15 @@ export class AddonModLessonProvider { } return site.read('mod_lesson_get_page_data', params, preSets).then((data) => { - if (forceCache && accessInfo && data.page) { + if (preSets.omitExpires && options.accessInfo && data.page) { // Offline mode and valid page. Calculate the data that might be affected. - return this.calculateOfflineData(lesson, accessInfo, password, review, undefined, siteId).then((calcData) => { + return this.calculateOfflineData(lesson, options).then((calcData) => { Object.assign(data, calcData); - return this.getPageViewMessages(lesson, accessInfo, data.page, review, jumps, password, siteId); + return this.getPageViewMessages(lesson, options.accessInfo, data.page, options.jumps, { + password: options.password, + siteId: options.siteId, + }); }).then((messages) => { data.messages = messages; @@ -1747,32 +1706,25 @@ export class AddonModLessonProvider { * Get lesson pages. * * @param lessonId Lesson ID. - * @param password Lesson password (if any). - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the pages. */ - getPages(lessonId: number, password?: string, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { + getPages(lessonId: number, options: AddonModLessonPwdReviewOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { - lessonid: lessonId, - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getPagesCacheKey(lessonId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; + lessonid: lessonId, + }; + const preSets = { + cacheKey: this.getPagesCacheKey(lessonId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; - if (typeof password == 'string') { - params.password = password; - } - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; + if (typeof options.password == 'string') { + params.password = options.password; } return site.read('mod_lesson_get_pages', params, preSets).then((response) => { @@ -1795,27 +1747,21 @@ export class AddonModLessonProvider { * Get possible jumps for a lesson. * * @param lessonId Lesson ID. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the jumps. */ - getPagesPossibleJumps(lessonId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { + getPagesPossibleJumps(lessonId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - lessonid: lessonId, - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getPagesPossibleJumpsCacheKey(lessonId) - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + }; + const preSets = { + cacheKey: this.getPagesPossibleJumpsCacheKey(lessonId), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_pages_possible_jumps', params, preSets).then((response) => { // Index the jumps by page and jumpto. @@ -1889,15 +1835,13 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param correct True to only fetch correct attempts, false to get them all. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's user. + * @param options Other options. * @return Promise resolved with the IDs. */ - getPagesIdsWithQuestionAttempts(lessonId: number, retake: number, correct?: boolean, siteId?: string, userId?: number) + getPagesIdsWithQuestionAttempts(lessonId: number, retake: number, options: AddonModLessonGetPagesIdsWithAttemptsOptions = {}) : Promise { - return this.getQuestionsAttempts(lessonId, retake, correct, undefined, siteId, userId).then((result) => { + return this.getQuestionsAttempts(lessonId, retake, options).then((result) => { const ids = {}, attempts = result.online.concat(result.offline); @@ -1921,13 +1865,11 @@ export class AddonModLessonProvider { * @param lesson Lesson. * @param accessInfo Result of get access info. Required if offline is true. * @param page Page loaded. - * @param review If the user wants to review just after finishing (1 hour margin). * @param jumps Result of get pages possible jumps. - * @param password Lesson password (if any). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of messages. */ - getPageViewMessages(lesson: any, accessInfo: any, page: any, review: boolean, jumps: any, password?: string, siteId?: string) + getPageViewMessages(lesson: any, accessInfo: any, page: any, jumps: any, options: AddonModLessonGetPageViewMessagesOptions = {}) : Promise { const messages = []; @@ -1938,7 +1880,7 @@ export class AddonModLessonProvider { // Tell student how many questions they have seen, how many are required and their grade. const retake = accessInfo.attemptscount; - promise = this.lessonGrade(lesson, retake, password, review, undefined, siteId).then((gradeInfo) => { + promise = this.lessonGrade(lesson, retake, options).then((gradeInfo) => { if (gradeInfo.attempts) { if (gradeInfo.nquestions < lesson.minquestions) { this.addMessage(messages, 'addon.mod_lesson.numberofpagesviewednotice', {$a: { @@ -1947,7 +1889,7 @@ export class AddonModLessonProvider { }}); } - if (!review && !lesson.retake) { + if (!options.review && !lesson.retake) { this.addMessage(messages, 'addon.mod_lesson.numberofcorrectanswers', {$a: gradeInfo.earned}); if (lesson.grade != CoreGradesProvider.TYPE_NONE) { @@ -1986,13 +1928,10 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param correct True to only fetch correct attempts, false to get them all. - * @param pageId If defined, only get attempts on this page. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's user. + * @param options Other options. * @return Promise resolved with the questions attempts. */ - getQuestionsAttempts(lessonId: number, retake: number, correct?: boolean, pageId?: number, siteId?: string, userId?: number) + getQuestionsAttempts(lessonId: number, retake: number, options: AddonModLessonGetQuestionsAttemptsOptions = {}) : Promise<{online: any[], offline: any[]}> { const promises = [], @@ -2001,12 +1940,12 @@ export class AddonModLessonProvider { offline: [] }; - promises.push(this.getQuestionsAttemptsOnline(lessonId, retake, correct, pageId, false, false, siteId, userId) - .then((attempts) => { + promises.push(this.getQuestionsAttemptsOnline(lessonId, retake, options).then((attempts) => { result.online = attempts; })); - promises.push(this.lessonOfflineProvider.getQuestionsAttempts(lessonId, retake, correct, pageId, siteId).catch(() => { + promises.push(this.lessonOfflineProvider.getQuestionsAttempts(lessonId, retake, options.correct, options.pageId, + options.siteId).catch(() => { // Error, assume no attempts. return []; }).then((attempts) => { @@ -2045,46 +1984,37 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number. - * @param correct True to only fetch correct attempts, false to get them all. - * @param pageId If defined, only get attempts on this page. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's user. + * @param options Other options. * @return Promise resolved with the questions attempts. */ - getQuestionsAttemptsOnline(lessonId: number, retake: number, correct?: boolean, pageId?: number, forceCache?: boolean, - ignoreCache?: boolean, siteId?: string, userId?: number): Promise { + getQuestionsAttemptsOnline(lessonId: number, retake: number, options: AddonModLessonGetQuestionsAttemptsOptions = {}) + : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); // Don't pass "pageId" and "correct" params, they will be filtered locally. const params = { - lessonid: lessonId, - attempt: retake, - userid: userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getQuestionsAttemptsCacheKey(lessonId, retake, userId) - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + attempt: retake, + userid: userId, + }; + const preSets = { + cacheKey: this.getQuestionsAttemptsCacheKey(lessonId, retake, userId), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_questions_attempts', params, preSets).then((response) => { - if (pageId || correct) { + if (options.pageId || options.correct) { // Filter the attempts. return response.attempts.filter((attempt) => { - if (correct && !attempt.correct) { + if (options.correct && !attempt.correct) { return false; } - if (pageId && attempt.pageid != pageId) { + if (options.pageId && attempt.pageid != options.pageId) { return false; } @@ -2101,33 +2031,25 @@ export class AddonModLessonProvider { * Get the overview of retakes in a lesson (named "attempts overview" in Moodle). * * @param lessonId Lesson ID. - * @param groupId The group to get. If not defined, all participants. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the retakes overview. */ - getRetakesOverview(lessonId: number, groupId?: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getRetakesOverview(lessonId: number, options: AddonModLessonGroupOptions = {}): Promise { - groupId = groupId || 0; + const groupId = options.groupId || 0; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - lessonid: lessonId, - groupid: groupId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getRetakesOverviewCacheKey(lessonId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + groupid: groupId, + }; + const preSets = { + cacheKey: this.getRetakesOverviewCacheKey(lessonId, groupId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_attempts_overview', params, preSets).then((response) => { return response.data; @@ -2204,30 +2126,23 @@ export class AddonModLessonProvider { * Get lesson timers. * * @param lessonId Lesson ID. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's current user. + * @param options Other options. * @return Promise resolved with the pages. */ - getTimers(lessonId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + getTimers(lessonId: number, options: AddonModLessonUserOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - lessonid: lessonId, - userid: userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getTimersCacheKey(lessonId, userId) - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + userid: userId, + }; + const preSets = { + cacheKey: this.getTimersCacheKey(lessonId, userId), + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_user_timers', params, preSets).then((response) => { return response.timers; @@ -2331,34 +2246,26 @@ export class AddonModLessonProvider { * * @param lessonId Lesson ID. * @param retake Retake number - * @param userId User ID. Undefined for current user. - * @param forceCache Whether it should always return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the retake data. */ - getUserRetake(lessonId: number, retake: number, userId?: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getUserRetake(lessonId: number, retake: number, options: AddonModLessonUserOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - lessonid: lessonId, - userid: userId, - lessonattempt: retake - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getUserRetakeCacheKey(lessonId, userId, retake), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + lessonid: lessonId, + userid: userId, + lessonattempt: retake, + }; + const preSets = { + cacheKey: this.getUserRetakeCacheKey(lessonId, userId, retake), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModLessonProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_lesson_get_user_attempt', params, preSets); }); @@ -2870,15 +2777,10 @@ export class AddonModLessonProvider { * * @param lesson Lesson. * @param retake Retake number. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param pageIndex Object containing all the pages indexed by ID. If not provided, it will be calculated. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined, site's user. + * @param options Other options. * @return Promise resolved with the grade data. */ - lessonGrade(lesson: any, retake: number, password?: string, review?: boolean, pageIndex?: any, siteId?: string, - userId?: number): Promise { + lessonGrade(lesson: any, retake: number, options: AddonModLessonGradeOptions = {}): Promise { // Initialize all variables. let nViewed = 0, @@ -2890,7 +2792,11 @@ export class AddonModLessonProvider { earned = 0; // Get the questions attempts for the user. - return this.getQuestionsAttempts(lesson.id, retake, false, undefined, siteId, userId).then((attemptsData) => { + return this.getQuestionsAttempts(lesson.id, retake, { + cmId: lesson.coursemodule, + siteId: options.siteId, + userId: options.userId, + }).then((attemptsData) => { const attempts = attemptsData.online.concat(attemptsData.offline); if (!attempts.length) { @@ -2902,9 +2808,14 @@ export class AddonModLessonProvider { let promise; // Create the pageIndex if it isn't provided. - if (!pageIndex) { - promise = this.getPages(lesson.id, password, true, false, siteId).then((pages) => { - pageIndex = this.createPagesIndex(pages); + if (!options.pageIndex) { + promise = this.getPages(lesson.id, { + password: options.password, + cmId: lesson.coursemodule, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((pages) => { + options.pageIndex = this.createPagesIndex(pages); }); } else { promise = Promise.resolve(); @@ -2933,7 +2844,7 @@ export class AddonModLessonProvider { } // Get all the answers from the pages the user answered. - return this.getPagesAnswers(lesson, pageIds, password, review, siteId); + return this.getPagesAnswers(lesson, pageIds, options); }).then((answers) => { // Number of pages answered. nQuestions = Object.keys(attemptSet).length; @@ -2944,7 +2855,7 @@ export class AddonModLessonProvider { if (lesson.custom) { // If essay question, handle it, otherwise add to score. - if (pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { + if (options.pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { if (lastAttempt.useranswer && typeof lastAttempt.useranswer.score != 'undefined') { earned += lastAttempt.useranswer.score; } @@ -2959,7 +2870,7 @@ export class AddonModLessonProvider { }); // If essay question, increase numbers. - if (pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { + if (options.pageIndex[lastAttempt.pageid].qtype == AddonModLessonProvider.LESSON_PAGE_ESSAY) { nManual++; manualPoints++; } @@ -3046,31 +2957,32 @@ export class AddonModLessonProvider { * @param courseId Course ID the lesson belongs to. * @param pageData Result of getPageData for the page to process. * @param data Data to save. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param offline Whether it's offline mode. - * @param accessInfo Result of get access info. Required if offline is true. - * @param jumps Result of get pages possible jumps. Required if offline is true. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - processPage(lesson: any, courseId: number, pageData: any, data: any, password?: string, review?: boolean, offline?: boolean, - accessInfo?: boolean, jumps?: any, siteId?: string): Promise { + processPage(lesson: any, courseId: number, pageData: any, data: any, options: AddonModLessonProcessPageOptions = {}) + : Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); const page = pageData.page, pageId = page.id; let result, pageIndex; - if (offline) { + if (options.offline) { // Get the list of pages of the lesson. - return this.getPages(lesson.id, password, true, false, siteId).then((pages) => { + return this.getPages(lesson.id, { + cmId: lesson.coursemodule, + password: options.password, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).then((pages) => { pageIndex = this.createPagesIndex(pages); if (pageData.answers.length) { - return this.recordAttempt(lesson, courseId, pageData, data, review, accessInfo, jumps, pageIndex, siteId); + return this.recordAttempt(lesson, courseId, pageData, data, options.review, options.accessInfo, options.jumps, + pageIndex, options.siteId); } else { // The page has no answers so we will just progress to the next page (as set by newpageid). return { @@ -3080,15 +2992,21 @@ export class AddonModLessonProvider { } }).then((res) => { result = res; - result.newpageid = this.getNewPageId(pageData.page.id, result.newpageid, jumps); + result.newpageid = this.getNewPageId(pageData.page.id, result.newpageid, options.jumps); // Calculate some needed offline data. - return this.calculateOfflineData(lesson, accessInfo, password, review, pageIndex, siteId); + return this.calculateOfflineData(lesson, { + accessInfo: options.accessInfo, + password: options.password, + review: options.review, + pageIndex, + siteId: options.siteId, + }); }).then((calculatedData) => { // Add some default data to match the WS response. result.warnings = []; result.displaymenu = pageData.displaymenu; // Keep the same value since we can't calculate it in offline. - result.messages = this.getPageProcessMessages(lesson, accessInfo, result, review, jumps); + result.messages = this.getPageProcessMessages(lesson, options.accessInfo, result, options.review, options.jumps); result.sent = false; Object.assign(result, calculatedData); @@ -3096,13 +3014,13 @@ export class AddonModLessonProvider { }); } - return this.processPageOnline(lesson.id, pageId, data, password, review, siteId).then((response) => { + return this.processPageOnline(lesson.id, pageId, data, options).then((response) => { this.eventsProvider.trigger(AddonModLessonProvider.DATA_SENT_EVENT, { lessonId: lesson.id, type: 'process', courseId: courseId, pageId: pageId, - review: review + review: options.review, }, this.sitesProvider.getCurrentSiteId()); response.sent = true; @@ -3117,24 +3035,22 @@ export class AddonModLessonProvider { * @param lessonId Lesson ID. * @param pageId Page ID. * @param data Data to save. - * @param password Lesson password (if any). - * @param review If the user wants to review just after finishing (1 hour margin). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved in success, rejected otherwise. */ - processPageOnline(lessonId: number, pageId: number, data: any, password?: string, review?: boolean, siteId?: string) + processPageOnline(lessonId: number, pageId: number, data: any, options: AddonModLessonProcessPageOnlineOptions = {}) : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { lessonid: lessonId, pageid: pageId, data: this.utils.objectToArrayOfObjects(data, 'name', 'value', true), - review: review ? 1 : 0 + review: options.review ? 1 : 0, }; - if (typeof password == 'string') { - params.password = password; + if (typeof options.password == 'string') { + params.password = options.password; } return site.write('mod_lesson_process_page', params); @@ -3189,7 +3105,11 @@ export class AddonModLessonProvider { } else { if (!accessInfo.canmanage) { // Get the number of attempts that have been made on this question for this student and retake. - promise = this.getQuestionsAttempts(lesson.id, retake, false, pageData.page.id, siteId).then((attempts) => { + promise = this.getQuestionsAttempts(lesson.id, retake, { + cmId: lesson.coursemodule, + pageId: pageData.page.id, + siteId, + }).then((attempts) => { nAttempts = attempts.online.length + attempts.offline.length; // Check if they have reached (or exceeded) the maximum number of attempts allowed. @@ -3264,8 +3184,11 @@ export class AddonModLessonProvider { if (lesson.review && !result.correctanswer && !result.isessayquestion) { // Calculate the number of question attempt in the page if it isn't calculated already. if (typeof nAttempts == 'undefined') { - subPromise = this.getQuestionsAttempts(lesson.id, retake, false, pageData.page.id, siteId) - .then((result) => { + subPromise = this.getQuestionsAttempts(lesson.id, retake, { + cmId: lesson.coursemodule, + pageId: pageData.page.id, + siteId, + }).then((result) => { nAttempts = result.online.length + result.offline.length; }); } else { @@ -3399,3 +3322,141 @@ export class AddonModLessonProvider { return page.nextpageid; } } + +/** + * Common options including a group ID. + */ +export type AddonModLessonGroupOptions = CoreCourseCommonModWSOptions & { + groupId?: number; // The group to get. If not defined, all participants. +}; + +/** + * Common options including a group ID. + */ +export type AddonModLessonUserOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User ID. If not defined, site's current user. +}; + +/** + * Common options including a password. + */ +export type AddonModLessonPasswordOptions = CoreCourseCommonModWSOptions & { + password?: string; // Lesson password (if any). +}; + +/** + * Common options including password and review. + */ +export type AddonModLessonPwdReviewOptions = AddonModLessonPasswordOptions & { + review?: boolean; // If the user wants to review just after finishing (1 hour margin). +}; + +/** + * Options to pass to get lesson with password. + */ +export type AddonModLessonGetWithPasswordOptions = AddonModLessonPasswordOptions & { + validatePassword?: boolean; // Defauls to true. If true, the function will fail if the password is wrong. + // If false, it will return a lesson with the basic data if password is wrong. +}; + +/** + * Options to pass to calculateProgress. + */ +export type AddonModLessonCalculateProgressBasicOptions = { + password?: string; // Lesson password (if any). + review?: boolean; // If the user wants to review just after finishing (1 hour margin). + pageIndex?: any; // Object containing all the pages indexed by ID. If not provided, it will be calculated. + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to calculateProgress. + */ +export type AddonModLessonCalculateProgressOptions = AddonModLessonCalculateProgressBasicOptions & { + cmId?: number; // Module ID. +}; + +/** + * Options to pass to lessonGrade. + */ +export type AddonModLessonGradeOptions = AddonModLessonCalculateProgressBasicOptions & { + userId?: number; // User ID. If not defined, site's user. +}; + +/** + * Options to pass to calculateOfflineData. + */ +export type AddonModLessonCalculateOfflineDataOptions = AddonModLessonCalculateProgressBasicOptions & { + accessInfo?: any; // Result of get access info. +}; + +/** + * Options to pass to get page data. + */ +export type AddonModLessonGetPageDataOptions = AddonModLessonPwdReviewOptions & { + includeContents?: boolean; // Include the page rendered contents. + accessInfo?: any; // Result of get access info. Required if offline is true. + jumps?: any; // Result of get pages possible jumps. Required if offline is true. +}; + +/** + * Options to pass to get page data. + */ +export type AddonModLessonGetPageViewMessagesOptions = { + password?: string; // Lesson password (if any). + review?: boolean; // If the user wants to review just after finishing (1 hour margin). + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to get questions attempts. + */ +export type AddonModLessonGetQuestionsAttemptsOptions = CoreCourseCommonModWSOptions & { + correct?: boolean; // True to only fetch correct attempts, false to get them all. + pageId?: number; // If defined, only get attempts on this page. + userId?: number; // User ID. If not defined, site's user. +}; + +/** + * Options to pass to getPagesIdsWithQuestionAttempts. + */ +export type AddonModLessonGetPagesIdsWithAttemptsOptions = CoreCourseCommonModWSOptions & { + correct?: boolean; // True to only fetch correct attempts, false to get them all. + userId?: number; // User ID. If not defined, site's user. +}; + +/** + * Options to pass to processPageOnline. + */ +export type AddonModLessonProcessPageOnlineOptions = { + password?: string; // Lesson password (if any). + review?: boolean; // If the user wants to review just after finishing (1 hour margin). + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to processPage. + */ +export type AddonModLessonProcessPageOptions = AddonModLessonProcessPageOnlineOptions & { + offline?: boolean; // Whether it's offline mode. + accessInfo?: any; // Result of get access info. Required if offline is true. + jumps?: any; // Result of get pages possible jumps. Required if offline is true. +}; + +/** + * Options to pass to finishRetakeOnline. + */ +export type AddonModLessonFinishRetakeOnlineOptions = { + password?: string; // Lesson password (if any). + outOfTime?: boolean; // Whether the user ran out of time. + review?: boolean; // If the user wants to review just after finishing (1 hour margin). + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to finishRetake. + */ +export type AddonModLessonFinishRetakeOptions = AddonModLessonFinishRetakeOnlineOptions & { + offline?: boolean; // Whether it's offline mode. + accessInfo?: any; // Result of get access info. Required if offline is true. +}; diff --git a/src/addon/mod/lesson/providers/prefetch-handler.ts b/src/addon/mod/lesson/providers/prefetch-handler.ts index c8601ae58..1550e4143 100644 --- a/src/addon/mod/lesson/providers/prefetch-handler.ts +++ b/src/addon/mod/lesson/providers/prefetch-handler.ts @@ -17,10 +17,10 @@ import { ModalController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; import { CoreGroupsProvider } from '@providers/groups'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { AddonModLessonProvider } from './lesson'; @@ -98,11 +98,15 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan password, result; - return this.lessonProvider.getLesson(courseId, module.id, false, false, siteId).then((lessonData) => { + return this.lessonProvider.getLesson(courseId, module.id, {siteId}).then((lessonData) => { lesson = lessonData; // Get the lesson password if it's needed. - return this.getLessonPassword(lesson.id, false, true, single, siteId); + return this.getLessonPassword(lesson.id, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + askPassword: single, + siteId, + }); }).then((data) => { password = data.password; lesson = data.lesson || lesson; @@ -116,7 +120,11 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan result = res; // Get the pages to calculate the size. - return this.lessonProvider.getPages(lesson.id, password, false, false, siteId); + return this.lessonProvider.getPages(lesson.id, { + cmId: module.id, + password, + siteId, + }); }).then((pages) => { pages.forEach((page) => { result.size += page.filessizetotal; @@ -130,19 +138,16 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan * Get the lesson password if needed. If not stored, it can ask the user to enter it. * * @param lessonId Lesson ID. - * @param forceCache Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param askPassword True if we should ask for password if needed, false otherwise. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - getLessonPassword(lessonId: number, forceCache?: boolean, ignoreCache?: boolean, askPassword?: boolean, siteId?: string) + getLessonPassword(lessonId: number, options: AddonModLessonGetPasswordOptions = {}) : Promise<{password?: string, lesson?: any, accessInfo: any}> { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); // Get access information to check if password is needed. - return this.lessonProvider.getAccessInformation(lessonId, forceCache, ignoreCache, siteId).then((info): any => { + return this.lessonProvider.getAccessInformation(lessonId, options).then((info): any => { if (info.preventaccessreasons && info.preventaccessreasons.length) { const passwordNeeded = info.preventaccessreasons.length == 1 && this.lessonProvider.isPasswordProtected(info); if (passwordNeeded) { @@ -152,15 +157,15 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan // No password found. }).then((password) => { if (password) { - return this.validatePassword(lessonId, info, password, forceCache, ignoreCache, siteId); + return this.validatePassword(lessonId, info, password, options); } else { return Promise.reject(null); } }).catch(() => { // No password or error validating it. Ask for it if allowed. - if (askPassword) { + if (options.askPassword) { return this.askUserPassword(info).then((password) => { - return this.validatePassword(lessonId, info, password, forceCache, ignoreCache, siteId); + return this.validatePassword(lessonId, info, password, options); }); } @@ -207,7 +212,10 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan const siteId = this.sitesProvider.getCurrentSiteId(); // Invalidate data to determine if module is downloadable. - return this.lessonProvider.getLesson(courseId, module.id, true, false, siteId).then((lesson) => { + return this.lessonProvider.getLesson(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }).then((lesson) => { const promises = []; promises.push(this.lessonProvider.invalidateLessonData(courseId, siteId)); @@ -227,9 +235,9 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan isDownloadable(module: any, courseId: number): boolean | Promise { const siteId = this.sitesProvider.getCurrentSiteId(); - return this.lessonProvider.getLesson(courseId, module.id, false, false, siteId).then((lesson) => { + return this.lessonProvider.getLesson(courseId, module.id, {siteId}).then((lesson) => { // Check if there is any prevent access reason. - return this.lessonProvider.getAccessInformation(lesson.id, false, false, siteId).then((info) => { + return this.lessonProvider.getAccessInformation(lesson.id, {cmId: module.id, siteId}).then((info) => { if (!info.canviewreports && !this.lessonProvider.isLessonOffline(lesson)) { return false; } @@ -273,15 +281,28 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan * @return Promise resolved when done. */ protected prefetchLesson(module: any, courseId: number, single: boolean, siteId: string): Promise { - let lesson, - password, - accessInfo; + let lesson; + let password; + let accessInfo; - return this.lessonProvider.getLesson(courseId, module.id, false, true, siteId).then((lessonData) => { + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + + return this.lessonProvider.getLesson(courseId, module.id, commonOptions).then((lessonData) => { lesson = lessonData; // Get the lesson password if it's needed. - return this.getLessonPassword(lesson.id, false, true, single, siteId); + return this.getLessonPassword(lesson.id, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + askPassword: single, + siteId, + }); }).then((data) => { password = data.password; lesson = data.lesson || lesson; @@ -297,7 +318,7 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan // Ignore errors. })); - promises.push(this.lessonProvider.getAccessInformation(lesson.id, false, true, siteId).then((info) => { + promises.push(this.lessonProvider.getAccessInformation(lesson.id, modOptions).then((info) => { accessInfo = info; })); @@ -316,7 +337,12 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan // Get the list of pages. if (this.lessonProvider.isLessonOffline(lesson)) { - promises.push(this.lessonProvider.getPages(lesson.id, password, false, true, siteId).then((pages) => { + const passwordOptions = { + password, + ...modOptions, // Include all mod options. + }; + + promises.push(this.lessonProvider.getPages(lesson.id, passwordOptions).then((pages) => { const subPromises = []; let hasRandomBranch = false; @@ -333,8 +359,10 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan } // Get the page data. We don't pass accessInfo because we don't need to calculate the offline data. - subPromises.push(this.lessonProvider.getPageData(lesson, data.page.id, password, false, true, false, - true, undefined, undefined, siteId).then((pageData) => { + subPromises.push(this.lessonProvider.getPageData(lesson, data.page.id, { + includeContents: true, + ...passwordOptions, // Include all options. + }).then((pageData) => { // Download the page files. let pageFiles = pageData.contentfiles || []; @@ -353,7 +381,7 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan }); // Prefetch the list of possible jumps for offline navigation. Do it here because we know hasRandomBranch. - subPromises.push(this.lessonProvider.getPagesPossibleJumps(lesson.id, false, true, siteId).catch((error) => { + subPromises.push(this.lessonProvider.getPagesPossibleJumps(lesson.id, modOptions).catch((error) => { if (hasRandomBranch) { // The WebSevice probably failed because RANDOMBRANCH aren't supported if the user hasn't seen any page. return Promise.reject(this.translate.instant('addon.mod_lesson.errorprefetchrandombranch')); @@ -366,16 +394,15 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan })); // Prefetch user timers to be able to calculate timemodified in offline. - promises.push(this.lessonProvider.getTimers(lesson.id, false, true, siteId).catch(() => { + promises.push(this.lessonProvider.getTimers(lesson.id, modOptions).catch(() => { // Ignore errors. })); // Prefetch viewed pages in last retake to calculate progress. - promises.push(this.lessonProvider.getContentPagesViewedOnline(lesson.id, retake, false, true, siteId)); + promises.push(this.lessonProvider.getContentPagesViewedOnline(lesson.id, retake, modOptions)); // Prefetch question attempts in last retake for offline calculations. - promises.push(this.lessonProvider.getQuestionsAttemptsOnline(lesson.id, retake, false, undefined, false, true, - siteId)); + promises.push(this.lessonProvider.getQuestionsAttemptsOnline(lesson.id, retake, modOptions)); } if (accessInfo.canviewreports) { @@ -384,11 +411,14 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan const subPromises = []; info.groups.forEach((group) => { - subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, group.id, false, true, siteId)); + subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, { + groupId: group.id, + ...modOptions, // Include all options. + })); }); - // Always get group 0, even if there are no groups. - subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, 0, false, true, siteId).then((data) => { + // Always get all participants, even if there are no groups. + subPromises.push(this.lessonProvider.getRetakesOverview(lesson.id, modOptions).then((data) => { if (!data || !data.students) { return; } @@ -406,8 +436,10 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan return; } - retakePromises.push(this.lessonProvider.getUserRetake(lesson.id, lastRetake.try, student.id, false, - true, siteId).then((attempt) => { + retakePromises.push(this.lessonProvider.getUserRetake(lesson.id, lastRetake.try, { + userId: student.id, + ...modOptions, // Include all options. + }).then((attempt) => { if (!attempt || !attempt.answerpages) { return; } @@ -445,19 +477,20 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan * @param lessonId Lesson ID. * @param info Lesson access info. * @param pwd Password to check. - * @param forceCache Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - protected validatePassword(lessonId: number, info: any, pwd: string, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise<{password: string, lesson: any, accessInfo: any}> { + protected validatePassword(lessonId: number, info: any, pwd: string, options: CoreCourseCommonModWSOptions = {}) + : Promise<{password: string, lesson: any, accessInfo: any}> { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.lessonProvider.getLessonWithPassword(lessonId, pwd, true, forceCache, ignoreCache, siteId).then((lesson) => { + return this.lessonProvider.getLessonWithPassword(lessonId, { + password: pwd, + ...options, // Include all options. + }).then((lesson) => { // Password is ok, store it and return the data. - return this.lessonProvider.storePassword(lesson.id, pwd, siteId).then(() => { + return this.lessonProvider.storePassword(lesson.id, pwd, options.siteId).then(() => { return { password: pwd, lesson: lesson, @@ -483,3 +516,10 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan return this.syncProvider.syncLesson(module.instance, false, false, siteId); } } + +/** + * Options to pass to get lesson password. + */ +export type AddonModLessonGetPasswordOptions = CoreCourseCommonModWSOptions & { + askPassword?: boolean; // True if we should ask for password if needed, false otherwise. +}; diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts index acae1b745..b2d301078 100644 --- a/src/addon/mod/lti/providers/lti.ts +++ b/src/addon/mod/lti/providers/lti.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFileProvider } from '@providers/file'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUrlUtilsProvider } from '@providers/utils/url'; @@ -100,29 +100,32 @@ export class AddonModLtiProvider { * * @param courseId Course ID. * @param cmId Course module ID. + * @param options Other options. * @return Promise resolved when the LTI is retrieved. */ - getLti(courseId: number, cmId: number): Promise { + async getLti(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { const params: any = { courseids: [courseId] }; - const preSets: any = { + const preSets = { cacheKey: this.getLtiCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModLtiProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - return this.sitesProvider.getCurrentSite().read('mod_lti_get_ltis_by_courses', params, preSets) - .then((response: AddonModLtiGetLtisByCoursesResult): any => { + const site = await this.sitesProvider.getSite(options.siteId); - if (response.ltis) { - const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId); - if (currentLti) { - return currentLti; - } + const response: AddonModLtiGetLtisByCoursesResult = await site.read('mod_lti_get_ltis_by_courses', params, preSets); + + if (response.ltis) { + const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId); + if (currentLti) { + return currentLti; } + } - return Promise.reject(null); - }); + throw new Error('Activity not found.'); } /** diff --git a/src/addon/mod/page/providers/page.ts b/src/addon/mod/page/providers/page.ts index 817e848b3..2fab4bcce 100644 --- a/src/addon/mod/page/providers/page.ts +++ b/src/addon/mod/page/providers/page.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; @@ -43,11 +43,11 @@ export class AddonModPageProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the page is retrieved. */ - getPageData(courseId: number, cmId: number, siteId?: string): Promise { - return this.getPageByKey(courseId, 'coursemodule', cmId, siteId); + getPageData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getPageByKey(courseId, 'coursemodule', cmId, options); } /** @@ -56,18 +56,21 @@ export class AddonModPageProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the page is retrieved. */ - protected getPageByKey(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getPageByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getPageCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getPageCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModPageProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_page_get_pages_by_courses', params, preSets) .then((response: AddonModPageGetPagesByCoursesResult): any => { diff --git a/src/addon/mod/quiz/components/index/index.ts b/src/addon/mod/quiz/components/index/index.ts index 3a2157138..d970c5e88 100644 --- a/src/addon/mod/quiz/components/index/index.ts +++ b/src/addon/mod/quiz/components/index/index.ts @@ -202,7 +202,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp } // Get quiz access info. - return this.quizProvider.getQuizAccessInformation(this.quizData.id).then((info) => { + return this.quizProvider.getQuizAccessInformation(this.quizData.id, {cmId: this.module.id}).then((info) => { this.quizAccessInfo = info; this.quizData.showReviewColumn = info.canreviewmyattempts; this.accessRules = info.accessrules; @@ -213,7 +213,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp } // Get question types in the quiz. - return this.quizProvider.getQuizRequiredQtypes(this.quizData.id).then((types) => { + return this.quizProvider.getQuizRequiredQtypes(this.quizData.id, {cmId: this.module.id}).then((types) => { this.unsupportedQuestions = this.quizProvider.getUnsupportedQuestions(types); this.hasSupportedQuestions = !!types.find((type) => { return type != 'random' && this.unsupportedQuestions.indexOf(type) == -1; @@ -239,11 +239,11 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp protected getAttempts(): Promise { // Get access information of last attempt (it also works if no attempts made). - return this.quizProvider.getAttemptAccessInformation(this.quizData.id, 0).then((info) => { + return this.quizProvider.getAttemptAccessInformation(this.quizData.id, 0, {cmId: this.module.id}).then((info) => { this.attemptAccessInfo = info; // Get attempts. - return this.quizProvider.getUserAttempts(this.quizData.id).then((atts) => { + return this.quizProvider.getUserAttempts(this.quizData.id, {cmId: this.module.id}).then((atts) => { return this.treatAttempts(atts).then((atts) => { this.attempts = atts; @@ -355,7 +355,9 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp if (this.quizData.showFeedbackColumn) { // Get the quiz overall feedback. - return this.quizProvider.getFeedbackForGrade(this.quizData.id, this.gradebookData.grade).then((response) => { + return this.quizProvider.getFeedbackForGrade(this.quizData.id, this.gradebookData.grade, { + cmId: this.module.id, + }).then((response) => { this.overallFeedback = response.feedbacktext; }); } @@ -379,7 +381,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp const attemptId = this.autoReview.attemptId; if (this.quizAccessInfo.canreviewmyattempts) { - return this.quizProvider.getAttemptReview(attemptId, -1).then(() => { + return this.quizProvider.getAttemptReview(attemptId, {page: -1, cmId: this.module.id}).then(() => { this.navCtrl.push('AddonModQuizReviewPage', {courseId: this.courseId, quizId: this.quizData.id, attemptId}); }).catch(() => { // Ignore errors. @@ -559,12 +561,12 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp promises.push(this.quizProvider.loadFinishedOfflineData(attempts)); // Get combined review options. - promises.push(this.quizProvider.getCombinedReviewOptions(this.quizData.id).then((result) => { + promises.push(this.quizProvider.getCombinedReviewOptions(this.quizData.id, {cmId: this.module.id}).then((result) => { this.options = result; })); // Get best grade. - promises.push(this.quizProvider.getUserBestGrade(this.quizData.id).then((best) => { + promises.push(this.quizProvider.getUserBestGrade(this.quizData.id, {cmId: this.module.id}).then((best) => { this.bestGrade = best; // Get gradebook grade. diff --git a/src/addon/mod/quiz/pages/attempt/attempt.ts b/src/addon/mod/quiz/pages/attempt/attempt.ts index dae185cc3..d4e3d1909 100644 --- a/src/addon/mod/quiz/pages/attempt/attempt.ts +++ b/src/addon/mod/quiz/pages/attempt/attempt.ts @@ -92,7 +92,7 @@ export class AddonModQuizAttemptPage implements OnInit { accessInfo; // Get all the attempts and search the one we want. - promises.push(this.quizProvider.getUserAttempts(this.quizId).then((attempts) => { + promises.push(this.quizProvider.getUserAttempts(this.quizId, {cmId: this.quiz.coursemodule}).then((attempts) => { for (let i = 0; i < attempts.length; i++) { const attempt = attempts[i]; if (attempt.id == this.attemptId) { @@ -110,12 +110,13 @@ export class AddonModQuizAttemptPage implements OnInit { return this.quizProvider.loadFinishedOfflineData([this.attempt]); })); - promises.push(this.quizProvider.getCombinedReviewOptions(this.quiz.id).then((opts) => { + promises.push(this.quizProvider.getCombinedReviewOptions(this.quiz.id, {cmId: this.quiz.coursemodule}).then((opts) => { options = opts; })); // Check if the user can review the attempt. - promises.push(this.quizProvider.getQuizAccessInformation(this.quiz.id).then((quizAccessInfo) => { + promises.push(this.quizProvider.getQuizAccessInformation(this.quiz.id, {cmId: this.quiz.coursemodule}) + .then((quizAccessInfo) => { accessInfo = quizAccessInfo; if (accessInfo.canreviewmyattempts) { @@ -123,7 +124,7 @@ export class AddonModQuizAttemptPage implements OnInit { return this.quizProvider.invalidateAttemptReviewForPage(this.attemptId, -1).catch(() => { // Ignore errors. }).then(() => { - return this.quizProvider.getAttemptReview(this.attemptId, -1); + return this.quizProvider.getAttemptReview(this.attemptId, {page: -1, cmId: this.quiz.coursemodule}); }).catch(() => { // Error getting the review, assume the user cannot review the attempt. accessInfo.canreviewmyattempts = false; @@ -146,7 +147,9 @@ export class AddonModQuizAttemptPage implements OnInit { options.someoptions.overallfeedback && !isNaN(grade)) { // Feedback should be displayed, get the feedback for the grade. - return this.quizProvider.getFeedbackForGrade(this.quiz.id, grade).then((response) => { + return this.quizProvider.getFeedbackForGrade(this.quiz.id, grade, { + cmId: this.quiz.coursemodule, + }).then((response) => { this.attempt.feedback = response.feedbacktext; }); } else { diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts index 653a9b89a..75fb54312 100644 --- a/src/addon/mod/quiz/pages/player/player.ts +++ b/src/addon/mod/quiz/pages/player/player.ts @@ -17,7 +17,7 @@ import { IonicPage, NavParams, Content, PopoverController, ModalController, Moda import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -315,12 +315,18 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { } // Get access information for the quiz. - return this.quizProvider.getQuizAccessInformation(this.quiz.id, this.offline, true); + return this.quizProvider.getQuizAccessInformation(this.quiz.id, { + cmId: this.quiz.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }); }).then((info) => { this.quizAccessInfo = info; // Get user attempts to determine last attempt. - return this.quizProvider.getUserAttempts(this.quiz.id, 'all', true, this.offline, true); + return this.quizProvider.getUserAttempts(this.quiz.id, { + cmId: this.quiz.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }); }).then((attempts) => { if (!attempts.length) { // There are no attempts, start a new one. @@ -396,8 +402,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { */ protected fixSequenceChecks(): Promise { // Get current page data again to get the latest sequencechecks. - return this.quizProvider.getAttemptData(this.attempt.id, this.attempt.currentpage, this.preflightData, this.offline, true) - .then((data) => { + return this.quizProvider.getAttemptData(this.attempt.id, this.attempt.currentpage, this.preflightData, { + cmId: this.quiz.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }).then((data) => { const newSequenceChecks = {}; @@ -443,7 +451,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { * @return Promise resolved when done. */ protected loadPage(page: number): Promise { - return this.quizProvider.getAttemptData(this.attempt.id, page, this.preflightData, this.offline, true).then((data) => { + return this.quizProvider.getAttemptData(this.attempt.id, page, this.preflightData, { + cmId: this.quiz.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }).then((data) => { // Update attempt, status could change during the execution. this.attempt = data.attempt; this.attempt.currentpage = page; @@ -487,7 +498,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { protected loadSummary(): Promise { this.summaryQuestions = []; - return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, this.offline, true, true).then((qs) => { + return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, { + cmId: this.quiz.coursemodule, + loadLocal: this.offline, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }).then((qs) => { this.showSummary = true; this.summaryQuestions = qs; @@ -511,8 +526,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { */ protected loadNavigation(): Promise { // We use the attempt summary to build the navigation because it contains all the questions. - return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, this.offline, true, true) - .then((questions) => { + return this.quizProvider.getAttemptSummary(this.attempt.id, this.preflightData, { + cmId: this.quiz.coursemodule, + loadLocal: this.offline, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }).then((questions) => { questions.forEach((question) => { question.stateClass = this.questionHelper.getQuestionStateClass(question.state); @@ -652,7 +670,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { false, 'addon.mod_quiz.startattempt').then((attempt) => { // Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created). - return this.quizProvider.getAttemptAccessInformation(this.quiz.id, attempt.id, this.offline, true).then((info) => { + return this.quizProvider.getAttemptAccessInformation(this.quiz.id, attempt.id, { + cmId: this.quiz.coursemodule, + readingStrategy: this.offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + }).then((info) => { this.attemptAccessInfo = info; this.attempt = attempt; diff --git a/src/addon/mod/quiz/pages/review/review.ts b/src/addon/mod/quiz/pages/review/review.ts index 5c2ef225d..50dfd53b8 100644 --- a/src/addon/mod/quiz/pages/review/review.ts +++ b/src/addon/mod/quiz/pages/review/review.ts @@ -134,7 +134,7 @@ export class AddonModQuizReviewPage implements OnInit { this.quiz = quizData; this.componentId = this.quiz.coursemodule; - return this.quizProvider.getCombinedReviewOptions(this.quizId).then((result) => { + return this.quizProvider.getCombinedReviewOptions(this.quizId, {cmId: this.quiz.coursemodule}).then((result) => { this.options = result; // Load the navigation data. @@ -155,7 +155,7 @@ export class AddonModQuizReviewPage implements OnInit { * @return Promise resolved when done. */ protected loadPage(page: number): Promise { - return this.quizProvider.getAttemptReview(this.attemptId, page).then((data) => { + return this.quizProvider.getAttemptReview(this.attemptId, {page, cmId: this.quiz.coursemodule}).then((data) => { this.attempt = data.attempt; this.attempt.currentpage = page; this.currentPage = page; @@ -187,7 +187,7 @@ export class AddonModQuizReviewPage implements OnInit { */ protected loadNavigation(): Promise { // Get all questions in single page to retrieve all the questions. - return this.quizProvider.getAttemptReview(this.attemptId, -1).then((data) => { + return this.quizProvider.getAttemptReview(this.attemptId, {page: -1, cmId: this.quiz.coursemodule}).then((data) => { const lastQuestion = data.questions[data.questions.length - 1]; data.questions.forEach((question) => { diff --git a/src/addon/mod/quiz/providers/helper.ts b/src/addon/mod/quiz/providers/helper.ts index c83293381..084cb6303 100644 --- a/src/addon/mod/quiz/providers/helper.ts +++ b/src/addon/mod/quiz/providers/helper.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { ModalController, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { AddonModQuizProvider } from './quiz'; @@ -166,12 +166,12 @@ export class AddonModQuizHelperProvider { * Get a quiz ID by attempt ID. * * @param attemptId Attempt ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the quiz ID. */ - getQuizIdByAttemptId(attemptId: number, siteId?: string): Promise { + getQuizIdByAttemptId(attemptId: number, options: {cmId?: number, siteId?: string} = {}): Promise { // Use getAttemptReview to retrieve the quiz ID. - return this.quizProvider.getAttemptReview(attemptId, undefined, false, siteId).then((reviewData) => { + return this.quizProvider.getAttemptReview(attemptId, options).then((reviewData) => { if (reviewData.attempt && reviewData.attempt.quiz) { return reviewData.attempt.quiz; } @@ -202,7 +202,7 @@ export class AddonModQuizHelperProvider { promise = Promise.resolve(quizId); } else { // Retrieve the quiz ID using the attempt ID. - promise = this.getQuizIdByAttemptId(attemptId); + promise = this.getQuizIdByAttemptId(attemptId, {siteId}); } return promise.then((id) => { @@ -298,6 +298,11 @@ export class AddonModQuizHelperProvider { siteId?: string): Promise { const rules = accessInfo && accessInfo.activerulenames; + const modOptions = { + cmId: quiz.coursemodule, + readingStrategy: offline ? CoreSitesReadingStrategy.PreferCache : CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; let promise; if (attempt) { @@ -305,7 +310,7 @@ export class AddonModQuizHelperProvider { // We're continuing an attempt. Call getAttemptData to validate the preflight data. const page = attempt.currentpage; - promise = this.quizProvider.getAttemptData(attempt.id, page, preflightData, offline, true, siteId).then(() => { + promise = this.quizProvider.getAttemptData(attempt.id, page, preflightData, modOptions).then(() => { if (offline) { // Get current page stored in local. return this.quizOfflineProvider.getAttemptById(attempt.id).then((localAttempt) => { @@ -318,7 +323,7 @@ export class AddonModQuizHelperProvider { } else { // Attempt is overdue or finished in offline, we can only see the summary. // Call getAttemptSummary to validate the preflight data. - promise = this.quizProvider.getAttemptSummary(attempt.id, preflightData, offline, true, false, siteId); + promise = this.quizProvider.getAttemptSummary(attempt.id, preflightData, modOptions); } } else { // We're starting a new attempt, call startAttempt. diff --git a/src/addon/mod/quiz/providers/prefetch-handler.ts b/src/addon/mod/quiz/providers/prefetch-handler.ts index f1832e11d..d0847f847 100644 --- a/src/addon/mod/quiz/providers/prefetch-handler.ts +++ b/src/addon/mod/quiz/providers/prefetch-handler.ts @@ -16,10 +16,10 @@ import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCourseProvider, CoreCourseCommonModWSOptions } from '@core/course/providers/course'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreQuestionHelperProvider } from '@core/question/providers/helper'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; @@ -90,7 +90,10 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl return this.quizProvider.getQuiz(courseId, module.id).then((quiz) => { const files = this.getIntroFilesFromInstance(module, quiz); - return this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true).then((attempts) => { + return this.quizProvider.getUserAttempts(quiz.id, { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }).then((attempts) => { return this.getAttemptsFeedbackFiles(quiz, attempts).then((attemptFiles) => { return files.concat(attemptFiles); }); @@ -106,9 +109,10 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl * * @param quiz Quiz. * @param attempts Quiz user attempts. + * @param siteId Site ID. If not defined, current site. * @return List of Files. */ - protected getAttemptsFeedbackFiles(quiz: any, attempts: any[]): Promise { + protected getAttemptsFeedbackFiles(quiz: any, attempts: any[], siteId?: string): Promise { // We have quiz data, now we'll get specific data for each attempt. const promises = []; const getInlineFiles = this.sitesProvider.getCurrentSite() && @@ -121,8 +125,11 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl const attemptGrade = this.quizProvider.rescaleGrade(attempt.sumgrades, quiz, false); if (typeof attemptGrade != 'undefined') { - promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), true) - .then((feedback) => { + promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), { + cmId: quiz.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((feedback) => { if (getInlineFiles && feedback.feedbackinlinefiles && feedback.feedbackinlinefiles.length) { files = files.concat(feedback.feedbackinlinefiles); } else if (feedback.feedbacktext && !getInlineFiles) { @@ -219,13 +226,16 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl const siteId = this.sitesProvider.getCurrentSiteId(); - return this.quizProvider.getQuiz(courseId, module.id, false, false, siteId).then((quiz) => { + return this.quizProvider.getQuiz(courseId, module.id, {siteId}).then((quiz) => { if (quiz.allowofflineattempts !== 1 || quiz.hasquestions === 0) { return false; } // Not downloadable if we reached max attempts or the quiz has an unfinished attempt. - return this.quizProvider.getUserAttempts(quiz.id, undefined, true, false, false, siteId).then((attempts) => { + return this.quizProvider.getUserAttempts(quiz.id, { + cmId: module.id, + siteId, + }).then((attempts) => { const isLastFinished = !attempts.length || this.quizProvider.isAttemptFinished(attempts[attempts.length - 1].state); return quiz.attempts === 0 || quiz.attempts > attempts.length || !isLastFinished; @@ -283,26 +293,35 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl attemptAccessInfo, preflightData; + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + // Get quiz. - return this.quizProvider.getQuiz(courseId, module.id, false, true, siteId).then((quizData) => { + return this.quizProvider.getQuiz(courseId, module.id, commonOptions).then((quizData) => { quiz = quizData; const promises = [], introFiles = this.getIntroFilesFromInstance(module, quiz); // Prefetch some quiz data. - promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, false, true, siteId).then((info) => { + promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { quizAccessInfo = info; })); - promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, true, siteId)); - promises.push(this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true, siteId).then((atts) => { + promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, modOptions)); + promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { attempts = atts; - return this.getAttemptsFeedbackFiles(quiz, attempts).then((attemptFiles) => { + return this.getAttemptsFeedbackFiles(quiz, attempts, siteId).then((attemptFiles) => { return this.filepoolProvider.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, module.id); }); })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, false, true, siteId).then((info) => { + promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions).then((info) => { attemptAccessInfo = info; })); @@ -338,10 +357,10 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl if (startAttempt) { // Re-fetch user attempts since we created a new one. - promises.push(this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true, siteId).then((atts) => { + promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { attempts = atts; - return this.getAttemptsFeedbackFiles(quiz, attempts).then((attemptFiles) => { + return this.getAttemptsFeedbackFiles(quiz, attempts, siteId).then((attemptFiles) => { return this.filepoolProvider.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, module.id); }); @@ -355,16 +374,16 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl } // Fetch attempt related data. - promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, true, siteId)); - promises.push(this.quizProvider.getUserBestGrade(quiz.id, true, siteId)); + promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, modOptions)); + promises.push(this.quizProvider.getUserBestGrade(quiz.id, modOptions)); promises.push(this.quizProvider.getGradeFromGradebook(courseId, module.id, true, siteId).then((gradebookData) => { if (typeof gradebookData.graderaw != 'undefined') { - return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, true, siteId); + return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, modOptions); } }).catch(() => { // Ignore errors. })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, false, true, siteId)); // Last attempt. + promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions)); // Last attempt. return Promise.all(promises); }).then(() => { @@ -410,23 +429,35 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl promises = [], isSequential = this.quizProvider.isNavigationSequential(quiz); + const modOptions = { + cmId: quiz.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + if (this.quizProvider.isAttemptFinished(attempt.state)) { // Attempt is finished, get feedback and review data. const attemptGrade = this.quizProvider.rescaleGrade(attempt.sumgrades, quiz, false); if (typeof attemptGrade != 'undefined') { - promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), true, siteId)); + promises.push(this.quizProvider.getFeedbackForGrade(quiz.id, Number(attemptGrade), modOptions)); } // Get the review for each page. pages.forEach((page) => { - promises.push(this.quizProvider.getAttemptReview(attempt.id, page, true, siteId).catch(() => { + promises.push(this.quizProvider.getAttemptReview(attempt.id, { + page, + ...modOptions, // Include all options. + }).catch(() => { // Ignore failures, maybe the user can't review the attempt. })); }); - // Get the review for all questions in same page. - promises.push(this.quizProvider.getAttemptReview(attempt.id, -1, true, siteId).then((data) => { + // Get the review for all questions in same page. + promises.push(this.quizProvider.getAttemptReview(attempt.id, { + page: -1, + ...modOptions, // Include all options. + }).then((data) => { // Download the files inside the questions. const questionPromises = []; @@ -442,8 +473,8 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl } else { // Attempt not finished, get data needed to continue the attempt. - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, attempt.id, false, true, siteId)); - promises.push(this.quizProvider.getAttemptSummary(attempt.id, preflightData, false, true, false, siteId)); + promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, attempt.id, modOptions)); + promises.push(this.quizProvider.getAttemptSummary(attempt.id, preflightData, modOptions)); if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) { // Get data for each page. @@ -453,8 +484,7 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl return; } - promises.push(this.quizProvider.getAttemptData(attempt.id, page, preflightData, false, true, siteId) - .then((data) => { + promises.push(this.quizProvider.getAttemptData(attempt.id, page, preflightData, modOptions).then((data) => { // Download the files inside the questions. const questionPromises = []; @@ -485,30 +515,35 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl siteId = siteId || this.sitesProvider.getCurrentSiteId(); const promises = []; + const modOptions = { + cmId: quiz.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; let attempts, quizAccessInfo, preflightData, lastAttempt; // Get quiz data. - promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, false, true, siteId).then((info) => { + promises.push(this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { quizAccessInfo = info; })); - promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, true, siteId)); - promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, true, siteId)); - promises.push(this.quizProvider.getUserBestGrade(quiz.id, true, siteId)); - promises.push(this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true, siteId).then((atts) => { + promises.push(this.quizProvider.getQuizRequiredQtypes(quiz.id, modOptions)); + promises.push(this.quizProvider.getCombinedReviewOptions(quiz.id, modOptions)); + promises.push(this.quizProvider.getUserBestGrade(quiz.id, modOptions)); + promises.push(this.quizProvider.getUserAttempts(quiz.id, modOptions).then((atts) => { attempts = atts; })); promises.push(this.quizProvider.getGradeFromGradebook(quiz.course, quiz.coursemodule, true, siteId) .then((gradebookData) => { if (typeof gradebookData.graderaw != 'undefined') { - return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, true, siteId); + return this.quizProvider.getFeedbackForGrade(quiz.id, gradebookData.graderaw, modOptions); } }).catch(() => { // Ignore errors. })); - promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, false, true, siteId)); // Last attempt. + promises.push(this.quizProvider.getAttemptAccessInformation(quiz.id, 0, modOptions)); // Last attempt. return Promise.all(promises).then(() => { lastAttempt = attempts[attempts.length - 1]; @@ -529,7 +564,12 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl } }).then(() => { // Prefetch finished, set the right status. - return this.setStatusAfterPrefetch(quiz, attempts, true, false, siteId); + return this.setStatusAfterPrefetch(quiz, { + cmId: quiz.coursemodule, + attempts, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }); }); } @@ -538,29 +578,25 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl * If the last attempt is finished or there isn't one, set it as not downloaded to show download icon. * * @param quiz Quiz. - * @param attempts List of attempts. If not provided, they will be calculated. - * @param forceCache Whether it should always return cached data. Only if attempts is undefined. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). Only if - * attempts is undefined. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - setStatusAfterPrefetch(quiz: any, attempts?: any[], forceCache?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + setStatusAfterPrefetch(quiz: any, options: AddonModQuizSetStatusAfterPrefetchOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); const promises = []; let status; + let attempts = options.attempts; if (!attempts) { // Get the attempts. - promises.push(this.quizProvider.getUserAttempts(quiz.id, 'all', true, forceCache, ignoreCache, siteId).then((atts) => { + promises.push(this.quizProvider.getUserAttempts(quiz.id, options).then((atts) => { attempts = atts; })); } // Check the current status of the quiz. - promises.push(this.filepoolProvider.getPackageStatus(siteId, this.component, quiz.coursemodule).then((stat) => { + promises.push(this.filepoolProvider.getPackageStatus(options.siteId, this.component, quiz.coursemodule).then((stat) => { status = stat; })); @@ -573,7 +609,7 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl isLastFinished = !lastAttempt || this.quizProvider.isAttemptFinished(lastAttempt.state), newStatus = isLastFinished ? CoreConstants.NOT_DOWNLOADED : CoreConstants.DOWNLOADED; - return this.filepoolProvider.storePackageStatus(siteId, newStatus, this.component, quiz.coursemodule); + return this.filepoolProvider.storePackageStatus(options.siteId, newStatus, this.component, quiz.coursemodule); } }); } @@ -591,7 +627,7 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl this.syncProvider = this.injector.get(AddonModQuizSyncProvider); } - return this.quizProvider.getQuiz(courseId, module.id).then((quiz) => { + return this.quizProvider.getQuiz(courseId, module.id, {siteId}).then((quiz) => { return this.syncProvider.syncQuiz(quiz, false, siteId).then((results) => { module.attemptFinished = (results && results.attemptFinished) || false; @@ -604,3 +640,10 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl }); } } + +/** + * Options to pass to setStatusAfterPrefetch. + */ +export type AddonModQuizSetStatusAfterPrefetchOptions = CoreCourseCommonModWSOptions & { + attempts?: any[]; // List of attempts. If not provided, they will be calculated. +}; diff --git a/src/addon/mod/quiz/providers/quiz-sync.ts b/src/addon/mod/quiz/providers/quiz-sync.ts index b49891a58..e13a013f5 100644 --- a/src/addon/mod/quiz/providers/quiz-sync.ts +++ b/src/addon/mod/quiz/providers/quiz-sync.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -111,7 +111,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider // Check if online attempt was finished because of the sync. if (onlineAttempt && !this.quizProvider.isAttemptFinished(onlineAttempt.state)) { // Attempt wasn't finished at start. Check if it's finished now. - return this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, false, siteId).then((attempts) => { + return this.quizProvider.getUserAttempts(quiz.id, {cmId: quiz.coursemodule, siteId}).then((attempts) => { // Search the attempt. for (const i in attempts) { const attempt = attempts[i]; @@ -180,7 +180,11 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider }).then(() => { // Prefetch finished or not needed, set the right status. - return this.prefetchHandler.setStatusAfterPrefetch(quiz, undefined, shouldDownload, false, siteId); + return this.prefetchHandler.setStatusAfterPrefetch(quiz, { + cmId: module.id, + readingStrategy: shouldDownload ? CoreSitesReadingStrategy.PreferCache : undefined, + siteId, + }); }); } @@ -226,7 +230,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider if (!this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) { // Quiz not blocked, try to synchronize it. - promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, false, false, siteId).then((quiz) => { + promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, {siteId}).then((quiz) => { const promise = force ? this.syncQuiz(quiz, false, siteId) : this.syncQuizIfNeeded(quiz, false, siteId); return promise.then((data) => { @@ -284,10 +288,15 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider syncQuiz(quiz: any, askPreflight?: boolean, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - const warnings = [], - courseId = quiz.course; - let syncPromise, - preflightData; + const warnings = []; + const courseId = quiz.course; + const modOptions = { + cmId: quiz.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + let syncPromise; + let preflightData; if (this.isSyncing(quiz.id, siteId)) { // There's already a sync ongoing for this quiz, return the promise. @@ -318,7 +327,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider const offlineAttempt = attempts.pop(); // Now get the list of online attempts to make sure this attempt exists and isn't finished. - return this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true, siteId).then((attempts) => { + return this.quizProvider.getUserAttempts(quiz.id, modOptions).then((attempts) => { const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined; let onlineAttempt; @@ -354,7 +363,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider let finish; // We're going to need preflightData, get it. - return this.quizProvider.getQuizAccessInformation(quiz.id, false, true, siteId).then((info) => { + return this.quizProvider.getQuizAccessInformation(quiz.id, modOptions).then((info) => { return this.prefetchHandler.getPreflightData(quiz, info, onlineAttempt, askPreflight, 'core.settings.synchronization', siteId); @@ -364,8 +373,11 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider // Now get the online questions data. const pages = this.quizProvider.getPagesFromLayoutAndQuestions(onlineAttempt.layout, offlineQuestions); - return this.quizProvider.getAllQuestionsData(quiz, onlineAttempt, preflightData, pages, false, true, - siteId); + return this.quizProvider.getAllQuestionsData(quiz, onlineAttempt, preflightData, { + pages, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }); }).then((onlineQuestions) => { // Validate questions, discarding the offline answers that can't be synchronized. diff --git a/src/addon/mod/quiz/providers/quiz.ts b/src/addon/mod/quiz/providers/quiz.ts index b4df1d58e..6caa9148e 100644 --- a/src/addon/mod/quiz/providers/quiz.ts +++ b/src/addon/mod/quiz/providers/quiz.ts @@ -16,18 +16,19 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { AddonModQuizOfflineProvider } from './quiz-offline'; import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for quiz. @@ -90,22 +91,16 @@ export class AddonModQuizProvider { * @param quiz Quiz. * @param attempt Attempt. * @param preflightData Preflight required data (like password). - * @param pages List of pages to get. If not defined, all pages. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the questions. */ - getAllQuestionsData(quiz: any, attempt: any, preflightData: any, pages?: number[], offline?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + getAllQuestionsData(quiz: any, attempt: any, preflightData: any, options: AddonModQuizAllQuestionsDataOptions = {}) + : Promise { - const promises = [], - questions = {}, - isSequential = this.isNavigationSequential(quiz); - - if (!pages) { - pages = this.getPagesFromLayout(attempt.layout); - } + const promises = []; + const questions = {}; + const isSequential = this.isNavigationSequential(quiz); + const pages = options.pages || this.getPagesFromLayout(attempt.layout); pages.forEach((page) => { if (isSequential && page < attempt.currentpage) { @@ -114,7 +109,7 @@ export class AddonModQuizProvider { } // Get the questions in the page. - promises.push(this.getAttemptData(attempt.id, page, preflightData, offline, ignoreCache, siteId).then((data) => { + promises.push(this.getAttemptData(attempt.id, page, preflightData, options).then((data) => { // Add the questions to the result object. data.questions.forEach((question) => { questions[question.slot] = question; @@ -153,29 +148,22 @@ export class AddonModQuizProvider { * * @param quizId Quiz ID. * @param attemptId Attempt ID. 0 for user's last attempt. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the access information. */ - getAttemptAccessInformation(quizId: number, attemptId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getAttemptAccessInformation(quizId: number, attemptId: number, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - quizid: quizId, - attemptid: attemptId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAttemptAccessInformationCacheKey(quizId, attemptId) - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + attemptid: attemptId, + }; + const preSets = { + cacheKey: this.getAttemptAccessInformationCacheKey(quizId, attemptId), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_attempt_access_information', params, preSets); }); @@ -208,30 +196,23 @@ export class AddonModQuizProvider { * @param attemptId Attempt ID. * @param page Page number. * @param preflightData Preflight required data (like password). - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the attempt data. */ - getAttemptData(attemptId: number, page: number, preflightData: any, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getAttemptData(attemptId: number, page: number, preflightData: any, options: CoreCourseCommonModWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - attemptid: attemptId, - page: page, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAttemptDataCacheKey(attemptId, page) - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + attemptid: attemptId, + page: page, + preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true), + }; + const preSets = { + cacheKey: this.getAttemptDataCacheKey(attemptId, page), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_attempt_data', params, preSets); }); @@ -389,30 +370,24 @@ export class AddonModQuizProvider { * Get an attempt's review. * * @param attemptId Attempt ID. - * @param page Page number. If not defined, return all the questions in all the pages. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the attempt review. */ - getAttemptReview(attemptId: number, page?: number, ignoreCache?: boolean, siteId?: string): Promise { - if (typeof page == 'undefined') { - page = -1; - } + getAttemptReview(attemptId: number, options: AddonModQuizGetAttemptReviewOptions = {}): Promise { + const page = typeof options.page == 'undefined' ? -1 : options.page; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - attemptid: attemptId, - page: page - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAttemptReviewCacheKey(attemptId, page), - cacheErrors: ['noreview'] - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + attemptid: attemptId, + page: page, + }; + const preSets = { + cacheKey: this.getAttemptReviewCacheKey(attemptId, page), + cacheErrors: ['noreview'], + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_attempt_review', params, preSets); }); @@ -433,34 +408,26 @@ export class AddonModQuizProvider { * * @param attemptId Attempt ID. * @param preflightData Preflight required data (like password). - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param loadLocal Whether it should load local state for each question. Only applicable if offline=true. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of questions for the attempt summary. */ - getAttemptSummary(attemptId: number, preflightData: any, offline?: boolean, ignoreCache?: boolean, loadLocal?: boolean, - siteId?: string): Promise { + getAttemptSummary(attemptId: number, preflightData: any, options: AddonModQuizGetAttemptSummaryOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - attemptid: attemptId, - preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAttemptSummaryCacheKey(attemptId) - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + attemptid: attemptId, + preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true), + }; + const preSets = { + cacheKey: this.getAttemptSummaryCacheKey(attemptId), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_attempt_summary', params, preSets).then((response) => { if (response && response.questions) { - if (offline && loadLocal) { + if (options.loadLocal) { return this.quizOfflineProvider.loadQuestionsLocalStates(attemptId, response.questions, site.getId()); } @@ -497,27 +464,23 @@ export class AddonModQuizProvider { * Get a quiz combined review options. * * @param quizId Quiz ID. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. + * @param options Other options. * @return Promise resolved with the combined review options. */ - getCombinedReviewOptions(quizId: number, ignoreCache?: boolean, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + getCombinedReviewOptions(quizId: number, options: AddonModQuizUserOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - quizid: quizId, - userid: userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getCombinedReviewOptionsCacheKey(quizId, userId) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + userid: userId, + }; + const preSets = { + cacheKey: this.getCombinedReviewOptionsCacheKey(quizId, userId), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_combined_review_options', params, preSets).then((response) => { if (response && response.someoptions && response.alloptions) { @@ -559,25 +522,22 @@ export class AddonModQuizProvider { * * @param quizId Quiz ID. * @param grade Grade. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the feedback. */ - getFeedbackForGrade(quizId: number, grade: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getFeedbackForGrade(quizId: number, grade: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - quizid: quizId, - grade: grade - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getFeedbackForGradeCacheKey(quizId, grade), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + grade: grade, + }; + const preSets = { + cacheKey: this.getFeedbackForGradeCacheKey(quizId, grade), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_quiz_feedback_for_grade', params, preSets); }); @@ -683,29 +643,21 @@ export class AddonModQuizProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the Quiz is retrieved. */ - protected getQuizByField(courseId: number, key: string, value: any, forceCache?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + protected getQuizByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getQuizDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getQuizDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModQuizProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_quizzes_by_courses', params, preSets).then((response) => { if (response && response.quizzes) { @@ -728,13 +680,11 @@ export class AddonModQuizProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the quiz is retrieved. */ - getQuiz(courseId: number, cmId: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getQuizByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId); + getQuiz(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getQuizByField(courseId, 'coursemodule', cmId, options); } /** @@ -742,13 +692,11 @@ export class AddonModQuizProvider { * * @param courseId Course ID. * @param id Quiz ID. - * @param forceCache Whether it should always return cached data. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the quiz is retrieved. */ - getQuizById(courseId: number, id: number, forceCache?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.getQuizByField(courseId, 'id', id, forceCache, ignoreCache, siteId); + getQuizById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getQuizByField(courseId, 'id', id, options); } /** @@ -765,26 +713,20 @@ export class AddonModQuizProvider { * Get access information for an attempt. * * @param quizId Quiz ID. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the access information. */ - getQuizAccessInformation(quizId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getQuizAccessInformation(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - quizid: quizId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getQuizAccessInformationCacheKey(quizId) - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + }; + const preSets = { + cacheKey: this.getQuizAccessInformationCacheKey(quizId), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_quiz_access_information', params, preSets); }); @@ -829,24 +771,21 @@ export class AddonModQuizProvider { * Get the potential question types that would be required for a given quiz. * * @param quizId Quiz ID. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the access information. */ - getQuizRequiredQtypes(quizId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getQuizRequiredQtypes(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - quizid: quizId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getQuizRequiredQtypesCacheKey(quizId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + }; + const preSets = { + cacheKey: this.getQuizRequiredQtypesCacheKey(quizId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_quiz_required_qtypes', params, preSets).then((response) => { if (response && response.questiontypes) { @@ -981,37 +920,29 @@ export class AddonModQuizProvider { * Get quiz attempts for a certain user. * * @param quizId Quiz ID. - * @param status Status of the attempts to get. By default, 'all'. - * @param includePreviews Whether to include previews. Defaults to true. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. + * @param options Other options. * @return Promise resolved with the attempts. */ - getUserAttempts(quizId: number, status: string = 'all', includePreviews: boolean = true, offline?: boolean, - ignoreCache?: boolean, siteId?: string, userId?: number): Promise { + getUserAttempts(quizId: number, options: AddonModQuizGetUserAttemptsOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + const status = options.status || 'all'; + const includePreviews = typeof options.includePreviews == 'undefined' ? true : options.includePreviews; + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - quizid: quizId, - userid: userId, - status: status, - includepreviews: includePreviews ? 1 : 0 - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getUserAttemptsCacheKey(quizId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + userid: userId, + status: status, + includepreviews: includePreviews ? 1 : 0, + }; + const preSets = { + cacheKey: this.getUserAttemptsCacheKey(quizId, userId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_user_attempts', params, preSets).then((response) => { if (response && response.attempts) { @@ -1048,27 +979,23 @@ export class AddonModQuizProvider { * Get best grade in a quiz for a certain user. * * @param quizId Quiz ID. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. + * @param options Other options. * @return Promise resolved with the best grade data. */ - getUserBestGrade(quizId: number, ignoreCache?: boolean, siteId?: string, userId?: number): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + getUserBestGrade(quizId: number, options: AddonModQuizUserOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - quizid: quizId, - userid: userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getUserBestGradeCacheKey(quizId, userId) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + quizid: quizId, + userid: userId, + }; + const preSets = { + cacheKey: this.getUserBestGradeCacheKey(quizId, userId), + component: AddonModQuizProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_quiz_get_user_best_grade', params, preSets); }); @@ -1246,8 +1173,11 @@ export class AddonModQuizProvider { siteId = siteId || this.sitesProvider.getCurrentSiteId(); // Get required data to call the invalidate functions. - return this.getQuiz(courseId, moduleId, true, false, siteId).then((quiz) => { - return this.getUserAttempts(quiz.id, 'all', true, false, false, siteId).then((attempts) => { + return this.getQuiz(courseId, moduleId, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }).then((quiz) => { + return this.getUserAttempts(quiz.id, {cmId: quiz.coursemodule, siteId}).then((attempts) => { // Now invalidate it. const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined; @@ -1703,7 +1633,12 @@ export class AddonModQuizProvider { : Promise { // Get attempt summary to have the list of questions. - return this.getAttemptSummary(attempt.id, preflightData, true, false, true, siteId).then((questionArray) => { + return this.getAttemptSummary(attempt.id, preflightData, { + cmId: quiz.coursemodule, + loadLocal: true, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }).then((questionArray) => { // Convert the question array to an object. const questions = this.utils.arrayToObject(questionArray, 'slot'); @@ -1860,3 +1795,40 @@ export class AddonModQuizProvider { }); } } + +/** + * Common options with user ID. + */ +export type AddonModQuizUserOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User ID. If not defined use site's current user. +}; + +/** + * Options to pass to getAllQuestionsData. + */ +export type AddonModQuizAllQuestionsDataOptions = CoreCourseCommonModWSOptions & { + pages?: number[]; // List of pages to get. If not defined, all pages. +}; + +/** + * Options to pass to getAttemptReview. + */ +export type AddonModQuizGetAttemptReviewOptions = CoreCourseCommonModWSOptions & { + page?: number; // List of pages to get. If not defined, all pages. +}; + +/** + * Options to pass to getAttemptSummary. + */ +export type AddonModQuizGetAttemptSummaryOptions = CoreCourseCommonModWSOptions & { + loadLocal?: boolean; // Whether it should load local state for each question. +}; + +/** + * Options to pass to getUserAttempts. + */ +export type AddonModQuizGetUserAttemptsOptions = CoreCourseCommonModWSOptions & { + status?: string; // Status of the attempts to get. By default, 'all'. + includePreviews?: boolean; // Whether to include previews. Defaults to true. + userId?: number; // User ID. If not defined use site's current user. +}; diff --git a/src/addon/mod/resource/providers/resource.ts b/src/addon/mod/resource/providers/resource.ts index 5445ec3b1..b8b5b2fd2 100644 --- a/src/addon/mod/resource/providers/resource.ts +++ b/src/addon/mod/resource/providers/resource.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; @@ -54,18 +54,22 @@ export class AddonModResourceProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the resource is retrieved. */ - protected getResourceDataByKey(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getResourceDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getResourceCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getResourceCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModResourceProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_resource_get_resources_by_courses', params, preSets) .then((response: AddonModResourceGetResourcesByCoursesResult): any => { @@ -89,11 +93,11 @@ export class AddonModResourceProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the resource is retrieved. */ - getResourceData(courseId: number, cmId: number, siteId?: string): Promise { - return this.getResourceDataByKey(courseId, 'coursemodule', cmId, siteId); + getResourceData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getResourceDataByKey(courseId, 'coursemodule', cmId, options); } /** diff --git a/src/addon/mod/scorm/components/index/index.ts b/src/addon/mod/scorm/components/index/index.ts index e16a6484a..f6f313360 100644 --- a/src/addon/mod/scorm/components/index/index.ts +++ b/src/addon/mod/scorm/components/index/index.ts @@ -146,7 +146,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { // Get the SCORM instance. - return this.scormProvider.getScorm(this.courseId, this.module.id, this.module.url).then((scormData) => { + return this.scormProvider.getScorm(this.courseId, this.module.id, {moduleUrl: this.module.url}).then((scormData) => { this.scorm = scormData; this.dataRetrieved.emit(this.scorm); @@ -185,12 +185,12 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom const promises = []; // Get access information. - promises.push(this.scormProvider.getAccessInformation(this.scorm.id).then((accessInfo) => { + promises.push(this.scormProvider.getAccessInformation(this.scorm.id, {cmId: this.module.id}).then((accessInfo) => { this.accessInfo = accessInfo; })); // Get the number of attempts. - promises.push(this.scormProvider.getAttemptCount(this.scorm.id).then((attemptsData) => { + promises.push(this.scormProvider.getAttemptCount(this.scorm.id, {cmId: this.module.id}).then((attemptsData) => { this.attempts = attemptsData; this.hasOffline = !!this.attempts.offline.length; @@ -207,7 +207,10 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom } // Check if the last attempt is incomplete. - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.lastAttempt, this.lastIsOffline); + return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.lastAttempt, { + offline: this.lastIsOffline, + cmId: this.module.id, + }); }).then((incomplete) => { const promises = []; @@ -260,7 +263,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom * @return Promise resolved when done. */ protected fetchStructure(): Promise { - return this.scormProvider.getOrganizations(this.scorm.id).then((organizations) => { + return this.scormProvider.getOrganizations(this.scorm.id, {cmId: this.module.id}).then((organizations) => { this.organizations = organizations; if (!this.currentOrganization.identifier) { @@ -460,8 +463,11 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom this.loadingToc = true; - return this.scormProvider.getOrganizationToc(this.scorm.id, this.lastAttempt, organizationId, this.lastIsOffline) - .then((toc) => { + return this.scormProvider.getOrganizationToc(this.scorm.id, this.lastAttempt, { + organization: organizationId, + offline: this.lastIsOffline, + cmId: this.module.id, + }).then((toc) => { this.toc = this.scormProvider.formatTocToArray(toc); diff --git a/src/addon/mod/scorm/pages/player/player.ts b/src/addon/mod/scorm/pages/player/player.ts index a21bd9e58..ed6e32de7 100644 --- a/src/addon/mod/scorm/pages/player/player.ts +++ b/src/addon/mod/scorm/pages/player/player.ts @@ -15,7 +15,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { IonicPage, NavParams, ModalController } from 'ionic-angular'; import { CoreEventsProvider } from '@providers/events'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreDomUtils } from '@providers/utils/dom'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -201,7 +201,10 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { // Check if current attempt is incomplete. if (this.attempt > 0) { - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, this.offline); + return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, { + offline: this.offline, + cmId: this.scorm.coursemodule, + }); } else { // User doesn't have attempts. Last attempt is not incomplete (since he doesn't have any). return false; @@ -217,7 +220,10 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { return this.scormHelper.createOfflineAttempt(this.scorm, result.attempt, attemptsData.online.length); } else { // Last attempt was online, verify that we can create a new online attempt. We ignore cache. - return this.scormProvider.getScormUserData(this.scorm.id, result.attempt, undefined, false, true).catch(() => { + return this.scormProvider.getScormUserData(this.scorm.id, result.attempt, { + cmId: this.scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }).catch(() => { // Cannot communicate with the server, create an offline attempt. this.offline = true; @@ -241,18 +247,22 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { // Wait for any ongoing sync to finish. We won't sync a SCORM while it's being played. return this.scormSyncProvider.waitForSync(this.scorm.id).then(() => { // Get attempts data. - return this.scormProvider.getAttemptCount(this.scorm.id).then((attemptsData) => { + return this.scormProvider.getAttemptCount(this.scorm.id, {cmId: this.scorm.coursemodule}).then((attemptsData) => { return this.determineAttemptAndMode(attemptsData).then(() => { // Fetch TOC and get user data. const promises = []; promises.push(this.fetchToc()); - promises.push(this.scormProvider.getScormUserData(this.scorm.id, this.attempt, undefined, this.offline) - .then((data) => { + promises.push(this.scormProvider.getScormUserData(this.scorm.id, this.attempt, { + cmId: this.scorm.coursemodule, + offline: this.offline, + }).then((data) => { this.userData = data; })); // Get access information. - promises.push(this.scormProvider.getAccessInformation(this.scorm.id).then((accessInfo) => { + promises.push(this.scormProvider.getAccessInformation(this.scorm.id, { + cmId: this.scorm.coursemodule, + }).then((accessInfo) => { this.accessInfo = accessInfo; })); @@ -273,11 +283,18 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { this.loadingToc = true; // We need to check incomplete again: attempt number or status might have changed. - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, this.offline).then((incomplete) => { + return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.attempt, { + offline: this.offline, + cmId: this.scorm.coursemodule, + }).then((incomplete) => { this.scorm.incomplete = incomplete; // Get TOC. - return this.scormProvider.getOrganizationToc(this.scorm.id, this.attempt, this.organizationId, this.offline); + return this.scormProvider.getOrganizationToc(this.scorm.id, this.attempt, { + organization: this.organizationId, + offline: this.offline, + cmId: this.scorm.coursemodule, + }); }).then((toc) => { this.toc = this.scormProvider.formatTocToArray(toc); @@ -300,8 +317,13 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { if (!this.currentSco) { // No SCO defined. Get the first valid one. - return this.scormHelper.getFirstSco(this.scorm.id, this.attempt, this.toc, this.organizationId, this.mode, - this.offline).then((sco) => { + return this.scormHelper.getFirstSco(this.scorm.id, this.attempt, { + toc: this.toc, + organization: this.organizationId, + mode: this.mode, + offline: this.offline, + cmId: this.scorm.coursemodule, + }).then((sco) => { if (sco) { this.currentSco = sco; @@ -374,7 +396,9 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { this.scormProvider.saveTracks(sco.id, this.attempt, tracks, this.scorm, this.offline).catch(() => { // Error saving data. We'll go offline if we're online and the asset is not marked as completed already. if (!this.offline) { - return this.scormProvider.getScormUserData(this.scorm.id, this.attempt, undefined, false).then((data) => { + return this.scormProvider.getScormUserData(this.scorm.id, this.attempt, { + cmId: this.scorm.coursemodule, + }).then((data) => { if (!data[sco.id] || data[sco.id].userdata['cmi.core.lesson_status'] != 'completed') { // Go offline. return this.scormHelper.convertAttemptToOffline(this.scorm, this.attempt).then(() => { @@ -462,7 +486,10 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { return this.scormProvider.saveTracks(scoId, this.attempt, tracks, this.scorm, this.offline).then(() => { if (!this.offline) { // New online attempt created, update cached data about online attempts. - this.scormProvider.getAttemptCount(this.scorm.id, false, true).catch(() => { + this.scormProvider.getAttemptCount(this.scorm.id, { + cmId: this.scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + }).catch(() => { // Ignore errors. }); } diff --git a/src/addon/mod/scorm/providers/helper.ts b/src/addon/mod/scorm/providers/helper.ts index 49fe8f6fd..c838894f1 100644 --- a/src/addon/mod/scorm/providers/helper.ts +++ b/src/addon/mod/scorm/providers/helper.ts @@ -19,6 +19,7 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { AddonModScormProvider, AddonModScormAttemptCountResult } from './scorm'; import { AddonModScormOfflineProvider } from './scorm-offline'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Helper service that provides some features for SCORM. @@ -78,7 +79,7 @@ export class AddonModScormHelperProvider { siteId = siteId || this.sitesProvider.getCurrentSiteId(); // Get data from the online attempt. - return this.scormProvider.getScormUserData(scorm.id, attempt, undefined, false, false, siteId).then((onlineData) => { + return this.scormProvider.getScormUserData(scorm.id, attempt, {cmId: scorm.coursemodule, siteId}).then((onlineData) => { // The SCORM API might have written some data to the offline attempt already. // We don't want to override it with cached online data. return this.scormOfflineProvider.getScormUserData(scorm.id, attempt, undefined, siteId).catch(() => { @@ -131,7 +132,7 @@ export class AddonModScormHelperProvider { siteId = siteId || this.sitesProvider.getCurrentSiteId(); // Try to get data from online attempts. - return this.searchOnlineAttemptUserData(scorm.id, lastOnline, siteId).then((userData) => { + return this.searchOnlineAttemptUserData(scorm.id, lastOnline, {cmId: scorm.coursemodule, siteId}).then((userData) => { // We're creating a new attempt, remove all the user data that is not needed for a new attempt. for (const scoId in userData) { const sco = userData[scoId], @@ -177,7 +178,11 @@ export class AddonModScormHelperProvider { // Check if last online incomplete. const hasOffline = attempts.offline.indexOf(lastOnline) > -1; - return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, hasOffline, false, siteId).then((incomplete) => { + return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { + offline: hasOffline, + cmId: scorm.coursemodule, + siteId, + }).then((incomplete) => { if (incomplete) { return { number: lastOnline, @@ -197,24 +202,19 @@ export class AddonModScormHelperProvider { * * @param scormId Scorm ID. * @param attempt Attempt number. - * @param toc SCORM's TOC. If not provided, it will be calculated. - * @param organization Organization to use. - * @param mode Mode. - * @param offline Whether the attempt is offline. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the first SCO. */ - getFirstSco(scormId: number, attempt: number, toc?: any[], organization?: string, mode?: string, offline?: boolean, - siteId?: string): Promise { + getFirstSco(scormId: number, attempt: number, options: AddonModScormGetFirstScoOptions = {}): Promise { - mode = mode || AddonModScormProvider.MODENORMAL; + const mode = options.mode || AddonModScormProvider.MODENORMAL; let promise; - if (toc && toc.length) { - promise = Promise.resolve(toc); + if (options.toc && options.toc.length) { + promise = Promise.resolve(options.toc); } else { // SCORM doesn't have a TOC. Get all the scos. - promise = this.scormProvider.getScosWithData(scormId, attempt, organization, offline, false, siteId); + promise = this.scormProvider.getScosWithData(scormId, attempt, options); } return promise.then((scos) => { @@ -319,16 +319,16 @@ export class AddonModScormHelperProvider { * * @param scormId SCORM ID. * @param attempt Online attempt to get the data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with user data. */ - searchOnlineAttemptUserData(scormId: number, attempt: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + searchOnlineAttemptUserData(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.scormProvider.getScormUserData(scormId, attempt, undefined, false, false, siteId).catch(() => { + return this.scormProvider.getScormUserData(scormId, attempt, options).catch(() => { if (attempt > 0) { // We couldn't retrieve the data. Try again with the previous online attempt. - return this.searchOnlineAttemptUserData(scormId, attempt - 1, siteId); + return this.searchOnlineAttemptUserData(scormId, attempt - 1, options); } else { // No more attempts to try. Reject return Promise.reject(null); @@ -336,3 +336,13 @@ export class AddonModScormHelperProvider { }); } } + +/** + * Options to pass to getFirstSco. + */ +export type AddonModScormGetFirstScoOptions = CoreCourseCommonModWSOptions & { + toc?: any[]; // SCORM's TOC. If not provided, it will be calculated. + organization?: string; // Organization to use. + mode?: string; // Mode. + offline?: boolean; // Whether the attempt is offline. +}; diff --git a/src/addon/mod/scorm/providers/prefetch-handler.ts b/src/addon/mod/scorm/providers/prefetch-handler.ts index d8263dbd7..dd34b8fe1 100644 --- a/src/addon/mod/scorm/providers/prefetch-handler.ts +++ b/src/addon/mod/scorm/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -111,7 +111,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand let scorm; - return this.scormProvider.getScorm(courseId, module.id, module.url, false, siteId).then((scormData) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scormData) => { scorm = scormData; const promises = [], @@ -132,9 +132,6 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand // Ignore errors. })); - // Prefetch access information. - promises.push(this.scormProvider.getAccessInformation(scorm.id)); - return Promise.all(promises); }).then(() => { // Success, return the hash. @@ -246,9 +243,14 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand siteId = siteId || this.sitesProvider.getCurrentSiteId(); const promises = []; + const modOptions = { + cmId: scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; // Prefetch number of attempts (including not completed). - promises.push(this.scormProvider.getAttemptCountOnline(scorm.id, undefined, true, siteId).catch(() => { + promises.push(this.scormProvider.getAttemptCountOnline(scorm.id, modOptions).catch(() => { // If it fails, assume we have no attempts. return 0; }).then((numAttempts) => { @@ -257,7 +259,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand const dataPromises = []; for (let i = 1; i <= numAttempts; i++) { - dataPromises.push(this.scormProvider.getScormUserDataOnline(scorm.id, i, true, siteId).catch((err) => { + dataPromises.push(this.scormProvider.getScormUserDataOnline(scorm.id, i, modOptions).catch((err) => { // Ignore failures of all the attempts that aren't the last one. if (i == numAttempts) { return Promise.reject(err); @@ -268,12 +270,15 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand return Promise.all(dataPromises); } else { // No attempts. We'll still try to get user data to be able to identify SCOs not visible and so. - return this.scormProvider.getScormUserDataOnline(scorm.id, 0, true, siteId); + return this.scormProvider.getScormUserDataOnline(scorm.id, 0, modOptions); } })); // Prefetch SCOs. - promises.push(this.scormProvider.getScos(scorm.id, undefined, true, siteId)); + promises.push(this.scormProvider.getScos(scorm.id, modOptions)); + + // Prefetch access information. + promises.push(this.scormProvider.getAccessInformation(scorm.id, modOptions)); return Promise.all(promises); } @@ -288,7 +293,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand * to calculate the total size. */ getDownloadSize(module: any, courseId: any, single?: boolean): Promise<{ size: number, total: boolean }> { - return this.scormProvider.getScorm(courseId, module.id, module.url).then((scorm) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { if (this.scormProvider.isScormUnsupported(scorm)) { return {size: -1, total: false}; } else if (!scorm.packagesize) { @@ -310,7 +315,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand * @return Size, or promise resolved with the size. */ getDownloadedSize(module: any, courseId: number): number | Promise { - return this.scormProvider.getScorm(courseId, module.id, module.url).then((scorm) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { // Get the folder where SCORM should be unzipped. return this.scormProvider.getScormFolder(scorm.moduleurl); }).then((path) => { @@ -327,7 +332,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand * @return Promise resolved with the list of files. */ getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.scormProvider.getScorm(courseId, module.id, module.url).then((scorm) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { return this.scormProvider.getScormFileList(scorm); }).catch(() => { // SCORM not found, return empty list. @@ -366,7 +371,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand * @return Whether the module can be downloaded. The promise should never be rejected. */ isDownloadable(module: any, courseId: number): boolean | Promise { - return this.scormProvider.getScorm(courseId, module.id, module.url).then((scorm) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url}).then((scorm) => { if (scorm.warningMessage) { // SCORM closed or not opened yet. return false; @@ -409,7 +414,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand const siteId = this.sitesProvider.getCurrentSiteId(); let scorm; - return this.scormProvider.getScorm(courseId, module.id, module.url, false, siteId).then((scormData) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scormData) => { scorm = scormData; // Get the folder where SCORM should be unzipped. @@ -448,7 +453,7 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand this.syncProvider = this.injector.get(AddonModScormSyncProvider); } - return this.scormProvider.getScorm(courseId, module.id, module.url, false, siteId).then((scorm) => { + return this.scormProvider.getScorm(courseId, module.id, {moduleUrl: module.url, siteId}).then((scorm) => { return this.syncProvider.syncScorm(scorm, siteId); }); } diff --git a/src/addon/mod/scorm/providers/scorm-sync.ts b/src/addon/mod/scorm/providers/scorm-sync.ts index 59ecbb813..aba64e427 100644 --- a/src/addon/mod/scorm/providers/scorm-sync.ts +++ b/src/addon/mod/scorm/providers/scorm-sync.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; @@ -129,14 +129,20 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * @param scormId SCORM ID. * @param attempt Attempt number. * @param lastOnline Last online attempt number. + * @param cmId Module ID. * @param siteId Site ID. * @return Promise resolved if can retry the synchronization, rejected otherwise. */ - protected canRetrySync(scormId: number, attempt: number, lastOnline: number, siteId: string): Promise { + protected canRetrySync(scormId: number, attempt: number, lastOnline: number, cmId: number, siteId: string): Promise { + // If it's the last attempt we don't need to ignore cache because we already did it. const refresh = lastOnline != attempt; - return this.scormProvider.getScormUserData(scormId, attempt, undefined, false, refresh, siteId).then((siteData) => { + return this.scormProvider.getScormUserData(scormId, attempt, { + cmId, + readingStrategy: refresh ? CoreSitesReadingStrategy.OnlyNetwork : undefined, + siteId, + }).then((siteData) => { // Get synchronization snapshot (if sync fails it should store a snapshot). return this.scormOfflineProvider.getAttemptSnapshot(scormId, attempt, siteId).then((snapshot) => { if (!snapshot || !Object.keys(snapshot).length || !this.snapshotEquals(snapshot, siteData)) { @@ -209,12 +215,16 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide // Check if an attempt was finished in Moodle. if (initialCount) { // Get attempt count again to check if an attempt was finished. - return this.scormProvider.getAttemptCount(scorm.id, undefined, false, siteId).then((attemptsData) => { + return this.scormProvider.getAttemptCount(scorm.id, {cmId: scorm.coursemodule, siteId}).then((attemptsData) => { if (attemptsData.online.length > initialCount.online.length) { return true; } else if (!lastOnlineWasFinished && lastOnline > 0) { // Last online attempt wasn't finished, let's check if it is now. - return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, false, true, siteId).then((inc) => { + return this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { + cmId: scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((inc) => { return !inc; }); } @@ -238,14 +248,19 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * * @param scormId SCORM ID. * @param attempt Attempt number. + * @param cmId Module ID. * @param siteId Site ID. * @return Promise resolved with the data. */ - protected getOfflineAttemptData(scormId: number, attempt: number, siteId: string) + protected getOfflineAttemptData(scormId: number, attempt: number, cmId: number, siteId: string) : Promise<{incomplete: boolean, timecreated: number}> { // Check if last offline attempt is incomplete. - return this.scormProvider.isAttemptIncomplete(scormId, attempt, true, false, siteId).then((incomplete) => { + return this.scormProvider.isAttemptIncomplete(scormId, attempt, { + offline: true, + cmId, + siteId, + }).then((incomplete) => { return this.scormOfflineProvider.getAttemptCreationTime(scormId, attempt, siteId).then((timecreated) => { return { incomplete: incomplete, @@ -363,17 +378,22 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * * @param scormId SCORM ID. * @param attempt Attemot number. + * @param cmId Module ID. * @param siteId Site ID. * @return Promise resolved when the snapshot is stored. */ - protected saveSyncSnapshot(scormId: number, attempt: number, siteId: string): Promise { + protected saveSyncSnapshot(scormId: number, attempt: number, cmId: number, siteId: string): Promise { // Try to get current state from the site. - return this.scormProvider.getScormUserData(scormId, attempt, undefined, false, true, siteId).then((data) => { + return this.scormProvider.getScormUserData(scormId, attempt, { + cmId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((data) => { return this.scormOfflineProvider.setAttemptSnapshot(scormId, attempt, data, siteId); }, () => { // Error getting user data from the site. We'll have to build it ourselves. // Let's try to get cached data about the attempt. - return this.scormProvider.getScormUserData(scormId, attempt, undefined, false, false, siteId).catch(() => { + return this.scormProvider.getScormUserData(scormId, attempt, {cmId, siteId}).catch(() => { // No cached data. return {}; }).then((data) => { @@ -479,7 +499,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide scorms.forEach((scorm) => { if (!this.syncProvider.isBlocked(AddonModScormProvider.COMPONENT, scorm.id, siteId)) { - promises.push(this.scormProvider.getScormById(scorm.courseId, scorm.id, '', false, siteId).then((scorm) => { + promises.push(this.scormProvider.getScormById(scorm.courseId, scorm.id, {siteId}).then((scorm) => { const promise = force ? this.syncScorm(scorm, siteId) : this.syncScormIfNeeded(scorm, siteId); return promise.then((data) => { @@ -506,10 +526,11 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * * @param scormId SCORM ID. * @param attempt Attempt number. + * @param cmId Module ID. * @param siteId Site ID. If not defined, current site. * @return Promise resolved when the attempt is successfully synced. */ - protected syncAttempt(scormId: number, attempt: number, siteId?: string): Promise { + protected syncAttempt(scormId: number, attempt: number, cmId: number, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); this.logger.debug('Try to sync attempt ' + attempt + ' in SCORM ' + scormId + ' and site ' + siteId); @@ -565,7 +586,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide this.logger.error('Error synchronizing some SCOs for attempt ' + attempt + ' in SCORM ' + scormId + '. Saving snapshot.'); - return this.saveSyncSnapshot(scormId, attempt, siteId).then(() => { + return this.saveSyncSnapshot(scormId, attempt, cmId, siteId).then(() => { return Promise.reject(error); }); } else { @@ -629,7 +650,11 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide // Ignore errors. }).then(() => { // Get attempts data. We ignore cache for online attempts, so this call will fail if offline or server down. - return this.scormProvider.getAttemptCount(scorm.id, false, true, siteId); + return this.scormProvider.getAttemptCount(scorm.id, { + cmId: scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }); }).then((attemptsData) => { if (!attemptsData.offline || !attemptsData.offline.length) { // Nothing to sync. @@ -649,8 +674,12 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide }); // Check if last online attempt is finished. Ignore cache. - const promise = lastOnline > 0 ? this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, false, true, siteId) : - Promise.resolve(false); + const promise = lastOnline <= 0 ? Promise.resolve(false) : + this.scormProvider.isAttemptIncomplete(scorm.id, lastOnline, { + cmId: scorm.coursemodule, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }); return promise.then((incomplete) => { lastOnlineWasFinished = !incomplete; @@ -661,7 +690,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide attemptsData.offline.forEach((attempt) => { if (scorm.maxattempt == 0 || attempt <= scorm.maxattempt) { - promises.push(this.syncAttempt(scorm.id, attempt, siteId)); + promises.push(this.syncAttempt(scorm.id, attempt, scorm.coursemodule, siteId)); } }); @@ -672,7 +701,8 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide } else if (collisions.length) { // We have collisions, treat them. - return this.treatCollisions(scorm.id, collisions, lastOnline, attemptsData.offline, siteId).then((warns) => { + return this.treatCollisions(scorm.id, collisions, lastOnline, attemptsData.offline, scorm.coursemodule, siteId) + .then((warns) => { warnings = warnings.concat(warns); // The offline attempts might have changed since some collisions can be converted to new attempts. @@ -694,7 +724,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide // We'll only sync new attemps if last online attempt is completed. if (!incomplete || attempt <= lastOnline) { if (scorm.maxattempt == 0 || attempt <= scorm.maxattempt) { - promises.push(this.syncAttempt(scorm.id, attempt, siteId)); + promises.push(this.syncAttempt(scorm.id, attempt, scorm.coursemodule, siteId)); } } else { cannotSyncSome = true; @@ -730,6 +760,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * @param collisions Numbers of attempts that exist both in online and offline. * @param lastOnline Last online attempt. * @param offlineAttempts Numbers of offline attempts. + * @param cmId Module ID. * @param siteId Site ID. * @return Promise resolved when the collisions have been treated. It returns warnings array. * @description @@ -751,8 +782,8 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * of the list if the last attempt is completed. If the last attempt is not completed then the offline data will de deleted * because we can't create a new attempt. */ - protected treatCollisions(scormId: number, collisions: number[], lastOnline: number, offlineAttempts: number[], siteId: string) - : Promise { + protected treatCollisions(scormId: number, collisions: number[], lastOnline: number, offlineAttempts: number[], cmId: number, + siteId: string): Promise { const warnings = [], newAttemptsSameOrder = [], // Attempts that will be created as new attempts but keeping the current order. @@ -761,7 +792,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide let lastOffline = Math.max.apply(Math, offlineAttempts); // Get needed data from the last offline attempt. - return this.getOfflineAttemptData(scormId, lastOffline, siteId).then((lastOfflineData) => { + return this.getOfflineAttemptData(scormId, lastOffline, cmId, siteId).then((lastOfflineData) => { const promises = []; collisions.forEach((attempt) => { @@ -785,7 +816,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide if (hasDataToSend) { // There are elements to sync. We need to check if it's possible to sync them or not. - return this.canRetrySync(scormId, attempt, lastOnline, siteId).catch(() => { + return this.canRetrySync(scormId, attempt, lastOnline, cmId, siteId).catch(() => { // Cannot retry sync, we'll create a new offline attempt if possible. return this.addToNewOrDelete(scormId, attempt, lastOffline, newAttemptsSameOrder, newAttemptsAtEnd, lastOfflineData.timecreated, lastOfflineData.incomplete, warnings, @@ -806,8 +837,11 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide // If it's the last attempt we don't need to ignore cache because we already did it. const refresh = lastOnline != attempt; - return this.scormProvider.getScormUserData(scormId, attempt, undefined, false, refresh, siteId) - .then((data) => { + return this.scormProvider.getScormUserData(scormId, attempt, { + cmId, + readingStrategy: refresh ? CoreSitesReadingStrategy.OnlyNetwork : undefined, + siteId, + }).then((data) => { if (!this.snapshotEquals(snapshot, data)) { // Snapshot has diverged, it will be converted into a new attempt if possible. diff --git a/src/addon/mod/scorm/providers/scorm.ts b/src/addon/mod/scorm/providers/scorm.ts index 2cec079ec..da74ecc02 100644 --- a/src/addon/mod/scorm/providers/scorm.ts +++ b/src/addon/mod/scorm/providers/scorm.ts @@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreSyncProvider } from '@providers/sync'; import { CoreWSProvider } from '@providers/ws'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -25,9 +25,10 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUrlUtils } from '@providers/utils/url'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { AddonModScormOfflineProvider } from './scorm-offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreConstants } from '@core/constants'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Result of getAttemptCount. @@ -463,24 +464,25 @@ export class AddonModScormProvider { * Get access information for a given SCORM. * * @param scormId SCORM ID. - * @param forceCache True to always get the value from cache. false otherwise. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Object with access information. * @since 3.7 */ - getAccessInformation(scormId: number, forceCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAccessInformation(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { if (!site.wsAvailable('mod_scorm_get_scorm_access_information')) { // Access information not available for 3.6 or older sites. return Promise.resolve({}); } const params = { - scormid: scormId + scormid: scormId, }; const preSets = { cacheKey: this.getAccessInformationCacheKey(scormId), - omitExpires: forceCache + component: AddonModScormProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_scorm_get_scorm_access_information', params, preSets); @@ -501,19 +503,15 @@ export class AddonModScormProvider { * Get the number of attempts done by a user in the given SCORM. * * @param scormId SCORM ID. - * @param ignoreMissing Whether it should ignore attempts without grade/completion. Only for online attempts. - * @param ignoreCache Whether it should ignore cached data for online attempts. - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. + * @param options Other options. * @return Promise resolved when done. */ - getAttemptCount(scormId: number, ignoreMissing?: boolean, ignoreCache?: boolean, siteId?: string, userId?: number) - : Promise { + getAttemptCount(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const result: AddonModScormAttemptCountResult = { lastAttempt: { @@ -523,7 +521,7 @@ export class AddonModScormProvider { }, promises = []; - promises.push(this.getAttemptCountOnline(scormId, ignoreMissing, ignoreCache, siteId, userId).then((count) => { + promises.push(this.getAttemptCountOnline(scormId, options).then((count) => { // Calculate numbers of online attempts. result.online = []; @@ -539,7 +537,7 @@ export class AddonModScormProvider { } })); - promises.push(this.scormOfflineProvider.getAttempts(scormId, siteId, userId).then((attempts) => { + promises.push(this.scormOfflineProvider.getAttempts(scormId, options.siteId, userId).then((attempts) => { // Get only attempt numbers. result.offline = attempts.map((entry) => { // Calculate last attempt. We use >= to prioritize offline events if an attempt is both online and offline. @@ -584,32 +582,26 @@ export class AddonModScormProvider { * Get the number of attempts done by a user in the given SCORM in online. * * @param scormId SCORM ID. - * @param ignoreMissing Whether it should ignore attempts that haven't reported a grade/completion. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. - * @param userId User ID. If not defined use site's current user. + * @param options Other options. * @return Promise resolved when the attempt count is retrieved. */ - getAttemptCountOnline(scormId: number, ignoreMissing?: boolean, ignoreCache?: boolean, siteId?: string, userId?: number) - : Promise { + getAttemptCountOnline(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - userId = userId || site.getUserId(); + return this.sitesProvider.getSite(options.siteId).then((site) => { + const userId = options.userId || site.getUserId(); const params = { - scormid: scormId, - userid: userId, - ignoremissingcompletion: ignoreMissing ? 1 : 0 - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getAttemptCountCacheKey(scormId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + scormid: scormId, + userid: userId, + ignoremissingcompletion: options.ignoreMissing ? 1 : 0, + }; + const preSets = { + cacheKey: this.getAttemptCountCacheKey(scormId, userId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModScormProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_scorm_get_scorm_attempt_count', params, preSets).then((response) => { if (response && typeof response.attemptscount != 'undefined') { @@ -640,7 +632,7 @@ export class AddonModScormProvider { }; // Get the user data and use it to calculate the grade. - return this.getScormUserData(scorm.id, attempt, undefined, offline, false, siteId).then((data) => { + return this.getScormUserData(scorm.id, attempt, {offline, cmId: scorm.coursemodule, siteId}).then((data) => { for (const scoId in data) { const sco = data[scoId], userData = sco.userdata; @@ -694,11 +686,11 @@ export class AddonModScormProvider { * Get the list of a organizations defined in a SCORM package. * * @param scormId SCORM ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of organizations. */ - getOrganizations(scormId: number, siteId?: string): Promise { - return this.getScos(scormId, undefined, false, siteId).then((scos) => { + getOrganizations(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.getScos(scormId, options).then((scos) => { const organizations = []; scos.forEach((sco) => { @@ -721,15 +713,12 @@ export class AddonModScormProvider { * * @param scormId SCORM ID. * @param attempt The attempt number (to populate SCO track data). - * @param organization Organization identifier. - * @param offline Whether the attempt is offline. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the toc object. */ - getOrganizationToc(scormId: number, attempt: number, organization?: string, offline?: boolean, siteId?: string) - : Promise { + getOrganizationToc(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise { - return this.getScosWithData(scormId, attempt, organization, offline, false, siteId).then((scos) => { + return this.getScosWithData(scormId, attempt, options).then((scos) => { const map = {}, rootScos = []; @@ -738,7 +727,7 @@ export class AddonModScormProvider { map[sco.identifier] = index; if (sco.parent !== '/') { - if (sco.parent == organization) { + if (sco.parent == options.organization) { // It's a root SCO, add it to the root array. rootScos.push(sco); } else { @@ -774,26 +763,22 @@ export class AddonModScormProvider { * * @param scormId SCORM ID. * @param attempt Attempt number. - * @param scos SCOs returned by getScos. Recommended if offline=true. - * @param offline Whether the attempt is offline. - * @param ignoreCache Whether it should ignore cached data for online attempts. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the user data is retrieved. */ - getScormUserData(scormId: number, attempt: number, scos?: any[], offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getScormUserData(scormId: number, attempt: number, options: AddonModScormGetUserDataOptions = {}): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - if (offline) { + if (options.offline) { // Get SCOs if not provided. - const promise = scos ? Promise.resolve(scos) : this.getScos(scormId, undefined, undefined, siteId); + const promise = options.scos ? Promise.resolve(options.scos) : this.getScos(scormId, options); return promise.then((scos) => { - return this.scormOfflineProvider.getScormUserData(scormId, attempt, scos, siteId); + return this.scormOfflineProvider.getScormUserData(scormId, attempt, scos, options.siteId); }); } else { - return this.getScormUserDataOnline(scormId, attempt, ignoreCache, siteId); + return this.getScormUserDataOnline(scormId, attempt, options); } } @@ -823,24 +808,21 @@ export class AddonModScormProvider { * * @param scormId SCORM ID. * @param attempt Attempt number. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the user data is retrieved. */ - getScormUserDataOnline(scormId: number, attempt: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getScormUserDataOnline(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - scormid: scormId, - attempt: attempt - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getScormUserDataCacheKey(scormId, attempt) - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + scormid: scormId, + attempt: attempt, + }; + const preSets = { + cacheKey: this.getScormUserDataCacheKey(scormId, attempt), + component: AddonModScormProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_scorm_get_scorm_user_data', params, preSets).then((response) => { if (response && response.data) { @@ -876,37 +858,33 @@ export class AddonModScormProvider { * Retrieves the list of SCO objects for a given SCORM and organization. * * @param scormId SCORM ID. - * @param organization Organization. - * @param ignoreCache Whether it should ignore cached data (it will always fail if offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with a list of SCO. */ - getScos(scormId: number, organization?: string, ignoreCache?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getScos(scormId: number, options: AddonModScormOrganizationOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { // Don't send the organization to the WS, we'll filter them locally. const params = { - scormid: scormId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getScosCacheKey(scormId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + scormid: scormId, + }; + const preSets = { + cacheKey: this.getScosCacheKey(scormId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModScormProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_scorm_get_scorm_scoes', params, preSets).then((response) => { if (response && response.scoes) { - if (organization) { + if (options.organization) { // Filter SCOs by organization. return response.scoes.filter((sco) => { - return sco.organization == organization; + return sco.organization == options.organization; }); } else { return response.scoes; @@ -924,20 +902,21 @@ export class AddonModScormProvider { * * @param scormId SCORM ID. * @param attempt Attempt number. - * @param organization Organization ID. - * @param offline Whether the attempt is offline. - * @param ignoreCache Whether it should ignore cached data for online attempts. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with a list of SCO objects. */ - getScosWithData(scormId: number, attempt: number, organization?: string, offline?: boolean, ignoreCache?: boolean, - siteId?: string): Promise { + getScosWithData(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise { // Get organization SCOs. - return this.getScos(scormId, organization, ignoreCache, siteId).then((scos) => { + return this.getScos(scormId, options).then((scos) => { // Get the track data for all the SCOs in the organization for the given attempt. // We'll use this data to set SCO data like isvisible, status and so. - return this.getScormUserData(scormId, attempt, scos, offline, ignoreCache, siteId).then((data) => { + const userDataOptions = { + scos, + ...options, // Include all options. + }; + + return this.getScormUserData(scormId, attempt, userDataOptions).then((data) => { const trackDataBySCO = {}; @@ -1134,26 +1113,22 @@ export class AddonModScormProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param moduleUrl Module URL. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the SCORM is retrieved. */ - protected getScormByField(courseId: number, key: string, value: any, moduleUrl?: string, forceCache?: boolean, siteId?: string) + protected getScormByField(courseId: number, key: string, value: any, options: AddonModScormGetScormOptions = {}) : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getScormDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (forceCache) { - preSets.omitExpires = true; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getScormDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModScormProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_scorm_get_scorms_by_courses', params, preSets).then((response) => { if (response && response.scorms) { @@ -1173,7 +1148,7 @@ export class AddonModScormProvider { } } - currentScorm.moduleurl = moduleUrl; + currentScorm.moduleurl = options.moduleUrl; return currentScorm; } @@ -1189,13 +1164,11 @@ export class AddonModScormProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param moduleUrl Module URL. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the SCORM is retrieved. */ - getScorm(courseId: number, cmId: number, moduleUrl?: string, forceCache?: boolean, siteId?: string): Promise { - return this.getScormByField(courseId, 'coursemodule', cmId, moduleUrl, forceCache, siteId); + getScorm(courseId: number, cmId: number, options: AddonModScormGetScormOptions = {}): Promise { + return this.getScormByField(courseId, 'coursemodule', cmId, options); } /** @@ -1203,13 +1176,11 @@ export class AddonModScormProvider { * * @param courseId Course ID. * @param id SCORM ID. - * @param moduleUrl Module URL. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the SCORM is retrieved. */ - getScormById(courseId: number, id: number, moduleUrl?: string, forceCache?: boolean, siteId?: string): Promise { - return this.getScormByField(courseId, 'id', id, moduleUrl, forceCache, siteId); + getScormById(courseId: number, id: number, options: AddonModScormGetScormOptions = {}): Promise { + return this.getScormByField(courseId, 'id', id, options); } /** @@ -1314,7 +1285,7 @@ export class AddonModScormProvider { invalidateContent(moduleId: number, courseId: number, siteId?: string, userId?: number): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.getScorm(courseId, moduleId, undefined, false, siteId).then((scorm) => { + return this.getScorm(courseId, moduleId, {siteId}).then((scorm) => { const promises = []; promises.push(this.invalidateAllScormData(scorm.id, siteId, userId)); @@ -1369,15 +1340,12 @@ export class AddonModScormProvider { * * @param scormId SCORM ID. * @param attempt Attempt. - * @param offline Whether the attempt is offline. - * @param ignoreCache Whether it should ignore cached data for online attempts. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with a boolean: true if incomplete, false otherwise. */ - isAttemptIncomplete(scormId: number, attempt: number, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + isAttemptIncomplete(scormId: number, attempt: number, options: AddonModScormOfflineOptions = {}): Promise { - return this.getScosWithData(scormId, attempt, undefined, offline, ignoreCache, siteId).then((scos) => { + return this.getScosWithData(scormId, attempt, options).then((scos) => { for (const i in scos) { const sco = scos[i]; @@ -1563,8 +1531,8 @@ export class AddonModScormProvider { siteId = siteId || this.sitesProvider.getCurrentSiteId(); if (offline) { - const promise = userData ? Promise.resolve(userData) : this.getScormUserData(scorm.id, attempt, undefined, offline, - false, siteId); + const promise = userData ? Promise.resolve(userData) : + this.getScormUserData(scorm.id, attempt, {offline, cmId: scorm.coursemodule, siteId}); return promise.then((userData) => { return this.scormOfflineProvider.saveTracks(scorm, scoId, attempt, tracks, userData, siteId); @@ -1572,7 +1540,7 @@ export class AddonModScormProvider { } else { return this.saveTracksOnline(scorm.id, scoId, attempt, tracks, siteId).then(() => { // Tracks have been saved, update cached user data. - this.updateUserDataAfterSave(scorm.id, attempt, tracks, siteId); + this.updateUserDataAfterSave(scorm.id, attempt, tracks, {cmId: scorm.coursemodule, siteId}); this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { scormId: scorm.id, @@ -1641,7 +1609,7 @@ export class AddonModScormProvider { if (success) { // Tracks have been saved, update cached user data. - this.updateUserDataAfterSave(scorm.id, attempt, tracks); + this.updateUserDataAfterSave(scorm.id, attempt, tracks, {cmId: scorm.coursemodule}); this.eventsProvider.trigger(AddonModScormProvider.DATA_SENT_EVENT, { scormId: scorm.id, @@ -1748,10 +1716,11 @@ export class AddonModScormProvider { * @param scormId SCORM ID. * @param attempt Attempt number. * @param tracks Tracking data saved. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when updated. */ - protected updateUserDataAfterSave(scormId: number, attempt: number, tracks: any[], siteId?: string): Promise { + protected updateUserDataAfterSave(scormId: number, attempt: number, tracks: any[], options: {cmId?: number, siteId?: string}) + : Promise { if (!tracks || !tracks.length) { return Promise.resolve(); } @@ -1767,9 +1736,54 @@ export class AddonModScormProvider { } if (needsUpdate) { - return this.getScormUserDataOnline(scormId, attempt, true, siteId); + return this.getScormUserDataOnline(scormId, attempt, { + cmId: options.cmId, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId: options.siteId, + }); } return Promise.resolve(); } } + +/** + * Options to pass to get SCORM. + */ +export type AddonModScormGetScormOptions = CoreSitesCommonWSOptions & { + moduleUrl?: string; // Module URL. +}; + +/** + * Common options with an organization ID. + */ +export type AddonModScormOrganizationOptions = CoreCourseCommonModWSOptions & { + organization?: string; // Organization ID. +}; + +/** + * Common options with offline boolean. + */ +export type AddonModScormOfflineOptions = CoreCourseCommonModWSOptions & { + offline?: boolean; // Whether the attempt is offline. +}; + +/** + * Options to pass to getAttemptCount. + */ +export type AddonModScormGetAttemptCountOptions = CoreCourseCommonModWSOptions & { + ignoreMissing?: boolean; // Whether it should ignore attempts that haven't reported a grade/completion. + userId?: number; // User ID. If not defined use site's current user. +}; + +/** + * Options to pass to getScormUserData. + */ +export type AddonModScormGetUserDataOptions = AddonModScormOfflineOptions & { + scos?: any[]; // SCOs returned by getScos. Recommended if offline=true. +}; + +/** + * Options to pass to getScosWithData. + */ +export type AddonModScormGetScosWithDataOptions = AddonModScormOfflineOptions & AddonModScormOrganizationOptions; diff --git a/src/addon/mod/survey/components/index/index.ts b/src/addon/mod/survey/components/index/index.ts index b7e71e144..4c375e42b 100644 --- a/src/addon/mod/survey/components/index/index.ts +++ b/src/addon/mod/survey/components/index/index.ts @@ -143,7 +143,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo * @return Promise resolved when done. */ protected fetchQuestions(): Promise { - return this.surveyProvider.getQuestions(this.survey.id).then((questions) => { + return this.surveyProvider.getQuestions(this.survey.id, {cmId: this.module.id}).then((questions) => { this.questions = this.surveyHelper.formatQuestions(questions); // Init answers object. diff --git a/src/addon/mod/survey/providers/prefetch-handler.ts b/src/addon/mod/survey/providers/prefetch-handler.ts index 026d00c0e..076da7258 100644 --- a/src/addon/mod/survey/providers/prefetch-handler.ts +++ b/src/addon/mod/survey/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -125,7 +125,10 @@ export class AddonModSurveyPrefetchHandler extends CoreCourseActivityPrefetchHan * @return Promise resolved when done. */ protected prefetchSurvey(module: any, courseId: number, single: boolean, siteId: string): Promise { - return this.surveyProvider.getSurvey(courseId, module.id, true, siteId).then((survey) => { + return this.surveyProvider.getSurvey(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((survey) => { const promises = [], files = this.getIntroFilesFromInstance(module, survey); @@ -134,7 +137,11 @@ export class AddonModSurveyPrefetchHandler extends CoreCourseActivityPrefetchHan // If survey isn't answered, prefetch the questions. if (!survey.surveydone) { - promises.push(this.surveyProvider.getQuestions(survey.id, true, siteId)); + promises.push(this.surveyProvider.getQuestions(survey.id, { + cmId: module.id, + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + })); } return Promise.all(promises); diff --git a/src/addon/mod/survey/providers/survey.ts b/src/addon/mod/survey/providers/survey.ts index 3063c07e8..9e05db3f4 100644 --- a/src/addon/mod/survey/providers/survey.ts +++ b/src/addon/mod/survey/providers/survey.ts @@ -14,14 +14,15 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModSurveyOfflineProvider } from './offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for surveys. @@ -43,24 +44,21 @@ export class AddonModSurveyProvider { * Get a survey's questions. * * @param surveyId Survey ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the questions are retrieved. */ - getQuestions(surveyId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getQuestions(surveyId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - surveyid: surveyId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getQuestionsCacheKey(surveyId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + surveyid: surveyId, + }; + const preSets = { + cacheKey: this.getQuestionsCacheKey(surveyId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModSurveyProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_survey_get_questions', params, preSets) .then((response: AddonModSurveyGetQuestionsResult): any => { @@ -100,26 +98,22 @@ export class AddonModSurveyProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the survey is retrieved. */ - protected getSurveyDataByKey(courseId: number, key: string, value: any, ignoreCache?: boolean, siteId?: string) + protected getSurveyDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) : Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSurveyCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getSurveyCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModSurveyProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_survey_get_surveys_by_courses', params, preSets) .then((response: AddonModSurveyGetSurveysByCoursesResult): any => { @@ -143,12 +137,11 @@ export class AddonModSurveyProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the survey is retrieved. */ - getSurvey(courseId: number, cmId: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, ignoreCache, siteId); + getSurvey(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, options); } /** @@ -156,12 +149,11 @@ export class AddonModSurveyProvider { * * @param courseId Course ID. * @param id Survey ID. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the survey is retrieved. */ - getSurveyById(courseId: number, id: number, ignoreCache?: boolean, siteId?: string): Promise { - return this.getSurveyDataByKey(courseId, 'id', id, ignoreCache, siteId); + getSurveyById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getSurveyDataByKey(courseId, 'id', id, options); } /** diff --git a/src/addon/mod/url/providers/url.ts b/src/addon/mod/url/providers/url.ts index 49f2cbe7f..5019cf89b 100644 --- a/src/addon/mod/url/providers/url.ts +++ b/src/addon/mod/url/providers/url.ts @@ -14,7 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -107,18 +107,22 @@ export class AddonModUrlProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the url is retrieved. */ - protected getUrlDataByKey(courseId: number, key: string, value: any, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getUrlDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}) + : Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getUrlCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getUrlCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModUrlProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_url_get_urls_by_courses', params, preSets) .then((response: AddonModUrlGetUrlsByCoursesResult): any => { @@ -142,11 +146,11 @@ export class AddonModUrlProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the url is retrieved. */ - getUrl(courseId: number, cmId: number, siteId?: string): Promise { - return this.getUrlDataByKey(courseId, 'coursemodule', cmId, siteId); + getUrl(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getUrlDataByKey(courseId, 'coursemodule', cmId, options); } /** diff --git a/src/addon/mod/wiki/components/index/index.ts b/src/addon/mod/wiki/components/index/index.ts index ca94dd980..1a52936bb 100644 --- a/src/addon/mod/wiki/components/index/index.ts +++ b/src/addon/mod/wiki/components/index/index.ts @@ -303,7 +303,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp this.pageIsOffline = false; - return this.wikiProvider.getPageContents(pageId); + return this.wikiProvider.getPageContents(pageId, {cmId: this.module.id}); } /** @@ -314,7 +314,11 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp protected fetchSubwikiPages(subwiki: any): Promise { let subwikiPages; - return this.wikiProvider.getSubwikiPages(subwiki.wikiid, subwiki.groupid, subwiki.userid).then((pages) => { + return this.wikiProvider.getSubwikiPages(subwiki.wikiid, { + groupId: subwiki.groupid, + userId: subwiki.userid, + cmId: this.module.id, + }).then((pages) => { subwikiPages = pages; // If no page specified, search first page. @@ -356,7 +360,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp * @param wikiId Wiki ID. */ protected fetchSubwikis(wikiId: number): Promise { - return this.wikiProvider.getSubwikis(wikiId).then((subwikis) => { + return this.wikiProvider.getSubwikis(wikiId, {cmId: this.module.id}).then((subwikis) => { this.loadedSubwikis = subwikis; return this.wikiOffline.subwikisHaveOfflineData(subwikis).then((hasOffline) => { diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts index b0bf66db0..90210ca12 100644 --- a/src/addon/mod/wiki/pages/edit/edit.ts +++ b/src/addon/mod/wiki/pages/edit/edit.ts @@ -155,7 +155,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { this.editOffline = false; // Cannot edit pages in offline. // Get page contents to obtain title and editing permission - promise = this.wikiProvider.getPageContents(this.pageId).then((pageContents) => { + promise = this.wikiProvider.getPageContents(this.pageId, {cmId: this.module.id}).then((pageContents) => { this.pageForm.controls.title.setValue(pageContents.title); // Set the title in the form group. this.wikiId = pageContents.wikiid; this.subwikiId = pageContents.subwikiid; @@ -168,7 +168,11 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { return this.wikiSync.waitForSync(this.blockId); }).then(() => { // Get subwiki files, needed to replace URLs for rich text editor. - return this.wikiProvider.getSubwikiFiles(this.wikiId, this.groupId, this.userId); + return this.wikiProvider.getSubwikiFiles(this.wikiId, { + groupId: this.groupId, + userId: this.userId, + cmId: this.module.id, + }); }).then((files) => { this.subwikiFiles = files; @@ -460,7 +464,13 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { // Try to send the page. let wikiId = this.wikiId || (this.module && this.module.instance); - return this.wikiProvider.newPage(title, text, this.subwikiId, wikiId, this.userId, this.groupId).then((id) => { + return this.wikiProvider.newPage(title, text, { + subwikiId: this.subwikiId, + wikiId, + userId: this.userId, + groupId: this.groupId, + cmId: this.module.id, + }).then((id) => { this.domUtils.triggerFormSubmittedEvent(this.formElement, id > 0, this.sitesProvider.getCurrentSiteId()); @@ -470,7 +480,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { // Page was created, get its data and go to the page. this.pageId = id; - return this.wikiProvider.getPageContents(this.pageId).then((pageContents) => { + return this.wikiProvider.getPageContents(this.pageId, {cmId: this.module.id}).then((pageContents) => { const promises = []; wikiId = parseInt(pageContents.wikiid, 10); diff --git a/src/addon/mod/wiki/providers/create-link-handler.ts b/src/addon/mod/wiki/providers/create-link-handler.ts index f56b8ee8e..f592b7510 100644 --- a/src/addon/mod/wiki/providers/create-link-handler.ts +++ b/src/addon/mod/wiki/providers/create-link-handler.ts @@ -48,13 +48,15 @@ export class AddonModWikiCreateLinkHandler extends CoreContentLinksHandlerBase { protected currentStateIsSameWiki(activeView: ViewController, subwikiId: number, siteId: string): Promise { if (activeView && activeView.component.name == 'AddonModWikiIndexPage') { + const moduleId = activeView.data.module && activeView.data.module.id; + if (activeView.data.subwikiId == subwikiId) { // Same subwiki, so it's same wiki. return Promise.resolve(true); } else if (activeView.data.pageId) { // Get the page contents to check the subwiki. - return this.wikiProvider.getPageContents(activeView.data.pageId, false, false, siteId).then((page) => { + return this.wikiProvider.getPageContents(activeView.data.pageId, {cmId: moduleId, siteId}).then((page) => { return page.subwikiid == subwikiId; }).catch(() => { // Not found, return false. @@ -63,15 +65,14 @@ export class AddonModWikiCreateLinkHandler extends CoreContentLinksHandlerBase { } else if (activeView.data.wikiId) { // Check if the subwiki belongs to this wiki. - return this.wikiProvider.wikiHasSubwiki(activeView.data.wikiId, subwikiId, false, false, siteId); + return this.wikiProvider.wikiHasSubwiki(activeView.data.wikiId, subwikiId, {cmId: moduleId, siteId}); } else if (activeView.data.courseId && activeView.data.module) { - const moduleId = activeView.data.module && activeView.data.module.id; if (moduleId) { // Get the wiki. - return this.wikiProvider.getWiki(activeView.data.courseId, moduleId, false, siteId).then((wiki) => { + return this.wikiProvider.getWiki(activeView.data.courseId, moduleId, {siteId}).then((wiki) => { // Check if the subwiki belongs to this wiki. - return this.wikiProvider.wikiHasSubwiki(wiki.id, subwikiId, false, false, siteId); + return this.wikiProvider.wikiHasSubwiki(wiki.id, subwikiId, {cmId: moduleId, siteId}); }).catch(() => { // Not found, return false. return false; diff --git a/src/addon/mod/wiki/providers/page-or-map-link-handler.ts b/src/addon/mod/wiki/providers/page-or-map-link-handler.ts index cad304c40..d148e4a46 100644 --- a/src/addon/mod/wiki/providers/page-or-map-link-handler.ts +++ b/src/addon/mod/wiki/providers/page-or-map-link-handler.ts @@ -55,7 +55,7 @@ export class AddonModWikiPageOrMapLinkHandler extends CoreContentLinksHandlerBas action = url.indexOf('mod/wiki/map.php') != -1 ? 'map' : 'page'; // Get the page data to obtain wikiId, subwikiId, etc. - this.wikiProvider.getPageContents(pageId, false, false, siteId).then((page) => { + this.wikiProvider.getPageContents(pageId, {siteId}).then((page) => { let promise; if (courseId) { promise = Promise.resolve(courseId); diff --git a/src/addon/mod/wiki/providers/prefetch-handler.ts b/src/addon/mod/wiki/providers/prefetch-handler.ts index f9677e893..af4c3e15b 100644 --- a/src/addon/mod/wiki/providers/prefetch-handler.ts +++ b/src/addon/mod/wiki/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesReadingStrategy, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -65,18 +65,15 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl * * @param module The module object returned by WS. * @param courseId The course ID. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return List of pages. */ - protected getAllPages(module: any, courseId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + protected getAllPages(module: any, courseId: number, options: CoreSitesCommonWSOptions = {}): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.wikiProvider.getWiki(courseId, module.id, offline, siteId).then((wiki) => { - return this.wikiProvider.getWikiPageList(wiki, offline, ignoreCache, siteId); + return this.wikiProvider.getWiki(courseId, module.id, options).then((wiki) => { + return this.wikiProvider.getWikiPageList(wiki, options); }).catch(() => { // Wiki not found, return empty list. return []; @@ -100,7 +97,10 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl return this.pluginFileDelegate.getFilesDownloadSize(files); })); - promises.push(this.getAllPages(module, courseId, false, true, siteId).then((pages) => { + promises.push(this.getAllPages(module, courseId, { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }).then((pages) => { let size = 0; pages.forEach((page) => { @@ -133,10 +133,10 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.wikiProvider.getWiki(courseId, module.id, false, siteId).then((wiki) => { + return this.wikiProvider.getWiki(courseId, module.id, {siteId}).then((wiki) => { const introFiles = this.getIntroFilesFromInstance(module, wiki); - return this.wikiProvider.getWikiFileList(wiki, false, false, siteId).then((files) => { + return this.wikiProvider.getWikiFileList(wiki, {siteId}).then((files) => { return introFiles.concat(files); }); }).catch(() => { @@ -191,14 +191,23 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl protected prefetchWiki(module: any, courseId: number, single: boolean, siteId: string, downloadTime: number): Promise { const userId = this.sitesProvider.getCurrentSiteUserId(); + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + // Get the list of pages. - return this.getAllPages(module, courseId, false, true, siteId).then((pages) => { + return this.getAllPages(module, courseId, commonOptions).then((pages) => { const promises = []; pages.forEach((page) => { // Fetch page contents if it needs to be fetched. if (page.timemodified > downloadTime) { - promises.push(this.wikiProvider.getPageContents(page.id, false, true, siteId)); + promises.push(this.wikiProvider.getPageContents(page.id, modOptions)); } }); @@ -206,7 +215,7 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl promises.push(this.groupsProvider.getActivityGroupInfo(module.id, false, userId, siteId)); // Fetch info to provide wiki links. - promises.push(this.wikiProvider.getWiki(courseId, module.id, false, siteId).then((wiki) => { + promises.push(this.wikiProvider.getWiki(courseId, module.id, {siteId}).then((wiki) => { return this.courseHelper.getModuleCourseIdByInstance(wiki.id, 'wiki', siteId); })); diff --git a/src/addon/mod/wiki/providers/wiki-sync.ts b/src/addon/mod/wiki/providers/wiki-sync.ts index 174fedcf6..4309c741c 100644 --- a/src/addon/mod/wiki/providers/wiki-sync.ts +++ b/src/addon/mod/wiki/providers/wiki-sync.ts @@ -266,8 +266,13 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider { // Send the pages. pages.forEach((page) => { - promises.push(this.wikiProvider.newPageOnline(page.title, page.cachedcontent, subwikiId, wikiId, userId, groupId, - siteId).then((pageId) => { + promises.push(this.wikiProvider.newPageOnline(page.title, page.cachedcontent, { + subwikiId, + wikiId, + userId, + groupId, + siteId, + }).then((pageId) => { result.updated = true; @@ -339,7 +344,7 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider { // Ignore errors. }).then(() => { // Sync is done at subwiki level, get all the subwikis. - return this.wikiProvider.getSubwikis(wikiId); + return this.wikiProvider.getSubwikis(wikiId, {cmId}); }).then((subwikis) => { const promises = [], result: AddonModWikiSyncWikiResult = { diff --git a/src/addon/mod/wiki/providers/wiki.ts b/src/addon/mod/wiki/providers/wiki.ts index ee9414c54..76c000570 100644 --- a/src/addon/mod/wiki/providers/wiki.ts +++ b/src/addon/mod/wiki/providers/wiki.ts @@ -16,13 +16,14 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModWikiOfflineProvider } from './wiki-offline'; -import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; +import { CoreSite } from '@classes/site'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; export interface AddonModWikiSubwikiListData { /** @@ -118,27 +119,21 @@ export class AddonModWikiProvider { * Get a wiki page contents. * * @param pageId Page ID. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the page data. */ - getPageContents(pageId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getPageContents(pageId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - pageid: pageId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getPageContentsCacheKey(pageId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + pageid: pageId, + }; + const preSets = { + cacheKey: this.getPageContentsCacheKey(pageId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModWikiProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_wiki_get_page_contents', params, preSets).then((response) => { return response.page || Promise.reject(null); @@ -191,36 +186,27 @@ export class AddonModWikiProvider { * Gets the list of files from a specific subwiki. * * @param wikiId Wiki ID. - * @param groupId Group to get files from. - * @param userId User to get files from. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with subwiki files. */ - getSubwikiFiles(wikiId: number, groupId?: number, userId?: number, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + getSubwikiFiles(wikiId: number, options: AddonModWikiGetSubwikiFilesOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - groupId = groupId || -1; - userId = userId || 0; + return this.sitesProvider.getSite(options.siteId).then((site) => { + const groupId = options.groupId || -1; + const userId = options.userId || 0; const params = { - wikiid: wikiId, - groupid: groupId, - userid: userId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSubwikiFilesCacheKey(wikiId, groupId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + wikiid: wikiId, + groupid: groupId, + userid: userId, + }; + const preSets = { + cacheKey: this.getSubwikiFilesCacheKey(wikiId, groupId, userId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModWikiProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_wiki_get_subwiki_files', params, preSets).then((response) => { return response.files || Promise.reject(null); @@ -264,48 +250,34 @@ export class AddonModWikiProvider { * Get the list of Pages of a SubWiki. * * @param wikiId Wiki ID. - * @param groupId Group to get pages from. - * @param userId User to get pages from. - * @param sortBy The attribute to sort the returned list. - * @param sortDirection Direction to sort the returned list (ASC | DESC). - * @param includeContent Whether the pages have to include its content. Default: false. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with wiki subwiki pages. */ - getSubwikiPages(wikiId: number, groupId?: number, userId?: number, sortBy: string = 'title', sortDirection: string = 'ASC', - includeContent?: boolean, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { + getSubwikiPages(wikiId: number, options: AddonModWikiGetSubwikiPagesOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { - groupId = groupId || -1; - userId = userId || 0; - sortBy = sortBy || 'title'; - sortDirection = sortDirection || 'ASC'; - includeContent = includeContent || false; + return this.sitesProvider.getSite(options.siteId).then((site) => { + const groupId = options.groupId || -1; + const userId = options.userId || 0; + const sortBy = options.sortBy || 'title'; + const sortDirection = options.sortDirection || 'ASC'; const params = { - wikiid: wikiId, - groupid: groupId, - userid: userId, - options: { - sortby: sortBy, - sortdirection: sortDirection, - includecontent: includeContent ? 1 : 0 - } - + wikiid: wikiId, + groupid: groupId, + userid: userId, + options: { + sortby: sortBy, + sortdirection: sortDirection, + includecontent: options.includeContent ? 1 : 0, }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSubwikiPagesCacheKey(wikiId, groupId, userId), - updateFrequency: CoreSite.FREQUENCY_SOMETIMES - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + }; + const preSets = { + cacheKey: this.getSubwikiPagesCacheKey(wikiId, groupId, userId), + updateFrequency: CoreSite.FREQUENCY_SOMETIMES, + component: AddonModWikiProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_wiki_get_subwiki_pages', params, preSets).then((response) => { return response.pages || Promise.reject(null); @@ -339,27 +311,21 @@ export class AddonModWikiProvider { * Get all the subwikis of a wiki. * * @param wikiId Wiki ID. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with subwikis. */ - getSubwikis(wikiId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getSubwikis(wikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - wikiid: wikiId - }, - preSets: CoreSiteWSPreSets = { - cacheKey: this.getSubwikisCacheKey(wikiId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = false; - preSets.emergencyCache = false; - } + wikiid: wikiId, + }; + const preSets = { + cacheKey: this.getSubwikisCacheKey(wikiId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModWikiProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_wiki_get_subwikis', params, preSets).then((response) => { return response.subwikis || Promise.reject(null); @@ -382,12 +348,11 @@ export class AddonModWikiProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the wiki is retrieved. */ - getWiki(courseId: number, cmId: number, forceCache?: boolean, siteId?: string): Promise { - return this.getWikiByField(courseId, 'coursemodule', cmId, forceCache, siteId); + getWiki(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getWikiByField(courseId, 'coursemodule', cmId, options); } /** @@ -396,20 +361,21 @@ export class AddonModWikiProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the wiki is retrieved. */ - protected getWikiByField(courseId: number, key: string, value: any, forceCache?: boolean, siteId?: string): Promise { + protected getWikiByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] - }, - preSets = { - cacheKey: this.getWikiDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY - }; + courseids: [courseId], + }; + const preSets = { + cacheKey: this.getWikiDataCacheKey(courseId), + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModWikiProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. + }; return site.read('mod_wiki_get_wikis_by_courses', params, preSets).then((response) => { if (response.wikis) { @@ -432,12 +398,11 @@ export class AddonModWikiProvider { * * @param courseId Course ID. * @param id Wiki ID. - * @param forceCache Whether it should always return cached data. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the wiki is retrieved. */ - getWikiById(courseId: number, id: number, forceCache?: boolean, siteId?: string): Promise { - return this.getWikiByField(courseId, 'id', id, forceCache, siteId); + getWikiById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getWikiByField(courseId, 'id', id, options); } /** @@ -454,22 +419,29 @@ export class AddonModWikiProvider { * Gets a list of files to download for a wiki, using a format similar to module.contents from get_course_contents. * * @param wiki Wiki. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the list of files. */ - getWikiFileList(wiki: any, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getWikiFileList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); let files = []; + const modOptions = { + cmId: wiki.coursemodule, + ...options, // Include all options. + }; - return this.getSubwikis(wiki.id, offline, ignoreCache, siteId).then((subwikis) => { + return this.getSubwikis(wiki.id, modOptions).then((subwikis) => { const promises = []; subwikis.forEach((subwiki) => { - promises.push(this.getSubwikiFiles(subwiki.wikiid, subwiki.groupid, subwiki.userid, offline, ignoreCache, siteId) - .then((swFiles) => { + const subwikiOptions = { + groupId: subwiki.groupid, + userId: subwiki.userid, + ...modOptions, // Include all options. + }; + + promises.push(this.getSubwikiFiles(subwiki.wikiid, subwikiOptions).then((swFiles) => { files = files.concat(swFiles); })); }); @@ -484,22 +456,27 @@ export class AddonModWikiProvider { * Gets a list of all pages for a Wiki. * * @param wiki Wiki. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Page list. */ - getWikiPageList(wiki: any, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getWikiPageList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); let pages = []; + const modOptions = { + cmId: wiki.coursemodule, + ...options, // Include all options. + }; - return this.getSubwikis(wiki.id, offline, ignoreCache, siteId).then((subwikis) => { + return this.getSubwikis(wiki.id, modOptions).then((subwikis) => { const promises = []; subwikis.forEach((subwiki) => { - promises.push(this.getSubwikiPages(subwiki.wikiid, subwiki.groupid, subwiki.userid, undefined, undefined, - undefined, offline, ignoreCache, siteId).then((subwikiPages) => { + promises.push(this.getSubwikiPages(subwiki.wikiid, { + groupId: subwiki.groupid, + userId: subwiki.userid, + ...modOptions, // Include all options. + }).then((subwikiPages) => { pages = pages.concat(subwikiPages); })); }); @@ -522,7 +499,7 @@ export class AddonModWikiProvider { invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.getWiki(courseId, moduleId, false, siteId).then((wiki) => { + return this.getWiki(courseId, moduleId, {siteId}).then((wiki) => { const promises = []; promises.push(this.invalidateWikiData(courseId, siteId)); @@ -618,16 +595,13 @@ export class AddonModWikiProvider { * @param wikiId Wiki ID. * @param subwikiId Subwiki ID. * @param title Page title. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with true if used, resolved with false if not used or cannot determine. */ - isTitleUsed(wikiId: number, subwikiId: number, title: string, offline?: boolean, ignoreCache?: boolean, siteId?: string) - : Promise { + isTitleUsed(wikiId: number, subwikiId: number, title: string, options: CoreCourseCommonModWSOptions = {}): Promise { // First get the subwiki. - return this.getSubwikis(wikiId, offline, ignoreCache, siteId).then((subwikis) => { + return this.getSubwikis(wikiId, options).then((subwikis) => { // Search the subwiki. const subwiki = subwikis.find((subwiki) => { return subwiki.id == subwikiId; @@ -636,8 +610,11 @@ export class AddonModWikiProvider { return subwiki || Promise.reject(null); }).then((subwiki) => { // Now get all the pages of the subwiki. - return this.getSubwikiPages(wikiId, subwiki.groupid, subwiki.userid, undefined, undefined, false, offline, - ignoreCache, siteId); + return this.getSubwikiPages(wikiId, { + groupId: subwiki.groupid, + userId: subwiki.userid, + ...options, // Include all options. + }); }).then((pages) => { // Check if there's any page with the same title. const page = pages.find((page) => { @@ -690,25 +667,24 @@ export class AddonModWikiProvider { * * @param title Title to create the page. * @param content Content to save on the page. - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - * @param userId User ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - * @param groupId Group ID. Optional, will be used to create a new subwiki if subwikiId not supplied. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with page ID if page was created in server, -1 if stored in device. */ - newPage(title: string, content: string, subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, - siteId?: string): Promise { + newPage(title: string, content: string, options: AddonModWikiNewPageOptions = {}): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); // Convenience function to store a new page to be synchronized later. const storeOffline = (): Promise => { let promise; - if (wikiId) { + if (options.wikiId) { // We have wiki ID, check if there's already an online page with this title and subwiki. - promise = this.isTitleUsed(wikiId, subwikiId, title, true, false, siteId).catch(() => { + promise = this.isTitleUsed(options.wikiId, options.subwikiId, title, { + cmId: options.cmId, + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId: options.siteId, + }).catch(() => { // Error, assume not used. return false; }).then((used) => { @@ -721,7 +697,8 @@ export class AddonModWikiProvider { } return promise.then(() => { - return this.wikiOffline.saveNewPage(title, content, subwikiId, wikiId, userId, groupId, siteId).then(() => { + return this.wikiOffline.saveNewPage(title, content, options.subwikiId, options.wikiId, options.userId, + options.groupId, options.siteId).then(() => { return -1; }); }); @@ -733,9 +710,10 @@ export class AddonModWikiProvider { } // Discard stored content for this page. If it exists it means the user is editing it. - return this.wikiOffline.deleteNewPage(title, subwikiId, wikiId, userId, groupId, siteId).then(() => { + return this.wikiOffline.deleteNewPage(title, options.subwikiId, options.wikiId, options.userId, options.groupId, + options.siteId).then(() => { // Try to create it in online. - return this.newPageOnline(title, content, subwikiId, wikiId, userId, groupId, siteId).catch((error) => { + return this.newPageOnline(title, content, options).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that the page cannot be added. return Promise.reject(error); @@ -752,32 +730,27 @@ export class AddonModWikiProvider { * * @param title Title to create the page. * @param content Content to save on the page. - * @param subwikiId Subwiki ID. If not defined, wikiId, userId and groupId should be defined. - * @param wikiId Wiki ID. Optional, will be used create subwiki if not informed. - * @param userId User ID. Optional, will be used create subwiki if not informed. - * @param groupId Group ID. Optional, will be used create subwiki if not informed. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the page ID if created, rejected otherwise. */ - newPageOnline(title: string, content: string, subwikiId?: number, wikiId?: number, userId?: number, groupId?: number, - siteId?: string): Promise { + newPageOnline(title: string, content: string, options: AddonModWikiNewPageOnlineOptions = {}): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { - title: title, - content: content, - contentformat: 'html' - }; + title: title, + content: content, + contentformat: 'html', + }; - subwikiId = this.wikiOffline.convertToPositiveNumber(subwikiId); - wikiId = this.wikiOffline.convertToPositiveNumber(wikiId); + const subwikiId = this.wikiOffline.convertToPositiveNumber(options.subwikiId); + const wikiId = this.wikiOffline.convertToPositiveNumber(options.wikiId); if (subwikiId && subwikiId > 0) { params.subwikiid = subwikiId; } else if (wikiId) { params.wikiid = wikiId; - params.userid = this.wikiOffline.convertToPositiveNumber(userId); - params.groupid = this.wikiOffline.convertToPositiveNumber(groupId); + params.userid = this.wikiOffline.convertToPositiveNumber(options.userId); + params.groupid = this.wikiOffline.convertToPositiveNumber(options.groupId); } return site.write('mod_wiki_new_page', params).then((response) => { @@ -830,14 +803,12 @@ export class AddonModWikiProvider { * * @param wikiId Wiki ID. * @param subwikiId Subwiki ID to search. - * @param offline Whether it should return cached data. Has priority over ignoreCache. - * @param ignoreCache Whether it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with true if it has subwiki, resolved with false otherwise. */ - wikiHasSubwiki(wikiId: number, subwikiId: number, offline?: boolean, ignoreCache?: boolean, siteId?: string): Promise { + wikiHasSubwiki(wikiId: number, subwikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise { // Get the subwikis to check if any of them matches the one passed as param. - return this.getSubwikis(wikiId, offline, ignoreCache, siteId).then((subwikis) => { + return this.getSubwikis(wikiId, options).then((subwikis) => { const subwiki = subwikis.find((subwiki) => { return subwiki.id == subwikiId; }); @@ -849,3 +820,40 @@ export class AddonModWikiProvider { }); } } + +/** + * Options to pass to getSubwikiFiles. + */ +export type AddonModWikiGetSubwikiFilesOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User to get files from. + groupId?: number; // Group to get files from. +}; + +/** + * Options to pass to getSubwikiPages. + */ +export type AddonModWikiGetSubwikiPagesOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User to get pages from. + groupId?: number; // Group to get pages from. + sortBy?: string; // The attribute to sort the returned list. Defaults to 'title'. + sortDirection?: string; // Direction to sort the returned list (ASC | DESC). Defaults to 'ASC'. + includeContent?: boolean; // Whether the pages have to include their content. +}; + +/** + * Options to pass to newPageOnline. + */ +export type AddonModWikiNewPageOnlineOptions = { + subwikiId?: number; // Subwiki ID. If not defined, wikiId, userId and groupId should be defined. + wikiId?: number; // Wiki ID. Optional, will be used to create a new subwiki if subwikiId not supplied. + userId?: number; // User ID. Optional, will be used to create a new subwiki if subwikiId not supplied. + groupId?: number; // Group ID. Optional, will be used to create a new subwiki if subwikiId not supplied. + siteId?: string; // Site ID. If not defined, current site. +}; + +/** + * Options to pass to newPage. + */ +export type AddonModWikiNewPageOptions = AddonModWikiNewPageOnlineOptions & { + cmId?: number; // Module ID. +}; diff --git a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts index 0eda6bceb..7418e92b5 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -147,8 +147,10 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { * @return Promised resvoled when data is loaded. */ protected load(): Promise { - return this.workshopHelper.getReviewerAssessmentById(this.workshop.id, this.assessmentId, this.userId) - .then((assessmentData) => { + return this.workshopHelper.getReviewerAssessmentById(this.workshop.id, this.assessmentId, { + userId: this.userId, + cmId: this.workshop.coursemodule, + }).then((assessmentData) => { this.data.assessment = assessmentData; let promise; diff --git a/src/addon/mod/workshop/components/assessment/assessment.ts b/src/addon/mod/workshop/components/assessment/assessment.ts index a565bf5a6..c80eed725 100644 --- a/src/addon/mod/workshop/components/assessment/assessment.ts +++ b/src/addon/mod/workshop/components/assessment/assessment.ts @@ -112,8 +112,8 @@ export class AddonModWorkshopAssessmentComponent implements OnInit { if (!this.submission) { const modal = this.domUtils.showModalLoading('core.sending', true); - this.workshopHelper.getSubmissionById(this.workshop.id, this.assessment.submissionid) - .then((submissionData) => { + this.workshopHelper.getSubmissionById(this.workshop.id, this.assessment.submissionid, + {cmId: this.workshop.coursemodule}).then((submissionData) => { params.submission = submissionData; this.navCtrl.push('AddonModWorkshopAssessmentPage', params); diff --git a/src/addon/mod/workshop/components/index/index.ts b/src/addon/mod/workshop/components/index/index.ts index d14b2b04e..d2e7b7903 100644 --- a/src/addon/mod/workshop/components/index/index.ts +++ b/src/addon/mod/workshop/components/index/index.ts @@ -198,7 +198,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity } }).then(() => { // Check if there are answers stored in offline. - return this.workshopProvider.getWorkshopAccessInformation(this.workshop.id); + return this.workshopProvider.getWorkshopAccessInformation(this.workshop.id, {cmId: this.module.id}); }).then((accessData) => { this.access = accessData; @@ -209,7 +209,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity }); } }).then(() => { - return this.workshopProvider.getUserPlanPhases(this.workshop.id); + return this.workshopProvider.getUserPlanPhases(this.workshop.id, {cmId: this.module.id}); }).then((phases) => { this.phases = phases; @@ -245,7 +245,11 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity * @return Resolved when done. */ gotoSubmissionsPage(page: number): Promise { - return this.workshopProvider.getGradesReport(this.workshop.id, this.group, page).then((report) => { + return this.workshopProvider.getGradesReport(this.workshop.id, { + groupId: this.group, + page, + cmId: this.module.id, + }).then((report) => { const numEntries = (report && report.grades && report.grades.length) || 0; this.page = page; @@ -348,7 +352,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity const promises = []; if (this.canSubmit) { - promises.push(this.workshopHelper.getUserSubmission(this.workshop.id).then((submission) => { + promises.push(this.workshopHelper.getUserSubmission(this.workshop.id, {cmId: this.module.id}).then((submission) => { const actions = this.workshopHelper.filterSubmissionActions(this.offlineSubmissions, submission.id || false); return this.workshopHelper.applyOfflineData(submission, actions).then((submission) => { @@ -366,7 +370,9 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity if (this.workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT) { this.canAssess = this.workshopHelper.canAssess(this.workshop, this.access); if (this.canAssess) { - assessPromise = this.workshopHelper.getReviewerAssessments(this.workshop.id).then((assessments) => { + assessPromise = this.workshopHelper.getReviewerAssessments(this.workshop.id, { + cmId: this.module.id, + }).then((assessments) => { const p2 = []; assessments.forEach((assessment) => { @@ -391,13 +397,13 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity } if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - promises.push(this.workshopProvider.getGrades(this.workshop.id).then((grades) => { + promises.push(this.workshopProvider.getGrades(this.workshop.id, {cmId: this.module.id}).then((grades) => { this.userGrades = grades.submissionlongstrgrade || grades.assessmentlongstrgrade ? grades : false; })); if (this.access.canviewpublishedsubmissions) { promises.push(assessPromise.then(() => { - return this.workshopProvider.getSubmissions(this.workshop.id).then((submissions) => { + return this.workshopProvider.getSubmissions(this.workshop.id, {cmId: this.module.id}).then((submissions) => { this.publishedSubmissions = submissions.filter((submission) => { if (submission.published) { this.assessments.forEach((assessment) => { diff --git a/src/addon/mod/workshop/pages/assessment/assessment.ts b/src/addon/mod/workshop/pages/assessment/assessment.ts index 5cc584349..337bd1a63 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.ts +++ b/src/addon/mod/workshop/pages/assessment/assessment.ts @@ -150,7 +150,7 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { }).then((gradeInfo) => { this.maxGrade = gradeInfo.grade; - return this.workshopProvider.getWorkshopAccessInformation(this.workshopId); + return this.workshopProvider.getWorkshopAccessInformation(this.workshopId, {cmId: this.workshop.coursemodule}); }).then((accessData) => { this.access = accessData; @@ -168,8 +168,10 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { if (this.evaluating || this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { // Get all info of the assessment. - return this.workshopHelper.getReviewerAssessmentById(this.workshopId, this.assessmentId, - this.profile && this.profile.id).then((assessment) => { + return this.workshopHelper.getReviewerAssessmentById(this.workshopId, this.assessmentId, { + userId: this.profile && this.profile.id, + cmId: this.workshop.coursemodule, + }).then((assessment) => { let defaultGrade, promise; this.assessment = this.workshopHelper.realGradeValue(this.workshop, assessment); diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts index 6d4f6d704..ea8e03f45 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts @@ -149,7 +149,8 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { if (this.submissionId > 0) { this.editing = true; - return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId).then((submissionData) => { + return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId, {cmId: this.module.id}) + .then((submissionData) => { this.submission = submissionData; const canEdit = (this.userId == submissionData.authorid && this.access.cansubmit && diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts index 245d320ac..f1d0ec97b 100644 --- a/src/addon/mod/workshop/pages/submission/submission.ts +++ b/src/addon/mod/workshop/pages/submission/submission.ts @@ -190,7 +190,9 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { * @return Resolved when done. */ protected fetchSubmissionData(): Promise { - return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId).then((submissionData) => { + return this.workshopHelper.getSubmissionById(this.workshopId, this.submissionId, { + cmId: this.module.id, + }).then((submissionData) => { const promises = []; this.submission = submissionData; @@ -207,8 +209,9 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { if (this.access.canviewallassessments) { // Get new data, different that came from stateParams. - promises.push(this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId) - .then((subAssessments) => { + promises.push(this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId, { + cmId: this.module.id, + }).then((subAssessments) => { // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. if (this.canDelete) { this.canDelete = !subAssessments.length; @@ -228,7 +231,9 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { })); } else if (this.currentUserId == this.userId && this.assessmentId) { // Get new data, different that came from stateParams. - promises.push(this.workshopProvider.getAssessment(this.workshopId, this.assessmentId).then((assessment) => { + promises.push(this.workshopProvider.getAssessment(this.workshopId, this.assessmentId, { + cmId: this.module.id, + }).then((assessment) => { // Only allow the student to delete their own submission if it's still editable and hasn't been assessed. if (this.canDelete) { this.canDelete = !assessment; @@ -239,7 +244,9 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { this.submissionInfo.reviewedby = [assessment]; })); } else if (this.workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED && this.userId == this.currentUserId) { - this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId).then((assessments) => { + this.workshopProvider.getSubmissionAssessments(this.workshopId, this.submissionId, { + cmId: this.module.id, + }).then((assessments) => { this.submissionInfo.reviewedby = assessments.map((assessment) => { return this.parseAssessment(assessment); }); diff --git a/src/addon/mod/workshop/providers/helper.ts b/src/addon/mod/workshop/providers/helper.ts index ad82e99e9..7adf38209 100644 --- a/src/addon/mod/workshop/providers/helper.ts +++ b/src/addon/mod/workshop/providers/helper.ts @@ -19,7 +19,7 @@ import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploa import { CoreSitesProvider } from '@providers/sites'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreUtilsProvider } from '@providers/utils/utils'; -import { AddonModWorkshopProvider } from './workshop'; +import { AddonModWorkshopProvider, AddonModWorkshopUserOptions } from './workshop'; import { AddonModWorkshopOfflineProvider } from './offline'; import { AddonWorkshopAssessmentStrategyDelegate } from './assessment-strategy-delegate'; @@ -109,12 +109,13 @@ export class AddonModWorkshopHelperProvider { * Return a particular user submission from the submission list. * * @param workshopId Workshop ID. - * @param userId User ID. If not defined current user Id. + * @param options Other options. * @return Resolved with the submission, resolved with false if not found. */ - getUserSubmission(workshopId: number, userId: number = 0): Promise { - return this.workshopProvider.getSubmissions(workshopId).then((submissions) => { - userId = userId || this.sitesProvider.getCurrentSiteUserId(); + getUserSubmission(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { + const userId = options.userId || this.sitesProvider.getCurrentSiteUserId(); + + return this.workshopProvider.getSubmissions(workshopId, options).then((submissions) => { for (const x in submissions) { if (submissions[x].authorid == userId) { @@ -131,13 +132,12 @@ export class AddonModWorkshopHelperProvider { * * @param workshopId Workshop ID. * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the submission, resolved with false if not found. */ - getSubmissionById(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.workshopProvider.getSubmission(workshopId, submissionId, siteId).catch(() => { - return this.workshopProvider.getSubmissions(workshopId, undefined, undefined, undefined, undefined, siteId) - .then((submissions) => { + getSubmissionById(workshopId: number, submissionId: number, options: {cmId?: number, siteId?: string} = {}): Promise { + return this.workshopProvider.getSubmission(workshopId, submissionId, options).catch(() => { + return this.workshopProvider.getSubmissions(workshopId, options).then((submissions) => { for (const x in submissions) { if (submissions[x].id == submissionId) { return submissions[x]; @@ -154,14 +154,12 @@ export class AddonModWorkshopHelperProvider { * * @param workshopId Workshop ID. * @param assessmentId Assessment ID. - * @param userId User ID. If not defined, current user. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Resolved with the assessment. */ - getReviewerAssessmentById(workshopId: number, assessmentId: number, userId: number = 0, siteId?: string): Promise { - return this.workshopProvider.getAssessment(workshopId, assessmentId, siteId).catch((error) => { - return this.workshopProvider.getReviewerAssessments(workshopId, userId, undefined, undefined, siteId) - .then((assessments) => { + getReviewerAssessmentById(workshopId: number, assessmentId: number, options: AddonModWorkshopUserOptions = {}): Promise { + return this.workshopProvider.getAssessment(workshopId, assessmentId, options).catch((error) => { + return this.workshopProvider.getReviewerAssessments(workshopId, options).then((assessments) => { for (const x in assessments) { if (assessments[x].id == assessmentId) { return assessments[x]; @@ -172,8 +170,7 @@ export class AddonModWorkshopHelperProvider { return Promise.reject(error); }); }).then((assessment) => { - return this.workshopProvider.getAssessmentForm(workshopId, assessmentId, undefined, undefined, undefined, siteId) - .then((assessmentForm) => { + return this.workshopProvider.getAssessmentForm(workshopId, assessmentId, options).then((assessmentForm) => { assessment.form = assessmentForm; return assessment; @@ -185,20 +182,15 @@ export class AddonModWorkshopHelperProvider { * Retrieves the assessment of the given user and all the related data. * * @param workshopId Workshop ID. - * @param userId User ID. If not defined, current user. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getReviewerAssessments(workshopId: number, userId: number = 0, offline: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + getReviewerAssessments(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { + options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId(); - return this.workshopProvider.getReviewerAssessments(workshopId, userId, offline, ignoreCache, siteId) - .then((assessments) => { + return this.workshopProvider.getReviewerAssessments(workshopId, options).then((assessments) => { const promises = assessments.map((assessment) => { - return this.getSubmissionById(workshopId, assessment.submissionid, siteId).then((submission) => { + return this.getSubmissionById(workshopId, assessment.submissionid, options).then((submission) => { assessment.submission = submission; }); }); diff --git a/src/addon/mod/workshop/providers/prefetch-handler.ts b/src/addon/mod/workshop/providers/prefetch-handler.ts index 1213804c6..9f69bb7d0 100644 --- a/src/addon/mod/workshop/providers/prefetch-handler.ts +++ b/src/addon/mod/workshop/providers/prefetch-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; @@ -68,7 +68,7 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH * @return Promise resolved with the list of files. */ getFiles(module: any, courseId: number, single?: boolean): Promise { - return this.getWorkshopInfoHelper(module, courseId, true).then((info) => { + return this.getWorkshopInfoHelper(module, courseId, {omitFail: true}).then((info) => { return info.files; }); } @@ -78,31 +78,32 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH * * @param module Module to get the files. * @param courseId Course ID the module belongs to. - * @param omitFail True to always return even if fails. Default false. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved with the info fetched. */ - protected getWorkshopInfoHelper(module: any, courseId: number, omitFail: boolean = false, forceCache: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { - let workshop, - groups = [], - files = [], - access; + protected getWorkshopInfoHelper(module: any, courseId: number, options: AddonModWorkshopGetInfoOptions = {}): Promise { + let workshop; + let groups = []; + let files = []; + let access; + const modOptions = { + cmId: module.id, + ...options, // Include all options. + }; - return this.sitesProvider.getSite(siteId).then((site) => { + return this.sitesProvider.getSite(options.siteId).then((site) => { const userId = site.getUserId(); - return this.workshopProvider.getWorkshop(courseId, module.id, siteId, forceCache).then((data) => { + return this.workshopProvider.getWorkshop(courseId, module.id, options).then((data) => { files = this.getIntroFilesFromInstance(module, data); files = files.concat(data.instructauthorsfiles).concat(data.instructreviewersfiles); workshop = data; - return this.workshopProvider.getWorkshopAccessInformation(workshop.id, false, true, siteId).then((accessData) => { + return this.workshopProvider.getWorkshopAccessInformation(workshop.id, modOptions).then((accessData) => { access = accessData; if (access.canviewallsubmissions) { - return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, siteId).then((groupInfo) => { + return this.groupsProvider.getActivityGroupInfo(module.id, false, undefined, options.siteId) + .then((groupInfo) => { if (!groupInfo.groups || groupInfo.groups.length == 0) { groupInfo.groups = [{id: 0}]; } @@ -111,7 +112,7 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH } }); }).then(() => { - return this.workshopProvider.getUserPlanPhases(workshop.id, false, true, siteId).then((phases) => { + return this.workshopProvider.getUserPlanPhases(workshop.id, modOptions).then((phases) => { // Get submission phase info. const submissionPhase = phases[AddonModWorkshopProvider.PHASE_SUBMISSION], canSubmit = this.workshopHelper.canSubmit(workshop, access, submissionPhase.tasks), @@ -119,7 +120,10 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH promises = []; if (canSubmit) { - promises.push(this.workshopHelper.getUserSubmission(workshop.id, userId).then((submission) => { + promises.push(this.workshopHelper.getUserSubmission(workshop.id, { + userId, + cmId: module.id, + }).then((submission) => { if (submission) { files = files.concat(submission.contentfiles).concat(submission.attachmentfiles); } @@ -127,12 +131,13 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH } if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { - promises.push(this.workshopProvider.getSubmissions(workshop.id).then((submissions) => { + promises.push(this.workshopProvider.getSubmissions(workshop.id, modOptions).then((submissions) => { const promises2 = []; submissions.forEach((submission) => { files = files.concat(submission.contentfiles).concat(submission.attachmentfiles); - promises2.push(this.workshopProvider.getSubmissionAssessments(workshop.id, submission.id) - .then((assessments) => { + promises2.push(this.workshopProvider.getSubmissionAssessments(workshop.id, submission.id, { + cmId: module.id, + }).then((assessments) => { assessments.forEach((assessment) => { files = files.concat(assessment.feedbackattachmentfiles) .concat(assessment.feedbackcontentfiles); @@ -146,7 +151,7 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH // Get assessment files. if (workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT && canAssess) { - promises.push(this.workshopHelper.getReviewerAssessments(workshop.id).then((assessments) => { + promises.push(this.workshopHelper.getReviewerAssessments(workshop.id, modOptions).then((assessments) => { assessments.forEach((assessment) => { files = files.concat(assessment.feedbackattachmentfiles).concat(assessment.feedbackcontentfiles); }); @@ -163,7 +168,7 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH files: files.filter((file) => typeof file !== 'undefined') }; }).catch((message): any => { - if (omitFail) { + if (options.omitFail) { // Any error, return the info we have. return { workshop: workshop, @@ -195,8 +200,10 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH * @return Whether the module can be downloaded. The promise should never be rejected. */ isDownloadable(module: any, courseId: number): boolean | Promise { - return this.workshopProvider.getWorkshop(courseId, module.id, undefined, true).then((workshop) => { - return this.workshopProvider.getWorkshopAccessInformation(workshop.id).then((accessData) => { + return this.workshopProvider.getWorkshop(courseId, module.id, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + }).then((workshop) => { + return this.workshopProvider.getWorkshopAccessInformation(workshop.id, {cmId: module.id}).then((accessData) => { // Check if workshop is setup by phase. return accessData.canswitchphase || workshop.phase > AddonModWorkshopProvider.PHASE_SETUP; }); @@ -230,15 +237,15 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH * * @param workshopId Workshop ID. * @param groups Array of groups in the activity. + * @param cmId Module ID. * @param siteId Site ID. If not defined, current site. * @return All unique entries. */ - protected getAllGradesReport(workshopId: number, groups: any[], siteId: string): Promise { + protected getAllGradesReport(workshopId: number, groups: any[], cmId: number, siteId: string): Promise { const promises = []; groups.forEach((group) => { - promises.push(this.workshopProvider.fetchAllGradeReports( - workshopId, group.id, undefined, false, false, siteId)); + promises.push(this.workshopProvider.fetchAllGradeReports(workshopId, {groupId: group.id, cmId, siteId})); }); return Promise.all(promises).then((grades) => { @@ -266,23 +273,31 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH * @return Promise resolved when done. */ protected prefetchWorkshop(module: any, courseId: number, single: boolean, siteId: string): Promise { - const userIds = []; siteId = siteId || this.sitesProvider.getCurrentSiteId(); + const userIds = []; + const commonOptions = { + readingStrategy: CoreSitesReadingStrategy.OnlyNetwork, + siteId, + }; + const modOptions = { + cmId: module.id, + ...commonOptions, // Include all common options. + }; + return this.sitesProvider.getSite(siteId).then((site) => { const currentUserId = site.getUserId(); // Prefetch the workshop data. - return this.getWorkshopInfoHelper(module, courseId, false, false, true, siteId).then((info) => { + return this.getWorkshopInfoHelper(module, courseId, commonOptions).then((info) => { const workshop = info.workshop, promises = [], assessments = []; promises.push(this.filepoolProvider.addFilesToQueue(siteId, info.files, this.component, module.id)); - promises.push(this.workshopProvider.getWorkshopAccessInformation(workshop.id, false, true, siteId) - .then((access) => { - return this.workshopProvider.getUserPlanPhases(workshop.id, false, true, siteId).then((phases) => { + promises.push(this.workshopProvider.getWorkshopAccessInformation(workshop.id, modOptions).then((access) => { + return this.workshopProvider.getUserPlanPhases(workshop.id, modOptions).then((phases) => { // Get submission phase info. const submissionPhase = phases[AddonModWorkshopProvider.PHASE_SUBMISSION], @@ -291,14 +306,14 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH promises2 = []; if (canSubmit) { - promises2.push(this.workshopProvider.getSubmissions(workshop.id)); + promises2.push(this.workshopProvider.getSubmissions(workshop.id, modOptions)); // Add userId to the profiles to prefetch. userIds.push(currentUserId); } let reportPromise = Promise.resolve(); if (access.canviewallsubmissions && workshop.phase >= AddonModWorkshopProvider.PHASE_SUBMISSION) { - reportPromise = this.getAllGradesReport(workshop.id, info.groups, siteId) + reportPromise = this.getAllGradesReport(workshop.id, info.groups, module.id, siteId) .then((grades) => { grades.forEach((grade) => { userIds.push(grade.userid); @@ -322,15 +337,19 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH if (workshop.phase >= AddonModWorkshopProvider.PHASE_ASSESSMENT && canAssess) { // Wait the report promise to finish to override assessments array if needed. reportPromise = reportPromise.finally(() => { - return this.workshopHelper.getReviewerAssessments(workshop.id, currentUserId, undefined, - undefined, siteId).then((revAssessments) => { + return this.workshopHelper.getReviewerAssessments(workshop.id, { + userId: currentUserId, + cmId: module.id, + siteId, + }).then((revAssessments) => { const promises = []; let files = []; // Files in each submission. revAssessments.forEach((assessment) => { if (assessment.submission.authorid == currentUserId) { - promises.push(this.workshopProvider.getAssessment(workshop.id, assessment.id)); + promises.push(this.workshopProvider.getAssessment(workshop.id, assessment.id, + modOptions)); } userIds.push(assessment.reviewerid); userIds.push(assessment.gradinggradeoverby); @@ -350,17 +369,16 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH reportPromise = reportPromise.finally(() => { if (assessments.length > 0) { return Promise.all(assessments.map((assessment, id) => { - return this.workshopProvider.getAssessmentForm(workshop.id, id, undefined, undefined, undefined, - siteId); + return this.workshopProvider.getAssessmentForm(workshop.id, id, modOptions); })); } }); promises2.push(reportPromise); if (workshop.phase == AddonModWorkshopProvider.PHASE_CLOSED) { - promises2.push(this.workshopProvider.getGrades(workshop.id)); + promises2.push(this.workshopProvider.getGrades(workshop.id, modOptions)); if (access.canviewpublishedsubmissions) { - promises2.push(this.workshopProvider.getSubmissions(workshop.id)); + promises2.push(this.workshopProvider.getSubmissions(workshop.id, modOptions)); } } @@ -391,3 +409,10 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH return this.syncProvider.syncWorkshop(module.instance, siteId); } } + +/** + * Options to pass to getWorkshopInfoHelper. + */ +export type AddonModWorkshopGetInfoOptions = CoreSitesCommonWSOptions & { + omitFail?: boolean; // True to always return even if fails. +}; diff --git a/src/addon/mod/workshop/providers/sync.ts b/src/addon/mod/workshop/providers/sync.ts index a7c87e2b4..905f21675 100644 --- a/src/addon/mod/workshop/providers/sync.ts +++ b/src/addon/mod/workshop/providers/sync.ts @@ -204,7 +204,7 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { return Promise.reject(null); } - return this.workshopProvider.getWorkshopById(courseId, workshopId, siteId).then((workshop) => { + return this.workshopProvider.getWorkshopById(courseId, workshopId, {siteId}).then((workshop) => { const submissionsActions = syncs[0], assessments = syncs[1], submissionEvaluations = syncs[2], @@ -289,7 +289,10 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { if (submissionId > 0) { editing = true; - timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, siteId).then((submission) => { + timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, { + cmId: workshop.coursemodule, + siteId, + }).then((submission) => { return submission.timemodified; }).catch(() => { return -1; @@ -403,7 +406,10 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { let discardError; const assessmentId = assessmentData.assessmentid; - const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, siteId).then((assessment) => { + const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, { + cmId: workshop.coursemodule, + siteId, + }).then((assessment) => { return assessment.timemodified; }).catch(() => { return -1; @@ -481,7 +487,10 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { let discardError; const submissionId = evaluate.submissionid; - const timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, siteId).then((submission) => { + const timePromise = this.workshopProvider.getSubmission(workshop.id, submissionId, { + cmId: workshop.coursemodule, + siteId, + }).then((submission) => { return submission.timemodified; }).catch(() => { return -1; @@ -540,7 +549,10 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { let discardError; const assessmentId = evaluate.assessmentid; - const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, siteId).then((assessment) => { + const timePromise = this.workshopProvider.getAssessment(workshop.id, assessmentId, { + cmId: workshop.coursemodule, + siteId, + }).then((assessment) => { return assessment.timemodified; }).catch(() => { return -1; diff --git a/src/addon/mod/workshop/providers/workshop.ts b/src/addon/mod/workshop/providers/workshop.ts index ec8fc292e..cf373a796 100644 --- a/src/addon/mod/workshop/providers/workshop.ts +++ b/src/addon/mod/workshop/providers/workshop.ts @@ -15,11 +15,12 @@ import { Injectable } from '@angular/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; -import { CoreSitesProvider } from '@providers/sites'; +import { CoreSitesProvider, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModWorkshopOfflineProvider } from './offline'; import { CoreSite } from '@classes/site'; +import { CoreCourseCommonModWSOptions } from '@core/course/providers/course'; /** * Service that provides some features for workshops. @@ -202,25 +203,21 @@ export class AddonModWorkshopProvider { * @param courseId Course ID. * @param key Name of the property to check. * @param value Value to search. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the workshop is retrieved. */ - protected getWorkshopByKey(courseId: number, key: string, value: any, siteId?: string, forceCache: boolean = false): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + protected getWorkshopByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - courseids: [courseId] + courseids: [courseId], }; - const preSets: any = { + const preSets = { cacheKey: this.getWorkshopDataCacheKey(courseId), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModWorkshopProvider.COMPONENT, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (forceCache) { - preSets.omitExpires = true; - } - return site.read('mod_workshop_get_workshops_by_courses', params, preSets).then((response) => { if (response && response.workshops) { const workshopFound = response.workshops.find((workshop) => workshop[key] == value); @@ -252,12 +249,11 @@ export class AddonModWorkshopProvider { * * @param courseId Course ID. * @param cmId Course module ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the workshop is retrieved. */ - getWorkshop(courseId: number, cmId: number, siteId?: string, forceCache: boolean = false): Promise { - return this.getWorkshopByKey(courseId, 'coursemodule', cmId, siteId, forceCache); + getWorkshop(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getWorkshopByKey(courseId, 'coursemodule', cmId, options); } /** @@ -265,12 +261,11 @@ export class AddonModWorkshopProvider { * * @param courseId Course ID. * @param id Workshop ID. - * @param siteId Site ID. If not defined, current site. - * @param forceCache True to always get the value from cache, false otherwise. Default false. + * @param options Other options. * @return Promise resolved when the workshop is retrieved. */ - getWorkshopById(courseId: number, id: number, siteId?: string, forceCache: boolean = false): Promise { - return this.getWorkshopByKey(courseId, 'id', id, siteId, forceCache); + getWorkshopById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise { + return this.getWorkshopByKey(courseId, 'id', id, options); } /** @@ -303,28 +298,21 @@ export class AddonModWorkshopProvider { * Get access information for a given workshop. * * @param workshopId Workshop ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop is retrieved. */ - getWorkshopAccessInformation(workshopId: number, offline: boolean = false, ignoreCache: boolean = false, siteId?: string): - Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getWorkshopAccessInformation(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - workshopid: workshopId + workshopid: workshopId, }; - const preSets: any = { - cacheKey: this.getWorkshopAccessInformationDataCacheKey(workshopId) + const preSets = { + cacheKey: this.getWorkshopAccessInformationDataCacheKey(workshopId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_workshop_access_information', params, preSets); }); } @@ -346,28 +334,22 @@ export class AddonModWorkshopProvider { * Return the planner information for the given user. * * @param workshopId Workshop ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getUserPlanPhases(workshopId: number, offline: boolean = false, ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getUserPlanPhases(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { workshopid: workshopId }; - const preSets: any = { + const preSets = { cacheKey: this.getUserPlanDataCacheKey(workshopId), - updateFrequency: CoreSite.FREQUENCY_OFTEN + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_user_plan', params, preSets).then((response) => { if (response && response.userplan && response.userplan.phases) { return this.utils.arrayToObject(response.userplan.phases, 'code'); @@ -395,33 +377,27 @@ export class AddonModWorkshopProvider { * Retrieves all the workshop submissions visible by the current user or the one done by the given user. * * @param workshopId Workshop ID. - * @param userId User ID, 0 means the current user. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop submissions are retrieved. */ - getSubmissions(workshopId: number, userId: number = 0, groupId: number = 0, offline: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getSubmissions(workshopId: number, options: AddonModWorkshopGetSubmissionsOptions = {}): Promise { + const userId = options.userId || 0; + const groupId = options.groupId || 0; + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { workshopid: workshopId, userid: userId, - groupid: groupId + groupid: groupId, }; - const preSets: any = { + const preSets = { cacheKey: this.getSubmissionsDataCacheKey(workshopId, userId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_submissions', params, preSets).then((response) => { if (response && response.submissions) { return response.submissions; @@ -452,16 +428,19 @@ export class AddonModWorkshopProvider { * * @param workshopId Workshop ID. * @param submissionId Submission ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop submission data is retrieved. */ - getSubmission(workshopId: number, submissionId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getSubmission(workshopId: number, submissionId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - submissionid: submissionId + submissionid: submissionId, }; const preSets = { - cacheKey: this.getSubmissionDataCacheKey(workshopId, submissionId) + cacheKey: this.getSubmissionDataCacheKey(workshopId, submissionId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_workshop_get_submission', params, preSets).then((response) => { @@ -492,16 +471,19 @@ export class AddonModWorkshopProvider { * Returns the grades information for the given workshop and user. * * @param workshopId Workshop ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop grades data is retrieved. */ - getGrades(workshopId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getGrades(workshopId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { workshopid: workshopId }; const preSets = { - cacheKey: this.getGradesDataCacheKey(workshopId) + cacheKey: this.getGradesDataCacheKey(workshopId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_workshop_get_grades', params, preSets); @@ -525,35 +507,26 @@ export class AddonModWorkshopProvider { * Retrieves the assessment grades report. * * @param workshopId Workshop ID. - * @param groupId Group id, 0 means that the function will determine the user group. - * @param page Page of records to return. Default 0. - * @param perPage Records per page to return. Default AddonModWorkshopProvider.PER_PAGE. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getGradesReport(workshopId: number, groupId: number = 0, page: number = 0, perPage: number = 0, offline: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getGradesReport(workshopId: number, options: AddonModWorkshopGetGradesReportOptions = {}): Promise { + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { workshopid: workshopId, - groupid: groupId, - page: page, - perpage: perPage || AddonModWorkshopProvider.PER_PAGE + groupid: options.groupId, + page: options.page || 0, + perpage: options.perPage || AddonModWorkshopProvider.PER_PAGE }; - const preSets: any = { - cacheKey: this.getGradesReportDataCacheKey(workshopId, groupId), - updateFrequency: CoreSite.FREQUENCY_OFTEN + const preSets = { + cacheKey: this.getGradesReportDataCacheKey(workshopId, options.groupId), + updateFrequency: CoreSite.FREQUENCY_OFTEN, + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_grades_report', params, preSets).then((response) => { if (response && response.report) { return response.report; @@ -568,44 +541,37 @@ export class AddonModWorkshopProvider { * Performs the whole fetch of the grade reports in the workshop. * * @param workshopId Workshop ID. - * @param groupId Group ID. - * @param perPage Records per page to fetch. It has to match with the prefetch. - * Default on AddonModWorkshopProvider.PER_PAGE. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when done. */ - fetchAllGradeReports(workshopId: number, groupId: number = 0, perPage: number = 0, forceCache: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); - perPage = perPage || AddonModWorkshopProvider.PER_PAGE; - - return this.fetchGradeReportsRecursive(workshopId, groupId, perPage, forceCache, ignoreCache, [], 0, siteId); + fetchAllGradeReports(workshopId: number, options: AddonModWorkshopFetchAllGradesReportOptions = {}): Promise { + return this.fetchGradeReportsRecursive(workshopId, [], { + ...options, // Include all options. + page: 0, + perPage: options.perPage || AddonModWorkshopProvider.PER_PAGE, + siteId: options.siteId || this.sitesProvider.getCurrentSiteId(), + }); } /** * Recursive call on fetch all grade reports. * * @param workshopId Workshop ID. - * @param groupId Group ID. - * @param perPage Records per page to fetch. It has to match with the prefetch. - * @param forceCache True to always get the value from cache, false otherwise. Default false. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). * @param grades Grades already fetched (just to concatenate them). - * @param page Page of records to return. - * @param siteId Site ID. + * @param options Other options. * @return Promise resolved when done. */ - protected fetchGradeReportsRecursive(workshopId: number, groupId: number, perPage: number, forceCache: boolean, - ignoreCache: boolean, grades: any[], page: number, siteId: string): Promise { - return this.getGradesReport(workshopId, groupId, page, perPage, forceCache, ignoreCache, siteId).then((report) => { + protected fetchGradeReportsRecursive(workshopId: number, grades: any[], options: AddonModWorkshopGetGradesReportOptions = {}) + : Promise { + + return this.getGradesReport(workshopId, options).then((report) => { Array.prototype.push.apply(grades, report.grades); - const canLoadMore = ((page + 1) * perPage) < report.totalcount; + const canLoadMore = ((options.page + 1) * options.perPage) < report.totalcount; if (canLoadMore) { - return this.fetchGradeReportsRecursive( - workshopId, groupId, perPage, forceCache, ignoreCache, grades, page + 1, siteId); + options.page++; + + return this.fetchGradeReportsRecursive(workshopId, grades, options); } return grades; @@ -631,28 +597,21 @@ export class AddonModWorkshopProvider { * * @param workshopId Workshop ID. * @param submissionId Submission ID. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getSubmissionAssessments(workshopId: number, submissionId: number, offline: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getSubmissionAssessments(workshopId: number, submissionId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - submissionid: submissionId + submissionid: submissionId, }; - const preSets: any = { - cacheKey: this.getSubmissionAssessmentsDataCacheKey(workshopId, submissionId) + const preSets = { + cacheKey: this.getSubmissionAssessmentsDataCacheKey(workshopId, submissionId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_submission_assessments', params, preSets).then((response) => { if (response && response.assessments) { return response.assessments; @@ -898,31 +857,23 @@ export class AddonModWorkshopProvider { * Retrieves all the assessments reviewed by the given user. * * @param workshopId Workshop ID. - * @param userId User ID. If not defined, current user. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getReviewerAssessments(workshopId: number, userId?: number, offline: boolean = false, ignoreCache: boolean = false, - siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getReviewerAssessments(workshopId: number, options: AddonModWorkshopUserOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params: any = { - workshopid: workshopId + workshopid: workshopId, }; - const preSets: any = { - cacheKey: this.getReviewerAssessmentsDataCacheKey(workshopId, userId) + const preSets = { + cacheKey: this.getReviewerAssessmentsDataCacheKey(workshopId, options.userId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (userId) { - params.userid = userId; - } - - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; + if (options.userId) { + params.userid = options.userId; } return site.read('mod_workshop_get_reviewer_assessments', params, preSets).then((response) => { @@ -954,16 +905,19 @@ export class AddonModWorkshopProvider { * * @param workshopId Workshop ID. * @param assessmentId Assessment ID. - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getAssessment(workshopId: number, assessmentId: number, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAssessment(workshopId: number, assessmentId: number, options: CoreCourseCommonModWSOptions = {}): Promise { + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { - assessmentid: assessmentId + assessmentid: assessmentId, }; const preSets = { - cacheKey: this.getAssessmentDataCacheKey(workshopId, assessmentId) + cacheKey: this.getAssessmentDataCacheKey(workshopId, assessmentId), + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('mod_workshop_get_assessment', params, preSets).then((response) => { @@ -995,31 +949,26 @@ export class AddonModWorkshopProvider { * * @param workshopId Workshop ID. * @param assessmentId Assessment ID. - * @param mode Mode assessment (default) or preview. - * @param offline True if it should return cached data. Has priority over ignoreCache. - * @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down). - * @param siteId Site ID. If not defined, current site. + * @param options Other options. * @return Promise resolved when the workshop data is retrieved. */ - getAssessmentForm(workshopId: number, assessmentId: number, mode: string = 'assessment', offline: boolean = false, - ignoreCache: boolean = false, siteId?: string): Promise { - return this.sitesProvider.getSite(siteId).then((site) => { + getAssessmentForm(workshopId: number, assessmentId: number, options: AddonModWorkshopGetAssessmentFormOptions = {}) + : Promise { + const mode = options.mode || 'assessment'; + + return this.sitesProvider.getSite(options.siteId).then((site) => { const params = { assessmentid: assessmentId, - mode: mode || 'assessment' + mode: mode, }; - const preSets: any = { + const preSets = { cacheKey: this.getAssessmentFormDataCacheKey(workshopId, assessmentId, mode), - updateFrequency: CoreSite.FREQUENCY_RARELY + updateFrequency: CoreSite.FREQUENCY_RARELY, + component: AddonModWorkshopProvider.COMPONENT, + componentId: options.cmId, + ...this.sitesProvider.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; - if (offline) { - preSets.omitExpires = true; - } else if (ignoreCache) { - preSets.getFromCache = 0; - preSets.emergencyCache = 0; - } - return site.read('mod_workshop_get_assessment_form_definition', params, preSets).then((response) => { if (response) { response.fields = this.parseFields(response.fields); @@ -1327,7 +1276,10 @@ export class AddonModWorkshopProvider { invalidateContent(moduleId: number, courseId: number, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.getWorkshop(courseId, moduleId, siteId, true).then((workshop) => { + return this.getWorkshop(courseId, moduleId, { + readingStrategy: CoreSitesReadingStrategy.PreferCache, + siteId, + }).then((workshop) => { return this.invalidateContentById(workshop.id, courseId, siteId); }); } @@ -1399,3 +1351,43 @@ export class AddonModWorkshopProvider { name, 'workshop', params, siteId); } } + +/** + * Common options with a user ID. + */ +export type AddonModWorkshopUserOptions = CoreCourseCommonModWSOptions & { + userId?: number; // User ID. If not defined, current user. +}; + +/** + * Common options with a group ID. + */ +export type AddonModWorkshopGroupOptions = CoreCourseCommonModWSOptions & { + groupId?: number; // Group id, 0 or not defined means that the function will determine the user group. +}; + +/** + * Options to pass to getSubmissions. + */ +export type AddonModWorkshopGetSubmissionsOptions = AddonModWorkshopUserOptions & AddonModWorkshopGroupOptions; + +/** + * Options to pass to fetchAllGradeReports. + */ +export type AddonModWorkshopFetchAllGradesReportOptions = AddonModWorkshopGroupOptions & { + perPage?: number; // Records per page to return. Default AddonModWorkshopProvider.PER_PAGE. +}; + +/** + * Options to pass to getGradesReport. + */ +export type AddonModWorkshopGetGradesReportOptions = AddonModWorkshopFetchAllGradesReportOptions & { + page?: number; // Page of records to return. Default 0. +}; + +/** + * Options to pass to getAssessmentForm. + */ +export type AddonModWorkshopGetAssessmentFormOptions = CoreCourseCommonModWSOptions & { + mode?: string; // Mode assessment (default) or preview. Defaults to 'assessment'. +}; diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index 5a734cd76..e4de0e08e 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -18,7 +18,7 @@ import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; -import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; +import { CoreSitesProvider, CoreSiteSchema, CoreSitesCommonWSOptions } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -1162,6 +1162,13 @@ export class CoreCourseProvider { } } +/** + * Common options used by modules when calling a WS through CoreSite. + */ +export type CoreCourseCommonModWSOptions = CoreSitesCommonWSOptions & { + cmId?: number; // Module ID. +}; + /** * Data returned by course_summary_exporter. */ diff --git a/src/providers/sites.ts b/src/providers/sites.ts index aed300a25..99e60cb42 100644 --- a/src/providers/sites.ts +++ b/src/providers/sites.ts @@ -210,12 +210,24 @@ export interface CoreRegisteredSiteSchema extends CoreSiteSchema { siteId?: string; } +/** + * Possible reading strategies (for cache). + */ export const enum CoreSitesReadingStrategy { OnlyCache, PreferCache, + OnlyNetwork, PreferNetwork, } +/** + * Common options used when calling a WS through CoreSite. + */ +export type CoreSitesCommonWSOptions = { + readingStrategy?: CoreSitesReadingStrategy; // Reading strategy. + siteId?: string; // Site ID. If not defined, current site. +}; + /* * Service to manage and interact with sites. * It allows creating tables in the databases of all sites. Each service or component should be responsible of creating @@ -2008,6 +2020,14 @@ export class CoreSitesProvider { forceOffline: true, }; case CoreSitesReadingStrategy.PreferNetwork: + return { + getFromCache: false, + }; + case CoreSitesReadingStrategy.OnlyNetwork: + return { + getFromCache: false, + emergencyCache: false, + }; default: return {}; } From 02da06a0fec64fb7bd31b5fc9e1a6d02fe78f532 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 10 Sep 2020 10:12:45 +0200 Subject: [PATCH 2/2] MOBILE-3338 course: Always get stored data and adapt strings --- scripts/langindex.json | 3 +- src/addon/block/myoverview/lang/en.json | 1 - .../index/addon-mod-assign-index.html | 2 +- .../index/addon-mod-book-index.html | 2 +- .../index/addon-mod-choice-index.html | 2 +- .../index/addon-mod-data-index.html | 2 +- .../index/addon-mod-feedback-index.html | 2 +- .../index/addon-mod-folder-index.html | 2 +- .../index/addon-mod-forum-index.html | 2 +- .../index/addon-mod-glossary-index.html | 2 +- .../index/addon-mod-h5pactivity-index.html | 2 +- .../index/addon-mod-imscp-index.html | 2 +- .../index/addon-mod-lesson-index.html | 2 +- .../index/addon-mod-page-index.html | 2 +- .../index/addon-mod-quiz-index.html | 2 +- .../index/addon-mod-resource-index.html | 2 +- .../index/addon-mod-scorm-index.html | 2 +- .../index/addon-mod-survey-index.html | 2 +- .../index/addon-mod-wiki-index.html | 2 +- .../index/addon-mod-workshop-index.html | 2 +- .../pages/course-storage/course-storage.ts | 16 +++------ .../pages/courses-storage/courses-storage.ts | 6 ++-- src/assets/lang/en.json | 3 +- src/classes/site.ts | 8 ++--- src/core/course/lang/en.json | 1 + src/core/course/providers/helper.ts | 33 ++++++++++++++----- .../course-progress/course-progress.ts | 2 +- .../core-siteplugins-module-index.html | 2 +- src/lang/en.json | 1 + 29 files changed, 61 insertions(+), 51 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index db78fc2ed..b0020c991 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -45,7 +45,6 @@ "addon.block_myoverview.hiddencourses": "block_myoverview", "addon.block_myoverview.inprogress": "block_myoverview", "addon.block_myoverview.lastaccessed": "block_myoverview", - "addon.block_myoverview.morecourses": "block_myoverview", "addon.block_myoverview.nocourses": "block_myoverview", "addon.block_myoverview.past": "block_myoverview", "addon.block_myoverview.pluginname": "block_myoverview", @@ -1383,6 +1382,7 @@ "core.choose": "moodle", "core.choosedots": "moodle", "core.clearsearch": "local_moodlemobileapp", + "core.clearstoreddata": "local_moodlemobileapp", "core.clicktohideshow": "moodle", "core.clicktoseefull": "local_moodlemobileapp", "core.close": "repository", @@ -1436,6 +1436,7 @@ "core.course.availablespace": "local_moodlemobileapp", "core.course.cannotdeletewhiledownloading": "local_moodlemobileapp", "core.course.confirmdeletemodulefiles": "local_moodlemobileapp", + "core.course.confirmdeletestoreddata": "local_moodlemobileapp", "core.course.confirmdownload": "local_moodlemobileapp", "core.course.confirmdownloadunknownsize": "local_moodlemobileapp", "core.course.confirmdownloadzerosize": "local_moodlemobileapp", diff --git a/src/addon/block/myoverview/lang/en.json b/src/addon/block/myoverview/lang/en.json index 9d38164d8..7bca82636 100644 --- a/src/addon/block/myoverview/lang/en.json +++ b/src/addon/block/myoverview/lang/en.json @@ -6,7 +6,6 @@ "hiddencourses": "Removed from view", "inprogress": "In progress", "lastaccessed": "Last accessed", - "morecourses": "More courses", "nocourses": "No courses", "past": "Past", "pluginname": "Course overview", diff --git a/src/addon/mod/assign/components/index/addon-mod-assign-index.html b/src/addon/mod/assign/components/index/addon-mod-assign-index.html index ee087a9df..18284862e 100644 --- a/src/addon/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addon/mod/assign/components/index/addon-mod-assign-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/book/components/index/addon-mod-book-index.html b/src/addon/mod/book/components/index/addon-mod-book-index.html index ee1e5bbad..74ad51a52 100644 --- a/src/addon/mod/book/components/index/addon-mod-book-index.html +++ b/src/addon/mod/book/components/index/addon-mod-book-index.html @@ -9,7 +9,7 @@ - + diff --git a/src/addon/mod/choice/components/index/addon-mod-choice-index.html b/src/addon/mod/choice/components/index/addon-mod-choice-index.html index 5cc0b88b7..bdadfd1f2 100644 --- a/src/addon/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addon/mod/choice/components/index/addon-mod-choice-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/data/components/index/addon-mod-data-index.html b/src/addon/mod/data/components/index/addon-mod-data-index.html index af4a82ad6..c0a92a4ad 100644 --- a/src/addon/mod/data/components/index/addon-mod-data-index.html +++ b/src/addon/mod/data/components/index/addon-mod-data-index.html @@ -12,7 +12,7 @@ - + diff --git a/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html index c7edc940d..d4f10a815 100644 --- a/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addon/mod/feedback/components/index/addon-mod-feedback-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/folder/components/index/addon-mod-folder-index.html b/src/addon/mod/folder/components/index/addon-mod-folder-index.html index 2aa7976f3..e56091080 100644 --- a/src/addon/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addon/mod/folder/components/index/addon-mod-folder-index.html @@ -6,7 +6,7 @@ - + diff --git a/src/addon/mod/forum/components/index/addon-mod-forum-index.html b/src/addon/mod/forum/components/index/addon-mod-forum-index.html index 31e15b3e0..4f00d77b0 100644 --- a/src/addon/mod/forum/components/index/addon-mod-forum-index.html +++ b/src/addon/mod/forum/components/index/addon-mod-forum-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html b/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html index 0c01a1288..475d5e969 100644 --- a/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html +++ b/src/addon/mod/glossary/components/index/addon-mod-glossary-index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index 23df4f6ad..cd0c7f9e8 100644 --- a/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addon/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -8,7 +8,7 @@ - + diff --git a/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html b/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html index 9f767d27c..7d14a8a56 100644 --- a/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html +++ b/src/addon/mod/imscp/components/index/addon-mod-imscp-index.html @@ -9,7 +9,7 @@ - + diff --git a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html index 834abcf38..ebe107144 100644 --- a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/page/components/index/addon-mod-page-index.html b/src/addon/mod/page/components/index/addon-mod-page-index.html index d3d6c7726..783080214 100644 --- a/src/addon/mod/page/components/index/addon-mod-page-index.html +++ b/src/addon/mod/page/components/index/addon-mod-page-index.html @@ -6,7 +6,7 @@ - + diff --git a/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html index ec8685448..d7586c3ac 100644 --- a/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addon/mod/quiz/components/index/addon-mod-quiz-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/resource/components/index/addon-mod-resource-index.html b/src/addon/mod/resource/components/index/addon-mod-resource-index.html index e6aa1efff..f910fabfb 100644 --- a/src/addon/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addon/mod/resource/components/index/addon-mod-resource-index.html @@ -6,7 +6,7 @@ - + diff --git a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html index cc16da2c2..b3e5d565e 100644 --- a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/survey/components/index/addon-mod-survey-index.html b/src/addon/mod/survey/components/index/addon-mod-survey-index.html index eb101c594..9b3531df3 100644 --- a/src/addon/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addon/mod/survey/components/index/addon-mod-survey-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html index 44a57c1ca..f9a3da731 100644 --- a/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addon/mod/wiki/components/index/addon-mod-wiki-index.html @@ -19,7 +19,7 @@ - + diff --git a/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html index f662c02ae..7e8daa461 100644 --- a/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addon/mod/workshop/components/index/addon-mod-workshop-index.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/storagemanager/pages/course-storage/course-storage.ts b/src/addon/storagemanager/pages/course-storage/course-storage.ts index f3ec571e1..7dace4b7b 100644 --- a/src/addon/storagemanager/pages/course-storage/course-storage.ts +++ b/src/addon/storagemanager/pages/course-storage/course-storage.ts @@ -20,7 +20,6 @@ import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { TranslateService } from '@ngx-translate/core'; import { CoreConstants } from '@core/constants'; -import { CoreSitesProvider } from '@providers/sites'; /** * Page that displays the amount of file storage used by each activity on the course, and allows @@ -40,7 +39,6 @@ export class AddonStorageManagerCourseStoragePage { totalSize: number; constructor(navParams: NavParams, - private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private courseHelperProvider: CoreCourseHelperProvider, @@ -102,7 +100,7 @@ export class AddonStorageManagerCourseStoragePage { */ async deleteForCourse(): Promise { try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; @@ -132,7 +130,7 @@ export class AddonStorageManagerCourseStoragePage { */ async deleteForSection(section: any): Promise { try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; @@ -162,7 +160,7 @@ export class AddonStorageManagerCourseStoragePage { } try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; @@ -186,13 +184,7 @@ export class AddonStorageManagerCourseStoragePage { const promises = []; modules.forEach((module) => { // Remove the files. - const promise = this.prefetchDelegate.removeModuleFiles(module, this.course.id).then(() => { - const handler = this.prefetchDelegate.getPrefetchHandlerFor(module); - if (handler) { - - return this.sitesProvider.getCurrentSite().deleteComponentFromCache(handler.component, module.id); - } - }).then(() => { + const promise = this.courseHelperProvider.removeModuleStoredData(module, this.course.id).then(() => { // When the files and cache are removed, update the size. module.parentSection.totalSize -= module.totalSize; this.totalSize -= module.totalSize; diff --git a/src/addon/storagemanager/pages/courses-storage/courses-storage.ts b/src/addon/storagemanager/pages/courses-storage/courses-storage.ts index 05b5aa709..5beb27b09 100644 --- a/src/addon/storagemanager/pages/courses-storage/courses-storage.ts +++ b/src/addon/storagemanager/pages/courses-storage/courses-storage.ts @@ -92,7 +92,7 @@ export class AddonStorageManagerCoursesStoragePage { */ async deleteCompletelyDownloadedCourses(): Promise { try { - await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; @@ -122,7 +122,7 @@ export class AddonStorageManagerCoursesStoragePage { */ async deleteCourse(course: DownloadedCourse): Promise { try { - await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await CoreDomUtils.instance.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; @@ -206,7 +206,7 @@ export class AddonStorageManagerCoursesStoragePage { const sections = await CoreCourse.instance.getSections(courseId); const modules = CoreArray.flatten(sections.map((section) => section.modules)); const promisedModuleSizes = modules.map(async (module) => { - const size = await CoreCourseModulePrefetch.instance.getModuleDownloadedSize(module, courseId); + const size = await CoreCourseModulePrefetch.instance.getModuleStoredSize(module, courseId); return isNaN(size) ? 0 : size; }); diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 5be7a28c6..d5fd154d5 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -45,7 +45,6 @@ "addon.block_myoverview.hiddencourses": "Removed from view", "addon.block_myoverview.inprogress": "In progress", "addon.block_myoverview.lastaccessed": "Last accessed", - "addon.block_myoverview.morecourses": "More courses", "addon.block_myoverview.nocourses": "No courses", "addon.block_myoverview.past": "Past", "addon.block_myoverview.pluginname": "Course overview", @@ -1384,6 +1383,7 @@ "core.choose": "Choose", "core.choosedots": "Choose...", "core.clearsearch": "Clear search", + "core.clearstoreddata": "Clear storage {{$a}}", "core.clicktohideshow": "Click to expand or collapse", "core.clicktoseefull": "Click to see full contents.", "core.close": "Close", @@ -1437,6 +1437,7 @@ "core.course.availablespace": " You currently have about {{available}} free space.", "core.course.cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.", "core.course.confirmdeletemodulefiles": "Are you sure you want to delete these files?", + "core.course.confirmdeletestoreddata": "Are you sure you want to delete the stored data?", "core.course.confirmdownload": "You are about to download {{size}}.{{availableSpace}} Are you sure you want to continue?", "core.course.confirmdownloadunknownsize": "It was not possible to calculate the size of the download.{{availableSpace}} Are you sure you want to continue?", "core.course.confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?", diff --git a/src/classes/site.ts b/src/classes/site.ts index 14bbf40fb..10c9c3e27 100644 --- a/src/classes/site.ts +++ b/src/classes/site.ts @@ -1107,10 +1107,10 @@ export class CoreSite { * @param componentId Optional component id (if not included, returns sum for whole component) * @return Promise resolved when we have calculated the size */ - getComponentCacheSize(component: string, componentId?: string): Promise { - const params = [component]; + getComponentCacheSize(component: string, componentId?: number): Promise { + const params: any[] = [component]; let extraClause = ''; - if (componentId) { + if (componentId !== undefined && componentId !== null) { params.push(componentId); extraClause = ' AND componentId = ?'; } @@ -1200,7 +1200,7 @@ export class CoreSite { * @param componentId Component id. * @return Promise resolved when the entries are deleted. */ - async deleteComponentFromCache(component: string, componentId?: string): Promise { + async deleteComponentFromCache(component: string, componentId?: number): Promise { if (!component) { return; } diff --git a/src/core/course/lang/en.json b/src/core/course/lang/en.json index 69cbe5cf9..2a74a13a0 100644 --- a/src/core/course/lang/en.json +++ b/src/core/course/lang/en.json @@ -7,6 +7,7 @@ "availablespace": " You currently have about {{available}} free space.", "cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.", "confirmdeletemodulefiles": "Are you sure you want to delete these files?", + "confirmdeletestoreddata": "Are you sure you want to delete the stored data?", "confirmdownload": "You are about to download {{size}}.{{availableSpace}} Are you sure you want to continue?", "confirmdownloadunknownsize": "It was not possible to calculate the size of the download.{{availableSpace}} Are you sure you want to continue?", "confirmdownloadzerosize": "You are about to start downloading.{{availableSpace}} Are you sure you want to continue?", diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index 99e181b96..bc1d52fef 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -419,16 +419,11 @@ export class CoreCourseHelperProvider { try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); modal = this.domUtils.showModalLoading(); - await this.prefetchDelegate.removeModuleFiles(module, courseId); - - const handler = this.prefetchDelegate.getPrefetchHandlerFor(module); - if (handler) { - await this.sitesProvider.getCurrentSite().deleteComponentFromCache(handler.component, String(module.id)); - } + await this.removeModuleStoredData(module, courseId); done && done(); @@ -859,7 +854,7 @@ export class CoreCourseHelperProvider { if (typeof instance.contextFileStatusObserver == 'undefined' && component) { // Debounce the update size function to prevent too many calls when downloading or deleting a whole activity. const debouncedUpdateSize = this.utils.debounce(() => { - this.prefetchDelegate.getModuleDownloadedSize(module, courseId).then((moduleSize) => { + this.prefetchDelegate.getModuleStoredSize(module, courseId).then((moduleSize) => { instance.size = moduleSize > 0 ? this.textUtils.bytesToSize(moduleSize, 2) : 0; }); }, 1000); @@ -1630,12 +1625,32 @@ export class CoreCourseHelperProvider { const modules = CoreArray.flatten(sections.map((section) => section.modules)); await Promise.all( - modules.map((module) => this.prefetchDelegate.removeModuleFiles(module, courseId)), + modules.map((module) => this.removeModuleStoredData(module, courseId)), ); await this.courseProvider.setCourseStatus(courseId, CoreConstants.NOT_DOWNLOADED); } + /** + * Remove module stored data. + * + * @param module Module to remove the files. + * @param courseId Course ID the module belongs to. + * @return Promise resolved when done. + */ + async removeModuleStoredData(module: any, courseId: number): Promise { + const promises = []; + + promises.push(this.prefetchDelegate.removeModuleFiles(module, courseId)); + + const handler = this.prefetchDelegate.getPrefetchHandlerFor(module); + if (handler) { + promises.push(this.sitesProvider.getCurrentSite().deleteComponentFromCache(handler.component, module.id)); + } + + await Promise.all(promises); + } + } export class CoreCourseHelper extends makeSingleton(CoreCourseHelperProvider) {} diff --git a/src/core/courses/components/course-progress/course-progress.ts b/src/core/courses/components/course-progress/course-progress.ts index 88923c044..1d30aa267 100644 --- a/src/core/courses/components/course-progress/course-progress.ts +++ b/src/core/courses/components/course-progress/course-progress.ts @@ -163,7 +163,7 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy { */ async deleteCourse(): Promise { try { - await this.domUtils.showDeleteConfirm('core.course.confirmdeletemodulefiles'); + await this.domUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); } catch (error) { if (!error.coreCanceled) { throw error; diff --git a/src/core/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/siteplugins/components/module-index/core-siteplugins-module-index.html index a601f05c3..54836ba2e 100644 --- a/src/core/siteplugins/components/module-index/core-siteplugins-module-index.html +++ b/src/core/siteplugins/components/module-index/core-siteplugins-module-index.html @@ -5,7 +5,7 @@ - + diff --git a/src/lang/en.json b/src/lang/en.json index f00f084d3..23d49ca72 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -26,6 +26,7 @@ "choose": "Choose", "choosedots": "Choose...", "clearsearch": "Clear search", + "clearstoreddata": "Clear storage {{$a}}", "clicktohideshow": "Click to expand or collapse", "clicktoseefull": "Click to see full contents.", "close": "Close",