MOBILE-2261 core: Implement filepool and file session
parent
39a1623f8d
commit
e216341838
|
@ -46,6 +46,8 @@ import { CoreSitesProvider } from '../providers/sites';
|
||||||
import { CoreLocalNotificationsProvider } from '../providers/local-notifications';
|
import { CoreLocalNotificationsProvider } from '../providers/local-notifications';
|
||||||
import { CoreGroupsProvider } from '../providers/groups';
|
import { CoreGroupsProvider } from '../providers/groups';
|
||||||
import { CoreCronDelegate } from '../providers/cron';
|
import { CoreCronDelegate } from '../providers/cron';
|
||||||
|
import { CoreFileSessionProvider } from '../providers/file-session';
|
||||||
|
import { CoreFilepoolProvider } from '../providers/filepool';
|
||||||
|
|
||||||
// For translate loader. AoT requires an exported function for factories.
|
// For translate loader. AoT requires an exported function for factories.
|
||||||
export function createTranslateLoader(http: HttpClient) {
|
export function createTranslateLoader(http: HttpClient) {
|
||||||
|
@ -99,6 +101,8 @@ export function createTranslateLoader(http: HttpClient) {
|
||||||
CoreLocalNotificationsProvider,
|
CoreLocalNotificationsProvider,
|
||||||
CoreGroupsProvider,
|
CoreGroupsProvider,
|
||||||
CoreCronDelegate,
|
CoreCronDelegate,
|
||||||
|
CoreFileSessionProvider,
|
||||||
|
CoreFilepoolProvider,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|
|
@ -56,6 +56,8 @@ export interface LocalMobileResponse {
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
* the tables are created in all the sites, not just the current one.
|
||||||
*/
|
*/
|
||||||
export class CoreSite {
|
export class CoreSite {
|
||||||
// List of injected services. This class isn't injectable, so it cannot use DI.
|
// List of injected services. This class isn't injectable, so it cannot use DI.
|
||||||
|
|
|
@ -101,7 +101,7 @@ export class SQLiteDB {
|
||||||
|
|
||||||
columnsSql.push(columnSql);
|
columnsSql.push(columnSql);
|
||||||
}
|
}
|
||||||
sql += columnsSql.join(', ') + ')';
|
sql += columnsSql.join(', ');
|
||||||
|
|
||||||
// Now add the table constraints.
|
// Now add the table constraints.
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ export class SQLiteDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sql;
|
return sql + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,9 +240,9 @@ export class SQLiteDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a table if it doesn't exist from a schema.
|
* Create several tables if they don't exist from a list of schemas.
|
||||||
*
|
*
|
||||||
* @param {any} table Table schema.
|
* @param {any[]} tables List of table schema.
|
||||||
* @return {Promise<any>} Promise resolved when success.
|
* @return {Promise<any>} Promise resolved when success.
|
||||||
*/
|
*/
|
||||||
createTablesFromSchema(tables: any[]) : Promise<any> {
|
createTablesFromSchema(tables: any[]) : Promise<any> {
|
||||||
|
@ -394,7 +394,7 @@ export class SQLiteDB {
|
||||||
* @param {any} items A single value or array of values for the expression. It doesn't accept objects.
|
* @param {any} items A single value or array of values for the expression. It doesn't accept objects.
|
||||||
* @param {boolean} [equal=true] True means we want to equate to the constructed expression.
|
* @param {boolean} [equal=true] True means we want to equate to the constructed expression.
|
||||||
* @param {any} [onEmptyItems] This defines the behavior when the array of items provided is empty. Defaults to false,
|
* @param {any} [onEmptyItems] This defines the behavior when the array of items provided is empty. Defaults to false,
|
||||||
* meaning throw exceptions. Other values will become part of the returned SQL fragment.
|
* meaning return empty. Other values will become part of the returned SQL fragment.
|
||||||
* @return {any[]} A list containing the constructed sql fragment and an array of parameters.
|
* @return {any[]} A list containing the constructed sql fragment and an array of parameters.
|
||||||
*/
|
*/
|
||||||
getInOrEqual(items: any, equal=true, onEmptyItems?: any) : any[] {
|
getInOrEqual(items: any, equal=true, onEmptyItems?: any) : any[] {
|
||||||
|
@ -774,7 +774,7 @@ export class SQLiteDB {
|
||||||
sql,
|
sql,
|
||||||
params;
|
params;
|
||||||
|
|
||||||
for (var key in data) {
|
for (let key in data) {
|
||||||
sets.push(`${key} = ?`);
|
sets.push(`${key} = ?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,6 +785,43 @@ export class SQLiteDB {
|
||||||
return this.execute(sql, params);
|
return this.execute(sql, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update one or more records in a table. It accepts a WHERE clause as a string.
|
||||||
|
*
|
||||||
|
* @param {string} string table The database table to update.
|
||||||
|
* @param {any} data An object with the fields to update: fieldname=>fieldvalue.
|
||||||
|
* @param {string} [where] Where clause. Must not include the "WHERE" word.
|
||||||
|
* @param {any[]} [whereParams] Params for the where clause.
|
||||||
|
* @return {Promise<any>} Promise resolved when updated.
|
||||||
|
*/
|
||||||
|
updateRecordsWhere(table: string, data: any, where?: string, whereParams?: any[]) : Promise<any> {
|
||||||
|
if (!data || !Object.keys(data).length) {
|
||||||
|
// No fields to update, consider it's done.
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let sets = [],
|
||||||
|
sql,
|
||||||
|
params;
|
||||||
|
|
||||||
|
for (let key in data) {
|
||||||
|
sets.push(`${key} = ?`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = `UPDATE ${table} SET ${sets.join(', ')}`;
|
||||||
|
if (where) {
|
||||||
|
sql += ` WHERE ${where}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the list of params using the "data" object and the params for the where clause.
|
||||||
|
params = Object.keys(data).map(key => data[key]);
|
||||||
|
if (where && whereParams) {
|
||||||
|
params = params.concat(whereParams[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.execute(sql, params);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the SQL WHERE conditions.
|
* Returns the SQL WHERE conditions.
|
||||||
*
|
*
|
||||||
|
|
|
@ -37,4 +37,11 @@ export class CoreConstants {
|
||||||
public static loginSSOCode = 2; // SSO in browser window is required.
|
public static loginSSOCode = 2; // SSO in browser window is required.
|
||||||
public static loginSSOInAppCode = 3; // SSO in embedded browser is required.
|
public static loginSSOInAppCode = 3; // SSO in embedded browser is required.
|
||||||
public static loginLaunchData = 'mmLoginLaunchData';
|
public static loginLaunchData = 'mmLoginLaunchData';
|
||||||
|
|
||||||
|
// Download status constants.
|
||||||
|
public static downloaded = 'downloaded';
|
||||||
|
public static downloading = 'downloading';
|
||||||
|
public static notDownloaded = 'notdownloaded';
|
||||||
|
public static outdated = 'outdated';
|
||||||
|
public static notDownloadable = 'notdownloadable';
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,9 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
this.db.transaction((tx) => {
|
this.db.transaction((tx) => {
|
||||||
tx.executeSql(sql, params, (tx, results) => {
|
tx.executeSql(sql, params, (tx, results) => {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
}, reject);
|
}, (tx, error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreSitesProvider } from './sites';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to store some temporary data for file submission.
|
||||||
|
*
|
||||||
|
* It uses siteId and component name to index the files.
|
||||||
|
* Every component can provide a File area identifier to indentify every file list on the session.
|
||||||
|
* This value can be the activity id or a mix of name and numbers.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreFileSessionProvider {
|
||||||
|
protected files = {};
|
||||||
|
|
||||||
|
constructor(private sitesProvider: CoreSitesProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a file to the session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {any} file File to add.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
addFile(component: string, id: string|number, file: any, siteId?: string) : void {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
this.initFileArea(component, id, siteId);
|
||||||
|
|
||||||
|
this.files[siteId][component][id].push(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear files stored in session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
clearFiles(component: string, id: string|number, siteId?: string) : void {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
if (this.files[siteId] && this.files[siteId][component] && this.files[siteId][component][id]) {
|
||||||
|
this.files[siteId][component][id] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get files stored in session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {any[]} Array of files in session.
|
||||||
|
*/
|
||||||
|
getFiles(component: string, id: string|number, siteId?: string) : any[] {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
if (this.files[siteId] && this.files[siteId][component] && this.files[siteId][component][id]) {
|
||||||
|
return this.files[siteId][component][id];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the filearea to store the file.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
protected initFileArea(component: string, id: string|number, siteId?: string) : void {
|
||||||
|
if (!this.files[siteId]) {
|
||||||
|
this.files[siteId] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.files[siteId][component]) {
|
||||||
|
this.files[siteId][component] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.files[siteId][component][id]) {
|
||||||
|
this.files[siteId][component][id] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file stored in session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {any} file File to remove. The instance should be exactly the same as the one stored in session.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
removeFile(component: string, id: string|number, file: any, siteId?: string) : void {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
if (this.files[siteId] && this.files[siteId][component] && this.files[siteId][component][id]) {
|
||||||
|
const position = this.files[siteId][component][id].indexOf(file);
|
||||||
|
if (position != -1) {
|
||||||
|
this.files[siteId][component][id].splice(position, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file stored in session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {number} index Position of the file to remove.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
removeFileByIndex(component: string, id: string|number, index: number, siteId?: string) : void {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
if (this.files[siteId] && this.files[siteId][component] && this.files[siteId][component][id] && index >= 0 &&
|
||||||
|
index < this.files[siteId][component][id].length) {
|
||||||
|
this.files[siteId][component][id].splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a group of files in the session.
|
||||||
|
*
|
||||||
|
* @param {string} component Component Name.
|
||||||
|
* @param {string|number} id File area identifier.
|
||||||
|
* @param {any[]} newFiles Files to set.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
*/
|
||||||
|
setFiles(component: string, id: string|number, newFiles: any[], siteId?: string) : void {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
this.initFileArea(component, id, siteId);
|
||||||
|
|
||||||
|
this.files[siteId][component][id] = newFiles;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -51,13 +51,21 @@ export interface CoreSiteBasicInfo {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Service to manage and interact with sites.
|
* Service to manage and interact with sites.
|
||||||
|
* It allows creating tables in the databases of all sites. Each service or component should be responsible of creating
|
||||||
|
* their own database tables. Example:
|
||||||
|
*
|
||||||
|
* constructor(sitesProvider: CoreSitesProvider) {
|
||||||
|
* this.sitesProvider.createTableFromSchema(this.tableSchema);
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreSitesProvider {
|
export class CoreSitesProvider {
|
||||||
// Variables for the database.
|
// Variables for the database.
|
||||||
protected SITES_TABLE = 'sites';
|
protected SITES_TABLE = 'sites';
|
||||||
protected CURRENT_SITE_TABLE = 'current_site';
|
protected CURRENT_SITE_TABLE = 'current_site';
|
||||||
protected tablesSchema = [
|
protected appTablesSchema = [
|
||||||
{
|
{
|
||||||
name: this.SITES_TABLE,
|
name: this.SITES_TABLE,
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -122,6 +130,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 siteTablesSchemas = []; // Schemas for site tables. Other providers can add schemas in here.
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private http: HttpClient, private sitesFactory: CoreSitesFactoryProvider,
|
constructor(logger: CoreLoggerProvider, private http: HttpClient, private sitesFactory: CoreSitesFactoryProvider,
|
||||||
private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private translate: TranslateService,
|
private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private translate: TranslateService,
|
||||||
|
@ -129,7 +138,7 @@ 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.tablesSchema);
|
this.appDB.createTablesFromSchema(this.appTablesSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -383,10 +392,16 @@ export class CoreSitesProvider {
|
||||||
this.addSite(siteId, siteUrl, token, info, privateToken, config);
|
this.addSite(siteId, siteUrl, token, info, privateToken, config);
|
||||||
// Turn candidate site into current site.
|
// Turn candidate site into current site.
|
||||||
this.currentSite = candidateSite;
|
this.currentSite = candidateSite;
|
||||||
|
this.sites[siteId] = candidateSite;
|
||||||
// Store session.
|
// Store session.
|
||||||
this.login(siteId);
|
this.login(siteId);
|
||||||
this.eventsProvider.trigger(CoreEventsProvider.SITE_ADDED, siteId);
|
this.eventsProvider.trigger(CoreEventsProvider.SITE_ADDED, siteId);
|
||||||
|
|
||||||
|
if (this.siteTablesSchemas.length) {
|
||||||
|
// Create tables in the site's database.
|
||||||
|
candidateSite.getDb().createTablesFromSchema(this.siteTablesSchemas);
|
||||||
|
}
|
||||||
|
|
||||||
return siteId;
|
return siteId;
|
||||||
});
|
});
|
||||||
} else if (result == this.LEGACY_APP_VERSION) {
|
} else if (result == this.LEGACY_APP_VERSION) {
|
||||||
|
@ -680,6 +695,11 @@ export class CoreSitesProvider {
|
||||||
site = this.sitesFactory.makeSite(entry.id, entry.siteUrl, entry.token,
|
site = this.sitesFactory.makeSite(entry.id, entry.siteUrl, entry.token,
|
||||||
info, entry.privateToken, config, entry.loggedOut == 1);
|
info, entry.privateToken, config, entry.loggedOut == 1);
|
||||||
this.sites[entry.id] = site;
|
this.sites[entry.id] = site;
|
||||||
|
if (this.siteTablesSchemas.length) {
|
||||||
|
// Create tables in the site's database.
|
||||||
|
site.getDb().createTablesFromSchema(this.siteTablesSchemas);
|
||||||
|
}
|
||||||
|
|
||||||
return site;
|
return site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1029,4 +1049,28 @@ export class CoreSitesProvider {
|
||||||
return site.isFeatureDisabled(name);
|
return site.isFeatureDisabled(name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a table in all the sites databases.
|
||||||
|
*
|
||||||
|
* @param {any} table Table schema.
|
||||||
|
*/
|
||||||
|
createTableFromSchema(table: any) : void {
|
||||||
|
this.createTablesFromSchema([table]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create several tables in all the sites databases.
|
||||||
|
*
|
||||||
|
* @param {any[]} tables List of tables schema.
|
||||||
|
*/
|
||||||
|
createTablesFromSchema(tables: any[]) : void {
|
||||||
|
// Add the tables to the list of schemas. This list is to create all the tables in new sites.
|
||||||
|
this.siteTablesSchemas = this.siteTablesSchemas.concat(tables);
|
||||||
|
|
||||||
|
// Now create these tables in current sites.
|
||||||
|
for (let id in this.sites) {
|
||||||
|
this.sites[id].getDb().createTablesFromSchema(tables);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,9 @@ export class CoreUrlUtilsProvider {
|
||||||
* Extracts the parameters from a URL and stores them in an object.
|
* Extracts the parameters from a URL and stores them in an object.
|
||||||
*
|
*
|
||||||
* @param {string} url URL to treat.
|
* @param {string} url URL to treat.
|
||||||
* @return {object} Object with the params.
|
* @return {any} Object with the params.
|
||||||
*/
|
*/
|
||||||
extractUrlParams(url: string) : object {
|
extractUrlParams(url: string) : any {
|
||||||
let regex = /[?&]+([^=&]+)=?([^&]*)?/gi,
|
let regex = /[?&]+([^=&]+)=?([^&]*)?/gi,
|
||||||
params = {};
|
params = {};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue