diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts index b22ad2110..655b654c2 100644 --- a/src/core/classes/site.ts +++ b/src/core/classes/site.ts @@ -40,6 +40,7 @@ import { CoreWSError } from '@classes/errors/wserror'; import { CoreLogger } from '@singletons/logger'; import { Translate } from '@singletons'; import { CoreIonLoadingElement } from './ion-loading'; +import { CoreLang } from '@services/lang'; /** * QR Code type enumeration. @@ -308,10 +309,7 @@ export class CoreSite { // Index function by name to speed up wsAvailable method. if (infos?.functions) { - infos.functionsByName = {}; - infos.functions.forEach((func) => { - infos.functionsByName![func.name] = func; - }); + infos.functionsByName = CoreUtils.arrayToObject(infos.functions, 'name'); } } @@ -574,9 +572,14 @@ export class CoreSite { // Call the WS. try { + // Send the language to use. Do it after checking cache to prevent losing offline data when changing language. + data.moodlewssettinglang = preSets.lang ?? await CoreLang.getCurrentLanguage(); + data.moodlewssettinglang = data.moodlewssettinglang.replace('-', '_'); // Moodle uses underscore instead of dash. + const response = await this.callOrEnqueueRequest(method, data, preSets, wsPreSets); if (preSets.saveToCache) { + delete data.moodlewssettinglang; this.saveToCache(method, data, response, preSets); } @@ -786,7 +789,8 @@ export class CoreSite { return; } - const requestsData = { + let lang: string | undefined; + const requestsData: Record = { requests: requests.map((request) => { const args = {}; const settings = {}; @@ -799,6 +803,11 @@ export class CoreSite { if (match[1] == 'settingfilter' || match[1] == 'settingfileurl') { // Undo special treatment of these settings in CoreWSProvider.convertValuesToString. value = (value == 'true' ? '1' : '0'); + } else if (match[1] == 'settinglang') { + // Use the lang globally to avoid exceptions with languages not installed. + lang = value; + + return; } settings[match[1]] = value; } else { @@ -813,6 +822,7 @@ export class CoreSite { }; }), }; + requestsData.moodlewssettinglang = lang; const wsPresets: CoreWSPreSets = { siteUrl: this.siteUrl, @@ -894,7 +904,8 @@ export class CoreSite { preSets: CoreSiteWSPreSets, emergency?: boolean, ): Promise { - if (!this.db || !preSets.getFromCache) { + const db = this.db; + if (!db || !preSets.getFromCache) { throw new CoreError('Get from cache is disabled.'); } @@ -902,11 +913,11 @@ export class CoreSite { let entry: CoreSiteWSCacheRecord | undefined; if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { - const entries = await this.db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }); + const entries = await db.getRecords(CoreSite.WS_CACHE_TABLE, { key: preSets.cacheKey }); if (!entries.length) { // Cache key not found, get by params sent. - entry = await this.db!.getRecord(CoreSite.WS_CACHE_TABLE, { id }); + entry = await db.getRecord(CoreSite.WS_CACHE_TABLE, { id }); } else { if (entries.length > 1) { // More than one entry found. Search the one with same ID as this call. @@ -918,7 +929,7 @@ export class CoreSite { } } } else { - entry = await this.db!.getRecord(CoreSite.WS_CACHE_TABLE, { id }); + entry = await db.getRecord(CoreSite.WS_CACHE_TABLE, { id }); } if (typeof entry == 'undefined') { @@ -933,7 +944,7 @@ export class CoreSite { if (!preSets.omitExpires) { expirationTime = entry.expirationTime + this.getExpirationDelay(preSets.updateFrequency); - if (now > expirationTime!) { + if (now > expirationTime) { this.logger.debug('Cached element found, but it is expired'); throw new CoreError('Cache entry is expired.'); @@ -1734,7 +1745,12 @@ export class CoreSite { if (CoreSite.MOODLE_RELEASES[data.major] === undefined) { // Major version not found. Use the last one. - data.major = Object.keys(CoreSite.MOODLE_RELEASES).pop()!; + const major = Object.keys(CoreSite.MOODLE_RELEASES).pop(); + if (!major) { + return 0; + } + + data.major = major; } return CoreSite.MOODLE_RELEASES[data.major] + data.minor; @@ -1942,6 +1958,11 @@ export type CoreSiteWSPreSets = { */ rewriteurls?: boolean; + /** + * Language to send to the WebService (moodlewssettinglang). Defaults to app's language. + */ + lang?: string; + /** * Defaults to true. Set to false when the expected response is null. */ diff --git a/src/core/features/settings/pages/general/general.ts b/src/core/features/settings/pages/general/general.ts index 344164a37..e8ef89874 100644 --- a/src/core/features/settings/pages/general/general.ts +++ b/src/core/features/settings/pages/general/general.ts @@ -23,6 +23,8 @@ import { CoreSettingsHelper, CoreColorScheme, CoreZoomLevel } from '../../servic import { CoreApp } from '@services/app'; import { CoreIframeUtils } from '@services/utils/iframe'; import { Diagnostic } from '@singletons'; +import { CoreSites } from '@services/sites'; +import { CoreUtils } from '@services/utils/utils'; /** * Page that displays the general settings. @@ -109,7 +111,11 @@ export class CoreSettingsGeneralPage { * Called when a new language is selected. */ languageChanged(): void { - CoreLang.changeCurrentLanguage(this.selectedLanguage).finally(() => { + CoreLang.changeCurrentLanguage(this.selectedLanguage).finally(async () => { + // Invalidate cache for all sites to get the content in the right language. + const sites = await CoreSites.getSitesInstances(); + await CoreUtils.ignoreErrors(Promise.all(sites.map((site) => site.invalidateWsCache()))); + CoreEvents.trigger(CoreEvents.LANGUAGE_CHANGED, this.selectedLanguage); }); } diff --git a/src/core/services/lang.ts b/src/core/services/lang.ts index b3eee335e..f0430b64f 100644 --- a/src/core/services/lang.ts +++ b/src/core/services/lang.ts @@ -403,11 +403,11 @@ export class CoreLangProvider { this.customStringsRaw = strings; - if (currentLangChanged) { + if (currentLangChanged && this.currentLanguage) { // Some lang strings have changed, emit an event to update the pipes. Translate.onLangChange.emit({ - lang: this.currentLanguage!, - translations: Translate.translations[this.currentLanguage!], + lang: this.currentLanguage, + translations: Translate.translations[this.currentLanguage], }); } } diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index fd3d4c85b..702ca7a50 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -1145,6 +1145,17 @@ export class CoreSitesProvider { return sites.map((site) => site.id); } + /** + * Get instances of all stored sites. + * + * @return Promise resolved when the sites are retrieved. + */ + async getSitesInstances(): Promise { + const siteIds = await this.getSitesIds(); + + return await Promise.all(siteIds.map(async (siteId) => await this.getSite(siteId))); + } + /** * Login the user in a site. * diff --git a/src/core/services/ws.ts b/src/core/services/ws.ts index bb2e63719..ff2d7e869 100644 --- a/src/core/services/ws.ts +++ b/src/core/services/ws.ts @@ -336,8 +336,9 @@ export class CoreWSProvider { * @return Promise resolved with the mimetype or '' if failure. */ async getRemoteFileMimeType(url: string, ignoreCache?: boolean): Promise { - if (this.mimeTypeCache[url] && !ignoreCache) { - return this.mimeTypeCache[url]!; + const cachedMimeType = this.mimeTypeCache[url]; + if (cachedMimeType && !ignoreCache) { + return cachedMimeType; } try { @@ -722,10 +723,12 @@ export class CoreWSProvider { */ protected processRetryQueue(): void { if (this.retryCalls.length > 0 && this.retryTimeout == 0) { - const call = this.retryCalls.shift(); + const call = this.retryCalls[0]; + this.retryCalls.shift(); + // Add a delay between calls. setTimeout(() => { - call!.deferred.resolve(this.performPost(call!.method, call!.siteUrl, call!.data, call!.preSets)); + call.deferred.resolve(this.performPost(call.method, call.siteUrl, call.data, call.preSets)); this.processRetryQueue(); }, 200); } else {