From 76e682bb538414524c41b1e266b3cb8e430e3c1a Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 28 Jan 2022 15:19:01 +0100 Subject: [PATCH] MOBILE-3833 core: Fix deep links to logged out sites --- .../services/contentlinks-delegate.ts | 23 +++++ .../login/pages/reconnect/reconnect.ts | 25 +++--- .../features/login/services/login-helper.ts | 83 ++++++++----------- .../mainmenu/classes/deep-link-manager.ts | 43 +++++----- src/core/guards/redirect.ts | 20 +++-- src/core/services/app.ts | 37 +++------ src/core/services/navigator.ts | 23 ++--- src/core/services/sites.ts | 35 ++++++-- src/core/services/tests/navigator.test.ts | 6 +- src/core/services/update-manager.ts | 9 +- src/core/services/urlschemes.ts | 32 +++---- src/core/singletons/events.ts | 7 +- upgrade.txt | 1 + 13 files changed, 184 insertions(+), 160 deletions(-) diff --git a/src/core/features/contentlinks/services/contentlinks-delegate.ts b/src/core/features/contentlinks/services/contentlinks-delegate.ts index eeaab8bbc..e8c36920e 100644 --- a/src/core/features/contentlinks/services/contentlinks-delegate.ts +++ b/src/core/features/contentlinks/services/contentlinks-delegate.ts @@ -191,6 +191,29 @@ export class CoreContentLinksDelegateService { action.message = action.message || 'core.view'; action.icon = action.icon || 'fas-eye'; action.sites = action.sites || siteIds; + + // Wrap the action function in our own function to treat logged out sites. + const actionFunction = action.action; + action.action = async (siteId) => { + const site = await CoreSites.getSite(siteId); + + if (!site.isLoggedOut()) { + // Call the action now. + return actionFunction(siteId); + } + + // Site is logged out, authenticate first before treating the URL. + const willReload = await CoreSites.logoutForRedirect(siteId, { + urlToOpen: url, + }); + + if (!willReload) { + // Load the site with the redirect data. + await CoreSites.loadSite(siteId, { + urlToOpen: url, + }); + } + }; }); // Add them to the list. diff --git a/src/core/features/login/pages/reconnect/reconnect.ts b/src/core/features/login/pages/reconnect/reconnect.ts index 4101ed7d7..0a884a552 100644 --- a/src/core/features/login/pages/reconnect/reconnect.ts +++ b/src/core/features/login/pages/reconnect/reconnect.ts @@ -23,7 +23,7 @@ import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreSite, CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreEvents } from '@singletons/events'; import { CoreError } from '@classes/errors/error'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; import { CoreForms } from '@singletons/form'; /** @@ -53,11 +53,10 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { siteId!: string; showScanQR = false; - protected page?: string; - protected pageOptions?: CoreNavigationOptions; protected siteConfig?: CoreSitePublicConfigResponse; protected viewLeft = false; protected eventThrown = false; + protected redirectData?: CoreRedirectPayload; constructor( protected fb: FormBuilder, @@ -77,8 +76,15 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { try { this.siteId = CoreNavigator.getRequiredRouteParam('siteId'); - this.page = CoreNavigator.getRouteParam('pageName'); - this.pageOptions = CoreNavigator.getRouteParam('pageOptions'); + const redirectPath = CoreNavigator.getRouteParam('redirectPath'); + const urlToOpen = CoreNavigator.getRouteParam('urlToOpen'); + if (redirectPath || urlToOpen) { + this.redirectData = { + redirectPath, + redirectOptions: CoreNavigator.getRouteParam('redirectOptions'), + urlToOpen, + }; + } const site = await CoreSites.getSite(this.siteId); @@ -208,9 +214,9 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { this.credForm.controls['password'].reset(); // Go to the site initial page. - this.page - ? await CoreNavigator.navigateToSitePath(this.page, this.pageOptions) - : await CoreNavigator.navigateToSiteHome(); + await CoreNavigator.navigateToSiteHome({ + params: this.redirectData, + }); } catch (error) { CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password); @@ -242,8 +248,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy { this.siteUrl, provider, this.siteConfig?.launchurl, - this.page, - this.pageOptions, + this.redirectData, ); if (!result) { diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index ec3052968..7416cf7c4 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -32,10 +32,9 @@ import { CoreWSError } from '@classes/errors/wserror'; import { makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreUrl } from '@singletons/url'; -import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; +import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreCustomURLSchemes } from '@services/urlschemes'; -import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; /** @@ -132,6 +131,7 @@ export class CoreLoginHelperProvider { * @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @param service The service to use. If not defined, core service will be used. * @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used. + * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. * @return Promise resolved when done or if user cancelled. */ async confirmAndOpenBrowserForSSOLogin( @@ -139,6 +139,7 @@ export class CoreLoginHelperProvider { typeOfLogin: number, service?: string, launchUrl?: string, + redirectData?: CoreRedirectPayload, ): Promise { // Show confirm only if it's needed. Treat "false" (string) as false to prevent typing errors. const showConfirmation = this.shouldShowSSOConfirm(typeOfLogin); @@ -152,7 +153,7 @@ export class CoreLoginHelperProvider { } } - this.openBrowserForSSOLogin(siteUrl, typeOfLogin, service, launchUrl); + this.openBrowserForSSOLogin(siteUrl, typeOfLogin, service, launchUrl, redirectData); } /** @@ -414,17 +415,14 @@ export class CoreLoginHelperProvider { let params: Params = { openAddSite: true , showKeyboard }; if (CoreSites.isLoggedIn()) { + const willReload = await CoreSites.logoutForRedirect(CoreConstants.NO_SITE_ID, { + redirectPath: path, + redirectOptions: { params }, + }); - if (CoreSitePlugins.hasSitePluginsLoaded) { - // The site has site plugins so the app will be restarted. Store the data and logout. - CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, path, { params }); - - await CoreSites.logout(); - + if (willReload) { return; } - - await CoreSites.logout(); } else { [path, params] = this.getAddSiteRouteInfo(showKeyboard); } @@ -477,7 +475,7 @@ export class CoreLoginHelperProvider { async goToSiteInitialPage(navCtrlUnused?: unknown, page?: string, params?: any, options?: any, url?: string): Promise { await CoreNavigator.navigateToSiteHome({ ...options, - params: { + params: { redirectPath: page, redirectOptions: { params }, urlToOpen: url, @@ -564,23 +562,19 @@ export class CoreLoginHelperProvider { } /** - * Check if current site is logged out, triggering mmCoreEventSessionExpired if it is. + * Check if current site is logged out, triggering session expired event if it is. * - * @param pageName Name of the page to go once authenticated if logged out. If not defined, site initial page. - * @param options Options of the page to go once authenticated if logged out. + * @param redirectData Data of the path/url to open once authenticated if logged out. If not defined, site initial page. * @return True if user is logged out, false otherwise. */ - isSiteLoggedOut(pageName?: string, options?: CoreNavigationOptions): boolean { + isSiteLoggedOut(redirectData?: CoreRedirectPayload): boolean { const site = CoreSites.getCurrentSite(); if (!site) { return false; } if (site.isLoggedOut()) { - CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { - pageName, - options, - }, site.getId()); + CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, redirectData || {}, site.getId()); return true; } @@ -652,16 +646,14 @@ export class CoreLoginHelperProvider { * @param siteUrl URL of the site where the login will be performed. * @param provider The identity provider. * @param launchUrl The URL to open for SSO. If not defined, tool/mobile launch URL will be used. - * @param pageName Name of the page to go once authenticated. If not defined, site initial page. - * @param pageOptions Options of the page to go once authenticated. + * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. * @return True if success, false if error. */ openBrowserForOAuthLogin( siteUrl: string, provider: CoreSiteIdentityProvider, launchUrl?: string, - pageName?: string, - pageOptions?: CoreNavigationOptions, + redirectData?: CoreRedirectPayload, ): boolean { launchUrl = launchUrl || siteUrl + '/admin/tool/mobile/launch.php'; if (!provider || !provider.url) { @@ -674,7 +666,7 @@ export class CoreLoginHelperProvider { return false; } - const loginUrl = this.prepareForSSOLogin(siteUrl, undefined, launchUrl, pageName, pageOptions, { + const loginUrl = this.prepareForSSOLogin(siteUrl, undefined, launchUrl, redirectData, { oauthsso: params.id, }); @@ -692,18 +684,16 @@ export class CoreLoginHelperProvider { * @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @param service The service to use. If not defined, core service will be used. * @param launchUrl The URL to open for SSO. If not defined, default tool mobile launch URL will be used. - * @param pageName Name of the page to go once authenticated. If not defined, site initial page. - * @param pageOptions Options of the state to go once authenticated. + * @param redirectData Data of the path/url to open once authenticated. If not defined, site initial page. */ openBrowserForSSOLogin( siteUrl: string, typeOfLogin: number, service?: string, launchUrl?: string, - pageName?: string, - pageOptions?: CoreNavigationOptions, + redirectData?: CoreRedirectPayload, ): void { - const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageOptions); + const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, redirectData); if (this.isSSOEmbeddedBrowser(typeOfLogin)) { CoreUtils.openInApp(loginUrl, { @@ -819,8 +809,7 @@ export class CoreLoginHelperProvider { siteUrl: string, service?: string, launchUrl?: string, - pageName?: string, - pageOptions?: CoreNavigationOptions, + redirectData: CoreRedirectPayload = {}, urlParams?: CoreUrlParams, ): string { @@ -842,8 +831,7 @@ export class CoreLoginHelperProvider { CoreConfig.set(CoreConstants.LOGIN_LAUNCH_DATA, JSON.stringify( { siteUrl: siteUrl, passport: passport, - pageName: pageName || '', - pageOptions: pageOptions || {}, + ...redirectData, ssoUrlParams: urlParams || {}, })); @@ -909,6 +897,11 @@ export class CoreLoginHelperProvider { } this.sessionExpiredCheckingSite[siteId || ''] = true; + const redirectData: CoreRedirectPayload = { + redirectPath: data.redirectPath, + redirectOptions: data.redirectOptions, + urlToOpen: data.urlToOpen, + }; try { // Check authentication method. @@ -930,8 +923,7 @@ export class CoreLoginHelperProvider { result.code, result.service, result.config?.launchurl, - data.pageName, - data.options, + redirectData, ); } catch (error) { // User cancelled, logout him. @@ -964,8 +956,7 @@ export class CoreLoginHelperProvider { siteUrl, providerToUse, result.config?.launchurl, - data.pageName, - data.options, + redirectData, ); } catch (error) { // User cancelled, logout him. @@ -987,8 +978,7 @@ export class CoreLoginHelperProvider { await CoreUtils.ignoreErrors(CoreNavigator.navigate('/login/reconnect', { params: { siteId, - pageName: data.pageName, - pageOptions: data.options, + ...redirectData, }, reset: true, })); @@ -1213,8 +1203,9 @@ export class CoreLoginHelperProvider { siteUrl: launchSiteURL, token: params[1], privateToken: params[2], - pageName: data.pageName, - pageOptions: data.pageOptions, + redirectPath: data.redirectPath, + redirectOptions: data.redirectOptions, + urlToOpen: data.urlToOpen, ssoUrlParams: data.ssoUrlParams, }; } else { @@ -1443,12 +1434,10 @@ export type CoreAccountsList = { /** * Data related to a SSO authentication. */ -export interface CoreLoginSSOData { +export type CoreLoginSSOData = CoreRedirectPayload & { siteUrl: string; // The site's URL. token?: string; // User's token. privateToken?: string; // User's private token. - pageName?: string; // Name of the page to go after authenticated. - pageOptions?: CoreNavigationOptions; // Options of the navigation to the page. ssoUrlParams?: CoreUrlParams; // Other params added to the login url. }; @@ -1531,11 +1520,9 @@ type ResendConfirmationEmailResult = { warnings?: CoreWSExternalWarning[]; }; -type StoredLoginLaunchData = { +type StoredLoginLaunchData = CoreRedirectPayload & { siteUrl: string; passport: number; - pageName: string; - pageOptions: CoreNavigationOptions; ssoUrlParams: CoreUrlParams; }; diff --git a/src/core/features/mainmenu/classes/deep-link-manager.ts b/src/core/features/mainmenu/classes/deep-link-manager.ts index 6cd480eb6..5ba0bfbfa 100644 --- a/src/core/features/mainmenu/classes/deep-link-manager.ts +++ b/src/core/features/mainmenu/classes/deep-link-manager.ts @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreCourse } from '@features/course/services/course'; import { CoreCourseHelper } from '@features/course/services/course-helper'; -import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; +import { CoreNavigationOptions, CoreNavigator, CoreRedirectPayload } from '@services/navigator'; /** * A class to handle opening deep links in a main menu page. There are 2 type of deep links: @@ -26,15 +25,15 @@ import { CoreNavigator, CoreRedirectPayload } from '@services/navigator'; export class CoreMainMenuDeepLinkManager { protected pendingRedirect?: CoreRedirectPayload; - protected urlToOpen?: string; constructor() { - this.urlToOpen = CoreNavigator.getRouteParam('urlToOpen'); + const urlToOpen = CoreNavigator.getRouteParam('urlToOpen'); const redirectPath = CoreNavigator.getRouteParam('redirectPath'); - if (redirectPath) { + if (urlToOpen || redirectPath) { this.pendingRedirect = { redirectPath, redirectOptions: CoreNavigator.getRouteParam('redirectOptions'), + urlToOpen, }; } } @@ -45,31 +44,35 @@ export class CoreMainMenuDeepLinkManager { * @return Whether there is a deep link to be treated. */ hasDeepLinkToTreat(): boolean { - return !!this.urlToOpen || !!this.pendingRedirect; + return !!this.pendingRedirect?.urlToOpen || !!this.pendingRedirect?.redirectPath; } /** * Treat a deep link if there's any to treat. */ treatLink(): void { - if (this.pendingRedirect) { - this.treatRedirect(this.pendingRedirect); - } else if (this.urlToOpen) { - this.treatUrlToOpen(this.urlToOpen); + if (!this.pendingRedirect) { + return; + } + + if (this.pendingRedirect.redirectPath) { + this.treatPath(this.pendingRedirect.redirectPath, this.pendingRedirect.redirectOptions); + } else if (this.pendingRedirect.urlToOpen) { + this.treatUrlToOpen(this.pendingRedirect.urlToOpen); } delete this.pendingRedirect; - delete this.urlToOpen; } /** - * Treat a redirect. + * Open a path. * - * @param data Data received. + * @param path Path. + * @param navOptions Navigation options. */ - protected treatRedirect(data: CoreRedirectPayload): void { - const params = data.redirectOptions?.params; - const coursePathMatches = data.redirectPath.match(/^course\/(\d+)\/?$/); + protected treatPath(path: string, navOptions: CoreNavigationOptions = {}): void { + const params = navOptions.params; + const coursePathMatches = path.match(/^course\/(\d+)\/?$/); if (coursePathMatches) { if (!params?.course) { @@ -78,8 +81,8 @@ export class CoreMainMenuDeepLinkManager { CoreCourse.openCourse(params.course, params); } } else { - CoreNavigator.navigateToSitePath(data.redirectPath, { - ...data.redirectOptions, + CoreNavigator.navigateToSitePath(path, { + ...navOptions, preferCurrentTab: false, }); } @@ -91,9 +94,7 @@ export class CoreMainMenuDeepLinkManager { * @param url URL to open. */ protected async treatUrlToOpen(url: string): Promise { - const actions = await CoreContentLinksDelegate.getActionsFor(url, undefined); - - const action = CoreContentLinksHelper.getFirstValidAction(actions); + const action = await CoreContentLinksHelper.getFirstValidActionFor(url); if (action?.sites?.[0]) { action.action(action.sites[0]); } diff --git a/src/core/guards/redirect.ts b/src/core/guards/redirect.ts index 38a1eb3d1..e8dc44e78 100644 --- a/src/core/guards/redirect.ts +++ b/src/core/guards/redirect.ts @@ -54,29 +54,31 @@ export class CoreRedirectGuard implements CanLoad, CanActivate { // Redirect to site path. if (redirect.siteId && redirect.siteId !== CoreConstants.NO_SITE_ID) { + const redirectData: CoreRedirectPayload = { + redirectPath: redirect.redirectPath, + redirectOptions: redirect.redirectOptions, + urlToOpen: redirect.urlToOpen, + }; + const loggedIn = await CoreSites.loadSite( redirect.siteId, - redirect.page, - redirect.options, + redirectData, ); const route = Router.parseUrl('/main'); - route.queryParams = { - redirectPath: redirect.page, - redirectOptions: redirect.options, - } as CoreRedirectPayload; + route.queryParams = redirectData; return loggedIn ? route : true; } // Abort redirect. - if (!redirect.page) { + if (!redirect.redirectPath) { return true; } // Redirect to non-site path. - const route = Router.parseUrl(redirect.page); - route.queryParams = redirect.options?.params || {}; + const route = Router.parseUrl(redirect.redirectPath); + route.queryParams = redirect.redirectOptions?.params || {}; return route; } diff --git a/src/core/services/app.ts b/src/core/services/app.ts index cbbdeb207..8edb6bd62 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -24,7 +24,7 @@ import { CoreLogger } from '@singletons/logger'; import { CoreColors } from '@singletons/colors'; import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app'; import { CoreObject } from '@singletons/object'; -import { CoreNavigationOptions } from './navigator'; +import { CoreRedirectPayload } from './navigator'; /** * Object responsible of managing schema versions. @@ -593,16 +593,18 @@ export class CoreAppProvider { * Store redirect params. * * @param siteId Site ID. - * @param page Page to go. - * @param options Navigation options. + * @param redirectData Redirect data. */ - storeRedirect(siteId: string, page: string, options: CoreNavigationOptions): void { + storeRedirect(siteId: string, redirectData: CoreRedirectPayload = {}): void { + if (!redirectData.redirectPath && !redirectData.urlToOpen) { + return; + } + try { const redirect: CoreRedirectData = { siteId, - page, - options, timemodified: Date.now(), + ...redirectData, }; localStorage.setItem('CoreRedirect', JSON.stringify(redirect)); @@ -691,26 +693,9 @@ export const CoreApp = makeSingleton(CoreAppProvider); /** * Data stored for a redirect to another page/site. */ -export type CoreRedirectData = { - /** - * ID of the site to load. - */ - siteId?: string; - - /** - * Path of the page to redirect to. - */ - page?: string; - - /** - * Options of the navigation. - */ - options?: CoreNavigationOptions; - - /** - * Timestamp when this redirect was last modified. - */ - timemodified?: number; +export type CoreRedirectData = CoreRedirectPayload & { + siteId?: string; // ID of the site to load. + timemodified?: number; // Timestamp when this redirect was last modified. }; /** diff --git a/src/core/services/navigator.ts b/src/core/services/navigator.ts index 5438abeca..88e012094 100644 --- a/src/core/services/navigator.ts +++ b/src/core/services/navigator.ts @@ -28,7 +28,6 @@ import { CoreTextUtils } from '@services/utils/text'; import { makeSingleton, NavController, Router } from '@singletons'; import { CoreScreen } from './screen'; import { CoreApp } from './app'; -import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreError } from '@classes/errors/error'; import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate'; @@ -36,8 +35,9 @@ import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-deleg * Redirect payload. */ export type CoreRedirectPayload = { - redirectPath: string; - redirectOptions?: CoreNavigationOptions; + redirectPath?: string; // Path of the page to redirect to. + redirectOptions?: CoreNavigationOptions; // Options of the navigation using redirectPath. + urlToOpen?: string; // URL to open instead of a page + options. }; /** @@ -207,16 +207,14 @@ export class CoreNavigatorService { // If we are logged into a different site, log out first. if (CoreSites.isLoggedIn() && CoreSites.getCurrentSiteId() !== siteId) { - if (CoreSitePlugins.hasSitePluginsLoaded) { - // The site has site plugins so the app will be restarted. Store the data and logout. - CoreApp.storeRedirect(siteId, path, options || {}); - - await CoreSites.logout(); + const willReload = await CoreSites.logoutForRedirect(siteId, { + redirectPath: path, + redirectOptions: options || {}, + }); + if (willReload) { return true; } - - await CoreSites.logout(); } // If the path doesn't belong to a site, call standard navigation. @@ -232,7 +230,10 @@ export class CoreNavigatorService { const modal = await CoreDomUtils.showModalLoading(); try { - const loggedIn = await CoreSites.loadSite(siteId, path, options.params); + const loggedIn = await CoreSites.loadSite(siteId, { + redirectPath: path, + redirectOptions: options, + }); if (!loggedIn) { // User has been redirected to the login page and will be redirected to the site path after login. diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 032ebcf23..6c5c5fca9 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -49,13 +49,14 @@ import { } from '@services/database/sites'; import { CoreArray } from '../singletons/array'; import { CoreNetworkError } from '@classes/errors/network-error'; -import { CoreNavigationOptions } from './navigator'; +import { CoreRedirectPayload } from './navigator'; import { CoreSitesFactory } from './sites-factory'; import { CoreText } from '@singletons/text'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle'; import { CoreAjaxError } from '@classes/errors/ajaxerror'; import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; +import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS'); @@ -781,11 +782,10 @@ export class CoreSitesProvider { * Login a user to a site from the list of sites. * * @param siteId ID of the site to load. - * @param pageName Name of the page to go once authenticated if logged out. If not defined, site initial page. - * @param pageOptions Options of the navigation to pageName. + * @param redirectData Data of the path/url to open once authenticated if logged out. If not defined, site initial page. * @return Promise resolved with true if site is loaded, resolved with false if cannot login. */ - async loadSite(siteId: string, pageName?: string, pageOptions?: CoreNavigationOptions): Promise { + async loadSite(siteId: string, redirectData?: CoreRedirectPayload): Promise { this.logger.debug(`Load site ${siteId}`); const site = await this.getSite(siteId); @@ -799,10 +799,7 @@ export class CoreSitesProvider { if (site.isLoggedOut()) { // Logged out, trigger session expired event and stop. - CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { - pageName, - options: pageOptions, - }, site.getId()); + CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, redirectData || {}, site.getId()); return false; } @@ -1208,6 +1205,28 @@ export class CoreSitesProvider { CoreEvents.trigger(CoreEvents.LOGOUT, {}, siteId); } + /** + * Logout the user if authenticated to open a page/url in another site. + * + * @param siteId Site that will be opened after logout. + * @param redirectData Page/url to open after logout. + * @return Promise resolved with boolean: true if app will be reloaded after logout. + */ + async logoutForRedirect(siteId: string, redirectData: CoreRedirectPayload): Promise { + if (!this.currentSite) { + return false; + } + + if (CoreSitePlugins.hasSitePluginsLoaded) { + // The site has site plugins so the app will be restarted. Store the data and logout. + CoreApp.storeRedirect(siteId, redirectData); + } + + await CoreSites.logout(); + + return CoreSitePlugins.hasSitePluginsLoaded; + } + /** * Restores the session to the previous one so the user doesn't has to login everytime the app is started. * diff --git a/src/core/services/tests/navigator.test.ts b/src/core/services/tests/navigator.test.ts index 694ba3f78..7b995a102 100644 --- a/src/core/services/tests/navigator.test.ts +++ b/src/core/services/tests/navigator.test.ts @@ -108,13 +108,13 @@ describe('CoreNavigator', () => { expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/users/user/42'], {}); }); - it('navigates to site paths using the default tab', async () => { + it('navigates to site paths using the main page', async () => { const success = await navigator.navigateToSitePath('/user/42'); expect(success).toBe(true); - expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/home'], { + expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main'], { queryParams: { - redirectPath: '/main/home/user/42', + redirectPath: 'user/42', }, }); }); diff --git a/src/core/services/update-manager.ts b/src/core/services/update-manager.ts index 38ad5d2b7..e73e50fd6 100644 --- a/src/core/services/update-manager.ts +++ b/src/core/services/update-manager.ts @@ -111,9 +111,12 @@ export class CoreUpdateManagerProvider { await CoreSites.removeStoredCurrentSite(); // Tell the app to open add site so the user can add the new site. - CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/sites', { - params: { - openAddSite: true, + CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, { + redirectPath: '/login/sites', + redirectOptions: { + params: { + openAddSite: true, + }, }, }); } diff --git a/src/core/services/urlschemes.ts b/src/core/services/urlschemes.ts index f1b6aaa36..9ae998d07 100644 --- a/src/core/services/urlschemes.ts +++ b/src/core/services/urlschemes.ts @@ -19,12 +19,11 @@ import { CoreWSError } from '@classes/errors/wserror'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreLoginHelper, CoreLoginSSOData } from '@features/login/services/login-helper'; -import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { ApplicationInit, makeSingleton, Translate } from '@singletons'; import { CoreLogger } from '@singletons/logger'; import { CoreConstants } from '../constants'; import { CoreApp } from './app'; -import { CoreNavigator } from './navigator'; +import { CoreNavigator, CoreRedirectPayload } from './navigator'; import { CoreSiteCheckResponse, CoreSites } from './sites'; import { CoreDomUtils } from './utils/dom'; import { CoreTextErrorObject, CoreTextUtils } from './utils/text'; @@ -151,12 +150,13 @@ export class CoreCustomURLSchemesProvider { if (data.isSSOToken || (data.isAuthenticationURL && siteId && CoreSites.getCurrentSiteId() == siteId)) { // Site created and authenticated, open the page to go. - if (data.pageName) { - // Page defined, go to that page instead of site initial page. - CoreNavigator.navigateToSitePath(data.pageName, data.pageOptions); - } else { - CoreNavigator.navigateToSiteHome(); - } + CoreNavigator.navigateToSiteHome({ + params: { + redirectPath: data.redirectPath, + redirectOptions: data.redirectOptions, + urlToOpen: data.urlToOpen, + }, + }); return; } @@ -396,20 +396,20 @@ export class CoreCustomURLSchemesProvider { urlToOpen: data.redirect, siteConfig: checkResponse.config, }; - let hasSitePluginsLoaded = false; if (CoreSites.isLoggedIn()) { // Ask the user before changing site. await CoreDomUtils.showConfirm(Translate.instant('core.contentlinks.confirmurlothersite')); if (!ssoNeeded) { - hasSitePluginsLoaded = CoreSitePlugins.hasSitePluginsLoaded; - if (hasSitePluginsLoaded) { - // Store the redirect since logout will restart the app. - CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/credentials', { params: pageParams }); - } + const willReload = await CoreSites.logoutForRedirect(CoreConstants.NO_SITE_ID, { + redirectPath: '/login/credentials', + redirectOptions: { params: pageParams }, + }); - await CoreSites.logout(); + if (willReload) { + return; + } } } @@ -420,7 +420,7 @@ export class CoreCustomURLSchemesProvider { checkResponse.service, checkResponse.config?.launchurl, ); - } else if (!hasSitePluginsLoaded) { + } else { await CoreNavigator.navigateToLoginCredentials(pageParams); } } diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index 288897bbd..d66a334e2 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -17,7 +17,7 @@ import { Subject } from 'rxjs'; import { CoreLogger } from '@singletons/logger'; import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreFilepoolComponentFileEventData } from '@services/filepool'; -import { CoreNavigationOptions } from '@services/navigator'; +import { CoreRedirectPayload } from '@services/navigator'; import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper'; import { CoreScreenOrientation } from '@services/screen'; import { CoreCourseCompletionType } from '@features/course/services/course'; @@ -270,10 +270,7 @@ export type CoreEventSiteAddedData = CoreSiteInfoResponse; /** * Data passed to SESSION_EXPIRED event. */ -export type CoreEventSessionExpiredData = { - pageName?: string; - options?: CoreNavigationOptions; -}; +export type CoreEventSessionExpiredData = CoreRedirectPayload; /** * Data passed to CORE_LOADING_CHANGED event. diff --git a/upgrade.txt b/upgrade.txt index 8b8313511..a7549b1c3 100644 --- a/upgrade.txt +++ b/upgrade.txt @@ -17,6 +17,7 @@ information provided here is intended especially for developers. Some user handler's functions have also changed to accept context + contextId instead of a courseId: isEnabledForUser, getDisplayData, action. - CoreCourseHelperProvider.openCourse parameters changed, now it admits CoreNavigationOptions + siteId on the same object that includes Params passed to page. - displaySectionSelector has been deprecated on CoreCourseFormatHandler, use displayCourseIndex instead. +- Most of the functions or callbacks that handle redirects/deeplinks have been modified to accept an object instead of just path + options. E.g.: CoreLoginHelper.isSiteLoggedOut, CoreLoginHelper.openBrowserForSSOLogin, CoreLoginHelper.openBrowserForOAuthLogin, CoreLoginHelper.prepareForSSOLogin, CoreApp.storeRedirect, CoreSites.loadSite. === 3.9.5 ===