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.overriddennotice": "grades",
|
||||||
"core.course.sections": "moodle",
|
"core.course.sections": "moodle",
|
||||||
"core.course.useactivityonbrowser": "local_moodlemobileapp",
|
"core.course.useactivityonbrowser": "local_moodlemobileapp",
|
||||||
|
"core.course.warningmanualcompletionmodified": "local_moodlemobileapp",
|
||||||
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
|
"core.course.warningofflinemanualcompletiondeleted": "local_moodlemobileapp",
|
||||||
"core.coursedetails": "moodle",
|
"core.coursedetails": "moodle",
|
||||||
"core.courses.allowguests": "enrol_guest",
|
"core.courses.allowguests": "enrol_guest",
|
||||||
|
|
|
@ -24,5 +24,6 @@
|
||||||
"refreshcourse": "Refresh course",
|
"refreshcourse": "Refresh course",
|
||||||
"sections": "Sections",
|
"sections": "Sections",
|
||||||
"useactivityonbrowser": "You can still use it using your device's web browser.",
|
"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}}"
|
"warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}"
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ export class CoreCourseOfflineProvider {
|
||||||
type: 'TEXT'
|
type: 'TEXT'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'timecreated',
|
name: 'timecompleted',
|
||||||
type: 'INTEGER'
|
type: 'INTEGER'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -131,7 +131,7 @@ export class CoreCourseOfflineProvider {
|
||||||
completed: completed,
|
completed: completed,
|
||||||
courseid: courseId,
|
courseid: courseId,
|
||||||
coursename: courseName || '',
|
coursename: courseName || '',
|
||||||
timecreated: Date.now()
|
timecompleted: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
return site.getDb().insertRecord(CoreCourseOfflineProvider.MANUAL_COMPLETION_TABLE, entry);
|
return site.getDb().insertRecord(CoreCourseOfflineProvider.MANUAL_COMPLETION_TABLE, entry);
|
||||||
|
|
|
@ -121,9 +121,14 @@ export class CoreCourseProvider {
|
||||||
* @param {number} courseId Course ID.
|
* @param {number} courseId Course ID.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @param {number} [userId] User ID. If not defined, current user.
|
* @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.
|
* @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) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
@ -133,10 +138,17 @@ export class CoreCourseProvider {
|
||||||
courseid: courseId,
|
courseid: courseId,
|
||||||
userid: userId
|
userid: userId
|
||||||
},
|
},
|
||||||
preSets = {
|
preSets: CoreSiteWSPreSets = {
|
||||||
cacheKey: this.getActivitiesCompletionCacheKey(courseId, userId)
|
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) => {
|
return site.read('core_completion_get_activities_completion_status', params, preSets).then((data) => {
|
||||||
if (data && data.statuses) {
|
if (data && data.statuses) {
|
||||||
return this.utils.arrayToObject(data.statuses, 'cmid');
|
return this.utils.arrayToObject(data.statuses, 'cmid');
|
||||||
|
@ -144,6 +156,10 @@ export class CoreCourseProvider {
|
||||||
|
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}).then((completionStatus) => {
|
}).then((completionStatus) => {
|
||||||
|
if (!includeOffline) {
|
||||||
|
return completionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
// Now get the offline completion (if any).
|
// Now get the offline completion (if any).
|
||||||
return this.courseOffline.getCourseManualCompletions(courseId, site.id).then((offlineCompletions) => {
|
return this.courseOffline.getCourseManualCompletions(courseId, site.id).then((offlineCompletions) => {
|
||||||
offlineCompletions.forEach((offlineCompletion) => {
|
offlineCompletions.forEach((offlineCompletion) => {
|
||||||
|
@ -686,42 +702,25 @@ export class CoreCourseProvider {
|
||||||
|
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
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> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
return this.courseOffline.markCompletedManually(cmId, completed, courseId, courseName, siteId);
|
return this.courseOffline.markCompletedManually(cmId, completed, courseId, courseName, siteId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if we already have a completion stored.
|
// The offline function requires a courseId and it could be missing because it's a calculated field.
|
||||||
return this.courseOffline.getManualCompletion(cmId, siteId).catch(() => {
|
|
||||||
// No completion stored.
|
|
||||||
}).then((entry) => {
|
|
||||||
|
|
||||||
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) {
|
if (!this.appProvider.isOnline() && courseId) {
|
||||||
// App is offline, store the action.
|
// App is offline, store the action.
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to send it to server.
|
||||||
return this.markCompletedManuallyOnline(cmId, completed, siteId).then((result) => {
|
return this.markCompletedManuallyOnline(cmId, completed, siteId).then((result) => {
|
||||||
// Data sent to server, if there is some offline data delete it now.
|
// Data sent to server, if there is some offline data delete it now.
|
||||||
if (entry) {
|
|
||||||
return this.courseOffline.deleteManualCompletion(cmId, siteId).catch(() => {
|
return this.courseOffline.deleteManualCompletion(cmId, siteId).catch(() => {
|
||||||
// Ignore errors, shouldn't happen.
|
// Ignore errors, shouldn't happen.
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error) || !courseId) {
|
if (this.utils.isWebServiceError(error) || !courseId) {
|
||||||
// The WebService has thrown an error, this means that responses cannot be submitted.
|
// The WebService has thrown an error, this means that responses cannot be submitted.
|
||||||
|
@ -731,7 +730,6 @@ export class CoreCourseProvider {
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -128,10 +128,32 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider {
|
||||||
return Promise.reject(null);
|
return Promise.reject(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Send all the completions.
|
// Send all the completions.
|
||||||
completions.forEach((entry) => {
|
completions.forEach((entry) => {
|
||||||
|
const onlineComp = onlineCompletions[entry.cmid];
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
promises.push(this.courseProvider.markCompletedManuallyOnline(entry.cmid, entry.completed, siteId).then(() => {
|
promises.push(this.courseProvider.markCompletedManuallyOnline(entry.cmid, entry.completed, siteId).then(() => {
|
||||||
result.updated = true;
|
result.updated = true;
|
||||||
|
|
||||||
|
@ -142,7 +164,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider {
|
||||||
result.updated = true;
|
result.updated = true;
|
||||||
|
|
||||||
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId).then(() => {
|
return this.courseOffline.deleteManualCompletion(entry.cmid, siteId).then(() => {
|
||||||
// Responses deleted, add a warning.
|
// Completion deleted, add a warning.
|
||||||
result.warnings.push(this.translate.instant('core.course.warningofflinemanualcompletiondeleted', {
|
result.warnings.push(this.translate.instant('core.course.warningofflinemanualcompletiondeleted', {
|
||||||
name: entry.coursename || courseId,
|
name: entry.coursename || courseId,
|
||||||
error: this.textUtils.getErrorMessageFromError(error)
|
error: this.textUtils.getErrorMessageFromError(error)
|
||||||
|
@ -156,6 +178,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (result.updated) {
|
if (result.updated) {
|
||||||
// Update data.
|
// Update data.
|
||||||
|
|
Loading…
Reference in New Issue