MOBILE-3565 core: Fix more ESLint after first ESLint fix integration

main
Dani Palou 2020-10-14 16:38:24 +02:00
parent 6fc97ed30f
commit 6592e22998
27 changed files with 365 additions and 243 deletions

View File

@ -78,7 +78,7 @@ export class CoreDelegate {
/**
* Function to resolve the handlers init promise.
*/
protected handlersInitResolve: (value?: any) => void;
protected handlersInitResolve: () => void;
/**
* Constructor of the Delegate.
@ -110,8 +110,8 @@ export class CoreDelegate {
* @param params Parameters to pass to the function.
* @return Function returned value or default value.
*/
protected executeFunctionOnEnabled(handlerName: string, fnName: string, params?: any[]): any {
return this.execute(this.enabledHandlers[handlerName], fnName, params);
protected executeFunctionOnEnabled<T = unknown>(handlerName: string, fnName: string, params?: unknown[]): T {
return this.execute<T>(this.enabledHandlers[handlerName], fnName, params);
}
/**
@ -123,7 +123,7 @@ export class CoreDelegate {
* @param params Parameters to pass to the function.
* @return Function returned value or default value.
*/
protected executeFunction(handlerName: string, fnName: string, params?: any[]): any {
protected executeFunction<T = unknown>(handlerName: string, fnName: string, params?: unknown[]): T {
return this.execute(this.handlers[handlerName], fnName, params);
}
@ -136,7 +136,7 @@ export class CoreDelegate {
* @param params Parameters to pass to the function.
* @return Function returned value or default value.
*/
private execute(handler: CoreDelegateHandler, fnName: string, params?: any[]): any {
private execute<T = unknown>(handler: CoreDelegateHandler, fnName: string, params?: unknown[]): T {
if (handler && handler[fnName]) {
return handler[fnName].apply(handler, params);
} else if (this.defaultHandler && this.defaultHandler[fnName]) {
@ -180,10 +180,10 @@ export class CoreDelegate {
* @param onlyEnabled If check only enabled handlers or all.
* @return Function returned value or default value.
*/
protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): any {
protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): boolean {
const handler = onlyEnabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName];
return handler && handler[fnName];
return handler && typeof handler[fnName] == 'function';
}
/**
@ -240,7 +240,7 @@ export class CoreDelegate {
* @param time Time this update process started.
* @return Resolved when done.
*/
protected updateHandler(handler: CoreDelegateHandler, time: number): Promise<void> {
protected updateHandler(handler: CoreDelegateHandler): Promise<void> {
const siteId = CoreSites.instance.getCurrentSiteId();
const currentSite = CoreSites.instance.getCurrentSite();
let promise: Promise<boolean>;
@ -255,7 +255,7 @@ export class CoreDelegate {
if (!CoreSites.instance.isLoggedIn() || this.isFeatureDisabled(handler, currentSite)) {
promise = Promise.resolve(false);
} else {
promise = handler.isEnabled().catch(() => false);
promise = Promise.resolve(handler.isEnabled()).catch(() => false);
}
// Checks if the handler is enabled.
@ -304,7 +304,7 @@ export class CoreDelegate {
// Loop over all the handlers.
for (const name in this.handlers) {
promises.push(this.updateHandler(this.handlers[name], now));
promises.push(this.updateHandler(this.handlers[name]));
}
try {
@ -326,7 +326,7 @@ export class CoreDelegate {
* Update handlers Data.
* Override this function to update handlers data.
*/
updateData(): any {
updateData(): void {
// To be overridden.
}

View File

@ -11,9 +11,10 @@
// 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 Faker from 'faker';
import { CoreError } from './error';
import { CoreError } from '@classes/errors/error';
describe('CoreError', () => {

View File

@ -30,28 +30,26 @@ export class CoreInterceptor implements HttpInterceptor {
* @param addNull Add null values to the serialized as empty parameters.
* @return Serialization of the object.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static serialize(obj: any, addNull?: boolean): string {
let query = '';
let fullSubName: string;
let subValue;
let innerObj;
for (const name in obj) {
const value = obj[name];
if (value instanceof Array) {
for (let i = 0; i < value.length; ++i) {
subValue = value[i];
fullSubName = name + '[' + i + ']';
innerObj = {};
const subValue = value[i];
const fullSubName = name + '[' + i + ']';
const innerObj = {};
innerObj[fullSubName] = subValue;
query += this.serialize(innerObj) + '&';
}
} else if (value instanceof Object) {
for (const subName in value) {
subValue = value[subName];
fullSubName = name + '[' + subName + ']';
innerObj = {};
const subValue = value[subName];
const fullSubName = name + '[' + subName + ']';
const innerObj = {};
innerObj[fullSubName] = subValue;
query += this.serialize(innerObj) + '&';
}
@ -63,6 +61,7 @@ export class CoreInterceptor implements HttpInterceptor {
return query.length ? query.substr(0, query.length - 1) : query;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
// Add the header and serialize the body if needed.
const newReq = req.clone({

View File

@ -17,11 +17,13 @@ import { CoreUtils, PromiseDefer } from '@services/utils/utils';
/**
* Function to add to the queue.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type CoreQueueRunnerFunction<T> = (...args: any[]) => T | Promise<T>;
/**
* Queue item.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type CoreQueueRunnerItem<T = any> = {
/**
* Item ID.

View File

@ -23,7 +23,7 @@ import { CoreWS, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets, Core
import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreUrlUtils, CoreUrlParams } from '@services/utils/url';
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json';
@ -857,7 +857,7 @@ export class CoreSite {
let promise: Promise<any>;
if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) {
promise = this.db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => {
promise = this.db.getRecords<any>(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => {
if (!entries.length) {
// Cache key not found, get by params sent.
return this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id });
@ -901,7 +901,7 @@ export class CoreSite {
this.logger.info(`Cached element found, id: ${id}. Expires in expires in ${expires} seconds`);
}
return CoreTextUtils.instance.parseJSON(entry.data, {});
return <T> CoreTextUtils.instance.parseJSON(entry.data, {});
}
return Promise.reject(new CoreError('Cache entry not valid.'));
@ -915,7 +915,7 @@ export class CoreSite {
* @param componentId Optional component id (if not included, returns sum for whole component)
* @return Promise resolved when we have calculated the size
*/
getComponentCacheSize(component: string, componentId?: number): Promise<number> {
async getComponentCacheSize(component: string, componentId?: number): Promise<number> {
const params: Array<string | number> = [component];
let extraClause = '';
if (componentId !== undefined && componentId !== null) {
@ -923,8 +923,10 @@ export class CoreSite {
extraClause = ' AND componentId = ?';
}
return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE +
const size = <number> await this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE +
' WHERE component = ?' + extraClause, params);
return size;
}
/**
@ -1018,7 +1020,7 @@ export class CoreSite {
params['componentId'] = componentId;
}
return this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params);
await this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params);
}
/*
@ -1192,8 +1194,10 @@ export class CoreSite {
*
* @return Promise resolved with the total size of all data in the cache table (bytes)
*/
getCacheUsage(): Promise<number> {
return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE);
async getCacheUsage(): Promise<number> {
const size = <number> await this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE);
return size;
}
/**
@ -1228,7 +1232,7 @@ export class CoreSite {
* @param anchor Anchor text if needed.
* @return URL with params.
*/
createSiteUrl(path: string, params?: {[key: string]: unknown}, anchor?: string): string {
createSiteUrl(path: string, params?: CoreUrlParams, anchor?: string): string {
return CoreUrlUtils.instance.addParamsToUrl(this.siteUrl + path, params, anchor);
}
@ -1797,7 +1801,8 @@ export class CoreSite {
* @return Resolves upon success along with the config data. Reject on failure.
*/
getLocalSiteConfig<T extends number | string>(name: string, defaultValue?: T): Promise<T> {
return this.db.getRecord(CoreSite.CONFIG_TABLE, { name }).then((entry) => entry.value).catch((error) => {
return this.db.getRecord<CoreSiteConfigDBRecord>(CoreSite.CONFIG_TABLE, { name }).then((entry) => <T> entry.value)
.catch((error) => {
if (typeof defaultValue != 'undefined') {
return defaultValue;
}
@ -2151,3 +2156,8 @@ export type CoreSiteCallExternalFunctionsResult = {
exception?: string; // JSON-encoed exception info.
}[];
};
export type CoreSiteConfigDBRecord = {
name: string;
value: string | number;
};

View File

@ -15,6 +15,7 @@
import { SQLiteObject } from '@ionic-native/sqlite/ngx';
import { SQLite, Platform } from '@singletons/core.singletons';
import { CoreError } from '@classes/errors/error';
/**
* Schema of a table.
@ -411,6 +412,7 @@ export class SQLiteDB {
* @param params Query parameters.
* @return Promise resolved with the result.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> {
await this.ready();
@ -425,7 +427,8 @@ export class SQLiteDB {
* @param sqlStatements SQL statements to execute.
* @return Promise resolved with the result.
*/
async executeBatch(sqlStatements: (string | SQLiteDBRecordValue[])[][]): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> {
await this.ready();
await this.db.sqlBatch(sqlStatements);
@ -453,9 +456,10 @@ export class SQLiteDB {
* Format the data to where params.
*
* @param data Object data.
* @return List of params.
*/
protected formatDataToSQLParams(data: SQLiteDBRecordValues): SQLiteDBRecordValue[] {
return Object.keys(data).map((key) => data[key]);
return Object.keys(data).map((key) => data[key]);
}
/**
@ -464,7 +468,7 @@ export class SQLiteDB {
* @param table The table to query.
* @return Promise resolved with the records.
*/
async getAllRecords(table: string): Promise<SQLiteDBRecordValues[]> {
async getAllRecords<T = unknown>(table: string): Promise<T[]> {
return this.getRecords(table);
}
@ -510,7 +514,7 @@ export class SQLiteDB {
async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> {
const record = await this.getRecordSql(sql, params);
if (!record) {
throw null;
throw new CoreError('No record found.');
}
return record[Object.keys(record)[0]];
@ -574,10 +578,10 @@ export class SQLiteDB {
* @param fields A comma separated list of fields to return.
* @return Promise resolved with the record, rejected if not found.
*/
getRecord(table: string, conditions?: SQLiteDBRecordValues, fields: string = '*'): Promise<SQLiteDBRecordValues> {
getRecord<T = unknown>(table: string, conditions?: SQLiteDBRecordValues, fields: string = '*'): Promise<T> {
const selectAndParams = this.whereClause(conditions);
return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields);
return this.getRecordSelect<T>(table, selectAndParams[0], selectAndParams[1], fields);
}
/**
@ -589,13 +593,13 @@ export class SQLiteDB {
* @param fields A comma separated list of fields to return.
* @return Promise resolved with the record, rejected if not found.
*/
getRecordSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'):
Promise<SQLiteDBRecordValues> {
getRecordSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'):
Promise<T> {
if (select) {
select = ' WHERE ' + select;
}
return this.getRecordSql(`SELECT ${fields} FROM ${table} ${select}`, params);
return this.getRecordSql<T>(`SELECT ${fields} FROM ${table} ${select}`, params);
}
/**
@ -608,11 +612,11 @@ export class SQLiteDB {
* @param params List of sql parameters
* @return Promise resolved with the records.
*/
async getRecordSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValues> {
const result = await this.getRecordsSql(sql, params, 0, 1);
async getRecordSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[]): Promise<T> {
const result = await this.getRecordsSql<T>(sql, params, 0, 1);
if (!result || !result.length) {
// Not found, reject.
throw null;
throw new CoreError('No records found.');
}
return result[0];
@ -629,11 +633,11 @@ export class SQLiteDB {
* @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records.
*/
getRecords(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*', limitFrom: number = 0,
limitNum: number = 0): Promise<SQLiteDBRecordValues[]> {
getRecords<T = unknown>(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*',
limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
const selectAndParams = this.whereClause(conditions);
return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
return this.getRecordsSelect<T>(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
}
/**
@ -648,11 +652,11 @@ export class SQLiteDB {
* @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records.
*/
getRecordsList(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '', fields: string = '*',
limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> {
getRecordsList<T = unknown>(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '',
fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
const selectAndParams = this.whereClauseList(field, values);
return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
return this.getRecordsSelect<T>(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
}
/**
@ -667,8 +671,8 @@ export class SQLiteDB {
* @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records.
*/
getRecordsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '',
fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> {
getRecordsSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '',
fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
if (select) {
select = ' WHERE ' + select;
}
@ -678,7 +682,7 @@ export class SQLiteDB {
const sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`;
return this.getRecordsSql(sql, params, limitFrom, limitNum);
return this.getRecordsSql<T>(sql, params, limitFrom, limitNum);
}
/**
@ -690,8 +694,8 @@ export class SQLiteDB {
* @param limitNum Return a subset comprising this many records.
* @return Promise resolved with the records.
*/
async getRecordsSql(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number):
Promise<SQLiteDBRecordValues[]> {
async getRecordsSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number):
Promise<T[]> {
const limits = this.normaliseLimitFromNum(limitFrom, limitNum);
if (limits[0] || limits[1]) {
@ -768,7 +772,7 @@ export class SQLiteDB {
*/
async insertRecords(table: string, dataObjects: SQLiteDBRecordValues[]): Promise<void> {
if (!Array.isArray(dataObjects)) {
throw null;
throw new CoreError('Invalid parameter supplied to insertRecords, it should be an array.');
}
const statements = dataObjects.map((dataObject) => {
@ -854,7 +858,7 @@ export class SQLiteDB {
async recordExists(table: string, conditions?: SQLiteDBRecordValues): Promise<void> {
const record = await this.getRecord(table, conditions);
if (!record) {
throw null;
throw new CoreError('Record does not exist.');
}
}
@ -869,7 +873,7 @@ export class SQLiteDB {
async recordExistsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = []): Promise<void> {
const record = await this.getRecordSelect(table, select, params);
if (!record) {
throw null;
throw new CoreError('Record does not exist.');
}
}
@ -883,7 +887,7 @@ export class SQLiteDB {
async recordExistsSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<void> {
const record = await this.getRecordSql(sql, params);
if (!record) {
throw null;
throw new CoreError('Record does not exist.');
}
}

View File

@ -21,6 +21,7 @@ import { Pipe, PipeTransform } from '@angular/core';
name: 'coreCreateLinks',
})
export class CoreCreateLinksPipe implements PipeTransform {
protected static replacePattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])(?![^<]*>|[^<>]*<\/)/gim;
/**
@ -32,4 +33,5 @@ export class CoreCreateLinksPipe implements PipeTransform {
transform(text: string): string {
return text.replace(CoreCreateLinksPipe.replacePattern, '<a href="$1">$1</a>');
}
}

View File

@ -31,4 +31,5 @@ export class CoreNoTagsPipe implements PipeTransform {
transform(text: string): string {
return text.replace(/(<([^>]+)>)/ig, '');
}
}

View File

@ -28,6 +28,6 @@ import { CoreTimeAgoPipe } from './time-ago.pipe';
CoreCreateLinksPipe,
CoreNoTagsPipe,
CoreTimeAgoPipe,
]
],
})
export class CorePipesModule {}

View File

@ -24,6 +24,7 @@ import moment from 'moment';
name: 'coreTimeAgo',
})
export class CoreTimeAgoPipe implements PipeTransform {
private logger: CoreLogger;
constructor() {
@ -48,6 +49,7 @@ export class CoreTimeAgoPipe implements PipeTransform {
timestamp = numberTimestamp;
}
return Translate.instance.instant('core.ago', {$a: moment(timestamp * 1000).fromNow(true)});
return Translate.instance.instant('core.ago', { $a: moment(timestamp * 1000).fromNow(true) });
}
}

View File

@ -174,7 +174,8 @@ export class CoreAppProvider {
await this.createVersionsTableReady;
// Fetch installed version of the schema.
const entry = await this.db.getRecord(SCHEMA_VERSIONS_TABLE, { name: schema.name });
const entry = await this.db.getRecord<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE, { name: schema.name });
oldVersion = entry.version;
} catch (error) {
// No installed version yet.
@ -796,3 +797,8 @@ export type WindowForAutomatedTests = Window & {
appProvider?: CoreAppProvider;
appRef?: ApplicationRef;
};
type SchemaVersionsDBEntry = {
name: string;
version: number;
};

View File

@ -81,7 +81,7 @@ export class CoreConfigProvider {
await this.dbReady;
try {
const entry = await this.appDB.getRecord(TABLE_NAME, { name });
const entry = await this.appDB.getRecord<ConfigDBEntry>(TABLE_NAME, { name });
return entry.value;
} catch (error) {
@ -109,3 +109,9 @@ export class CoreConfigProvider {
}
export class CoreConfig extends makeSingleton(CoreConfigProvider) {}
type ConfigDBEntry = {
name: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
};

View File

@ -64,7 +64,7 @@ export class CoreCronDelegate {
protected appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected handlers: { [s: string]: CoreCronHandler } = {};
protected queuePromise = Promise.resolve();
protected queuePromise: Promise<void> = Promise.resolve();
constructor(zone: NgZone) {
this.logger = CoreLogger.getInstance('CoreCronDelegate');
@ -271,8 +271,9 @@ export class CoreCronDelegate {
const id = this.getHandlerLastExecutionId(name);
try {
const entry = await this.appDB.getRecord(CRON_TABLE, { id });
const time = parseInt(entry.value, 10);
const entry = await this.appDB.getRecord<CronDBEntry>(CRON_TABLE, { id });
const time = Number(entry.value);
return isNaN(time) ? 0 : time;
} catch (err) {
@ -573,3 +574,8 @@ export interface CoreCronHandler {
export type WindowForAutomatedTests = Window & {
cronProvider?: CoreCronDelegate;
};
type CronDBEntry = {
id: string;
value: number;
};

View File

@ -29,8 +29,9 @@ import { CoreTimeUtils } from '@services/utils/time';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { SQLiteDB } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
import { CoreConstants } from '@core/constants';
import { makeSingleton, Network, NgZone } from '@singletons/core.singletons';
import { makeSingleton, Network, NgZone, Translate } from '@singletons/core.singletons';
import { CoreLogger } from '@singletons/logger';
/*
@ -56,7 +57,7 @@ export class CoreFilepoolProvider {
protected static readonly ERR_FS_OR_NETWORK_UNAVAILABLE = 'CoreFilepoolError:ERR_FS_OR_NETWORK_UNAVAILABLE';
protected static readonly ERR_QUEUE_ON_PAUSE = 'CoreFilepoolError:ERR_QUEUE_ON_PAUSE';
protected static readonly FILE_UPDATE_ANY_WHERE_CLAUSE =
protected static readonly FILE_UPDATE_UNKNOWN_WHERE_CLAUSE =
'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))';
// Variables for database.
@ -239,7 +240,7 @@ export class CoreFilepoolProvider {
protected appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected queueState: string;
protected urlAttributes = [
protected urlAttributes: RegExp[] = [
new RegExp('(\\?|&)token=([A-Za-z0-9]*)'),
new RegExp('(\\?|&)forcedownload=[0-1]'),
new RegExp('(\\?|&)preview=[A-Za-z0-9]+'),
@ -248,7 +249,7 @@ export class CoreFilepoolProvider {
// To handle file downloads using the queue.
protected queueDeferreds: { [s: string]: { [s: string]: CoreFilepoolPromiseDefer } } = {};
protected sizeCache = {}; // A "cache" to store file sizes to prevent performing too many HEAD requests.
protected sizeCache: {[fileUrl: string]: number} = {}; // A "cache" to store file sizes.
// Variables to prevent downloading packages/files twice at the same time.
protected packagesPromises: { [s: string]: { [s: string]: Promise<void> } } = {};
protected filePromises: { [s: string]: { [s: string]: Promise<string> } } = {};
@ -288,7 +289,7 @@ export class CoreFilepoolProvider {
*/
protected async addFileLink(siteId: string, fileId: string, component: string, componentId?: string | number): Promise<void> {
if (!component) {
throw null;
throw new CoreError('Cannot add link because component is invalid.');
}
componentId = this.fixComponentId(componentId);
@ -358,8 +359,10 @@ export class CoreFilepoolProvider {
* @return Promise resolved on success.
*/
protected async addFileToPool(siteId: string, fileId: string, data: CoreFilepoolFileEntry): Promise<void> {
const record = Object.assign({}, data);
record.fileId = fileId;
const record = {
fileId,
...data,
};
const db = await CoreSites.instance.getSiteDb(siteId);
@ -457,12 +460,12 @@ export class CoreFilepoolProvider {
await this.dbReady;
if (!CoreFile.instance.isAvailable()) {
throw null;
throw new CoreError('File system cannot be used.');
}
const site = await CoreSites.instance.getSite(siteId);
if (!site.canDownloadFiles()) {
throw null;
throw new CoreError('Site doesn\'t allow downloading files.');
}
let file: CoreWSExternalFile;
@ -488,7 +491,7 @@ export class CoreFilepoolProvider {
const queueDeferred = this.getQueueDeferred(siteId, fileId, false, onProgress);
return this.hasFileInQueue(siteId, fileId).then((entry: CoreFilepoolQueueEntry) => {
const newData: CoreFilepoolQueueEntry = {};
const newData: CoreFilepoolQueueDBEntry = {};
let foundLink = false;
if (entry) {
@ -562,14 +565,14 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified.
* @param checkSize True if we shouldn't download files if their size is big, false otherwise.
* @param downloadAny True to download file in WiFi if their size is any, false otherwise.
* @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise.
* Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL.
* @return Promise resolved when the file is downloaded.
*/
protected async addToQueueIfNeeded(siteId: string, fileUrl: string, component: string, componentId?: string | number,
timemodified: number = 0, checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {},
timemodified: number = 0, checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {},
revision?: number): Promise<void> {
if (!checkSize) {
// No need to check size, just add it to the queue.
@ -584,7 +587,7 @@ export class CoreFilepoolProvider {
} else {
if (!CoreApp.instance.isOnline()) {
// Cannot check size in offline, stop.
throw null;
throw new CoreError(Translate.instance.instant('core.cannotconnect'));
}
size = await CoreWS.instance.getRemoteFileSize(fileUrl);
@ -592,16 +595,16 @@ export class CoreFilepoolProvider {
// Calculate the size of the file.
const isWifi = CoreApp.instance.isWifi();
const sizeAny = size <= 0;
const sizeUnknown = size <= 0;
if (!sizeAny) {
if (!sizeUnknown) {
// Store the size in the cache.
this.sizeCache[fileUrl] = size;
}
// Check if the file should be downloaded.
if (sizeAny) {
if (downloadAny && isWifi) {
if (sizeUnknown) {
if (downloadUnknown && isWifi) {
await this.addToQueueByUrl(siteId, fileUrl, component, componentId, timemodified, undefined, undefined,
0, options, revision, true);
}
@ -685,7 +688,7 @@ export class CoreFilepoolProvider {
const count = await db.countRecords(CoreFilepoolProvider.LINKS_TABLE, conditions);
if (count <= 0) {
return null;
throw new CoreError('Component doesn\'t have files');
}
}
@ -696,7 +699,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component.
* @return Link, null if nothing to link.
*/
protected createComponentLink(component: string, componentId?: string | number): CoreFilepoolComponentLink {
protected createComponentLink(component: string, componentId?: string | number): CoreFilepoolComponentLink | null {
if (typeof component != 'undefined' && component != null) {
return { component, componentId: this.fixComponentId(componentId) };
}
@ -779,7 +782,7 @@ export class CoreFilepoolProvider {
if (poolFileObject && poolFileObject.fileId !== fileId) {
this.logger.error('Invalid object to update passed');
throw null;
throw new CoreError('Invalid object to update passed.');
}
const downloadId = this.getFileDownloadId(fileUrl, filePath);
@ -793,7 +796,7 @@ export class CoreFilepoolProvider {
this.filePromises[siteId][downloadId] = CoreSites.instance.getSite(siteId).then(async (site) => {
if (!site.canDownloadFiles()) {
return Promise.reject(null);
throw new CoreError('Site doesn\'t allow downloading files.');
}
const entry = await CoreWS.instance.downloadFile(fileUrl, filePath, addExtension, onProgress);
@ -952,7 +955,7 @@ export class CoreFilepoolProvider {
try {
await Promise.all(promises);
// Success prefetching, store package as downloaded.
this.storePackageStatus(siteId, CoreConstants.DOWNLOADED, component, componentId, extra);
await this.storePackageStatus(siteId, CoreConstants.DOWNLOADED, component, componentId, extra);
} catch (error) {
// Error downloading, go back to previous status and reject the promise.
await this.setPackagePreviousStatus(siteId, component, componentId);
@ -1014,7 +1017,7 @@ export class CoreFilepoolProvider {
let alreadyDownloaded = true;
if (!CoreFile.instance.isAvailable()) {
throw null;
throw new CoreError('File system cannot be used.');
}
const file = await this.fixPluginfileURL(siteId, fileUrl);
@ -1093,14 +1096,12 @@ export class CoreFilepoolProvider {
let urls = [];
const element = CoreDomUtils.instance.convertToElement(html);
const elements = element.querySelectorAll('a, img, audio, video, source, track');
const elements: (HTMLAnchorElement | HTMLImageElement | HTMLAudioElement | HTMLVideoElement | HTMLSourceElement |
HTMLTrackElement)[] = Array.from(element.querySelectorAll('a, img, audio, video, source, track'));
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
let url = element.tagName === 'A'
? (element as HTMLAnchorElement).href
: (element as HTMLImageElement | HTMLVideoElement | HTMLAudioElement |
HTMLAudioElement | HTMLTrackElement | HTMLSourceElement).src;
let url = 'href' in element ? element.href : element.src;
if (url && CoreUrlUtils.instance.isDownloadableUrl(url) && urls.indexOf(url) == -1) {
urls.push(url);
@ -1236,7 +1237,7 @@ export class CoreFilepoolProvider {
componentId: this.fixComponentId(componentId),
};
const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, conditions);
const items = await db.getRecords<CoreFilepoolLinksRecord>(CoreFilepoolProvider.LINKS_TABLE, conditions);
items.forEach((item) => {
item.componentId = this.fixComponentId(item.componentId);
});
@ -1252,16 +1253,16 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise.
*/
async getDirectoryUrlByUrl(siteId: string, fileUrl: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl);
const filePath = await this.getFilePath(siteId, fileId, '');
const dirEntry = await CoreFile.instance.getDir(filePath);
return dirEntry.toURL();
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl);
const filePath = await this.getFilePath(siteId, fileId, '');
const dirEntry = await CoreFile.instance.getDir(filePath);
return dirEntry.toURL();
}
/**
@ -1346,7 +1347,8 @@ export class CoreFilepoolProvider {
*/
protected async getFileLinks(siteId: string, fileId: string): Promise<CoreFilepoolLinksRecord[]> {
const db = await CoreSites.instance.getSiteDb(siteId);
const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, { fileId });
const items = await db.getRecords<CoreFilepoolLinksRecord>(CoreFilepoolProvider.LINKS_TABLE, { fileId });
items.forEach((item) => {
item.componentId = this.fixComponentId(item.componentId);
});
@ -1421,7 +1423,7 @@ export class CoreFilepoolProvider {
const files = [];
const promises = items.map((item) =>
db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId: item.fileId }).then((fileEntry) => {
db.getRecord<CoreFilepoolFileEntry>(CoreFilepoolProvider.FILES_TABLE, { fileId: item.fileId }).then((fileEntry) => {
if (!fileEntry) {
return;
}
@ -1532,7 +1534,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified.
* @param checkSize True if we shouldn't download files if their size is big, false otherwise.
* @param downloadAny True to download file in WiFi if their size is any, false otherwise.
* @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise.
* Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL.
@ -1544,12 +1546,12 @@ export class CoreFilepoolProvider {
* If the file isn't downloaded or it's outdated, return the online URL and add it to the queue to be downloaded later.
*/
protected async getFileUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number,
mode: string = 'url', timemodified: number = 0, checkSize: boolean = true, downloadAny?: boolean,
mode: string = 'url', timemodified: number = 0, checkSize: boolean = true, downloadUnknown?: boolean,
options: CoreFilepoolFileOptions = {}, revision?: number): Promise<string> {
const addToQueue = (fileUrl: string): void => {
// Add the file to queue if needed and ignore errors.
this.addToQueueIfNeeded(siteId, fileUrl, component, componentId, timemodified, checkSize,
downloadAny, options, revision).catch(() => {
downloadUnknown, options, revision).catch(() => {
// Ignore errors.
});
};
@ -1594,7 +1596,7 @@ export class CoreFilepoolProvider {
return fileUrl;
}
throw null;
throw new CoreError('File not found.');
}
}, () => {
// We do not have the file in store yet. Add to queue and return the fixed URL.
@ -1614,14 +1616,14 @@ export class CoreFilepoolProvider {
* @return Resolved with the internal URL. Rejected otherwise.
*/
protected async getInternalSrcById(siteId: string, fileId: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path);
return CoreFile.instance.convertFileSrc(fileEntry.toURL());
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path);
return CoreFile.instance.convertFileSrc(fileEntry.toURL());
}
/**
@ -1632,19 +1634,19 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise.
*/
protected async getInternalUrlById(siteId: string, fileId: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path);
// This URL is usually used to launch files or put them in HTML. In desktop we need the internal URL.
if (CoreApp.instance.isDesktop()) {
return fileEntry.toInternalURL();
} else {
return fileEntry.toURL();
}
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path);
// This URL is usually used to launch files or put them in HTML. In desktop we need the internal URL.
if (CoreApp.instance.isDesktop()) {
return fileEntry.toInternalURL();
} else {
return fileEntry.toURL();
}
}
/**
@ -1654,13 +1656,13 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL.
*/
protected async getInternalUrlByPath(filePath: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const fileEntry = await CoreFile.instance.getFile(filePath);
return fileEntry.toURL();
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const fileEntry = await CoreFile.instance.getFile(filePath);
return fileEntry.toURL();
}
/**
@ -1671,14 +1673,14 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise.
*/
async getInternalUrlByUrl(siteId: string, fileUrl: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl);
return this.getInternalUrlById(siteId, fileId);
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl);
return this.getInternalUrlById(siteId, fileId);
}
/**
@ -1748,16 +1750,16 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL.
*/
async getPackageDirUrlByUrl(siteId: string, url: string): Promise<string> {
if (CoreFile.instance.isAvailable()) {
const file = await this.fixPluginfileURL(siteId, url);
const dirName = this.getPackageDirNameByUrl(file.fileurl);
const dirPath = await this.getFilePath(siteId, dirName, '');
const dirEntry = await CoreFile.instance.getDir(dirPath);
return dirEntry.toURL();
if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
throw null;
const file = await this.fixPluginfileURL(siteId, url);
const dirName = this.getPackageDirNameByUrl(file.fileurl);
const dirPath = await this.getFilePath(siteId, dirName, '');
const dirEntry = await CoreFile.instance.getDir(dirPath);
return dirEntry.toURL();
}
/**
@ -1973,7 +1975,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified.
* @param checkSize True if we shouldn't download files if their size is big, false otherwise.
* @param downloadAny True to download file in WiFi if their size is any, false otherwise.
* @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise.
* Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL.
@ -1983,10 +1985,10 @@ export class CoreFilepoolProvider {
* The URL returned is compatible to use with IMG tags.
*/
getSrcByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0,
checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number):
checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number):
Promise<string> {
return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'src',
timemodified, checkSize, downloadAny, options, revision);
timemodified, checkSize, downloadUnknown, options, revision);
}
/**
@ -2017,7 +2019,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified.
* @param checkSize True if we shouldn't download files if their size is big, false otherwise.
* @param downloadAny True to download file in WiFi if their size is any, false otherwise.
* @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise.
* Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL.
@ -2027,10 +2029,10 @@ export class CoreFilepoolProvider {
* The URL returned is compatible to use with a local browser.
*/
getUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0,
checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number):
checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number):
Promise<string> {
return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'url',
timemodified, checkSize, downloadAny, options, revision);
timemodified, checkSize, downloadUnknown, options, revision);
}
/**
@ -2100,9 +2102,10 @@ export class CoreFilepoolProvider {
*/
protected async hasFileInPool(siteId: string, fileId: string): Promise<CoreFilepoolFileEntry> {
const db = await CoreSites.instance.getSiteDb(siteId);
const entry = await db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId });
const entry = await db.getRecord<CoreFilepoolFileEntry>(CoreFilepoolProvider.FILES_TABLE, { fileId });
if (typeof entry === 'undefined') {
throw null;
throw new CoreError('File not found in filepool.');
}
return entry;
@ -2118,12 +2121,13 @@ export class CoreFilepoolProvider {
protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> {
await this.dbReady;
const entry = await this.appDB.getRecord(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId });
const entry = await this.appDB.getRecord<CoreFilepoolQueueEntry>(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId });
if (typeof entry === 'undefined') {
throw null;
throw new CoreError('File not found in queue.');
}
// Convert the links to an object.
entry.linksUnserialized = CoreTextUtils.instance.parseJSON(entry.links, []);
entry.linksUnserialized = <CoreFilepoolComponentLink[]> CoreTextUtils.instance.parseJSON(entry.links, []);
return entry;
}
@ -2132,14 +2136,14 @@ export class CoreFilepoolProvider {
* Invalidate all the files in a site.
*
* @param siteId The site ID.
* @param onlyAny True to only invalidate files from external repos or without revision/timemodified.
* @param onlyUnknown True to only invalidate files from external repos or without revision/timemodified.
* It is advised to set it to true to reduce the performance and data usage of the app.
* @return Resolved on success.
*/
async invalidateAllFiles(siteId: string, onlyAny: boolean = true): Promise<void> {
async invalidateAllFiles(siteId: string, onlyUnknown: boolean = true): Promise<void> {
const db = await CoreSites.instance.getSiteDb(siteId);
const where = onlyAny ? CoreFilepoolProvider.FILE_UPDATE_ANY_WHERE_CLAUSE : null;
const where = onlyUnknown ? CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE : null;
await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, where);
}
@ -2171,11 +2175,11 @@ export class CoreFilepoolProvider {
* @param siteId The site ID.
* @param component The component to invalidate.
* @param componentId An ID to use in conjunction with the component.
* @param onlyAny True to only invalidate files from external repos or without revision/timemodified.
* It is advised to set it to true to reduce the performance and data usage of the app.
* @param onlyUnknown True to only invalidate files from external repos or without revision/timemodified.
* It is advised to set it to true to reduce the performance and data usage of the app.
* @return Resolved when done.
*/
async invalidateFilesByComponent(siteId: string, component: string, componentId?: string | number, onlyAny: boolean = true):
async invalidateFilesByComponent(siteId: string, component: string, componentId?: string | number, onlyUnknown: boolean = true):
Promise<void> {
const db = await CoreSites.instance.getSiteDb(siteId);
@ -2191,8 +2195,8 @@ export class CoreFilepoolProvider {
whereAndParams[0] = 'fileId ' + whereAndParams[0];
if (onlyAny) {
whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_ANY_WHERE_CLAUSE + ')';
if (onlyUnknown) {
whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE + ')';
}
await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, whereAndParams[0], whereAndParams[1]);
@ -2258,7 +2262,7 @@ export class CoreFilepoolProvider {
* @param entry Filepool entry.
* @return Whether it cannot determine updates.
*/
protected isFileUpdateAny(entry: CoreFilepoolFileEntry): boolean {
protected isFileUpdateUnknown(entry: CoreFilepoolFileEntry): boolean {
return !!entry.isexternalfile || (!entry.revision && !entry.timemodified);
}
@ -2433,7 +2437,7 @@ export class CoreFilepoolProvider {
let items: CoreFilepoolQueueEntry[];
try {
items = await this.appDB.getRecords(CoreFilepoolProvider.QUEUE_TABLE, undefined,
items = await this.appDB.getRecords<CoreFilepoolQueueEntry>(CoreFilepoolProvider.QUEUE_TABLE, undefined,
'priority DESC, added ASC', undefined, 0, 1);
} catch (err) {
throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY;
@ -2444,7 +2448,7 @@ export class CoreFilepoolProvider {
throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY;
}
// Convert the links to an object.
item.linksUnserialized = CoreTextUtils.instance.parseJSON(item.links, []);
item.linksUnserialized = <CoreFilepoolComponentLink[]> CoreTextUtils.instance.parseJSON(item.links, []);
return this.processQueueItem(item);
}
@ -2760,7 +2764,7 @@ export class CoreFilepoolProvider {
const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url);
// If the file is streaming (audio or video) we reject.
if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) {
throw null;
throw new CoreError('File is audio or video.');
}
}
@ -2988,9 +2992,9 @@ export type CoreFilepoolFileEntry = CoreFilepoolFileOptions & {
};
/**
* Entry from the file's queue.
* DB data for entry from file's queue.
*/
export type CoreFilepoolQueueEntry = CoreFilepoolFileOptions & {
export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & {
/**
* The site the file belongs to.
*/
@ -3025,7 +3029,12 @@ export type CoreFilepoolQueueEntry = CoreFilepoolFileOptions & {
* File links (to link the file to components and componentIds). Serialized to store on DB.
*/
links?: string;
};
/**
* Entry from the file's queue.
*/
export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & {
/**
* File links (to link the file to components and componentIds).
*/

View File

@ -116,6 +116,7 @@ export class CoreGeolocationProvider {
*
* @param error Error.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
protected isCordovaPermissionDeniedError(error?: any): boolean {
return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED;
}

View File

@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreSites } from '@services/sites';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CoreError } from '@classes/errors/error';
import { makeSingleton, Translate } from '@singletons/core.singletons';
import { CoreWSExternalWarning } from '@services/ws';
import { CoreCourseBase } from '@/types/global';
@ -79,9 +80,11 @@ export class CoreGroupsProvider {
preSets.emergencyCache = false;
}
const response = await site.read('core_group_get_activity_allowed_groups', params, preSets);
const response: CoreGroupGetActivityAllowedGroupsResponse =
await site.read('core_group_get_activity_allowed_groups', params, preSets);
if (!response || !response.groups) {
throw null;
throw new CoreError('Activity allowed groups not found.');
}
return response;
@ -195,9 +198,11 @@ export class CoreGroupsProvider {
preSets.emergencyCache = false;
}
const response = await site.read('core_group_get_activity_groupmode', params, preSets);
const response: CoreGroupGetActivityGroupModeResponse =
await site.read('core_group_get_activity_groupmode', params, preSets);
if (!response || typeof response.groupmode == 'undefined') {
throw null;
throw new CoreError('Activity group mode not found.');
}
return response.groupmode;
@ -267,9 +272,11 @@ export class CoreGroupsProvider {
updateFrequency: CoreSite.FREQUENCY_RARELY,
};
const response = await site.read('core_group_get_course_user_groups', data, preSets);
const response: CoreGroupGetCourseUserGroupsResponse =
await site.read('core_group_get_course_user_groups', data, preSets);
if (!response || !response.groups) {
throw null;
throw new CoreError('User groups in course not found.');
}
return response.groups;
@ -461,3 +468,26 @@ export type CoreGroupGetActivityAllowedGroupsResponse = {
canaccessallgroups?: boolean; // Whether the user will be able to access all the activity groups.
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS core_group_get_activity_groupmode.
*/
export type CoreGroupGetActivityGroupModeResponse = {
groupmode: number; // Group mode: 0 for no groups, 1 for separate groups, 2 for visible groups.
warnings?: CoreWSExternalWarning[];
};
/**
* Result of WS core_group_get_course_user_groups.
*/
export type CoreGroupGetCourseUserGroupsResponse = {
groups: {
id: number; // Group record id.
name: string; // Multilang compatible name, course unique.
description: string; // Group description text.
descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
idnumber: string; // Id number.
courseid?: number; // Course id.
}[];
warnings?: CoreWSExternalWarning[];
};

View File

@ -30,9 +30,9 @@ export class CoreLangProvider {
protected fallbackLanguage = 'en'; // Always use English as fallback language since it contains all strings.
protected defaultLanguage = CoreConfigConstants.default_lang || 'en'; // Lang to use if device lang not valid or is forced.
protected currentLanguage: string; // Save current language in a variable to speed up the get function.
protected customStrings = {}; // Strings defined using the admin tool.
protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
protected customStringsRaw: string;
protected sitePluginsStrings = {}; // Strings defined by site plugins.
protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
constructor() {
// Set fallback language and language to use until the app determines the right language to use.
@ -110,11 +110,11 @@ export class CoreLangProvider {
* @param language New language to use.
* @return Promise resolved when the change is finished.
*/
changeCurrentLanguage(language: string): Promise<unknown> {
async changeCurrentLanguage(language: string): Promise<void> {
const promises = [];
// Change the language, resolving the promise when we receive the first value.
promises.push(new Promise((resolve, reject): void => {
promises.push(new Promise((resolve, reject) => {
const subscription = Translate.instance.use(language).subscribe((data) => {
// It's a language override, load the original one first.
const fallbackLang = Translate.instance.instant('core.parentlanguage');
@ -165,13 +165,15 @@ export class CoreLangProvider {
this.currentLanguage = language;
return Promise.all(promises).finally(() => {
try {
await Promise.all(promises);
} finally {
// Load the custom and site plugins strings for the language.
if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) {
// Some lang strings have changed, emit an event to update the pipes.
Translate.instance.onLangChange.emit({ lang: language, translations: Translate.instance.translations[language] });
}
});
}
}
/**
@ -196,7 +198,7 @@ export class CoreLangProvider {
*
* @return Custom strings.
*/
getAllCustomStrings(): unknown {
getAllCustomStrings(): CoreLanguageObject {
return this.customStrings;
}
@ -205,7 +207,7 @@ export class CoreLangProvider {
*
* @return Site plugins strings.
*/
getAllSitePluginsStrings(): unknown {
getAllSitePluginsStrings(): CoreLanguageObject {
return this.sitePluginsStrings;
}
@ -220,7 +222,7 @@ export class CoreLangProvider {
}
// Get current language from config (user might have changed it).
return CoreConfig.instance.get('current_language').then((language) => language).catch(() => {
return CoreConfig.instance.get<string>('current_language').then((language) => language).catch(() => {
// User hasn't defined a language. If default language is forced, use it.
if (CoreConfigConstants.default_lang && CoreConfigConstants.forcedefaultlanguage) {
return CoreConfigConstants.default_lang;
@ -283,7 +285,7 @@ export class CoreLangProvider {
* @param lang The language to check.
* @return Promise resolved when done.
*/
getTranslationTable(lang: string): Promise<unknown> {
getTranslationTable(lang: string): Promise<Record<string, unknown>> {
// Create a promise to convert the observable into a promise.
return new Promise((resolve, reject): void => {
const observer = Translate.instance.getTranslation(lang).subscribe((table) => {

View File

@ -20,9 +20,11 @@ import { CoreApp, CoreAppSchema } from '@services/app';
import { CoreConfig } from '@services/config';
import { CoreEventObserver, CoreEvents, CoreEventsProvider } from '@services/events';
import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { SQLiteDB } from '@classes/sqlitedb';
import { CoreSite } from '@classes/site';
import { CoreQueueRunner } from '@classes/queue-runner';
import { CoreError } from '@classes/errors/error';
import { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json';
import { makeSingleton, NgZone, Platform, Translate, LocalNotifications, Push, Device } from '@singletons/core.singletons';
@ -94,14 +96,9 @@ export class CoreLocalNotificationsProvider {
protected appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected codes: { [s: string]: number } = {};
protected codeRequestsQueue = {};
protected observables = {};
protected currentNotification = {
title: '',
texts: [],
ids: [],
timeouts: [],
};
protected codeRequestsQueue: {[key: string]: CodeRequestsQueueItem} = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected observables: {[eventName: string]: {[component: string]: Subject<any>}} = {};
protected triggerSubscription: Subscription;
protected clickSubscription: Subscription;
@ -156,7 +153,7 @@ export class CoreLocalNotificationsProvider {
});
});
CoreEvents.instance.on(CoreEventsProvider.SITE_DELETED, (site) => {
CoreEvents.instance.on(CoreEventsProvider.SITE_DELETED, (site: CoreSite) => {
if (site) {
this.cancelSiteNotifications(site.id);
}
@ -270,13 +267,15 @@ export class CoreLocalNotificationsProvider {
try {
// Check if we already have a code stored for that ID.
const entry = await this.appDB.getRecord(table, { id: id });
const entry = await this.appDB.getRecord<{id: string; code: number}>(table, { id: id });
this.codes[key] = entry.code;
return entry.code;
} catch (err) {
// No code stored for that ID. Create a new code for it.
const entries = await this.appDB.getRecords(table, undefined, 'code DESC');
const entries = await this.appDB.getRecords<{id: string; code: number}>(table, undefined, 'code DESC');
let newCode = 0;
if (entries.length > 0) {
newCode = entries[0].code + 1;
@ -326,7 +325,7 @@ export class CoreLocalNotificationsProvider {
*/
protected getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise<number> {
if (!siteId || !component) {
return Promise.reject(null);
return Promise.reject(new CoreError('Site ID or component not supplied.'));
}
return this.getSiteCode(siteId).then((siteCode) => this.getComponentCode(component).then((componentCode) =>
@ -372,7 +371,9 @@ export class CoreLocalNotificationsProvider {
await this.dbReady;
try {
const stored = await this.appDB.getRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, { id: notification.id });
const stored = await this.appDB.getRecord<{id: number; at: number}>(CoreLocalNotificationsProvider.TRIGGERED_TABLE,
{ id: notification.id });
let triggered = (notification.trigger && notification.trigger.at) || 0;
if (typeof triggered != 'number') {
@ -398,6 +399,7 @@ export class CoreLocalNotificationsProvider {
*
* @param data Data received by the notification.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
notifyClick(data: any): void {
this.notifyEvent('click', data);
}
@ -408,6 +410,7 @@ export class CoreLocalNotificationsProvider {
* @param eventName Name of the event to notify.
* @param data Data received by the notification.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
notifyEvent(eventName: string, data: any): void {
// Execute the code in the Angular zone, so change detection doesn't stop working.
NgZone.instance.run(() => {
@ -426,6 +429,7 @@ export class CoreLocalNotificationsProvider {
* @param data Notification data.
* @return Parsed data.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
protected parseNotificationData(data: any): any {
if (!data) {
return {};
@ -454,11 +458,11 @@ export class CoreLocalNotificationsProvider {
if (typeof request == 'object' && typeof request.table != 'undefined' && typeof request.id != 'undefined') {
// Get the code and resolve/reject all the promises of this request.
promise = this.getCode(request.table, request.id).then((code) => {
request.promises.forEach((p) => {
request.deferreds.forEach((p) => {
p.resolve(code);
});
}).catch((error) => {
request.promises.forEach((p) => {
request.deferreds.forEach((p) => {
p.reject(error);
});
});
@ -508,7 +512,7 @@ export class CoreLocalNotificationsProvider {
return {
off: (): void => {
this.observables[eventName][component].unsubscribe(callback);
this.observables[eventName][component].unsubscribe();
},
};
}
@ -539,13 +543,13 @@ export class CoreLocalNotificationsProvider {
if (typeof this.codeRequestsQueue[key] != 'undefined') {
// There's already a pending request for this store and ID, add the promise to it.
this.codeRequestsQueue[key].promises.push(deferred);
this.codeRequestsQueue[key].deferreds.push(deferred);
} else {
// Add a pending request to the queue.
this.codeRequestsQueue[key] = {
table: table,
id: id,
promises: [deferred],
deferreds: [deferred],
};
}
@ -682,7 +686,7 @@ export class CoreLocalNotificationsProvider {
const entry = {
id: notification.id,
at: notification.trigger && notification.trigger.at ? notification.trigger.at : Date.now(),
at: notification.trigger && notification.trigger.at ? notification.trigger.at.getTime() : Date.now(),
};
return this.appDB.insertRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, entry);
@ -709,3 +713,9 @@ export class CoreLocalNotificationsProvider {
export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {}
export type CoreLocalNotificationsClickCallback<T = unknown> = (value: T) => void;
type CodeRequestsQueueItem = {
table: string;
id: string;
deferreds: PromiseDefer<number>[];
};

View File

@ -26,7 +26,13 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json';
import {
CoreSite, CoreSiteWSPreSets, LocalMobileResponse, CoreSiteConfig, CoreSitePublicConfigResponse, CoreSiteInfoResponse,
CoreSite,
CoreSiteWSPreSets,
LocalMobileResponse,
CoreSiteInfo,
CoreSiteConfig,
CoreSitePublicConfigResponse,
CoreSiteInfoResponse,
} from '@classes/site';
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
@ -123,7 +129,7 @@ export class CoreSitesProvider {
await db.tableExists(oldTable);
// Move the records from the old table.
const sites = await db.getAllRecords(oldTable);
const sites = await db.getAllRecords<SiteDBEntry>(oldTable);
const promises = [];
sites.forEach((site) => {
@ -818,9 +824,9 @@ export class CoreSitesProvider {
id,
siteUrl,
token,
info: info ? JSON.stringify(info) : info,
info: info ? JSON.stringify(info) : undefined,
privateToken,
config: config ? JSON.stringify(config) : config,
config: config ? JSON.stringify(config) : undefined,
loggedOut: 0,
oauthId,
};
@ -1094,7 +1100,7 @@ export class CoreSitesProvider {
return this.sites[siteId];
} else {
// Retrieve and create the site.
const data = await this.appDB.getRecord(SITES_TABLE, { id: siteId });
const data = await this.appDB.getRecord<SiteDBEntry>(SITES_TABLE, { id: siteId });
return this.makeSiteFromSiteListEntry(data);
}
@ -1106,16 +1112,12 @@ export class CoreSitesProvider {
* @param entry Site list entry.
* @return Promised resolved with the created site.
*/
makeSiteFromSiteListEntry(entry: any): Promise<CoreSite> {
let info = entry.info;
let config = entry.config;
makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> {
// Parse info and config.
info = info ? CoreTextUtils.instance.parseJSON(info) : info;
config = config ? CoreTextUtils.instance.parseJSON(config) : config;
const info = entry.info ? <CoreSiteInfo> CoreTextUtils.instance.parseJSON(entry.info) : undefined;
const config = entry.config ? <CoreSiteConfig> CoreTextUtils.instance.parseJSON(entry.config) : undefined;
const site = new CoreSite(entry.id, entry.siteUrl, entry.token,
info, entry.privateToken, config, entry.loggedOut == 1);
const site = new CoreSite(entry.id, entry.siteUrl, entry.token, info, entry.privateToken, config, entry.loggedOut == 1);
site.setOAuthId(entry.oauthId);
return this.migrateSiteSchemas(site).then(() => {
@ -1171,20 +1173,20 @@ export class CoreSitesProvider {
async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> {
await this.dbReady;
const sites = await this.appDB.getAllRecords(SITES_TABLE);
const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE);
const formattedSites = [];
sites.forEach((site) => {
if (!ids || ids.indexOf(site.id) > -1) {
// Parse info.
const siteInfo = site.info ? CoreTextUtils.instance.parseJSON(site.info) : site.info;
const siteInfo = site.info ? <CoreSiteInfo> CoreTextUtils.instance.parseJSON(site.info) : undefined;
const 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,
fullName: siteInfo?.fullname,
siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo?.sitename,
avatar: siteInfo?.userpictureurl,
siteHomeId: siteInfo?.siteid || 1,
};
formattedSites.push(basicInfo);
}
@ -1231,7 +1233,7 @@ export class CoreSitesProvider {
async getLoggedInSitesIds(): Promise<string[]> {
await this.dbReady;
const sites = await this.appDB.getRecords(SITES_TABLE, { loggedOut : 0 });
const sites = await this.appDB.getRecords<SiteDBEntry>(SITES_TABLE, { loggedOut : 0 });
return sites.map((site) => site.id);
}
@ -1244,7 +1246,7 @@ export class CoreSitesProvider {
async getSitesIds(): Promise<string[]> {
await this.dbReady;
const sites = await this.appDB.getAllRecords(SITES_TABLE);
const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE);
return sites.map((site) => site.id);
}
@ -1314,7 +1316,7 @@ export class CoreSitesProvider {
this.sessionRestored = true;
try {
const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 });
const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE, { id: 1 });
const siteId = currentSite.siteId;
this.logger.debug(`Restore session in site ${siteId}`);
@ -1495,7 +1497,7 @@ export class CoreSitesProvider {
}
try {
const siteEntries = await this.appDB.getAllRecords(SITES_TABLE);
const siteEntries = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE);
const ids = [];
const promises = [];
@ -1528,7 +1530,7 @@ export class CoreSitesProvider {
async getStoredCurrentSiteId(): Promise<string> {
await this.dbReady;
const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 });
const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE, { id: 1 });
return currentSite.siteId;
}
@ -1685,7 +1687,7 @@ export class CoreSitesProvider {
const db = site.getDb();
// Fetch installed versions of the schema.
const records = await db.getAllRecords(SCHEMA_VERSIONS_TABLE);
const records = await db.getAllRecords<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE);
const versions: {[name: string]: number} = {};
records.forEach((record) => {
@ -2048,3 +2050,24 @@ export type CoreSitesLoginTokenResponse = {
debuginfo?: string;
reproductionlink?: string;
};
type SiteDBEntry = {
id: string;
siteUrl: string;
token: string;
info: string;
privateToken: string;
config: string;
loggedOut: number;
oauthId: number;
};
type CurrentSiteDBEntry = {
id: number;
siteId: string;
};
type SchemaVersionsDBEntry = {
name: string;
version: number;
};

View File

@ -132,7 +132,7 @@ export class CoreSyncProvider {
* @param siteId Site ID. If not defined, current site.
* @return Record if found or reject.
*/
getSyncRecord(component: string, id: string | number, siteId?: string): Promise<Record<string, unknown>> {
getSyncRecord(component: string, id: string | number, siteId?: string): Promise<CoreSyncRecord> {
return CoreSites.instance.getSiteDb(siteId).then((db) => db.getRecord(SYNC_TABLE, { component: component, id: id }));
}
@ -145,8 +145,7 @@ export class CoreSyncProvider {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with done.
*/
async insertOrUpdateSyncRecord(component: string, id: string | number, data: Record<string, unknown>, siteId?: string):
Promise<void> {
async insertOrUpdateSyncRecord(component: string, id: string, data: CoreSyncRecord, siteId?: string): Promise<void> {
const db = await CoreSites.instance.getSiteDb(siteId);
data.component = component;
@ -212,3 +211,10 @@ export class CoreSyncProvider {
}
export class CoreSync extends makeSingleton(CoreSyncProvider) {}
export type CoreSyncRecord = {
component: string;
id: string;
time: number;
warnings: string;
};

View File

@ -51,7 +51,7 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
const promises = [];
const versionCode = CoreConfigConstants.versioncode;
const versionApplied: number = await CoreConfig.instance.get(VERSION_APPLIED, 0);
const versionApplied = await CoreConfig.instance.get<number>(VERSION_APPLIED, 0);
if (versionCode >= 3900 && versionApplied < 3900 && versionApplied > 0) {
// @todo: H5P update.

View File

@ -391,7 +391,7 @@ export class CoreIframeUtilsProvider {
return;
}
if (!CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain)) {
if (!CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol)) {
// Scheme suggests it's an external resource.
event && event.preventDefault();

View File

@ -424,18 +424,16 @@ export class CoreUrlUtilsProvider {
isLocalFileUrl(url: string): boolean {
const urlParts = CoreUrl.parse(url);
return this.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain);
return this.isLocalFileUrlScheme(urlParts.protocol);
}
/**
* Check whether a URL scheme belongs to a local file.
*
* @param scheme Scheme to check.
* @param notUsed Unused parameter.
* @return Whether the scheme belongs to a local file.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
isLocalFileUrlScheme(scheme: string, notUsed?: string): boolean {
isLocalFileUrlScheme(scheme: string): boolean {
if (scheme) {
scheme = scheme.toLowerCase();
}

View File

@ -15,6 +15,7 @@
import { Injectable } from '@angular/core';
import { HttpResponse, HttpParams } from '@angular/common/http';
import { FileEntry } from '@ionic-native/file';
import { FileUploadOptions } from '@ionic-native/file-transfer/ngx';
import { Md5 } from 'ts-md5/dist/md5';
import { Observable } from 'rxjs';

View File

@ -38,7 +38,7 @@ export class CoreArray {
*/
static flatten<T>(arr: T[][]): T[] {
if ('flat' in arr) {
return (arr as any).flat();
return (arr as any).flat(); // eslint-disable-line @typescript-eslint/no-explicit-any
}
return [].concat(...arr);

View File

@ -400,7 +400,7 @@ function unserialize (str) {
}
}
function substr_replace (str, replace, start, length) { // eslint-disable-line camelcase
function substr_replace (str, replace, start, length) {
// discuss at: https://locutus.io/php/substr_replace/
// original by: Brett Zamir (https://brett-zamir.me)
// example 1: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 0)

View File

@ -24,6 +24,7 @@ export type CoreWindowOpenOptions = {
/**
* NavController to use when opening the link in the app.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
navCtrl?: any; // @todo NavController;
};
@ -36,7 +37,7 @@ export class CoreWindow {
private constructor() {
// Nothing to do.
}
/**
* "Safe" implementation of window.open. It will open the URL without overriding the app.
*
@ -60,11 +61,13 @@ export class CoreWindow {
await CoreUtils.instance.openFile(url);
} else {
let treated: boolean;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options = options || {};
if (name != '_system') {
// Check if it can be opened in the app.
treated = false; // @todo await CoreContentLinksHelper.instance.handleLink(url, undefined, options.navCtrl, true, true);
treated = false;
// @todo await CoreContentLinksHelper.instance.handleLink(url, undefined, options.navCtrl, true, true);
}
if (!treated) {