forked from CIT/Vmeda.Online
		
	MOBILE-2853 core: Let app DB tables define a scheme to handle updates
This commit is contained in:
		
							parent
							
								
									84f559264b
								
							
						
					
					
						commit
						80c2aef0d0
					
				| @ -170,7 +170,7 @@ interface RequestQueueItem { | ||||
| /** | ||||
|  * Class that represents a site (combination of site + user). | ||||
|  * It will have all the site data and provide utility functions regarding a site. | ||||
|  * To add tables to the site's database, please use CoreSitesProvider.createTablesFromSchema. This will make sure that | ||||
|  * To add tables to the site's database, please use CoreSitesProvider.registerSiteSchema. This will make sure that | ||||
|  * the tables are created in all the sites, not just the current one. | ||||
|  */ | ||||
| export class CoreSite { | ||||
|  | ||||
| @ -14,10 +14,10 @@ | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { LocalNotifications, ILocalNotification, ILocalNotificationAction } from '@ionic-native/local-notifications'; | ||||
| import { CoreAppProvider } from '@providers/app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from '@providers/app'; | ||||
| import { CoreTextUtilsProvider } from '@providers/utils/text'; | ||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { CoreConfigConstants } from '../../../configconstants'; | ||||
| import * as moment from 'moment'; | ||||
| @ -43,41 +43,48 @@ export class LocalNotificationsMock extends LocalNotifications { | ||||
| 
 | ||||
|     // Variables for database.
 | ||||
|     protected DESKTOP_NOTIFS_TABLE = 'desktop_local_notifications'; | ||||
|     protected tableSchema: SQLiteDBTableSchema = { | ||||
|         name: this.DESKTOP_NOTIFS_TABLE, | ||||
|         columns: [ | ||||
|     protected tableSchema: CoreAppSchema = { | ||||
|         name: 'LocalNotificationsMock', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: 'id', | ||||
|                 type: 'INTEGER', | ||||
|                 primaryKey: true | ||||
|                 name: this.DESKTOP_NOTIFS_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'INTEGER', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'title', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'text', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'at', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'data', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'triggered', | ||||
|                         type: 'INTEGER' | ||||
|                     } | ||||
|                 ], | ||||
|             }, | ||||
|             { | ||||
|                 name: 'title', | ||||
|                 type: 'TEXT' | ||||
|             }, | ||||
|             { | ||||
|                 name: 'text', | ||||
|                 type: 'TEXT' | ||||
|             }, | ||||
|             { | ||||
|                 name: 'at', | ||||
|                 type: 'INTEGER' | ||||
|             }, | ||||
|             { | ||||
|                 name: 'data', | ||||
|                 type: 'TEXT' | ||||
|             }, | ||||
|             { | ||||
|                 name: 'triggered', | ||||
|                 type: 'INTEGER' | ||||
|             } | ||||
|         ] | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected scheduled: { [i: number]: any } = {}; | ||||
|     protected triggered: { [i: number]: any } = {}; | ||||
|     protected observers: {[event: string]: Subject<any>}; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected defaults = { | ||||
|         actions       : [], | ||||
|         attachments   : [], | ||||
| @ -117,7 +124,9 @@ export class LocalNotificationsMock extends LocalNotifications { | ||||
|         super(); | ||||
| 
 | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTableFromSchema(this.tableSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.tableSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
| 
 | ||||
|         // Initialize observers.
 | ||||
|         this.observers = { | ||||
| @ -550,20 +559,21 @@ export class LocalNotificationsMock extends LocalNotifications { | ||||
|      * | ||||
|      * @return Promise resolved with the notifications. | ||||
|      */ | ||||
|     protected getAllNotifications(): Promise<any> { | ||||
|         return this.appDB.getAllRecords(this.DESKTOP_NOTIFS_TABLE).then((notifications) => { | ||||
|             notifications.forEach((notification) => { | ||||
|                 notification.trigger = { | ||||
|                     at: new Date(notification.at) | ||||
|                 }; | ||||
|                 notification.data = this.textUtils.parseJSON(notification.data); | ||||
|                 notification.triggered = !!notification.triggered; | ||||
|     protected async getAllNotifications(): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|                 this.mergeWithDefaults(notification); | ||||
|             }); | ||||
|         const notifications = await this.appDB.getAllRecords(this.DESKTOP_NOTIFS_TABLE); | ||||
|         notifications.forEach((notification) => { | ||||
|             notification.trigger = { | ||||
|                 at: new Date(notification.at) | ||||
|             }; | ||||
|             notification.data = this.textUtils.parseJSON(notification.data); | ||||
|             notification.triggered = !!notification.triggered; | ||||
| 
 | ||||
|             return notifications; | ||||
|             this.mergeWithDefaults(notification); | ||||
|         }); | ||||
| 
 | ||||
|         return notifications; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -889,7 +899,9 @@ export class LocalNotificationsMock extends LocalNotifications { | ||||
|      * @param id ID of the notification. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected removeNotification(id: number): Promise<any> { | ||||
|     protected async removeNotification(id: number): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.deleteRecords(this.DESKTOP_NOTIFS_TABLE, { id: id }); | ||||
|     } | ||||
| 
 | ||||
| @ -979,7 +991,9 @@ export class LocalNotificationsMock extends LocalNotifications { | ||||
|      * @param triggered Whether the notification has been triggered. | ||||
|      * @return Promise resolved when stored. | ||||
|      */ | ||||
|     protected storeNotification(notification: ILocalNotification, triggered: boolean): Promise<any> { | ||||
|     protected async storeNotification(notification: ILocalNotification, triggered: boolean): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         // Only store some of the properties.
 | ||||
|         const entry = { | ||||
|             id : notification.id, | ||||
|  | ||||
| @ -18,7 +18,7 @@ import { Badge } from '@ionic-native/badge'; | ||||
| import { Push, PushObject, PushOptions } from '@ionic-native/push'; | ||||
| import { Device } from '@ionic-native/device'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreAppProvider } from '@providers/app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from '@providers/app'; | ||||
| import { CoreInitDelegate } from '@providers/init'; | ||||
| import { CoreLoggerProvider } from '@providers/logger'; | ||||
| import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; | ||||
| @ -31,7 +31,7 @@ import { CoreConfigProvider } from '@providers/config'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { CoreConfigConstants } from '../../../configconstants'; | ||||
| import { ILocalNotification } from '@ionic-native/local-notifications'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreSite } from '@classes/site'; | ||||
| import { CoreFilterProvider } from '@core/filter/providers/filter'; | ||||
| import { CoreFilterDelegate } from '@core/filter/providers/delegate'; | ||||
| @ -84,54 +84,59 @@ export class CorePushNotificationsProvider { | ||||
|     protected logger; | ||||
|     protected pushID: string; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     static COMPONENT = 'CorePushNotificationsProvider'; | ||||
| 
 | ||||
|     // Variables for database. The name still contains the name "addon" for backwards compatibility.
 | ||||
|     static BADGE_TABLE = 'addon_pushnotifications_badge'; | ||||
|     static PENDING_UNREGISTER_TABLE = 'addon_pushnotifications_pending_unregister'; | ||||
|     static REGISTERED_DEVICES_TABLE = 'addon_pushnotifications_registered_devices'; | ||||
|     protected appTablesSchema: SQLiteDBTableSchema[] = [ | ||||
|         { | ||||
|             name: CorePushNotificationsProvider.BADGE_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'siteid', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'addon', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'number', | ||||
|                     type: 'INTEGER' | ||||
|                 } | ||||
|             ], | ||||
|             primaryKeys: ['siteid', 'addon'] | ||||
|         }, | ||||
|         { | ||||
|             name: CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'siteid', | ||||
|                     type: 'TEXT', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'siteurl', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'token', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'info', | ||||
|                     type: 'TEXT' | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ]; | ||||
|     protected appTablesSchema: CoreAppSchema = { | ||||
|         name: 'CorePushNotificationsProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: CorePushNotificationsProvider.BADGE_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'siteid', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'addon', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'number', | ||||
|                         type: 'INTEGER' | ||||
|                     } | ||||
|                 ], | ||||
|                 primaryKeys: ['siteid', 'addon'] | ||||
|             }, | ||||
|             { | ||||
|                 name: CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'siteid', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'siteurl', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'token', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'info', | ||||
|                         type: 'TEXT' | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         ] | ||||
|     }; | ||||
|     protected siteSchema: CoreSiteSchema = { | ||||
|         name: 'AddonPushNotificationsProvider', // The name still contains "Addon" for backwards compatibility.
 | ||||
|         version: 1, | ||||
| @ -182,7 +187,9 @@ export class CorePushNotificationsProvider { | ||||
|             private filterProvider: CoreFilterProvider, private filterDelegate: CoreFilterDelegate) { | ||||
|         this.logger = logger.getInstance('CorePushNotificationsProvider'); | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTablesFromSchema(this.appTablesSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.appTablesSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
|         this.sitesProvider.registerSiteSchema(this.siteSchema); | ||||
| 
 | ||||
|         platform.ready().then(() => { | ||||
| @ -211,10 +218,14 @@ export class CorePushNotificationsProvider { | ||||
|      * @param siteId Site ID. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     cleanSiteCounters(siteId: string): Promise<any> { | ||||
|         return this.appDB.deleteRecords(CorePushNotificationsProvider.BADGE_TABLE, {siteid: siteId} ).finally(() => { | ||||
|     async cleanSiteCounters(siteId: string): Promise<void> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             await this.appDB.deleteRecords(CorePushNotificationsProvider.BADGE_TABLE, {siteid: siteId} ); | ||||
|         } finally { | ||||
|             this.updateAppCounter(); | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -532,11 +543,13 @@ export class CorePushNotificationsProvider { | ||||
|      * @param site Site to unregister from. | ||||
|      * @return Promise resolved when device is unregistered. | ||||
|      */ | ||||
|     unregisterDeviceOnMoodle(site: CoreSite): Promise<any> { | ||||
|     async unregisterDeviceOnMoodle(site: CoreSite): Promise<any> { | ||||
|         if (!site || !this.appProvider.isMobile()) { | ||||
|             return Promise.reject(null); | ||||
|         } | ||||
| 
 | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         this.logger.debug(`Unregister device on Moodle: '${site.id}'`); | ||||
| 
 | ||||
|         const data = { | ||||
| @ -544,9 +557,11 @@ export class CorePushNotificationsProvider { | ||||
|             uuid:  this.device.uuid | ||||
|         }; | ||||
| 
 | ||||
|         return site.write('core_user_remove_user_device', data).then((response) => { | ||||
|         try { | ||||
|             const response = await site.write('core_user_remove_user_device', data); | ||||
| 
 | ||||
|             if (!response || !response.removed) { | ||||
|                 return Promise.reject(null); | ||||
|                 throw null; | ||||
|             } | ||||
| 
 | ||||
|             const promises = []; | ||||
| @ -561,22 +576,19 @@ export class CorePushNotificationsProvider { | ||||
|             return Promise.all(promises).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }); | ||||
|         }).catch((error) => { | ||||
|             if (this.utils.isWebServiceError(error)) { | ||||
|                 // It's a WebService error, can't unregister.
 | ||||
|                 return Promise.reject(error); | ||||
|         } catch (error) { | ||||
|             if (!this.utils.isWebServiceError(error)) { | ||||
|                 // Store the pending unregister so it's retried again later.
 | ||||
|                 await this.appDB.insertRecord(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, { | ||||
|                     siteid: site.id, | ||||
|                     siteurl: site.getURL(), | ||||
|                     token: site.getToken(), | ||||
|                     info: JSON.stringify(site.getInfo()) | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             // Store the pending unregister so it's retried again later.
 | ||||
|             return this.appDB.insertRecord(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, { | ||||
|                 siteid: site.id, | ||||
|                 siteurl: site.getURL(), | ||||
|                 token: site.getToken(), | ||||
|                 info: JSON.stringify(site.getInfo()) | ||||
|             }).then(() => { | ||||
|                 return Promise.reject(error); | ||||
|             }); | ||||
|         }); | ||||
|             throw error; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -724,52 +736,53 @@ export class CorePushNotificationsProvider { | ||||
|      * @param forceUnregister Whether to force unregister and register. | ||||
|      * @return Promise resolved when device is registered. | ||||
|      */ | ||||
|     registerDeviceOnMoodle(siteId?: string, forceUnregister?: boolean): Promise<any> { | ||||
|     async registerDeviceOnMoodle(siteId?: string, forceUnregister?: boolean): Promise<void> { | ||||
|         this.logger.debug('Register device on Moodle.'); | ||||
| 
 | ||||
|         if (!this.canRegisterOnMoodle()) { | ||||
|             return Promise.reject(null); | ||||
|         } | ||||
| 
 | ||||
|         const data = this.getRegisterData(); | ||||
|         let result, | ||||
|             site: CoreSite; | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.sitesProvider.getSite(siteId).then((s) => { | ||||
|             site = s; | ||||
|         const data = this.getRegisterData(); | ||||
|         let result; | ||||
| 
 | ||||
|         const site = await this.sitesProvider.getSite(siteId); | ||||
| 
 | ||||
|         try { | ||||
| 
 | ||||
|             if (forceUnregister) { | ||||
|                 return {unregister: true, register: true}; | ||||
|                 result = {unregister: true, register: true}; | ||||
|             } else { | ||||
|                 // Check if the device is already registered.
 | ||||
|                 return this.shouldRegister(data, site); | ||||
|                 result = await this.shouldRegister(data, site); | ||||
|             } | ||||
|         }).then((res) => { | ||||
|             result = res; | ||||
| 
 | ||||
|             if (result.unregister) { | ||||
|                 // Unregister the device first.
 | ||||
|                 return this.unregisterDeviceOnMoodle(site).catch(() => { | ||||
|                 await this.unregisterDeviceOnMoodle(site).catch(() => { | ||||
|                     // Ignore errors.
 | ||||
|                 }); | ||||
|             } | ||||
|         }).then(() => { | ||||
| 
 | ||||
|             if (result.register) { | ||||
|                 // Now register the device.
 | ||||
|                 return site.write('core_user_add_user_device', this.utils.clone(data)).then((response) => { | ||||
|                     // Insert the device in the local DB.
 | ||||
|                     return site.getDb().insertRecord(CorePushNotificationsProvider.REGISTERED_DEVICES_TABLE, data) | ||||
|                             .catch((error) => { | ||||
|                         // Ignore errors.
 | ||||
|                     }); | ||||
|                 }); | ||||
|                 await site.write('core_user_add_user_device', this.utils.clone(data)); | ||||
| 
 | ||||
|                 // Insert the device in the local DB.
 | ||||
|                 try { | ||||
|                     await site.getDb().insertRecord(CorePushNotificationsProvider.REGISTERED_DEVICES_TABLE, data); | ||||
|                 } catch (err) { | ||||
|                     // Ignore errors.
 | ||||
|                 } | ||||
|             } | ||||
|         }).finally(() => { | ||||
|         } finally { | ||||
|             // Remove pending unregisters for this site.
 | ||||
|             this.appDB.deleteRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id}).catch(() => { | ||||
|             await this.appDB.deleteRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: site.id}).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }); | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -779,12 +792,16 @@ export class CorePushNotificationsProvider { | ||||
|      * @param addon Registered addon name. If not defined it will store the site total. | ||||
|      * @return Promise resolved with the stored badge counter for the addon or site or 0 if none. | ||||
|      */ | ||||
|     protected getAddonBadge(siteId?: string, addon: string = 'site'): Promise<any> { | ||||
|         return this.appDB.getRecord(CorePushNotificationsProvider.BADGE_TABLE, {siteid: siteId, addon: addon}).then((entry) => { | ||||
|              return (entry && entry.number) || 0; | ||||
|         }).catch(() => { | ||||
|     protected async getAddonBadge(siteId?: string, addon: string = 'site'): Promise<number> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             const entry = await this.appDB.getRecord(CorePushNotificationsProvider.BADGE_TABLE, {siteid: siteId, addon: addon}); | ||||
| 
 | ||||
|             return (entry && entry.number) || 0; | ||||
|         } catch (err) { | ||||
|             return 0; | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -793,30 +810,26 @@ export class CorePushNotificationsProvider { | ||||
|      * @param siteId If defined, retry only for that site if needed. Otherwise, retry all pending unregisters. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     retryUnregisters(siteId?: string): Promise<any> { | ||||
|         let promise; | ||||
|     async retryUnregisters(siteId?: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         let results; | ||||
| 
 | ||||
|         if (siteId) { | ||||
|             // Check if the site has a pending unregister.
 | ||||
|             promise = this.appDB.getRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: siteId}); | ||||
|             results = await this.appDB.getRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE, {siteid: siteId}); | ||||
|         } else { | ||||
|             // Get all pending unregisters.
 | ||||
|             promise = this.appDB.getAllRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE); | ||||
|             results = await this.appDB.getAllRecords(CorePushNotificationsProvider.PENDING_UNREGISTER_TABLE); | ||||
|         } | ||||
| 
 | ||||
|         return promise.then((results) => { | ||||
|             const promises = []; | ||||
|         return Promise.all(results.map((result) => { | ||||
|             // Create a temporary site to unregister.
 | ||||
|             const tmpSite = this.sitesFactory.makeSite(result.siteid, result.siteurl, result.token, | ||||
|                     this.textUtils.parseJSON(result.info, {})); | ||||
| 
 | ||||
|             results.forEach((result) => { | ||||
|                 // Create a temporary site to unregister.
 | ||||
|                 const tmpSite = this.sitesFactory.makeSite(result.siteid, result.siteurl, result.token, | ||||
|                         this.textUtils.parseJSON(result.info, {})); | ||||
| 
 | ||||
|                 promises.push(this.unregisterDeviceOnMoodle(tmpSite)); | ||||
|             }); | ||||
| 
 | ||||
|             return Promise.all(promises); | ||||
|         }); | ||||
|             return this.unregisterDeviceOnMoodle(tmpSite); | ||||
|         })); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -827,7 +840,9 @@ export class CorePushNotificationsProvider { | ||||
|      * @param addon Registered addon name. If not defined it will store the site total. | ||||
|      * @return Promise resolved with the stored badge counter for the addon or site. | ||||
|      */ | ||||
|     protected saveAddonBadge(value: number, siteId?: string, addon: string = 'site'): Promise<any> { | ||||
|     protected async saveAddonBadge(value: number, siteId?: string, addon: string = 'site'): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         siteId = siteId || this.sitesProvider.getCurrentSiteId(); | ||||
| 
 | ||||
|         const entry = { | ||||
| @ -836,9 +851,9 @@ export class CorePushNotificationsProvider { | ||||
|             number: value | ||||
|         }; | ||||
| 
 | ||||
|         return this.appDB.insertRecord(CorePushNotificationsProvider.BADGE_TABLE, entry).then(() => { | ||||
|             return value; | ||||
|         }); | ||||
|         await this.appDB.insertRecord(CorePushNotificationsProvider.BADGE_TABLE, entry); | ||||
| 
 | ||||
|         return value; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreAppProvider } from '@providers/app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from '@providers/app'; | ||||
| import { CoreEventsProvider } from '@providers/events'; | ||||
| import { CoreFileProvider } from '@providers/file'; | ||||
| import { CoreLoggerProvider } from '@providers/logger'; | ||||
| @ -21,7 +21,7 @@ import { CoreSitesProvider } from '@providers/sites'; | ||||
| import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; | ||||
| import { CoreTextUtilsProvider } from '@providers/utils/text'; | ||||
| import { Md5 } from 'ts-md5/dist/md5'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| 
 | ||||
| /** | ||||
|  * Service to share files with the app. | ||||
| @ -32,19 +32,26 @@ export class CoreSharedFilesProvider { | ||||
| 
 | ||||
|     // Variables for the database.
 | ||||
|     protected SHARED_FILES_TABLE = 'shared_files'; | ||||
|     protected tableSchema: SQLiteDBTableSchema = { | ||||
|         name: this.SHARED_FILES_TABLE, | ||||
|         columns: [ | ||||
|     protected tableSchema: CoreAppSchema = { | ||||
|         name: 'CoreSharedFilesProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: 'id', | ||||
|                 type: 'TEXT', | ||||
|                 primaryKey: true | ||||
|             } | ||||
|         ] | ||||
|                 name: this.SHARED_FILES_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                 ], | ||||
|             }, | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     protected logger; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
| 
 | ||||
|     constructor(logger: CoreLoggerProvider, private fileProvider: CoreFileProvider, appProvider: CoreAppProvider, | ||||
|         private textUtils: CoreTextUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, | ||||
| @ -52,7 +59,9 @@ export class CoreSharedFilesProvider { | ||||
|         this.logger = logger.getInstance('CoreSharedFilesProvider'); | ||||
| 
 | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTableFromSchema(this.tableSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.tableSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -189,7 +198,9 @@ export class CoreSharedFilesProvider { | ||||
|      * @param fileId File ID. | ||||
|      * @return Resolved if treated, rejected otherwise. | ||||
|      */ | ||||
|     protected isFileTreated(fileId: string): Promise<any> { | ||||
|     protected async isFileTreated(fileId: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.getRecord(this.SHARED_FILES_TABLE, { id: fileId }); | ||||
|     } | ||||
| 
 | ||||
| @ -199,12 +210,16 @@ export class CoreSharedFilesProvider { | ||||
|      * @param fileId File ID. | ||||
|      * @return Promise resolved when marked. | ||||
|      */ | ||||
|     protected markAsTreated(fileId: string): Promise<any> { | ||||
|         // Check if it's already marked.
 | ||||
|         return this.isFileTreated(fileId).catch(() => { | ||||
|     protected async markAsTreated(fileId: string): Promise<void> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             // Check if it's already marked.
 | ||||
|             await this.isFileTreated(fileId); | ||||
|         } catch (err) { | ||||
|             // Doesn't exist, insert it.
 | ||||
|             return this.appDB.insertRecord(this.SHARED_FILES_TABLE, { id: fileId }); | ||||
|         }); | ||||
|             await this.appDB.insertRecord(this.SHARED_FILES_TABLE, { id: fileId }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -243,7 +258,9 @@ export class CoreSharedFilesProvider { | ||||
|      * @param fileId File ID. | ||||
|      * @return Resolved when unmarked. | ||||
|      */ | ||||
|     protected unmarkAsTreated(fileId: string): Promise<any> { | ||||
|     protected async unmarkAsTreated(fileId: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.deleteRecords(this.SHARED_FILES_TABLE, { id: fileId }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -21,7 +21,7 @@ import { StatusBar } from '@ionic-native/status-bar'; | ||||
| import { CoreDbProvider } from './db'; | ||||
| import { CoreLoggerProvider } from './logger'; | ||||
| import { CoreEventsProvider } from './events'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { CoreConfigConstants } from '../configconstants'; | ||||
| 
 | ||||
| /** | ||||
| @ -49,6 +49,37 @@ export interface CoreRedirectData { | ||||
|     timemodified?: number; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * App DB schema and migration function. | ||||
|  */ | ||||
| export interface CoreAppSchema { | ||||
|     /** | ||||
|      * Name of the schema. | ||||
|      */ | ||||
|     name: string; | ||||
| 
 | ||||
|     /** | ||||
|      * Latest version of the schema (integer greater than 0). | ||||
|      */ | ||||
|     version: number; | ||||
| 
 | ||||
|     /** | ||||
|      * Tables to create when installing or upgrading the schema. | ||||
|      */ | ||||
|     tables?: SQLiteDBTableSchema[]; | ||||
| 
 | ||||
|     /** | ||||
|      * Migrates the schema to the latest version. | ||||
|      * | ||||
|      * Called when installing and upgrading the schema, after creating the defined tables. | ||||
|      * | ||||
|      * @param db The affected DB. | ||||
|      * @param oldVersion Old version of the schema or 0 if not installed. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     migrate?(db: SQLiteDB, oldVersion: number): Promise<any>; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Factory to provide some global functionalities, like access to the global app database. | ||||
|  * @description | ||||
| @ -71,12 +102,33 @@ export class CoreAppProvider { | ||||
|     protected mainMenuOpen: number; | ||||
|     protected forceOffline = false; | ||||
| 
 | ||||
|     // Variables for DB.
 | ||||
|     protected createVersionsTableReady: Promise<any>; | ||||
|     protected SCHEMA_VERSIONS_TABLE = 'schema_versions'; | ||||
|     protected versionsTableSchema: SQLiteDBTableSchema = { | ||||
|         name: this.SCHEMA_VERSIONS_TABLE, | ||||
|         columns: [ | ||||
|             { | ||||
|                 name: 'name', | ||||
|                 type: 'TEXT', | ||||
|                 primaryKey: true, | ||||
|             }, | ||||
|             { | ||||
|                 name: 'version', | ||||
|                 type: 'INTEGER' | ||||
|             } | ||||
|         ] | ||||
|     }; | ||||
| 
 | ||||
|     constructor(dbProvider: CoreDbProvider, private platform: Platform, private keyboard: Keyboard, private appCtrl: App, | ||||
|             private network: Network, logger: CoreLoggerProvider, private events: CoreEventsProvider, zone: NgZone, | ||||
|             private menuCtrl: MenuController, private statusBar: StatusBar) { | ||||
|         this.logger = logger.getInstance('CoreAppProvider'); | ||||
|         this.db = dbProvider.getDB(this.DBNAME); | ||||
| 
 | ||||
|         // Create the schema versions table.
 | ||||
|         this.createVersionsTableReady = this.db.createTableFromSchema(this.versionsTableSchema); | ||||
| 
 | ||||
|         this.keyboard.onKeyboardShow().subscribe((data) => { | ||||
|             // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | ||||
|             zone.run(() => { | ||||
| @ -133,6 +185,47 @@ export class CoreAppProvider { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Install and upgrade a certain schema. | ||||
|      * | ||||
|      * @param schema The schema to create. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async createTablesFromSchema(schema: CoreAppSchema): Promise<any> { | ||||
|         this.logger.debug(`Apply schema to app DB: ${schema.name}`); | ||||
| 
 | ||||
|         let oldVersion; | ||||
| 
 | ||||
|         try { | ||||
|             // Wait for the schema versions table to be created.
 | ||||
|             await this.createVersionsTableReady; | ||||
| 
 | ||||
|             // Fetch installed version of the schema.
 | ||||
|             const entry = await this.db.getRecord(this.SCHEMA_VERSIONS_TABLE, {name: schema.name}); | ||||
|             oldVersion = entry.version; | ||||
|         } catch (error) { | ||||
|             // No installed version yet.
 | ||||
|             oldVersion = 0; | ||||
|         } | ||||
| 
 | ||||
|         if (oldVersion >= schema.version) { | ||||
|             // Version already installed, nothing else to do.
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.logger.debug(`Migrating schema '${schema.name}' of app DB from version ${oldVersion} to ${schema.version}`); | ||||
| 
 | ||||
|         if (schema.tables) { | ||||
|             await this.db.createTablesFromSchema(schema.tables); | ||||
|         } | ||||
|         if (schema.migrate) { | ||||
|             await schema.migrate(this.db, oldVersion); | ||||
|         } | ||||
| 
 | ||||
|         // Set installed version.
 | ||||
|         await this.db.insertRecord(this.SCHEMA_VERSIONS_TABLE, {name: schema.name, version: schema.version}); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the application global database. | ||||
|      * | ||||
|  | ||||
| @ -13,8 +13,8 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreAppProvider } from './app'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { CoreAppProvider, CoreAppSchema } from './app'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| 
 | ||||
| /** | ||||
|  * Factory to provide access to dynamic and permanent config and settings. | ||||
| @ -24,24 +24,34 @@ import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| export class CoreConfigProvider { | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected TABLE_NAME = 'core_config'; | ||||
|     protected tableSchema: SQLiteDBTableSchema = { | ||||
|         name: this.TABLE_NAME, | ||||
|         columns: [ | ||||
|     protected tableSchema: CoreAppSchema = { | ||||
|         name: 'CoreConfigProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: 'name', | ||||
|                 type: 'TEXT', | ||||
|                 unique: true, | ||||
|                 notNull: true | ||||
|                 name: this.TABLE_NAME, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'name', | ||||
|                         type: 'TEXT', | ||||
|                         unique: true, | ||||
|                         notNull: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'value' | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 name: 'value' | ||||
|             } | ||||
|         ] | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
| 
 | ||||
|     constructor(appProvider: CoreAppProvider) { | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTableFromSchema(this.tableSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.tableSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -50,7 +60,9 @@ export class CoreConfigProvider { | ||||
|      * @param name The config name. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     delete(name: string): Promise<any> { | ||||
|     async delete(name: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.deleteRecords(this.TABLE_NAME, { name: name }); | ||||
|     } | ||||
| 
 | ||||
| @ -61,16 +73,20 @@ export class CoreConfigProvider { | ||||
|      * @param defaultValue Default value to use if the entry is not found. | ||||
|      * @return Resolves upon success along with the config data. Reject on failure. | ||||
|      */ | ||||
|     get(name: string, defaultValue?: any): Promise<any> { | ||||
|         return this.appDB.getRecord(this.TABLE_NAME, { name: name }).then((entry) => { | ||||
|     async get(name: string, defaultValue?: any): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             const entry = await this.appDB.getRecord(this.TABLE_NAME, { name: name }); | ||||
| 
 | ||||
|             return entry.value; | ||||
|         }).catch((error) => { | ||||
|         } catch (error) { | ||||
|             if (typeof defaultValue != 'undefined') { | ||||
|                 return defaultValue; | ||||
|             } | ||||
| 
 | ||||
|             return Promise.reject(error); | ||||
|         }); | ||||
|             throw error; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -80,7 +96,9 @@ export class CoreConfigProvider { | ||||
|      * @param value The config value. Can only store number or strings. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     set(name: string, value: number | string): Promise<any> { | ||||
|     async set(name: string, value: number | string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.insertRecord(this.TABLE_NAME, { name: name, value: value }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -14,12 +14,12 @@ | ||||
| 
 | ||||
| import { Injectable, NgZone } from '@angular/core'; | ||||
| import { Network } from '@ionic-native/network'; | ||||
| import { CoreAppProvider } from './app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from './app'; | ||||
| import { CoreConfigProvider } from './config'; | ||||
| import { CoreLoggerProvider } from './logger'; | ||||
| import { CoreUtilsProvider } from './utils/utils'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| 
 | ||||
| /** | ||||
|  * Interface that all cron handlers must implement. | ||||
| @ -92,23 +92,30 @@ export class CoreCronDelegate { | ||||
| 
 | ||||
|     // Variables for database.
 | ||||
|     protected CRON_TABLE = 'cron'; | ||||
|     protected tableSchema: SQLiteDBTableSchema = { | ||||
|         name: this.CRON_TABLE, | ||||
|         columns: [ | ||||
|     protected tableSchema: CoreAppSchema = { | ||||
|         name: 'CoreCronDelegate', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: 'id', | ||||
|                 type: 'TEXT', | ||||
|                 primaryKey: true | ||||
|                 name: this.CRON_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'value', | ||||
|                         type: 'INTEGER' | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 name: 'value', | ||||
|                 type: 'INTEGER' | ||||
|             } | ||||
|         ] | ||||
|         ], | ||||
|     }; | ||||
| 
 | ||||
|     protected logger; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected handlers: { [s: string]: CoreCronHandler } = {}; | ||||
|     protected queuePromise = Promise.resolve(); | ||||
| 
 | ||||
| @ -117,7 +124,9 @@ export class CoreCronDelegate { | ||||
|         this.logger = logger.getInstance('CoreCronDelegate'); | ||||
| 
 | ||||
|         this.appDB = this.appProvider.getDB(); | ||||
|         this.appDB.createTableFromSchema(this.tableSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.tableSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
| 
 | ||||
|         // When the app is re-connected, start network handlers that were stopped.
 | ||||
|         network.onConnect().subscribe(() => { | ||||
| @ -306,16 +315,19 @@ export class CoreCronDelegate { | ||||
|      * @param name Handler's name. | ||||
|      * @return Promise resolved with the handler's last execution time. | ||||
|      */ | ||||
|     protected getHandlerLastExecutionTime(name: string): Promise<number> { | ||||
|     protected async getHandlerLastExecutionTime(name: string): Promise<number> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const id = this.getHandlerLastExecutionId(name); | ||||
| 
 | ||||
|         return this.appDB.getRecord(this.CRON_TABLE, { id: id }).then((entry) => { | ||||
|         try { | ||||
|             const entry = await this.appDB.getRecord(this.CRON_TABLE, { id: id }); | ||||
|             const time = parseInt(entry.value, 10); | ||||
| 
 | ||||
|             return isNaN(time) ? 0 : time; | ||||
|         }).catch(() => { | ||||
|         } catch (err) { | ||||
|             return 0; // Not set, return 0.
 | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -471,7 +483,9 @@ export class CoreCronDelegate { | ||||
|      * @param time Time to set. | ||||
|      * @return Promise resolved when the execution time is saved. | ||||
|      */ | ||||
|     protected setHandlerLastExecutionTime(name: string, time: number): Promise<any> { | ||||
|     protected async setHandlerLastExecutionTime(name: string, time: number): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const id = this.getHandlerLastExecutionId(name), | ||||
|             entry = { | ||||
|                 id: id, | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| 
 | ||||
| import { Injectable, NgZone } from '@angular/core'; | ||||
| import { Network } from '@ionic-native/network'; | ||||
| import { CoreAppProvider } from './app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from './app'; | ||||
| import { CoreEventsProvider } from './events'; | ||||
| import { CoreFileProvider } from './file'; | ||||
| import { CoreInitDelegate } from './init'; | ||||
| @ -28,7 +28,7 @@ import { CoreTextUtilsProvider } from './utils/text'; | ||||
| import { CoreTimeUtilsProvider } from './utils/time'; | ||||
| import { CoreUrlUtilsProvider } from './utils/url'; | ||||
| import { CoreUtilsProvider } from './utils/utils'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { Md5 } from 'ts-md5/dist/md5'; | ||||
| 
 | ||||
| @ -224,58 +224,62 @@ export class CoreFilepoolProvider { | ||||
|     protected FILES_TABLE = 'filepool_files'; // Downloaded files.
 | ||||
|     protected LINKS_TABLE = 'filepool_files_links'; // Links between downloaded files and components.
 | ||||
|     protected PACKAGES_TABLE = 'filepool_packages'; // Downloaded packages (sets of files).
 | ||||
|     protected appTablesSchema: SQLiteDBTableSchema[] = [ | ||||
|         { | ||||
|             name: this.QUEUE_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'siteId', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'fileId', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'added', | ||||
|                     type: 'INTEGER' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'priority', | ||||
|                     type: 'INTEGER' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'url', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'revision', | ||||
|                     type: 'INTEGER' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'timemodified', | ||||
|                     type: 'INTEGER' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'isexternalfile', | ||||
|                     type: 'INTEGER' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'repositorytype', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'path', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'links', | ||||
|                     type: 'TEXT' | ||||
|                 } | ||||
|             ], | ||||
|             primaryKeys: ['siteId', 'fileId'] | ||||
|         } | ||||
|     ]; | ||||
|     protected appTablesSchema: CoreAppSchema = { | ||||
|         name: 'CoreFilepoolProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: this.QUEUE_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'siteId', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'fileId', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'added', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'priority', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'url', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'revision', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'timemodified', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'isexternalfile', | ||||
|                         type: 'INTEGER' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'repositorytype', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'path', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'links', | ||||
|                         type: 'TEXT' | ||||
|                     } | ||||
|                 ], | ||||
|                 primaryKeys: ['siteId', 'fileId'] | ||||
|             } | ||||
|         ] | ||||
|     }; | ||||
|     protected siteSchema: CoreSiteSchema = { | ||||
|         name: 'CoreFilepoolProvider', | ||||
|         version: 1, | ||||
| @ -392,6 +396,7 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|     protected logger; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected tokenRegex = new RegExp('(\\?|&)token=([A-Za-z0-9]*)'); | ||||
|     protected queueState: string; | ||||
|     protected urlAttributes = [ | ||||
| @ -415,7 +420,9 @@ export class CoreFilepoolProvider { | ||||
|         this.logger = logger.getInstance('CoreFilepoolProvider'); | ||||
| 
 | ||||
|         this.appDB = this.appProvider.getDB(); | ||||
|         this.appDB.createTablesFromSchema(this.appTablesSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.appTablesSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
| 
 | ||||
|         this.sitesProvider.registerSiteSchema(this.siteSchema); | ||||
| 
 | ||||
| @ -567,11 +574,13 @@ export class CoreFilepoolProvider { | ||||
|      * @param link The link to add for the file. | ||||
|      * @return Promise resolved when the file is downloaded. | ||||
|      */ | ||||
|     protected addToQueue(siteId: string, fileId: string, url: string, priority: number, revision: number, timemodified: number, | ||||
|             filePath: string, onProgress?: (event: any) => any, options: any = {}, link?: any): Promise<any> { | ||||
|     protected async addToQueue(siteId: string, fileId: string, url: string, priority: number, revision: number, | ||||
|             timemodified: number, filePath: string, onProgress?: (event: any) => any, options: any = {}, link?: any): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         this.logger.debug(`Adding ${fileId} to the queue`); | ||||
| 
 | ||||
|         return this.appDB.insertRecord(this.QUEUE_TABLE, { | ||||
|         await this.appDB.insertRecord(this.QUEUE_TABLE, { | ||||
|             siteId: siteId, | ||||
|             fileId: fileId, | ||||
|             url: url, | ||||
| @ -583,13 +592,13 @@ export class CoreFilepoolProvider { | ||||
|             repositorytype: options.repositorytype, | ||||
|             links: JSON.stringify(link ? [link] : []), | ||||
|             added: Date.now() | ||||
|         }).then(() => { | ||||
|             // Check if the queue is running.
 | ||||
|             this.checkQueueProcessing(); | ||||
|             this.notifyFileDownloading(siteId, fileId); | ||||
| 
 | ||||
|             return this.getQueuePromise(siteId, fileId, true, onProgress); | ||||
|         }); | ||||
| 
 | ||||
|         // Check if the queue is running.
 | ||||
|         this.checkQueueProcessing(); | ||||
|         this.notifyFileDownloading(siteId, fileId); | ||||
| 
 | ||||
|         return this.getQueuePromise(siteId, fileId, true, onProgress); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -608,9 +617,11 @@ export class CoreFilepoolProvider { | ||||
|      * @param alreadyFixed Whether the URL has already been fixed. | ||||
|      * @return Resolved on success. | ||||
|      */ | ||||
|     addToQueueByUrl(siteId: string, fileUrl: string, component?: string, componentId?: string | number, timemodified: number = 0, | ||||
|             filePath?: string, onProgress?: (event: any) => any, priority: number = 0, options: any = {}, revision?: number, | ||||
|             alreadyFixed?: boolean): Promise<any> { | ||||
|     async addToQueueByUrl(siteId: string, fileUrl: string, component?: string, componentId?: string | number, | ||||
|             timemodified: number = 0, filePath?: string, onProgress?: (event: any) => any, priority: number = 0, options: any = {}, | ||||
|             revision?: number, alreadyFixed?: boolean): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         let fileId, | ||||
|             link, | ||||
|             queueDeferred; | ||||
| @ -2309,16 +2320,17 @@ export class CoreFilepoolProvider { | ||||
|      * @param fileUrl The file URL. | ||||
|      * @return Resolved with file object from DB on success, rejected otherwise. | ||||
|      */ | ||||
|     protected hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> { | ||||
|         return this.appDB.getRecord(this.QUEUE_TABLE, { siteId: siteId, fileId: fileId }).then((entry) => { | ||||
|             if (typeof entry === 'undefined') { | ||||
|                 return Promise.reject(null); | ||||
|             } | ||||
|             // Convert the links to an object.
 | ||||
|             entry.links = this.textUtils.parseJSON(entry.links, []); | ||||
|     protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|             return entry; | ||||
|         }); | ||||
|         const entry = await this.appDB.getRecord(this.QUEUE_TABLE, { siteId: siteId, fileId: fileId }); | ||||
|         if (typeof entry === 'undefined') { | ||||
|             throw null; | ||||
|         } | ||||
|         // Convert the links to an object.
 | ||||
|         entry.links = this.textUtils.parseJSON(entry.links, []); | ||||
| 
 | ||||
|         return entry; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2546,19 +2558,25 @@ export class CoreFilepoolProvider { | ||||
|      * | ||||
|      * @return Resolved on success. Rejected on failure. | ||||
|      */ | ||||
|     protected processImportantQueueItem(): Promise<any> { | ||||
|         return this.appDB.getRecords(this.QUEUE_TABLE, undefined, 'priority DESC, added ASC', undefined, 0, 1).then((items) => { | ||||
|             const item = items.pop(); | ||||
|             if (!item) { | ||||
|                 return Promise.reject(this.ERR_QUEUE_IS_EMPTY); | ||||
|             } | ||||
|             // Convert the links to an object.
 | ||||
|             item.links = this.textUtils.parseJSON(item.links, []); | ||||
|     protected async processImportantQueueItem(): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|             return this.processQueueItem(item); | ||||
|         }, () => { | ||||
|             return Promise.reject(this.ERR_QUEUE_IS_EMPTY); | ||||
|         }); | ||||
|         let items; | ||||
| 
 | ||||
|         try { | ||||
|             items = await this.appDB.getRecords(this.QUEUE_TABLE, undefined, 'priority DESC, added ASC', undefined, 0, 1); | ||||
|         } catch (err) { | ||||
|             throw this.ERR_QUEUE_IS_EMPTY; | ||||
|         } | ||||
| 
 | ||||
|         const item = items.pop(); | ||||
|         if (!item) { | ||||
|             throw this.ERR_QUEUE_IS_EMPTY; | ||||
|         } | ||||
|         // Convert the links to an object.
 | ||||
|         item.links = this.textUtils.parseJSON(item.links, []); | ||||
| 
 | ||||
|         return this.processQueueItem(item); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2685,7 +2703,9 @@ export class CoreFilepoolProvider { | ||||
|      * @param fileId The file ID. | ||||
|      * @return Resolved on success. Rejected on failure. It is advised to silently ignore failures. | ||||
|      */ | ||||
|     protected removeFromQueue(siteId: string, fileId: string): Promise<any> { | ||||
|     protected async removeFromQueue(siteId: string, fileId: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.deleteRecords(this.QUEUE_TABLE, { siteId: siteId, fileId: fileId }); | ||||
|     } | ||||
| 
 | ||||
| @ -3003,27 +3023,26 @@ export class CoreFilepoolProvider { | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     treatExtensionInQueue(): Promise<any> { | ||||
|     async treatExtensionInQueue(): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         this.logger.debug('Treat extensions in queue'); | ||||
| 
 | ||||
|         return this.appDB.getAllRecords(this.QUEUE_TABLE).then((entries) => { | ||||
|             const promises = []; | ||||
|             entries.forEach((entry) => { | ||||
|         const entries = await this.appDB.getAllRecords(this.QUEUE_TABLE); | ||||
| 
 | ||||
|                 // For files in the queue, we only need to remove the extension from the fileId.
 | ||||
|                 // After downloading, additional info will be added.
 | ||||
|                 const fileId = entry.fileId; | ||||
|                 entry.fileId = this.mimeUtils.removeExtension(fileId); | ||||
|         return Promise.all(entries.map((entry) => { | ||||
| 
 | ||||
|                 if (fileId == entry.fileId) { | ||||
|                     return; | ||||
|                 } | ||||
|             // For files in the queue, we only need to remove the extension from the fileId.
 | ||||
|             // After downloading, additional info will be added.
 | ||||
|             const fileId = entry.fileId; | ||||
|             entry.fileId = this.mimeUtils.removeExtension(fileId); | ||||
| 
 | ||||
|                 promises.push(this.appDB.updateRecords(this.QUEUE_TABLE, { fileId: entry.fileId }, { fileId: fileId })); | ||||
|             }); | ||||
|             if (fileId == entry.fileId) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             return Promise.all(promises); | ||||
|         }); | ||||
|             return this.appDB.updateRecords(this.QUEUE_TABLE, { fileId: entry.fileId }, { fileId: fileId }); | ||||
|         })); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -17,13 +17,13 @@ import { Platform, Alert, AlertController } from 'ionic-angular'; | ||||
| import { LocalNotifications, ILocalNotification } from '@ionic-native/local-notifications'; | ||||
| import { Push } from '@ionic-native/push'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreAppProvider } from './app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from './app'; | ||||
| import { CoreConfigProvider } from './config'; | ||||
| import { CoreEventsProvider } from './events'; | ||||
| import { CoreLoggerProvider } from './logger'; | ||||
| import { CoreTextUtilsProvider } from './utils/text'; | ||||
| import { CoreUtilsProvider } from './utils/utils'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { CoreConfigConstants } from '../configconstants'; | ||||
| import { Subject, Subscription } from 'rxjs'; | ||||
| @ -40,56 +40,61 @@ export class CoreLocalNotificationsProvider { | ||||
|     protected SITES_TABLE = 'notification_sites'; // Store to asigne unique codes to each site.
 | ||||
|     protected COMPONENTS_TABLE = 'notification_components'; // Store to asigne unique codes to each component.
 | ||||
|     protected TRIGGERED_TABLE = 'notifications_triggered'; // Store to prevent re-triggering notifications.
 | ||||
|     protected tablesSchema: SQLiteDBTableSchema[] = [ | ||||
|         { | ||||
|             name: this.SITES_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'id', | ||||
|                     type: 'TEXT', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'code', | ||||
|                     type: 'INTEGER', | ||||
|                     notNull: true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             name: this.COMPONENTS_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'id', | ||||
|                     type: 'TEXT', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'code', | ||||
|                     type: 'INTEGER', | ||||
|                     notNull: true | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             name: this.TRIGGERED_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'id', | ||||
|                     type: 'INTEGER', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'at', | ||||
|                     type: 'INTEGER', | ||||
|                     notNull: true | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ]; | ||||
|     protected tablesSchema: CoreAppSchema = { | ||||
|         name: 'CoreLocalNotificationsProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: this.SITES_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'code', | ||||
|                         type: 'INTEGER', | ||||
|                         notNull: true | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 name: this.COMPONENTS_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'code', | ||||
|                         type: 'INTEGER', | ||||
|                         notNull: true | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 name: this.TRIGGERED_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'INTEGER', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'at', | ||||
|                         type: 'INTEGER', | ||||
|                         notNull: true | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         ] | ||||
|     }; | ||||
| 
 | ||||
|     protected logger; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected codes: { [s: string]: number } = {}; | ||||
|     protected codeRequestsQueue = {}; | ||||
|     protected observables = {}; | ||||
| @ -114,7 +119,9 @@ export class CoreLocalNotificationsProvider { | ||||
| 
 | ||||
|         this.logger = logger.getInstance('CoreLocalNotificationsProvider'); | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTablesFromSchema(this.tablesSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.tablesSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
| 
 | ||||
|         platform.ready().then(() => { | ||||
|             // Listen to events.
 | ||||
| @ -242,34 +249,35 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param id ID of the element to get its code. | ||||
|      * @return Promise resolved when the code is retrieved. | ||||
|      */ | ||||
|     protected getCode(table: string, id: string): Promise<number> { | ||||
|     protected async getCode(table: string, id: string): Promise<number> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const key = table + '#' + id; | ||||
| 
 | ||||
|         // Check if the code is already in memory.
 | ||||
|         if (typeof this.codes[key] != 'undefined') { | ||||
|             return Promise.resolve(this.codes[key]); | ||||
|             return this.codes[key]; | ||||
|         } | ||||
| 
 | ||||
|         // Check if we already have a code stored for that ID.
 | ||||
|         return this.appDB.getRecord(table, { id: id }).then((entry) => { | ||||
|         try { | ||||
|             // Check if we already have a code stored for that ID.
 | ||||
|             const entry = await this.appDB.getRecord(table, { id: id }); | ||||
|             this.codes[key] = entry.code; | ||||
| 
 | ||||
|             return entry.code; | ||||
|         }).catch(() => { | ||||
|         } catch (err) { | ||||
|             // No code stored for that ID. Create a new code for it.
 | ||||
|             return this.appDB.getRecords(table, undefined, 'code DESC').then((entries) => { | ||||
|                 let newCode = 0; | ||||
|                 if (entries.length > 0) { | ||||
|                     newCode = entries[0].code + 1; | ||||
|                 } | ||||
|             const entries = await this.appDB.getRecords(table, undefined, 'code DESC'); | ||||
|             let newCode = 0; | ||||
|             if (entries.length > 0) { | ||||
|                 newCode = entries[0].code + 1; | ||||
|             } | ||||
| 
 | ||||
|                 return this.appDB.insertRecord(table, { id: id, code: newCode }).then(() => { | ||||
|                     this.codes[key] = newCode; | ||||
|             await this.appDB.insertRecord(table, { id: id, code: newCode }); | ||||
|             this.codes[key] = newCode; | ||||
| 
 | ||||
|                     return newCode; | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|             return newCode; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -352,8 +360,11 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param notification Notification to check. | ||||
|      * @return Promise resolved with a boolean indicating if promise is triggered (true) or not. | ||||
|      */ | ||||
|     isTriggered(notification: ILocalNotification): Promise<any> { | ||||
|         return this.appDB.getRecord(this.TRIGGERED_TABLE, { id: notification.id }).then((stored) => { | ||||
|     async isTriggered(notification: ILocalNotification): Promise<boolean> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             const stored = await this.appDB.getRecord(this.TRIGGERED_TABLE, { id: notification.id }); | ||||
|             let triggered = (notification.trigger && notification.trigger.at) || 0; | ||||
| 
 | ||||
|             if (typeof triggered != 'number') { | ||||
| @ -361,9 +372,9 @@ export class CoreLocalNotificationsProvider { | ||||
|             } | ||||
| 
 | ||||
|             return stored.at === triggered; | ||||
|         }).catch(() => { | ||||
|         } catch (err) { | ||||
|             return this.localNotifications.isTriggered(notification.id); | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -477,7 +488,9 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param id Notification ID. | ||||
|      * @return Promise resolved when it is removed. | ||||
|      */ | ||||
|     removeTriggered(id: number): Promise<any> { | ||||
|     async removeTriggered(id: number): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         return this.appDB.deleteRecords(this.TRIGGERED_TABLE, { id: id }); | ||||
|     } | ||||
| 
 | ||||
| @ -714,7 +727,9 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param notification Triggered notification. | ||||
|      * @return Promise resolved when stored, rejected otherwise. | ||||
|      */ | ||||
|     trigger(notification: ILocalNotification): Promise<any> { | ||||
|     async trigger(notification: ILocalNotification): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const entry = { | ||||
|             id: notification.id, | ||||
|             at: notification.trigger && notification.trigger.at ? notification.trigger.at : Date.now() | ||||
| @ -730,7 +745,9 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param newName The new name. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     updateComponentName(oldName: string, newName: string): Promise<any> { | ||||
|     async updateComponentName(oldName: string, newName: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const oldId = this.COMPONENTS_TABLE + '#' + oldName, | ||||
|             newId = this.COMPONENTS_TABLE + '#' + newName; | ||||
| 
 | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| import { Injectable, Injector } from '@angular/core'; | ||||
| import { HttpClient } from '@angular/common/http'; | ||||
| import { TranslateService } from '@ngx-translate/core'; | ||||
| import { CoreAppProvider } from './app'; | ||||
| import { CoreAppProvider, CoreAppSchema } from './app'; | ||||
| import { CoreEventsProvider } from './events'; | ||||
| import { CoreLoggerProvider } from './logger'; | ||||
| import { CoreSitesFactoryProvider } from './sites-factory'; | ||||
| @ -170,7 +170,7 @@ export const enum CoreSitesReadingStrategy { | ||||
|  * their own database tables. Example: | ||||
|  * | ||||
|  * constructor(sitesProvider: CoreSitesProvider) { | ||||
|  *     this.sitesProvider.createTableFromSchema(this.tableSchema); | ||||
|  *     this.sitesProvider.registerSiteSchema(this.siteSchema); | ||||
|  * | ||||
|  * This provider will automatically create the tables in the databases of all the instantiated sites, and also to the | ||||
|  * databases of sites instantiated from now on. | ||||
| @ -181,59 +181,63 @@ export class CoreSitesProvider { | ||||
|     protected SITES_TABLE = 'sites'; | ||||
|     protected CURRENT_SITE_TABLE = 'current_site'; | ||||
|     protected SCHEMA_VERSIONS_TABLE = 'schema_versions'; | ||||
|     protected appTablesSchema: SQLiteDBTableSchema[] = [ | ||||
|         { | ||||
|             name: this.SITES_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'id', | ||||
|                     type: 'TEXT', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'siteUrl', | ||||
|                     type: 'TEXT', | ||||
|                     notNull: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'token', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'info', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'privateToken', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'config', | ||||
|                     type: 'TEXT' | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'loggedOut', | ||||
|                     type: 'INTEGER' | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         { | ||||
|             name: this.CURRENT_SITE_TABLE, | ||||
|             columns: [ | ||||
|                 { | ||||
|                     name: 'id', | ||||
|                     type: 'INTEGER', | ||||
|                     primaryKey: true | ||||
|                 }, | ||||
|                 { | ||||
|                     name: 'siteId', | ||||
|                     type: 'TEXT', | ||||
|                     notNull: true, | ||||
|                     unique: true | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ]; | ||||
|     protected appTablesSchema: CoreAppSchema = { | ||||
|         name: 'CoreSitesProvider', | ||||
|         version: 1, | ||||
|         tables: [ | ||||
|             { | ||||
|                 name: this.SITES_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'TEXT', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'siteUrl', | ||||
|                         type: 'TEXT', | ||||
|                         notNull: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'token', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'info', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'privateToken', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'config', | ||||
|                         type: 'TEXT' | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'loggedOut', | ||||
|                         type: 'INTEGER' | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             { | ||||
|                 name: this.CURRENT_SITE_TABLE, | ||||
|                 columns: [ | ||||
|                     { | ||||
|                         name: 'id', | ||||
|                         type: 'INTEGER', | ||||
|                         primaryKey: true | ||||
|                     }, | ||||
|                     { | ||||
|                         name: 'siteId', | ||||
|                         type: 'TEXT', | ||||
|                         notNull: true, | ||||
|                         unique: true | ||||
|                     } | ||||
|                 ] | ||||
|             } | ||||
|         ] | ||||
|     }; | ||||
| 
 | ||||
|     // Constants to validate a site version.
 | ||||
|     protected WORKPLACE_APP = 3; | ||||
| @ -249,6 +253,7 @@ export class CoreSitesProvider { | ||||
|     protected currentSite: CoreSite; | ||||
|     protected sites: { [s: string]: CoreSite } = {}; | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<any>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected siteSchemasMigration: { [siteId: string]: Promise<any> } = {}; | ||||
| 
 | ||||
|     // Schemas for site tables. Other providers can add schemas in here.
 | ||||
| @ -323,7 +328,9 @@ export class CoreSitesProvider { | ||||
|         this.logger = logger.getInstance('CoreSitesProvider'); | ||||
| 
 | ||||
|         this.appDB = appProvider.getDB(); | ||||
|         this.appDB.createTablesFromSchema(this.appTablesSchema); | ||||
|         this.dbReady = appProvider.createTablesFromSchema(this.appTablesSchema).catch(() => { | ||||
|             // Ignore errors.
 | ||||
|         }); | ||||
|         this.registerSiteSchema(this.siteSchema); | ||||
|     } | ||||
| 
 | ||||
| @ -859,7 +866,9 @@ export class CoreSitesProvider { | ||||
|      * @param config Site config (from tool_mobile_get_config). | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     addSite(id: string, siteUrl: string, token: string, info: any, privateToken: string = '', config?: any): Promise<any> { | ||||
|     async addSite(id: string, siteUrl: string, token: string, info: any, privateToken: string = '', config?: any): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const entry = { | ||||
|             id: id, | ||||
|             siteUrl: siteUrl, | ||||
| @ -1070,29 +1079,32 @@ export class CoreSitesProvider { | ||||
|      * @param siteId ID of the site to delete. | ||||
|      * @return Promise to be resolved when the site is deleted. | ||||
|      */ | ||||
|     deleteSite(siteId: string): Promise<any> { | ||||
|     async deleteSite(siteId: string): Promise<void> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         this.logger.debug(`Delete site ${siteId}`); | ||||
| 
 | ||||
|         if (typeof this.currentSite != 'undefined' && this.currentSite.id == siteId) { | ||||
|             this.logout(); | ||||
|         } | ||||
| 
 | ||||
|         return this.getSite(siteId).then((site: CoreSite) => { | ||||
|             return site.deleteDB().then(() => { | ||||
|                 // Site DB deleted, now delete the app from the list of sites.
 | ||||
|                 delete this.sites[siteId]; | ||||
|         const site = await this.getSite(siteId); | ||||
| 
 | ||||
|                 return this.appDB.deleteRecords(this.SITES_TABLE, { id: siteId }).then(() => { | ||||
|                     // Site deleted from sites list, now delete the folder.
 | ||||
|                     return site.deleteFolder(); | ||||
|                 }, () => { | ||||
|                     // DB remove shouldn't fail, but we'll go ahead even if it does.
 | ||||
|                     return site.deleteFolder(); | ||||
|                 }).then(() => { | ||||
|                     this.eventsProvider.trigger(CoreEventsProvider.SITE_DELETED, site, siteId); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|         await site.deleteDB(); | ||||
| 
 | ||||
|         // Site DB deleted, now delete the app from the list of sites.
 | ||||
|         delete this.sites[siteId]; | ||||
| 
 | ||||
|         try { | ||||
|             await this.appDB.deleteRecords(this.SITES_TABLE, { id: siteId }); | ||||
|         } catch (err) { | ||||
|             // DB remove shouldn't fail, but we'll go ahead even if it does.
 | ||||
|         } | ||||
| 
 | ||||
|         // Site deleted from sites list, now delete the folder.
 | ||||
|         await site.deleteFolder(); | ||||
| 
 | ||||
|         this.eventsProvider.trigger(CoreEventsProvider.SITE_DELETED, site, siteId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1100,10 +1112,12 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved with true if there are sites and false if there aren't. | ||||
|      */ | ||||
|     hasSites(): Promise<boolean> { | ||||
|         return this.appDB.countRecords(this.SITES_TABLE).then((count) => { | ||||
|             return count > 0; | ||||
|         }); | ||||
|     async hasSites(): Promise<boolean> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const count = await this.appDB.countRecords(this.SITES_TABLE); | ||||
| 
 | ||||
|         return count > 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1112,18 +1126,24 @@ export class CoreSitesProvider { | ||||
|      * @param siteId The site ID. If not defined, current site (if available). | ||||
|      * @return Promise resolved with the site. | ||||
|      */ | ||||
|     getSite(siteId?: string): Promise<CoreSite> { | ||||
|     async getSite(siteId?: string): Promise<CoreSite> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         if (!siteId) { | ||||
|             return this.currentSite ? Promise.resolve(this.currentSite) : Promise.reject(null); | ||||
|             if (this.currentSite) { | ||||
|                 return this.currentSite; | ||||
|             } | ||||
| 
 | ||||
|             throw null; | ||||
|         } else if (this.currentSite && this.currentSite.getId() == siteId) { | ||||
|             return Promise.resolve(this.currentSite); | ||||
|             return this.currentSite; | ||||
|         } else if (typeof this.sites[siteId] != 'undefined') { | ||||
|             return Promise.resolve(this.sites[siteId]); | ||||
|             return this.sites[siteId]; | ||||
|         } else { | ||||
|             // Retrieve and create the site.
 | ||||
|             return this.appDB.getRecord(this.SITES_TABLE, { id: siteId }).then((data) => { | ||||
|                 return this.makeSiteFromSiteListEntry(data); | ||||
|             }); | ||||
|             const data = await this.appDB.getRecord(this.SITES_TABLE, { id: siteId }); | ||||
| 
 | ||||
|             return this.makeSiteFromSiteListEntry(data); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -1199,27 +1219,29 @@ export class CoreSitesProvider { | ||||
|      * @param ids IDs of the sites to get. If not defined, return all sites. | ||||
|      * @return Promise resolved when the sites are retrieved. | ||||
|      */ | ||||
|     getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> { | ||||
|         return this.appDB.getAllRecords(this.SITES_TABLE).then((sites) => { | ||||
|             const formattedSites = []; | ||||
|             sites.forEach((site) => { | ||||
|                 if (!ids || ids.indexOf(site.id) > -1) { | ||||
|                     // Parse info.
 | ||||
|                     const siteInfo = site.info ? this.textUtils.parseJSON(site.info) : site.info, | ||||
|                         basicInfo: CoreSiteBasicInfo = { | ||||
|                             id: site.id, | ||||
|                             siteUrl: site.siteUrl, | ||||
|                             fullName: siteInfo && siteInfo.fullname, | ||||
|                             siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo && siteInfo.sitename, | ||||
|                             avatar: siteInfo && siteInfo.userpictureurl, | ||||
|                             siteHomeId: siteInfo && siteInfo.siteid || 1 | ||||
|                         }; | ||||
|                     formattedSites.push(basicInfo); | ||||
|                 } | ||||
|             }); | ||||
|     async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|             return formattedSites; | ||||
|         const sites = await this.appDB.getAllRecords(this.SITES_TABLE); | ||||
| 
 | ||||
|         const formattedSites = []; | ||||
|         sites.forEach((site) => { | ||||
|             if (!ids || ids.indexOf(site.id) > -1) { | ||||
|                 // Parse info.
 | ||||
|                 const siteInfo = site.info ? this.textUtils.parseJSON(site.info) : site.info, | ||||
|                     basicInfo: CoreSiteBasicInfo = { | ||||
|                         id: site.id, | ||||
|                         siteUrl: site.siteUrl, | ||||
|                         fullName: siteInfo && siteInfo.fullname, | ||||
|                         siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo && siteInfo.sitename, | ||||
|                         avatar: siteInfo && siteInfo.userpictureurl, | ||||
|                         siteHomeId: siteInfo && siteInfo.siteid || 1 | ||||
|                     }; | ||||
|                 formattedSites.push(basicInfo); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         return formattedSites; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1257,11 +1279,13 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved when the sites IDs are retrieved. | ||||
|      */ | ||||
|     getLoggedInSitesIds(): Promise<string[]> { | ||||
|         return this.appDB.getRecords(this.SITES_TABLE, {loggedOut : 0}).then((sites) => { | ||||
|             return sites.map((site) => { | ||||
|                 return site.id; | ||||
|             }); | ||||
|     async getLoggedInSitesIds(): Promise<string[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const sites = await this.appDB.getRecords(this.SITES_TABLE, {loggedOut : 0}); | ||||
| 
 | ||||
|         return sites.map((site) => { | ||||
|             return site.id; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -1270,11 +1294,13 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved when the sites IDs are retrieved. | ||||
|      */ | ||||
|     getSitesIds(): Promise<string[]> { | ||||
|         return this.appDB.getAllRecords(this.SITES_TABLE).then((sites) => { | ||||
|             return sites.map((site) => { | ||||
|                 return site.id; | ||||
|             }); | ||||
|     async getSitesIds(): Promise<string[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const sites = await this.appDB.getAllRecords(this.SITES_TABLE); | ||||
| 
 | ||||
|         return sites.map((site) => { | ||||
|             return site.id; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -1284,15 +1310,17 @@ export class CoreSitesProvider { | ||||
|      * @param siteid ID of the site the user is accessing. | ||||
|      * @return Promise resolved when current site is stored. | ||||
|      */ | ||||
|     login(siteId: string): Promise<void> { | ||||
|     async login(siteId: string): Promise<void> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const entry = { | ||||
|             id: 1, | ||||
|             siteId: siteId | ||||
|         }; | ||||
| 
 | ||||
|         return this.appDB.insertRecord(this.CURRENT_SITE_TABLE, entry).then(() => { | ||||
|             this.eventsProvider.trigger(CoreEventsProvider.LOGIN, {}, siteId); | ||||
|         }); | ||||
|         await this.appDB.insertRecord(this.CURRENT_SITE_TABLE, entry); | ||||
| 
 | ||||
|         this.eventsProvider.trigger(CoreEventsProvider.LOGIN, {}, siteId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1300,7 +1328,9 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved when the user is logged out. | ||||
|      */ | ||||
|     logout(): Promise<any> { | ||||
|     async logout(): Promise<void> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         let siteId; | ||||
|         const promises = []; | ||||
| 
 | ||||
| @ -1317,9 +1347,11 @@ export class CoreSitesProvider { | ||||
|             promises.push(this.appDB.deleteRecords(this.CURRENT_SITE_TABLE, { id: 1 })); | ||||
|         } | ||||
| 
 | ||||
|         return Promise.all(promises).finally(() => { | ||||
|         try { | ||||
|             await Promise.all(promises); | ||||
|         } finally { | ||||
|             this.eventsProvider.trigger(CoreEventsProvider.LOGOUT, {}, siteId); | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1327,21 +1359,24 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved if a session is restored. | ||||
|      */ | ||||
|     restoreSession(): Promise<any> { | ||||
|     async restoreSession(): Promise<any> { | ||||
|         if (this.sessionRestored) { | ||||
|             return Promise.reject(null); | ||||
|         } | ||||
| 
 | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         this.sessionRestored = true; | ||||
| 
 | ||||
|         return this.appDB.getRecord(this.CURRENT_SITE_TABLE, { id: 1 }).then((currentSite) => { | ||||
|         try { | ||||
|             const currentSite = await this.appDB.getRecord(this.CURRENT_SITE_TABLE, { id: 1 }); | ||||
|             const siteId = currentSite.siteId; | ||||
|             this.logger.debug(`Restore session in site ${siteId}`); | ||||
| 
 | ||||
|             return this.loadSite(siteId); | ||||
|         }).catch(() => { | ||||
|         } catch (err) { | ||||
|             // No current session.
 | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1351,17 +1386,18 @@ export class CoreSitesProvider { | ||||
|      * @param loggedOut True to set the site as logged out, false otherwise. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     setSiteLoggedOut(siteId: string, loggedOut: boolean): Promise<any> { | ||||
|         return this.getSite(siteId).then((site) => { | ||||
|             const newValues = { | ||||
|                 token: '', // Erase the token for security.
 | ||||
|                 loggedOut: loggedOut ? 1 : 0 | ||||
|             }; | ||||
|     async setSiteLoggedOut(siteId: string, loggedOut: boolean): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|             site.setLoggedOut(loggedOut); | ||||
|         const site = await this.getSite(siteId); | ||||
|         const newValues = { | ||||
|             token: '', // Erase the token for security.
 | ||||
|             loggedOut: loggedOut ? 1 : 0 | ||||
|         }; | ||||
| 
 | ||||
|             return this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }); | ||||
|         }); | ||||
|         site.setLoggedOut(loggedOut); | ||||
| 
 | ||||
|         return this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1396,20 +1432,21 @@ export class CoreSitesProvider { | ||||
|      * @param privateToken User's private token. | ||||
|      * @return A promise resolved when the site is updated. | ||||
|      */ | ||||
|     updateSiteTokenBySiteId(siteId: string, token: string, privateToken: string = ''): Promise<any> { | ||||
|         return this.getSite(siteId).then((site) => { | ||||
|             const newValues = { | ||||
|                 token: token, | ||||
|                 privateToken: privateToken, | ||||
|                 loggedOut: 0 | ||||
|             }; | ||||
|     async updateSiteTokenBySiteId(siteId: string, token: string, privateToken: string = ''): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|             site.token = token; | ||||
|             site.privateToken = privateToken; | ||||
|             site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore.
 | ||||
|         const site = await this.getSite(siteId); | ||||
|         const newValues = { | ||||
|             token: token, | ||||
|             privateToken: privateToken, | ||||
|             loggedOut: 0 | ||||
|         }; | ||||
| 
 | ||||
|             return this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }); | ||||
|         }); | ||||
|         site.token = token; | ||||
|         site.privateToken = privateToken; | ||||
|         site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore.
 | ||||
| 
 | ||||
|         return this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1418,39 +1455,49 @@ export class CoreSitesProvider { | ||||
|      * @param siteid Site's ID. | ||||
|      * @return A promise resolved when the site is updated. | ||||
|      */ | ||||
|     updateSiteInfo(siteId: string): Promise<any> { | ||||
|         return this.getSite(siteId).then((site) => { | ||||
|             return site.fetchSiteInfo().then((info) => { | ||||
|                 site.setInfo(info); | ||||
|     async updateSiteInfo(siteId: string): Promise<any> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|                 const versionCheck = this.isValidMoodleVersion(info); | ||||
|                 if (versionCheck != this.VALID_VERSION) { | ||||
|                     // The Moodle version is not supported, reject.
 | ||||
|                     return this.treatInvalidAppVersion(versionCheck, site.getURL(), site.getId()); | ||||
|                 } | ||||
|         const site = await this.getSite(siteId); | ||||
| 
 | ||||
|                 // Try to get the site config.
 | ||||
|                 return this.getSiteConfig(site).catch(() => { | ||||
|                     // Error getting config, keep the current one.
 | ||||
|                 }).then((config) => { | ||||
|                     const newValues: any = { | ||||
|                         info: JSON.stringify(info), | ||||
|                         loggedOut: site.isLoggedOut() ? 1 : 0 | ||||
|                     }; | ||||
|         try { | ||||
| 
 | ||||
|                     if (typeof config != 'undefined') { | ||||
|                         site.setConfig(config); | ||||
|                         newValues.config = JSON.stringify(config); | ||||
|                     } | ||||
|             const info = await site.fetchSiteInfo(); | ||||
|             site.setInfo(info); | ||||
| 
 | ||||
|                     return this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }).finally(() => { | ||||
|                         this.eventsProvider.trigger(CoreEventsProvider.SITE_UPDATED, info, siteId); | ||||
|                     }); | ||||
|                 }); | ||||
|             }).catch((error) => { | ||||
|                 // Ignore that we cannot fetch site info. Probably the auth token is invalid.
 | ||||
|             }); | ||||
|         }); | ||||
|             const versionCheck = this.isValidMoodleVersion(info); | ||||
|             if (versionCheck != this.VALID_VERSION) { | ||||
|                 // The Moodle version is not supported, reject.
 | ||||
|                 return this.treatInvalidAppVersion(versionCheck, site.getURL(), site.getId()); | ||||
|             } | ||||
| 
 | ||||
|             // Try to get the site config.
 | ||||
|             let config; | ||||
| 
 | ||||
|             try { | ||||
|                 config = await this.getSiteConfig(site); | ||||
|             } catch (error) { | ||||
|                 // Error getting config, keep the current one.
 | ||||
|             } | ||||
| 
 | ||||
|             const newValues: any = { | ||||
|                 info: JSON.stringify(info), | ||||
|                 loggedOut: site.isLoggedOut() ? 1 : 0 | ||||
|             }; | ||||
| 
 | ||||
|             if (typeof config != 'undefined') { | ||||
|                 site.setConfig(config); | ||||
|                 newValues.config = JSON.stringify(config); | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 await this.appDB.updateRecords(this.SITES_TABLE, newValues, { id: siteId }); | ||||
|             } finally { | ||||
|                 this.eventsProvider.trigger(CoreEventsProvider.SITE_UPDATED, info, siteId); | ||||
|             } | ||||
|         } catch (error) { | ||||
|             // Ignore that we cannot fetch site info. Probably the auth token is invalid.
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1476,11 +1523,13 @@ export class CoreSitesProvider { | ||||
|      * @param username If set, it will return only the sites where the current user has this username. | ||||
|      * @return Promise resolved with the site IDs (array). | ||||
|      */ | ||||
|     getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string): Promise<string[]> { | ||||
|     async getSiteIdsFromUrl(url: string, prioritize?: boolean, username?: string): Promise<string[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         // If prioritize is true, check current site first.
 | ||||
|         if (prioritize && this.currentSite && this.currentSite.containsUrl(url)) { | ||||
|             if (!username || this.currentSite.getInfo().username == username) { | ||||
|                 return Promise.resolve([this.currentSite.getId()]); | ||||
|                 return [this.currentSite.getId()]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -1489,18 +1538,19 @@ export class CoreSitesProvider { | ||||
|             // URL doesn't have http(s) protocol. Check if it has any protocol.
 | ||||
|             if (this.urlUtils.isAbsoluteURL(url)) { | ||||
|                 // It has some protocol. Return empty array.
 | ||||
|                 return Promise.resolve([]); | ||||
|                 return []; | ||||
|             } else { | ||||
|                 // No protocol, probably a relative URL. Return current site.
 | ||||
|                 if (this.currentSite) { | ||||
|                     return Promise.resolve([this.currentSite.getId()]); | ||||
|                     return [this.currentSite.getId()]; | ||||
|                 } else { | ||||
|                     return Promise.resolve([]); | ||||
|                     return []; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return this.appDB.getAllRecords(this.SITES_TABLE).then((siteEntries) => { | ||||
|         try { | ||||
|             const siteEntries = await this.appDB.getAllRecords(this.SITES_TABLE); | ||||
|             const ids = []; | ||||
|             const promises = []; | ||||
| 
 | ||||
| @ -1516,13 +1566,13 @@ export class CoreSitesProvider { | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             return Promise.all(promises).then(() => { | ||||
|                 return ids; | ||||
|             }); | ||||
|         }).catch(() => { | ||||
|             await Promise.all(promises); | ||||
| 
 | ||||
|             return ids; | ||||
|         } catch (error) { | ||||
|             // Shouldn't happen.
 | ||||
|             return []; | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1530,10 +1580,12 @@ export class CoreSitesProvider { | ||||
|      * | ||||
|      * @return Promise resolved with the site ID. | ||||
|      */ | ||||
|     getStoredCurrentSiteId(): Promise<string> { | ||||
|         return this.appDB.getRecord(this.CURRENT_SITE_TABLE, { id: 1 }).then((currentSite) => { | ||||
|             return currentSite.siteId; | ||||
|         }); | ||||
|     async getStoredCurrentSiteId(): Promise<string> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const currentSite = await this.appDB.getRecord(this.CURRENT_SITE_TABLE, { id: 1 }); | ||||
| 
 | ||||
|         return currentSite.siteId; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1580,6 +1632,7 @@ export class CoreSitesProvider { | ||||
|      * Create a table in all the sites databases. | ||||
|      * | ||||
|      * @param table Table schema. | ||||
|      * @deprecated. Please use registerSiteSchema instead. | ||||
|      */ | ||||
|     createTableFromSchema(table: SQLiteDBTableSchema): void { | ||||
|         this.createTablesFromSchema([table]); | ||||
| @ -1589,6 +1642,7 @@ export class CoreSitesProvider { | ||||
|      * Create several tables in all the sites databases. | ||||
|      * | ||||
|      * @param tables List of tables schema. | ||||
|      * @deprecated. Please use registerSiteSchema instead. | ||||
|      */ | ||||
|     createTablesFromSchema(tables: SQLiteDBTableSchema[]): void { | ||||
|         // Add the tables to the list of schemas. This list is to create all the tables in new sites.
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user