diff --git a/src/core/classes/sites/unauthenticated-site.ts b/src/core/classes/sites/unauthenticated-site.ts index e213cc4f2f3..05ab1c80bf0 100644 --- a/src/core/classes/sites/unauthenticated-site.ts +++ b/src/core/classes/sites/unauthenticated-site.ts @@ -219,7 +219,7 @@ export class CoreUnauthenticatedSite { * @returns URL with params. */ createSiteUrl(path: string, params?: Record, anchor?: string): string { - return CoreUrl.addParamsToUrl(CorePath.concatenatePaths(this.siteUrl, path), params, anchor); + return CoreUrl.addParamsToUrl(CorePath.concatenatePaths(this.siteUrl, path), params, { anchor }); } /** diff --git a/src/core/components/iframe/iframe.ts b/src/core/components/iframe/iframe.ts index fdd472b85f8..5836438b984 100644 --- a/src/core/components/iframe/iframe.ts +++ b/src/core/components/iframe/iframe.ts @@ -30,6 +30,7 @@ import { CoreSites } from '@services/sites'; import { toBoolean } from '@/core/transforms/boolean'; import { CoreDom } from '@singletons/dom'; import { CoreAlerts } from '@services/overlays/alerts'; +import { CoreLang, CoreLangFormat } from '@services/lang'; @Component({ selector: 'core-iframe', @@ -234,9 +235,16 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { this.displayHelp = CoreIframeUtils.shouldDisplayHelpForUrl(url); const currentSite = CoreSites.getCurrentSite(); - if (this.allowAutoLogin && currentSite) { - // Format the URL to add auto-login if needed. - url = await currentSite.getAutoLoginUrl(url, false); + if (currentSite?.containsUrl(url)) { + // Format the URL to add auto-login if needed and add the lang parameter. + const autoLoginUrl = this.allowAutoLogin ? + await currentSite.getAutoLoginUrl(url, false) : + url; + + const lang = await CoreLang.getCurrentLanguage(CoreLangFormat.LMS); + url = CoreUrl.addParamsToUrl(autoLoginUrl, { lang }, { + checkAutoLoginUrl: autoLoginUrl !== url, + }); } if (currentSite?.isVersionGreaterEqualThan('3.7') && CoreUrl.isVimeoVideoUrl(url)) { diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts index 9ad3c2ed52b..63857fe912d 100644 --- a/src/core/directives/format-text.ts +++ b/src/core/directives/format-text.ts @@ -60,6 +60,7 @@ import { toBoolean } from '../transforms/boolean'; import { CoreViewer } from '@features/viewer/services/viewer'; import { CorePromiseUtils } from '@singletons/promise-utils'; import { CoreAlerts } from '@services/overlays/alerts'; +import { CoreLang, CoreLangFormat } from '@services/lang'; /** * Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective @@ -833,7 +834,13 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec // Remove iframe src, otherwise it can cause auto-login issues if there are several iframes with auto-login. iframe.src = ''; - const finalUrl = await CoreIframeUtils.getAutoLoginUrlForIframe(iframe, src); + let finalUrl = await CoreIframeUtils.getAutoLoginUrlForIframe(iframe, src); + + const lang = await CoreLang.getCurrentLanguage(CoreLangFormat.LMS); + finalUrl = CoreUrl.addParamsToUrl(finalUrl, { lang }, { + checkAutoLoginUrl: src !== finalUrl, + }); + await CoreIframeUtils.fixIframeCookies(finalUrl); iframe.src = finalUrl; diff --git a/src/core/features/user/services/support.ts b/src/core/features/user/services/support.ts index bcf2d0ec787..1698b33f4ea 100644 --- a/src/core/features/user/services/support.ts +++ b/src/core/features/user/services/support.ts @@ -24,7 +24,6 @@ import { CoreSubscriptions } from '@singletons/subscriptions'; import { AlertButton } from '@ionic/angular'; import { CoreLang } from '@services/lang'; import { CoreUserNullSupportConfig } from '@features/user/classes/support/null-support-config'; -import { CoreOpener } from '@singletons/opener'; import { CoreAlerts } from '@services/overlays/alerts'; /** @@ -41,10 +40,9 @@ export class CoreUserSupportService { async contact(options: CoreUserSupportContactOptions = {}): Promise { const supportConfig = options.supportConfig ?? CoreUserAuthenticatedSupportConfig.forCurrentSite(); const supportPageUrl = supportConfig.getSupportPageUrl(); - const autoLoginUrl = await CoreSites.getCurrentSite()?.getAutoLoginUrl(supportPageUrl, false); - const browser = CoreOpener.openInApp(autoLoginUrl ?? supportPageUrl); + const browser = await CoreSites.getCurrentSite()?.openInAppWithAutoLogin(supportPageUrl); - if (supportPageUrl.endsWith('/user/contactsitesupport.php')) { + if (browser && supportPageUrl.endsWith('/user/contactsitesupport.php')) { this.populateSupportForm(browser, options.subject, options.message); this.listenSupportFormSubmission(browser, supportConfig.getSupportPageLang()); } @@ -121,6 +119,10 @@ export class CoreUserSupportService { * @param lang Language used in the support page. */ protected async listenSupportFormSubmission(browser: InAppBrowserObject, lang: string | null): Promise { + if (!CorePlatform.isMobile()) { + return; + } + const appSuccessMessage = Translate.instant('core.user.supportmessagesent'); const lmsSuccessMessage = lang && await CoreLang.getMessage('core.user.supportmessagesent', lang); const subscription = browser.on('loadstop').subscribe(async () => { diff --git a/src/core/services/utils/url.ts b/src/core/services/utils/url.ts index d00d4e62e5c..80c413a17dc 100644 --- a/src/core/services/utils/url.ts +++ b/src/core/services/utils/url.ts @@ -46,7 +46,7 @@ export class CoreUrlUtilsProvider { * @deprecated since 4.5. Use CoreUrl.addParamsToUrl instead. */ addParamsToUrl(url: string, params?: Record, anchor?: string, boolToNumber?: boolean): string { - return CoreUrl.addParamsToUrl(url, params, anchor, boolToNumber); + return CoreUrl.addParamsToUrl(url, params, { anchor, boolToNumber }); } /** diff --git a/src/core/singletons/opener.ts b/src/core/singletons/opener.ts index c8fbc59acae..62aafd90321 100644 --- a/src/core/singletons/opener.ts +++ b/src/core/singletons/opener.ts @@ -190,14 +190,14 @@ export class CoreOpener { } } - const site = CoreSites.getCurrentSite(); + if (CoreSites.getCurrentSite()?.containsUrl(url)) { + url = CoreUrl.addParamsToUrl(url, { lang: await CoreLang.getCurrentLanguage(CoreLangFormat.LMS) }, { + checkAutoLoginUrl: options.originalUrl !== url, + }); + } + CoreAnalytics.logEvent({ type: CoreAnalyticsEventType.OPEN_LINK, link: originaUrl }); - window.open( - site?.containsUrl(url) - ? CoreUrl.addParamsToUrl(url, { lang: await CoreLang.getCurrentLanguage(CoreLangFormat.LMS) }) - : url, - '_system', - ); + window.open(url, '_system'); } /** @@ -309,6 +309,12 @@ export class CoreOpener { CoreOpener.setInAppBrowserToolbarColors(options); + if (CoreSites.getCurrentSite()?.containsUrl(url)) { + url = CoreUrl.addParamsToUrl(url, { lang: CoreLang.getCurrentLanguageSync(CoreLangFormat.LMS) }, { + checkAutoLoginUrl: options.originalUrl !== url, + }); + } + CoreOpener.iabInstance = InAppBrowser.create(url, '_blank', options); if (CorePlatform.isMobile()) { diff --git a/src/core/singletons/tests/url.test.ts b/src/core/singletons/tests/url.test.ts index 697b383d821..3684cb47c82 100644 --- a/src/core/singletons/tests/url.test.ts +++ b/src/core/singletons/tests/url.test.ts @@ -83,55 +83,50 @@ describe('CoreUrl singleton', () => { expect(url).toEqual('https://moodle.org'); }); - it('adds params to URL without params', () => { - const originalUrl = 'https://moodle.org'; - const params = { + it('adds params and anchors to URLs', () => { + // Add params to a URL without params. + expect(CoreUrl.addParamsToUrl('https://moodle.org', { first: '1', second: '2', - }; - const url = CoreUrl.addParamsToUrl(originalUrl, params); - - expect(url).toEqual('https://moodle.org?first=1&second=2'); - }); + })).toEqual('https://moodle.org?first=1&second=2'); - it('adds params to URL with existing params', () => { - const originalUrl = 'https://moodle.org?existing=1'; - const params = { + // Add params to a URL with existing params. + expect(CoreUrl.addParamsToUrl('https://moodle.org?existing=1', { first: '1', second: '2', - }; - const url = CoreUrl.addParamsToUrl(originalUrl, params); - - expect(url).toEqual('https://moodle.org?existing=1&first=1&second=2'); - }); - - it('doesn\'t change URL if no params supplied', () => { - const originalUrl = 'https://moodle.org'; - const url = CoreUrl.addParamsToUrl(originalUrl); + })).toEqual('https://moodle.org?existing=1&first=1&second=2'); - expect(url).toEqual(originalUrl); - }); + // No params supplied. + expect(CoreUrl.addParamsToUrl('https://moodle.org')).toEqual('https://moodle.org'); - it('doesn\'t add undefined or null params', () => { - const originalUrl = 'https://moodle.org'; - const url = CoreUrl.addParamsToUrl(originalUrl, { + // Undefined or null params aren't added. + expect(CoreUrl.addParamsToUrl('https://moodle.org', { foo: undefined, bar: null, baz: 1, - }); + })).toEqual('https://moodle.org?baz=1'); - expect(url).toEqual('https://moodle.org?baz=1'); - }); + // Adds anchor to URL. + expect(CoreUrl.addParamsToUrl('https://moodle.org', { + first: '1', + second: '2', + }, { + anchor: 'myanchor', + })).toEqual('https://moodle.org?first=1&second=2#myanchor'); - it('adds anchor to URL', () => { - const originalUrl = 'https://moodle.org'; - const params = { + // Adds params to the urltogo in case it's an auto-login URL. + expect(CoreUrl.addParamsToUrl('https://mysite.com/autologin.php?urltogo=https%3A%2F%2Fmoodle.org', { first: '1', second: '2', - }; - const url = CoreUrl.addParamsToUrl(originalUrl, params, 'myanchor'); + }, { + checkAutoLoginUrl: true, + })).toEqual('https://mysite.com/autologin.php?urltogo=https%3A%2F%2Fmoodle.org%3Ffirst%3D1%26second%3D2'); - expect(url).toEqual('https://moodle.org?first=1&second=2#myanchor'); + // Adds params to the base URL even if it has urltogo if checkAutoLoginUrl is not set. + expect(CoreUrl.addParamsToUrl('https://mysite.com/autologin.php?urltogo=https%3A%2F%2Fmoodle.org', { + first: '1', + second: '2', + })).toEqual('https://mysite.com/autologin.php?urltogo=https%3A%2F%2Fmoodle.org&first=1&second=2'); }); it('parses standard urls', () => { diff --git a/src/core/singletons/url.ts b/src/core/singletons/url.ts index 29e7db27d13..d1415474b07 100644 --- a/src/core/singletons/url.ts +++ b/src/core/singletons/url.ts @@ -403,28 +403,31 @@ export class CoreUrl { * * @param url URL to add the params to. * @param params Object with the params to add. - * @param anchor Anchor text if needed. - * @param boolToNumber Whether to convert bools to 1 or 0. + * @param options Other options. * @returns URL with params. */ - static addParamsToUrl(url: string, params?: Record, anchor?: string, boolToNumber?: boolean): string { + static addParamsToUrl(url: string, params?: Record, options: CoreUrlAddParamsOptions = {}): string { + // If it's an auto-login URL, add the params to the urltogo. extractUrlParams returns the urltogo already decoded. + const urlParams = options.checkAutoLoginUrl ? CoreUrl.extractUrlParams(url) : undefined; + let urlToTreat = urlParams?.urltogo ?? url; + // Remove any existing anchor to add the params before it. - const urlAndAnchor = url.split('#'); - url = urlAndAnchor[0]; + const urlAndAnchor = urlToTreat.split('#'); + urlToTreat = urlAndAnchor[0]; - let separator = url.indexOf('?') !== -1 ? '&' : '?'; + let separator = urlToTreat.indexOf('?') !== -1 ? '&' : '?'; for (const key in params) { let value = params[key]; - if (boolToNumber && typeof value === 'boolean') { + if (options.boolToNumber && typeof value === 'boolean') { // Convert booleans to 1 or 0. value = value ? '1' : '0'; } // Ignore objects and undefined. if (typeof value !== 'object' && value !== undefined) { - url += separator + key + '=' + value; + urlToTreat += separator + key + '=' + value; separator = '&'; } } @@ -435,14 +438,19 @@ export class CoreUrl { urlAndAnchor.shift(); // Use a join in case there is more than one #. - url += '#' + urlAndAnchor.join('#'); + urlToTreat += '#' + urlAndAnchor.join('#'); } - if (anchor) { - url += '#' + anchor; + if (options.anchor) { + urlToTreat += '#' + options.anchor; } - return url; + if (!urlParams?.urltogo) { + return urlToTreat; + } + + // Replace the urltogo with the treated one. + return url.replace(encodeURIComponent(urlParams.urltogo), encodeURIComponent(urlToTreat)); } /** @@ -997,3 +1005,12 @@ export class CoreUrl { } export type CoreUrlParams = {[key: string]: string}; + +/** + * Options for addParamsToUrl. + */ +export type CoreUrlAddParamsOptions = { + anchor?: string; // Anchor text if needed. + boolToNumber?: boolean; // Whether to convert bools to 1 / 0. + checkAutoLoginUrl?: boolean; // Whether the URL could be an auto-login URL. If so, any param will be added to the urltogo. +}; diff --git a/upgrade.txt b/upgrade.txt index 9d24bc4290e..afbffdbf044 100644 --- a/upgrade.txt +++ b/upgrade.txt @@ -7,6 +7,7 @@ For more information about upgrading, read the official documentation: https://m - The logout process has been refactored, now it uses a logout page to trigger Angular guards. CoreSites.logout now uses this process, and CoreSites.logoutForRedirect is deprecated and shouldn't be used anymore. - The parameters of treatDownloadedFile of plugin file handlers have changed. Now the third parameter is an object with all the optional parameters. - Some CoreColors functions have been refactored to handle alpha and to validate colors. + - The parameters of CoreUrl.addParamsToUrl have changed. Now the third parameter is an object with all the optional parameters. === 4.5.0 ===