2019-04-01 10:57:53 +02:00

201 lines
9.3 KiB
TypeScript

// (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 { CoreSyncBaseProvider } from '@classes/base-sync';
import { CoreAppProvider } from '@providers/app';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
import { CoreSyncProvider } from '@providers/sync';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreRatingProvider } from './rating';
import { CoreRatingOfflineProvider, CoreRatingItemSet } from './offline';
import { CoreEventsProvider } from '@providers/events';
/**
* Service to sync ratings.
*/
@Injectable()
export class CoreRatingSyncProvider extends CoreSyncBaseProvider {
static SYNCED_EVENT = 'core_rating_synced';
constructor(translate: TranslateService,
appProvider: CoreAppProvider,
private eventsProvider: CoreEventsProvider,
loggerProvider: CoreLoggerProvider,
sitesProvider: CoreSitesProvider,
syncProvider: CoreSyncProvider,
textUtils: CoreTextUtilsProvider,
timeUtils: CoreTimeUtilsProvider,
private utils: CoreUtilsProvider,
private ratingProvider: CoreRatingProvider,
private ratingOffline: CoreRatingOfflineProvider) {
super('CoreRatingSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils);
}
/**
* Try to synchronize all the ratings of a certain component, instance or item set.
*
* This function should be called from the sync provider of activities with ratings.
*
* @param {string} component Component. Example: "mod_forum".
* @param {string} ratingArea Rating Area. Example: "post".
* @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<any>} Promise resolved if sync is successful, rejected if sync fails.
*/
syncRatings(component: string, ratingArea: string, contextLevel?: string, instanceId?: number, itemSetId?: number,
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) => {
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, {
...itemSet,
warnings: result.warnings
}, siteId);
}
results.push({itemSet, ...result});
});
});
return Promise.all(promises).then(() => {
return results;
});
});
}
/**
* Sync ratings of an item set only if a certain time has passed since the last time.
*
* @param {string} component Component. Example: "mod_forum".
* @param {string} ratingArea Rating Area. Example: "post".
* @param {string} contextLevel Context level: course, module, user, etc.
* @param {number} instanceId Context instance id.
* @param {number} itemSetId Item set id. Example: forum discussion id.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when ratings are synced or if it doesn't need to be synced.
*/
protected syncItemSetIfNeeded(component: string, ratingArea: string, contextLevel: string, instanceId: number,
itemSetId: number, siteId?: string): Promise<{updated: number[], warnings: string[]}> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const syncId = this.getItemSetSyncId(component, ratingArea, contextLevel, instanceId, itemSetId);
return this.isSyncNeeded(syncId, siteId).then((needed) => {
if (needed) {
return this.syncItemSet(component, ratingArea, contextLevel, instanceId, itemSetId, siteId);
}
});
}
/**
* Synchronize all offline ratings of an item set.
*
* @param {string} component Component. Example: "mod_forum".
* @param {string} ratingArea Rating Area. Example: "post".
* @param {string} contextLevel Context level: course, module, user, etc.
* @param {number} instanceId Context instance id.
* @param {number} itemSetId Item set id. Example: forum discussion id.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
*/
protected syncItemSet(component: string, ratingArea: string, contextLevel: string, instanceId: number, itemSetId: number,
siteId?: string): Promise<{updated: number[], warnings: string[]}> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const syncId = this.getItemSetSyncId(component, ratingArea, contextLevel, instanceId, itemSetId);
if (this.isSyncing(syncId, siteId)) {
// There's already a sync ongoing for this item set, return the promise.
return this.getOngoingSync(syncId, siteId);
}
this.logger.debug(`Try to sync ratings of component '${component}' rating area '${ratingArea}'` +
` context level '${contextLevel}' instance ${instanceId} item set ${itemSetId}`);
const updated = [];
const warnings = [];
return this.ratingOffline.getRatings(component, ratingArea, contextLevel, instanceId, itemSetId, siteId).then((ratings) => {
if (!ratings.length) {
// Nothing to sync.
return;
} else if (!this.appProvider.isOnline()) {
// Cannot sync in offline.
return Promise.reject(null);
}
const promises = ratings.map((rating) => {
return this.ratingProvider.addRatingOnline(component, ratingArea, rating.contextlevel, rating.instanceid,
rating.itemid, rating.scaleid, rating.rating, rating.rateduserid, rating.aggregation, siteId)
.catch((error) => {
if (this.utils.isWebServiceError(error)) {
warnings.push(this.textUtils.getErrorMessageFromError(error));
} else {
// Couldn't connect to server, reject.
return Promise.reject(error);
}
}).then(() => {
updated.push(rating.itemid);
return this.ratingOffline.deleteRating(component, ratingArea, rating.contextlevel, rating.instanceid,
rating.itemid, siteId).finally(() => {
return this.ratingProvider.invalidateRatingItems(rating.contextlevel, rating.instanceid, component,
ratingArea, rating.itemid, rating.scaleid, undefined, siteId);
});
});
});
return Promise.all(promises).then(() => {
// All done, return the warnings.
return { updated, warnings };
});
});
}
/**
* Get the sync id of an item set.
*
* @param {string} component Component. Example: "mod_forum".
* @param {string} ratingArea Rating Area. Example: "post".
* @param {string} contextLevel Context level: course, module, user, etc.
* @param {number} instanceId Context instance id.
* @param {number} itemSetId Item set id. Example: forum discussion id.
* @return {string} Sync id.
*/
protected getItemSetSyncId(component: string, ratingArea: string, contextLevel: string, instanceId: number, itemSetId: number):
string {
return `itemSet#${component}#${ratingArea}#${contextLevel}#${instanceId}#${itemSetId}`;
}
}