diff --git a/scripts/aot.sh b/scripts/aot.sh index f10a70790..12aed8d2e 100755 --- a/scripts/aot.sh +++ b/scripts/aot.sh @@ -17,7 +17,7 @@ if [ $TRAVIS_BRANCH == 'integration' ] || [ $TRAVIS_BRANCH == 'master' ] || [ $T version=`grep versionname src/config.json| cut -d: -f2|cut -d'"' -f2` date=`date +%Y%m%d`'00' - pushd ../../moodle-local_moodlemobileapp + pushd ../moodle-local_moodlemobileapp sed -ie "s/release[ ]*=[ ]*'[^']*';/release = '$version';/1" version.php sed -ie "s/version[ ]*=[ ]*[0-9]*;/version = $date;/1" version.php rm version.phpe diff --git a/scripts/langindex.json b/scripts/langindex.json index d86efa26d..c87666a88 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1342,6 +1342,7 @@ "core.favourites": "moodle", "core.filename": "repository", "core.filenameexist": "local_moodlemobileapp", + "core.filenotfound": "resource", "core.fileuploader.addfiletext": "repository", "core.fileuploader.audio": "local_moodlemobileapp", "core.fileuploader.camera": "local_moodlemobileapp", diff --git a/src/addon/messages/providers/mainmenu-handler.ts b/src/addon/messages/providers/mainmenu-handler.ts index 155e0c2e8..77d453ce1 100644 --- a/src/addon/messages/providers/mainmenu-handler.ts +++ b/src/addon/messages/providers/mainmenu-handler.ts @@ -166,9 +166,10 @@ export class AddonMessagesMainMenuHandler implements CoreMainMenuHandler, CoreCr * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { + execute(siteId?: string, force?: boolean): Promise { if (this.sitesProvider.isCurrentSite(siteId)) { this.refreshBadge(); } diff --git a/src/addon/messages/providers/sync-cron-handler.ts b/src/addon/messages/providers/sync-cron-handler.ts index 79dc464a3..466a593c3 100644 --- a/src/addon/messages/providers/sync-cron-handler.ts +++ b/src/addon/messages/providers/sync-cron-handler.ts @@ -30,9 +30,10 @@ export class AddonMessagesSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { + execute(siteId?: string, force?: boolean): Promise { return this.messagesSync.syncAllDiscussions(siteId); } diff --git a/src/addon/mod/assign/providers/assign-sync.ts b/src/addon/mod/assign/providers/assign-sync.ts index cff8c421a..049b44ba3 100644 --- a/src/addon/mod/assign/providers/assign-sync.ts +++ b/src/addon/mod/assign/providers/assign-sync.ts @@ -110,26 +110,28 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the assignments in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllAssignments(siteId?: string): Promise { - return this.syncOnSites('all assignments', this.syncAllAssignmentsFunc.bind(this), [], siteId); + syncAllAssignments(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all assignments', this.syncAllAssignmentsFunc.bind(this), [force], siteId); } /** * Sync all assignments on a site. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllAssignmentsFunc(siteId?: string): Promise { + protected syncAllAssignmentsFunc(siteId?: string, force?: boolean): Promise { // Get all assignments that have offline data. return this.assignOfflineProvider.getAllAssigns(siteId).then((assignIds) => { - const promises = []; - // Sync all assignments that haven't been synced for a while. - assignIds.forEach((assignId) => { - promises.push(this.syncAssignIfNeeded(assignId, siteId).then((data) => { + const promises = assignIds.map((assignId) => { + const promise = force ? this.syncAssign(assignId, siteId) : this.syncAssignIfNeeded(assignId, siteId); + + return promise.then((data) => { if (data && data.updated) { // Sync done. Send event. this.eventsProvider.trigger(AddonModAssignSyncProvider.AUTO_SYNCED, { @@ -137,7 +139,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider { warnings: data.warnings }, siteId); } - })); + }); }); return Promise.all(promises); diff --git a/src/addon/mod/assign/providers/prefetch-handler.ts b/src/addon/mod/assign/providers/prefetch-handler.ts index cae9b6fec..2bac491cc 100644 --- a/src/addon/mod/assign/providers/prefetch-handler.ts +++ b/src/addon/mod/assign/providers/prefetch-handler.ts @@ -28,6 +28,7 @@ import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; import { CoreUserProvider } from '@core/user/providers/user'; import { AddonModAssignProvider } from './assign'; import { AddonModAssignHelperProvider } from './helper'; +import { AddonModAssignSyncProvider } from './assign-sync'; import { AddonModAssignFeedbackDelegate } from './feedback-delegate'; import { AddonModAssignSubmissionDelegate } from './submission-delegate'; @@ -47,7 +48,8 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan protected textUtils: CoreTextUtilsProvider, protected feedbackDelegate: AddonModAssignFeedbackDelegate, protected submissionDelegate: AddonModAssignSubmissionDelegate, protected courseHelper: CoreCourseHelperProvider, protected groupsProvider: CoreGroupsProvider, protected gradesHelper: CoreGradesHelperProvider, - protected userProvider: CoreUserProvider, protected assignHelper: AddonModAssignHelperProvider) { + protected userProvider: CoreUserProvider, protected assignHelper: AddonModAssignHelperProvider, + protected syncProvider: AddonModAssignSyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -454,4 +456,16 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan return Promise.all(promises); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + return this.syncProvider.syncAssign(module.instance, siteId); + } } diff --git a/src/addon/mod/assign/providers/sync-cron-handler.ts b/src/addon/mod/assign/providers/sync-cron-handler.ts index fe68ccddd..fbfb6600c 100644 --- a/src/addon/mod/assign/providers/sync-cron-handler.ts +++ b/src/addon/mod/assign/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModAssignSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.assignSync.syncAllAssignments(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.assignSync.syncAllAssignments(siteId, force); } /** diff --git a/src/addon/mod/choice/providers/prefetch-handler.ts b/src/addon/mod/choice/providers/prefetch-handler.ts index 0b2f74eba..eb8a72d4b 100644 --- a/src/addon/mod/choice/providers/prefetch-handler.ts +++ b/src/addon/mod/choice/providers/prefetch-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; @@ -22,6 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { CoreUserProvider } from '@core/user/providers/user'; +import { AddonModChoiceSyncProvider } from './sync'; import { AddonModChoiceProvider } from './choice'; /** @@ -34,10 +35,12 @@ export class AddonModChoicePrefetchHandler extends CoreCourseActivityPrefetchHan component = AddonModChoiceProvider.COMPONENT; updatesNames = /^configuration$|^.*files$|^answers$/; + protected syncProvider: AddonModChoiceSyncProvider; // It will be injected later to prevent circular dependencies. + constructor(translate: TranslateService, appProvider: CoreAppProvider, utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected choiceProvider: AddonModChoiceProvider, - protected userProvider: CoreUserProvider) { + protected userProvider: CoreUserProvider, protected injector: Injector) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -133,4 +136,20 @@ export class AddonModChoicePrefetchHandler extends CoreCourseActivityPrefetchHan invalidateModule(module: any, courseId: number): Promise { return this.choiceProvider.invalidateChoiceData(courseId); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModChoiceSyncProvider); + } + + return this.syncProvider.syncChoice(module.instance, undefined, siteId); + } } diff --git a/src/addon/mod/choice/providers/sync-cron-handler.ts b/src/addon/mod/choice/providers/sync-cron-handler.ts index af6cb8e81..92d88b52b 100644 --- a/src/addon/mod/choice/providers/sync-cron-handler.ts +++ b/src/addon/mod/choice/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModChoiceSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. - * @return {Promise} Promise resolved when done, rejected if failure. + * @param {boolean} [force] Wether the execution is forced (manual sync). + * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.choiceSync.syncAllChoices(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.choiceSync.syncAllChoices(siteId, force); } /** diff --git a/src/addon/mod/choice/providers/sync.ts b/src/addon/mod/choice/providers/sync.ts index 9a14b71d4..965a1b06d 100644 --- a/src/addon/mod/choice/providers/sync.ts +++ b/src/addon/mod/choice/providers/sync.ts @@ -68,25 +68,28 @@ export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvid * Try to synchronize all the choices in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllChoices(siteId?: string): Promise { - return this.syncOnSites('choices', this.syncAllChoicesFunc.bind(this), undefined, siteId); + syncAllChoices(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('choices', this.syncAllChoicesFunc.bind(this), [force], siteId); } /** * Sync all pending choices on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllChoicesFunc(siteId?: string): Promise { + protected syncAllChoicesFunc(siteId?: string, force?: boolean): Promise { return this.choiceOffline.getResponses(siteId).then((responses) => { - const promises = []; - // Sync all responses. - responses.forEach((response) => { - promises.push(this.syncChoiceIfNeeded(response.choiceid, response.userid, siteId).then((result) => { + const promises = responses.map((response) => { + const promise = force ? this.syncChoice(response.choiceid, response.userid, siteId) : + this.syncChoiceIfNeeded(response.choiceid, response.userid, siteId); + + return promise.then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModChoiceSyncProvider.AUTO_SYNCED, { @@ -95,8 +98,10 @@ export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvid warnings: result.warnings }, siteId); } - })); + }); }); + + return Promise.all(promises); }); } @@ -122,98 +127,101 @@ export class AddonModChoiceSyncProvider extends CoreCourseActivitySyncBaseProvid * Synchronize a choice. * * @param {number} choiceId Choice ID to be synced. - * @param {number} userId User the answers belong to. + * @param {number} [userId] User the answers belong to. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected otherwise. */ - syncChoice(choiceId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + syncChoice(choiceId: number, userId?: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + userId = userId || site.getUserId(); + siteId = site.getId(); - const syncId = this.getSyncId(choiceId, userId); - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this discussion, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - this.logger.debug(`Try to sync choice '${choiceId}' for user '${userId}'`); - - let courseId; - const result = { - warnings: [], - updated: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModChoiceProvider.COMPONENT, choiceId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - return this.choiceOffline.getResponse(choiceId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return {}; - }); - }).then((data) => { - if (!data.choiceid) { - // Nothing to sync. - return; + const syncId = this.getSyncId(choiceId, userId); + if (this.isSyncing(syncId, siteId)) { + // There's already a sync ongoing for this discussion, return the promise. + return this.getOngoingSync(syncId, siteId); } - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } + this.logger.debug(`Try to sync choice '${choiceId}' for user '${userId}'`); - courseId = data.courseid; + let courseId; + const result = { + warnings: [], + updated: false + }; - // Send the responses. - let promise; - - if (data.deleting) { - // A user has deleted some responses. - promise = this.choiceProvider.deleteResponsesOnline(choiceId, data.responses, siteId); - } else { - // A user has added some responses. - promise = this.choiceProvider.submitResponseOnline(choiceId, data.responses, siteId); - } - - return promise.then(() => { - result.updated = true; - - return this.choiceOffline.deleteResponse(choiceId, siteId, userId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. - result.updated = true; - - return this.choiceOffline.deleteResponse(choiceId, siteId, userId).then(() => { - // Responses deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); + // Sync offline logs. + const syncPromise = this.logHelper.syncIfNeeded(AddonModChoiceProvider.COMPONENT, choiceId, siteId).catch(() => { + // Ignore errors. + }).then(() => { + return this.choiceOffline.getResponse(choiceId, siteId, userId).catch(() => { + // No offline data found, return empty object. + return {}; + }); + }).then((data) => { + if (!data.choiceid) { + // Nothing to sync. + return; } - // Couldn't connect to server, reject. - return Promise.reject(error); - }); - }).then(() => { - if (courseId) { - // Data has been sent to server, prefetch choice if needed. - return this.courseProvider.getModuleBasicInfoByInstance(choiceId, 'choice', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId); - }).then(() => { - // All done, return the warnings. - return result; - }); + if (!this.appProvider.isOnline()) { + // Cannot sync in offline. + return Promise.reject(null); + } - return this.addOngoingSync(syncId, syncPromise, siteId); + courseId = data.courseid; + + // Send the responses. + let promise; + + if (data.deleting) { + // A user has deleted some responses. + promise = this.choiceProvider.deleteResponsesOnline(choiceId, data.responses, siteId); + } else { + // A user has added some responses. + promise = this.choiceProvider.submitResponseOnline(choiceId, data.responses, siteId); + } + + return promise.then(() => { + result.updated = true; + + return this.choiceOffline.deleteResponse(choiceId, siteId, userId); + }).catch((error) => { + if (this.utils.isWebServiceError(error)) { + // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. + result.updated = true; + + return this.choiceOffline.deleteResponse(choiceId, siteId, userId).then(() => { + // Responses deleted, add a warning. + result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { + component: this.componentTranslate, + name: data.name, + error: this.textUtils.getErrorMessageFromError(error) + })); + }); + } + + // Couldn't connect to server, reject. + return Promise.reject(error); + }); + }).then(() => { + if (courseId) { + // Data has been sent to server, prefetch choice if needed. + return this.courseProvider.getModuleBasicInfoByInstance(choiceId, 'choice', siteId).then((module) => { + return this.prefetchAfterUpdate(module, courseId, undefined, siteId); + }).catch(() => { + // Ignore errors. + }); + } + }).then(() => { + // Sync finished, set sync time. + return this.setSyncTime(syncId, siteId); + }).then(() => { + // All done, return the warnings. + return result; + }); + + return this.addOngoingSync(syncId, syncPromise, siteId); + }); } } diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts index ddd5975a9..54c6d61cd 100644 --- a/src/addon/mod/data/components/index/index.ts +++ b/src/addon/mod/data/components/index/index.ts @@ -27,6 +27,7 @@ import { AddonModDataHelperProvider } from '../../providers/helper'; import { AddonModDataOfflineProvider } from '../../providers/offline'; import { AddonModDataSyncProvider } from '../../providers/sync'; import { AddonModDataComponentsModule } from '../components.module'; +import { AddonModDataPrefetchHandler } from '../../providers/prefetch-handler'; /** * Component that displays a data index page. @@ -82,7 +83,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp constructor(injector: Injector, private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider, private dataOffline: AddonModDataOfflineProvider, @Optional() content: Content, - private dataSync: AddonModDataSyncProvider, private timeUtils: CoreTimeUtilsProvider, + private prefetchHandler: AddonModDataPrefetchHandler, private timeUtils: CoreTimeUtilsProvider, private groupsProvider: CoreGroupsProvider, private commentsProvider: CoreCommentsProvider, private modalCtrl: ModalController, private utils: CoreUtilsProvider, protected navCtrl: NavController, private ratingOffline: CoreRatingOfflineProvider) { @@ -520,17 +521,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp * @return {Promise} Promise resolved when done. */ protected sync(): Promise { - const promises = [ - this.dataSync.syncDatabase(this.data.id), - this.dataSync.syncRatings(this.data.coursemodule) - ]; - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); + return this.prefetchHandler.sync(this.module, this.courseId); } /** diff --git a/src/addon/mod/data/providers/prefetch-handler.ts b/src/addon/mod/data/providers/prefetch-handler.ts index 85c54d10e..5586ef558 100644 --- a/src/addon/mod/data/providers/prefetch-handler.ts +++ b/src/addon/mod/data/providers/prefetch-handler.ts @@ -26,6 +26,7 @@ import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { CoreRatingProvider } from '@core/rating/providers/rating'; import { AddonModDataProvider } from './data'; +import { AddonModDataSyncProvider } from './sync'; import { AddonModDataHelperProvider } from './helper'; /** @@ -43,7 +44,7 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl domUtils: CoreDomUtilsProvider, protected dataProvider: AddonModDataProvider, protected timeUtils: CoreTimeUtilsProvider, protected dataHelper: AddonModDataHelperProvider, protected groupsProvider: CoreGroupsProvider, protected commentsProvider: CoreCommentsProvider, - private ratingProvider: CoreRatingProvider) { + private ratingProvider: CoreRatingProvider, protected syncProvider: AddonModDataSyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -301,4 +302,26 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl return Promise.all(promises); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + const promises = [ + this.syncProvider.syncDatabase(module.instance, siteId), + this.syncProvider.syncRatings(module.id, true, siteId) + ]; + + return Promise.all(promises).then((results) => { + return results.reduce((a, b) => ({ + updated: a.updated || b.updated, + warnings: (a.warnings || []).concat(b.warnings || []), + }), {updated: false}); + }); + } } diff --git a/src/addon/mod/data/providers/sync-cron-handler.ts b/src/addon/mod/data/providers/sync-cron-handler.ts index 31a3db30a..c67e7a102 100644 --- a/src/addon/mod/data/providers/sync-cron-handler.ts +++ b/src/addon/mod/data/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModDataSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.dataSync.syncAllDatabases(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.dataSync.syncAllDatabases(siteId, force); } /** diff --git a/src/addon/mod/data/providers/sync.ts b/src/addon/mod/data/providers/sync.ts index aeb16b190..5bfe991bd 100644 --- a/src/addon/mod/data/providers/sync.ts +++ b/src/addon/mod/data/providers/sync.ts @@ -67,18 +67,21 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the databases in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllDatabases(siteId?: string): Promise { - return this.syncOnSites('all databases', this.syncAllDatabasesFunc.bind(this), undefined, siteId); + syncAllDatabases(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all databases', this.syncAllDatabasesFunc.bind(this), [force], siteId); } /** * Sync all pending databases on a site. - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * + * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllDatabasesFunc(siteId?: string): Promise { + protected syncAllDatabasesFunc(siteId?: string, force?: boolean): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); const promises = []; @@ -93,8 +96,10 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { return; } - promises[action.dataid] = this.syncDatabaseIfNeeded(action.dataid, siteId) - .then((result) => { + promises[action.dataid] = force ? this.syncDatabase(action.dataid, siteId) : + this.syncDatabaseIfNeeded(action.dataid, siteId); + + promises[action.dataid].then((result) => { if (result && result.updated) { // Sync done. Send event. this.eventsProvider.trigger(AddonModDataSyncProvider.AUTO_SYNCED, { @@ -109,7 +114,7 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { return Promise.all(this.utils.objectToArray(promises)); })); - promises.push(this.syncRatings(undefined, siteId)); + promises.push(this.syncRatings(undefined, force, siteId)); return Promise.all(promises); } @@ -357,13 +362,14 @@ export class AddonModDataSyncProvider extends CoreSyncBaseProvider { * Synchronize offline ratings. * * @param {number} [cmId] Course module to be synced. If not defined, sync all databases. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected otherwise. */ - syncRatings(cmId?: number, siteId?: string): Promise { + syncRatings(cmId?: number, force?: boolean, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.ratingSync.syncRatings('mod_data', 'entry', 'module', cmId, 0, siteId).then((results) => { + return this.ratingSync.syncRatings('mod_data', 'entry', 'module', cmId, 0, force, siteId).then((results) => { let updated = false; const warnings = []; const promises = []; diff --git a/src/addon/mod/feedback/providers/prefetch-handler.ts b/src/addon/mod/feedback/providers/prefetch-handler.ts index ab9528fc7..b6ab91297 100644 --- a/src/addon/mod/feedback/providers/prefetch-handler.ts +++ b/src/addon/mod/feedback/providers/prefetch-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; @@ -25,6 +25,7 @@ import { AddonModFeedbackProvider } from './feedback'; import { AddonModFeedbackHelperProvider } from './helper'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreGroupsProvider } from '@providers/groups'; +import { AddonModFeedbackSyncProvider } from './sync'; import { CoreUserProvider } from '@core/user/providers/user'; /** @@ -37,11 +38,14 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH component = AddonModFeedbackProvider.COMPONENT; updatesNames = /^configuration$|^.*files$|^attemptsfinished|^attemptsunfinished$/; + protected syncProvider: AddonModFeedbackSyncProvider; // It will be injected later to prevent circular dependencies. + constructor(translate: TranslateService, appProvider: CoreAppProvider, utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected feedbackProvider: AddonModFeedbackProvider, protected userProvider: CoreUserProvider, protected feedbackHelper: AddonModFeedbackHelperProvider, - protected timeUtils: CoreTimeUtilsProvider, protected groupsProvider: CoreGroupsProvider) { + protected timeUtils: CoreTimeUtilsProvider, protected groupsProvider: CoreGroupsProvider, + protected injector: Injector) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -239,4 +243,20 @@ export class AddonModFeedbackPrefetchHandler extends CoreCourseActivityPrefetchH }); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModFeedbackSyncProvider); + } + + return this.syncProvider.syncFeedback(module.instance, siteId); + } } diff --git a/src/addon/mod/feedback/providers/sync-cron-handler.ts b/src/addon/mod/feedback/providers/sync-cron-handler.ts index 6ace28013..94d43e905 100644 --- a/src/addon/mod/feedback/providers/sync-cron-handler.ts +++ b/src/addon/mod/feedback/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModFeedbackSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.feedbackSync.syncAllFeedbacks(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.feedbackSync.syncAllFeedbacks(siteId, force); } /** diff --git a/src/addon/mod/feedback/providers/sync.ts b/src/addon/mod/feedback/providers/sync.ts index 6c6e008a4..c643f237b 100644 --- a/src/addon/mod/feedback/providers/sync.ts +++ b/src/addon/mod/feedback/providers/sync.ts @@ -72,19 +72,21 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv * Try to synchronize all the feedbacks in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllFeedbacks(siteId?: string): Promise { - return this.syncOnSites('all feedbacks', this.syncAllFeedbacksFunc.bind(this), undefined, siteId); + syncAllFeedbacks(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all feedbacks', this.syncAllFeedbacksFunc.bind(this), [force], siteId); } /** * Sync all pending feedbacks on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllFeedbacksFunc(siteId?: string): Promise { + protected syncAllFeedbacksFunc(siteId?: string, force?: boolean): Promise { // Sync all new responses. return this.feedbackOffline.getAllFeedbackResponses(siteId).then((responses) => { const promises = {}; @@ -97,7 +99,10 @@ export class AddonModFeedbackSyncProvider extends CoreCourseActivitySyncBaseProv continue; } - promises[response.feedbackid] = this.syncFeedbackIfNeeded(response.feedbackid, siteId).then((result) => { + promises[response.feedbackid] = force ? this.syncFeedback(response.feedbackid, siteId) : + this.syncFeedbackIfNeeded(response.feedbackid, siteId); + + promises[response.feedbackid].then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModFeedbackSyncProvider.AUTO_SYNCED, { diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts index 036697410..999974f31 100644 --- a/src/addon/mod/forum/components/index/index.ts +++ b/src/addon/mod/forum/components/index/index.ts @@ -376,18 +376,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom * @return {Promise} Promise resolved when done. */ protected sync(): Promise { - const promises = []; - - promises.push(this.forumSync.syncForumDiscussions(this.forum.id)); - promises.push(this.forumSync.syncForumReplies(this.forum.id)); - promises.push(this.forumSync.syncRatings(this.forum.cmid)); - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); + return this.prefetchHandler.sync(this.module, this.courseId); } /** diff --git a/src/addon/mod/forum/providers/prefetch-handler.ts b/src/addon/mod/forum/providers/prefetch-handler.ts index 03e60a6ba..2b03e2525 100644 --- a/src/addon/mod/forum/providers/prefetch-handler.ts +++ b/src/addon/mod/forum/providers/prefetch-handler.ts @@ -24,6 +24,7 @@ import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/acti import { CoreGroupsProvider } from '@providers/groups'; import { CoreUserProvider } from '@core/user/providers/user'; import { AddonModForumProvider } from './forum'; +import { AddonModForumSyncProvider } from './sync'; import { CoreRatingProvider } from '@core/rating/providers/rating'; /** @@ -46,7 +47,8 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand private groupsProvider: CoreGroupsProvider, private userProvider: CoreUserProvider, private forumProvider: AddonModForumProvider, - private ratingProvider: CoreRatingProvider) { + private ratingProvider: CoreRatingProvider, + private syncProvider: AddonModForumSyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -264,4 +266,27 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand } }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + const promises = []; + + promises.push(this.syncProvider.syncForumDiscussions(module.instance, undefined, siteId)); + promises.push(this.syncProvider.syncForumReplies(module.instance, undefined, siteId)); + promises.push(this.syncProvider.syncRatings(module.id, undefined, true, siteId)); + + return Promise.all(promises).then((results) => { + return results.reduce((a, b) => ({ + updated: a.updated || b.updated, + warnings: (a.warnings || []).concat(b.warnings || []), + }), {updated: false}); + }); + } } diff --git a/src/addon/mod/forum/providers/sync-cron-handler.ts b/src/addon/mod/forum/providers/sync-cron-handler.ts index 860368b25..53a2406e7 100644 --- a/src/addon/mod/forum/providers/sync-cron-handler.ts +++ b/src/addon/mod/forum/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModForumSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.forumSync.syncAllForums(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.forumSync.syncAllForums(siteId, force); } /** diff --git a/src/addon/mod/forum/providers/sync.ts b/src/addon/mod/forum/providers/sync.ts index 8d63ccfc5..a829d2662 100644 --- a/src/addon/mod/forum/providers/sync.ts +++ b/src/addon/mod/forum/providers/sync.ts @@ -69,19 +69,21 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the forums in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllForums(siteId?: string): Promise { - return this.syncOnSites('all forums', this.syncAllForumsFunc.bind(this), [], siteId); + syncAllForums(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all forums', this.syncAllForumsFunc.bind(this), [force], siteId); } /** * Sync all forums on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllForumsFunc(siteId?: string): Promise { + protected syncAllForumsFunc(siteId: string, force?: boolean): Promise { const sitePromises = []; // Sync all new discussions. @@ -94,8 +96,10 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { return; } - promises[discussion.forumid] = this.syncForumDiscussionsIfNeeded(discussion.forumid, discussion.userid, siteId) - .then((result) => { + promises[discussion.forumid] = force ? this.syncForumDiscussions(discussion.forumid, discussion.userid, siteId) : + this.syncForumDiscussionsIfNeeded(discussion.forumid, discussion.userid, siteId); + + promises[discussion.forumid].then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModForumSyncProvider.AUTO_SYNCED, { @@ -120,8 +124,10 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { return; } - promises[reply.discussionid] = this.syncDiscussionRepliesIfNeeded(reply.discussionid, reply.userid, siteId) - .then((result) => { + promises[reply.discussionid] = force ? this.syncDiscussionReplies(reply.discussionid, reply.userid, siteId) : + this.syncDiscussionRepliesIfNeeded(reply.discussionid, reply.userid, siteId); + + promises[reply.discussionid].then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModForumSyncProvider.AUTO_SYNCED, { @@ -137,7 +143,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { return Promise.all(this.utils.objectToArray(promises)); })); - sitePromises.push(this.syncRatings(undefined, undefined, siteId)); + sitePromises.push(this.syncRatings(undefined, undefined, force, siteId)); return Promise.all(sitePromises); } @@ -282,13 +288,14 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider { * * @param {number} [cmId] Course module to be synced. If not defined, sync all forums. * @param {number} [discussionId] Discussion id to be synced. If not defined, sync all discussions. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected otherwise. */ - syncRatings(cmId?: number, discussionId?: number, siteId?: string): Promise { + syncRatings(cmId?: number, discussionId?: number, force?: boolean, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.ratingSync.syncRatings('mod_forum', 'post', 'module', cmId, discussionId, siteId).then((results) => { + return this.ratingSync.syncRatings('mod_forum', 'post', 'module', cmId, discussionId, force, siteId).then((results) => { let updated = false; const warnings = []; const promises = []; diff --git a/src/addon/mod/glossary/components/index/index.ts b/src/addon/mod/glossary/components/index/index.ts index e05699f71..1f473e081 100644 --- a/src/addon/mod/glossary/components/index/index.ts +++ b/src/addon/mod/glossary/components/index/index.ts @@ -23,6 +23,7 @@ import { AddonModGlossaryProvider } from '../../providers/glossary'; import { AddonModGlossaryOfflineProvider } from '../../providers/offline'; import { AddonModGlossarySyncProvider } from '../../providers/sync'; import { AddonModGlossaryModePickerPopoverComponent } from '../mode-picker/mode-picker'; +import { AddonModGlossaryPrefetchHandler } from '../../providers/prefetch-handler'; type FetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'search' | 'letter_all'; @@ -68,7 +69,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity private popoverCtrl: PopoverController, private glossaryProvider: AddonModGlossaryProvider, private glossaryOffline: AddonModGlossaryOfflineProvider, - private glossarySync: AddonModGlossarySyncProvider, + private prefetchHandler: AddonModGlossaryPrefetchHandler, private ratingOffline: CoreRatingOfflineProvider) { super(injector); } @@ -219,17 +220,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity * @return {Promise} Promise resolved when done. */ protected sync(): Promise { - const promises = [ - this.glossarySync.syncGlossaryEntries(this.glossary.id), - this.glossarySync.syncRatings(this.glossary.coursemodule) - ]; - - return Promise.all(promises).then((results) => { - return results.reduce((a, b) => ({ - updated: a.updated || b.updated, - warnings: (a.warnings || []).concat(b.warnings || []), - }), {updated: false}); - }); + return this.prefetchHandler.sync(this.module, this.courseId); } /** diff --git a/src/addon/mod/glossary/providers/prefetch-handler.ts b/src/addon/mod/glossary/providers/prefetch-handler.ts index 024fece96..fb283d9b4 100644 --- a/src/addon/mod/glossary/providers/prefetch-handler.ts +++ b/src/addon/mod/glossary/providers/prefetch-handler.ts @@ -24,6 +24,7 @@ import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/acti import { CoreUserProvider } from '@core/user/providers/user'; import { AddonModGlossaryProvider } from './glossary'; import { CoreRatingProvider } from '@core/rating/providers/rating'; +import { AddonModGlossarySyncProvider } from './sync'; /** * Handler to prefetch forums. @@ -44,7 +45,8 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH domUtils: CoreDomUtilsProvider, private userProvider: CoreUserProvider, private ratingProvider: CoreRatingProvider, - private glossaryProvider: AddonModGlossaryProvider) { + private glossaryProvider: AddonModGlossaryProvider, + private syncProvider: AddonModGlossarySyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -187,4 +189,26 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH return Promise.all(promises); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + const promises = [ + this.syncProvider.syncGlossaryEntries(module.instance, undefined, siteId), + this.syncProvider.syncRatings(module.id, undefined, siteId) + ]; + + return Promise.all(promises).then((results) => { + return results.reduce((a, b) => ({ + updated: a.updated || b.updated, + warnings: (a.warnings || []).concat(b.warnings || []), + }), {updated: false}); + }); + } } diff --git a/src/addon/mod/glossary/providers/sync-cron-handler.ts b/src/addon/mod/glossary/providers/sync-cron-handler.ts index 77ea930d8..d952ce356 100644 --- a/src/addon/mod/glossary/providers/sync-cron-handler.ts +++ b/src/addon/mod/glossary/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModGlossarySyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.glossarySync.syncAllGlossaries(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.glossarySync.syncAllGlossaries(siteId, force); } /** diff --git a/src/addon/mod/glossary/providers/sync.ts b/src/addon/mod/glossary/providers/sync.ts index 3a0dbe0e2..746563f67 100644 --- a/src/addon/mod/glossary/providers/sync.ts +++ b/src/addon/mod/glossary/providers/sync.ts @@ -68,19 +68,21 @@ export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the glossaries in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllGlossaries(siteId?: string): Promise { - return this.syncOnSites('all glossaries', this.syncAllGlossariesFunc.bind(this), [], siteId); + syncAllGlossaries(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all glossaries', this.syncAllGlossariesFunc.bind(this), [force], siteId); } /** * Sync all glossaries on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllGlossariesFunc(siteId?: string): Promise { + protected syncAllGlossariesFunc(siteId: string, force?: boolean): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); const promises = []; @@ -97,8 +99,10 @@ export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { continue; } - promises[entry.glossaryid] = this.syncGlossaryEntriesIfNeeded(entry.glossaryid, entry.userid, siteId) - .then((result) => { + promises[entry.glossaryid] = force ? this.syncGlossaryEntries(entry.glossaryid, entry.userid, siteId) : + this.syncGlossaryEntriesIfNeeded(entry.glossaryid, entry.userid, siteId); + + promises[entry.glossaryid].then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModGlossarySyncProvider.AUTO_SYNCED, { @@ -114,7 +118,7 @@ export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { return Promise.all(this.utils.objectToArray(promises)); })); - promises.push(this.syncRatings(undefined, siteId)); + promises.push(this.syncRatings(undefined, force, siteId)); return Promise.all(promises); } @@ -255,13 +259,14 @@ export class AddonModGlossarySyncProvider extends CoreSyncBaseProvider { * Synchronize offline ratings. * * @param {number} [cmId] Course module to be synced. If not defined, sync all glossaries. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected otherwise. */ - syncRatings(cmId?: number, siteId?: string): Promise { + syncRatings(cmId?: number, force?: boolean, siteId?: string): Promise { siteId = siteId || this.sitesProvider.getCurrentSiteId(); - return this.ratingSync.syncRatings('mod_glossary', 'entry', 'module', cmId, 0, siteId).then((results) => { + return this.ratingSync.syncRatings('mod_glossary', 'entry', 'module', cmId, 0, force, siteId).then((results) => { let updated = false; const warnings = []; const promises = []; diff --git a/src/addon/mod/lesson/providers/lesson-sync.ts b/src/addon/mod/lesson/providers/lesson-sync.ts index 907e8f4d3..1a1abdaf6 100644 --- a/src/addon/mod/lesson/providers/lesson-sync.ts +++ b/src/addon/mod/lesson/providers/lesson-sync.ts @@ -186,26 +186,31 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid * Try to synchronize all the lessons in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllLessons(siteId?: string): Promise { - return this.syncOnSites('all lessons', this.syncAllLessonsFunc.bind(this), [], siteId); + syncAllLessons(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all lessons', this.syncAllLessonsFunc.bind(this), [force], siteId); } /** * Sync all lessons on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllLessonsFunc(siteId?: string): Promise { + protected syncAllLessonsFunc(siteId: string, force?: boolean): Promise { // Get all the lessons that have something to be synchronized. return this.lessonOfflineProvider.getAllLessonsWithData(siteId).then((lessons) => { // Sync all lessons that haven't been synced for a while. const promises = []; - lessons.forEach((lesson) => { - promises.push(this.syncLessonIfNeeded(lesson.id, false, siteId).then((result) => { + lessons.map((lesson) => { + const promise = force ? this.syncLesson(lesson.id, false, false, siteId) : + this.syncLessonIfNeeded(lesson.id, false, siteId); + + return promise.then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(AddonModLessonSyncProvider.AUTO_SYNCED, { @@ -213,7 +218,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid warnings: result.warnings }, siteId); } - })); + }); }); return Promise.all(promises); diff --git a/src/addon/mod/lesson/providers/prefetch-handler.ts b/src/addon/mod/lesson/providers/prefetch-handler.ts index 3660a90cd..01eed6fa3 100644 --- a/src/addon/mod/lesson/providers/prefetch-handler.ts +++ b/src/addon/mod/lesson/providers/prefetch-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { ModalController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; @@ -24,6 +24,7 @@ import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreGroupsProvider } from '@providers/groups'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { AddonModLessonProvider } from './lesson'; +import { AddonModLessonSyncProvider } from './lesson-sync'; /** * Handler to prefetch lessons. @@ -36,10 +37,12 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan // Don't check timers to decrease positives. If a user performs some action it will be reflected in other items. updatesNames = /^configuration$|^.*files$|^grades$|^gradeitems$|^pages$|^answers$|^questionattempts$|^pagesviewed$/; + protected syncProvider: AddonModLessonSyncProvider; // It will be injected later to prevent circular dependencies. + constructor(translate: TranslateService, appProvider: CoreAppProvider, utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected modalCtrl: ModalController, protected groupsProvider: CoreGroupsProvider, - protected lessonProvider: AddonModLessonProvider) { + protected lessonProvider: AddonModLessonProvider, protected injector: Injector) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -429,4 +432,20 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan }); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModLessonSyncProvider); + } + + return this.syncProvider.syncLesson(module.instance, false, false, siteId); + } } diff --git a/src/addon/mod/lesson/providers/sync-cron-handler.ts b/src/addon/mod/lesson/providers/sync-cron-handler.ts index 0ede470f1..3d204645a 100644 --- a/src/addon/mod/lesson/providers/sync-cron-handler.ts +++ b/src/addon/mod/lesson/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModLessonSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.lessonSync.syncAllLessons(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.lessonSync.syncAllLessons(siteId, force); } /** diff --git a/src/addon/mod/quiz/providers/helper.ts b/src/addon/mod/quiz/providers/helper.ts index 3eb4f5225..71897ff50 100644 --- a/src/addon/mod/quiz/providers/helper.ts +++ b/src/addon/mod/quiz/providers/helper.ts @@ -54,7 +54,7 @@ export class AddonModQuizHelperProvider { getAndCheckPreflightData(quiz: any, accessInfo: any, preflightData: any, attempt: any, offline?: boolean, prefetch?: boolean, title?: string, siteId?: string, retrying?: boolean): Promise { - const rules = accessInfo.activerulenames; + const rules = accessInfo && accessInfo.activerulenames; let isPreflightCheckRequired = false; // Check if the user needs to input preflight data. diff --git a/src/addon/mod/quiz/providers/prefetch-handler.ts b/src/addon/mod/quiz/providers/prefetch-handler.ts index d292dc958..b4ec1137c 100644 --- a/src/addon/mod/quiz/providers/prefetch-handler.ts +++ b/src/addon/mod/quiz/providers/prefetch-handler.ts @@ -239,7 +239,16 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl * @return {Promise} Promise resolved when done. */ prefetch(module: any, courseId?: number, single?: boolean, dirPath?: string, canStart: boolean = true): Promise { + if (module.attemptFinished) { + // Delete the value so it does not block anything if true. + delete module.attemptFinished; + + // Quiz got synced recently and an attempt has finished. Do not prefetch. + return Promise.resolve(); + } + return this.prefetchPackage(module, courseId, single, this.prefetchQuiz.bind(this), undefined, canStart); + } /** @@ -554,4 +563,30 @@ export class AddonModQuizPrefetchHandler extends CoreCourseActivityPrefetchHandl } }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModQuizSyncProvider); + } + + return this.quizProvider.getQuiz(courseId, module.id).then((quiz) => { + return this.syncProvider.syncQuiz(quiz, false, siteId).then((results) => { + module.attemptFinished = (results && results.attemptFinished) || false; + + return results; + }).catch(() => { + // Ignore errors. + + module.attemptFinished = false; + }); + }); + } } diff --git a/src/addon/mod/quiz/providers/quiz-sync.ts b/src/addon/mod/quiz/providers/quiz-sync.ts index 854fb8a83..e14b3f1d6 100644 --- a/src/addon/mod/quiz/providers/quiz-sync.ts +++ b/src/addon/mod/quiz/providers/quiz-sync.ts @@ -190,19 +190,21 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider * Try to synchronize all the quizzes in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllQuizzes(siteId?: string): Promise { - return this.syncOnSites('all quizzes', this.syncAllQuizzesFunc.bind(this), [], siteId); + syncAllQuizzes(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all quizzes', this.syncAllQuizzesFunc.bind(this), [force], siteId); } /** * Sync all quizzes on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllQuizzesFunc(siteId?: string): Promise { + protected syncAllQuizzesFunc(siteId?: string, force?: boolean): Promise { // Get all offline attempts. return this.quizOfflineProvider.getAllAttempts(siteId).then((attempts) => { const quizzes = [], @@ -227,7 +229,9 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider // Quiz not blocked, try to synchronize it. promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, false, false, siteId).then((quiz) => { - return this.syncQuizIfNeeded(quiz, false, siteId).then((data) => { + const promise = force ? this.syncQuiz(quiz, false, siteId) : this.syncQuizIfNeeded(quiz, false, siteId); + + return promise.then((data) => { if (data && data.warnings && data.warnings.length) { // Store the warnings to show them when the user opens the quiz. return this.setSyncWarnings(quiz.id, data.warnings, siteId).then(() => { diff --git a/src/addon/mod/quiz/providers/sync-cron-handler.ts b/src/addon/mod/quiz/providers/sync-cron-handler.ts index 2866f3a08..2c3c1bcbe 100644 --- a/src/addon/mod/quiz/providers/sync-cron-handler.ts +++ b/src/addon/mod/quiz/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModQuizSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.quizSync.syncAllQuizzes(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.quizSync.syncAllQuizzes(siteId, force); } /** diff --git a/src/addon/mod/scorm/providers/prefetch-handler.ts b/src/addon/mod/scorm/providers/prefetch-handler.ts index c6f19e138..d64ad764b 100644 --- a/src/addon/mod/scorm/providers/prefetch-handler.ts +++ b/src/addon/mod/scorm/providers/prefetch-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; @@ -24,6 +24,7 @@ import { CoreFileProvider } from '@providers/file'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { AddonModScormProvider } from './scorm'; +import { AddonModScormSyncProvider } from './scorm-sync'; /** * Progress event used when downloading a SCORM. @@ -58,10 +59,12 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand component = AddonModScormProvider.COMPONENT; updatesNames = /^configuration$|^.*files$|^tracks$/; + protected syncProvider: AddonModScormSyncProvider; // It will be injected later to prevent circular dependencies. + constructor(translate: TranslateService, appProvider: CoreAppProvider, utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected fileProvider: CoreFileProvider, protected textUtils: CoreTextUtilsProvider, - protected scormProvider: AddonModScormProvider) { + protected scormProvider: AddonModScormProvider, protected injector: Injector) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -423,4 +426,20 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand return Promise.all(promises); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModScormSyncProvider); + } + + return this.syncProvider.syncScorm(module.instance, siteId); + } } diff --git a/src/addon/mod/scorm/providers/scorm-sync.ts b/src/addon/mod/scorm/providers/scorm-sync.ts index 683cfd450..bb6074179 100644 --- a/src/addon/mod/scorm/providers/scorm-sync.ts +++ b/src/addon/mod/scorm/providers/scorm-sync.ts @@ -444,19 +444,21 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide * Try to synchronize all the SCORMs in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllScorms(siteId?: string): Promise { - return this.syncOnSites('all SCORMs', this.syncAllScormsFunc.bind(this), [], siteId); + syncAllScorms(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all SCORMs', this.syncAllScormsFunc.bind(this), [force], siteId); } /** * Sync all SCORMs on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllScormsFunc(siteId?: string): Promise { + protected syncAllScormsFunc(siteId: string, force?: boolean): Promise { // Get all offline attempts. return this.scormOfflineProvider.getAllAttempts(siteId).then((attempts) => { @@ -481,7 +483,9 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide if (!this.syncProvider.isBlocked(AddonModScormProvider.COMPONENT, scorm.id, siteId)) { promises.push(this.scormProvider.getScormById(scorm.courseId, scorm.id, '', false, siteId).then((scorm) => { - return this.syncScormIfNeeded(scorm, siteId).then((data) => { + const promise = force ? this.syncScorm(scorm, siteId) : this.syncScormIfNeeded(scorm, siteId); + + return promise.then((data) => { if (typeof data != 'undefined') { // We tried to sync. Send event. this.eventsProvider.trigger(AddonModScormSyncProvider.AUTO_SYNCED, { diff --git a/src/addon/mod/scorm/providers/sync-cron-handler.ts b/src/addon/mod/scorm/providers/sync-cron-handler.ts index 797eeb4bb..33999d0cb 100644 --- a/src/addon/mod/scorm/providers/sync-cron-handler.ts +++ b/src/addon/mod/scorm/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModScormSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.scormSync.syncAllScorms(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.scormSync.syncAllScorms(siteId, force); } /** diff --git a/src/addon/mod/survey/providers/prefetch-handler.ts b/src/addon/mod/survey/providers/prefetch-handler.ts index e6b785486..83945c844 100644 --- a/src/addon/mod/survey/providers/prefetch-handler.ts +++ b/src/addon/mod/survey/providers/prefetch-handler.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Injectable } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; import { CoreFilepoolProvider } from '@providers/filepool'; @@ -22,6 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler'; import { AddonModSurveyProvider } from './survey'; +import { AddonModSurveySyncProvider } from './sync'; import { AddonModSurveyHelperProvider } from './helper'; /** @@ -34,10 +35,12 @@ export class AddonModSurveyPrefetchHandler extends CoreCourseActivityPrefetchHan component = AddonModSurveyProvider.COMPONENT; updatesNames = /^configuration$|^.*files$|^answers$/; + protected syncProvider: AddonModSurveySyncProvider; // It will be injected later to prevent circular dependencies. + constructor(translate: TranslateService, appProvider: CoreAppProvider, utils: CoreUtilsProvider, courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected surveyProvider: AddonModSurveyProvider, - protected surveyHelper: AddonModSurveyHelperProvider) { + protected surveyHelper: AddonModSurveyHelperProvider, protected injector: Injector) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -126,4 +129,20 @@ export class AddonModSurveyPrefetchHandler extends CoreCourseActivityPrefetchHan return Promise.all(promises); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + if (!this.syncProvider) { + this.syncProvider = this.injector.get(AddonModSurveySyncProvider); + } + + return this.syncProvider.syncSurvey(module.instance, undefined, siteId); + } } diff --git a/src/addon/mod/survey/providers/sync-cron-handler.ts b/src/addon/mod/survey/providers/sync-cron-handler.ts index 1949d9e14..f3e2e9552 100644 --- a/src/addon/mod/survey/providers/sync-cron-handler.ts +++ b/src/addon/mod/survey/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModSurveySyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.surveySync.syncAllSurveys(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.surveySync.syncAllSurveys(siteId, force); } /** diff --git a/src/addon/mod/survey/providers/sync.ts b/src/addon/mod/survey/providers/sync.ts index d38b0a898..0a57a0258 100644 --- a/src/addon/mod/survey/providers/sync.ts +++ b/src/addon/mod/survey/providers/sync.ts @@ -68,23 +68,29 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid * Try to synchronize all the surveys in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllSurveys(siteId?: string): Promise { - return this.syncOnSites('all surveys', this.syncAllSurveysFunc.bind(this), undefined, siteId); + syncAllSurveys(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all surveys', this.syncAllSurveysFunc.bind(this), [force], siteId); } /** * Sync all pending surveys on a site. - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllSurveysFunc(siteId?: string): Promise { + protected syncAllSurveysFunc(siteId: string, force?: boolean): Promise { // Get all survey answers pending to be sent in the site. return this.surveyOffline.getAllData(siteId).then((entries) => { // Sync all surveys. const promises = entries.map((entry) => { - return this.syncSurveyIfNeeded(entry.surveyid, entry.userid, siteId).then((result) => { + const promise = force ? this.syncSurvey(entry.surveyid, entry.userid, siteId) : + this.syncSurveyIfNeeded(entry.surveyid, entry.userid, siteId); + + return promise.then((result) => { if (result && result.answersSent) { // Sync successful, send event. this.eventsProvider.trigger(AddonModSurveySyncProvider.AUTO_SYNCED, { @@ -124,91 +130,94 @@ export class AddonModSurveySyncProvider extends CoreCourseActivitySyncBaseProvid * Synchronize a survey. * * @param {number} surveyId Survey ID. - * @param {number} userId User the answers belong to. + * @param {number} [userId] User the answers belong to. If not defined, current user. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected otherwise. */ - syncSurvey(surveyId: number, userId: number, siteId?: string): Promise { - siteId = siteId || this.sitesProvider.getCurrentSiteId(); + syncSurvey(surveyId: number, userId?: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + userId = userId || site.getUserId(); + siteId = site.getId(); - const syncId = this.getSyncId(surveyId, userId); - if (this.isSyncing(syncId, siteId)) { - // There's already a sync ongoing for this survey and user, return the promise. - return this.getOngoingSync(syncId, siteId); - } - - this.logger.debug(`Try to sync survey '${surveyId}' for user '${userId}'`); - - let courseId; - const result = { - warnings: [], - answersSent: false - }; - - // Sync offline logs. - const syncPromise = this.logHelper.syncIfNeeded(AddonModSurveyProvider.COMPONENT, surveyId, siteId).catch(() => { - // Ignore errors. - }).then(() => { - // Get answers to be sent. - return this.surveyOffline.getSurveyData(surveyId, siteId, userId).catch(() => { - // No offline data found, return empty object. - return {}; - }); - }).then((data) => { - if (!data.answers || !data.answers.length) { - // Nothing to sync. - return; + const syncId = this.getSyncId(surveyId, userId); + if (this.isSyncing(syncId, siteId)) { + // There's already a sync ongoing for this survey and user, return the promise. + return this.getOngoingSync(syncId, siteId); } - if (!this.appProvider.isOnline()) { - // Cannot sync in offline. - return Promise.reject(null); - } + this.logger.debug(`Try to sync survey '${surveyId}' for user '${userId}'`); - courseId = data.courseid; + let courseId; + const result = { + warnings: [], + answersSent: false + }; - // Send the answers. - return this.surveyProvider.submitAnswersOnline(surveyId, data.answers, siteId).then(() => { - result.answersSent = true; - - // Answers sent, delete them. - return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId); - }).catch((error) => { - if (this.utils.isWebServiceError(error)) { - - // The WebService has thrown an error, this means that answers cannot be submitted. Delete them. - result.answersSent = true; - - return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId).then(() => { - // Answers deleted, add a warning. - result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, - name: data.name, - error: this.textUtils.getErrorMessageFromError(error) - })); - }); + // Sync offline logs. + const syncPromise = this.logHelper.syncIfNeeded(AddonModSurveyProvider.COMPONENT, surveyId, siteId).catch(() => { + // Ignore errors. + }).then(() => { + // Get answers to be sent. + return this.surveyOffline.getSurveyData(surveyId, siteId, userId).catch(() => { + // No offline data found, return empty object. + return {}; + }); + }).then((data) => { + if (!data.answers || !data.answers.length) { + // Nothing to sync. + return; } - // Couldn't connect to server, reject. - return Promise.reject(error); - }); - }).then(() => { - if (courseId) { - // Data has been sent to server, update survey data. - return this.courseProvider.getModuleBasicInfoByInstance(surveyId, 'survey', siteId).then((module) => { - return this.prefetchAfterUpdate(module, courseId, undefined, siteId); - }).catch(() => { - // Ignore errors. - }); - } - }).then(() => { - // Sync finished, set sync time. - return this.setSyncTime(syncId, siteId); - }).then(() => { - return result; - }); + if (!this.appProvider.isOnline()) { + // Cannot sync in offline. + return Promise.reject(null); + } - return this.addOngoingSync(syncId, syncPromise, siteId); + courseId = data.courseid; + + // Send the answers. + return this.surveyProvider.submitAnswersOnline(surveyId, data.answers, siteId).then(() => { + result.answersSent = true; + + // Answers sent, delete them. + return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId); + }).catch((error) => { + if (this.utils.isWebServiceError(error)) { + + // The WebService has thrown an error, this means that answers cannot be submitted. Delete them. + result.answersSent = true; + + return this.surveyOffline.deleteSurveyAnswers(surveyId, siteId, userId).then(() => { + // Answers deleted, add a warning. + result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { + component: this.componentTranslate, + name: data.name, + error: this.textUtils.getErrorMessageFromError(error) + })); + }); + } + + // Couldn't connect to server, reject. + return Promise.reject(error); + }); + }).then(() => { + if (courseId) { + // Data has been sent to server, update survey data. + return this.courseProvider.getModuleBasicInfoByInstance(surveyId, 'survey', siteId).then((module) => { + return this.prefetchAfterUpdate(module, courseId, undefined, siteId); + }).catch(() => { + // Ignore errors. + }); + } + }).then(() => { + // Sync finished, set sync time. + return this.setSyncTime(syncId, siteId); + }).then(() => { + return result; + }); + + return this.addOngoingSync(syncId, syncPromise, siteId); + }); } } diff --git a/src/addon/mod/wiki/providers/prefetch-handler.ts b/src/addon/mod/wiki/providers/prefetch-handler.ts index a8848db0c..5aaa5d158 100644 --- a/src/addon/mod/wiki/providers/prefetch-handler.ts +++ b/src/addon/mod/wiki/providers/prefetch-handler.ts @@ -27,6 +27,7 @@ import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreGradesHelperProvider } from '@core/grades/providers/helper'; import { CoreUserProvider } from '@core/user/providers/user'; import { AddonModWikiProvider } from './wiki'; +import { AddonModWikiSyncProvider } from './wiki-sync'; /** * Handler to prefetch wikis. @@ -42,7 +43,8 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl courseProvider: CoreCourseProvider, filepoolProvider: CoreFilepoolProvider, sitesProvider: CoreSitesProvider, domUtils: CoreDomUtilsProvider, protected wikiProvider: AddonModWikiProvider, protected userProvider: CoreUserProvider, protected textUtils: CoreTextUtilsProvider, protected courseHelper: CoreCourseHelperProvider, - protected groupsProvider: CoreGroupsProvider, protected gradesHelper: CoreGradesHelperProvider) { + protected groupsProvider: CoreGroupsProvider, protected gradesHelper: CoreGradesHelperProvider, + protected syncProvider: AddonModWikiSyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -210,4 +212,16 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl return Promise.all(promises); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + return this.syncProvider.syncWiki(module.instance, module.course, module.id, siteId); + } } diff --git a/src/addon/mod/wiki/providers/sync-cron-handler.ts b/src/addon/mod/wiki/providers/sync-cron-handler.ts index ce1fa6980..c74a439fe 100644 --- a/src/addon/mod/wiki/providers/sync-cron-handler.ts +++ b/src/addon/mod/wiki/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModWikiSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.wikiSync.syncAllWikis(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.wikiSync.syncAllWikis(siteId, force); } /** diff --git a/src/addon/mod/wiki/providers/wiki-sync.ts b/src/addon/mod/wiki/providers/wiki-sync.ts index cfd37e1a0..9fa0200c9 100644 --- a/src/addon/mod/wiki/providers/wiki-sync.ts +++ b/src/addon/mod/wiki/providers/wiki-sync.ts @@ -143,19 +143,21 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the wikis in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllWikis(siteId?: string): Promise { - return this.syncOnSites('all wikis', this.syncAllWikisFunc.bind(this), [], siteId); + syncAllWikis(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all wikis', this.syncAllWikisFunc.bind(this), [force], siteId); } /** * Sync all wikis on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllWikisFunc(siteId?: string): Promise { + protected syncAllWikisFunc(siteId: string, force?: boolean): Promise { // Get all the pages created in offline. return this.wikiOfflineProvider.getAllNewPages(siteId).then((pages) => { const promises = [], @@ -171,8 +173,10 @@ export class AddonModWikiSyncProvider extends CoreSyncBaseProvider { for (const id in subwikis) { const subwiki = subwikis[id]; - promises.push(this.syncSubwikiIfNeeded(subwiki.subwikiid, subwiki.wikiid, subwiki.userid, subwiki.groupid, - siteId).then((result) => { + const promise = force ? this.syncSubwiki(subwiki.subwikiid, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId) + : this.syncSubwikiIfNeeded(subwiki.subwikiid, subwiki.wikiid, subwiki.userid, subwiki.groupid, siteId); + + promises.push(promise.then((result) => { if (result && result.updated) { // Sync successful, send event. diff --git a/src/addon/mod/workshop/providers/prefetch-handler.ts b/src/addon/mod/workshop/providers/prefetch-handler.ts index ccdb6c4d2..9ec409fa3 100644 --- a/src/addon/mod/workshop/providers/prefetch-handler.ts +++ b/src/addon/mod/workshop/providers/prefetch-handler.ts @@ -24,6 +24,7 @@ import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/acti import { CoreGroupsProvider } from '@providers/groups'; import { CoreUserProvider } from '@core/user/providers/user'; import { AddonModWorkshopProvider } from './workshop'; +import { AddonModWorkshopSyncProvider } from './sync'; import { AddonModWorkshopHelperProvider } from './helper'; /** @@ -47,7 +48,8 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH private groupsProvider: CoreGroupsProvider, private userProvider: CoreUserProvider, private workshopProvider: AddonModWorkshopProvider, - private workshopHelper: AddonModWorkshopHelperProvider) { + private workshopHelper: AddonModWorkshopHelperProvider, + private syncProvider: AddonModWorkshopSyncProvider) { super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils); } @@ -365,4 +367,16 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH }); }); } + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync(module: any, courseId: number, siteId?: any): Promise { + return this.syncProvider.syncWorkshop(module.instance, siteId); + } } diff --git a/src/addon/mod/workshop/providers/sync-cron-handler.ts b/src/addon/mod/workshop/providers/sync-cron-handler.ts index 05f6a011f..3202ff253 100644 --- a/src/addon/mod/workshop/providers/sync-cron-handler.ts +++ b/src/addon/mod/workshop/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonModWorkshopSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.workshopSync.syncAllWorkshops(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.workshopSync.syncAllWorkshops(siteId, force); } /** diff --git a/src/addon/mod/workshop/providers/sync.ts b/src/addon/mod/workshop/providers/sync.ts index 74f7619cf..e30f6c387 100644 --- a/src/addon/mod/workshop/providers/sync.ts +++ b/src/addon/mod/workshop/providers/sync.ts @@ -76,25 +76,27 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all workshops that need it and haven't been synchronized in a while. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved when the sync is done. */ - syncAllWorkshops(siteId?: string): Promise { - return this.syncOnSites('all workshops', this.syncAllWorkshopsFunc.bind(this), [], siteId); + syncAllWorkshops(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all workshops', this.syncAllWorkshopsFunc.bind(this), [force], siteId); } /** * Sync all workshops on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllWorkshopsFunc(siteId?: string): Promise { + protected syncAllWorkshopsFunc(siteId: string, force?: boolean): Promise { return this.workshopOffline.getAllWorkshops(siteId).then((workshopIds) => { - const promises = []; - // Sync all workshops that haven't been synced for a while. - workshopIds.forEach((workshopId) => { - promises.push(this.syncWorkshopIfNeeded(workshopId, siteId).then((data) => { + const promises = workshopIds.map((workshopId) => { + const promise = force ? this.syncWorkshop(workshopId, siteId) : this.syncWorkshopIfNeeded(workshopId, siteId); + + return promise.then((data) => { if (data && data.updated) { // Sync done. Send event. this.eventsProvider.trigger(AddonModWorkshopSyncProvider.AUTO_SYNCED, { @@ -102,7 +104,7 @@ export class AddonModWorkshopSyncProvider extends CoreSyncBaseProvider { warnings: data.warnings }, siteId); } - })); + }); }); return Promise.all(promises); diff --git a/src/addon/notes/providers/notes-sync.ts b/src/addon/notes/providers/notes-sync.ts index 376e5b673..77b7039c3 100644 --- a/src/addon/notes/providers/notes-sync.ts +++ b/src/addon/notes/providers/notes-sync.ts @@ -48,19 +48,21 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the notes in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllNotes(siteId?: string): Promise { - return this.syncOnSites('all notes', this.syncAllNotesFunc.bind(this), [], siteId); + syncAllNotes(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('all notes', this.syncAllNotesFunc.bind(this), [force], siteId); } /** * Synchronize all the notes in a certain site * * @param {string} siteId Site ID to sync. + * @param {boolean} force Wether to force sync not depending on last execution. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - private syncAllNotesFunc(siteId: string): Promise { + private syncAllNotesFunc(siteId: string, force: boolean): Promise { return this.notesOffline.getAllNotes(siteId).then((notes) => { // Get all the courses to be synced. const courseIds = []; @@ -72,7 +74,9 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider { // Sync all courses. const promises = courseIds.map((courseId) => { - return this.syncNotesIfNeeded(courseId, siteId).then((warnings) => { + const promise = force ? this.syncNotes(courseId, siteId) : this.syncNotesIfNeeded(courseId, siteId); + + return promise.then((warnings) => { if (typeof warnings != 'undefined') { // Sync successful, send event. this.eventsProvider.trigger(AddonNotesSyncProvider.AUTO_SYNCED, { diff --git a/src/addon/notes/providers/sync-cron-handler.ts b/src/addon/notes/providers/sync-cron-handler.ts index 25d5d2c24..dcd1ad5d6 100644 --- a/src/addon/notes/providers/sync-cron-handler.ts +++ b/src/addon/notes/providers/sync-cron-handler.ts @@ -30,10 +30,11 @@ export class AddonNotesSyncCronHandler implements CoreCronHandler { * Receives the ID of the site affected, undefined for all sites. * * @param {string} [siteId] ID of the site affected, undefined for all sites. - * @return {Promise} Promise resolved when done, rejected if failure. + * @param {boolean} [force] Wether the execution is forced (manual sync). + * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - return this.notesSync.syncAllNotes(siteId); + execute(siteId?: string, force?: boolean): Promise { + return this.notesSync.syncAllNotes(siteId, force); } /** diff --git a/src/addon/notifications/providers/cron-handler.ts b/src/addon/notifications/providers/cron-handler.ts index e16ed9a1f..e7ae59f17 100644 --- a/src/addon/notifications/providers/cron-handler.ts +++ b/src/addon/notifications/providers/cron-handler.ts @@ -66,12 +66,14 @@ export class AddonNotificationsCronHandler implements CoreCronHandler { /** * Execute the process. + * Receives the ID of the site affected, undefined for all sites. * - * @param {string} [siteId] ID of the site affected. If not defined, all sites. - * @return {Promise} Promise resolved when done. If the promise is rejected, this function will be called again often, - * it shouldn't be abused. + * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). + * @return {Promise} Promise resolved when done, rejected if failure. If the promise is rejected, this function + * will be called again often, it shouldn't be abused. */ - execute(siteId?: string): Promise { + execute(siteId?: string, force?: boolean): Promise { if (this.sitesProvider.isCurrentSite(siteId)) { this.eventsProvider.trigger(AddonNotificationsProvider.READ_CRON_EVENT, {}, this.sitesProvider.getCurrentSiteId()); } diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index ba486f2a9..5d90106fb 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -291,6 +291,7 @@ export class CoreCourseHelperProvider { } return promise.then((sections) => { + // Confirm the download. return this.confirmDownloadSizeSection(course.id, undefined, sections, true).then(() => { // User confirmed, get the course handlers if needed. @@ -1268,7 +1269,7 @@ export class CoreCourseHelperProvider { return promise.catch(() => { // Ignore errors. }).then(() => { - return handler.prefetch(module, courseId, true); + return this.prefetchDelegate.prefetchModule(module, courseId, true); }); }); } @@ -1341,18 +1342,22 @@ export class CoreCourseHelperProvider { section.isDownloading = true; - // Validate the section needs to be downloaded and calculate amount of modules that need to be downloaded. - promises.push(this.prefetchDelegate.getModulesStatus(section.modules, courseId, section.id).then((result) => { - if (result.status == CoreConstants.DOWNLOADED || result.status == CoreConstants.NOT_DOWNLOADABLE) { - // Section is downloaded or not downloadable, nothing to do. - return; - } + // Sync the modules first. + promises.push(this.prefetchDelegate.syncModules(section.modules, courseId).then(() => { + // Validate the section needs to be downloaded and calculate amount of modules that need to be downloaded. + return this.prefetchDelegate.getModulesStatus(section.modules, courseId, section.id).then((result) => { + if (result.status == CoreConstants.DOWNLOADED || result.status == CoreConstants.NOT_DOWNLOADABLE) { + // Section is downloaded or not downloadable, nothing to do. - return this.prefetchSingleSection(section, result, courseId); - }, (error) => { - section.isDownloading = false; + return ; + } - return Promise.reject(error); + return this.prefetchSingleSection(section, result, courseId); + }, (error) => { + section.isDownloading = false; + + return Promise.reject(error); + }); })); // Download the files in the section description. diff --git a/src/core/course/providers/log-cron-handler.ts b/src/core/course/providers/log-cron-handler.ts index c04dea1b7..063a37ea6 100644 --- a/src/core/course/providers/log-cron-handler.ts +++ b/src/core/course/providers/log-cron-handler.ts @@ -28,12 +28,13 @@ export class CoreCourseLogCronHandler implements CoreCronHandler { /** * Execute the process. - * Receives the ID of the site affected, undefined for the current site. + * Receives the ID of the site affected, undefined for all sites. * - * @param {string} [siteId] ID of the site affected, undefined for the current site. + * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { + execute(siteId?: string, force?: boolean): Promise { return this.sitesProvider.getSite(siteId).then((site) => { return this.courseProvider.logView(site.getSiteHomeId(), undefined, site.getId()); }); diff --git a/src/core/course/providers/log-helper.ts b/src/core/course/providers/log-helper.ts index 9ec8ea25b..89b76231f 100644 --- a/src/core/course/providers/log-helper.ts +++ b/src/core/course/providers/log-helper.ts @@ -227,7 +227,7 @@ export class CoreCourseLogHelperProvider { * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when done. */ - syncAll(siteId?: string): Promise { + syncSite(siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { const siteId = site.getId(); diff --git a/src/core/course/providers/module-prefetch-delegate.ts b/src/core/course/providers/module-prefetch-delegate.ts index 24ca8c933..25aae46ab 100644 --- a/src/core/course/providers/module-prefetch-delegate.ts +++ b/src/core/course/providers/module-prefetch-delegate.ts @@ -206,6 +206,16 @@ export interface CoreCourseModulePrefetchHandler extends CoreDelegateHandler { * @return {Promise} Promise resolved when done. */ removeFiles?(module: any, courseId: number): Promise; + + /** + * Sync a module. + * + * @param {any} module Module. + * @param {number} courseId Course ID the module belongs to + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + sync?(module: any, courseId: number, siteId?: any): Promise; } /** @@ -1139,12 +1149,44 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate { // Check if the module has a prefetch handler. if (handler) { - return handler.prefetch(module, courseId, single); + return this.syncModule(module, courseId).then(() => { + return handler.prefetch(module, courseId, single); + }); } return Promise.resolve(); } + /** + * Sync a group of modules. + * + * @param {any[]} modules Array of modules to sync. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when finished. + */ + syncModules(modules: any[], courseId: number): Promise { + return Promise.all(modules.map((module) => { + return this.syncModule(module, courseId); + })); + } + + /** + * Sync a module. + * + * @param {any} module Module to sync. + * @param {number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when finished. + */ + syncModule(module: any, courseId: number): Promise { + const handler = this.getPrefetchHandlerFor(module); + + const promise = handler && handler.sync ? handler.sync(module, courseId) : Promise.resolve(); + + return promise.catch(() => { + // Ignore errors. + }); + } + /** * Prefetches a list of modules using their prefetch handlers. * If a prefetch already exists for this site and id, returns the current promise. diff --git a/src/core/course/providers/sync-cron-handler.ts b/src/core/course/providers/sync-cron-handler.ts index 4e85dd398..8f3adc3a6 100644 --- a/src/core/course/providers/sync-cron-handler.ts +++ b/src/core/course/providers/sync-cron-handler.ts @@ -15,7 +15,6 @@ import { Injectable } from '@angular/core'; import { CoreCronHandler } from '@providers/cron'; import { CoreCourseSyncProvider } from './sync'; -import { CoreCourseLogHelperProvider } from './log-helper'; /** * Synchronization cron handler. @@ -24,24 +23,18 @@ import { CoreCourseLogHelperProvider } from './log-helper'; export class CoreCourseSyncCronHandler implements CoreCronHandler { name = 'CoreCourseSyncCronHandler'; - constructor(private courseSync: CoreCourseSyncProvider, private logHelper: CoreCourseLogHelperProvider) {} + constructor(private courseSync: CoreCourseSyncProvider) {} /** * Execute the process. * Receives the ID of the site affected, undefined for all sites. * - * @param {string} [siteId] ID of the site affected, undefined for all sites. - * @return {Promise} Promise resolved when done, rejected if failure. + * @param {string} [siteId] ID of the site affected, undefined for all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). + * @return {Promise} Promise resolved when done, rejected if failure. */ - execute(siteId?: string): Promise { - const promises = []; - // Sync activity logs even if the activity does not have sync handler. - // This will sync all the activity logs even if there's nothing else to sync and also recources. - promises.push(this.logHelper.syncAll(siteId)); - - promises.push(this.courseSync.syncAllCourses(siteId)); - - return Promise.all(promises); + execute(siteId?: string, force?: boolean): Promise { + return this.courseSync.syncAllCourses(siteId); } /** diff --git a/src/core/course/providers/sync.ts b/src/core/course/providers/sync.ts index 058fba729..2c091a863 100644 --- a/src/core/course/providers/sync.ts +++ b/src/core/course/providers/sync.ts @@ -13,18 +13,19 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreLoggerProvider } from '@providers/logger'; +import { TranslateService } from '@ngx-translate/core'; import { CoreSyncBaseProvider } from '@classes/base-sync'; +import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreAppProvider } from '@providers/app'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSyncProvider } from '@providers/sync'; import { CoreCourseOfflineProvider } from './course-offline'; import { CoreCourseProvider } from './course'; -import { CoreEventsProvider } from '@providers/events'; -import { TranslateService } from '@ngx-translate/core'; -import { CoreSyncProvider } from '@providers/sync'; +import { CoreCourseLogHelperProvider } from './log-helper'; /** * Service to sync course offline data. This only syncs the offline data of the course itself, not the offline data of @@ -39,7 +40,7 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider { protected appProvider: CoreAppProvider, private courseOffline: CoreCourseOfflineProvider, private eventsProvider: CoreEventsProvider, private courseProvider: CoreCourseProvider, translate: TranslateService, private utils: CoreUtilsProvider, protected textUtils: CoreTextUtilsProvider, - syncProvider: CoreSyncProvider, timeUtils: CoreTimeUtilsProvider) { + syncProvider: CoreSyncProvider, timeUtils: CoreTimeUtilsProvider, protected logHelper: CoreCourseLogHelperProvider) { super('CoreCourseSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); } @@ -48,25 +49,32 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider { * Try to synchronize all the courses in a certain site or in all sites. * * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - syncAllCourses(siteId?: string): Promise { - return this.syncOnSites('courses', this.syncAllCoursesFunc.bind(this), undefined, siteId); + syncAllCourses(siteId?: string, force?: boolean): Promise { + return this.syncOnSites('courses', this.syncAllCoursesFunc.bind(this), [force], siteId); } /** * Sync all courses on a site. * - * @param {string} [siteId] Site ID to sync. If not defined, sync all sites. + * @param {string} siteId Site ID to sync. If not defined, sync all sites. + * @param {boolean} force Wether the execution is forced (manual sync). * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ - protected syncAllCoursesFunc(siteId?: string): Promise { - return this.courseOffline.getAllManualCompletions(siteId).then((completions) => { - const promises = []; + protected syncAllCoursesFunc(siteId: string, force: boolean): Promise { + const p1 = []; + p1.push(this.logHelper.syncSite(siteId)); + + p1.push(this.courseOffline.getAllManualCompletions(siteId).then((completions) => { // Sync all courses. - completions.forEach((completion) => { - promises.push(this.syncCourseIfNeeded(completion.courseid, siteId).then((result) => { + const p2 = completions.map((completion) => { + const promise = force ? this.syncCourse(completion.courseid, siteId) : + this.syncCourseIfNeeded(completion.courseid, siteId); + + return promise.then((result) => { if (result && result.updated) { // Sync successful, send event. this.eventsProvider.trigger(CoreCourseSyncProvider.AUTO_SYNCED, { @@ -74,9 +82,13 @@ export class CoreCourseSyncProvider extends CoreSyncBaseProvider { warnings: result.warnings }, siteId); } - })); + }); }); - }); + + return Promise.all(p2); + })); + + return Promise.all(p1); } /** diff --git a/src/core/mainmenu/pages/menu/menu.scss b/src/core/mainmenu/pages/menu/menu.scss index 2fa0db196..5b532fcb8 100644 --- a/src/core/mainmenu/pages/menu/menu.scss +++ b/src/core/mainmenu/pages/menu/menu.scss @@ -17,4 +17,20 @@ ion-app.app-root page-core-mainmenu { font-size: 23px; height: 23px; } + + .ion-md-fa-newspaper-o, + .ion-ios-fa-newspaper-o, + .ion-ios-fa-newspaper-o-outline, + .ion-wp-fa-newspaper-o, + .ion-fa-newspaper-o { + @extend .fa-newspaper-o; + @extend .fa; + font-size: 22px; + height: 22px; + } + + .ion-ios-fa-newspaper-o-outline { + font-size: 23px; + height: 23px; + } } diff --git a/src/core/rating/providers/sync.ts b/src/core/rating/providers/sync.ts index 0f34ce359..341797fe2 100644 --- a/src/core/rating/providers/sync.ts +++ b/src/core/rating/providers/sync.ts @@ -59,19 +59,23 @@ export class CoreRatingSyncProvider extends CoreSyncBaseProvider { * @param {string} [contextLevel] Context level: course, module, user, etc. * @param {numnber} [instanceId] Context instance id. * @param {number} [itemSetId] Item set id. + * @param {boolean} [force] Wether to force sync not depending on last execution. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ syncRatings(component: string, ratingArea: string, contextLevel?: string, instanceId?: number, itemSetId?: number, - siteId?: string): Promise<{itemSet: CoreRatingItemSet, updated: number[], warnings: string[]}[]> { + force?: boolean, siteId?: string): Promise<{itemSet: CoreRatingItemSet, updated: number[], warnings: string[]}[]> { siteId = siteId || this.sitesProvider.getCurrentSiteId(); return this.ratingOffline.getItemSets(component, ratingArea, contextLevel, instanceId, itemSetId, siteId) .then((itemSets) => { const results = []; const promises = itemSets.map((itemSet) => { - return this.syncItemSetIfNeeded(component, ratingArea, itemSet.contextLevel, itemSet.instanceId, - itemSet.itemSetId, siteId).then((result) => { + const promise = force ? this.syncItemSet(component, ratingArea, itemSet.contextLevel, itemSet.instanceId, + itemSet.itemSetId, siteId) : this.syncItemSetIfNeeded(component, ratingArea, itemSet.contextLevel, + itemSet.instanceId, itemSet.itemSetId, siteId); + + return promise.then((result) => { if (result.updated) { // Sync successful, send event. this.eventsProvider.trigger(CoreRatingSyncProvider.SYNCED_EVENT, { diff --git a/src/providers/cron.ts b/src/providers/cron.ts index 99d12b95d..b29bc654f 100644 --- a/src/providers/cron.ts +++ b/src/providers/cron.ts @@ -63,10 +63,11 @@ export interface CoreCronHandler { * Execute the process. * * @param {string} [siteId] ID of the site affected. If not defined, all sites. + * @param {boolean} [force] Determines if it's a forced execution. * @return {Promise} Promise resolved when done. If the promise is rejected, this function will be called again often, * it shouldn't be abused. */ - execute?(siteId?: string): Promise; + execute?(siteId?: string, force?: boolean): Promise; /** * Whether the handler is running. Used internally by the provider, there's no need to set it. @@ -181,7 +182,7 @@ export class CoreCronDelegate { this.queuePromise = this.queuePromise.catch(() => { // Ignore errors in previous handlers. }).then(() => { - return this.executeHandler(name, siteId).then(() => { + return this.executeHandler(name, force, siteId).then(() => { this.logger.debug(`Execution of handler '${name}' was a success.`); return this.setHandlerLastExecutionTime(name, Date.now()).then(() => { @@ -204,16 +205,18 @@ export class CoreCronDelegate { * Run a handler, cancelling the execution if it takes more than MAX_TIME_PROCESS. * * @param {string} name Name of the handler. + * @param {boolean} [force] Wether the execution is forced (manual sync). * @param {string} [siteId] Site ID. If not defined, all sites. * @return {Promise} Promise resolved when the handler finishes or reaches max time, rejected if it fails. */ - protected executeHandler(name: string, siteId?: string): Promise { + protected executeHandler(name: string, force?: boolean, siteId?: string): Promise { return new Promise((resolve, reject): void => { let cancelTimeout; this.logger.debug('Executing handler: ' + name); + // Wrap the call in Promise.resolve to make sure it's a promise. - Promise.resolve(this.handlers[name].execute(siteId)).then(resolve).catch(reject).finally(() => { + Promise.resolve(this.handlers[name].execute(siteId, force)).then(resolve).catch(reject).finally(() => { clearTimeout(cancelTimeout); });