From 6592e2299827a033e0e8fa0c6b2a8aae8f27669c Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 14 Oct 2020 16:38:24 +0200 Subject: [PATCH] MOBILE-3565 core: Fix more ESLint after first ESLint fix integration --- src/app/classes/delegate.ts | 22 +-- src/app/classes/error.test.ts | 3 +- src/app/classes/interceptor.ts | 17 +- src/app/classes/queue-runner.ts | 2 + src/app/classes/site.ts | 30 ++-- src/app/classes/sqlitedb.ts | 58 ++++--- src/app/pipes/create-links.pipe.ts | 2 + src/app/pipes/no-tags.pipe.ts | 1 + src/app/pipes/pipes.module.ts | 2 +- src/app/pipes/time-ago.pipe.ts | 4 +- src/app/services/app.ts | 8 +- src/app/services/config.ts | 8 +- src/app/services/cron.ts | 12 +- src/app/services/filepool.ts | 217 ++++++++++++------------ src/app/services/geolocation.ts | 1 + src/app/services/groups.ts | 42 ++++- src/app/services/lang.ts | 22 +-- src/app/services/local-notifications.ts | 50 +++--- src/app/services/sites.ts | 73 +++++--- src/app/services/sync.ts | 12 +- src/app/services/update-manager.ts | 2 +- src/app/services/utils/iframe.ts | 2 +- src/app/services/utils/url.ts | 6 +- src/app/services/ws.ts | 1 + src/app/singletons/array.ts | 2 +- src/app/singletons/locutus.ts | 2 +- src/app/singletons/window.ts | 7 +- 27 files changed, 365 insertions(+), 243 deletions(-) diff --git a/src/app/classes/delegate.ts b/src/app/classes/delegate.ts index dc455ea1b..a3c9665fa 100644 --- a/src/app/classes/delegate.ts +++ b/src/app/classes/delegate.ts @@ -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(handlerName: string, fnName: string, params?: unknown[]): T { + return this.execute(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(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(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 { + protected updateHandler(handler: CoreDelegateHandler): Promise { const siteId = CoreSites.instance.getCurrentSiteId(); const currentSite = CoreSites.instance.getCurrentSite(); let promise: Promise; @@ -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. } diff --git a/src/app/classes/error.test.ts b/src/app/classes/error.test.ts index 133f5bd01..e7f0e9cf5 100644 --- a/src/app/classes/error.test.ts +++ b/src/app/classes/error.test.ts @@ -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', () => { diff --git a/src/app/classes/interceptor.ts b/src/app/classes/interceptor.ts index 587c9ca9d..c7fcaa782 100644 --- a/src/app/classes/interceptor.ts +++ b/src/app/classes/interceptor.ts @@ -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, next: HttpHandler): Observable { // Add the header and serialize the body if needed. const newReq = req.clone({ diff --git a/src/app/classes/queue-runner.ts b/src/app/classes/queue-runner.ts index aa9868ccd..3b05b1369 100644 --- a/src/app/classes/queue-runner.ts +++ b/src/app/classes/queue-runner.ts @@ -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 = (...args: any[]) => T | Promise; /** * Queue item. */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type CoreQueueRunnerItem = { /** * Item ID. diff --git a/src/app/classes/site.ts b/src/app/classes/site.ts index 9651d195f..97cad6e9c 100644 --- a/src/app/classes/site.ts +++ b/src/app/classes/site.ts @@ -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; if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { - promise = this.db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => { + promise = this.db.getRecords(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 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 { + async getComponentCacheSize(component: string, componentId?: number): Promise { const params: Array = [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 = 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 { - return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE); + async getCacheUsage(): Promise { + const size = 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(name: string, defaultValue?: T): Promise { - return this.db.getRecord(CoreSite.CONFIG_TABLE, { name }).then((entry) => entry.value).catch((error) => { + return this.db.getRecord(CoreSite.CONFIG_TABLE, { name }).then((entry) => 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; +}; diff --git a/src/app/classes/sqlitedb.ts b/src/app/classes/sqlitedb.ts index 135770a52..ff0acb7da 100644 --- a/src/app/classes/sqlitedb.ts +++ b/src/app/classes/sqlitedb.ts @@ -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 { 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 { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async executeBatch(sqlStatements: (string | string[] | any)[]): Promise { 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 { + async getAllRecords(table: string): Promise { return this.getRecords(table); } @@ -510,7 +514,7 @@ export class SQLiteDB { async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise { 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 { + getRecord(table: string, conditions?: SQLiteDBRecordValues, fields: string = '*'): Promise { const selectAndParams = this.whereClause(conditions); - return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields); + return this.getRecordSelect(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 { + getRecordSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'): + Promise { if (select) { select = ' WHERE ' + select; } - return this.getRecordSql(`SELECT ${fields} FROM ${table} ${select}`, params); + return this.getRecordSql(`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 { - const result = await this.getRecordsSql(sql, params, 0, 1); + async getRecordSql(sql: string, params?: SQLiteDBRecordValue[]): Promise { + const result = await this.getRecordsSql(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 { + getRecords(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*', + limitFrom: number = 0, limitNum: number = 0): Promise { const selectAndParams = this.whereClause(conditions); - return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); + return this.getRecordsSelect(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 { + getRecordsList(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '', + fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise { const selectAndParams = this.whereClauseList(field, values); - return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); + return this.getRecordsSelect(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 { + getRecordsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '', + fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise { 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(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 { + async getRecordsSql(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number): + Promise { 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 { 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 { 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 { 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 { const record = await this.getRecordSql(sql, params); if (!record) { - throw null; + throw new CoreError('Record does not exist.'); } } diff --git a/src/app/pipes/create-links.pipe.ts b/src/app/pipes/create-links.pipe.ts index e031c9b0d..72a8ced9f 100644 --- a/src/app/pipes/create-links.pipe.ts +++ b/src/app/pipes/create-links.pipe.ts @@ -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, '$1'); } + } diff --git a/src/app/pipes/no-tags.pipe.ts b/src/app/pipes/no-tags.pipe.ts index c1da56431..8fb0c83b5 100644 --- a/src/app/pipes/no-tags.pipe.ts +++ b/src/app/pipes/no-tags.pipe.ts @@ -31,4 +31,5 @@ export class CoreNoTagsPipe implements PipeTransform { transform(text: string): string { return text.replace(/(<([^>]+)>)/ig, ''); } + } diff --git a/src/app/pipes/pipes.module.ts b/src/app/pipes/pipes.module.ts index 053fec971..62373d7f6 100644 --- a/src/app/pipes/pipes.module.ts +++ b/src/app/pipes/pipes.module.ts @@ -28,6 +28,6 @@ import { CoreTimeAgoPipe } from './time-ago.pipe'; CoreCreateLinksPipe, CoreNoTagsPipe, CoreTimeAgoPipe, - ] + ], }) export class CorePipesModule {} diff --git a/src/app/pipes/time-ago.pipe.ts b/src/app/pipes/time-ago.pipe.ts index ad53c086a..a768b9a37 100644 --- a/src/app/pipes/time-ago.pipe.ts +++ b/src/app/pipes/time-ago.pipe.ts @@ -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) }); } + } diff --git a/src/app/services/app.ts b/src/app/services/app.ts index 496761fe9..84d223281 100644 --- a/src/app/services/app.ts +++ b/src/app/services/app.ts @@ -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(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; +}; diff --git a/src/app/services/config.ts b/src/app/services/config.ts index 46faa027b..5fb874158 100644 --- a/src/app/services/config.ts +++ b/src/app/services/config.ts @@ -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(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; +}; diff --git a/src/app/services/cron.ts b/src/app/services/cron.ts index ab6b2c9c4..53c23b38a 100644 --- a/src/app/services/cron.ts +++ b/src/app/services/cron.ts @@ -64,7 +64,7 @@ export class CoreCronDelegate { protected appDB: SQLiteDB; protected dbReady: Promise; // Promise resolved when the app DB is initialized. protected handlers: { [s: string]: CoreCronHandler } = {}; - protected queuePromise = Promise.resolve(); + protected queuePromise: Promise = 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(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; +}; diff --git a/src/app/services/filepool.ts b/src/app/services/filepool.ts index ac0ea8b9a..b910c9334 100644 --- a/src/app/services/filepool.ts +++ b/src/app/services/filepool.ts @@ -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; // 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 } } = {}; protected filePromises: { [s: string]: { [s: string]: Promise } } = {}; @@ -288,7 +289,7 @@ export class CoreFilepoolProvider { */ protected async addFileLink(siteId: string, fileId: string, component: string, componentId?: string | number): Promise { 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 { - 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 { 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(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 { - 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 { const db = await CoreSites.instance.getSiteDb(siteId); - const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, { fileId }); + const items = await db.getRecords(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(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 { 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 { - 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 { - 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 { - 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 { - 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 { - 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 { 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 { 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 { const db = await CoreSites.instance.getSiteDb(siteId); - const entry = await db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId }); + const entry = await db.getRecord(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 { await this.dbReady; - const entry = await this.appDB.getRecord(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId }); + const entry = await this.appDB.getRecord(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 = 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 { + async invalidateAllFiles(siteId: string, onlyUnknown: boolean = true): Promise { 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 { 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(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 = 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). */ diff --git a/src/app/services/geolocation.ts b/src/app/services/geolocation.ts index 0423c0cfc..317e053f2 100644 --- a/src/app/services/geolocation.ts +++ b/src/app/services/geolocation.ts @@ -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; } diff --git a/src/app/services/groups.ts b/src/app/services/groups.ts index b49c42339..ed829076a 100644 --- a/src/app/services/groups.ts +++ b/src/app/services/groups.ts @@ -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[]; +}; diff --git a/src/app/services/lang.ts b/src/app/services/lang.ts index 0f56a1edc..7a8d30cbb 100644 --- a/src/app/services/lang.ts +++ b/src/app/services/lang.ts @@ -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 { + async changeCurrentLanguage(language: string): Promise { 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('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 { + getTranslationTable(lang: string): Promise> { // Create a promise to convert the observable into a promise. return new Promise((resolve, reject): void => { const observer = Translate.instance.getTranslation(lang).subscribe((table) => { diff --git a/src/app/services/local-notifications.ts b/src/app/services/local-notifications.ts index 22da70f92..f3638d86e 100644 --- a/src/app/services/local-notifications.ts +++ b/src/app/services/local-notifications.ts @@ -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; // 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}} = {}; 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 { 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 = (value: T) => void; + +type CodeRequestsQueueItem = { + table: string; + id: string; + deferreds: PromiseDefer[]; +}; diff --git a/src/app/services/sites.ts b/src/app/services/sites.ts index 37aae6fff..6efc5d214 100644 --- a/src/app/services/sites.ts +++ b/src/app/services/sites.ts @@ -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(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(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 { - let info = entry.info; - let config = entry.config; - + makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise { // Parse info and config. - info = info ? CoreTextUtils.instance.parseJSON(info) : info; - config = config ? CoreTextUtils.instance.parseJSON(config) : config; + const info = entry.info ? CoreTextUtils.instance.parseJSON(entry.info) : undefined; + const config = entry.config ? 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 { await this.dbReady; - const sites = await this.appDB.getAllRecords(SITES_TABLE); + const sites = await this.appDB.getAllRecords(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 ? 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 { await this.dbReady; - const sites = await this.appDB.getRecords(SITES_TABLE, { loggedOut : 0 }); + const sites = await this.appDB.getRecords(SITES_TABLE, { loggedOut : 0 }); return sites.map((site) => site.id); } @@ -1244,7 +1246,7 @@ export class CoreSitesProvider { async getSitesIds(): Promise { await this.dbReady; - const sites = await this.appDB.getAllRecords(SITES_TABLE); + const sites = await this.appDB.getAllRecords(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(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(SITES_TABLE); const ids = []; const promises = []; @@ -1528,7 +1530,7 @@ export class CoreSitesProvider { async getStoredCurrentSiteId(): Promise { await this.dbReady; - const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 }); + const currentSite = await this.appDB.getRecord(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(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; +}; diff --git a/src/app/services/sync.ts b/src/app/services/sync.ts index 525c5c558..fae730556 100644 --- a/src/app/services/sync.ts +++ b/src/app/services/sync.ts @@ -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> { + getSyncRecord(component: string, id: string | number, siteId?: string): Promise { 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, siteId?: string): - Promise { + async insertOrUpdateSyncRecord(component: string, id: string, data: CoreSyncRecord, siteId?: string): Promise { 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; +}; diff --git a/src/app/services/update-manager.ts b/src/app/services/update-manager.ts index 96de478d9..daa952309 100644 --- a/src/app/services/update-manager.ts +++ b/src/app/services/update-manager.ts @@ -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(VERSION_APPLIED, 0); if (versionCode >= 3900 && versionApplied < 3900 && versionApplied > 0) { // @todo: H5P update. diff --git a/src/app/services/utils/iframe.ts b/src/app/services/utils/iframe.ts index 3dae9ab13..ecaa31db6 100644 --- a/src/app/services/utils/iframe.ts +++ b/src/app/services/utils/iframe.ts @@ -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(); diff --git a/src/app/services/utils/url.ts b/src/app/services/utils/url.ts index 760eb01aa..4af96bb88 100644 --- a/src/app/services/utils/url.ts +++ b/src/app/services/utils/url.ts @@ -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(); } diff --git a/src/app/services/ws.ts b/src/app/services/ws.ts index fa7fc3884..51145b979 100644 --- a/src/app/services/ws.ts +++ b/src/app/services/ws.ts @@ -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'; diff --git a/src/app/singletons/array.ts b/src/app/singletons/array.ts index a35ca1450..1e3da6b62 100644 --- a/src/app/singletons/array.ts +++ b/src/app/singletons/array.ts @@ -38,7 +38,7 @@ export class CoreArray { */ static flatten(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); diff --git a/src/app/singletons/locutus.ts b/src/app/singletons/locutus.ts index 0cc32e5c7..8f11cc7a1 100644 --- a/src/app/singletons/locutus.ts +++ b/src/app/singletons/locutus.ts @@ -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) diff --git a/src/app/singletons/window.ts b/src/app/singletons/window.ts index 2fe01da42..59b6542e6 100644 --- a/src/app/singletons/window.ts +++ b/src/app/singletons/window.ts @@ -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) {