MOBILE-2348 quiz: Implement sync provider and handler
parent
5a2e57796d
commit
02f25f50ac
|
@ -34,12 +34,12 @@ export class AddonMessagesSyncProvider extends CoreSyncBaseProvider {
|
|||
|
||||
static AUTO_SYNCED = 'addon_messages_autom_synced';
|
||||
|
||||
constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider, private messagesOffline: AddonMessagesOfflineProvider,
|
||||
private eventsProvider: CoreEventsProvider, private messagesProvider: AddonMessagesProvider,
|
||||
private userProvider: CoreUserProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||
syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider) {
|
||||
super('AddonMessagesSync', sitesProvider, loggerProvider, appProvider, syncProvider, textUtils);
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
translate: TranslateService, syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider,
|
||||
private messagesOffline: AddonMessagesOfflineProvider, private eventsProvider: CoreEventsProvider,
|
||||
private messagesProvider: AddonMessagesProvider, private userProvider: CoreUserProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
super('AddonMessagesSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,404 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreSyncProvider } from '@providers/sync';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreQuestionProvider } from '@core/question/providers/question';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { AddonModQuizProvider } from './quiz';
|
||||
import { AddonModQuizOfflineProvider } from './quiz-offline';
|
||||
import { AddonModQuizPrefetchHandler } from './prefetch-handler';
|
||||
|
||||
/**
|
||||
* Data returned by a quiz sync.
|
||||
*/
|
||||
export interface AddonModQuizSyncResult {
|
||||
/**
|
||||
* List of warnings.
|
||||
* @type {string[]}
|
||||
*/
|
||||
warnings: string[];
|
||||
|
||||
/**
|
||||
* Whether an attempt was finished in the site due to the sync,
|
||||
* @type {boolean}
|
||||
*/
|
||||
attemptFinished: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to sync quizzes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModQuizSyncProvider extends CoreSyncBaseProvider {
|
||||
|
||||
static AUTO_SYNCED = 'addon_mod_quiz_autom_synced';
|
||||
static SYNC_TIME = 300000;
|
||||
|
||||
protected componentTranslate: string;
|
||||
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
||||
courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider,
|
||||
private quizProvider: AddonModQuizProvider, private quizOfflineProvider: AddonModQuizOfflineProvider,
|
||||
private prefetchHandler: AddonModQuizPrefetchHandler, private questionProvider: CoreQuestionProvider,
|
||||
private questionDelegate: CoreQuestionDelegate) {
|
||||
|
||||
super('AddonModQuizSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate);
|
||||
|
||||
this.componentTranslate = courseProvider.translateModuleName('quiz');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish a sync process: remove offline data if needed, prefetch quiz data, set sync time and return the result.
|
||||
*
|
||||
* @param {string} siteId Site ID.
|
||||
* @param {any} quiz Quiz.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string[]} warnings List of warnings generated by the sync.
|
||||
* @param {number} [attemptId] Last attempt ID.
|
||||
* @param {any} [offlineAttempt] Offline attempt synchronized, if any.
|
||||
* @param {any} [onlineAttempt] Online data for the offline attempt.
|
||||
* @param {boolean} [removeAttempt] Whether the offline data should be removed.
|
||||
* @param {boolean} [updated] Whether some data was sent to the site.
|
||||
* @return {Promise<AddonModQuizSyncResult>} Promise resolved on success.
|
||||
*/
|
||||
protected finishSync(siteId: string, quiz: any, courseId: number, warnings: string[], attemptId?: number, offlineAttempt?: any,
|
||||
onlineAttempt?: any, removeAttempt?: boolean, updated?: boolean): Promise<AddonModQuizSyncResult> {
|
||||
|
||||
// Invalidate the data for the quiz and attempt.
|
||||
return this.quizProvider.invalidateAllQuizData(quiz.id, courseId, attemptId, siteId).catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
if (removeAttempt && attemptId) {
|
||||
return this.quizOfflineProvider.removeAttemptAndAnswers(attemptId, siteId);
|
||||
}
|
||||
}).then(() => {
|
||||
if (updated) {
|
||||
// Data has been sent. Update prefetched data.
|
||||
return this.prefetchHandler.prefetchQuizAndLastAttempt(quiz, false, siteId);
|
||||
}
|
||||
}).then(() => {
|
||||
return this.setSyncTime(quiz.id, siteId).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}).then(() => {
|
||||
// Check if online attempt was finished because of the sync.
|
||||
if (onlineAttempt && !this.quizProvider.isAttemptFinished(onlineAttempt.state)) {
|
||||
// Attempt wasn't finished at start. Check if it's finished now.
|
||||
return this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, false, siteId).then((attempts) => {
|
||||
// Search the attempt.
|
||||
for (const i in attempts) {
|
||||
const attempt = attempts[i];
|
||||
|
||||
if (attempt.id == onlineAttempt.id) {
|
||||
return this.quizProvider.isAttemptFinished(attempt.state);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}).then((attemptFinished) => {
|
||||
return {
|
||||
warnings: warnings,
|
||||
attemptFinished: attemptFinished
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a quiz has data to synchronize.
|
||||
*
|
||||
* @param {number} quizId Quiz ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether it has data to sync.
|
||||
*/
|
||||
hasDataToSync(quizId: number, siteId?: string): Promise<boolean> {
|
||||
return this.quizOfflineProvider.getQuizAttempts(quizId, siteId).then((attempts) => {
|
||||
return !!attempts.length;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected if sync fails.
|
||||
*/
|
||||
syncAllQuizzes(siteId?: string): Promise<any> {
|
||||
return this.syncOnSites('all quizzes', this.syncAllQuizzesFunc.bind(this), [], siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync all quizzes on a site.
|
||||
*
|
||||
* @param {string} [siteId] Site ID to sync. If not defined, sync all sites.
|
||||
* @param {Promise<any>} Promise resolved if sync is successful, rejected if sync fails.
|
||||
*/
|
||||
protected syncAllQuizzesFunc(siteId?: string): Promise<any> {
|
||||
// Get all offline attempts.
|
||||
return this.quizOfflineProvider.getAllAttempts(siteId).then((attempts) => {
|
||||
const quizzes = [],
|
||||
ids = [], // To prevent duplicates.
|
||||
promises = [];
|
||||
|
||||
// Get the IDs of all the quizzes that have something to be synced.
|
||||
attempts.forEach((attempt) => {
|
||||
if (ids.indexOf(attempt.quizid) == -1) {
|
||||
ids.push(attempt.quizid);
|
||||
|
||||
quizzes.push({
|
||||
id: attempt.quizid,
|
||||
courseid: attempt.courseid
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sync all quizzes that haven't been synced for a while and that aren't attempted right now.
|
||||
quizzes.forEach((quiz) => {
|
||||
if (!this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) {
|
||||
|
||||
// Quiz not blocked, try to synchronize it.
|
||||
promises.push(this.quizProvider.getQuizById(quiz.courseid, quiz.id, false, siteId).then((quiz) => {
|
||||
return this.syncQuizIfNeeded(quiz, false, siteId).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(() => {
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}).then((data) => {
|
||||
if (typeof data != 'undefined') {
|
||||
// Sync successful. Send event.
|
||||
this.eventsProvider.trigger(AddonModQuizSyncProvider.AUTO_SYNCED, {
|
||||
quizId: quiz.id,
|
||||
attemptFinished: data.attemptFinished,
|
||||
warnings: data.warnings
|
||||
}, siteId);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync a quiz only if a certain time has passed since the last time.
|
||||
*
|
||||
* @param {any} quiz Quiz.
|
||||
* @param {boolean} [askPreflight] Whether we should ask for preflight data if needed.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the quiz is synced or if it doesn't need to be synced.
|
||||
*/
|
||||
syncQuizIfNeeded(quiz: any, askPreflight?: boolean, siteId?: string): Promise<any> {
|
||||
return this.isSyncNeeded(quiz.id, siteId).then((needed) => {
|
||||
if (needed) {
|
||||
return this.syncQuiz(quiz, askPreflight, siteId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to synchronize a quiz.
|
||||
* The promise returned will be resolved with an array with warnings if the synchronization is successful.
|
||||
*
|
||||
* @param {any} quiz Quiz.
|
||||
* @param {boolean} [askPreflight] Whether we should ask for preflight data if needed.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<AddonModQuizSyncResult>} Promise resolved in success.
|
||||
*/
|
||||
syncQuiz(quiz: any, askPreflight?: boolean, siteId?: string): Promise<AddonModQuizSyncResult> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const warnings = [],
|
||||
courseId = quiz.course;
|
||||
let syncPromise,
|
||||
preflightData;
|
||||
|
||||
if (this.isSyncing(quiz.id, siteId)) {
|
||||
// There's already a sync ongoing for this quiz, return the promise.
|
||||
return this.getOngoingSync(quiz.id, siteId);
|
||||
}
|
||||
|
||||
// Verify that quiz isn't blocked.
|
||||
if (this.syncProvider.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) {
|
||||
this.logger.debug('Cannot sync quiz ' + quiz.id + ' because it is blocked.');
|
||||
|
||||
return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate}));
|
||||
}
|
||||
|
||||
this.logger.debug('Try to sync quiz ' + quiz.id + ' in site ' + siteId);
|
||||
|
||||
// Get all the offline attempts for the quiz.
|
||||
syncPromise = this.quizOfflineProvider.getQuizAttempts(quiz.id, siteId).then((attempts) => {
|
||||
// Should return 0 or 1 attempt.
|
||||
if (!attempts.length) {
|
||||
return this.finishSync(siteId, quiz, courseId, warnings);
|
||||
}
|
||||
|
||||
const offlineAttempt = attempts.pop();
|
||||
|
||||
// Now get the list of online attempts to make sure this attempt exists and isn't finished.
|
||||
return this.quizProvider.getUserAttempts(quiz.id, 'all', true, false, true, siteId).then((attempts) => {
|
||||
const lastAttemptId = attempts.length ? attempts[attempts.length - 1].id : undefined;
|
||||
let onlineAttempt;
|
||||
|
||||
// Search the attempt we retrieved from offline.
|
||||
for (const i in attempts) {
|
||||
const attempt = attempts[i];
|
||||
|
||||
if (attempt.id == offlineAttempt.id) {
|
||||
onlineAttempt = attempt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!onlineAttempt || this.quizProvider.isAttemptFinished(onlineAttempt.state)) {
|
||||
// Attempt not found or it's finished in online. Discard it.
|
||||
warnings.push(this.translate.instant('addon.mod_quiz.warningattemptfinished'));
|
||||
|
||||
return this.finishSync(siteId, quiz, courseId, warnings, lastAttemptId, offlineAttempt, onlineAttempt, true);
|
||||
}
|
||||
|
||||
// Get the data stored in offline.
|
||||
return this.quizOfflineProvider.getAttemptAnswers(offlineAttempt.id, siteId).then((answersList) => {
|
||||
|
||||
if (!answersList.length) {
|
||||
// No answers stored, finish.
|
||||
return this.finishSync(siteId, quiz, courseId, warnings, lastAttemptId, offlineAttempt, onlineAttempt,
|
||||
true);
|
||||
}
|
||||
|
||||
const answers = this.questionProvider.convertAnswersArrayToObject(answersList),
|
||||
offlineQuestions = this.quizOfflineProvider.classifyAnswersInQuestions(answers);
|
||||
let finish;
|
||||
|
||||
// We're going to need preflightData, get it.
|
||||
return this.quizProvider.getQuizAccessInformation(quiz.id, false, true, siteId).then((info) => {
|
||||
|
||||
return this.prefetchHandler.getPreflightData(quiz, info, onlineAttempt, askPreflight,
|
||||
'core.settings.synchronization', siteId);
|
||||
}).then((data) => {
|
||||
preflightData = data;
|
||||
|
||||
// Now get the online questions data.
|
||||
const pages = this.quizProvider.getPagesFromLayoutAndQuestions(onlineAttempt.layout, offlineQuestions);
|
||||
|
||||
return this.quizProvider.getAllQuestionsData(quiz, onlineAttempt, preflightData, pages, false, true,
|
||||
siteId);
|
||||
}).then((onlineQuestions) => {
|
||||
|
||||
// Validate questions, discarding the offline answers that can't be synchronized.
|
||||
return this.validateQuestions(onlineAttempt.id, onlineQuestions, offlineQuestions, siteId);
|
||||
}).then((discardedData) => {
|
||||
|
||||
// Get the answers to send.
|
||||
const answers = this.quizOfflineProvider.extractAnswersFromQuestions(offlineQuestions);
|
||||
finish = offlineAttempt.finished && !discardedData;
|
||||
|
||||
if (discardedData) {
|
||||
if (offlineAttempt.finished) {
|
||||
warnings.push(this.translate.instant('addon.mod_quiz.warningdatadiscardedfromfinished'));
|
||||
} else {
|
||||
warnings.push(this.translate.instant('addon.mod_quiz.warningdatadiscarded'));
|
||||
}
|
||||
}
|
||||
|
||||
return this.quizProvider.processAttempt(quiz, onlineAttempt, answers, preflightData, finish, false, false,
|
||||
siteId);
|
||||
}).then(() => {
|
||||
|
||||
// Answers sent, now set the current page if the attempt isn't finished.
|
||||
if (!finish) {
|
||||
return this.quizProvider.logViewAttempt(onlineAttempt.id, offlineAttempt.currentpage, preflightData,
|
||||
false).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}
|
||||
}).then(() => {
|
||||
|
||||
// Data sent. Finish the sync.
|
||||
return this.finishSync(siteId, quiz, courseId, warnings, lastAttemptId, offlineAttempt, onlineAttempt,
|
||||
true, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return this.addOngoingSync(quiz.id, syncPromise, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate questions, discarding the offline answers that can't be synchronized.
|
||||
*
|
||||
* @param {number} attemptId Attempt ID.
|
||||
* @param {any} onlineQuestions Online questions
|
||||
* @param {any} offlineQuestions Offline questions.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: true if some offline data was discarded, false otherwise.
|
||||
*/
|
||||
validateQuestions(attemptId: number, onlineQuestions: any, offlineQuestions: any, siteId?: string): Promise<boolean> {
|
||||
const promises = [];
|
||||
let discardedData = false;
|
||||
|
||||
for (const slot in offlineQuestions) {
|
||||
const offlineQuestion = offlineQuestions[slot],
|
||||
onlineQuestion = onlineQuestions[slot],
|
||||
offlineSequenceCheck = offlineQuestion.answers[':sequencecheck'];
|
||||
|
||||
if (onlineQuestion) {
|
||||
|
||||
// We found the online data for the question, validate that the sequence check is ok.
|
||||
if (!this.questionDelegate.validateSequenceCheck(onlineQuestion, offlineSequenceCheck)) {
|
||||
// Sequence check is not valid, remove the offline data.
|
||||
discardedData = true;
|
||||
promises.push(this.quizOfflineProvider.removeQuestionAndAnswers(attemptId, Number(slot), siteId));
|
||||
delete offlineQuestions[slot];
|
||||
} else {
|
||||
// Sequence check is valid. Use the online one to prevent synchronization errors.
|
||||
offlineQuestion.answers[':sequencecheck'] = onlineQuestion.sequencecheck;
|
||||
}
|
||||
} else {
|
||||
// Online question not found, it can happen for 2 reasons:
|
||||
// 1- It's a sequential quiz and the question is in a page already passed.
|
||||
// 2- Quiz layout has changed (shouldn't happen since it's blocked if there are attempts).
|
||||
discardedData = true;
|
||||
promises.push(this.quizOfflineProvider.removeQuestionAndAnswers(attemptId, Number(slot), siteId));
|
||||
delete offlineQuestions[slot];
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return discardedData;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreCronHandler } from '@providers/cron';
|
||||
import { AddonModQuizSyncProvider } from './quiz-sync';
|
||||
|
||||
/**
|
||||
* Synchronization cron handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonModQuizSyncCronHandler implements CoreCronHandler {
|
||||
name = 'AddonModQuizSyncCronHandler';
|
||||
|
||||
constructor(private quizSync: AddonModQuizSyncProvider) {}
|
||||
|
||||
/**
|
||||
* 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<any>} Promise resolved when done, rejected if failure.
|
||||
*/
|
||||
execute(siteId?: string): Promise<any> {
|
||||
return this.quizSync.syncAllQuizzes(siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time between consecutive executions.
|
||||
*
|
||||
* @return {number} Time between consecutive executions (in ms).
|
||||
*/
|
||||
getInterval(): number {
|
||||
return AddonModQuizSyncProvider.SYNC_TIME;
|
||||
}
|
||||
}
|
|
@ -35,12 +35,14 @@ export class AddonModSurveySyncProvider extends CoreSyncBaseProvider {
|
|||
static AUTO_SYNCED = 'addon_mod_survey_autom_synced';
|
||||
protected componentTranslate: string;
|
||||
|
||||
constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider, private surveyOffline: AddonModSurveyOfflineProvider,
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
||||
courseProvider: CoreCourseProvider, private surveyOffline: AddonModSurveyOfflineProvider,
|
||||
private eventsProvider: CoreEventsProvider, private surveyProvider: AddonModSurveyProvider,
|
||||
private translate: TranslateService, private utils: CoreUtilsProvider, protected textUtils: CoreTextUtilsProvider,
|
||||
courseProvider: CoreCourseProvider, syncProvider: CoreSyncProvider) {
|
||||
super('AddonModSurveySyncProvider', sitesProvider, loggerProvider, appProvider, syncProvider, textUtils);
|
||||
private utils: CoreUtilsProvider) {
|
||||
|
||||
super('AddonModSurveySyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate);
|
||||
|
||||
this.componentTranslate = courseProvider.translateModuleName('survey');
|
||||
}
|
||||
|
||||
|
|
|
@ -34,12 +34,13 @@ export class AddonNotesSyncProvider extends CoreSyncBaseProvider {
|
|||
|
||||
static AUTO_SYNCED = 'addon_notes_autom_synced';
|
||||
|
||||
constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider, private notesOffline: AddonNotesOfflineProvider,
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
||||
private notesOffline: AddonNotesOfflineProvider, private utils: CoreUtilsProvider,
|
||||
private eventsProvider: CoreEventsProvider, private notesProvider: AddonNotesProvider,
|
||||
private coursesProvider: CoreCoursesProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||
syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider) {
|
||||
super('AddonNotesSync', sitesProvider, loggerProvider, appProvider, syncProvider, textUtils);
|
||||
private coursesProvider: CoreCoursesProvider) {
|
||||
|
||||
super('AddonNotesSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreSyncProvider } from '@providers/sync';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
* Base class to create sync providers. It provides some common functions.
|
||||
|
@ -44,10 +46,14 @@ export class CoreSyncBaseProvider {
|
|||
// Store sync promises.
|
||||
protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise<any> } } = {};
|
||||
|
||||
constructor(component: string, protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
// List of services that will be injected using injector.
|
||||
// It's done like this so subclasses don't have to send all the services to the parent in the constructor.
|
||||
|
||||
constructor(component: string, loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,
|
||||
protected appProvider: CoreAppProvider, protected syncProvider: CoreSyncProvider,
|
||||
protected textUtils: CoreTextUtilsProvider) {
|
||||
this.logger = this.loggerProvider.getInstance(component);
|
||||
protected textUtils: CoreTextUtilsProvider, protected translate: TranslateService) {
|
||||
|
||||
this.logger = loggerProvider.getInstance(component);
|
||||
this.component = component;
|
||||
}
|
||||
|
||||
|
@ -93,6 +99,33 @@ export class CoreSyncBaseProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the synchronization time in a human readable format.
|
||||
*
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the readable time.
|
||||
*/
|
||||
getReadableSyncTime(id: string | number, siteId?: string): Promise<string> {
|
||||
return this.getSyncTime(id, siteId).then((time) => {
|
||||
return this.getReadableTimeFromTimestamp(time);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a timestamp return it in a human readable format.
|
||||
*
|
||||
* @param {number} timestamp Timestamp
|
||||
* @return {string} Human readable time.
|
||||
*/
|
||||
getReadableTimeFromTimestamp(timestamp: number): string {
|
||||
if (!timestamp) {
|
||||
return this.translate.instant('core.never');
|
||||
} else {
|
||||
return moment(timestamp).format('LLL');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the synchronization time. Returns 0 if no time stored.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue