MOBILE-2061 course: Don't sync completion if modified in the site
parent
1c6618b42a
commit
83ef6467c8
|
@ -1113,6 +1113,7 @@
|
|||
"core.course.overriddennotice": "grades",
|
||||
"core.course.sections": "moodle",
|
||||
"core.course.useactivityonbrowser": "local_moodlemobileapp",
|
||||
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
|
||||
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
|
||||
"core.coursedetails": "moodle",
|
||||
"core.courses.allowguests": "enrol_guest",
|
||||
|
|
|
@ -24,5 +24,6 @@
|
|||
"refreshcourse": "Refresh course",
|
||||
"sections": "Sections",
|
||||
"useactivityonbrowser": "You can still use it using your device's web browser.",
|
||||
"warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
|
||||
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"
|
||||
}
|
|
@ -45,7 +45,7 @@ export class CoreCourseOfflineProvider {
|
|||
type: 'TEXT'
|
||||
},
|
||||
{
|
||||
name: 'timecreated',
|
||||
name: 'timecompleted',
|
||||
type: 'INTEGER'
|
||||
}
|
||||
]
|
||||
|
@ -131,7 +131,7 @@ export class CoreCourseOfflineProvider {
|
|||
completed: completed,
|
||||
courseid: courseId,
|
||||
coursename: courseName || '',
|
||||
timecreated: Date.now()
|
||||
timecompleted: Date.now()
|
||||
};
|
||||
|
||||
return site.getDb().insertRecord(CoreCourseOfflineProvider.MANUAL_COMPLETION_TABLE, entry);
|
||||
|
|
|
@ -121,9 +121,14 @@ export class CoreCourseProvider {
|
|||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {number} [userId] User ID. If not defined, current user.
|
||||
* @param {boolean} [forceCache] True if it should return cached data. Has priority over ignoreCache.
|
||||
* @param {boolean} [ignoreCache] True if it should ignore cached data (it will always fail in offline or server down).
|
||||
* @param {boolean} [includeOffline=true] True if it should load offline data in the completion status.
|
||||
* @return {Promise<any>} Promise resolved with the completion statuses: object where the key is module ID.
|
||||
*/
|
||||
getActivitiesCompletionStatus(courseId: number, siteId?: string, userId?: number): Promise<any> {
|
||||
getActivitiesCompletionStatus(courseId: number, siteId?: string, userId?: number, forceCache: boolean = false,
|
||||
ignoreCache: boolean = false, includeOffline: boolean = true): Promise<any> {
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
@ -133,10 +138,17 @@ export class CoreCourseProvider {
|
|||
courseid: courseId,
|
||||
userid: userId
|
||||
},
|
||||
preSets = {
|
||||
preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getActivitiesCompletionCacheKey(courseId, userId)
|
||||
};
|
||||
|
||||
if (forceCache) {
|
||||
preSets.omitExpires = true;
|
||||
} else if (ignoreCache) {
|
||||
preSets.getFromCache = false;
|
||||
preSets.emergencyCache = false;
|
||||
}
|
||||
|
||||
return site.read('core_completion_get_activities_completion_status', params, preSets).then((data) => {
|
||||
if (data && data.statuses) {
|
||||
return this.utils.arrayToObject(data.statuses, 'cmid');
|
||||
|
@ -144,6 +156,10 @@ export class CoreCourseProvider {
|
|||
|
||||
return Promise.reject(null);
|
||||
}).then((completionStatus) => {
|
||||
if (!includeOffline) {
|
||||
return completionStatus;
|
||||
}
|
||||
|
||||
// Now get the offline completion (if any).
|
||||
return this.courseOffline.getCourseManualCompletions(courseId, site.id).then((offlineCompletions) => {
|
||||
offlineCompletions.forEach((offlineCompletion) => {
|
||||
|
@ -686,51 +702,33 @@ export class CoreCourseProvider {
|
|||
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a message to be synchronized later.
|
||||
// Convenience function to store a completion to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.courseOffline.markCompletedManually(cmId, completed, courseId, courseName, siteId);
|
||||
};
|
||||
|
||||
// Check if we already have a completion stored.
|
||||
return this.courseOffline.getManualCompletion(cmId, siteId).catch(() => {
|
||||
// No completion stored.
|
||||
}).then((entry) => {
|
||||
// The offline function requires a courseId and it could be missing because it's a calculated field.
|
||||
if (!this.appProvider.isOnline() && courseId) {
|
||||
// App is offline, store the action.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
if (entry && completed != entry.completed) {
|
||||
// It has changed, this means that the offline data can be deleted because the action was undone.
|
||||
return this.courseOffline.deleteManualCompletion(cmId, siteId).then(() => {
|
||||
return {
|
||||
status: true,
|
||||
offline: true
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.appProvider.isOnline() && courseId) {
|
||||
// App is offline, store the action.
|
||||
// Try to send it to server.
|
||||
return this.markCompletedManuallyOnline(cmId, completed, siteId).then((result) => {
|
||||
// Data sent to server, if there is some offline data delete it now.
|
||||
return this.courseOffline.deleteManualCompletion(cmId, siteId).catch(() => {
|
||||
// Ignore errors, shouldn't happen.
|
||||
}).then(() => {
|
||||
return result;
|
||||
});
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error) || !courseId) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
} else {
|
||||
// Couldn't connect to server, store it offline.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
return this.markCompletedManuallyOnline(cmId, completed, siteId).then((result) => {
|
||||
// Data sent to server, if there is some offline data delete it now.
|
||||
if (entry) {
|
||||
return this.courseOffline.deleteManualCompletion(cmId, siteId).catch(() => {
|
||||
// Ignore errors, shouldn't happen.
|
||||
}).then(() => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error) || !courseId) {
|
||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||
return Promise.reject(error);
|
||||
} else {
|
||||
// Couldn't connect to server, store it offline.
|
||||
return storeOffline();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -128,34 +128,57 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider {
|
|||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
// Get the current completion status to check if any completion was modified in web.
|
||||
return this.courseProvider.getActivitiesCompletionStatus(courseId, siteId, undefined, false, true, false)
|
||||
.then((onlineCompletions) => {
|
||||
|
||||
// Send all the completions.
|
||||
completions.forEach((entry) => {
|
||||
promises.push(this.courseProvider.markCompletedManuallyOnline(entry.cmid, entry.completed, siteId).then(() => {
|
||||
result.updated = true;
|
||||
const promises = [];
|
||||
|
||||
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId);
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that the completion cannot be submitted. Delete it.
|
||||
result.updated = true;
|
||||
// Send all the completions.
|
||||
completions.forEach((entry) => {
|
||||
const onlineComp = onlineCompletions[entry.cmid];
|
||||
|
||||
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId).then(() => {
|
||||
// Responses deleted, add a warning.
|
||||
result.warnings.push(this.translate.instant('core.course.warningofflinemanualcompletiondeleted', {
|
||||
name: entry.coursename || courseId,
|
||||
error: this.textUtils.getErrorMessageFromError(error)
|
||||
}));
|
||||
});
|
||||
// Check if the completion was modified in online. If so, discard it.
|
||||
if (onlineComp && onlineComp.timecompleted * 1000 > entry.timecompleted) {
|
||||
promises.push(this.courseOffline.deleteManualCompletion(entry.cmid, siteId).then(() => {
|
||||
|
||||
// Completion deleted, add a warning if the completion status doesn't match.
|
||||
if (onlineComp.state != entry.completed) {
|
||||
result.warnings.push(this.translate.instant('core.course.warningofflinemanualcompletiondeleted', {
|
||||
name: entry.coursename || courseId,
|
||||
error: this.translate.instant('core.course.warningmanualcompletionmodified')
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't connect to server, reject.
|
||||
return Promise.reject(error);
|
||||
}));
|
||||
});
|
||||
promises.push(this.courseProvider.markCompletedManuallyOnline(entry.cmid, entry.completed, siteId).then(() => {
|
||||
result.updated = true;
|
||||
|
||||
return Promise.all(promises);
|
||||
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId);
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// The WebService has thrown an error, this means that the completion cannot be submitted. Delete it.
|
||||
result.updated = true;
|
||||
|
||||
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId).then(() => {
|
||||
// Completion deleted, add a warning.
|
||||
result.warnings.push(this.translate.instant('core.course.warningofflinemanualcompletiondeleted', {
|
||||
name: entry.coursename || courseId,
|
||||
error: this.textUtils.getErrorMessageFromError(error)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Couldn't connect to server, reject.
|
||||
return Promise.reject(error);
|
||||
}));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}).then(() => {
|
||||
if (result.updated) {
|
||||
// Update data.
|
||||
|
|
Loading…
Reference in New Issue