MOBILE-2992 user: Get and sync preferences
parent
b6e15defdd
commit
65055653be
|
@ -0,0 +1,114 @@
|
|||
// (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 { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
||||
|
||||
/**
|
||||
* Structure of offline user preferences.
|
||||
*/
|
||||
export interface CoreUserOfflinePreference {
|
||||
name: string;
|
||||
value: string;
|
||||
onlinevalue: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to handle offline user preferences.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreUserOfflineProvider {
|
||||
|
||||
// Variables for database.
|
||||
static PREFERENCES_TABLE = 'user_preferences';
|
||||
protected siteSchema: CoreSiteSchema = {
|
||||
name: 'CoreUserOfflineProvider',
|
||||
version: 1,
|
||||
tables: [
|
||||
{
|
||||
name: CoreUserOfflineProvider.PREFERENCES_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'TEXT',
|
||||
unique: true,
|
||||
notNull: true
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'TEXT'
|
||||
},
|
||||
{
|
||||
name: 'onlinevalue',
|
||||
type: 'TEXT'
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider) {
|
||||
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preferences that were changed offline.
|
||||
*
|
||||
* @return {Promise<CoreUserOfflinePreference[]>} Promise resolved with list of preferences.
|
||||
*/
|
||||
getChangedPreferences(siteId?: string): Promise<CoreUserOfflinePreference[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecordsSelect(CoreUserOfflineProvider.PREFERENCES_TABLE, 'value != onlineValue');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an offline preference.
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @return {Promise<CoreUserOfflinePreference>} Promise resolved with the preference, rejected if not found.
|
||||
*/
|
||||
getPreference(name: string, siteId?: string): Promise<CoreUserOfflinePreference> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const conditions = { name };
|
||||
|
||||
return site.getDb().getRecord(CoreUserOfflineProvider.PREFERENCES_TABLE, conditions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an offline preference.
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @param {string} value Value of the preference.
|
||||
* @param {string} onlineValue Online value of the preference. If unedfined, preserve previously stored value.
|
||||
* @return {Promise<CoreUserPreference>} Promise resolved when done.
|
||||
*/
|
||||
setPreference(name: string, value: string, onlineValue?: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
let promise: Promise<string>;
|
||||
if (typeof onlineValue == 'undefined') {
|
||||
promise = this.getPreference(name, site.id).then((preference) => preference.onlinevalue);
|
||||
} else {
|
||||
promise = Promise.resolve(onlineValue);
|
||||
}
|
||||
|
||||
return promise.then((onlineValue) => {
|
||||
const record = { name, value, onlinevalue: onlineValue };
|
||||
|
||||
return site.getDb().insertRecord(CoreUserOfflineProvider.PREFERENCES_TABLE, record);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// (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 { CoreUserSyncProvider } from './sync';
|
||||
|
||||
/**
|
||||
* Synchronization cron handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreUserSyncCronHandler implements CoreCronHandler {
|
||||
name = 'CoreUserSyncCronHandler';
|
||||
|
||||
constructor(private userSync: CoreUserSyncProvider) {}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {boolean} [force] Wether the execution is forced (manual sync).
|
||||
* @return {Promise<any>} Promise resolved when done, rejected if failure.
|
||||
*/
|
||||
execute(siteId?: string, force?: boolean): Promise<any> {
|
||||
return this.userSync.syncPreferences(siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time between consecutive executions.
|
||||
*
|
||||
* @return {number} Time between consecutive executions (in ms).
|
||||
*/
|
||||
getInterval(): number {
|
||||
return 300000; // 5 minutes.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// (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 { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreUserOfflineProvider } from './offline';
|
||||
import { CoreUserProvider } from './user';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSyncProvider } from '@providers/sync';
|
||||
|
||||
/**
|
||||
* Service to sync user preferences.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreUserSyncProvider extends CoreSyncBaseProvider {
|
||||
|
||||
static AUTO_SYNCED = 'core_user_autom_synced';
|
||||
|
||||
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||
translate: TranslateService, syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider,
|
||||
private userOffline: CoreUserOfflineProvider, private userProvider: CoreUserProvider,
|
||||
private utils: CoreUtilsProvider, timeUtils: CoreTimeUtilsProvider) {
|
||||
super('CoreUserSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to synchronize user preferences 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.
|
||||
*/
|
||||
syncPreferences(siteId?: string): Promise<any> {
|
||||
const syncFunctionLog = 'all user preferences';
|
||||
|
||||
return this.syncOnSites(syncFunctionLog, this.syncPreferencesFunc.bind(this), [], siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync user preferences of 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 syncPreferencesFunc(siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const syncId = 'preferences';
|
||||
|
||||
if (this.isSyncing(syncId, siteId)) {
|
||||
// There's already a sync ongoing, return the promise.
|
||||
return this.getOngoingSync(syncId, siteId);
|
||||
}
|
||||
|
||||
const warnings = [];
|
||||
|
||||
this.logger.debug(`Try to sync user preferences`);
|
||||
|
||||
const syncPromise = this.userOffline.getChangedPreferences(siteId).then((preferences) => {
|
||||
return this.utils.allPromises(preferences.map((preference) => {
|
||||
return this.userProvider.getUserPreferenceOnline(preference.name, siteId).then((onlineValue) => {
|
||||
if (preference.onlinevalue != onlineValue) {
|
||||
// Prefernce was changed on web while the app was offline, do not sync.
|
||||
return this.userOffline.setPreference(preference.name, onlineValue, onlineValue, siteId);
|
||||
}
|
||||
|
||||
return this.userProvider.setUserPreference(name, preference.value, 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(() => {
|
||||
// All done, return the warnings.
|
||||
return warnings;
|
||||
});
|
||||
|
||||
return this.addOngoingSync(syncId, syncPromise, siteId);
|
||||
}
|
||||
}
|
|
@ -15,9 +15,11 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSite } from '@classes/site';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreUserOfflineProvider } from './offline';
|
||||
|
||||
/**
|
||||
* Service to provide user functionalities.
|
||||
|
@ -59,7 +61,8 @@ export class CoreUserProvider {
|
|||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider) {
|
||||
private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider,
|
||||
private userOffline: CoreUserOfflineProvider) {
|
||||
this.logger = logger.getInstance('CoreUserProvider');
|
||||
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||
}
|
||||
|
@ -272,6 +275,67 @@ export class CoreUserProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user preference (online or offline).
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||
* @return {string} Preference value or null if preference not set.
|
||||
*/
|
||||
getUserPreference(name: string, siteId?: string): Promise<string> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
return this.userOffline.getPreference(name, siteId).catch(() => {
|
||||
return null;
|
||||
}).then((preference) => {
|
||||
if (preference && !this.appProvider.isOnline()) {
|
||||
// Offline, return stored value.
|
||||
return preference.value;
|
||||
}
|
||||
|
||||
return this.getUserPreferenceOnline(name, siteId).then((wsValue) => {
|
||||
if (preference && preference.value != preference.onlinevalue && preference.onlinevalue == wsValue) {
|
||||
// Sync is pending for this preference, return stored value.
|
||||
return preference.value;
|
||||
}
|
||||
|
||||
return this.userOffline.setPreference(name, wsValue, wsValue).then(() => {
|
||||
return wsValue;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for a user preference WS call.
|
||||
*
|
||||
* @param {string} name Preference name.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
protected getUserPreferenceCacheKey(name: string): string {
|
||||
return this.ROOT_CACHE_KEY + 'preference:' + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user preference online.
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||
* @return {string} Preference value or null if preference not set.
|
||||
*/
|
||||
getUserPreferenceOnline(name: string, siteId?: string): Promise<string> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const data = { name };
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getUserPreferenceCacheKey(data.name)
|
||||
};
|
||||
|
||||
return site.read('core_user_get_user_preferences', data, preSets).then((result) => {
|
||||
return result.preferences[0] ? result.preferences[0].value : null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates user WS calls.
|
||||
*
|
||||
|
@ -298,6 +362,19 @@ export class CoreUserProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate user preference.
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||
*/
|
||||
invalidateUserPreference(name: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getUserPreferenceCacheKey(name));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if course participants is disabled in a certain site.
|
||||
*
|
||||
|
@ -455,6 +532,45 @@ export class CoreUserProvider {
|
|||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user preference (online or offline).
|
||||
*
|
||||
* @param {string} name Name of the preference.
|
||||
* @param {string} value Value of the preference.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved on success.
|
||||
*/
|
||||
setUserPreference(name: string, value: string, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
let isOnline = this.appProvider.isOnline();
|
||||
let promise: Promise<any>;
|
||||
|
||||
if (isOnline) {
|
||||
const preferences = [{type: name, value}];
|
||||
promise = this.updateUserPreferences(preferences, undefined, undefined, siteId).catch((error) => {
|
||||
// Preference not saved online.
|
||||
isOnline = false;
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise.finally(() => {
|
||||
// Update stored online value if saved online.
|
||||
const onlineValue = isOnline ? value : undefined;
|
||||
|
||||
return Promise.all([
|
||||
this.userOffline.setPreference(name, value, onlineValue),
|
||||
this.invalidateUserPreference(name).catch(() => {
|
||||
// Ignore error.
|
||||
})
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a preference for a user.
|
||||
*
|
||||
|
@ -478,13 +594,14 @@ export class CoreUserProvider {
|
|||
/**
|
||||
* Update some preferences for a user.
|
||||
*
|
||||
* @param {any} preferences List of preferences.
|
||||
* @param {{name: string, value: string}[]} preferences List of preferences.
|
||||
* @param {boolean} [disableNotifications] Whether to disable all notifications. Undefined to not update this value.
|
||||
* @param {number} [userId] User ID. If not defined, site's current user.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if success.
|
||||
*/
|
||||
updateUserPreferences(preferences: any, disableNotifications: boolean, userId?: number, siteId?: string): Promise<any> {
|
||||
updateUserPreferences(preferences: {type: string, value: string}[], disableNotifications?: boolean, userId?: number,
|
||||
siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
userId = userId || site.getUserId();
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ import { CoreUserParticipantsCourseOptionHandler } from './providers/course-opti
|
|||
import { CoreUserParticipantsLinkHandler } from './providers/participants-link-handler';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreUserComponentsModule } from './components/components.module';
|
||||
import { CoreCronDelegate } from '@providers/cron';
|
||||
import { CoreUserOfflineProvider } from './providers/offline';
|
||||
import { CoreUserSyncProvider } from './providers/sync';
|
||||
import { CoreUserSyncCronHandler } from './providers/sync-cron-handler';
|
||||
|
||||
// List of providers (without handlers).
|
||||
export const CORE_USER_PROVIDERS: any[] = [
|
||||
|
@ -33,6 +37,8 @@ export const CORE_USER_PROVIDERS: any[] = [
|
|||
CoreUserProfileFieldDelegate,
|
||||
CoreUserProvider,
|
||||
CoreUserHelperProvider,
|
||||
CoreUserOfflineProvider,
|
||||
CoreUserSyncProvider
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -46,10 +52,13 @@ export const CORE_USER_PROVIDERS: any[] = [
|
|||
CoreUserProfileFieldDelegate,
|
||||
CoreUserProvider,
|
||||
CoreUserHelperProvider,
|
||||
CoreUserOfflineProvider,
|
||||
CoreUserSyncProvider,
|
||||
CoreUserProfileMailHandler,
|
||||
CoreUserProfileLinkHandler,
|
||||
CoreUserParticipantsCourseOptionHandler,
|
||||
CoreUserParticipantsLinkHandler
|
||||
CoreUserParticipantsLinkHandler,
|
||||
CoreUserSyncCronHandler,
|
||||
]
|
||||
})
|
||||
export class CoreUserModule {
|
||||
|
@ -57,12 +66,14 @@ export class CoreUserModule {
|
|||
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, userProvider: CoreUserProvider,
|
||||
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreUserProfileLinkHandler,
|
||||
courseOptionHandler: CoreUserParticipantsCourseOptionHandler, linkHandler: CoreUserParticipantsLinkHandler,
|
||||
courseOptionsDelegate: CoreCourseOptionsDelegate) {
|
||||
courseOptionsDelegate: CoreCourseOptionsDelegate, cronDelegate: CoreCronDelegate,
|
||||
syncHandler: CoreUserSyncCronHandler) {
|
||||
|
||||
userDelegate.registerHandler(userProfileMailHandler);
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
contentLinksDelegate.registerHandler(userLinkHandler);
|
||||
contentLinksDelegate.registerHandler(linkHandler);
|
||||
cronDelegate.register(syncHandler);
|
||||
|
||||
eventsProvider.on(CoreEventsProvider.USER_DELETED, (data) => {
|
||||
// Search for userid in params.
|
||||
|
|
Loading…
Reference in New Issue