2019-09-26 10:43:29 +00:00
|
|
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
2017-11-21 08:56:16 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
import { Injectable } from '@angular/core';
|
2018-05-30 10:13:39 +00:00
|
|
|
import { CoreAppProvider } from './app';
|
2017-11-21 08:56:16 +00:00
|
|
|
import { CoreConfigProvider } from './config';
|
|
|
|
import { CoreFilepoolProvider } from './filepool';
|
|
|
|
import { CoreInitHandler, CoreInitDelegate } from './init';
|
|
|
|
import { CoreLocalNotificationsProvider } from './local-notifications';
|
|
|
|
import { CoreLoggerProvider } from './logger';
|
|
|
|
import { CoreSitesProvider } from './sites';
|
2018-05-30 10:13:39 +00:00
|
|
|
import { CoreUtilsProvider } from './utils/utils';
|
2019-02-08 09:32:21 +00:00
|
|
|
import { CoreTimeUtilsProvider } from './utils/time';
|
2017-11-21 08:56:16 +00:00
|
|
|
import { CoreConfigConstants } from '../configconstants';
|
2018-06-05 06:11:40 +00:00
|
|
|
import { AddonCalendarProvider } from '@addon/calendar/providers/calendar';
|
2018-05-30 10:13:39 +00:00
|
|
|
import { SQLiteDB } from '@classes/sqlitedb';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data to migrate a store of Ionic 1 app to the SQLite DB.
|
|
|
|
*/
|
|
|
|
export interface CoreUpdateManagerMigrateTable {
|
|
|
|
/**
|
|
|
|
* Current name of the store/table.
|
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* New name of the table. If not defined, "name" will be used.
|
|
|
|
*/
|
|
|
|
newName?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An object to rename and convert some fields of the table/store.
|
|
|
|
*/
|
|
|
|
fields?: {
|
|
|
|
name: string, // Field name in the old app.
|
|
|
|
newName?: string, // New field name. If not provided, keep the same name.
|
|
|
|
type?: string, // Type of the field if it needs to be treated: 'any', 'object', 'date', 'boolean'.
|
|
|
|
delete?: boolean // Whether the field must be deleted because it isn't needed in the new schema.
|
|
|
|
}[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If set, all the fields that aren't in this array will be deleted. The names in this list should be the new names.
|
|
|
|
*/
|
|
|
|
filterFields?: string[];
|
|
|
|
}
|
2017-11-21 08:56:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Factory to handle app updates. This factory shouldn't be used outside of core.
|
|
|
|
*
|
|
|
|
* This service handles processes that need to be run when updating the app, like migrate Ionic 1 database data to Ionic 3.
|
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|
|
|
// Data for init delegate.
|
2018-01-29 09:05:20 +00:00
|
|
|
name = 'CoreUpdateManager';
|
|
|
|
priority = CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 300;
|
|
|
|
blocking = true;
|
2017-11-21 08:56:16 +00:00
|
|
|
|
|
|
|
protected VERSION_APPLIED = 'version_applied';
|
|
|
|
protected logger;
|
2018-06-14 07:59:34 +00:00
|
|
|
protected localNotificationsComponentsMigrate: {[old: string]: string} = {};
|
2017-11-21 08:56:16 +00:00
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
/**
|
|
|
|
* Tables to migrate from app DB ('MoodleMobile'). Include all the core ones to decrease the dependencies.
|
|
|
|
*/
|
|
|
|
protected appDBTables: CoreUpdateManagerMigrateTable[] = [
|
|
|
|
{
|
|
|
|
name: 'config',
|
|
|
|
newName: 'core_config',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'value',
|
|
|
|
type: 'any'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'cron'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'current_site',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'siteid',
|
|
|
|
newName: 'siteId'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'desktop_local_notifications',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'data',
|
|
|
|
type: 'object'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'triggered',
|
|
|
|
type: 'boolean'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
filterFields: ['id', 'title', 'text', 'at', 'data', 'triggered']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'files_queue',
|
|
|
|
newName: 'filepool_files_queue',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'isexternalfile',
|
|
|
|
type: 'boolean'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'links',
|
|
|
|
type: 'object'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'sortorder',
|
|
|
|
delete: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'notification_components'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'notification_sites'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'notifications_triggered'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'shared_files'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'sites',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'siteurl',
|
|
|
|
newName: 'siteUrl'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'infos',
|
|
|
|
newName: 'info',
|
|
|
|
type: 'object'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'privatetoken',
|
|
|
|
newName: 'privateToken'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'config',
|
|
|
|
type: 'object'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'loggedout',
|
|
|
|
newName: 'loggedOut'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2018-05-31 06:47:20 +00:00
|
|
|
/**
|
|
|
|
* Tables to migrate from each site DB. Include all the core ones to decrease the dependencies.
|
|
|
|
*/
|
|
|
|
protected siteDBTables: CoreUpdateManagerMigrateTable[] = [
|
|
|
|
{
|
|
|
|
name: 'check_updates_times',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'courseid',
|
|
|
|
newName: 'courseId'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'course_status',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'previous',
|
|
|
|
newName: 'previousStatus'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'downloadtime',
|
|
|
|
newName: 'downloadTime'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'previousdownloadtime',
|
|
|
|
newName: 'previousDownloadTime'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'filepool',
|
|
|
|
newName: 'filepool_files',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'stale',
|
|
|
|
type: 'boolean'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'downloaded',
|
|
|
|
newName: 'downloadTime'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'isexternalfile',
|
|
|
|
type: 'boolean'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'files_links',
|
|
|
|
newName: 'filepool_files_links',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'componentAndId',
|
|
|
|
delete: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'filepool_packages',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'downloadtime',
|
|
|
|
newName: 'downloadTime'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'previousdownloadtime',
|
|
|
|
newName: 'previousDownloadTime'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'revision', // Move the value of 'revision' to 'extra' so SCORMs keep working.
|
|
|
|
newName: 'extra'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'timemodified',
|
|
|
|
delete: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'mm_emulator_last_received_notification',
|
|
|
|
newName: 'core_emulator_last_received_notification',
|
|
|
|
filterFields: ['component', 'id', 'timecreated']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'questions',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'componentId',
|
|
|
|
newName: 'componentid'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'componentAndAttempt',
|
|
|
|
delete: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'componentAndComponentId',
|
|
|
|
delete: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'question_answers',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'componentId',
|
|
|
|
newName: 'componentid'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'componentAndAttempt',
|
|
|
|
delete: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'componentAndComponentId',
|
|
|
|
delete: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'componentAndAttemptAndQuestion',
|
|
|
|
delete: true
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'sync'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'users'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'wscache',
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'data',
|
|
|
|
type: 'object'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'expirationtime',
|
|
|
|
newName: 'expirationTime'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
2017-11-21 08:56:16 +00:00
|
|
|
constructor(logger: CoreLoggerProvider, private configProvider: CoreConfigProvider, private sitesProvider: CoreSitesProvider,
|
2018-05-30 10:13:39 +00:00
|
|
|
private filepoolProvider: CoreFilepoolProvider, private notifProvider: CoreLocalNotificationsProvider,
|
2019-02-08 09:32:21 +00:00
|
|
|
private utils: CoreUtilsProvider, private appProvider: CoreAppProvider, private timeUtils: CoreTimeUtilsProvider,
|
2018-06-05 06:11:40 +00:00
|
|
|
private calendarProvider: AddonCalendarProvider) {
|
2017-11-21 08:56:16 +00:00
|
|
|
this.logger = logger.getInstance('CoreUpdateManagerProvider');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the app has been updated and performs the needed processes.
|
|
|
|
* This function shouldn't be used outside of core.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when the update process finishes.
|
2017-11-21 08:56:16 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
load(): Promise<any> {
|
|
|
|
const promises = [],
|
2017-11-21 08:56:16 +00:00
|
|
|
versionCode = CoreConfigConstants.versioncode;
|
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
return this.configProvider.get(this.VERSION_APPLIED, 0).then((versionApplied) => {
|
|
|
|
if (!versionApplied) {
|
|
|
|
// No version applied, either the app was just installed or it's being updated from Ionic 1.
|
|
|
|
return this.migrateAllDBs().then(() => {
|
2018-06-14 07:59:34 +00:00
|
|
|
// Now that the DBs have been migrated, migrate the local notification components names.
|
|
|
|
return this.migrateLocalNotificationsComponents();
|
|
|
|
}).then(() => {
|
2018-05-30 10:13:39 +00:00
|
|
|
// DBs migrated, get the version applied again.
|
|
|
|
return this.configProvider.get(this.VERSION_APPLIED, 0);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return versionApplied;
|
|
|
|
}
|
|
|
|
}).then((versionApplied: number) => {
|
2017-11-21 08:56:16 +00:00
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
if (versionCode >= 2013 && versionApplied < 2013 && versionApplied > 0) {
|
2017-11-21 08:56:16 +00:00
|
|
|
promises.push(this.migrateFileExtensions());
|
|
|
|
}
|
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
if (versionCode >= 2017 && versionApplied < 2017 && versionApplied > 0) {
|
2017-11-21 08:56:16 +00:00
|
|
|
promises.push(this.setCalendarDefaultNotifTime());
|
|
|
|
promises.push(this.setSitesConfig());
|
|
|
|
}
|
|
|
|
|
2018-06-05 06:11:40 +00:00
|
|
|
// In version 2018 we adapted the forum offline stores to match a new schema.
|
|
|
|
// However, due to the migration of data to SQLite we can no longer do that.
|
2017-11-21 08:56:16 +00:00
|
|
|
|
2018-06-19 11:16:16 +00:00
|
|
|
if (versionCode >= 3500 && versionApplied < 3500 && versionApplied > 0) {
|
|
|
|
promises.push(this.logoutLegacySites());
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:16 +00:00
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
return this.configProvider.set(this.VERSION_APPLIED, versionCode);
|
|
|
|
}).catch((error) => {
|
|
|
|
this.logger.error(`Error applying update from ${versionApplied} to ${versionCode}`, error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
/**
|
2018-05-31 10:57:40 +00:00
|
|
|
* Register several app tables to be migrated to the new schema.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param tables The tables to migrate.
|
2018-05-31 10:57:40 +00:00
|
|
|
*/
|
|
|
|
registerAppTablesMigration(tables: CoreUpdateManagerMigrateTable[]): void {
|
|
|
|
tables.forEach((table) => {
|
|
|
|
this.registerAppTableMigration(table);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register an app table to be migrated to the new schema.
|
2018-05-30 10:13:39 +00:00
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param table The table to migrate.
|
2018-05-30 10:13:39 +00:00
|
|
|
*/
|
|
|
|
registerAppTableMigration(table: CoreUpdateManagerMigrateTable): void {
|
|
|
|
this.appDBTables.push(table);
|
|
|
|
}
|
|
|
|
|
2018-05-31 10:57:40 +00:00
|
|
|
/**
|
|
|
|
* Register several site tables to be migrated to the new schema.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param tables The tables to migrate.
|
2018-05-31 10:57:40 +00:00
|
|
|
*/
|
|
|
|
registerSiteTablesMigration(tables: CoreUpdateManagerMigrateTable[]): void {
|
|
|
|
tables.forEach((table) => {
|
|
|
|
this.registerSiteTableMigration(table);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a site table to be migrated to the new schema.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param table The table to migrate.
|
2018-05-31 10:57:40 +00:00
|
|
|
*/
|
|
|
|
registerSiteTableMigration(table: CoreUpdateManagerMigrateTable): void {
|
|
|
|
this.siteDBTables.push(table);
|
|
|
|
}
|
|
|
|
|
2018-06-14 07:59:34 +00:00
|
|
|
/**
|
|
|
|
* Register a migration of component name for local notifications.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param oldName The old name.
|
|
|
|
* @param newName The new name.
|
2018-06-14 07:59:34 +00:00
|
|
|
*/
|
|
|
|
registerLocalNotifComponentMigration(oldName: string, newName: string): void {
|
|
|
|
this.localNotificationsComponentsMigrate[oldName] = newName;
|
|
|
|
}
|
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
/**
|
|
|
|
* Migrate all DBs and tables from the old format to SQLite.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when done.
|
2018-05-30 10:13:39 +00:00
|
|
|
*/
|
|
|
|
protected migrateAllDBs(): Promise<any> {
|
|
|
|
if (!(<any> window).ydn) {
|
|
|
|
// The ydn-db library is not loaded, stop.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
// First migrate the app DB.
|
|
|
|
return this.migrateAppDB().then(() => {
|
2018-05-31 06:47:20 +00:00
|
|
|
// Now migrate all site DBs.
|
|
|
|
return this.sitesProvider.getSitesIds();
|
|
|
|
}).then((ids) => {
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
ids.forEach((id) => {
|
|
|
|
promises.push(this.migrateSiteDB(id));
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises);
|
2018-05-30 10:13:39 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Migrate the app DB.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when done.
|
2018-05-30 10:13:39 +00:00
|
|
|
*/
|
|
|
|
protected migrateAppDB(): Promise<any> {
|
|
|
|
const oldDb = new (<any> window).ydn.db.Storage('MoodleMobile'),
|
|
|
|
newDb = this.appProvider.getDB();
|
|
|
|
|
|
|
|
return this.migrateDB(oldDb, newDb, this.appDBTables);
|
|
|
|
}
|
|
|
|
|
2018-05-31 06:47:20 +00:00
|
|
|
/**
|
|
|
|
* Migrate the DB of a certain site.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param siteId The site ID.
|
|
|
|
* @return Promise resolved when done.
|
2018-05-31 06:47:20 +00:00
|
|
|
*/
|
|
|
|
protected migrateSiteDB(siteId: string): Promise<any> {
|
|
|
|
// Get the site DB.
|
|
|
|
return this.sitesProvider.getSiteDb(siteId).then((newDb) => {
|
|
|
|
const oldDb = new (<any> window).ydn.db.Storage('Site-' + siteId);
|
|
|
|
|
|
|
|
return this.migrateDB(oldDb, newDb, this.siteDBTables);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-30 10:13:39 +00:00
|
|
|
/**
|
|
|
|
* Migrate all the tables of a certain DB to the SQLite DB.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param oldDb The old DB (created using ydn-db).
|
|
|
|
* @param newDb The new DB.
|
|
|
|
* @param tables The tables to migrate.
|
|
|
|
* @return Promise resolved when done.
|
2018-05-30 10:13:39 +00:00
|
|
|
*/
|
|
|
|
protected migrateDB(oldDb: any, newDb: SQLiteDB, tables: CoreUpdateManagerMigrateTable[]): Promise<any> {
|
|
|
|
if (!oldDb || !newDb) {
|
|
|
|
// Some of the DBs doesn't exist, stop.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
tables.forEach((table) => {
|
|
|
|
|
|
|
|
// Get current values.
|
|
|
|
promises.push(Promise.resolve(oldDb.values(table.name, undefined, 99999999)).then((entries) => {
|
|
|
|
const fields = table.fields || [],
|
|
|
|
filterFields = table.filterFields || [];
|
|
|
|
|
|
|
|
// Treat the entries.
|
|
|
|
for (let i = 0; i < entries.length; i++) {
|
|
|
|
const entry = entries[i];
|
|
|
|
|
|
|
|
// Convert and rename the fields to match the new schema.
|
|
|
|
fields.forEach((field) => {
|
|
|
|
const value = entry[field.name];
|
|
|
|
|
|
|
|
// Convert the field to the right format.
|
|
|
|
if (field.type == 'object' || (field.type == 'any' && typeof value == 'object')) {
|
|
|
|
entry[field.name] = JSON.stringify(value);
|
|
|
|
} else if (field.type == 'date' && value) {
|
|
|
|
entry[field.name] = value.getTime();
|
|
|
|
} else if (field.type == 'boolean' || (field.type == 'any' && typeof value == 'boolean')) {
|
|
|
|
entry[field.name] = value ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (field.newName) {
|
|
|
|
// Rename the field.
|
|
|
|
entry[field.newName] = entry[field.name];
|
|
|
|
delete entry[field.name];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (field.delete) {
|
|
|
|
// Delete the field.
|
|
|
|
delete entry[field.name];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Remove invalid and unneeded properties.
|
|
|
|
for (const name in entry) {
|
|
|
|
if (name.indexOf('$') === 0) {
|
|
|
|
// Property not valid, remove.
|
|
|
|
delete entry[name];
|
|
|
|
|
|
|
|
} else if (filterFields.length && filterFields.indexOf(name) == -1) {
|
|
|
|
// The property isn't present in filterFields, remove it.
|
|
|
|
delete entry[name];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now store the entries in the new DB.
|
|
|
|
return newDb.insertRecords(table.newName || table.name, entries);
|
|
|
|
}).catch((error) => {
|
|
|
|
this.logger.error('Error migrating table ' + table.name + ' to ' + (table.newName || table.name) + ': ', error);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises);
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:16 +00:00
|
|
|
/**
|
|
|
|
* Migrates files filling extensions.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when the site migration is finished.
|
2017-11-21 08:56:16 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected migrateFileExtensions(): Promise<any> {
|
2017-11-21 08:56:16 +00:00
|
|
|
return this.sitesProvider.getSitesIds().then((sites) => {
|
2018-01-29 09:05:20 +00:00
|
|
|
const promises = [];
|
2017-11-21 08:56:16 +00:00
|
|
|
sites.forEach((siteId) => {
|
|
|
|
promises.push(this.filepoolProvider.fillMissingExtensionInFiles(siteId));
|
|
|
|
});
|
|
|
|
promises.push(this.filepoolProvider.treatExtensionInQueue());
|
2018-01-29 09:05:20 +00:00
|
|
|
|
2017-11-21 08:56:16 +00:00
|
|
|
return Promise.all(promises);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-06-14 07:59:34 +00:00
|
|
|
/**
|
|
|
|
* Migrate local notifications components from the old nomenclature to the new one.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when done.
|
2018-06-14 07:59:34 +00:00
|
|
|
*/
|
|
|
|
protected migrateLocalNotificationsComponents(): Promise<any> {
|
|
|
|
if (!this.notifProvider.isAvailable()) {
|
|
|
|
// Local notifications not available, nothing to do.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
for (const oldName in this.localNotificationsComponentsMigrate) {
|
|
|
|
const newName = this.localNotificationsComponentsMigrate[oldName];
|
|
|
|
|
|
|
|
promises.push(this.notifProvider.updateComponentName(oldName, newName).catch((error) => {
|
|
|
|
this.logger.error('Error migrating local notif component from ' + oldName + ' to ' + newName + ': ', error);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
2017-11-21 08:56:16 +00:00
|
|
|
/**
|
|
|
|
* Calendar default notification time is configurable from version 3.2.1, and a new option "Default" is added.
|
|
|
|
* All events that were configured to use the fixed default time should now be configured to use "Default" option.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when the events are configured.
|
2017-11-21 08:56:16 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected setCalendarDefaultNotifTime(): Promise<any> {
|
2017-11-21 08:56:16 +00:00
|
|
|
if (!this.notifProvider.isAvailable()) {
|
|
|
|
// Local notifications not available, nothing to do.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2019-02-08 09:32:21 +00:00
|
|
|
const now = this.timeUtils.timestamp();
|
|
|
|
|
2018-06-05 06:11:40 +00:00
|
|
|
return this.sitesProvider.getSitesIds().then((siteIds) => {
|
|
|
|
|
|
|
|
const promises = [];
|
|
|
|
siteIds.forEach((siteId) => {
|
|
|
|
// Get stored events.
|
|
|
|
promises.push(this.calendarProvider.getAllEventsFromLocalDb(siteId).then((events) => {
|
|
|
|
const eventPromises = [];
|
|
|
|
|
|
|
|
events.forEach((event) => {
|
2019-02-08 09:32:21 +00:00
|
|
|
if (event.notificationtime && event.notificationtime == AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME) {
|
|
|
|
eventPromises.push(this.calendarProvider.addEventReminder(event, -1, siteId));
|
|
|
|
} else if (event.notificationtime && event.notificationtime > 0) {
|
|
|
|
const time = event.timestart - event.notificationtime * 60;
|
|
|
|
|
|
|
|
if (time < now) {
|
|
|
|
// Old reminder, just not add this.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
eventPromises.push(this.calendarProvider.addEventReminder(event, time, siteId));
|
2018-06-05 06:11:40 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(eventPromises);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises);
|
|
|
|
});
|
2017-11-21 08:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* In version 3.2.1 we want the site config to be stored in each site if available.
|
|
|
|
* Since it can be slow, we'll only block retrieving the config of current site, the rest will be in background.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when the config is loaded for the current site (if any).
|
2017-11-21 08:56:16 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected setSitesConfig(): Promise<any> {
|
2017-11-21 08:56:16 +00:00
|
|
|
return this.sitesProvider.getSitesIds().then((siteIds) => {
|
|
|
|
|
|
|
|
return this.sitesProvider.getStoredCurrentSiteId().catch(() => {
|
|
|
|
// Error getting current site.
|
|
|
|
}).then((currentSiteId) => {
|
|
|
|
let promise;
|
|
|
|
|
|
|
|
// Load the config of current site first.
|
|
|
|
if (currentSiteId) {
|
|
|
|
promise = this.setSiteConfig(currentSiteId);
|
|
|
|
} else {
|
|
|
|
promise = Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the config of rest of sites in background.
|
|
|
|
siteIds.forEach((siteId) => {
|
|
|
|
if (siteId != currentSiteId) {
|
|
|
|
this.setSiteConfig(siteId);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Store the config of a site.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @param siteId Site ID.
|
|
|
|
* @return Promise resolved when the config is loaded for the site.
|
2017-11-21 08:56:16 +00:00
|
|
|
*/
|
2018-01-29 09:05:20 +00:00
|
|
|
protected setSiteConfig(siteId: string): Promise<any> {
|
2017-11-21 08:56:16 +00:00
|
|
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
2018-01-29 09:05:20 +00:00
|
|
|
if (site.getStoredConfig() || !site.wsAvailable('tool_mobile_get_config')) {
|
2017-11-21 08:56:16 +00:00
|
|
|
// Site already has the config or it cannot be retrieved. Stop.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the site config.
|
|
|
|
return site.getConfig().then((config) => {
|
|
|
|
return this.sitesProvider.addSite(
|
2018-01-29 09:05:20 +00:00
|
|
|
site.getId(), site.getURL(), site.getToken(), site.getInfo(), site.getPrivateToken(), config);
|
2017-11-21 08:56:16 +00:00
|
|
|
}).catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2018-06-19 11:16:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Logout from legacy sites.
|
|
|
|
*
|
2019-09-10 14:48:56 +00:00
|
|
|
* @return Promise resolved when done.
|
2018-06-19 11:16:16 +00:00
|
|
|
*/
|
|
|
|
protected logoutLegacySites(): Promise<any> {
|
|
|
|
return this.sitesProvider.getSitesIds().then((siteIds) => {
|
|
|
|
const promises = [];
|
|
|
|
|
|
|
|
siteIds.forEach((siteId) => {
|
|
|
|
promises.push(this.sitesProvider.getSite(siteId).then((site) => {
|
|
|
|
// If the site is a legacy site, mark it as logged out so the user has to authenticate again.
|
|
|
|
if (this.sitesProvider.isLegacyMoodleByInfo(site.getInfo())) {
|
|
|
|
return this.sitesProvider.setSiteLoggedOut(site.getId(), true);
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises);
|
|
|
|
});
|
|
|
|
}
|
2017-11-21 08:56:16 +00:00
|
|
|
}
|