MOBILE-3833 core: Fix deep links to logged out sites

main
Dani Palou 2022-01-28 15:19:01 +01:00
parent f83c8f4e1a
commit 76e682bb53
13 changed files with 184 additions and 160 deletions

View File

@ -191,6 +191,29 @@ export class CoreContentLinksDelegateService {
action.message = action.message || 'core.view'; action.message = action.message || 'core.view';
action.icon = action.icon || 'fas-eye'; action.icon = action.icon || 'fas-eye';
action.sites = action.sites || siteIds; 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. // Add them to the list.

View File

@ -23,7 +23,7 @@ import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSite, CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreSite, CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site';
import { CoreEvents } from '@singletons/events'; import { CoreEvents } from '@singletons/events';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreNavigator, CoreRedirectPayload } from '@services/navigator';
import { CoreForms } from '@singletons/form'; import { CoreForms } from '@singletons/form';
/** /**
@ -53,11 +53,10 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
siteId!: string; siteId!: string;
showScanQR = false; showScanQR = false;
protected page?: string;
protected pageOptions?: CoreNavigationOptions;
protected siteConfig?: CoreSitePublicConfigResponse; protected siteConfig?: CoreSitePublicConfigResponse;
protected viewLeft = false; protected viewLeft = false;
protected eventThrown = false; protected eventThrown = false;
protected redirectData?: CoreRedirectPayload;
constructor( constructor(
protected fb: FormBuilder, protected fb: FormBuilder,
@ -77,8 +76,15 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
try { try {
this.siteId = CoreNavigator.getRequiredRouteParam<string>('siteId'); this.siteId = CoreNavigator.getRequiredRouteParam<string>('siteId');
this.page = CoreNavigator.getRouteParam('pageName'); const redirectPath = CoreNavigator.getRouteParam('redirectPath');
this.pageOptions = CoreNavigator.getRouteParam('pageOptions'); const urlToOpen = CoreNavigator.getRouteParam('urlToOpen');
if (redirectPath || urlToOpen) {
this.redirectData = {
redirectPath,
redirectOptions: CoreNavigator.getRouteParam('redirectOptions'),
urlToOpen,
};
}
const site = await CoreSites.getSite(this.siteId); const site = await CoreSites.getSite(this.siteId);
@ -208,9 +214,9 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
this.credForm.controls['password'].reset(); this.credForm.controls['password'].reset();
// Go to the site initial page. // Go to the site initial page.
this.page await CoreNavigator.navigateToSiteHome({
? await CoreNavigator.navigateToSitePath(this.page, this.pageOptions) params: this.redirectData,
: await CoreNavigator.navigateToSiteHome(); });
} catch (error) { } catch (error) {
CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password); CoreLoginHelper.treatUserTokenError(this.siteUrl, error, this.username, password);
@ -242,8 +248,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
this.siteUrl, this.siteUrl,
provider, provider,
this.siteConfig?.launchurl, this.siteConfig?.launchurl,
this.page, this.redirectData,
this.pageOptions,
); );
if (!result) { if (!result) {

View File

@ -32,10 +32,9 @@ import { CoreWSError } from '@classes/errors/wserror';
import { makeSingleton, Translate } from '@singletons'; import { makeSingleton, Translate } from '@singletons';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CoreUrl } from '@singletons/url'; import { CoreUrl } from '@singletons/url';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreNavigator, CoreRedirectPayload } from '@services/navigator';
import { CoreCanceledError } from '@classes/errors/cancelederror'; import { CoreCanceledError } from '@classes/errors/cancelederror';
import { CoreCustomURLSchemes } from '@services/urlschemes'; import { CoreCustomURLSchemes } from '@services/urlschemes';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications'; 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 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 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 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. * @return Promise resolved when done or if user cancelled.
*/ */
async confirmAndOpenBrowserForSSOLogin( async confirmAndOpenBrowserForSSOLogin(
@ -139,6 +139,7 @@ export class CoreLoginHelperProvider {
typeOfLogin: number, typeOfLogin: number,
service?: string, service?: string,
launchUrl?: string, launchUrl?: string,
redirectData?: CoreRedirectPayload,
): Promise<void> { ): Promise<void> {
// Show confirm only if it's needed. Treat "false" (string) as false to prevent typing errors. // Show confirm only if it's needed. Treat "false" (string) as false to prevent typing errors.
const showConfirmation = this.shouldShowSSOConfirm(typeOfLogin); 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 }; let params: Params = { openAddSite: true , showKeyboard };
if (CoreSites.isLoggedIn()) { if (CoreSites.isLoggedIn()) {
const willReload = await CoreSites.logoutForRedirect(CoreConstants.NO_SITE_ID, {
redirectPath: path,
redirectOptions: { params },
});
if (CoreSitePlugins.hasSitePluginsLoaded) { if (willReload) {
// 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();
return; return;
} }
await CoreSites.logout();
} else { } else {
[path, params] = this.getAddSiteRouteInfo(showKeyboard); [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<void> { async goToSiteInitialPage(navCtrlUnused?: unknown, page?: string, params?: any, options?: any, url?: string): Promise<void> {
await CoreNavigator.navigateToSiteHome({ await CoreNavigator.navigateToSiteHome({
...options, ...options,
params: { params: <CoreRedirectPayload> {
redirectPath: page, redirectPath: page,
redirectOptions: { params }, redirectOptions: { params },
urlToOpen: url, 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 redirectData Data of the path/url to open once authenticated if logged out. If not defined, site initial page.
* @param options Options of the page to go once authenticated if logged out.
* @return True if user is logged out, false otherwise. * @return True if user is logged out, false otherwise.
*/ */
isSiteLoggedOut(pageName?: string, options?: CoreNavigationOptions): boolean { isSiteLoggedOut(redirectData?: CoreRedirectPayload): boolean {
const site = CoreSites.getCurrentSite(); const site = CoreSites.getCurrentSite();
if (!site) { if (!site) {
return false; return false;
} }
if (site.isLoggedOut()) { if (site.isLoggedOut()) {
CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, redirectData || {}, site.getId());
pageName,
options,
}, site.getId());
return true; return true;
} }
@ -652,16 +646,14 @@ export class CoreLoginHelperProvider {
* @param siteUrl URL of the site where the login will be performed. * @param siteUrl URL of the site where the login will be performed.
* @param provider The identity provider. * @param provider The identity provider.
* @param launchUrl The URL to open for SSO. If not defined, tool/mobile launch URL will be used. * @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 redirectData Data of the path/url to open once authenticated. If not defined, site initial page.
* @param pageOptions Options of the page to go once authenticated.
* @return True if success, false if error. * @return True if success, false if error.
*/ */
openBrowserForOAuthLogin( openBrowserForOAuthLogin(
siteUrl: string, siteUrl: string,
provider: CoreSiteIdentityProvider, provider: CoreSiteIdentityProvider,
launchUrl?: string, launchUrl?: string,
pageName?: string, redirectData?: CoreRedirectPayload,
pageOptions?: CoreNavigationOptions,
): boolean { ): boolean {
launchUrl = launchUrl || siteUrl + '/admin/tool/mobile/launch.php'; launchUrl = launchUrl || siteUrl + '/admin/tool/mobile/launch.php';
if (!provider || !provider.url) { if (!provider || !provider.url) {
@ -674,7 +666,7 @@ export class CoreLoginHelperProvider {
return false; return false;
} }
const loginUrl = this.prepareForSSOLogin(siteUrl, undefined, launchUrl, pageName, pageOptions, { const loginUrl = this.prepareForSSOLogin(siteUrl, undefined, launchUrl, redirectData, {
oauthsso: params.id, oauthsso: params.id,
}); });
@ -692,18 +684,16 @@ export class CoreLoginHelperProvider {
* @param typeOfLogin CoreConstants.LOGIN_SSO_CODE or CoreConstants.LOGIN_SSO_INAPP_CODE. * @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 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 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 redirectData Data of the path/url to open once authenticated. If not defined, site initial page.
* @param pageOptions Options of the state to go once authenticated.
*/ */
openBrowserForSSOLogin( openBrowserForSSOLogin(
siteUrl: string, siteUrl: string,
typeOfLogin: number, typeOfLogin: number,
service?: string, service?: string,
launchUrl?: string, launchUrl?: string,
pageName?: string, redirectData?: CoreRedirectPayload,
pageOptions?: CoreNavigationOptions,
): void { ): void {
const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, pageName, pageOptions); const loginUrl = this.prepareForSSOLogin(siteUrl, service, launchUrl, redirectData);
if (this.isSSOEmbeddedBrowser(typeOfLogin)) { if (this.isSSOEmbeddedBrowser(typeOfLogin)) {
CoreUtils.openInApp(loginUrl, { CoreUtils.openInApp(loginUrl, {
@ -819,8 +809,7 @@ export class CoreLoginHelperProvider {
siteUrl: string, siteUrl: string,
service?: string, service?: string,
launchUrl?: string, launchUrl?: string,
pageName?: string, redirectData: CoreRedirectPayload = {},
pageOptions?: CoreNavigationOptions,
urlParams?: CoreUrlParams, urlParams?: CoreUrlParams,
): string { ): string {
@ -842,8 +831,7 @@ export class CoreLoginHelperProvider {
CoreConfig.set(CoreConstants.LOGIN_LAUNCH_DATA, JSON.stringify(<StoredLoginLaunchData> { CoreConfig.set(CoreConstants.LOGIN_LAUNCH_DATA, JSON.stringify(<StoredLoginLaunchData> {
siteUrl: siteUrl, siteUrl: siteUrl,
passport: passport, passport: passport,
pageName: pageName || '', ...redirectData,
pageOptions: pageOptions || {},
ssoUrlParams: urlParams || {}, ssoUrlParams: urlParams || {},
})); }));
@ -909,6 +897,11 @@ export class CoreLoginHelperProvider {
} }
this.sessionExpiredCheckingSite[siteId || ''] = true; this.sessionExpiredCheckingSite[siteId || ''] = true;
const redirectData: CoreRedirectPayload = {
redirectPath: data.redirectPath,
redirectOptions: data.redirectOptions,
urlToOpen: data.urlToOpen,
};
try { try {
// Check authentication method. // Check authentication method.
@ -930,8 +923,7 @@ export class CoreLoginHelperProvider {
result.code, result.code,
result.service, result.service,
result.config?.launchurl, result.config?.launchurl,
data.pageName, redirectData,
data.options,
); );
} catch (error) { } catch (error) {
// User cancelled, logout him. // User cancelled, logout him.
@ -964,8 +956,7 @@ export class CoreLoginHelperProvider {
siteUrl, siteUrl,
providerToUse, providerToUse,
result.config?.launchurl, result.config?.launchurl,
data.pageName, redirectData,
data.options,
); );
} catch (error) { } catch (error) {
// User cancelled, logout him. // User cancelled, logout him.
@ -987,8 +978,7 @@ export class CoreLoginHelperProvider {
await CoreUtils.ignoreErrors(CoreNavigator.navigate('/login/reconnect', { await CoreUtils.ignoreErrors(CoreNavigator.navigate('/login/reconnect', {
params: { params: {
siteId, siteId,
pageName: data.pageName, ...redirectData,
pageOptions: data.options,
}, },
reset: true, reset: true,
})); }));
@ -1213,8 +1203,9 @@ export class CoreLoginHelperProvider {
siteUrl: launchSiteURL, siteUrl: launchSiteURL,
token: params[1], token: params[1],
privateToken: params[2], privateToken: params[2],
pageName: data.pageName, redirectPath: data.redirectPath,
pageOptions: data.pageOptions, redirectOptions: data.redirectOptions,
urlToOpen: data.urlToOpen,
ssoUrlParams: data.ssoUrlParams, ssoUrlParams: data.ssoUrlParams,
}; };
} else { } else {
@ -1443,12 +1434,10 @@ export type CoreAccountsList = {
/** /**
* Data related to a SSO authentication. * Data related to a SSO authentication.
*/ */
export interface CoreLoginSSOData { export type CoreLoginSSOData = CoreRedirectPayload & {
siteUrl: string; // The site's URL. siteUrl: string; // The site's URL.
token?: string; // User's token. token?: string; // User's token.
privateToken?: string; // User's private 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. ssoUrlParams?: CoreUrlParams; // Other params added to the login url.
}; };
@ -1531,11 +1520,9 @@ type ResendConfirmationEmailResult = {
warnings?: CoreWSExternalWarning[]; warnings?: CoreWSExternalWarning[];
}; };
type StoredLoginLaunchData = { type StoredLoginLaunchData = CoreRedirectPayload & {
siteUrl: string; siteUrl: string;
passport: number; passport: number;
pageName: string;
pageOptions: CoreNavigationOptions;
ssoUrlParams: CoreUrlParams; ssoUrlParams: CoreUrlParams;
}; };

View File

@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreCourseHelper } from '@features/course/services/course-helper'; 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: * 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 { export class CoreMainMenuDeepLinkManager {
protected pendingRedirect?: CoreRedirectPayload; protected pendingRedirect?: CoreRedirectPayload;
protected urlToOpen?: string;
constructor() { constructor() {
this.urlToOpen = CoreNavigator.getRouteParam('urlToOpen'); const urlToOpen = CoreNavigator.getRouteParam('urlToOpen');
const redirectPath = CoreNavigator.getRouteParam('redirectPath'); const redirectPath = CoreNavigator.getRouteParam('redirectPath');
if (redirectPath) { if (urlToOpen || redirectPath) {
this.pendingRedirect = { this.pendingRedirect = {
redirectPath, redirectPath,
redirectOptions: CoreNavigator.getRouteParam('redirectOptions'), redirectOptions: CoreNavigator.getRouteParam('redirectOptions'),
urlToOpen,
}; };
} }
} }
@ -45,31 +44,35 @@ export class CoreMainMenuDeepLinkManager {
* @return Whether there is a deep link to be treated. * @return Whether there is a deep link to be treated.
*/ */
hasDeepLinkToTreat(): boolean { hasDeepLinkToTreat(): boolean {
return !!this.urlToOpen || !!this.pendingRedirect; return !!this.pendingRedirect?.urlToOpen || !!this.pendingRedirect?.redirectPath;
} }
/** /**
* Treat a deep link if there's any to treat. * Treat a deep link if there's any to treat.
*/ */
treatLink(): void { treatLink(): void {
if (this.pendingRedirect) { if (!this.pendingRedirect) {
this.treatRedirect(this.pendingRedirect); return;
} else if (this.urlToOpen) { }
this.treatUrlToOpen(this.urlToOpen);
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.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 { protected treatPath(path: string, navOptions: CoreNavigationOptions = {}): void {
const params = data.redirectOptions?.params; const params = navOptions.params;
const coursePathMatches = data.redirectPath.match(/^course\/(\d+)\/?$/); const coursePathMatches = path.match(/^course\/(\d+)\/?$/);
if (coursePathMatches) { if (coursePathMatches) {
if (!params?.course) { if (!params?.course) {
@ -78,8 +81,8 @@ export class CoreMainMenuDeepLinkManager {
CoreCourse.openCourse(params.course, params); CoreCourse.openCourse(params.course, params);
} }
} else { } else {
CoreNavigator.navigateToSitePath(data.redirectPath, { CoreNavigator.navigateToSitePath(path, {
...data.redirectOptions, ...navOptions,
preferCurrentTab: false, preferCurrentTab: false,
}); });
} }
@ -91,9 +94,7 @@ export class CoreMainMenuDeepLinkManager {
* @param url URL to open. * @param url URL to open.
*/ */
protected async treatUrlToOpen(url: string): Promise<void> { protected async treatUrlToOpen(url: string): Promise<void> {
const actions = await CoreContentLinksDelegate.getActionsFor(url, undefined); const action = await CoreContentLinksHelper.getFirstValidActionFor(url);
const action = CoreContentLinksHelper.getFirstValidAction(actions);
if (action?.sites?.[0]) { if (action?.sites?.[0]) {
action.action(action.sites[0]); action.action(action.sites[0]);
} }

View File

@ -54,29 +54,31 @@ export class CoreRedirectGuard implements CanLoad, CanActivate {
// Redirect to site path. // Redirect to site path.
if (redirect.siteId && redirect.siteId !== CoreConstants.NO_SITE_ID) { 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( const loggedIn = await CoreSites.loadSite(
redirect.siteId, redirect.siteId,
redirect.page, redirectData,
redirect.options,
); );
const route = Router.parseUrl('/main'); const route = Router.parseUrl('/main');
route.queryParams = { route.queryParams = redirectData;
redirectPath: redirect.page,
redirectOptions: redirect.options,
} as CoreRedirectPayload;
return loggedIn ? route : true; return loggedIn ? route : true;
} }
// Abort redirect. // Abort redirect.
if (!redirect.page) { if (!redirect.redirectPath) {
return true; return true;
} }
// Redirect to non-site path. // Redirect to non-site path.
const route = Router.parseUrl(redirect.page); const route = Router.parseUrl(redirect.redirectPath);
route.queryParams = redirect.options?.params || {}; route.queryParams = redirect.redirectOptions?.params || {};
return route; return route;
} }

View File

@ -24,7 +24,7 @@ import { CoreLogger } from '@singletons/logger';
import { CoreColors } from '@singletons/colors'; import { CoreColors } from '@singletons/colors';
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app'; import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app';
import { CoreObject } from '@singletons/object'; import { CoreObject } from '@singletons/object';
import { CoreNavigationOptions } from './navigator'; import { CoreRedirectPayload } from './navigator';
/** /**
* Object responsible of managing schema versions. * Object responsible of managing schema versions.
@ -593,16 +593,18 @@ export class CoreAppProvider {
* Store redirect params. * Store redirect params.
* *
* @param siteId Site ID. * @param siteId Site ID.
* @param page Page to go. * @param redirectData Redirect data.
* @param options Navigation options.
*/ */
storeRedirect(siteId: string, page: string, options: CoreNavigationOptions): void { storeRedirect(siteId: string, redirectData: CoreRedirectPayload = {}): void {
if (!redirectData.redirectPath && !redirectData.urlToOpen) {
return;
}
try { try {
const redirect: CoreRedirectData = { const redirect: CoreRedirectData = {
siteId, siteId,
page,
options,
timemodified: Date.now(), timemodified: Date.now(),
...redirectData,
}; };
localStorage.setItem('CoreRedirect', JSON.stringify(redirect)); localStorage.setItem('CoreRedirect', JSON.stringify(redirect));
@ -691,26 +693,9 @@ export const CoreApp = makeSingleton(CoreAppProvider);
/** /**
* Data stored for a redirect to another page/site. * Data stored for a redirect to another page/site.
*/ */
export type CoreRedirectData = { export type CoreRedirectData = CoreRedirectPayload & {
/** siteId?: string; // ID of the site to load.
* ID of the site to load. timemodified?: number; // Timestamp when this redirect was last modified.
*/
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;
}; };
/** /**

View File

@ -28,7 +28,6 @@ import { CoreTextUtils } from '@services/utils/text';
import { makeSingleton, NavController, Router } from '@singletons'; import { makeSingleton, NavController, Router } from '@singletons';
import { CoreScreen } from './screen'; import { CoreScreen } from './screen';
import { CoreApp } from './app'; import { CoreApp } from './app';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
@ -36,8 +35,9 @@ import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-deleg
* Redirect payload. * Redirect payload.
*/ */
export type CoreRedirectPayload = { export type CoreRedirectPayload = {
redirectPath: string; redirectPath?: string; // Path of the page to redirect to.
redirectOptions?: CoreNavigationOptions; 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 we are logged into a different site, log out first.
if (CoreSites.isLoggedIn() && CoreSites.getCurrentSiteId() !== siteId) { if (CoreSites.isLoggedIn() && CoreSites.getCurrentSiteId() !== siteId) {
if (CoreSitePlugins.hasSitePluginsLoaded) { const willReload = await CoreSites.logoutForRedirect(siteId, {
// The site has site plugins so the app will be restarted. Store the data and logout. redirectPath: path,
CoreApp.storeRedirect(siteId, path, options || {}); redirectOptions: options || {},
});
await CoreSites.logout();
if (willReload) {
return true; return true;
} }
await CoreSites.logout();
} }
// If the path doesn't belong to a site, call standard navigation. // If the path doesn't belong to a site, call standard navigation.
@ -232,7 +230,10 @@ export class CoreNavigatorService {
const modal = await CoreDomUtils.showModalLoading(); const modal = await CoreDomUtils.showModalLoading();
try { try {
const loggedIn = await CoreSites.loadSite(siteId, path, options.params); const loggedIn = await CoreSites.loadSite(siteId, {
redirectPath: path,
redirectOptions: options,
});
if (!loggedIn) { if (!loggedIn) {
// User has been redirected to the login page and will be redirected to the site path after login. // User has been redirected to the login page and will be redirected to the site path after login.

View File

@ -49,13 +49,14 @@ import {
} from '@services/database/sites'; } from '@services/database/sites';
import { CoreArray } from '../singletons/array'; import { CoreArray } from '../singletons/array';
import { CoreNetworkError } from '@classes/errors/network-error'; import { CoreNetworkError } from '@classes/errors/network-error';
import { CoreNavigationOptions } from './navigator'; import { CoreRedirectPayload } from './navigator';
import { CoreSitesFactory } from './sites-factory'; import { CoreSitesFactory } from './sites-factory';
import { CoreText } from '@singletons/text'; import { CoreText } from '@singletons/text';
import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle'; import { CoreErrorWithTitle } from '@classes/errors/errorwithtitle';
import { CoreAjaxError } from '@classes/errors/ajaxerror'; import { CoreAjaxError } from '@classes/errors/ajaxerror';
import { CoreAjaxWSError } from '@classes/errors/ajaxwserror'; import { CoreAjaxWSError } from '@classes/errors/ajaxwserror';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS'); export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
@ -781,11 +782,10 @@ export class CoreSitesProvider {
* Login a user to a site from the list of sites. * Login a user to a site from the list of sites.
* *
* @param siteId ID of the site to load. * @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 redirectData Data of the path/url to open once authenticated if logged out. If not defined, site initial page.
* @param pageOptions Options of the navigation to pageName.
* @return Promise resolved with true if site is loaded, resolved with false if cannot login. * @return Promise resolved with true if site is loaded, resolved with false if cannot login.
*/ */
async loadSite(siteId: string, pageName?: string, pageOptions?: CoreNavigationOptions): Promise<boolean> { async loadSite(siteId: string, redirectData?: CoreRedirectPayload): Promise<boolean> {
this.logger.debug(`Load site ${siteId}`); this.logger.debug(`Load site ${siteId}`);
const site = await this.getSite(siteId); const site = await this.getSite(siteId);
@ -799,10 +799,7 @@ export class CoreSitesProvider {
if (site.isLoggedOut()) { if (site.isLoggedOut()) {
// Logged out, trigger session expired event and stop. // Logged out, trigger session expired event and stop.
CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, { CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, redirectData || {}, site.getId());
pageName,
options: pageOptions,
}, site.getId());
return false; return false;
} }
@ -1208,6 +1205,28 @@ export class CoreSitesProvider {
CoreEvents.trigger(CoreEvents.LOGOUT, {}, siteId); 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<boolean> {
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. * Restores the session to the previous one so the user doesn't has to login everytime the app is started.
* *

View File

@ -108,13 +108,13 @@ describe('CoreNavigator', () => {
expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/users/user/42'], {}); 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'); const success = await navigator.navigateToSitePath('/user/42');
expect(success).toBe(true); expect(success).toBe(true);
expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main/home'], { expect(navControllerMock.navigateForward).toHaveBeenCalledWith(['/main'], {
queryParams: { queryParams: {
redirectPath: '/main/home/user/42', redirectPath: 'user/42',
}, },
}); });
}); });

View File

@ -111,9 +111,12 @@ export class CoreUpdateManagerProvider {
await CoreSites.removeStoredCurrentSite(); await CoreSites.removeStoredCurrentSite();
// Tell the app to open add site so the user can add the new site. // Tell the app to open add site so the user can add the new site.
CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/sites', { CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, {
params: { redirectPath: '/login/sites',
openAddSite: true, redirectOptions: {
params: {
openAddSite: true,
},
}, },
}); });
} }

View File

@ -19,12 +19,11 @@ import { CoreWSError } from '@classes/errors/wserror';
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
import { CoreLoginHelper, CoreLoginSSOData } from '@features/login/services/login-helper'; import { CoreLoginHelper, CoreLoginSSOData } from '@features/login/services/login-helper';
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
import { ApplicationInit, makeSingleton, Translate } from '@singletons'; import { ApplicationInit, makeSingleton, Translate } from '@singletons';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CoreConstants } from '../constants'; import { CoreConstants } from '../constants';
import { CoreApp } from './app'; import { CoreApp } from './app';
import { CoreNavigator } from './navigator'; import { CoreNavigator, CoreRedirectPayload } from './navigator';
import { CoreSiteCheckResponse, CoreSites } from './sites'; import { CoreSiteCheckResponse, CoreSites } from './sites';
import { CoreDomUtils } from './utils/dom'; import { CoreDomUtils } from './utils/dom';
import { CoreTextErrorObject, CoreTextUtils } from './utils/text'; import { CoreTextErrorObject, CoreTextUtils } from './utils/text';
@ -151,12 +150,13 @@ export class CoreCustomURLSchemesProvider {
if (data.isSSOToken || (data.isAuthenticationURL && siteId && CoreSites.getCurrentSiteId() == siteId)) { if (data.isSSOToken || (data.isAuthenticationURL && siteId && CoreSites.getCurrentSiteId() == siteId)) {
// Site created and authenticated, open the page to go. // Site created and authenticated, open the page to go.
if (data.pageName) { CoreNavigator.navigateToSiteHome({
// Page defined, go to that page instead of site initial page. params: <CoreRedirectPayload> {
CoreNavigator.navigateToSitePath(data.pageName, data.pageOptions); redirectPath: data.redirectPath,
} else { redirectOptions: data.redirectOptions,
CoreNavigator.navigateToSiteHome(); urlToOpen: data.urlToOpen,
} },
});
return; return;
} }
@ -396,20 +396,20 @@ export class CoreCustomURLSchemesProvider {
urlToOpen: data.redirect, urlToOpen: data.redirect,
siteConfig: checkResponse.config, siteConfig: checkResponse.config,
}; };
let hasSitePluginsLoaded = false;
if (CoreSites.isLoggedIn()) { if (CoreSites.isLoggedIn()) {
// Ask the user before changing site. // Ask the user before changing site.
await CoreDomUtils.showConfirm(Translate.instant('core.contentlinks.confirmurlothersite')); await CoreDomUtils.showConfirm(Translate.instant('core.contentlinks.confirmurlothersite'));
if (!ssoNeeded) { if (!ssoNeeded) {
hasSitePluginsLoaded = CoreSitePlugins.hasSitePluginsLoaded; const willReload = await CoreSites.logoutForRedirect(CoreConstants.NO_SITE_ID, {
if (hasSitePluginsLoaded) { redirectPath: '/login/credentials',
// Store the redirect since logout will restart the app. redirectOptions: { params: pageParams },
CoreApp.storeRedirect(CoreConstants.NO_SITE_ID, '/login/credentials', { params: pageParams }); });
}
await CoreSites.logout(); if (willReload) {
return;
}
} }
} }
@ -420,7 +420,7 @@ export class CoreCustomURLSchemesProvider {
checkResponse.service, checkResponse.service,
checkResponse.config?.launchurl, checkResponse.config?.launchurl,
); );
} else if (!hasSitePluginsLoaded) { } else {
await CoreNavigator.navigateToLoginCredentials(pageParams); await CoreNavigator.navigateToLoginCredentials(pageParams);
} }
} }

View File

@ -17,7 +17,7 @@ import { Subject } from 'rxjs';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/site'; import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@classes/site';
import { CoreFilepoolComponentFileEventData } from '@services/filepool'; import { CoreFilepoolComponentFileEventData } from '@services/filepool';
import { CoreNavigationOptions } from '@services/navigator'; import { CoreRedirectPayload } from '@services/navigator';
import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper'; import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
import { CoreScreenOrientation } from '@services/screen'; import { CoreScreenOrientation } from '@services/screen';
import { CoreCourseCompletionType } from '@features/course/services/course'; import { CoreCourseCompletionType } from '@features/course/services/course';
@ -270,10 +270,7 @@ export type CoreEventSiteAddedData = CoreSiteInfoResponse;
/** /**
* Data passed to SESSION_EXPIRED event. * Data passed to SESSION_EXPIRED event.
*/ */
export type CoreEventSessionExpiredData = { export type CoreEventSessionExpiredData = CoreRedirectPayload;
pageName?: string;
options?: CoreNavigationOptions;
};
/** /**
* Data passed to CORE_LOADING_CHANGED event. * Data passed to CORE_LOADING_CHANGED event.

View File

@ -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. 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. - 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. - 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 === === 3.9.5 ===