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

View File

@ -11,9 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import Faker from 'faker'; import Faker from 'faker';
import { CoreError } from './error'; import { CoreError } from '@classes/errors/error';
describe('CoreError', () => { describe('CoreError', () => {

View File

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

View File

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

View File

@ -23,7 +23,7 @@ import { CoreWS, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets, Core
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time'; 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 { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { CoreConstants } from '@core/constants'; import { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json'; import CoreConfigConstants from '@app/config.json';
@ -857,7 +857,7 @@ export class CoreSite {
let promise: Promise<any>; let promise: Promise<any>;
if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { 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) { if (!entries.length) {
// Cache key not found, get by params sent. // Cache key not found, get by params sent.
return this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id }); 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`); 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.')); 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) * @param componentId Optional component id (if not included, returns sum for whole component)
* @return Promise resolved when we have calculated the size * @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]; const params: Array<string | number> = [component];
let extraClause = ''; let extraClause = '';
if (componentId !== undefined && componentId !== null) { if (componentId !== undefined && componentId !== null) {
@ -923,8 +923,10 @@ export class CoreSite {
extraClause = ' AND componentId = ?'; 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); ' WHERE component = ?' + extraClause, params);
return size;
} }
/** /**
@ -1018,7 +1020,7 @@ export class CoreSite {
params['componentId'] = componentId; 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) * @return Promise resolved with the total size of all data in the cache table (bytes)
*/ */
getCacheUsage(): Promise<number> { async getCacheUsage(): Promise<number> {
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);
return size;
} }
/** /**
@ -1228,7 +1232,7 @@ export class CoreSite {
* @param anchor Anchor text if needed. * @param anchor Anchor text if needed.
* @return URL with params. * @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); 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. * @return Resolves upon success along with the config data. Reject on failure.
*/ */
getLocalSiteConfig<T extends number | string>(name: string, defaultValue?: T): Promise<T> { 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') { if (typeof defaultValue != 'undefined') {
return defaultValue; return defaultValue;
} }
@ -2151,3 +2156,8 @@ export type CoreSiteCallExternalFunctionsResult = {
exception?: string; // JSON-encoed exception info. 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 { SQLiteObject } from '@ionic-native/sqlite/ngx';
import { SQLite, Platform } from '@singletons/core.singletons'; import { SQLite, Platform } from '@singletons/core.singletons';
import { CoreError } from '@classes/errors/error';
/** /**
* Schema of a table. * Schema of a table.
@ -411,6 +412,7 @@ export class SQLiteDB {
* @param params Query parameters. * @param params Query parameters.
* @return Promise resolved with the result. * @return Promise resolved with the result.
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> { async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> {
await this.ready(); await this.ready();
@ -425,7 +427,8 @@ export class SQLiteDB {
* @param sqlStatements SQL statements to execute. * @param sqlStatements SQL statements to execute.
* @return Promise resolved with the result. * @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.ready();
await this.db.sqlBatch(sqlStatements); await this.db.sqlBatch(sqlStatements);
@ -453,6 +456,7 @@ export class SQLiteDB {
* Format the data to where params. * Format the data to where params.
* *
* @param data Object data. * @param data Object data.
* @return List of params.
*/ */
protected formatDataToSQLParams(data: SQLiteDBRecordValues): SQLiteDBRecordValue[] { 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. * @param table The table to query.
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
async getAllRecords(table: string): Promise<SQLiteDBRecordValues[]> { async getAllRecords<T = unknown>(table: string): Promise<T[]> {
return this.getRecords(table); return this.getRecords(table);
} }
@ -510,7 +514,7 @@ export class SQLiteDB {
async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> { async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> {
const record = await this.getRecordSql(sql, params); const record = await this.getRecordSql(sql, params);
if (!record) { if (!record) {
throw null; throw new CoreError('No record found.');
} }
return record[Object.keys(record)[0]]; return record[Object.keys(record)[0]];
@ -574,10 +578,10 @@ export class SQLiteDB {
* @param fields A comma separated list of fields to return. * @param fields A comma separated list of fields to return.
* @return Promise resolved with the record, rejected if not found. * @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); 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. * @param fields A comma separated list of fields to return.
* @return Promise resolved with the record, rejected if not found. * @return Promise resolved with the record, rejected if not found.
*/ */
getRecordSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'): getRecordSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'):
Promise<SQLiteDBRecordValues> { Promise<T> {
if (select) { if (select) {
select = ' WHERE ' + 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 * @param params List of sql parameters
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
async getRecordSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValues> { async getRecordSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[]): Promise<T> {
const result = await this.getRecordsSql(sql, params, 0, 1); const result = await this.getRecordsSql<T>(sql, params, 0, 1);
if (!result || !result.length) { if (!result || !result.length) {
// Not found, reject. // Not found, reject.
throw null; throw new CoreError('No records found.');
} }
return result[0]; return result[0];
@ -629,11 +633,11 @@ export class SQLiteDB {
* @param limitNum Return a subset comprising this many records in total. * @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
getRecords(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*', limitFrom: number = 0, getRecords<T = unknown>(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*',
limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
const selectAndParams = this.whereClause(conditions); 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. * @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
getRecordsList(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '', fields: string = '*', getRecordsList<T = unknown>(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '',
limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
const selectAndParams = this.whereClauseList(field, values); 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. * @param limitNum Return a subset comprising this many records in total.
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
getRecordsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '', getRecordsSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '',
fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> {
if (select) { if (select) {
select = ' WHERE ' + select; select = ' WHERE ' + select;
} }
@ -678,7 +682,7 @@ export class SQLiteDB {
const sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`; 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. * @param limitNum Return a subset comprising this many records.
* @return Promise resolved with the records. * @return Promise resolved with the records.
*/ */
async getRecordsSql(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number): async getRecordsSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number):
Promise<SQLiteDBRecordValues[]> { Promise<T[]> {
const limits = this.normaliseLimitFromNum(limitFrom, limitNum); const limits = this.normaliseLimitFromNum(limitFrom, limitNum);
if (limits[0] || limits[1]) { if (limits[0] || limits[1]) {
@ -768,7 +772,7 @@ export class SQLiteDB {
*/ */
async insertRecords(table: string, dataObjects: SQLiteDBRecordValues[]): Promise<void> { async insertRecords(table: string, dataObjects: SQLiteDBRecordValues[]): Promise<void> {
if (!Array.isArray(dataObjects)) { if (!Array.isArray(dataObjects)) {
throw null; throw new CoreError('Invalid parameter supplied to insertRecords, it should be an array.');
} }
const statements = dataObjects.map((dataObject) => { const statements = dataObjects.map((dataObject) => {
@ -854,7 +858,7 @@ export class SQLiteDB {
async recordExists(table: string, conditions?: SQLiteDBRecordValues): Promise<void> { async recordExists(table: string, conditions?: SQLiteDBRecordValues): Promise<void> {
const record = await this.getRecord(table, conditions); const record = await this.getRecord(table, conditions);
if (!record) { 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> { async recordExistsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = []): Promise<void> {
const record = await this.getRecordSelect(table, select, params); const record = await this.getRecordSelect(table, select, params);
if (!record) { 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> { async recordExistsSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<void> {
const record = await this.getRecordSql(sql, params); const record = await this.getRecordSql(sql, params);
if (!record) { 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', name: 'coreCreateLinks',
}) })
export class CoreCreateLinksPipe implements PipeTransform { export class CoreCreateLinksPipe implements PipeTransform {
protected static replacePattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])(?![^<]*>|[^<>]*<\/)/gim; 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 { transform(text: string): string {
return text.replace(CoreCreateLinksPipe.replacePattern, '<a href="$1">$1</a>'); 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 { transform(text: string): string {
return text.replace(/(<([^>]+)>)/ig, ''); return text.replace(/(<([^>]+)>)/ig, '');
} }
} }

View File

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

View File

@ -24,6 +24,7 @@ import moment from 'moment';
name: 'coreTimeAgo', name: 'coreTimeAgo',
}) })
export class CoreTimeAgoPipe implements PipeTransform { export class CoreTimeAgoPipe implements PipeTransform {
private logger: CoreLogger; private logger: CoreLogger;
constructor() { constructor() {
@ -48,6 +49,7 @@ export class CoreTimeAgoPipe implements PipeTransform {
timestamp = numberTimestamp; 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; await this.createVersionsTableReady;
// Fetch installed version of the schema. // 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; oldVersion = entry.version;
} catch (error) { } catch (error) {
// No installed version yet. // No installed version yet.
@ -796,3 +797,8 @@ export type WindowForAutomatedTests = Window & {
appProvider?: CoreAppProvider; appProvider?: CoreAppProvider;
appRef?: ApplicationRef; appRef?: ApplicationRef;
}; };
type SchemaVersionsDBEntry = {
name: string;
version: number;
};

View File

@ -81,7 +81,7 @@ export class CoreConfigProvider {
await this.dbReady; await this.dbReady;
try { try {
const entry = await this.appDB.getRecord(TABLE_NAME, { name }); const entry = await this.appDB.getRecord<ConfigDBEntry>(TABLE_NAME, { name });
return entry.value; return entry.value;
} catch (error) { } catch (error) {
@ -109,3 +109,9 @@ export class CoreConfigProvider {
} }
export class CoreConfig extends makeSingleton(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 appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized. protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected handlers: { [s: string]: CoreCronHandler } = {}; protected handlers: { [s: string]: CoreCronHandler } = {};
protected queuePromise = Promise.resolve(); protected queuePromise: Promise<void> = Promise.resolve();
constructor(zone: NgZone) { constructor(zone: NgZone) {
this.logger = CoreLogger.getInstance('CoreCronDelegate'); this.logger = CoreLogger.getInstance('CoreCronDelegate');
@ -271,8 +271,9 @@ export class CoreCronDelegate {
const id = this.getHandlerLastExecutionId(name); const id = this.getHandlerLastExecutionId(name);
try { try {
const entry = await this.appDB.getRecord(CRON_TABLE, { id }); const entry = await this.appDB.getRecord<CronDBEntry>(CRON_TABLE, { id });
const time = parseInt(entry.value, 10);
const time = Number(entry.value);
return isNaN(time) ? 0 : time; return isNaN(time) ? 0 : time;
} catch (err) { } catch (err) {
@ -573,3 +574,8 @@ export interface CoreCronHandler {
export type WindowForAutomatedTests = Window & { export type WindowForAutomatedTests = Window & {
cronProvider?: CoreCronDelegate; 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 { CoreUrlUtils } from '@services/utils/url';
import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { SQLiteDB } from '@classes/sqlitedb'; import { SQLiteDB } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error';
import { CoreConstants } from '@core/constants'; 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'; 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_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 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))'; 'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))';
// Variables for database. // Variables for database.
@ -239,7 +240,7 @@ export class CoreFilepoolProvider {
protected appDB: SQLiteDB; protected appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized. protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected queueState: string; protected queueState: string;
protected urlAttributes = [ protected urlAttributes: RegExp[] = [
new RegExp('(\\?|&)token=([A-Za-z0-9]*)'), new RegExp('(\\?|&)token=([A-Za-z0-9]*)'),
new RegExp('(\\?|&)forcedownload=[0-1]'), new RegExp('(\\?|&)forcedownload=[0-1]'),
new RegExp('(\\?|&)preview=[A-Za-z0-9]+'), new RegExp('(\\?|&)preview=[A-Za-z0-9]+'),
@ -248,7 +249,7 @@ export class CoreFilepoolProvider {
// To handle file downloads using the queue. // To handle file downloads using the queue.
protected queueDeferreds: { [s: string]: { [s: string]: CoreFilepoolPromiseDefer } } = {}; 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. // Variables to prevent downloading packages/files twice at the same time.
protected packagesPromises: { [s: string]: { [s: string]: Promise<void> } } = {}; protected packagesPromises: { [s: string]: { [s: string]: Promise<void> } } = {};
protected filePromises: { [s: string]: { [s: string]: Promise<string> } } = {}; 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> { protected async addFileLink(siteId: string, fileId: string, component: string, componentId?: string | number): Promise<void> {
if (!component) { if (!component) {
throw null; throw new CoreError('Cannot add link because component is invalid.');
} }
componentId = this.fixComponentId(componentId); componentId = this.fixComponentId(componentId);
@ -358,8 +359,10 @@ export class CoreFilepoolProvider {
* @return Promise resolved on success. * @return Promise resolved on success.
*/ */
protected async addFileToPool(siteId: string, fileId: string, data: CoreFilepoolFileEntry): Promise<void> { protected async addFileToPool(siteId: string, fileId: string, data: CoreFilepoolFileEntry): Promise<void> {
const record = Object.assign({}, data); const record = {
record.fileId = fileId; fileId,
...data,
};
const db = await CoreSites.instance.getSiteDb(siteId); const db = await CoreSites.instance.getSiteDb(siteId);
@ -457,12 +460,12 @@ export class CoreFilepoolProvider {
await this.dbReady; await this.dbReady;
if (!CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw null; throw new CoreError('File system cannot be used.');
} }
const site = await CoreSites.instance.getSite(siteId); const site = await CoreSites.instance.getSite(siteId);
if (!site.canDownloadFiles()) { if (!site.canDownloadFiles()) {
throw null; throw new CoreError('Site doesn\'t allow downloading files.');
} }
let file: CoreWSExternalFile; let file: CoreWSExternalFile;
@ -488,7 +491,7 @@ export class CoreFilepoolProvider {
const queueDeferred = this.getQueueDeferred(siteId, fileId, false, onProgress); const queueDeferred = this.getQueueDeferred(siteId, fileId, false, onProgress);
return this.hasFileInQueue(siteId, fileId).then((entry: CoreFilepoolQueueEntry) => { return this.hasFileInQueue(siteId, fileId).then((entry: CoreFilepoolQueueEntry) => {
const newData: CoreFilepoolQueueEntry = {}; const newData: CoreFilepoolQueueDBEntry = {};
let foundLink = false; let foundLink = false;
if (entry) { if (entry) {
@ -562,14 +565,14 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component. * @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified. * @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 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. * Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype). * @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL. * @param revision File revision. If not defined, it will be calculated using the URL.
* @return Promise resolved when the file is downloaded. * @return Promise resolved when the file is downloaded.
*/ */
protected async addToQueueIfNeeded(siteId: string, fileUrl: string, component: string, componentId?: string | number, 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> { revision?: number): Promise<void> {
if (!checkSize) { if (!checkSize) {
// No need to check size, just add it to the queue. // No need to check size, just add it to the queue.
@ -584,7 +587,7 @@ export class CoreFilepoolProvider {
} else { } else {
if (!CoreApp.instance.isOnline()) { if (!CoreApp.instance.isOnline()) {
// Cannot check size in offline, stop. // Cannot check size in offline, stop.
throw null; throw new CoreError(Translate.instance.instant('core.cannotconnect'));
} }
size = await CoreWS.instance.getRemoteFileSize(fileUrl); size = await CoreWS.instance.getRemoteFileSize(fileUrl);
@ -592,16 +595,16 @@ export class CoreFilepoolProvider {
// Calculate the size of the file. // Calculate the size of the file.
const isWifi = CoreApp.instance.isWifi(); const isWifi = CoreApp.instance.isWifi();
const sizeAny = size <= 0; const sizeUnknown = size <= 0;
if (!sizeAny) { if (!sizeUnknown) {
// Store the size in the cache. // Store the size in the cache.
this.sizeCache[fileUrl] = size; this.sizeCache[fileUrl] = size;
} }
// Check if the file should be downloaded. // Check if the file should be downloaded.
if (sizeAny) { if (sizeUnknown) {
if (downloadAny && isWifi) { if (downloadUnknown && isWifi) {
await this.addToQueueByUrl(siteId, fileUrl, component, componentId, timemodified, undefined, undefined, await this.addToQueueByUrl(siteId, fileUrl, component, componentId, timemodified, undefined, undefined,
0, options, revision, true); 0, options, revision, true);
} }
@ -685,7 +688,7 @@ export class CoreFilepoolProvider {
const count = await db.countRecords(CoreFilepoolProvider.LINKS_TABLE, conditions); const count = await db.countRecords(CoreFilepoolProvider.LINKS_TABLE, conditions);
if (count <= 0) { 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. * @param componentId An ID to use in conjunction with the component.
* @return Link, null if nothing to link. * @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) { if (typeof component != 'undefined' && component != null) {
return { component, componentId: this.fixComponentId(componentId) }; return { component, componentId: this.fixComponentId(componentId) };
} }
@ -779,7 +782,7 @@ export class CoreFilepoolProvider {
if (poolFileObject && poolFileObject.fileId !== fileId) { if (poolFileObject && poolFileObject.fileId !== fileId) {
this.logger.error('Invalid object to update passed'); this.logger.error('Invalid object to update passed');
throw null; throw new CoreError('Invalid object to update passed.');
} }
const downloadId = this.getFileDownloadId(fileUrl, filePath); const downloadId = this.getFileDownloadId(fileUrl, filePath);
@ -793,7 +796,7 @@ export class CoreFilepoolProvider {
this.filePromises[siteId][downloadId] = CoreSites.instance.getSite(siteId).then(async (site) => { this.filePromises[siteId][downloadId] = CoreSites.instance.getSite(siteId).then(async (site) => {
if (!site.canDownloadFiles()) { 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); const entry = await CoreWS.instance.downloadFile(fileUrl, filePath, addExtension, onProgress);
@ -952,7 +955,7 @@ export class CoreFilepoolProvider {
try { try {
await Promise.all(promises); await Promise.all(promises);
// Success prefetching, store package as downloaded. // 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) { } catch (error) {
// Error downloading, go back to previous status and reject the promise. // Error downloading, go back to previous status and reject the promise.
await this.setPackagePreviousStatus(siteId, component, componentId); await this.setPackagePreviousStatus(siteId, component, componentId);
@ -1014,7 +1017,7 @@ export class CoreFilepoolProvider {
let alreadyDownloaded = true; let alreadyDownloaded = true;
if (!CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw null; throw new CoreError('File system cannot be used.');
} }
const file = await this.fixPluginfileURL(siteId, fileUrl); const file = await this.fixPluginfileURL(siteId, fileUrl);
@ -1093,14 +1096,12 @@ export class CoreFilepoolProvider {
let urls = []; let urls = [];
const element = CoreDomUtils.instance.convertToElement(html); 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++) { for (let i = 0; i < elements.length; i++) {
const element = elements[i]; const element = elements[i];
let url = element.tagName === 'A' let url = 'href' in element ? element.href : element.src;
? (element as HTMLAnchorElement).href
: (element as HTMLImageElement | HTMLVideoElement | HTMLAudioElement |
HTMLAudioElement | HTMLTrackElement | HTMLSourceElement).src;
if (url && CoreUrlUtils.instance.isDownloadableUrl(url) && urls.indexOf(url) == -1) { if (url && CoreUrlUtils.instance.isDownloadableUrl(url) && urls.indexOf(url) == -1) {
urls.push(url); urls.push(url);
@ -1236,7 +1237,7 @@ export class CoreFilepoolProvider {
componentId: this.fixComponentId(componentId), 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) => { items.forEach((item) => {
item.componentId = this.fixComponentId(item.componentId); item.componentId = this.fixComponentId(item.componentId);
}); });
@ -1252,7 +1253,10 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise. * @return Resolved with the URL. Rejected otherwise.
*/ */
async getDirectoryUrlByUrl(siteId: string, fileUrl: string): Promise<string> { async getDirectoryUrlByUrl(siteId: string, fileUrl: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const file = await this.fixPluginfileURL(siteId, fileUrl); const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl); const fileId = this.getFileIdByUrl(file.fileurl);
const filePath = await this.getFilePath(siteId, fileId, ''); const filePath = await this.getFilePath(siteId, fileId, '');
@ -1261,9 +1265,6 @@ export class CoreFilepoolProvider {
return dirEntry.toURL(); return dirEntry.toURL();
} }
throw null;
}
/** /**
* Get the ID of a file download. Used to keep track of filePromises. * Get the ID of a file download. Used to keep track of filePromises.
* *
@ -1346,7 +1347,8 @@ export class CoreFilepoolProvider {
*/ */
protected async getFileLinks(siteId: string, fileId: string): Promise<CoreFilepoolLinksRecord[]> { protected async getFileLinks(siteId: string, fileId: string): Promise<CoreFilepoolLinksRecord[]> {
const db = await CoreSites.instance.getSiteDb(siteId); 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) => { items.forEach((item) => {
item.componentId = this.fixComponentId(item.componentId); item.componentId = this.fixComponentId(item.componentId);
}); });
@ -1421,7 +1423,7 @@ export class CoreFilepoolProvider {
const files = []; const files = [];
const promises = items.map((item) => 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) { if (!fileEntry) {
return; return;
} }
@ -1532,7 +1534,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component. * @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified. * @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 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. * Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype). * @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL. * @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. * 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, 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> { options: CoreFilepoolFileOptions = {}, revision?: number): Promise<string> {
const addToQueue = (fileUrl: string): void => { const addToQueue = (fileUrl: string): void => {
// Add the file to queue if needed and ignore errors. // Add the file to queue if needed and ignore errors.
this.addToQueueIfNeeded(siteId, fileUrl, component, componentId, timemodified, checkSize, this.addToQueueIfNeeded(siteId, fileUrl, component, componentId, timemodified, checkSize,
downloadAny, options, revision).catch(() => { downloadUnknown, options, revision).catch(() => {
// Ignore errors. // Ignore errors.
}); });
}; };
@ -1594,7 +1596,7 @@ export class CoreFilepoolProvider {
return fileUrl; 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. // We do not have the file in store yet. Add to queue and return the fixed URL.
@ -1614,16 +1616,16 @@ export class CoreFilepoolProvider {
* @return Resolved with the internal URL. Rejected otherwise. * @return Resolved with the internal URL. Rejected otherwise.
*/ */
protected async getInternalSrcById(siteId: string, fileId: string): Promise<string> { protected async getInternalSrcById(siteId: string, fileId: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const path = await this.getFilePath(siteId, fileId); const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path); const fileEntry = await CoreFile.instance.getFile(path);
return CoreFile.instance.convertFileSrc(fileEntry.toURL()); return CoreFile.instance.convertFileSrc(fileEntry.toURL());
} }
throw null;
}
/** /**
* Returns the local URL of a file. * Returns the local URL of a file.
* *
@ -1632,7 +1634,10 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise. * @return Resolved with the URL. Rejected otherwise.
*/ */
protected async getInternalUrlById(siteId: string, fileId: string): Promise<string> { protected async getInternalUrlById(siteId: string, fileId: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const path = await this.getFilePath(siteId, fileId); const path = await this.getFilePath(siteId, fileId);
const fileEntry = await CoreFile.instance.getFile(path); const fileEntry = await CoreFile.instance.getFile(path);
@ -1644,9 +1649,6 @@ export class CoreFilepoolProvider {
} }
} }
throw null;
}
/** /**
* Returns the local URL of a file. * Returns the local URL of a file.
* *
@ -1654,15 +1656,15 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. * @return Resolved with the URL.
*/ */
protected async getInternalUrlByPath(filePath: string): Promise<string> { protected async getInternalUrlByPath(filePath: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const fileEntry = await CoreFile.instance.getFile(filePath); const fileEntry = await CoreFile.instance.getFile(filePath);
return fileEntry.toURL(); return fileEntry.toURL();
} }
throw null;
}
/** /**
* Returns the local URL of a file. * Returns the local URL of a file.
* *
@ -1671,16 +1673,16 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. Rejected otherwise. * @return Resolved with the URL. Rejected otherwise.
*/ */
async getInternalUrlByUrl(siteId: string, fileUrl: string): Promise<string> { async getInternalUrlByUrl(siteId: string, fileUrl: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const file = await this.fixPluginfileURL(siteId, fileUrl); const file = await this.fixPluginfileURL(siteId, fileUrl);
const fileId = this.getFileIdByUrl(file.fileurl); const fileId = this.getFileIdByUrl(file.fileurl);
return this.getInternalUrlById(siteId, fileId); return this.getInternalUrlById(siteId, fileId);
} }
throw null;
}
/** /**
* Get the data stored for a package. * Get the data stored for a package.
* *
@ -1748,7 +1750,10 @@ export class CoreFilepoolProvider {
* @return Resolved with the URL. * @return Resolved with the URL.
*/ */
async getPackageDirUrlByUrl(siteId: string, url: string): Promise<string> { async getPackageDirUrlByUrl(siteId: string, url: string): Promise<string> {
if (CoreFile.instance.isAvailable()) { if (!CoreFile.instance.isAvailable()) {
throw new CoreError('File system cannot be used.');
}
const file = await this.fixPluginfileURL(siteId, url); const file = await this.fixPluginfileURL(siteId, url);
const dirName = this.getPackageDirNameByUrl(file.fileurl); const dirName = this.getPackageDirNameByUrl(file.fileurl);
const dirPath = await this.getFilePath(siteId, dirName, ''); const dirPath = await this.getFilePath(siteId, dirName, '');
@ -1757,9 +1762,6 @@ export class CoreFilepoolProvider {
return dirEntry.toURL(); return dirEntry.toURL();
} }
throw null;
}
/** /**
* Get a download promise. If the promise is not set, return undefined. * Get a download promise. If the promise is not set, return undefined.
* *
@ -1973,7 +1975,7 @@ export class CoreFilepoolProvider {
* @param componentId An ID to use in conjunction with the component. * @param componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified. * @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 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. * Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype). * @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL. * @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. * The URL returned is compatible to use with IMG tags.
*/ */
getSrcByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0, 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> { Promise<string> {
return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'src', 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 componentId An ID to use in conjunction with the component.
* @param timemodified The time this file was modified. * @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 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. * Ignored if checkSize=false.
* @param options Extra options (isexternalfile, repositorytype). * @param options Extra options (isexternalfile, repositorytype).
* @param revision File revision. If not defined, it will be calculated using the URL. * @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. * The URL returned is compatible to use with a local browser.
*/ */
getUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0, 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> { Promise<string> {
return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'url', 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> { protected async hasFileInPool(siteId: string, fileId: string): Promise<CoreFilepoolFileEntry> {
const db = await CoreSites.instance.getSiteDb(siteId); 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') { if (typeof entry === 'undefined') {
throw null; throw new CoreError('File not found in filepool.');
} }
return entry; return entry;
@ -2118,12 +2121,13 @@ export class CoreFilepoolProvider {
protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> { protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> {
await this.dbReady; 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') { if (typeof entry === 'undefined') {
throw null; throw new CoreError('File not found in queue.');
} }
// Convert the links to an object. // Convert the links to an object.
entry.linksUnserialized = CoreTextUtils.instance.parseJSON(entry.links, []); entry.linksUnserialized = <CoreFilepoolComponentLink[]> CoreTextUtils.instance.parseJSON(entry.links, []);
return entry; return entry;
} }
@ -2132,14 +2136,14 @@ export class CoreFilepoolProvider {
* Invalidate all the files in a site. * Invalidate all the files in a site.
* *
* @param siteId The site ID. * @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. * It is advised to set it to true to reduce the performance and data usage of the app.
* @return Resolved on success. * @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 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); await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, where);
} }
@ -2171,11 +2175,11 @@ export class CoreFilepoolProvider {
* @param siteId The site ID. * @param siteId The site ID.
* @param component The component to invalidate. * @param component The component to invalidate.
* @param componentId An ID to use in conjunction with the component. * @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. * @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. * It is advised to set it to true to reduce the performance and data usage of the app.
* @return Resolved when done. * @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> { Promise<void> {
const db = await CoreSites.instance.getSiteDb(siteId); const db = await CoreSites.instance.getSiteDb(siteId);
@ -2191,8 +2195,8 @@ export class CoreFilepoolProvider {
whereAndParams[0] = 'fileId ' + whereAndParams[0]; whereAndParams[0] = 'fileId ' + whereAndParams[0];
if (onlyAny) { if (onlyUnknown) {
whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_ANY_WHERE_CLAUSE + ')'; whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE + ')';
} }
await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, whereAndParams[0], whereAndParams[1]); await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, whereAndParams[0], whereAndParams[1]);
@ -2258,7 +2262,7 @@ export class CoreFilepoolProvider {
* @param entry Filepool entry. * @param entry Filepool entry.
* @return Whether it cannot determine updates. * @return Whether it cannot determine updates.
*/ */
protected isFileUpdateAny(entry: CoreFilepoolFileEntry): boolean { protected isFileUpdateUnknown(entry: CoreFilepoolFileEntry): boolean {
return !!entry.isexternalfile || (!entry.revision && !entry.timemodified); return !!entry.isexternalfile || (!entry.revision && !entry.timemodified);
} }
@ -2433,7 +2437,7 @@ export class CoreFilepoolProvider {
let items: CoreFilepoolQueueEntry[]; let items: CoreFilepoolQueueEntry[];
try { 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); 'priority DESC, added ASC', undefined, 0, 1);
} catch (err) { } catch (err) {
throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY; throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY;
@ -2444,7 +2448,7 @@ export class CoreFilepoolProvider {
throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY; throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY;
} }
// Convert the links to an object. // 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); return this.processQueueItem(item);
} }
@ -2760,7 +2764,7 @@ export class CoreFilepoolProvider {
const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url); const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url);
// If the file is streaming (audio or video) we reject. // If the file is streaming (audio or video) we reject.
if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { 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. * 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. * File links (to link the file to components and componentIds). Serialized to store on DB.
*/ */
links?: string; links?: string;
};
/**
* Entry from the file's queue.
*/
export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & {
/** /**
* File links (to link the file to components and componentIds). * File links (to link the file to components and componentIds).
*/ */

View File

@ -116,6 +116,7 @@ export class CoreGeolocationProvider {
* *
* @param error Error. * @param error Error.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
protected isCordovaPermissionDeniedError(error?: any): boolean { protected isCordovaPermissionDeniedError(error?: any): boolean {
return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; 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 { CoreSites } from '@services/sites';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { CoreError } from '@classes/errors/error';
import { makeSingleton, Translate } from '@singletons/core.singletons'; import { makeSingleton, Translate } from '@singletons/core.singletons';
import { CoreWSExternalWarning } from '@services/ws'; import { CoreWSExternalWarning } from '@services/ws';
import { CoreCourseBase } from '@/types/global'; import { CoreCourseBase } from '@/types/global';
@ -79,9 +80,11 @@ export class CoreGroupsProvider {
preSets.emergencyCache = false; 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) { if (!response || !response.groups) {
throw null; throw new CoreError('Activity allowed groups not found.');
} }
return response; return response;
@ -195,9 +198,11 @@ export class CoreGroupsProvider {
preSets.emergencyCache = false; 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') { if (!response || typeof response.groupmode == 'undefined') {
throw null; throw new CoreError('Activity group mode not found.');
} }
return response.groupmode; return response.groupmode;
@ -267,9 +272,11 @@ export class CoreGroupsProvider {
updateFrequency: CoreSite.FREQUENCY_RARELY, 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) { if (!response || !response.groups) {
throw null; throw new CoreError('User groups in course not found.');
} }
return response.groups; return response.groups;
@ -461,3 +468,26 @@ export type CoreGroupGetActivityAllowedGroupsResponse = {
canaccessallgroups?: boolean; // Whether the user will be able to access all the activity groups. canaccessallgroups?: boolean; // Whether the user will be able to access all the activity groups.
warnings?: CoreWSExternalWarning[]; 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 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 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 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 customStringsRaw: string;
protected sitePluginsStrings = {}; // Strings defined by site plugins. protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
constructor() { constructor() {
// Set fallback language and language to use until the app determines the right language to use. // 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. * @param language New language to use.
* @return Promise resolved when the change is finished. * @return Promise resolved when the change is finished.
*/ */
changeCurrentLanguage(language: string): Promise<unknown> { async changeCurrentLanguage(language: string): Promise<void> {
const promises = []; const promises = [];
// Change the language, resolving the promise when we receive the first value. // 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) => { const subscription = Translate.instance.use(language).subscribe((data) => {
// It's a language override, load the original one first. // It's a language override, load the original one first.
const fallbackLang = Translate.instance.instant('core.parentlanguage'); const fallbackLang = Translate.instance.instant('core.parentlanguage');
@ -165,13 +165,15 @@ export class CoreLangProvider {
this.currentLanguage = language; this.currentLanguage = language;
return Promise.all(promises).finally(() => { try {
await Promise.all(promises);
} finally {
// Load the custom and site plugins strings for the language. // Load the custom and site plugins strings for the language.
if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) { if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) {
// Some lang strings have changed, emit an event to update the pipes. // Some lang strings have changed, emit an event to update the pipes.
Translate.instance.onLangChange.emit({ lang: language, translations: Translate.instance.translations[language] }); Translate.instance.onLangChange.emit({ lang: language, translations: Translate.instance.translations[language] });
} }
}); }
} }
/** /**
@ -196,7 +198,7 @@ export class CoreLangProvider {
* *
* @return Custom strings. * @return Custom strings.
*/ */
getAllCustomStrings(): unknown { getAllCustomStrings(): CoreLanguageObject {
return this.customStrings; return this.customStrings;
} }
@ -205,7 +207,7 @@ export class CoreLangProvider {
* *
* @return Site plugins strings. * @return Site plugins strings.
*/ */
getAllSitePluginsStrings(): unknown { getAllSitePluginsStrings(): CoreLanguageObject {
return this.sitePluginsStrings; return this.sitePluginsStrings;
} }
@ -220,7 +222,7 @@ export class CoreLangProvider {
} }
// Get current language from config (user might have changed it). // 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. // User hasn't defined a language. If default language is forced, use it.
if (CoreConfigConstants.default_lang && CoreConfigConstants.forcedefaultlanguage) { if (CoreConfigConstants.default_lang && CoreConfigConstants.forcedefaultlanguage) {
return CoreConfigConstants.default_lang; return CoreConfigConstants.default_lang;
@ -283,7 +285,7 @@ export class CoreLangProvider {
* @param lang The language to check. * @param lang The language to check.
* @return Promise resolved when done. * @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. // Create a promise to convert the observable into a promise.
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
const observer = Translate.instance.getTranslation(lang).subscribe((table) => { 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 { CoreConfig } from '@services/config';
import { CoreEventObserver, CoreEvents, CoreEventsProvider } from '@services/events'; import { CoreEventObserver, CoreEvents, CoreEventsProvider } from '@services/events';
import { CoreTextUtils } from '@services/utils/text'; 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 { SQLiteDB } from '@classes/sqlitedb';
import { CoreSite } from '@classes/site';
import { CoreQueueRunner } from '@classes/queue-runner'; import { CoreQueueRunner } from '@classes/queue-runner';
import { CoreError } from '@classes/errors/error';
import { CoreConstants } from '@core/constants'; import { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json'; import CoreConfigConstants from '@app/config.json';
import { makeSingleton, NgZone, Platform, Translate, LocalNotifications, Push, Device } from '@singletons/core.singletons'; import { makeSingleton, NgZone, Platform, Translate, LocalNotifications, Push, Device } from '@singletons/core.singletons';
@ -94,14 +96,9 @@ export class CoreLocalNotificationsProvider {
protected appDB: SQLiteDB; protected appDB: SQLiteDB;
protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized. protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
protected codes: { [s: string]: number } = {}; protected codes: { [s: string]: number } = {};
protected codeRequestsQueue = {}; protected codeRequestsQueue: {[key: string]: CodeRequestsQueueItem} = {};
protected observables = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any
protected currentNotification = { protected observables: {[eventName: string]: {[component: string]: Subject<any>}} = {};
title: '',
texts: [],
ids: [],
timeouts: [],
};
protected triggerSubscription: Subscription; protected triggerSubscription: Subscription;
protected clickSubscription: 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) { if (site) {
this.cancelSiteNotifications(site.id); this.cancelSiteNotifications(site.id);
} }
@ -270,13 +267,15 @@ export class CoreLocalNotificationsProvider {
try { try {
// Check if we already have a code stored for that ID. // 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; this.codes[key] = entry.code;
return entry.code; return entry.code;
} catch (err) { } catch (err) {
// No code stored for that ID. Create a new code for it. // No code stored for that ID. Create a new code for it.
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; let newCode = 0;
if (entries.length > 0) { if (entries.length > 0) {
newCode = entries[0].code + 1; newCode = entries[0].code + 1;
@ -326,7 +325,7 @@ export class CoreLocalNotificationsProvider {
*/ */
protected getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise<number> { protected getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise<number> {
if (!siteId || !component) { 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) => return this.getSiteCode(siteId).then((siteCode) => this.getComponentCode(component).then((componentCode) =>
@ -372,7 +371,9 @@ export class CoreLocalNotificationsProvider {
await this.dbReady; await this.dbReady;
try { 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; let triggered = (notification.trigger && notification.trigger.at) || 0;
if (typeof triggered != 'number') { if (typeof triggered != 'number') {
@ -398,6 +399,7 @@ export class CoreLocalNotificationsProvider {
* *
* @param data Data received by the notification. * @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 { notifyClick(data: any): void {
this.notifyEvent('click', data); this.notifyEvent('click', data);
} }
@ -408,6 +410,7 @@ export class CoreLocalNotificationsProvider {
* @param eventName Name of the event to notify. * @param eventName Name of the event to notify.
* @param data Data received by the notification. * @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 { notifyEvent(eventName: string, data: any): void {
// Execute the code in the Angular zone, so change detection doesn't stop working. // Execute the code in the Angular zone, so change detection doesn't stop working.
NgZone.instance.run(() => { NgZone.instance.run(() => {
@ -426,6 +429,7 @@ export class CoreLocalNotificationsProvider {
* @param data Notification data. * @param data Notification data.
* @return Parsed data. * @return Parsed data.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
protected parseNotificationData(data: any): any { protected parseNotificationData(data: any): any {
if (!data) { if (!data) {
return {}; return {};
@ -454,11 +458,11 @@ export class CoreLocalNotificationsProvider {
if (typeof request == 'object' && typeof request.table != 'undefined' && typeof request.id != 'undefined') { if (typeof request == 'object' && typeof request.table != 'undefined' && typeof request.id != 'undefined') {
// Get the code and resolve/reject all the promises of this request. // Get the code and resolve/reject all the promises of this request.
promise = this.getCode(request.table, request.id).then((code) => { promise = this.getCode(request.table, request.id).then((code) => {
request.promises.forEach((p) => { request.deferreds.forEach((p) => {
p.resolve(code); p.resolve(code);
}); });
}).catch((error) => { }).catch((error) => {
request.promises.forEach((p) => { request.deferreds.forEach((p) => {
p.reject(error); p.reject(error);
}); });
}); });
@ -508,7 +512,7 @@ export class CoreLocalNotificationsProvider {
return { return {
off: (): void => { 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') { if (typeof this.codeRequestsQueue[key] != 'undefined') {
// There's already a pending request for this store and ID, add the promise to it. // 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 { } else {
// Add a pending request to the queue. // Add a pending request to the queue.
this.codeRequestsQueue[key] = { this.codeRequestsQueue[key] = {
table: table, table: table,
id: id, id: id,
promises: [deferred], deferreds: [deferred],
}; };
} }
@ -682,7 +686,7 @@ export class CoreLocalNotificationsProvider {
const entry = { const entry = {
id: notification.id, id: notification.id,
at: notification.trigger && notification.trigger.at ? notification.trigger.at : Date.now(), at: notification.trigger && notification.trigger.at ? notification.trigger.at.getTime() : Date.now(),
}; };
return this.appDB.insertRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, entry); return this.appDB.insertRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, entry);
@ -709,3 +713,9 @@ export class CoreLocalNotificationsProvider {
export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {} export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {}
export type CoreLocalNotificationsClickCallback<T = unknown> = (value: T) => void; 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 { CoreConstants } from '@core/constants';
import CoreConfigConstants from '@app/config.json'; import CoreConfigConstants from '@app/config.json';
import { import {
CoreSite, CoreSiteWSPreSets, LocalMobileResponse, CoreSiteConfig, CoreSitePublicConfigResponse, CoreSiteInfoResponse, CoreSite,
CoreSiteWSPreSets,
LocalMobileResponse,
CoreSiteInfo,
CoreSiteConfig,
CoreSitePublicConfigResponse,
CoreSiteInfoResponse,
} from '@classes/site'; } from '@classes/site';
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
@ -123,7 +129,7 @@ export class CoreSitesProvider {
await db.tableExists(oldTable); await db.tableExists(oldTable);
// Move the records from the old table. // Move the records from the old table.
const sites = await db.getAllRecords(oldTable); const sites = await db.getAllRecords<SiteDBEntry>(oldTable);
const promises = []; const promises = [];
sites.forEach((site) => { sites.forEach((site) => {
@ -818,9 +824,9 @@ export class CoreSitesProvider {
id, id,
siteUrl, siteUrl,
token, token,
info: info ? JSON.stringify(info) : info, info: info ? JSON.stringify(info) : undefined,
privateToken, privateToken,
config: config ? JSON.stringify(config) : config, config: config ? JSON.stringify(config) : undefined,
loggedOut: 0, loggedOut: 0,
oauthId, oauthId,
}; };
@ -1094,7 +1100,7 @@ export class CoreSitesProvider {
return this.sites[siteId]; return this.sites[siteId];
} else { } else {
// Retrieve and create the site. // 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); return this.makeSiteFromSiteListEntry(data);
} }
@ -1106,16 +1112,12 @@ export class CoreSitesProvider {
* @param entry Site list entry. * @param entry Site list entry.
* @return Promised resolved with the created site. * @return Promised resolved with the created site.
*/ */
makeSiteFromSiteListEntry(entry: any): Promise<CoreSite> { makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> {
let info = entry.info;
let config = entry.config;
// Parse info and config. // Parse info and config.
info = info ? CoreTextUtils.instance.parseJSON(info) : info; const info = entry.info ? <CoreSiteInfo> CoreTextUtils.instance.parseJSON(entry.info) : undefined;
config = config ? CoreTextUtils.instance.parseJSON(config) : config; const config = entry.config ? <CoreSiteConfig> CoreTextUtils.instance.parseJSON(entry.config) : undefined;
const site = new CoreSite(entry.id, entry.siteUrl, entry.token, const site = new CoreSite(entry.id, entry.siteUrl, entry.token, info, entry.privateToken, config, entry.loggedOut == 1);
info, entry.privateToken, config, entry.loggedOut == 1);
site.setOAuthId(entry.oauthId); site.setOAuthId(entry.oauthId);
return this.migrateSiteSchemas(site).then(() => { return this.migrateSiteSchemas(site).then(() => {
@ -1171,20 +1173,20 @@ export class CoreSitesProvider {
async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> { async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> {
await this.dbReady; await this.dbReady;
const sites = await this.appDB.getAllRecords(SITES_TABLE); const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE);
const formattedSites = []; const formattedSites = [];
sites.forEach((site) => { sites.forEach((site) => {
if (!ids || ids.indexOf(site.id) > -1) { if (!ids || ids.indexOf(site.id) > -1) {
// Parse info. // 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 = { const basicInfo: CoreSiteBasicInfo = {
id: site.id, id: site.id,
siteUrl: site.siteUrl, siteUrl: site.siteUrl,
fullName: siteInfo && siteInfo.fullname, fullName: siteInfo?.fullname,
siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo && siteInfo.sitename, siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo?.sitename,
avatar: siteInfo && siteInfo.userpictureurl, avatar: siteInfo?.userpictureurl,
siteHomeId: siteInfo && siteInfo.siteid || 1, siteHomeId: siteInfo?.siteid || 1,
}; };
formattedSites.push(basicInfo); formattedSites.push(basicInfo);
} }
@ -1231,7 +1233,7 @@ export class CoreSitesProvider {
async getLoggedInSitesIds(): Promise<string[]> { async getLoggedInSitesIds(): Promise<string[]> {
await this.dbReady; 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); return sites.map((site) => site.id);
} }
@ -1244,7 +1246,7 @@ export class CoreSitesProvider {
async getSitesIds(): Promise<string[]> { async getSitesIds(): Promise<string[]> {
await this.dbReady; 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); return sites.map((site) => site.id);
} }
@ -1314,7 +1316,7 @@ export class CoreSitesProvider {
this.sessionRestored = true; this.sessionRestored = true;
try { 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; const siteId = currentSite.siteId;
this.logger.debug(`Restore session in site ${siteId}`); this.logger.debug(`Restore session in site ${siteId}`);
@ -1495,7 +1497,7 @@ export class CoreSitesProvider {
} }
try { try {
const siteEntries = await this.appDB.getAllRecords(SITES_TABLE); const siteEntries = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE);
const ids = []; const ids = [];
const promises = []; const promises = [];
@ -1528,7 +1530,7 @@ export class CoreSitesProvider {
async getStoredCurrentSiteId(): Promise<string> { async getStoredCurrentSiteId(): Promise<string> {
await this.dbReady; 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; return currentSite.siteId;
} }
@ -1685,7 +1687,7 @@ export class CoreSitesProvider {
const db = site.getDb(); const db = site.getDb();
// Fetch installed versions of the schema. // 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} = {}; const versions: {[name: string]: number} = {};
records.forEach((record) => { records.forEach((record) => {
@ -2048,3 +2050,24 @@ export type CoreSitesLoginTokenResponse = {
debuginfo?: string; debuginfo?: string;
reproductionlink?: 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. * @param siteId Site ID. If not defined, current site.
* @return Record if found or reject. * @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 })); 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. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved with done. * @return Promise resolved with done.
*/ */
async insertOrUpdateSyncRecord(component: string, id: string | number, data: Record<string, unknown>, siteId?: string): async insertOrUpdateSyncRecord(component: string, id: string, data: CoreSyncRecord, siteId?: string): Promise<void> {
Promise<void> {
const db = await CoreSites.instance.getSiteDb(siteId); const db = await CoreSites.instance.getSiteDb(siteId);
data.component = component; data.component = component;
@ -212,3 +211,10 @@ export class CoreSyncProvider {
} }
export class CoreSync extends makeSingleton(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 promises = [];
const versionCode = CoreConfigConstants.versioncode; 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) { if (versionCode >= 3900 && versionApplied < 3900 && versionApplied > 0) {
// @todo: H5P update. // @todo: H5P update.

View File

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

View File

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

View File

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

View File

@ -38,7 +38,7 @@ export class CoreArray {
*/ */
static flatten<T>(arr: T[][]): T[] { static flatten<T>(arr: T[][]): T[] {
if ('flat' in arr) { 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); 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/ // discuss at: https://locutus.io/php/substr_replace/
// original by: Brett Zamir (https://brett-zamir.me) // original by: Brett Zamir (https://brett-zamir.me)
// example 1: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 0) // 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. * NavController to use when opening the link in the app.
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
navCtrl?: any; // @todo NavController; navCtrl?: any; // @todo NavController;
}; };
@ -60,11 +61,13 @@ export class CoreWindow {
await CoreUtils.instance.openFile(url); await CoreUtils.instance.openFile(url);
} else { } else {
let treated: boolean; let treated: boolean;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options = options || {}; options = options || {};
if (name != '_system') { if (name != '_system') {
// Check if it can be opened in the app. // 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) { if (!treated) {