MOBILE-3338 mod: Apply component + id to mod WS calls

main
Dani Palou 2020-09-02 16:00:18 +02:00
parent 8e6412d0ee
commit 421634dff7
109 changed files with 3535 additions and 2929 deletions

View File

@ -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<any> {
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';

View File

@ -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 {

View File

@ -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.

View File

@ -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[]]) => {

View File

@ -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) {

View File

@ -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<AddonModAssignAssign> {
return this.getAssignmentByField(courseId, 'cmid', cmId, ignoreCache, siteId);
getAssignment(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModAssignAssign> {
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<AddonModAssignAssign> {
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<AddonModAssignAssign> {
return this.getAssignmentByField(courseId, 'id', id, ignoreCache, siteId);
getAssignmentById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModAssignAssign> {
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<number> {
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<number> {
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<AddonModAssignGrade[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
assignmentids: [assignId]
},
preSets: CoreSiteWSPreSets = {
cacheKey: this.getAssignmentGradesCacheKey(assignId)
};
getAssignmentGrades(assignId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModAssignGrade[]> {
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<AddonModAssignGetSubmissionStatusResult> {
getSubmissionStatus(assignId: number, options: AddonModAssignSubmissionStatusOptions = {})
: Promise<AddonModAssignGetSubmissionStatusResult> {
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<AddonModAssignGetSubmissionStatusResult> {
getSubmissionStatusWithRetry(assign: any, options: AddonModAssignSubmissionStatusOptions = {})
: Promise<AddonModAssignGetSubmissionStatusResult> {
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<AddonModAssignParticipant[]> {
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<any> {
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.
*/

View File

@ -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<AddonModAssignParticipant[]> {
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<AddonModAssignSubmissionFormatted[]> {
options: CoreSitesCommonWSOptions = {}): Promise<AddonModAssignSubmissionFormatted[]> {
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;
});
}

View File

@ -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<boolean> {
// 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<any[]> {
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<any> {
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<any> {
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.

View File

@ -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<AddonModBookBook> {
return this.getBookByField(courseId, 'coursemodule', cmId, siteId);
getBook(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModBookBook> {
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<AddonModBookBook> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getBookByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModBookBook> {
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 => {

View File

@ -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);

View File

@ -60,8 +60,8 @@ export class AddonModChatSessionMessagesPage {
* @return Promise resolved when done.
*/
protected fetchMessages(): Promise<any> {
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 = <AddonModChatSessionMessageForView[]> messages;

View File

@ -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 = [];

View File

@ -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);

View File

@ -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<AddonModChatChat> {
return this.sitesProvider.getSite(siteId).then((site) => {
getChat(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModChatChat> {
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<AddonModChatGetChatUsersResult> {
const params = {
chatsid: sessionId
};
const preSets = {
getFromCache: false
};
getChatUsers(sessionId: string, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChatGetChatUsersResult> {
// 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<AddonModChatSession[]> {
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<AddonModChatSessionMessage[]> {
getSessionMessages(chatId: number, sessionStart: number, sessionEnd: number, groupId: number = 0,
options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChatSessionMessage[]> {
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 => {

View File

@ -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<any> {
// Prefetch chat and group info.
const promises: Promise<any>[] = [
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<any> {
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<any> {
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;

View File

@ -174,7 +174,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
* @return Promise resolved when done.
*/
protected fetchOptions(hasOffline: boolean): Promise<any> {
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 = [];

View File

@ -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<AddonModChoiceChoice> {
protected getChoiceByDataKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModChoiceChoice> {
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<AddonModChoiceChoice> {
return this.getChoiceByDataKey(siteId, courseId, 'coursemodule', cmId, forceCache, ignoreCache);
getChoice(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModChoiceChoice> {
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<AddonModChoiceChoice> {
return this.getChoiceByDataKey(siteId, courseId, 'id', choiceId, forceCache, ignoreCache);
getChoiceById(courseId: number, choiceId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModChoiceChoice> {
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<AddonModChoiceOption[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getOptions(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChoiceOption[]> {
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<AddonModChoiceResult[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getResults(choiceId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModChoiceResult[]> {
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 => {

View File

@ -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<any> {
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) => {

View File

@ -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<any> {
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;

View File

@ -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');

View File

@ -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;
});
}

View File

@ -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<AddonModDataEntry[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
fetchAllEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise<AddonModDataEntry[]> {
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<AddonModDataEntry[]> {
return this.getEntries(dataId, groupId, sort, order, page, perPage, forceCache, ignoreCache, siteId)
.then((result) => {
protected fetchEntriesRecursive(dataId: number, entries: any, options: AddonModDataGetEntriesOptions = {})
: Promise<AddonModDataEntry[]> {
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<any> {
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<any> {
return this.getDatabaseByKey(courseId, 'coursemodule', cmId, siteId, forceCache);
getDatabase(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getDatabaseByKey(courseId, 'id', id, siteId, forceCache);
getDatabaseById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getDatabaseAccessInformation(dataId: number, options: AddonModDataAccessInfoOptions = {}): Promise<any> {
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<AddonModDataEntries> {
return this.sitesProvider.getSite(siteId).then((site) => {
getEntries(dataId: number, options: AddonModDataGetEntriesOptions = {}): Promise<AddonModDataEntries> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getFields(dataId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<AddonModDataEntries> {
return this.sitesProvider.getSite(siteId).then((site) => {
searchEntries(dataId: number, options?: AddonModDataSearchEntriesOptions): Promise<AddonModDataEntries> {
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.
};

View File

@ -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<AddonModDataEntries> {
return this.sitesProvider.getSite(siteId).then((site) => {
fetchEntries(data: any, fields: any[], options: AddonModDataSearchEntriesOptions = {}): Promise<AddonModDataEntries> {
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<void>;
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({

View File

@ -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<AddonModDataEntry[]> {
protected getAllUniqueEntries(dataId: number, groups: any[], options: CoreSitesCommonWSOptions = {})
: Promise<AddonModDataEntry[]> {
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<any> {
protected getDatabaseInfoHelper(module: any, courseId: number, omitFail: boolean, options: CoreSitesCommonWSOptions = {})
: Promise<any> {
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<boolean> {
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<any> {
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,

View File

@ -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<any> {
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 = [];

View File

@ -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<any> {
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;

View File

@ -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) => {

View File

@ -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<any> {
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<any> {
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<void> {
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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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<any> {
return this.getCurrentValues(feedbackId, offline, ignoreCache, siteId).then((valuesArray) => {
protected fillValues(feedbackId: number, items: any[], options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
getAllNonRespondents(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise<any> {
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<any> {
getAllResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}, previous?: any): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getAnalysis(feedbackId: number, options: AddonModFeedbackGroupOptions = {}): Promise<any> {
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<any> {
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<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getCurrentCompletedTimeModified(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getCurrentValues(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getFeedbackAccessInformation(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
protected getFeedbackDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<any> {
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<any> {
return this.getFeedbackDataByKey(courseId, 'coursemodule', cmId, siteId, forceCache, ignoreCache);
getFeedback(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getFeedbackDataByKey(courseId, 'id', id, siteId, forceCache, ignoreCache);
getFeedbackById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getItems(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise<any> {
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<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
getPageItemsWithValues(feedbackId: number, page: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<number | false> {
return this.getPageItemsWithValues(feedbackId, page, true, false, siteId).then((resp) => {
protected getPageJumpTo(feedbackId: number, page: number, changePage: number, options: {cmId?: number, siteId?: string})
: Promise<number | false> {
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<any> {
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<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getResumePage(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => {
isCompleted(feedbackId: number, options: CoreCourseCommonModWSOptions = {}): Promise<boolean> {
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<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
processPage(feedbackId: number, page: number, responses: any, options: AddonModFeedbackProcessPageOptions = {}): Promise<any> {
options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId();
// Convenience function to store a message to be synchronized later.
const storeOffline = (): Promise<any> => {
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.;
};

View File

@ -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<any> {
return this.feedbackProvider.getNonRespondents(feedbackId, groupId, page).then((responses) => {
getNonRespondents(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise<any> {
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<any> {
return this.feedbackProvider.getResponsesAnalysis(feedbackId, groupId, page).then((responses) => {
getResponsesAnalysis(feedbackId: number, options: AddonModFeedbackGroupPaginatedOptions = {}): Promise<any> {
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,

View File

@ -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<boolean> {
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<any> {
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);
}));

View File

@ -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;

View File

@ -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<AddonModFolderFolder> {
return this.getFolderByKey(courseId, 'coursemodule', cmId, siteId);
getFolder(courseId: number, cmId: number, options?: CoreSitesCommonWSOptions): Promise<AddonModFolderFolder> {
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<AddonModFolderFolder> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getFolderByKey(courseId: number, key: string, value: any, options?: CoreSitesCommonWSOptions)
: Promise<AddonModFolderFolder> {
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 => {

View File

@ -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;

View File

@ -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);

View File

@ -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.');
}

View File

@ -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) {

View File

@ -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;

View File

@ -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<any[]> {
// 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;

View File

@ -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<any> {
canAddDiscussion(forumId: number, groupId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, siteId);
canAddDiscussionToAll(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getCourseForums(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise<any[]> {
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<any> {
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<any> {
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<any> {
return this.getCourseForums(courseId, siteId).then((forums) => {
getForum(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getCourseForums(courseId, siteId).then((forums) => {
getForumById(courseId: number, forumId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getAccessInformation(forumId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
sortOrder = sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC;
getDiscussions(forumId: number, options: AddonModForumGetDiscussionsOptions = {}): Promise<any> {
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<any> {
if (typeof numPages == 'undefined') {
numPages = -1;
}
startPage = startPage || 0;
getDiscussionsInPages(forumId: number, options: AddonModForumGetDiscussionsInPagesOptions = {}): Promise<any> {
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<any> => {
// 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.
};

View File

@ -279,7 +279,11 @@ export class AddonModForumHelperProvider {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const findDiscussion = (page: number): Promise<any> => {
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);

View File

@ -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 }) : '';

View File

@ -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<any[]> {
protected getPostsForPrefetch(forum: any, options: CoreCourseCommonModWSOptions = {}): Promise<any[]> {
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<any> {
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.
}));
});

View File

@ -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', {

View File

@ -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 {

View File

@ -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.

View File

@ -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);

View File

@ -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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getCourseGlossaries(courseId: number, options: CoreSitesCommonWSOptions = {}): Promise<any[]> {
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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string,
options: AddonModGlossaryGetEntriesOptions = {}): Promise<any[]> {
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<any[]> {
getEntriesByCategory(glossaryId: number, categoryId: number, options: AddonModGlossaryGetEntriesOptions = {}): Promise<any[]> {
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<any[]> {
getEntriesByDate(glossaryId: number, order: string, sort: string, options: AddonModGlossaryGetEntriesOptions = {})
: Promise<any[]> {
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<any> {
getEntriesByLetter(glossaryId: number, letter: string, options: AddonModGlossaryGetEntriesOptions = {}): Promise<any> {
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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string,
options: AddonModGlossaryGetEntriesOptions = {}): Promise<any[]> {
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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
return this.getCategories(glossaryId, 0, AddonModGlossaryProvider.LIMIT_CATEGORIES, [], site);
getAllCategories(glossaryId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any[]> {
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<any[]> {
protected getCategories(glossaryId: number, categories: any[], site: CoreSite,
options: AddonModGlossaryGetCategoriesOptions = {}): Promise<any[]> {
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<any> => {
// 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<any> {
limitNum = limitNum || AddonModGlossaryProvider.LIMIT_ENTRIES;
siteId = siteId || this.sitesProvider.getCurrentSiteId();
fetchEntries(fetchFunction: Function, fetchArguments: any[], options: AddonModGlossaryGetEntriesOptions = {}): Promise<any> {
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<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
fetchAllEntries(fetchFunction: Function, fetchArguments: any[], options: CoreCourseCommonModWSOptions = {}): Promise<any[]> {
options.siteId = options.siteId || this.sitesProvider.getCurrentSiteId();
const entries = [];
const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES;
const fetchMoreEntries = (): Promise<any[]> => {
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<any> {
return this.getCourseGlossaries(courseId, siteId).then((glossaries) => {
getGlossary(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getCourseGlossaries(courseId, siteId).then((glossaries) => {
getGlossaryById(courseId: number, glossaryId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<number | false> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
addEntry(glossaryId: number, concept: string, definition: string, courseId: number, entryOptions: any, attach: any,
otherOptions: AddonModGlossaryAddEntryOptions = {}): Promise<number | false> {
otherOptions.siteId = otherOptions.siteId || this.sitesProvider.getCurrentSiteId();
// Convenience function to store a new entry to be synchronized later.
const storeOffline = (): Promise<number | false> => {
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<boolean> {
isConceptUsed(glossaryId: number, concept: string, options: AddonModGlossaryIsConceptUsedOptions = {}): Promise<boolean> {
// 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.
};

View File

@ -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<any[]> {
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<any> {
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));

View File

@ -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', {

View File

@ -108,7 +108,9 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
*/
protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
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<void> {
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,
});
}
/**

View File

@ -77,32 +77,15 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit {
* @return Promise resolved when done.
*/
protected async fetchData(): Promise<void> {
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<void> {
this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId);
}
/**
* Get attempts.
*
* @return Promise resolved when done.
*/
protected async fetchAttempt(): Promise<void> {
this.attempt = await AddonModH5PActivity.instance.getAttemptResults(this.h5pActivityId, this.attemptId);
}
/**
* Get user profile.
*

View File

@ -79,29 +79,24 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit {
* @return Promise resolved when done.
*/
protected async fetchData(): Promise<void> {
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<void> {
this.h5pActivity = await AddonModH5PActivity.instance.getH5PActivityById(this.courseId, this.h5pActivityId);
}
/**
* Get attempts.
*
* @return Promise resolved when done.
*/
protected async fetchAttempts(): Promise<void> {
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,
});
}
/**

View File

@ -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<AddonModH5PActivityAccessInfo> {
async getAccessInformation(id: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModH5PActivityAccessInfo> {
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<AddonModH5PActivityData> {
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<AddonModH5PActivityData> {
return this.getH5PActivityByField(courseId, 'coursemodule', cmId, forceCache, siteId);
getH5PActivity(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModH5PActivityData> {
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<AddonModH5PActivityData> {
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<AddonModH5PActivityData> {
return this.getH5PActivityByField(courseId, 'id', id, forceCache, siteId);
getH5PActivityById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModH5PActivityData> {
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<AddonModH5PActivityUserAttempts> {
options = options || {};
async getUserAttempts(id: number, options: AddonModH5PActivityGetAttemptsOptions = {})
: Promise<AddonModH5PActivityUserAttempts> {
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.
};

View File

@ -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<void> {
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<void> {
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,
};

View File

@ -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 {

View File

@ -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<AddonModImscpImscp> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getImscpByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModImscpImscp> {
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<AddonModImscpImscp> {
return this.getImscpByKey(courseId, 'coursemodule', cmId, siteId);
getImscp(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModImscpImscp> {
return this.getImscpByKey(courseId, 'coursemodule', cmId, options);
}
/**

View File

@ -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);
});

View File

@ -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<AddonModLabelLabel> {
protected getLabelByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModLabelLabel> {
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<AddonModLabelLabel> {
return this.getLabelByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId);
getLabel(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModLabelLabel> {
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<AddonModLabelLabel> {
return this.getLabelByField(courseId, 'id', labelId, forceCache, ignoreCache, siteId);
getLabelById(courseId: number, labelId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModLabelLabel> {
return this.getLabelByField(courseId, 'id', labelId, options);
}
/**

View File

@ -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();
}

View File

@ -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<any> {
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) => {

View File

@ -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<any> {
protected callFunction(func: Function, args: any[], options: any): Promise<any> {
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<any> {
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);
}
}

View File

@ -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<any> {
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.

View File

@ -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.

View File

@ -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<any> {
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,

File diff suppressed because it is too large Load Diff

View File

@ -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<boolean> {
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<any> {
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.
};

View File

@ -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<AddonModLtiLti> {
async getLti(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModLtiLti> {
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.');
}
/**

View File

@ -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<AddonModPagePage> {
return this.getPageByKey(courseId, 'coursemodule', cmId, siteId);
getPageData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModPagePage> {
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<AddonModPagePage> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getPageByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModPagePage> {
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 => {

View File

@ -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<void> {
// 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.

View File

@ -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 {

View File

@ -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<any> {
// 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<void> {
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<void> {
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<void> {
// 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;

View File

@ -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<void> {
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<void> {
// 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) => {

View File

@ -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<number> {
getQuizIdByAttemptId(attemptId: number, options: {cmId?: number, siteId?: string} = {}): Promise<number> {
// 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<any> {
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.

View File

@ -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<any[]> {
protected getAttemptsFeedbackFiles(quiz: any, attempts: any[], siteId?: string): Promise<any[]> {
// 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<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
setStatusAfterPrefetch(quiz: any, options: AddonModQuizSetStatusAfterPrefetchOptions = {}): Promise<any> {
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.
};

View File

@ -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<AddonModQuizSyncResult> {
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.

View File

@ -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<any> {
getAllQuestionsData(quiz: any, attempt: any, preflightData: any, options: AddonModQuizAllQuestionsDataOptions = {})
: Promise<any> {
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<any> {
getAttemptAccessInformation(quizId: number, attemptId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
getAttemptData(attemptId: number, page: number, preflightData: any, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
if (typeof page == 'undefined') {
page = -1;
}
getAttemptReview(attemptId: number, options: AddonModQuizGetAttemptReviewOptions = {}): Promise<any> {
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<any[]> {
getAttemptSummary(attemptId: number, preflightData: any, options: AddonModQuizGetAttemptSummaryOptions = {}): Promise<any[]> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId();
getCombinedReviewOptions(quizId: number, options: AddonModQuizUserOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getFeedbackForGrade(quizId: number, grade: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
protected getQuizByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getQuizByField(courseId, 'coursemodule', cmId, forceCache, ignoreCache, siteId);
getQuiz(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getQuizByField(courseId, 'id', id, forceCache, ignoreCache, siteId);
getQuizById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getQuizAccessInformation(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getQuizRequiredQtypes(quizId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any[]> {
getUserAttempts(quizId: number, options: AddonModQuizGetUserAttemptsOptions = {}): Promise<any[]> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId();
getUserBestGrade(quizId: number, options: AddonModQuizUserOptions = {}): Promise<any> {
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<any> {
// 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.
};

View File

@ -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<AddonModResourceResource> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getResourceDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModResourceResource> {
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<AddonModResourceResource> {
return this.getResourceDataByKey(courseId, 'coursemodule', cmId, siteId);
getResourceData(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModResourceResource> {
return this.getResourceDataByKey(courseId, 'coursemodule', cmId, options);
}
/**

View File

@ -146,7 +146,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
// 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<any> {
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);

View File

@ -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.
});
}

View File

@ -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<any> {
getFirstSco(scormId: number, attempt: number, options: AddonModScormGetFirstScoOptions = {}): Promise<any> {
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<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
searchOnlineAttemptUserData(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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.
};

View File

@ -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<number> {
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<any[]> {
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<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 (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);
});
}

View File

@ -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<any> {
protected canRetrySync(scormId: number, attempt: number, lastOnline: number, cmId: number, siteId: string): Promise<any> {
// 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<any> {
protected saveSyncSnapshot(scormId: number, attempt: number, cmId: number, siteId: string): Promise<any> {
// 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<any> {
protected syncAttempt(scormId: number, attempt: number, cmId: number, siteId?: string): Promise<any> {
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<string[]> {
protected treatCollisions(scormId: number, collisions: number[], lastOnline: number, offlineAttempts: number[], cmId: number,
siteId: string): Promise<string[]> {
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.

View File

@ -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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getAccessInformation(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<AddonModScormAttemptCountResult> {
getAttemptCount(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise<AddonModScormAttemptCountResult> {
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<number> {
getAttemptCountOnline(scormId: number, options: AddonModScormGetAttemptCountOptions = {}): Promise<number> {
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<any[]> {
return this.getScos(scormId, undefined, false, siteId).then((scos) => {
getOrganizations(scormId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any[]> {
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<any[]> {
getOrganizationToc(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise<any[]> {
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<any> {
getScormUserData(scormId: number, attempt: number, options: AddonModScormGetUserDataOptions = {}): Promise<any> {
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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getScormUserDataOnline(scormId: number, attempt: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
getScos(scormId: number, options: AddonModScormOrganizationOptions = {}): Promise<any[]> {
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<any[]> {
getScosWithData(scormId: number, attempt: number, options: AddonModScormGetScosWithDataOptions = {}): Promise<any[]> {
// 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<any> {
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<any> {
return this.getScormByField(courseId, 'coursemodule', cmId, moduleUrl, forceCache, siteId);
getScorm(courseId: number, cmId: number, options: AddonModScormGetScormOptions = {}): Promise<any> {
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<any> {
return this.getScormByField(courseId, 'id', id, moduleUrl, forceCache, siteId);
getScormById(courseId: number, id: number, options: AddonModScormGetScormOptions = {}): Promise<any> {
return this.getScormByField(courseId, 'id', id, options);
}
/**
@ -1314,7 +1285,7 @@ export class AddonModScormProvider {
invalidateContent(moduleId: number, courseId: number, siteId?: string, userId?: number): Promise<any> {
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<boolean> {
isAttemptIncomplete(scormId: number, attempt: number, options: AddonModScormOfflineOptions = {}): Promise<boolean> {
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<any> {
protected updateUserDataAfterSave(scormId: number, attempt: number, tracks: any[], options: {cmId?: number, siteId?: string})
: Promise<any> {
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;

View File

@ -143,7 +143,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
* @return Promise resolved when done.
*/
protected fetchQuestions(): Promise<any> {
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.

View File

@ -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<any> {
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);

View File

@ -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<AddonModSurveyQuestion[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getQuestions(surveyId: number, options: CoreCourseCommonModWSOptions = {}): Promise<AddonModSurveyQuestion[]> {
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<AddonModSurveySurvey> {
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<AddonModSurveySurvey> {
return this.getSurveyDataByKey(courseId, 'coursemodule', cmId, ignoreCache, siteId);
getSurvey(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModSurveySurvey> {
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<AddonModSurveySurvey> {
return this.getSurveyDataByKey(courseId, 'id', id, ignoreCache, siteId);
getSurveyById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModSurveySurvey> {
return this.getSurveyDataByKey(courseId, 'id', id, options);
}
/**

View File

@ -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<AddonModUrlUrl> {
return this.sitesProvider.getSite(siteId).then((site) => {
protected getUrlDataByKey(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {})
: Promise<AddonModUrlUrl> {
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<AddonModUrlUrl> {
return this.getUrlDataByKey(courseId, 'coursemodule', cmId, siteId);
getUrl(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModUrlUrl> {
return this.getUrlDataByKey(courseId, 'coursemodule', cmId, options);
}
/**

View File

@ -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<any> {
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<any> {
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) => {

View File

@ -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);

View File

@ -48,13 +48,15 @@ export class AddonModWikiCreateLinkHandler extends CoreContentLinksHandlerBase {
protected currentStateIsSameWiki(activeView: ViewController, subwikiId: number, siteId: string): Promise<boolean> {
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;

View File

@ -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);

View File

@ -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<any[]> {
protected getAllPages(module: any, courseId: number, options: CoreSitesCommonWSOptions = {}): Promise<any[]> {
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<any> {
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);
}));

View File

@ -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 = {

View File

@ -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<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
getPageContents(pageId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any> {
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<any[]> {
getSubwikiFiles(wikiId: number, options: AddonModWikiGetSubwikiFilesOptions = {}): Promise<any[]> {
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<any[]> {
getSubwikiPages(wikiId: number, options: AddonModWikiGetSubwikiPagesOptions = {}): Promise<any[]> {
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<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => {
getSubwikis(wikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise<any[]> {
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<any> {
return this.getWikiByField(courseId, 'coursemodule', cmId, forceCache, siteId);
getWiki(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
protected getWikiByField(courseId: number, key: string, value: any, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any> {
return this.getWikiByField(courseId, 'id', id, forceCache, siteId);
getWikiById(courseId: number, id: number, options: CoreSitesCommonWSOptions = {}): Promise<any> {
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<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
getWikiFileList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise<any[]> {
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<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
getWikiPageList(wiki: any, options: CoreSitesCommonWSOptions = {}): Promise<any[]> {
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<any> {
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<boolean> {
isTitleUsed(wikiId: number, subwikiId: number, title: string, options: CoreCourseCommonModWSOptions = {}): Promise<boolean> {
// 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<number> {
newPage(title: string, content: string, options: AddonModWikiNewPageOptions = {}): Promise<number> {
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<number> => {
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<number> {
newPageOnline(title: string, content: string, options: AddonModWikiNewPageOnlineOptions = {}): Promise<number> {
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<boolean> {
wikiHasSubwiki(wikiId: number, subwikiId: number, options: CoreCourseCommonModWSOptions = {}): Promise<boolean> {
// 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.
};

View File

@ -147,8 +147,10 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit {
* @return Promised resvoled when data is loaded.
*/
protected load(): Promise<any> {
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;

View File

@ -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);

View File

@ -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<any> {
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) => {

Some files were not shown because too many files have changed in this diff Show More