MOBILE-3565 core: Fix more ESLint after first ESLint fix integration
This commit is contained in:
		
							parent
							
								
									6fc97ed30f
								
							
						
					
					
						commit
						6592e22998
					
				| @ -78,7 +78,7 @@ export class CoreDelegate { | ||||
|     /** | ||||
|      * Function to resolve the handlers init promise. | ||||
|      */ | ||||
|     protected handlersInitResolve: (value?: any) => void; | ||||
|     protected handlersInitResolve: () => void; | ||||
| 
 | ||||
|     /** | ||||
|      * Constructor of the Delegate. | ||||
| @ -110,8 +110,8 @@ export class CoreDelegate { | ||||
|      * @param params Parameters to pass to the function. | ||||
|      * @return Function returned value or default value. | ||||
|      */ | ||||
|     protected executeFunctionOnEnabled(handlerName: string, fnName: string, params?: any[]): any { | ||||
|         return this.execute(this.enabledHandlers[handlerName], fnName, params); | ||||
|     protected executeFunctionOnEnabled<T = unknown>(handlerName: string, fnName: string, params?: unknown[]): T { | ||||
|         return this.execute<T>(this.enabledHandlers[handlerName], fnName, params); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -123,7 +123,7 @@ export class CoreDelegate { | ||||
|      * @param params Parameters to pass to the function. | ||||
|      * @return Function returned value or default value. | ||||
|      */ | ||||
|     protected executeFunction(handlerName: string, fnName: string, params?: any[]): any { | ||||
|     protected executeFunction<T = unknown>(handlerName: string, fnName: string, params?: unknown[]): T { | ||||
|         return this.execute(this.handlers[handlerName], fnName, params); | ||||
|     } | ||||
| 
 | ||||
| @ -136,7 +136,7 @@ export class CoreDelegate { | ||||
|      * @param params Parameters to pass to the function. | ||||
|      * @return Function returned value or default value. | ||||
|      */ | ||||
|     private execute(handler: CoreDelegateHandler, fnName: string, params?: any[]): any { | ||||
|     private execute<T = unknown>(handler: CoreDelegateHandler, fnName: string, params?: unknown[]): T { | ||||
|         if (handler && handler[fnName]) { | ||||
|             return handler[fnName].apply(handler, params); | ||||
|         } else if (this.defaultHandler && this.defaultHandler[fnName]) { | ||||
| @ -180,10 +180,10 @@ export class CoreDelegate { | ||||
|      * @param onlyEnabled If check only enabled handlers or all. | ||||
|      * @return Function returned value or default value. | ||||
|      */ | ||||
|     protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): any { | ||||
|     protected hasFunction(handlerName: string, fnName: string, onlyEnabled: boolean = true): boolean { | ||||
|         const handler = onlyEnabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName]; | ||||
| 
 | ||||
|         return handler && handler[fnName]; | ||||
|         return handler && typeof handler[fnName] == 'function'; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -240,7 +240,7 @@ export class CoreDelegate { | ||||
|      * @param time Time this update process started. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     protected updateHandler(handler: CoreDelegateHandler, time: number): Promise<void> { | ||||
|     protected updateHandler(handler: CoreDelegateHandler): Promise<void> { | ||||
|         const siteId = CoreSites.instance.getCurrentSiteId(); | ||||
|         const currentSite = CoreSites.instance.getCurrentSite(); | ||||
|         let promise: Promise<boolean>; | ||||
| @ -255,7 +255,7 @@ export class CoreDelegate { | ||||
|         if (!CoreSites.instance.isLoggedIn() || this.isFeatureDisabled(handler, currentSite)) { | ||||
|             promise = Promise.resolve(false); | ||||
|         } else { | ||||
|             promise = handler.isEnabled().catch(() => false); | ||||
|             promise = Promise.resolve(handler.isEnabled()).catch(() => false); | ||||
|         } | ||||
| 
 | ||||
|         // Checks if the handler is enabled.
 | ||||
| @ -304,7 +304,7 @@ export class CoreDelegate { | ||||
| 
 | ||||
|         // Loop over all the handlers.
 | ||||
|         for (const name in this.handlers) { | ||||
|             promises.push(this.updateHandler(this.handlers[name], now)); | ||||
|             promises.push(this.updateHandler(this.handlers[name])); | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
| @ -326,7 +326,7 @@ export class CoreDelegate { | ||||
|      * Update handlers Data. | ||||
|      * Override this function to update handlers data. | ||||
|      */ | ||||
|     updateData(): any { | ||||
|     updateData(): void { | ||||
|         // To be overridden.
 | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -11,9 +11,10 @@ | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import Faker from 'faker'; | ||||
| 
 | ||||
| import { CoreError } from './error'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| 
 | ||||
| describe('CoreError', () => { | ||||
| 
 | ||||
|  | ||||
| @ -30,28 +30,26 @@ export class CoreInterceptor implements HttpInterceptor { | ||||
|      * @param addNull Add null values to the serialized as empty parameters. | ||||
|      * @return Serialization of the object. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     static serialize(obj: any, addNull?: boolean): string { | ||||
|         let query = ''; | ||||
|         let fullSubName: string; | ||||
|         let subValue; | ||||
|         let innerObj; | ||||
| 
 | ||||
|         for (const name in obj) { | ||||
|             const value = obj[name]; | ||||
| 
 | ||||
|             if (value instanceof Array) { | ||||
|                 for (let i = 0; i < value.length; ++i) { | ||||
|                     subValue = value[i]; | ||||
|                     fullSubName = name + '[' + i + ']'; | ||||
|                     innerObj = {}; | ||||
|                     const subValue = value[i]; | ||||
|                     const fullSubName = name + '[' + i + ']'; | ||||
|                     const innerObj = {}; | ||||
|                     innerObj[fullSubName] = subValue; | ||||
|                     query += this.serialize(innerObj) + '&'; | ||||
|                 } | ||||
|             } else if (value instanceof Object) { | ||||
|                 for (const subName in value) { | ||||
|                     subValue = value[subName]; | ||||
|                     fullSubName = name + '[' + subName + ']'; | ||||
|                     innerObj = {}; | ||||
|                     const subValue = value[subName]; | ||||
|                     const fullSubName = name + '[' + subName + ']'; | ||||
|                     const innerObj = {}; | ||||
|                     innerObj[fullSubName] = subValue; | ||||
|                     query += this.serialize(innerObj) + '&'; | ||||
|                 } | ||||
| @ -63,6 +61,7 @@ export class CoreInterceptor implements HttpInterceptor { | ||||
|         return query.length ? query.substr(0, query.length - 1) : query; | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> { | ||||
|         // Add the header and serialize the body if needed.
 | ||||
|         const newReq = req.clone({ | ||||
|  | ||||
| @ -17,11 +17,13 @@ import { CoreUtils, PromiseDefer } from '@services/utils/utils'; | ||||
| /** | ||||
|  * Function to add to the queue. | ||||
|  */ | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
| export type CoreQueueRunnerFunction<T> = (...args: any[]) => T | Promise<T>; | ||||
| 
 | ||||
| /** | ||||
|  * Queue item. | ||||
|  */ | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
| export type CoreQueueRunnerItem<T = any> = { | ||||
|     /** | ||||
|      * Item ID. | ||||
|  | ||||
| @ -23,7 +23,7 @@ import { CoreWS, CoreWSPreSets, CoreWSFileUploadOptions, CoreWSAjaxPreSets, Core | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| import { CoreUrlUtils, CoreUrlParams } from '@services/utils/url'; | ||||
| import { CoreUtils, PromiseDefer } from '@services/utils/utils'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import CoreConfigConstants from '@app/config.json'; | ||||
| @ -857,7 +857,7 @@ export class CoreSite { | ||||
|         let promise: Promise<any>; | ||||
| 
 | ||||
|         if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { | ||||
|             promise = this.db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => { | ||||
|             promise = this.db.getRecords<any>(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => { | ||||
|                 if (!entries.length) { | ||||
|                     // Cache key not found, get by params sent.
 | ||||
|                     return this.db.getRecord(CoreSite.WS_CACHE_TABLE, { id }); | ||||
| @ -901,7 +901,7 @@ export class CoreSite { | ||||
|                     this.logger.info(`Cached element found, id: ${id}. Expires in expires in ${expires} seconds`); | ||||
|                 } | ||||
| 
 | ||||
|                 return CoreTextUtils.instance.parseJSON(entry.data, {}); | ||||
|                 return <T> CoreTextUtils.instance.parseJSON(entry.data, {}); | ||||
|             } | ||||
| 
 | ||||
|             return Promise.reject(new CoreError('Cache entry not valid.')); | ||||
| @ -915,7 +915,7 @@ export class CoreSite { | ||||
|      * @param componentId Optional component id (if not included, returns sum for whole component) | ||||
|      * @return Promise resolved when we have calculated the size | ||||
|      */ | ||||
|     getComponentCacheSize(component: string, componentId?: number): Promise<number> { | ||||
|     async getComponentCacheSize(component: string, componentId?: number): Promise<number> { | ||||
|         const params: Array<string | number> = [component]; | ||||
|         let extraClause = ''; | ||||
|         if (componentId !== undefined && componentId !== null) { | ||||
| @ -923,8 +923,10 @@ export class CoreSite { | ||||
|             extraClause = ' AND componentId = ?'; | ||||
|         } | ||||
| 
 | ||||
|         return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE + | ||||
|         const size = <number> await this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE + | ||||
|                 ' WHERE component = ?' + extraClause, params); | ||||
| 
 | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1018,7 +1020,7 @@ export class CoreSite { | ||||
|             params['componentId'] = componentId; | ||||
|         } | ||||
| 
 | ||||
|         return this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params); | ||||
|         await this.db.deleteRecords(CoreSite.WS_CACHE_TABLE, params); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
| @ -1192,8 +1194,10 @@ export class CoreSite { | ||||
|      * | ||||
|      * @return Promise resolved with the total size of all data in the cache table (bytes) | ||||
|      */ | ||||
|     getCacheUsage(): Promise<number> { | ||||
|         return this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE); | ||||
|     async getCacheUsage(): Promise<number> { | ||||
|         const size = <number> await this.db.getFieldSql('SELECT SUM(length(data)) FROM ' + CoreSite.WS_CACHE_TABLE); | ||||
| 
 | ||||
|         return size; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1228,7 +1232,7 @@ export class CoreSite { | ||||
|      * @param anchor Anchor text if needed. | ||||
|      * @return URL with params. | ||||
|      */ | ||||
|     createSiteUrl(path: string, params?: {[key: string]: unknown}, anchor?: string): string { | ||||
|     createSiteUrl(path: string, params?: CoreUrlParams, anchor?: string): string { | ||||
|         return CoreUrlUtils.instance.addParamsToUrl(this.siteUrl + path, params, anchor); | ||||
|     } | ||||
| 
 | ||||
| @ -1797,7 +1801,8 @@ export class CoreSite { | ||||
|      * @return Resolves upon success along with the config data. Reject on failure. | ||||
|      */ | ||||
|     getLocalSiteConfig<T extends number | string>(name: string, defaultValue?: T): Promise<T> { | ||||
|         return this.db.getRecord(CoreSite.CONFIG_TABLE, { name }).then((entry) => entry.value).catch((error) => { | ||||
|         return this.db.getRecord<CoreSiteConfigDBRecord>(CoreSite.CONFIG_TABLE, { name }).then((entry) => <T> entry.value) | ||||
|                 .catch((error) => { | ||||
|             if (typeof defaultValue != 'undefined') { | ||||
|                 return defaultValue; | ||||
|             } | ||||
| @ -2151,3 +2156,8 @@ export type CoreSiteCallExternalFunctionsResult = { | ||||
|         exception?: string; // JSON-encoed exception info.
 | ||||
|     }[]; | ||||
| }; | ||||
| 
 | ||||
| export type CoreSiteConfigDBRecord = { | ||||
|     name: string; | ||||
|     value: string | number; | ||||
| }; | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| import { SQLiteObject } from '@ionic-native/sqlite/ngx'; | ||||
| 
 | ||||
| import { SQLite, Platform } from '@singletons/core.singletons'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| 
 | ||||
| /** | ||||
|  * Schema of a table. | ||||
| @ -411,6 +412,7 @@ export class SQLiteDB { | ||||
|      * @param params Query parameters. | ||||
|      * @return Promise resolved with the result. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|     async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> { | ||||
|         await this.ready(); | ||||
| 
 | ||||
| @ -425,7 +427,8 @@ export class SQLiteDB { | ||||
|      * @param sqlStatements SQL statements to execute. | ||||
|      * @return Promise resolved with the result. | ||||
|      */ | ||||
|     async executeBatch(sqlStatements: (string | SQLiteDBRecordValue[])[][]): Promise<void> { | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|     async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> { | ||||
|         await this.ready(); | ||||
| 
 | ||||
|         await this.db.sqlBatch(sqlStatements); | ||||
| @ -453,9 +456,10 @@ export class SQLiteDB { | ||||
|      * Format the data to where params. | ||||
|      * | ||||
|      * @param data Object data. | ||||
|      * @return List of params. | ||||
|      */ | ||||
|     protected formatDataToSQLParams(data: SQLiteDBRecordValues): SQLiteDBRecordValue[] { | ||||
|         return  Object.keys(data).map((key) => data[key]); | ||||
|         return Object.keys(data).map((key) => data[key]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -464,7 +468,7 @@ export class SQLiteDB { | ||||
|      * @param table The table to query. | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     async getAllRecords(table: string): Promise<SQLiteDBRecordValues[]> { | ||||
|     async getAllRecords<T = unknown>(table: string): Promise<T[]> { | ||||
|         return this.getRecords(table); | ||||
|     } | ||||
| 
 | ||||
| @ -510,7 +514,7 @@ export class SQLiteDB { | ||||
|     async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> { | ||||
|         const record = await this.getRecordSql(sql, params); | ||||
|         if (!record) { | ||||
|             throw null; | ||||
|             throw new CoreError('No record found.'); | ||||
|         } | ||||
| 
 | ||||
|         return record[Object.keys(record)[0]]; | ||||
| @ -574,10 +578,10 @@ export class SQLiteDB { | ||||
|      * @param fields A comma separated list of fields to return. | ||||
|      * @return Promise resolved with the record, rejected if not found. | ||||
|      */ | ||||
|     getRecord(table: string, conditions?: SQLiteDBRecordValues, fields: string = '*'): Promise<SQLiteDBRecordValues> { | ||||
|     getRecord<T = unknown>(table: string, conditions?: SQLiteDBRecordValues, fields: string = '*'): Promise<T> { | ||||
|         const selectAndParams = this.whereClause(conditions); | ||||
| 
 | ||||
|         return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields); | ||||
|         return this.getRecordSelect<T>(table, selectAndParams[0], selectAndParams[1], fields); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -589,13 +593,13 @@ export class SQLiteDB { | ||||
|      * @param fields A comma separated list of fields to return. | ||||
|      * @return Promise resolved with the record, rejected if not found. | ||||
|      */ | ||||
|     getRecordSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'): | ||||
|             Promise<SQLiteDBRecordValues> { | ||||
|     getRecordSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], fields: string = '*'): | ||||
|             Promise<T> { | ||||
|         if (select) { | ||||
|             select = ' WHERE ' + select; | ||||
|         } | ||||
| 
 | ||||
|         return this.getRecordSql(`SELECT ${fields} FROM ${table} ${select}`, params); | ||||
|         return this.getRecordSql<T>(`SELECT ${fields} FROM ${table} ${select}`, params); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -608,11 +612,11 @@ export class SQLiteDB { | ||||
|      * @param params List of sql parameters | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     async getRecordSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValues> { | ||||
|         const result = await this.getRecordsSql(sql, params, 0, 1); | ||||
|     async getRecordSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[]): Promise<T> { | ||||
|         const result = await this.getRecordsSql<T>(sql, params, 0, 1); | ||||
|         if (!result || !result.length) { | ||||
|             // Not found, reject.
 | ||||
|             throw null; | ||||
|             throw new CoreError('No records found.'); | ||||
|         } | ||||
| 
 | ||||
|         return result[0]; | ||||
| @ -629,11 +633,11 @@ export class SQLiteDB { | ||||
|      * @param limitNum Return a subset comprising this many records in total. | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     getRecords(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*', limitFrom: number = 0, | ||||
|             limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { | ||||
|     getRecords<T = unknown>(table: string, conditions?: SQLiteDBRecordValues, sort: string = '', fields: string = '*', | ||||
|             limitFrom: number = 0, limitNum: number = 0): Promise<T[]> { | ||||
|         const selectAndParams = this.whereClause(conditions); | ||||
| 
 | ||||
|         return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); | ||||
|         return this.getRecordsSelect<T>(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -648,11 +652,11 @@ export class SQLiteDB { | ||||
|      * @param limitNum Return a subset comprising this many records in total. | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     getRecordsList(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '', fields: string = '*', | ||||
|             limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { | ||||
|     getRecordsList<T = unknown>(table: string, field: string, values: SQLiteDBRecordValue[], sort: string = '', | ||||
|             fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> { | ||||
|         const selectAndParams = this.whereClauseList(field, values); | ||||
| 
 | ||||
|         return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); | ||||
|         return this.getRecordsSelect<T>(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -667,8 +671,8 @@ export class SQLiteDB { | ||||
|      * @param limitNum Return a subset comprising this many records in total. | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     getRecordsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '', | ||||
|             fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<SQLiteDBRecordValues[]> { | ||||
|     getRecordsSelect<T = unknown>(table: string, select: string = '', params: SQLiteDBRecordValue[] = [], sort: string = '', | ||||
|             fields: string = '*', limitFrom: number = 0, limitNum: number = 0): Promise<T[]> { | ||||
|         if (select) { | ||||
|             select = ' WHERE ' + select; | ||||
|         } | ||||
| @ -678,7 +682,7 @@ export class SQLiteDB { | ||||
| 
 | ||||
|         const sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`; | ||||
| 
 | ||||
|         return this.getRecordsSql(sql, params, limitFrom, limitNum); | ||||
|         return this.getRecordsSql<T>(sql, params, limitFrom, limitNum); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -690,8 +694,8 @@ export class SQLiteDB { | ||||
|      * @param limitNum Return a subset comprising this many records. | ||||
|      * @return Promise resolved with the records. | ||||
|      */ | ||||
|     async getRecordsSql(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number): | ||||
|             Promise<SQLiteDBRecordValues[]> { | ||||
|     async getRecordsSql<T = unknown>(sql: string, params?: SQLiteDBRecordValue[], limitFrom?: number, limitNum?: number): | ||||
|             Promise<T[]> { | ||||
|         const limits = this.normaliseLimitFromNum(limitFrom, limitNum); | ||||
| 
 | ||||
|         if (limits[0] || limits[1]) { | ||||
| @ -768,7 +772,7 @@ export class SQLiteDB { | ||||
|      */ | ||||
|     async insertRecords(table: string, dataObjects: SQLiteDBRecordValues[]): Promise<void> { | ||||
|         if (!Array.isArray(dataObjects)) { | ||||
|            throw null; | ||||
|            throw new CoreError('Invalid parameter supplied to insertRecords, it should be an array.'); | ||||
|         } | ||||
| 
 | ||||
|         const statements = dataObjects.map((dataObject) => { | ||||
| @ -854,7 +858,7 @@ export class SQLiteDB { | ||||
|     async recordExists(table: string, conditions?: SQLiteDBRecordValues): Promise<void> { | ||||
|         const record = await this.getRecord(table, conditions); | ||||
|         if (!record) { | ||||
|             throw null; | ||||
|             throw new CoreError('Record does not exist.'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -869,7 +873,7 @@ export class SQLiteDB { | ||||
|     async recordExistsSelect(table: string, select: string = '', params: SQLiteDBRecordValue[] = []): Promise<void> { | ||||
|         const record = await this.getRecordSelect(table, select, params); | ||||
|         if (!record) { | ||||
|             throw null; | ||||
|             throw new CoreError('Record does not exist.'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -883,7 +887,7 @@ export class SQLiteDB { | ||||
|     async recordExistsSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<void> { | ||||
|         const record = await this.getRecordSql(sql, params); | ||||
|         if (!record) { | ||||
|             throw null; | ||||
|             throw new CoreError('Record does not exist.'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,7 @@ import { Pipe, PipeTransform } from '@angular/core'; | ||||
|     name: 'coreCreateLinks', | ||||
| }) | ||||
| export class CoreCreateLinksPipe implements PipeTransform { | ||||
| 
 | ||||
|     protected static replacePattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])(?![^<]*>|[^<>]*<\/)/gim; | ||||
| 
 | ||||
|     /** | ||||
| @ -32,4 +33,5 @@ export class CoreCreateLinksPipe implements PipeTransform { | ||||
|     transform(text: string): string { | ||||
|         return text.replace(CoreCreateLinksPipe.replacePattern, '<a href="$1">$1</a>'); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -31,4 +31,5 @@ export class CoreNoTagsPipe implements PipeTransform { | ||||
|     transform(text: string): string { | ||||
|         return text.replace(/(<([^>]+)>)/ig, ''); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -28,6 +28,6 @@ import { CoreTimeAgoPipe } from './time-ago.pipe'; | ||||
|         CoreCreateLinksPipe, | ||||
|         CoreNoTagsPipe, | ||||
|         CoreTimeAgoPipe, | ||||
|     ] | ||||
|     ], | ||||
| }) | ||||
| export class CorePipesModule {} | ||||
|  | ||||
| @ -24,6 +24,7 @@ import moment from 'moment'; | ||||
|     name: 'coreTimeAgo', | ||||
| }) | ||||
| export class CoreTimeAgoPipe implements PipeTransform { | ||||
| 
 | ||||
|     private logger: CoreLogger; | ||||
| 
 | ||||
|     constructor() { | ||||
| @ -48,6 +49,7 @@ export class CoreTimeAgoPipe implements PipeTransform { | ||||
|             timestamp = numberTimestamp; | ||||
|         } | ||||
| 
 | ||||
|         return Translate.instance.instant('core.ago', {$a: moment(timestamp * 1000).fromNow(true)}); | ||||
|         return Translate.instance.instant('core.ago', { $a: moment(timestamp * 1000).fromNow(true) }); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -174,7 +174,8 @@ export class CoreAppProvider { | ||||
|             await this.createVersionsTableReady; | ||||
| 
 | ||||
|             // Fetch installed version of the schema.
 | ||||
|             const entry = await this.db.getRecord(SCHEMA_VERSIONS_TABLE, { name: schema.name }); | ||||
|             const entry = await this.db.getRecord<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE, { name: schema.name }); | ||||
| 
 | ||||
|             oldVersion = entry.version; | ||||
|         } catch (error) { | ||||
|             // No installed version yet.
 | ||||
| @ -796,3 +797,8 @@ export type WindowForAutomatedTests = Window & { | ||||
|     appProvider?: CoreAppProvider; | ||||
|     appRef?: ApplicationRef; | ||||
| }; | ||||
| 
 | ||||
| type SchemaVersionsDBEntry = { | ||||
|     name: string; | ||||
|     version: number; | ||||
| }; | ||||
|  | ||||
| @ -81,7 +81,7 @@ export class CoreConfigProvider { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             const entry = await this.appDB.getRecord(TABLE_NAME, { name }); | ||||
|             const entry = await this.appDB.getRecord<ConfigDBEntry>(TABLE_NAME, { name }); | ||||
| 
 | ||||
|             return entry.value; | ||||
|         } catch (error) { | ||||
| @ -109,3 +109,9 @@ export class CoreConfigProvider { | ||||
| } | ||||
| 
 | ||||
| export class CoreConfig extends makeSingleton(CoreConfigProvider) {} | ||||
| 
 | ||||
| type ConfigDBEntry = { | ||||
|     name: string; | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|     value: any; | ||||
| }; | ||||
|  | ||||
| @ -64,7 +64,7 @@ export class CoreCronDelegate { | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected handlers: { [s: string]: CoreCronHandler } = {}; | ||||
|     protected queuePromise = Promise.resolve(); | ||||
|     protected queuePromise: Promise<void> = Promise.resolve(); | ||||
| 
 | ||||
|     constructor(zone: NgZone) { | ||||
|         this.logger = CoreLogger.getInstance('CoreCronDelegate'); | ||||
| @ -271,8 +271,9 @@ export class CoreCronDelegate { | ||||
|         const id = this.getHandlerLastExecutionId(name); | ||||
| 
 | ||||
|         try { | ||||
|             const entry = await this.appDB.getRecord(CRON_TABLE, { id }); | ||||
|             const time = parseInt(entry.value, 10); | ||||
|             const entry = await this.appDB.getRecord<CronDBEntry>(CRON_TABLE, { id }); | ||||
| 
 | ||||
|             const time = Number(entry.value); | ||||
| 
 | ||||
|             return isNaN(time) ? 0 : time; | ||||
|         } catch (err) { | ||||
| @ -573,3 +574,8 @@ export interface CoreCronHandler { | ||||
| export type WindowForAutomatedTests = Window & { | ||||
|     cronProvider?: CoreCronDelegate; | ||||
| }; | ||||
| 
 | ||||
| type CronDBEntry = { | ||||
|     id: string; | ||||
|     value: number; | ||||
| }; | ||||
|  | ||||
| @ -29,8 +29,9 @@ import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| import { CoreUtils, PromiseDefer } from '@services/utils/utils'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { makeSingleton, Network, NgZone } from '@singletons/core.singletons'; | ||||
| import { makeSingleton, Network, NgZone, Translate } from '@singletons/core.singletons'; | ||||
| import { CoreLogger } from '@singletons/logger'; | ||||
| 
 | ||||
| /* | ||||
| @ -56,7 +57,7 @@ export class CoreFilepoolProvider { | ||||
|     protected static readonly ERR_FS_OR_NETWORK_UNAVAILABLE = 'CoreFilepoolError:ERR_FS_OR_NETWORK_UNAVAILABLE'; | ||||
|     protected static readonly ERR_QUEUE_ON_PAUSE = 'CoreFilepoolError:ERR_QUEUE_ON_PAUSE'; | ||||
| 
 | ||||
|     protected static readonly FILE_UPDATE_ANY_WHERE_CLAUSE = | ||||
|     protected static readonly FILE_UPDATE_UNKNOWN_WHERE_CLAUSE = | ||||
|         'isexternalfile = 1 OR ((revision IS NULL OR revision = 0) AND (timemodified IS NULL OR timemodified = 0))'; | ||||
| 
 | ||||
|     // Variables for database.
 | ||||
| @ -239,7 +240,7 @@ export class CoreFilepoolProvider { | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected queueState: string; | ||||
|     protected urlAttributes = [ | ||||
|     protected urlAttributes: RegExp[] = [ | ||||
|         new RegExp('(\\?|&)token=([A-Za-z0-9]*)'), | ||||
|         new RegExp('(\\?|&)forcedownload=[0-1]'), | ||||
|         new RegExp('(\\?|&)preview=[A-Za-z0-9]+'), | ||||
| @ -248,7 +249,7 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|     // To handle file downloads using the queue.
 | ||||
|     protected queueDeferreds: { [s: string]: { [s: string]: CoreFilepoolPromiseDefer } } = {}; | ||||
|     protected sizeCache = {}; // A "cache" to store file sizes to prevent performing too many HEAD requests.
 | ||||
|     protected sizeCache: {[fileUrl: string]: number} = {}; // A "cache" to store file sizes.
 | ||||
|     // Variables to prevent downloading packages/files twice at the same time.
 | ||||
|     protected packagesPromises: { [s: string]: { [s: string]: Promise<void> } } = {}; | ||||
|     protected filePromises: { [s: string]: { [s: string]: Promise<string> } } = {}; | ||||
| @ -288,7 +289,7 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     protected async addFileLink(siteId: string, fileId: string, component: string, componentId?: string | number): Promise<void> { | ||||
|         if (!component) { | ||||
|             throw null; | ||||
|             throw new CoreError('Cannot add link because component is invalid.'); | ||||
|         } | ||||
| 
 | ||||
|         componentId = this.fixComponentId(componentId); | ||||
| @ -358,8 +359,10 @@ export class CoreFilepoolProvider { | ||||
|      * @return Promise resolved on success. | ||||
|      */ | ||||
|     protected async addFileToPool(siteId: string, fileId: string, data: CoreFilepoolFileEntry): Promise<void> { | ||||
|         const record = Object.assign({}, data); | ||||
|         record.fileId = fileId; | ||||
|         const record = { | ||||
|             fileId, | ||||
|             ...data, | ||||
|         }; | ||||
| 
 | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
| 
 | ||||
| @ -457,12 +460,12 @@ export class CoreFilepoolProvider { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw null; | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         const site = await CoreSites.instance.getSite(siteId); | ||||
|         if (!site.canDownloadFiles()) { | ||||
|             throw null; | ||||
|             throw new CoreError('Site doesn\'t allow downloading files.'); | ||||
|         } | ||||
| 
 | ||||
|         let file: CoreWSExternalFile; | ||||
| @ -488,7 +491,7 @@ export class CoreFilepoolProvider { | ||||
|         const queueDeferred = this.getQueueDeferred(siteId, fileId, false, onProgress); | ||||
| 
 | ||||
|         return this.hasFileInQueue(siteId, fileId).then((entry: CoreFilepoolQueueEntry) => { | ||||
|             const newData: CoreFilepoolQueueEntry = {}; | ||||
|             const newData: CoreFilepoolQueueDBEntry = {}; | ||||
|             let foundLink = false; | ||||
| 
 | ||||
|             if (entry) { | ||||
| @ -562,14 +565,14 @@ export class CoreFilepoolProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param timemodified The time this file was modified. | ||||
|      * @param checkSize True if we shouldn't download files if their size is big, false otherwise. | ||||
|      * @param downloadAny True to download file in WiFi if their size is any, false otherwise. | ||||
|      * @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise. | ||||
|      *                        Ignored if checkSize=false. | ||||
|      * @param options Extra options (isexternalfile, repositorytype). | ||||
|      * @param revision File revision. If not defined, it will be calculated using the URL. | ||||
|      * @return Promise resolved when the file is downloaded. | ||||
|      */ | ||||
|     protected async addToQueueIfNeeded(siteId: string, fileUrl: string, component: string, componentId?: string | number, | ||||
|             timemodified: number = 0, checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {}, | ||||
|             timemodified: number = 0, checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {}, | ||||
|             revision?: number): Promise<void> { | ||||
|         if (!checkSize) { | ||||
|             // No need to check size, just add it to the queue.
 | ||||
| @ -584,7 +587,7 @@ export class CoreFilepoolProvider { | ||||
|         } else { | ||||
|             if (!CoreApp.instance.isOnline()) { | ||||
|                 // Cannot check size in offline, stop.
 | ||||
|                 throw null; | ||||
|                 throw new CoreError(Translate.instance.instant('core.cannotconnect')); | ||||
|             } | ||||
| 
 | ||||
|             size = await CoreWS.instance.getRemoteFileSize(fileUrl); | ||||
| @ -592,16 +595,16 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|         // Calculate the size of the file.
 | ||||
|         const isWifi = CoreApp.instance.isWifi(); | ||||
|         const sizeAny = size <= 0; | ||||
|         const sizeUnknown = size <= 0; | ||||
| 
 | ||||
|         if (!sizeAny) { | ||||
|         if (!sizeUnknown) { | ||||
|             // Store the size in the cache.
 | ||||
|             this.sizeCache[fileUrl] = size; | ||||
|         } | ||||
| 
 | ||||
|         // Check if the file should be downloaded.
 | ||||
|         if (sizeAny) { | ||||
|             if (downloadAny && isWifi) { | ||||
|         if (sizeUnknown) { | ||||
|             if (downloadUnknown && isWifi) { | ||||
|                 await this.addToQueueByUrl(siteId, fileUrl, component, componentId, timemodified, undefined, undefined, | ||||
|                     0, options, revision, true); | ||||
|             } | ||||
| @ -685,7 +688,7 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|         const count = await db.countRecords(CoreFilepoolProvider.LINKS_TABLE, conditions); | ||||
|         if (count <= 0) { | ||||
|             return null; | ||||
|             throw new CoreError('Component doesn\'t have files'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -696,7 +699,7 @@ export class CoreFilepoolProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @return Link, null if nothing to link. | ||||
|      */ | ||||
|     protected createComponentLink(component: string, componentId?: string | number): CoreFilepoolComponentLink { | ||||
|     protected createComponentLink(component: string, componentId?: string | number): CoreFilepoolComponentLink | null { | ||||
|         if (typeof component != 'undefined' && component != null)  { | ||||
|             return { component, componentId: this.fixComponentId(componentId) }; | ||||
|         } | ||||
| @ -779,7 +782,7 @@ export class CoreFilepoolProvider { | ||||
|         if (poolFileObject && poolFileObject.fileId !== fileId) { | ||||
|             this.logger.error('Invalid object to update passed'); | ||||
| 
 | ||||
|             throw null; | ||||
|             throw new CoreError('Invalid object to update passed.'); | ||||
|         } | ||||
| 
 | ||||
|         const downloadId = this.getFileDownloadId(fileUrl, filePath); | ||||
| @ -793,7 +796,7 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|         this.filePromises[siteId][downloadId] = CoreSites.instance.getSite(siteId).then(async (site) => { | ||||
|             if (!site.canDownloadFiles()) { | ||||
|                 return Promise.reject(null); | ||||
|                 throw new CoreError('Site doesn\'t allow downloading files.'); | ||||
|             } | ||||
| 
 | ||||
|             const entry = await CoreWS.instance.downloadFile(fileUrl, filePath, addExtension, onProgress); | ||||
| @ -952,7 +955,7 @@ export class CoreFilepoolProvider { | ||||
|             try { | ||||
|                 await Promise.all(promises); | ||||
|                 // Success prefetching, store package as downloaded.
 | ||||
|                 this.storePackageStatus(siteId, CoreConstants.DOWNLOADED, component, componentId, extra); | ||||
|                 await this.storePackageStatus(siteId, CoreConstants.DOWNLOADED, component, componentId, extra); | ||||
|             } catch (error) { | ||||
|                 // Error downloading, go back to previous status and reject the promise.
 | ||||
|                 await this.setPackagePreviousStatus(siteId, component, componentId); | ||||
| @ -1014,7 +1017,7 @@ export class CoreFilepoolProvider { | ||||
|         let alreadyDownloaded = true; | ||||
| 
 | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw null; | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         const file = await this.fixPluginfileURL(siteId, fileUrl); | ||||
| @ -1093,14 +1096,12 @@ export class CoreFilepoolProvider { | ||||
|         let urls = []; | ||||
| 
 | ||||
|         const element = CoreDomUtils.instance.convertToElement(html); | ||||
|         const elements = element.querySelectorAll('a, img, audio, video, source, track'); | ||||
|         const elements: (HTMLAnchorElement | HTMLImageElement | HTMLAudioElement | HTMLVideoElement | HTMLSourceElement | | ||||
|             HTMLTrackElement)[] = Array.from(element.querySelectorAll('a, img, audio, video, source, track')); | ||||
| 
 | ||||
|         for (let i = 0; i < elements.length; i++) { | ||||
|             const element = elements[i]; | ||||
|             let url = element.tagName === 'A' | ||||
|                 ? (element as HTMLAnchorElement).href | ||||
|                 : (element as HTMLImageElement | HTMLVideoElement | HTMLAudioElement | | ||||
|                 HTMLAudioElement | HTMLTrackElement | HTMLSourceElement).src; | ||||
|             let url = 'href' in element ? element.href : element.src; | ||||
| 
 | ||||
|             if (url && CoreUrlUtils.instance.isDownloadableUrl(url) && urls.indexOf(url) == -1) { | ||||
|                 urls.push(url); | ||||
| @ -1236,7 +1237,7 @@ export class CoreFilepoolProvider { | ||||
|             componentId: this.fixComponentId(componentId), | ||||
|         }; | ||||
| 
 | ||||
|         const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, conditions); | ||||
|         const items = await db.getRecords<CoreFilepoolLinksRecord>(CoreFilepoolProvider.LINKS_TABLE, conditions); | ||||
|         items.forEach((item) => { | ||||
|             item.componentId = this.fixComponentId(item.componentId); | ||||
|         }); | ||||
| @ -1252,16 +1253,16 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the URL. Rejected otherwise. | ||||
|      */ | ||||
|     async getDirectoryUrlByUrl(siteId: string, fileUrl: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const file = await  this.fixPluginfileURL(siteId, fileUrl); | ||||
|             const fileId = this.getFileIdByUrl(file.fileurl); | ||||
|             const filePath = await this.getFilePath(siteId, fileId, ''); | ||||
|             const dirEntry = await CoreFile.instance.getDir(filePath); | ||||
| 
 | ||||
|             return dirEntry.toURL(); | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const file = await  this.fixPluginfileURL(siteId, fileUrl); | ||||
|         const fileId = this.getFileIdByUrl(file.fileurl); | ||||
|         const filePath = await this.getFilePath(siteId, fileId, ''); | ||||
|         const dirEntry = await CoreFile.instance.getDir(filePath); | ||||
| 
 | ||||
|         return dirEntry.toURL(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1346,7 +1347,8 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     protected async getFileLinks(siteId: string, fileId: string): Promise<CoreFilepoolLinksRecord[]> { | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
|         const items = await db.getRecords(CoreFilepoolProvider.LINKS_TABLE, { fileId }); | ||||
|         const items = await db.getRecords<CoreFilepoolLinksRecord>(CoreFilepoolProvider.LINKS_TABLE, { fileId }); | ||||
| 
 | ||||
|         items.forEach((item) => { | ||||
|             item.componentId = this.fixComponentId(item.componentId); | ||||
|         }); | ||||
| @ -1421,7 +1423,7 @@ export class CoreFilepoolProvider { | ||||
|         const files = []; | ||||
| 
 | ||||
|         const promises = items.map((item) => | ||||
|             db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId: item.fileId }).then((fileEntry) => { | ||||
|             db.getRecord<CoreFilepoolFileEntry>(CoreFilepoolProvider.FILES_TABLE, { fileId: item.fileId }).then((fileEntry) => { | ||||
|                 if (!fileEntry) { | ||||
|                     return; | ||||
|                 } | ||||
| @ -1532,7 +1534,7 @@ export class CoreFilepoolProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param timemodified The time this file was modified. | ||||
|      * @param checkSize True if we shouldn't download files if their size is big, false otherwise. | ||||
|      * @param downloadAny True to download file in WiFi if their size is any, false otherwise. | ||||
|      * @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise. | ||||
|      *                        Ignored if checkSize=false. | ||||
|      * @param options Extra options (isexternalfile, repositorytype). | ||||
|      * @param revision File revision. If not defined, it will be calculated using the URL. | ||||
| @ -1544,12 +1546,12 @@ export class CoreFilepoolProvider { | ||||
|      * If the file isn't downloaded or it's outdated, return the online URL and add it to the queue to be downloaded later. | ||||
|      */ | ||||
|     protected async getFileUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, | ||||
|             mode: string = 'url', timemodified: number = 0, checkSize: boolean = true, downloadAny?: boolean, | ||||
|             mode: string = 'url', timemodified: number = 0, checkSize: boolean = true, downloadUnknown?: boolean, | ||||
|             options: CoreFilepoolFileOptions = {}, revision?: number): Promise<string> { | ||||
|         const addToQueue = (fileUrl: string): void => { | ||||
|             // Add the file to queue if needed and ignore errors.
 | ||||
|             this.addToQueueIfNeeded(siteId, fileUrl, component, componentId, timemodified, checkSize, | ||||
|                 downloadAny, options, revision).catch(() => { | ||||
|                 downloadUnknown, options, revision).catch(() => { | ||||
|                 // Ignore errors.
 | ||||
|             }); | ||||
|         }; | ||||
| @ -1594,7 +1596,7 @@ export class CoreFilepoolProvider { | ||||
|                     return fileUrl; | ||||
|                 } | ||||
| 
 | ||||
|                 throw null; | ||||
|                 throw new CoreError('File not found.'); | ||||
|             } | ||||
|         }, () => { | ||||
|             // We do not have the file in store yet. Add to queue and return the fixed URL.
 | ||||
| @ -1614,14 +1616,14 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the internal URL. Rejected otherwise. | ||||
|      */ | ||||
|     protected async getInternalSrcById(siteId: string, fileId: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const path = await this.getFilePath(siteId, fileId); | ||||
|             const fileEntry = await CoreFile.instance.getFile(path); | ||||
| 
 | ||||
|             return CoreFile.instance.convertFileSrc(fileEntry.toURL()); | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const path = await this.getFilePath(siteId, fileId); | ||||
|         const fileEntry = await CoreFile.instance.getFile(path); | ||||
| 
 | ||||
|         return CoreFile.instance.convertFileSrc(fileEntry.toURL()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1632,19 +1634,19 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the URL. Rejected otherwise. | ||||
|      */ | ||||
|     protected async getInternalUrlById(siteId: string, fileId: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const path = await this.getFilePath(siteId, fileId); | ||||
|             const fileEntry = await CoreFile.instance.getFile(path); | ||||
| 
 | ||||
|             // This URL is usually used to launch files or put them in HTML. In desktop we need the internal URL.
 | ||||
|             if (CoreApp.instance.isDesktop()) { | ||||
|                 return fileEntry.toInternalURL(); | ||||
|             } else { | ||||
|                 return fileEntry.toURL(); | ||||
|             } | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const path = await this.getFilePath(siteId, fileId); | ||||
|         const fileEntry = await CoreFile.instance.getFile(path); | ||||
| 
 | ||||
|         // This URL is usually used to launch files or put them in HTML. In desktop we need the internal URL.
 | ||||
|         if (CoreApp.instance.isDesktop()) { | ||||
|             return fileEntry.toInternalURL(); | ||||
|         } else { | ||||
|             return fileEntry.toURL(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1654,13 +1656,13 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the URL. | ||||
|      */ | ||||
|     protected async getInternalUrlByPath(filePath: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const fileEntry = await CoreFile.instance.getFile(filePath); | ||||
| 
 | ||||
|             return fileEntry.toURL(); | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const fileEntry = await CoreFile.instance.getFile(filePath); | ||||
| 
 | ||||
|         return fileEntry.toURL(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1671,14 +1673,14 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the URL. Rejected otherwise. | ||||
|      */ | ||||
|     async getInternalUrlByUrl(siteId: string, fileUrl: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const file = await this.fixPluginfileURL(siteId, fileUrl); | ||||
|             const fileId = this.getFileIdByUrl(file.fileurl); | ||||
| 
 | ||||
|             return this.getInternalUrlById(siteId, fileId); | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const file = await this.fixPluginfileURL(siteId, fileUrl); | ||||
|         const fileId = this.getFileIdByUrl(file.fileurl); | ||||
| 
 | ||||
|         return this.getInternalUrlById(siteId, fileId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1748,16 +1750,16 @@ export class CoreFilepoolProvider { | ||||
|      * @return Resolved with the URL. | ||||
|      */ | ||||
|     async getPackageDirUrlByUrl(siteId: string, url: string): Promise<string> { | ||||
|         if (CoreFile.instance.isAvailable()) { | ||||
|             const file = await this.fixPluginfileURL(siteId, url); | ||||
|             const dirName = this.getPackageDirNameByUrl(file.fileurl); | ||||
|             const dirPath = await this.getFilePath(siteId, dirName, ''); | ||||
|             const dirEntry = await CoreFile.instance.getDir(dirPath); | ||||
| 
 | ||||
|             return dirEntry.toURL(); | ||||
|         if (!CoreFile.instance.isAvailable()) { | ||||
|             throw new CoreError('File system cannot be used.'); | ||||
|         } | ||||
| 
 | ||||
|         throw null; | ||||
|         const file = await this.fixPluginfileURL(siteId, url); | ||||
|         const dirName = this.getPackageDirNameByUrl(file.fileurl); | ||||
|         const dirPath = await this.getFilePath(siteId, dirName, ''); | ||||
|         const dirEntry = await CoreFile.instance.getDir(dirPath); | ||||
| 
 | ||||
|         return dirEntry.toURL(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1973,7 +1975,7 @@ export class CoreFilepoolProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param timemodified The time this file was modified. | ||||
|      * @param checkSize True if we shouldn't download files if their size is big, false otherwise. | ||||
|      * @param downloadAny True to download file in WiFi if their size is any, false otherwise. | ||||
|      * @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise. | ||||
|      *                        Ignored if checkSize=false. | ||||
|      * @param options Extra options (isexternalfile, repositorytype). | ||||
|      * @param revision File revision. If not defined, it will be calculated using the URL. | ||||
| @ -1983,10 +1985,10 @@ export class CoreFilepoolProvider { | ||||
|      * The URL returned is compatible to use with IMG tags. | ||||
|      */ | ||||
|     getSrcByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0, | ||||
|             checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number): | ||||
|             checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number): | ||||
|             Promise<string> { | ||||
|         return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'src', | ||||
|             timemodified, checkSize, downloadAny, options, revision); | ||||
|             timemodified, checkSize, downloadUnknown, options, revision); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2017,7 +2019,7 @@ export class CoreFilepoolProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param timemodified The time this file was modified. | ||||
|      * @param checkSize True if we shouldn't download files if their size is big, false otherwise. | ||||
|      * @param downloadAny True to download file in WiFi if their size is any, false otherwise. | ||||
|      * @param downloadUnknown True to download file in WiFi if their size is unknown, false otherwise. | ||||
|      *                        Ignored if checkSize=false. | ||||
|      * @param options Extra options (isexternalfile, repositorytype). | ||||
|      * @param revision File revision. If not defined, it will be calculated using the URL. | ||||
| @ -2027,10 +2029,10 @@ export class CoreFilepoolProvider { | ||||
|      * The URL returned is compatible to use with a local browser. | ||||
|      */ | ||||
|     getUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string | number, timemodified: number = 0, | ||||
|             checkSize: boolean = true, downloadAny?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number): | ||||
|             checkSize: boolean = true, downloadUnknown?: boolean, options: CoreFilepoolFileOptions = {}, revision?: number): | ||||
|             Promise<string> { | ||||
|         return this.getFileUrlByUrl(siteId, fileUrl, component, componentId, 'url', | ||||
|             timemodified, checkSize, downloadAny, options, revision); | ||||
|             timemodified, checkSize, downloadUnknown, options, revision); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -2100,9 +2102,10 @@ export class CoreFilepoolProvider { | ||||
|      */ | ||||
|     protected async hasFileInPool(siteId: string, fileId: string): Promise<CoreFilepoolFileEntry> { | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
|         const entry = await db.getRecord(CoreFilepoolProvider.FILES_TABLE, { fileId }); | ||||
|         const entry = await db.getRecord<CoreFilepoolFileEntry>(CoreFilepoolProvider.FILES_TABLE, { fileId }); | ||||
| 
 | ||||
|         if (typeof entry === 'undefined') { | ||||
|             throw null; | ||||
|             throw new CoreError('File not found in filepool.'); | ||||
|         } | ||||
| 
 | ||||
|         return entry; | ||||
| @ -2118,12 +2121,13 @@ export class CoreFilepoolProvider { | ||||
|     protected async hasFileInQueue(siteId: string, fileId: string): Promise<CoreFilepoolQueueEntry> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const entry = await this.appDB.getRecord(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId }); | ||||
|         const entry = await this.appDB.getRecord<CoreFilepoolQueueEntry>(CoreFilepoolProvider.QUEUE_TABLE, { siteId, fileId }); | ||||
| 
 | ||||
|         if (typeof entry === 'undefined') { | ||||
|             throw null; | ||||
|             throw new CoreError('File not found in queue.'); | ||||
|         } | ||||
|         // Convert the links to an object.
 | ||||
|         entry.linksUnserialized = CoreTextUtils.instance.parseJSON(entry.links, []); | ||||
|         entry.linksUnserialized = <CoreFilepoolComponentLink[]> CoreTextUtils.instance.parseJSON(entry.links, []); | ||||
| 
 | ||||
|         return entry; | ||||
|     } | ||||
| @ -2132,14 +2136,14 @@ export class CoreFilepoolProvider { | ||||
|      * Invalidate all the files in a site. | ||||
|      * | ||||
|      * @param siteId The site ID. | ||||
|      * @param onlyAny True to only invalidate files from external repos or without revision/timemodified. | ||||
|      * @param onlyUnknown True to only invalidate files from external repos or without revision/timemodified. | ||||
|      *                    It is advised to set it to true to reduce the performance and data usage of the app. | ||||
|      * @return Resolved on success. | ||||
|      */ | ||||
|     async invalidateAllFiles(siteId: string, onlyAny: boolean = true): Promise<void> { | ||||
|     async invalidateAllFiles(siteId: string, onlyUnknown: boolean = true): Promise<void> { | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
| 
 | ||||
|         const where = onlyAny ? CoreFilepoolProvider.FILE_UPDATE_ANY_WHERE_CLAUSE : null; | ||||
|         const where = onlyUnknown ? CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE : null; | ||||
| 
 | ||||
|         await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, where); | ||||
|     } | ||||
| @ -2171,11 +2175,11 @@ export class CoreFilepoolProvider { | ||||
|      * @param siteId The site ID. | ||||
|      * @param component The component to invalidate. | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param onlyAny True to only invalidate files from external repos or without revision/timemodified. | ||||
|      *                    It is advised to set it to true to reduce the performance and data usage of the app. | ||||
|      * @param onlyUnknown True to only invalidate files from external repos or without revision/timemodified. | ||||
|      *                It is advised to set it to true to reduce the performance and data usage of the app. | ||||
|      * @return Resolved when done. | ||||
|      */ | ||||
|     async invalidateFilesByComponent(siteId: string, component: string, componentId?: string | number, onlyAny: boolean = true): | ||||
|     async invalidateFilesByComponent(siteId: string, component: string, componentId?: string | number, onlyUnknown: boolean = true): | ||||
|             Promise<void> { | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
| 
 | ||||
| @ -2191,8 +2195,8 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|         whereAndParams[0] = 'fileId ' + whereAndParams[0]; | ||||
| 
 | ||||
|         if (onlyAny) { | ||||
|             whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_ANY_WHERE_CLAUSE + ')'; | ||||
|         if (onlyUnknown) { | ||||
|             whereAndParams[0] += ' AND (' + CoreFilepoolProvider.FILE_UPDATE_UNKNOWN_WHERE_CLAUSE + ')'; | ||||
|         } | ||||
| 
 | ||||
|         await db.updateRecordsWhere(CoreFilepoolProvider.FILES_TABLE, { stale: 1 }, whereAndParams[0], whereAndParams[1]); | ||||
| @ -2258,7 +2262,7 @@ export class CoreFilepoolProvider { | ||||
|      * @param entry Filepool entry. | ||||
|      * @return Whether it cannot determine updates. | ||||
|      */ | ||||
|     protected isFileUpdateAny(entry: CoreFilepoolFileEntry): boolean { | ||||
|     protected isFileUpdateUnknown(entry: CoreFilepoolFileEntry): boolean { | ||||
|         return !!entry.isexternalfile || (!entry.revision && !entry.timemodified); | ||||
|     } | ||||
| 
 | ||||
| @ -2433,7 +2437,7 @@ export class CoreFilepoolProvider { | ||||
|         let items: CoreFilepoolQueueEntry[]; | ||||
| 
 | ||||
|         try { | ||||
|             items = await this.appDB.getRecords(CoreFilepoolProvider.QUEUE_TABLE, undefined, | ||||
|             items = await this.appDB.getRecords<CoreFilepoolQueueEntry>(CoreFilepoolProvider.QUEUE_TABLE, undefined, | ||||
|                 'priority DESC, added ASC', undefined, 0, 1); | ||||
|         } catch (err) { | ||||
|             throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY; | ||||
| @ -2444,7 +2448,7 @@ export class CoreFilepoolProvider { | ||||
|             throw CoreFilepoolProvider.ERR_QUEUE_IS_EMPTY; | ||||
|         } | ||||
|         // Convert the links to an object.
 | ||||
|         item.linksUnserialized = CoreTextUtils.instance.parseJSON(item.links, []); | ||||
|         item.linksUnserialized = <CoreFilepoolComponentLink[]> CoreTextUtils.instance.parseJSON(item.links, []); | ||||
| 
 | ||||
|         return this.processQueueItem(item); | ||||
|     } | ||||
| @ -2760,7 +2764,7 @@ export class CoreFilepoolProvider { | ||||
|         const mimetype = await CoreUtils.instance.getMimeTypeFromUrl(url); | ||||
|         // If the file is streaming (audio or video) we reject.
 | ||||
|         if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { | ||||
|             throw null; | ||||
|             throw new CoreError('File is audio or video.'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -2988,9 +2992,9 @@ export type CoreFilepoolFileEntry = CoreFilepoolFileOptions & { | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Entry from the file's queue. | ||||
|  * DB data for entry from file's queue. | ||||
|  */ | ||||
| export type CoreFilepoolQueueEntry = CoreFilepoolFileOptions & { | ||||
| export type CoreFilepoolQueueDBEntry = CoreFilepoolFileOptions & { | ||||
|     /** | ||||
|      * The site the file belongs to. | ||||
|      */ | ||||
| @ -3025,7 +3029,12 @@ export type CoreFilepoolQueueEntry = CoreFilepoolFileOptions & { | ||||
|      * File links (to link the file to components and componentIds). Serialized to store on DB. | ||||
|      */ | ||||
|     links?: string; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Entry from the file's queue. | ||||
|  */ | ||||
| export type CoreFilepoolQueueEntry = CoreFilepoolQueueDBEntry & { | ||||
|     /** | ||||
|      * File links (to link the file to components and componentIds). | ||||
|      */ | ||||
|  | ||||
| @ -116,6 +116,7 @@ export class CoreGeolocationProvider { | ||||
|      * | ||||
|      * @param error Error. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     protected isCordovaPermissionDeniedError(error?: any): boolean { | ||||
|         return error && 'code' in error && 'PERMISSION_DENIED' in error && error.code === error.PERMISSION_DENIED; | ||||
|     } | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; | ||||
| 
 | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreSite, CoreSiteWSPreSets } from '@classes/site'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { makeSingleton, Translate } from '@singletons/core.singletons'; | ||||
| import { CoreWSExternalWarning } from '@services/ws'; | ||||
| import { CoreCourseBase } from '@/types/global'; | ||||
| @ -79,9 +80,11 @@ export class CoreGroupsProvider { | ||||
|             preSets.emergencyCache = false; | ||||
|         } | ||||
| 
 | ||||
|         const response = await site.read('core_group_get_activity_allowed_groups', params, preSets); | ||||
|         const response: CoreGroupGetActivityAllowedGroupsResponse = | ||||
|             await site.read('core_group_get_activity_allowed_groups', params, preSets); | ||||
| 
 | ||||
|         if (!response || !response.groups) { | ||||
|             throw null; | ||||
|             throw new CoreError('Activity allowed groups not found.'); | ||||
|         } | ||||
| 
 | ||||
|         return response; | ||||
| @ -195,9 +198,11 @@ export class CoreGroupsProvider { | ||||
|             preSets.emergencyCache = false; | ||||
|         } | ||||
| 
 | ||||
|         const response = await site.read('core_group_get_activity_groupmode', params, preSets); | ||||
|         const response: CoreGroupGetActivityGroupModeResponse = | ||||
|             await site.read('core_group_get_activity_groupmode', params, preSets); | ||||
| 
 | ||||
|         if (!response || typeof response.groupmode == 'undefined') { | ||||
|             throw null; | ||||
|             throw new CoreError('Activity group mode not found.'); | ||||
|         } | ||||
| 
 | ||||
|         return response.groupmode; | ||||
| @ -267,9 +272,11 @@ export class CoreGroupsProvider { | ||||
|             updateFrequency: CoreSite.FREQUENCY_RARELY, | ||||
|         }; | ||||
| 
 | ||||
|         const response = await site.read('core_group_get_course_user_groups', data, preSets); | ||||
|         const response: CoreGroupGetCourseUserGroupsResponse = | ||||
|             await site.read('core_group_get_course_user_groups', data, preSets); | ||||
| 
 | ||||
|         if (!response || !response.groups) { | ||||
|             throw null; | ||||
|             throw new CoreError('User groups in course not found.'); | ||||
|         } | ||||
| 
 | ||||
|         return response.groups; | ||||
| @ -461,3 +468,26 @@ export type CoreGroupGetActivityAllowedGroupsResponse = { | ||||
|     canaccessallgroups?: boolean; // Whether the user will be able to access all the activity groups.
 | ||||
|     warnings?: CoreWSExternalWarning[]; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Result of WS core_group_get_activity_groupmode. | ||||
|  */ | ||||
| export type CoreGroupGetActivityGroupModeResponse = { | ||||
|     groupmode: number; // Group mode: 0 for no groups, 1 for separate groups, 2 for visible groups.
 | ||||
|     warnings?: CoreWSExternalWarning[]; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Result of WS core_group_get_course_user_groups. | ||||
|  */ | ||||
| export type CoreGroupGetCourseUserGroupsResponse = { | ||||
|     groups: { | ||||
|         id: number; // Group record id.
 | ||||
|         name: string; // Multilang compatible name, course unique.
 | ||||
|         description: string; // Group description text.
 | ||||
|         descriptionformat: number; // Description format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | ||||
|         idnumber: string; // Id number.
 | ||||
|         courseid?: number; // Course id.
 | ||||
|     }[]; | ||||
|     warnings?: CoreWSExternalWarning[]; | ||||
| }; | ||||
|  | ||||
| @ -30,9 +30,9 @@ export class CoreLangProvider { | ||||
|     protected fallbackLanguage = 'en'; // Always use English as fallback language since it contains all strings.
 | ||||
|     protected defaultLanguage = CoreConfigConstants.default_lang || 'en'; // Lang to use if device lang not valid or is forced.
 | ||||
|     protected currentLanguage: string; // Save current language in a variable to speed up the get function.
 | ||||
|     protected customStrings = {}; // Strings defined using the admin tool.
 | ||||
|     protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
 | ||||
|     protected customStringsRaw: string; | ||||
|     protected sitePluginsStrings = {}; // Strings defined by site plugins.
 | ||||
|     protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
 | ||||
| 
 | ||||
|     constructor() { | ||||
|         // Set fallback language and language to use until the app determines the right language to use.
 | ||||
| @ -110,11 +110,11 @@ export class CoreLangProvider { | ||||
|      * @param language New language to use. | ||||
|      * @return Promise resolved when the change is finished. | ||||
|      */ | ||||
|     changeCurrentLanguage(language: string): Promise<unknown> { | ||||
|     async changeCurrentLanguage(language: string): Promise<void> { | ||||
|         const promises = []; | ||||
| 
 | ||||
|         // Change the language, resolving the promise when we receive the first value.
 | ||||
|         promises.push(new Promise((resolve, reject): void => { | ||||
|         promises.push(new Promise((resolve, reject) => { | ||||
|             const subscription = Translate.instance.use(language).subscribe((data) => { | ||||
|                 // It's a language override, load the original one first.
 | ||||
|                 const fallbackLang = Translate.instance.instant('core.parentlanguage'); | ||||
| @ -165,13 +165,15 @@ export class CoreLangProvider { | ||||
| 
 | ||||
|         this.currentLanguage = language; | ||||
| 
 | ||||
|         return Promise.all(promises).finally(() => { | ||||
|         try { | ||||
|             await Promise.all(promises); | ||||
|         } finally { | ||||
|             // Load the custom and site plugins strings for the language.
 | ||||
|             if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) { | ||||
|                 // Some lang strings have changed, emit an event to update the pipes.
 | ||||
|                 Translate.instance.onLangChange.emit({ lang: language, translations: Translate.instance.translations[language] }); | ||||
|             } | ||||
|         }); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -196,7 +198,7 @@ export class CoreLangProvider { | ||||
|      * | ||||
|      * @return Custom strings. | ||||
|      */ | ||||
|     getAllCustomStrings(): unknown { | ||||
|     getAllCustomStrings(): CoreLanguageObject { | ||||
|         return this.customStrings; | ||||
|     } | ||||
| 
 | ||||
| @ -205,7 +207,7 @@ export class CoreLangProvider { | ||||
|      * | ||||
|      * @return Site plugins strings. | ||||
|      */ | ||||
|     getAllSitePluginsStrings(): unknown { | ||||
|     getAllSitePluginsStrings(): CoreLanguageObject { | ||||
|         return this.sitePluginsStrings; | ||||
|     } | ||||
| 
 | ||||
| @ -220,7 +222,7 @@ export class CoreLangProvider { | ||||
|         } | ||||
| 
 | ||||
|         // Get current language from config (user might have changed it).
 | ||||
|         return CoreConfig.instance.get('current_language').then((language) => language).catch(() => { | ||||
|         return CoreConfig.instance.get<string>('current_language').then((language) => language).catch(() => { | ||||
|             // User hasn't defined a language. If default language is forced, use it.
 | ||||
|             if (CoreConfigConstants.default_lang && CoreConfigConstants.forcedefaultlanguage) { | ||||
|                 return CoreConfigConstants.default_lang; | ||||
| @ -283,7 +285,7 @@ export class CoreLangProvider { | ||||
|      * @param lang The language to check. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     getTranslationTable(lang: string): Promise<unknown> { | ||||
|     getTranslationTable(lang: string): Promise<Record<string, unknown>> { | ||||
|         // Create a promise to convert the observable into a promise.
 | ||||
|         return new Promise((resolve, reject): void => { | ||||
|             const observer = Translate.instance.getTranslation(lang).subscribe((table) => { | ||||
|  | ||||
| @ -20,9 +20,11 @@ import { CoreApp, CoreAppSchema } from '@services/app'; | ||||
| import { CoreConfig } from '@services/config'; | ||||
| import { CoreEventObserver, CoreEvents, CoreEventsProvider } from '@services/events'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreUtils, PromiseDefer } from '@services/utils/utils'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreSite } from '@classes/site'; | ||||
| import { CoreQueueRunner } from '@classes/queue-runner'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import CoreConfigConstants from '@app/config.json'; | ||||
| import { makeSingleton, NgZone, Platform, Translate, LocalNotifications, Push, Device } from '@singletons/core.singletons'; | ||||
| @ -94,14 +96,9 @@ export class CoreLocalNotificationsProvider { | ||||
|     protected appDB: SQLiteDB; | ||||
|     protected dbReady: Promise<void>; // Promise resolved when the app DB is initialized.
 | ||||
|     protected codes: { [s: string]: number } = {}; | ||||
|     protected codeRequestsQueue = {}; | ||||
|     protected observables = {}; | ||||
|     protected currentNotification = { | ||||
|         title: '', | ||||
|         texts: [], | ||||
|         ids: [], | ||||
|         timeouts: [], | ||||
|     }; | ||||
|     protected codeRequestsQueue: {[key: string]: CodeRequestsQueueItem} = {}; | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|     protected observables: {[eventName: string]: {[component: string]: Subject<any>}} = {}; | ||||
| 
 | ||||
|     protected triggerSubscription: Subscription; | ||||
|     protected clickSubscription: Subscription; | ||||
| @ -156,7 +153,7 @@ export class CoreLocalNotificationsProvider { | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         CoreEvents.instance.on(CoreEventsProvider.SITE_DELETED, (site) => { | ||||
|         CoreEvents.instance.on(CoreEventsProvider.SITE_DELETED, (site: CoreSite) => { | ||||
|             if (site) { | ||||
|                 this.cancelSiteNotifications(site.id); | ||||
|             } | ||||
| @ -270,13 +267,15 @@ export class CoreLocalNotificationsProvider { | ||||
| 
 | ||||
|         try { | ||||
|             // Check if we already have a code stored for that ID.
 | ||||
|             const entry = await this.appDB.getRecord(table, { id: id }); | ||||
|             const entry = await this.appDB.getRecord<{id: string; code: number}>(table, { id: id }); | ||||
| 
 | ||||
|             this.codes[key] = entry.code; | ||||
| 
 | ||||
|             return entry.code; | ||||
|         } catch (err) { | ||||
|             // No code stored for that ID. Create a new code for it.
 | ||||
|             const entries = await this.appDB.getRecords(table, undefined, 'code DESC'); | ||||
|             const entries = await this.appDB.getRecords<{id: string; code: number}>(table, undefined, 'code DESC'); | ||||
| 
 | ||||
|             let newCode = 0; | ||||
|             if (entries.length > 0) { | ||||
|                 newCode = entries[0].code + 1; | ||||
| @ -326,7 +325,7 @@ export class CoreLocalNotificationsProvider { | ||||
|      */ | ||||
|     protected getUniqueNotificationId(notificationId: number, component: string, siteId: string): Promise<number> { | ||||
|         if (!siteId || !component) { | ||||
|             return Promise.reject(null); | ||||
|             return Promise.reject(new CoreError('Site ID or component not supplied.')); | ||||
|         } | ||||
| 
 | ||||
|         return this.getSiteCode(siteId).then((siteCode) => this.getComponentCode(component).then((componentCode) => | ||||
| @ -372,7 +371,9 @@ export class CoreLocalNotificationsProvider { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         try { | ||||
|             const stored = await this.appDB.getRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, { id: notification.id }); | ||||
|             const stored = await this.appDB.getRecord<{id: number; at: number}>(CoreLocalNotificationsProvider.TRIGGERED_TABLE, | ||||
|                 { id: notification.id }); | ||||
| 
 | ||||
|             let triggered = (notification.trigger && notification.trigger.at) || 0; | ||||
| 
 | ||||
|             if (typeof triggered != 'number') { | ||||
| @ -398,6 +399,7 @@ export class CoreLocalNotificationsProvider { | ||||
|      * | ||||
|      * @param data Data received by the notification. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     notifyClick(data: any): void { | ||||
|         this.notifyEvent('click', data); | ||||
|     } | ||||
| @ -408,6 +410,7 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param eventName Name of the event to notify. | ||||
|      * @param data Data received by the notification. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     notifyEvent(eventName: string, data: any): void { | ||||
|         // Execute the code in the Angular zone, so change detection doesn't stop working.
 | ||||
|         NgZone.instance.run(() => { | ||||
| @ -426,6 +429,7 @@ export class CoreLocalNotificationsProvider { | ||||
|      * @param data Notification data. | ||||
|      * @return Parsed data. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | ||||
|     protected parseNotificationData(data: any): any { | ||||
|         if (!data) { | ||||
|             return {}; | ||||
| @ -454,11 +458,11 @@ export class CoreLocalNotificationsProvider { | ||||
|         if (typeof request == 'object' && typeof request.table != 'undefined' && typeof request.id != 'undefined') { | ||||
|             // Get the code and resolve/reject all the promises of this request.
 | ||||
|             promise = this.getCode(request.table, request.id).then((code) => { | ||||
|                 request.promises.forEach((p) => { | ||||
|                 request.deferreds.forEach((p) => { | ||||
|                     p.resolve(code); | ||||
|                 }); | ||||
|             }).catch((error) => { | ||||
|                 request.promises.forEach((p) => { | ||||
|                 request.deferreds.forEach((p) => { | ||||
|                     p.reject(error); | ||||
|                 }); | ||||
|             }); | ||||
| @ -508,7 +512,7 @@ export class CoreLocalNotificationsProvider { | ||||
| 
 | ||||
|         return { | ||||
|             off: (): void => { | ||||
|                 this.observables[eventName][component].unsubscribe(callback); | ||||
|                 this.observables[eventName][component].unsubscribe(); | ||||
|             }, | ||||
|         }; | ||||
|     } | ||||
| @ -539,13 +543,13 @@ export class CoreLocalNotificationsProvider { | ||||
| 
 | ||||
|         if (typeof this.codeRequestsQueue[key] != 'undefined') { | ||||
|             // There's already a pending request for this store and ID, add the promise to it.
 | ||||
|             this.codeRequestsQueue[key].promises.push(deferred); | ||||
|             this.codeRequestsQueue[key].deferreds.push(deferred); | ||||
|         } else { | ||||
|             // Add a pending request to the queue.
 | ||||
|             this.codeRequestsQueue[key] = { | ||||
|                 table: table, | ||||
|                 id: id, | ||||
|                 promises: [deferred], | ||||
|                 deferreds: [deferred], | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
| @ -682,7 +686,7 @@ export class CoreLocalNotificationsProvider { | ||||
| 
 | ||||
|         const entry = { | ||||
|             id: notification.id, | ||||
|             at: notification.trigger && notification.trigger.at ? notification.trigger.at : Date.now(), | ||||
|             at: notification.trigger && notification.trigger.at ? notification.trigger.at.getTime() : Date.now(), | ||||
|         }; | ||||
| 
 | ||||
|         return this.appDB.insertRecord(CoreLocalNotificationsProvider.TRIGGERED_TABLE, entry); | ||||
| @ -709,3 +713,9 @@ export class CoreLocalNotificationsProvider { | ||||
| export class CoreLocalNotifications extends makeSingleton(CoreLocalNotificationsProvider) {} | ||||
| 
 | ||||
| export type CoreLocalNotificationsClickCallback<T = unknown> = (value: T) => void; | ||||
| 
 | ||||
| type CodeRequestsQueueItem = { | ||||
|     table: string; | ||||
|     id: string; | ||||
|     deferreds: PromiseDefer<number>[]; | ||||
| }; | ||||
|  | ||||
| @ -26,7 +26,13 @@ import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import CoreConfigConstants from '@app/config.json'; | ||||
| import { | ||||
|     CoreSite, CoreSiteWSPreSets, LocalMobileResponse, CoreSiteConfig, CoreSitePublicConfigResponse, CoreSiteInfoResponse, | ||||
|     CoreSite, | ||||
|     CoreSiteWSPreSets, | ||||
|     LocalMobileResponse, | ||||
|     CoreSiteInfo, | ||||
|     CoreSiteConfig, | ||||
|     CoreSitePublicConfigResponse, | ||||
|     CoreSiteInfoResponse, | ||||
| } from '@classes/site'; | ||||
| import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| @ -123,7 +129,7 @@ export class CoreSitesProvider { | ||||
|                     await db.tableExists(oldTable); | ||||
| 
 | ||||
|                     // Move the records from the old table.
 | ||||
|                     const sites = await db.getAllRecords(oldTable); | ||||
|                     const sites = await db.getAllRecords<SiteDBEntry>(oldTable); | ||||
|                     const promises = []; | ||||
| 
 | ||||
|                     sites.forEach((site) => { | ||||
| @ -818,9 +824,9 @@ export class CoreSitesProvider { | ||||
|             id, | ||||
|             siteUrl, | ||||
|             token, | ||||
|             info: info ? JSON.stringify(info) : info, | ||||
|             info: info ? JSON.stringify(info) : undefined, | ||||
|             privateToken, | ||||
|             config: config ? JSON.stringify(config) : config, | ||||
|             config: config ? JSON.stringify(config) : undefined, | ||||
|             loggedOut: 0, | ||||
|             oauthId, | ||||
|         }; | ||||
| @ -1094,7 +1100,7 @@ export class CoreSitesProvider { | ||||
|             return this.sites[siteId]; | ||||
|         } else { | ||||
|             // Retrieve and create the site.
 | ||||
|             const data = await this.appDB.getRecord(SITES_TABLE, { id: siteId }); | ||||
|             const data = await this.appDB.getRecord<SiteDBEntry>(SITES_TABLE, { id: siteId }); | ||||
| 
 | ||||
|             return this.makeSiteFromSiteListEntry(data); | ||||
|         } | ||||
| @ -1106,16 +1112,12 @@ export class CoreSitesProvider { | ||||
|      * @param entry Site list entry. | ||||
|      * @return Promised resolved with the created site. | ||||
|      */ | ||||
|     makeSiteFromSiteListEntry(entry: any): Promise<CoreSite> { | ||||
|         let info = entry.info; | ||||
|         let config = entry.config; | ||||
| 
 | ||||
|     makeSiteFromSiteListEntry(entry: SiteDBEntry): Promise<CoreSite> { | ||||
|         // Parse info and config.
 | ||||
|         info = info ? CoreTextUtils.instance.parseJSON(info) : info; | ||||
|         config = config ? CoreTextUtils.instance.parseJSON(config) : config; | ||||
|         const info = entry.info ? <CoreSiteInfo> CoreTextUtils.instance.parseJSON(entry.info) : undefined; | ||||
|         const config = entry.config ? <CoreSiteConfig> CoreTextUtils.instance.parseJSON(entry.config) : undefined; | ||||
| 
 | ||||
|         const site = new CoreSite(entry.id, entry.siteUrl, entry.token, | ||||
|             info, entry.privateToken, config, entry.loggedOut == 1); | ||||
|         const site = new CoreSite(entry.id, entry.siteUrl, entry.token, info, entry.privateToken, config, entry.loggedOut == 1); | ||||
|         site.setOAuthId(entry.oauthId); | ||||
| 
 | ||||
|         return this.migrateSiteSchemas(site).then(() => { | ||||
| @ -1171,20 +1173,20 @@ export class CoreSitesProvider { | ||||
|     async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const sites = await this.appDB.getAllRecords(SITES_TABLE); | ||||
|         const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE); | ||||
| 
 | ||||
|         const formattedSites = []; | ||||
|         sites.forEach((site) => { | ||||
|             if (!ids || ids.indexOf(site.id) > -1) { | ||||
|                 // Parse info.
 | ||||
|                 const siteInfo = site.info ? CoreTextUtils.instance.parseJSON(site.info) : site.info; | ||||
|                 const siteInfo = site.info ? <CoreSiteInfo> CoreTextUtils.instance.parseJSON(site.info) : undefined; | ||||
|                 const basicInfo: CoreSiteBasicInfo = { | ||||
|                     id: site.id, | ||||
|                     siteUrl: site.siteUrl, | ||||
|                     fullName: siteInfo && siteInfo.fullname, | ||||
|                     siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo && siteInfo.sitename, | ||||
|                     avatar: siteInfo && siteInfo.userpictureurl, | ||||
|                     siteHomeId: siteInfo && siteInfo.siteid || 1, | ||||
|                     fullName: siteInfo?.fullname, | ||||
|                     siteName: CoreConfigConstants.sitename ? CoreConfigConstants.sitename : siteInfo?.sitename, | ||||
|                     avatar: siteInfo?.userpictureurl, | ||||
|                     siteHomeId: siteInfo?.siteid || 1, | ||||
|                 }; | ||||
|                 formattedSites.push(basicInfo); | ||||
|             } | ||||
| @ -1231,7 +1233,7 @@ export class CoreSitesProvider { | ||||
|     async getLoggedInSitesIds(): Promise<string[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const sites = await this.appDB.getRecords(SITES_TABLE, { loggedOut : 0 }); | ||||
|         const sites = await this.appDB.getRecords<SiteDBEntry>(SITES_TABLE, { loggedOut : 0 }); | ||||
| 
 | ||||
|         return sites.map((site) => site.id); | ||||
|     } | ||||
| @ -1244,7 +1246,7 @@ export class CoreSitesProvider { | ||||
|     async getSitesIds(): Promise<string[]> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const sites = await this.appDB.getAllRecords(SITES_TABLE); | ||||
|         const sites = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE); | ||||
| 
 | ||||
|         return sites.map((site) => site.id); | ||||
|     } | ||||
| @ -1314,7 +1316,7 @@ export class CoreSitesProvider { | ||||
|         this.sessionRestored = true; | ||||
| 
 | ||||
|         try { | ||||
|             const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 }); | ||||
|             const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE, { id: 1 }); | ||||
|             const siteId = currentSite.siteId; | ||||
|             this.logger.debug(`Restore session in site ${siteId}`); | ||||
| 
 | ||||
| @ -1495,7 +1497,7 @@ export class CoreSitesProvider { | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const siteEntries = await this.appDB.getAllRecords(SITES_TABLE); | ||||
|             const siteEntries = await this.appDB.getAllRecords<SiteDBEntry>(SITES_TABLE); | ||||
|             const ids = []; | ||||
|             const promises = []; | ||||
| 
 | ||||
| @ -1528,7 +1530,7 @@ export class CoreSitesProvider { | ||||
|     async getStoredCurrentSiteId(): Promise<string> { | ||||
|         await this.dbReady; | ||||
| 
 | ||||
|         const currentSite = await this.appDB.getRecord(CURRENT_SITE_TABLE, { id: 1 }); | ||||
|         const currentSite = await this.appDB.getRecord<CurrentSiteDBEntry>(CURRENT_SITE_TABLE, { id: 1 }); | ||||
| 
 | ||||
|         return currentSite.siteId; | ||||
|     } | ||||
| @ -1685,7 +1687,7 @@ export class CoreSitesProvider { | ||||
|         const db = site.getDb(); | ||||
| 
 | ||||
|         // Fetch installed versions of the schema.
 | ||||
|         const records = await db.getAllRecords(SCHEMA_VERSIONS_TABLE); | ||||
|         const records = await db.getAllRecords<SchemaVersionsDBEntry>(SCHEMA_VERSIONS_TABLE); | ||||
| 
 | ||||
|         const versions: {[name: string]: number} = {}; | ||||
|         records.forEach((record) => { | ||||
| @ -2048,3 +2050,24 @@ export type CoreSitesLoginTokenResponse = { | ||||
|     debuginfo?: string; | ||||
|     reproductionlink?: string; | ||||
| }; | ||||
| 
 | ||||
| type SiteDBEntry = { | ||||
|     id: string; | ||||
|     siteUrl: string; | ||||
|     token: string; | ||||
|     info: string; | ||||
|     privateToken: string; | ||||
|     config: string; | ||||
|     loggedOut: number; | ||||
|     oauthId: number; | ||||
| }; | ||||
| 
 | ||||
| type CurrentSiteDBEntry = { | ||||
|     id: number; | ||||
|     siteId: string; | ||||
| }; | ||||
| 
 | ||||
| type SchemaVersionsDBEntry = { | ||||
|     name: string; | ||||
|     version: number; | ||||
| }; | ||||
|  | ||||
| @ -132,7 +132,7 @@ export class CoreSyncProvider { | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Record if found or reject. | ||||
|      */ | ||||
|     getSyncRecord(component: string, id: string | number, siteId?: string): Promise<Record<string, unknown>> { | ||||
|     getSyncRecord(component: string, id: string | number, siteId?: string): Promise<CoreSyncRecord> { | ||||
|         return CoreSites.instance.getSiteDb(siteId).then((db) => db.getRecord(SYNC_TABLE, { component: component, id: id })); | ||||
|     } | ||||
| 
 | ||||
| @ -145,8 +145,7 @@ export class CoreSyncProvider { | ||||
|      * @param siteId Site ID. If not defined, current site. | ||||
|      * @return Promise resolved with done. | ||||
|      */ | ||||
|     async insertOrUpdateSyncRecord(component: string, id: string | number, data: Record<string, unknown>, siteId?: string): | ||||
|             Promise<void> { | ||||
|     async insertOrUpdateSyncRecord(component: string, id: string, data: CoreSyncRecord, siteId?: string): Promise<void> { | ||||
|         const db = await CoreSites.instance.getSiteDb(siteId); | ||||
| 
 | ||||
|         data.component = component; | ||||
| @ -212,3 +211,10 @@ export class CoreSyncProvider { | ||||
| } | ||||
| 
 | ||||
| export class CoreSync extends makeSingleton(CoreSyncProvider) {} | ||||
| 
 | ||||
| export type CoreSyncRecord = { | ||||
|     component: string; | ||||
|     id: string; | ||||
|     time: number; | ||||
|     warnings: string; | ||||
| }; | ||||
|  | ||||
| @ -51,7 +51,7 @@ export class CoreUpdateManagerProvider implements CoreInitHandler { | ||||
|         const promises = []; | ||||
|         const versionCode = CoreConfigConstants.versioncode; | ||||
| 
 | ||||
|         const versionApplied: number = await CoreConfig.instance.get(VERSION_APPLIED, 0); | ||||
|         const versionApplied = await CoreConfig.instance.get<number>(VERSION_APPLIED, 0); | ||||
| 
 | ||||
|         if (versionCode >= 3900 && versionApplied < 3900 && versionApplied > 0) { | ||||
|             // @todo: H5P update.
 | ||||
|  | ||||
| @ -391,7 +391,7 @@ export class CoreIframeUtilsProvider { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain)) { | ||||
|         if (!CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol)) { | ||||
|             // Scheme suggests it's an external resource.
 | ||||
|             event && event.preventDefault(); | ||||
| 
 | ||||
|  | ||||
| @ -424,18 +424,16 @@ export class CoreUrlUtilsProvider { | ||||
|     isLocalFileUrl(url: string): boolean { | ||||
|         const urlParts = CoreUrl.parse(url); | ||||
| 
 | ||||
|         return this.isLocalFileUrlScheme(urlParts.protocol, urlParts.domain); | ||||
|         return this.isLocalFileUrlScheme(urlParts.protocol); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check whether a URL scheme belongs to a local file. | ||||
|      * | ||||
|      * @param scheme Scheme to check. | ||||
|      * @param notUsed Unused parameter. | ||||
|      * @return Whether the scheme belongs to a local file. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||
|     isLocalFileUrlScheme(scheme: string, notUsed?: string): boolean { | ||||
|     isLocalFileUrlScheme(scheme: string): boolean { | ||||
|         if (scheme) { | ||||
|             scheme = scheme.toLowerCase(); | ||||
|         } | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { HttpResponse, HttpParams } from '@angular/common/http'; | ||||
| 
 | ||||
| import { FileEntry } from '@ionic-native/file'; | ||||
| import { FileUploadOptions } from '@ionic-native/file-transfer/ngx'; | ||||
| import { Md5 } from 'ts-md5/dist/md5'; | ||||
| import { Observable } from 'rxjs'; | ||||
|  | ||||
| @ -38,7 +38,7 @@ export class CoreArray { | ||||
|      */ | ||||
|     static flatten<T>(arr: T[][]): T[] { | ||||
|         if ('flat' in arr) { | ||||
|             return (arr as any).flat(); | ||||
|             return (arr as any).flat(); // eslint-disable-line @typescript-eslint/no-explicit-any
 | ||||
|         } | ||||
| 
 | ||||
|         return [].concat(...arr); | ||||
|  | ||||
| @ -400,7 +400,7 @@ function unserialize (str) { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function substr_replace (str, replace, start, length) { // eslint-disable-line camelcase
 | ||||
| function substr_replace (str, replace, start, length) { | ||||
|   //  discuss at: https://locutus.io/php/substr_replace/
 | ||||
|   // original by: Brett Zamir (https://brett-zamir.me)
 | ||||
|   //   example 1: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 0)
 | ||||
|  | ||||
| @ -24,6 +24,7 @@ export type CoreWindowOpenOptions = { | ||||
|     /** | ||||
|      * NavController to use when opening the link in the app. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|     navCtrl?: any; // @todo NavController;
 | ||||
| }; | ||||
| 
 | ||||
| @ -36,7 +37,7 @@ export class CoreWindow { | ||||
|     private constructor() { | ||||
|         // Nothing to do.
 | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /** | ||||
|      * "Safe" implementation of window.open. It will open the URL without overriding the app. | ||||
|      * | ||||
| @ -60,11 +61,13 @@ export class CoreWindow { | ||||
|             await CoreUtils.instance.openFile(url); | ||||
|         } else { | ||||
|             let treated: boolean; | ||||
|             // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||
|             options = options || {}; | ||||
| 
 | ||||
|             if (name != '_system') { | ||||
|                 // Check if it can be opened in the app.
 | ||||
|                 treated = false; // @todo await CoreContentLinksHelper.instance.handleLink(url, undefined, options.navCtrl, true, true);
 | ||||
|                 treated = false; | ||||
|                 // @todo await CoreContentLinksHelper.instance.handleLink(url, undefined, options.navCtrl, true, true);
 | ||||
|             } | ||||
| 
 | ||||
|             if (!treated) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user