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