MOBILE-2253 login: Restore session in init, handle SSO and other events

main
Dani Palou 2017-12-08 08:23:52 +01:00
parent 7893d718a2
commit 26d5690c36
6 changed files with 391 additions and 49 deletions

View File

@ -12,24 +12,82 @@
// 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 { Component } from '@angular/core'; import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { Platform } from 'ionic-angular'; import { Platform, Nav } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar'; import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen'; import { SplashScreen } from '@ionic-native/splash-screen';
import { CoreEventsProvider } from '../providers/events';
import { CoreLoginHelperProvider } from '../core/login/providers/helper';
@Component({ @Component({
templateUrl: 'app.html' templateUrl: 'app.html'
}) })
export class MyApp { export class MyApp implements AfterViewInit {
@ViewChild(Nav) navCtrl;
rootPage:any = 'CoreLoginInitPage'; rootPage:any = 'CoreLoginInitPage';
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) { constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen,
private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider) {
platform.ready().then(() => { platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available. // Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need. // Here you can do any higher level native things you might need.
statusBar.styleDefault(); statusBar.styleDefault();
splashScreen.hide(); splashScreen.hide();
}); });
}
/**
* View has been initialized.
*/
ngAfterViewInit() {
this.loginHelper.setNavCtrl(this.navCtrl);
// Go to sites page when user is logged out.
this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
this.navCtrl.setRoot('CoreLoginSitesPage');
});
// Listen for session expired events.
this.eventsProvider.on(CoreEventsProvider.SESSION_EXPIRED, (data) => {
this.loginHelper.sessionExpired(data);
});
// Listen for passwordchange and usernotfullysetup events to open InAppBrowser.
this.eventsProvider.on(CoreEventsProvider.PASSWORD_CHANGE_FORCED, (data) => {
this.loginHelper.openInAppForEdit(data.siteId, '/login/change_password.php', 'core.forcepasswordchangenotice', true);
});
this.eventsProvider.on(CoreEventsProvider.USER_NOT_FULLY_SETUP, (data) => {
this.loginHelper.openInAppForEdit(data.siteId, '/user/edit.php', 'core.usernotfullysetup');
});
// Listen for sitepolicynotagreed event to accept the site policy.
this.eventsProvider.on(CoreEventsProvider.SITE_POLICY_NOT_AGREED, (data) => {
this.loginHelper.sitePolicyNotAgreed(data.siteId);
});
// Check URLs loaded in any InAppBrowser.
this.eventsProvider.on(CoreEventsProvider.IAB_LOAD_START, (event) => {
this.loginHelper.inAppBrowserLoadStart(event.url);
});
// Check InAppBrowser closed.
this.eventsProvider.on(CoreEventsProvider.IAB_EXIT, () => {
this.loginHelper.waitingForBrowser = false;
this.loginHelper.lastInAppUrl = '';
this.loginHelper.checkLogout();
});
this.platform.resume.subscribe(() => {
// Wait a second before setting it to false since in iOS there could be some frozen WS calls.
setTimeout(() => {
this.loginHelper.waitingForBrowser = false;
this.loginHelper.checkLogout();
}, 1000);
});
// @todo: Register observer to check if the app was launched via URL scheme.
// $mmURLDelegate.register('mmLoginSSO', appLaunchedByURL);
} }
} }

View File

@ -53,6 +53,8 @@ import { CoreFilepoolProvider } from '../providers/filepool';
import { CoreUpdateManagerProvider } from '../providers/update-manager'; import { CoreUpdateManagerProvider } from '../providers/update-manager';
import { CorePluginFileDelegate } from '../providers/plugin-file-delegate'; import { CorePluginFileDelegate } from '../providers/plugin-file-delegate';
import { CoreLoginModule } from '../core/login/login.module';
// For translate loader. AoT requires an exported function for factories. // For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/lang/', '.json'); return new TranslateHttpLoader(http, './assets/lang/', '.json');
@ -76,7 +78,8 @@ export function createTranslateLoader(http: HttpClient) {
deps: [HttpClient] deps: [HttpClient]
} }
}), }),
CoreEmulatorModule CoreEmulatorModule,
CoreLoginModule
], ],
bootstrap: [IonicApp], bootstrap: [IonicApp],
entryComponents: [ entryComponents: [
@ -119,19 +122,27 @@ export function createTranslateLoader(http: HttpClient) {
] ]
}) })
export class AppModule { export class AppModule {
constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider) { constructor(platform: Platform, initDelegate: CoreInitDelegate, updateManager: CoreUpdateManagerProvider,
// Create a handler for platform ready and register it in the init delegate. sitesProvider: CoreSitesProvider) {
let handler = { // Register a handler for platform ready.
initDelegate.registerProcess({
name: 'CorePlatformReady', name: 'CorePlatformReady',
priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 400, priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 400,
blocking: true, blocking: true,
load: platform.ready load: platform.ready
}; });
initDelegate.registerProcess(handler);
// Register the update manager as an init process. // Register the update manager as an init process.
initDelegate.registerProcess(updateManager); initDelegate.registerProcess(updateManager);
// Restore the user's session during the init process.
initDelegate.registerProcess({
name: 'CoreRestoreSession',
priority: CoreInitDelegate.MAX_RECOMMENDED_PRIORITY + 200,
blocking: false,
load: sitesProvider.restoreSession.bind(sitesProvider)
});
// Execute the init processes. // Execute the init processes.
initDelegate.executeInitProcesses(); initDelegate.executeInitProcesses();
} }

View File

@ -30,6 +30,7 @@ import { CoreUtilsProvider } from '../providers/utils/utils';
import { CoreConstants } from '../core/constants'; import { CoreConstants } from '../core/constants';
import { CoreConfigConstants } from '../configconstants'; import { CoreConfigConstants } from '../configconstants';
import { Md5 } from 'ts-md5/dist/md5'; import { Md5 } from 'ts-md5/dist/md5';
import { InAppBrowserObject } from '@ionic-native/in-app-browser';
export interface CoreSiteWSPreSets { export interface CoreSiteWSPreSets {
getFromCache?: boolean; // Get the value from the cache if it's still valid. getFromCache?: boolean; // Get the value from the cache if it's still valid.
@ -1017,9 +1018,9 @@ export class CoreSite {
* @param {string} url The URL to open. * @param {string} url The URL to open.
* @param {any} [options] Override default options passed to InAppBrowser. * @param {any} [options] Override default options passed to InAppBrowser.
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<InAppBrowserObject>} Promise resolved when done.
*/ */
openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string) : Promise<any> { openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> {
return this.openWithAutoLogin(true, url, options, alertMessage); return this.openWithAutoLogin(true, url, options, alertMessage);
} }
@ -1029,9 +1030,9 @@ export class CoreSite {
* @param {string} url The URL to open. * @param {string} url The URL to open.
* @param {object} [options] Override default options passed to inappbrowser. * @param {object} [options] Override default options passed to inappbrowser.
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<InAppBrowserObject>} Promise resolved when done.
*/ */
openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string) : Promise<any> { openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> {
return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage); return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage);
} }
@ -1042,39 +1043,40 @@ export class CoreSite {
* @param {string} url The URL to open. * @param {string} url The URL to open.
* @param {object} [options] Override default options passed to $cordovaInAppBrowser#open. * @param {object} [options] Override default options passed to $cordovaInAppBrowser#open.
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
*/ */
openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<any> { openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> {
// Convenience function to open the URL. // Convenience function to open the URL.
let open = (url) => { let open = (url) => {
if (modal) { return new Promise<InAppBrowserObject>((resolve, reject) => {
modal.dismiss(); if (modal) {
} modal.dismiss();
if (alertMessage) {
let alert = this.domUtils.showAlert('mm.core.notice', alertMessage, null, 3000);
alert.onDidDismiss(() => {
if (inApp) {
this.utils.openInApp(url, options);
} else {
this.utils.openInBrowser(url);
}
});
} else {
if (inApp) {
this.utils.openInApp(url, options);
} else {
this.utils.openInBrowser(url);
} }
}
if (alertMessage) {
let alert = this.domUtils.showAlert('mm.core.notice', alertMessage, null, 3000);
alert.onDidDismiss(() => {
if (inApp) {
resolve(this.utils.openInApp(url, options));
} else {
resolve(this.utils.openInBrowser(url));
}
});
} else {
if (inApp) {
resolve(this.utils.openInApp(url, options));
} else {
resolve(this.utils.openInBrowser(url));
}
}
});
}; };
if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') || if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') ||
(this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < 6 * CoreConstants.secondsMinute)) { (this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < 6 * CoreConstants.secondsMinute)) {
// No private token, WS not available or last auto-login was less than 6 minutes ago. // No private token, WS not available or last auto-login was less than 6 minutes ago.
// Open the final URL without auto-login. // Open the final URL without auto-login.
open(url); return Promise.resolve(open(url));
return Promise.resolve();
} }
const userId = this.getUserId(), const userId = this.getUserId(),
@ -1087,16 +1089,15 @@ export class CoreSite {
return this.write('tool_mobile_get_autologin_key', params).then((data) => { return this.write('tool_mobile_get_autologin_key', params).then((data) => {
if (!data.autologinurl || !data.key) { if (!data.autologinurl || !data.key) {
// Not valid data, open the final URL without auto-login. // Not valid data, open the final URL without auto-login.
open(url); return open(url);
return;
} }
this.lastAutoLogin = this.timeUtils.timestamp(); this.lastAutoLogin = this.timeUtils.timestamp();
open(data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + url); return open(data.autologinurl + '?userid=' + userId + '&key=' + data.key + '&urltogo=' + url);
}).catch(() => { }).catch(() => {
// Couldn't get autologin key, open the final URL without auto-login. // Couldn't get autologin key, open the final URL without auto-login.
open(url); return open(url);
}); });
} }
@ -1107,9 +1108,9 @@ export class CoreSite {
* @param {string} url The URL to open. * @param {string} url The URL to open.
* @param {object} [options] Override default options passed to inappbrowser. * @param {object} [options] Override default options passed to inappbrowser.
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
*/ */
openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<any> { openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> {
if (this.containsUrl(url)) { if (this.containsUrl(url)) {
return this.openWithAutoLogin(inApp, url, options, alertMessage); return this.openWithAutoLogin(inApp, url, options, alertMessage);
} else { } else {
@ -1118,7 +1119,7 @@ export class CoreSite {
} else { } else {
this.utils.openInBrowser(url); this.utils.openInBrowser(url);
} }
return Promise.resolve(); return Promise.resolve(null);
} }
} }

View File

@ -13,11 +13,12 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { NavController } from 'ionic-angular'; import { NavController, Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreAppProvider } from '../../../providers/app'; import { CoreAppProvider } from '../../../providers/app';
import { CoreConfigProvider } from '../../../providers/config'; import { CoreConfigProvider } from '../../../providers/config';
import { CoreEventsProvider } from '../../../providers/events'; import { CoreEventsProvider } from '../../../providers/events';
import { CoreInitDelegate } from '../../../providers/init';
import { CoreLoggerProvider } from '../../../providers/logger'; import { CoreLoggerProvider } from '../../../providers/logger';
import { CoreSitesProvider } from '../../../providers/sites'; import { CoreSitesProvider } from '../../../providers/sites';
import { CoreWSProvider } from '../../../providers/ws'; import { CoreWSProvider } from '../../../providers/ws';
@ -44,12 +45,17 @@ export interface CoreLoginSSOData {
@Injectable() @Injectable()
export class CoreLoginHelperProvider { export class CoreLoginHelperProvider {
protected logger; protected logger;
protected isSSOConfirmShown = false;
protected isOpenEditAlertShown = false;
protected navCtrl: NavController;
lastInAppUrl: string;
waitingForBrowser = false;
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
private wsProvider: CoreWSProvider, private translate: TranslateService, private textUtils: CoreTextUtilsProvider, private wsProvider: CoreWSProvider, private translate: TranslateService, private textUtils: CoreTextUtilsProvider,
private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider,
private urlUtils: CoreUrlUtilsProvider,private configProvider: CoreConfigProvider, private urlUtils: CoreUrlUtilsProvider,private configProvider: CoreConfigProvider, private platform: Platform,
private emulatorHelper: CoreEmulatorHelperProvider) { private emulatorHelper: CoreEmulatorHelperProvider, private initDelegate: CoreInitDelegate) {
this.logger = logger.getInstance('CoreLoginHelper'); this.logger = logger.getInstance('CoreLoginHelper');
} }
@ -84,6 +90,75 @@ export class CoreLoginHelperProvider {
}); });
} }
/**
* Function to handle URL received by Custom URL Scheme. If it's a SSO login, perform authentication.
*
* @param {string} url URL received.
* @return {boolean} True if it's a SSO URL, false otherwise.
*/
appLaunchedByURL(url: string) : boolean {
let ssoScheme = CoreConfigConstants.customurlscheme + '://token=';
if (url.indexOf(ssoScheme) == -1) {
return false;
}
if (this.appProvider.isSSOAuthenticationOngoing()) {
// Authentication ongoing, probably duplicated request.
return true;
}
if (this.appProvider.isDesktop()) {
// In desktop, make sure InAppBrowser is closed.
this.utils.closeInAppBrowser(true);
}
// App opened using custom URL scheme. Probably an SSO authentication.
this.appProvider.startSSOAuthentication();
this.logger.debug('App launched by URL');
// Delete the sso scheme from the URL.
url = url.replace(ssoScheme, '');
// Some platforms like Windows add a slash at the end. Remove it.
// Some sites add a # at the end of the URL. If it's there, remove it.
url = url.replace(/\/?#?\/?$/, '');
// Decode from base64.
try {
url = atob(url);
} catch(err) {
// Error decoding the parameter.
this.logger.error('Error decoding parameter received for login SSO');
return false;
}
let modal = this.domUtils.showModalLoading('mm.login.authenticating', true),
siteData: CoreLoginSSOData;
// Wait for app to be ready.
this.initDelegate.ready().then(() => {
return this.validateBrowserSSOLogin(url);
}).then((data) => {
siteData = data;
return this.handleSSOLoginAuthentication(siteData.siteUrl, siteData.token, siteData.privateToken);
}).then(() => {
if (siteData.pageName) {
// State defined, go to that state instead of site initial page.
this.navCtrl.push(siteData.pageName, siteData.pageParams);
} else {
this.goToSiteInitialPage(this.navCtrl, true);
}
}).catch((errorMessage) => {
if (typeof errorMessage == 'string' && errorMessage != '') {
this.domUtils.showErrorModal(errorMessage);
}
}).finally(() => {
modal.dismiss();
this.appProvider.finishSSOAuthentication();
});
return true;
}
/** /**
* Check if a site allows requesting a password reset through the app. * Check if a site allows requesting a password reset through the app.
* *
@ -98,6 +173,17 @@ export class CoreLoginHelperProvider {
}); });
} }
/**
* Function called when an SSO InAppBrowser is closed or the app is resumed. Check if user needs to be logged out.
*/
checkLogout() {
if (!this.appProvider.isSSOAuthenticationOngoing() && this.sitesProvider.isLoggedIn() &&
this.sitesProvider.getCurrentSite().isLoggedOut() && this.navCtrl.getActive().name == 'CoreLoginReconnectPage') {
// User must reauthenticate but he closed the InAppBrowser without doing so, logout him.
this.sitesProvider.logout();
}
}
/** /**
* Show a confirm modal if needed and open a browser to perform SSO login. * Show a confirm modal if needed and open a browser to perform SSO login.
* *
@ -374,6 +460,39 @@ export class CoreLoginHelperProvider {
CoreConfigConstants.siteurl.length > 1; CoreConfigConstants.siteurl.length > 1;
} }
/**
* Function called when a page starts loading in any InAppBrowser window.
*
* @param {string} url Loaded url.
*/
inAppBrowserLoadStart(url: string) : void {
// URLs with a custom scheme can be prefixed with "http://" or "https://", we need to remove this.
url = url.replace(/^https?:\/\//, '');
if (this.appLaunchedByURL(url)) {
// Close the browser if it's a valid SSO URL.
this.utils.closeInAppBrowser(false);
} else if (this.platform.is('android')) {
// Check if the URL has a custom URL scheme. In Android they need to be opened manually.
const urlScheme = this.urlUtils.getUrlProtocol(url);
if (urlScheme && urlScheme !== 'file' && urlScheme !== 'cdvfile') {
// Open in browser should launch the right app if found and do nothing if not found.
this.utils.openInBrowser(url);
// At this point the InAppBrowser is showing a "Webpage not available" error message.
// Try to navigate to last loaded URL so this error message isn't found.
if (this.lastInAppUrl) {
this.utils.openInApp(this.lastInAppUrl);
} else {
// No last URL loaded, close the InAppBrowser.
this.utils.closeInAppBrowser(false);
}
} else {
this.lastInAppUrl = url;
}
}
}
/** /**
* Given a site public config, check if email signup is disabled. * Given a site public config, check if email signup is disabled.
* *
@ -580,6 +699,43 @@ export class CoreLoginHelperProvider {
this.utils.openInApp(siteUrl + '/login/forgot_password.php'); this.utils.openInApp(siteUrl + '/login/forgot_password.php');
} }
/*
* Function to open in app browser to change password or complete user profile.
*
* @param {string} siteId The site ID.
* @param {string} path The relative path of the URL to open.
* @param {string} alertMessage The key of the message to display before opening the in app browser.
* @param {boolean} [invalidateCache] Whether to invalidate site's cache (e.g. when the user is forced to change password).
*/
openInAppForEdit(siteId: string, path: string, alertMessage: string, invalidateCache?: boolean) : void {
if (!siteId || siteId !== this.sitesProvider.getCurrentSiteId()) {
// Site that triggered the event is not current site, nothing to do.
return;
}
let currentSite = this.sitesProvider.getCurrentSite(),
siteUrl = currentSite && currentSite.getURL();
if (!currentSite || !siteUrl) {
return;
}
if (!this.isOpenEditAlertShown && !this.waitingForBrowser) {
this.isOpenEditAlertShown = true;
if (invalidateCache) {
currentSite.invalidateWsCache();
}
// Open change password.
alertMessage = this.translate.instant(alertMessage) + '<br>' + this.translate.instant('mm.core.redirectingtosite');
currentSite.openInAppWithAutoLogin(siteUrl + path, undefined, alertMessage).then(() => {
this.waitingForBrowser = true;
}).finally(() => {
this.isOpenEditAlertShown = false;
});
}
}
/** /**
* Prepare the app to perform SSO login. * Prepare the app to perform SSO login.
* *
@ -633,6 +789,88 @@ export class CoreLoginHelperProvider {
return this.wsProvider.callAjax('core_auth_request_password_reset', params, {siteUrl: siteUrl}); return this.wsProvider.callAjax('core_auth_request_password_reset', params, {siteUrl: siteUrl});
} }
/**
* Function that should be called when the session expires. Reserved for core use.
*
* @param {any} data Data received by the SESSION_EXPIRED event.
*/
sessionExpired(data: any) : void {
let siteId = data && data.siteId,
currentSite = this.sitesProvider.getCurrentSite(),
siteUrl = currentSite && currentSite.getURL(),
promise;
if (!currentSite || !siteUrl) {
return;
}
if (siteId && siteId !== currentSite.getId()) {
return; // Site that triggered the event is not current site.
}
// Check authentication method.
this.sitesProvider.checkSite(siteUrl).then((result) => {
if (result.warning) {
this.domUtils.showErrorModal(result.warning, true, 4000);
}
if (this.isSSOLoginNeeded(result.code)) {
// SSO. User needs to authenticate in a browser. Prevent showing the message several times
// or show it again if the user is already authenticating using SSO.
if (!this.appProvider.isSSOAuthenticationOngoing() && !this.isSSOConfirmShown && !this.waitingForBrowser) {
this.isSSOConfirmShown = true;
if (this.shouldShowSSOConfirm(result.code)) {
promise = this.domUtils.showConfirm(this.translate.instant('mm.login.' +
(currentSite.isLoggedOut() ? 'loggedoutssodescription' : 'reconnectssodescription')));
} else {
promise = Promise.resolve();
}
promise.then(() => {
this.waitingForBrowser = true;
this.openBrowserForSSOLogin(result.siteUrl, result.code, result.service,
result.config && result.config.launchurl, data.pageName, data.params);
}).catch(() => {
// User cancelled, logout him.
this.sitesProvider.logout();
}).finally(() => {
this.isSSOConfirmShown = false;
});
}
} else {
let info = currentSite.getInfo();
if (typeof info != 'undefined' && typeof info.username != 'undefined') {
this.navCtrl.setRoot('CoreLoginReconnectPage', {
infoSiteUrl: info.siteurl,
siteUrl: result.siteUrl,
siteId: siteId,
pageName: data.pageName,
pageParams: data.params,
siteConfig: result.config
});
}
}
}).catch((error) => {
// Error checking site.
if (currentSite.isLoggedOut()) {
// Site is logged out, show error and logout the user.
this.domUtils.showErrorModalDefault(error, 'mm.core.networkerrormsg', true);
this.sitesProvider.logout();
}
});
}
/**
* Set a NavController to use.
*
* @param {NavController} navCtrl Nav controller.
*/
setNavCtrl(navCtrl: NavController) : void {
this.navCtrl = navCtrl;
}
/** /**
* Check if a confirm should be shown to open a SSO authentication. * Check if a confirm should be shown to open a SSO authentication.
* *
@ -644,6 +882,26 @@ export class CoreLoginHelperProvider {
(!CoreConfigConstants.skipssoconfirmation || String(CoreConfigConstants.skipssoconfirmation) === 'false'); (!CoreConfigConstants.skipssoconfirmation || String(CoreConfigConstants.skipssoconfirmation) === 'false');
} }
/**
* Function called when site policy is not agreed. Reserved for core use.
*
* @param {string} [siteId] Site ID. If not defined, current site.
*/
sitePolicyNotAgreed(siteId?: string) : void {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (!siteId || siteId != this.sitesProvider.getCurrentSiteId()) {
// Only current site allowed.
return;
}
if (!this.sitesProvider.getCurrentSite().wsAvailable('core_user_agree_site_policy')) {
// WS not available, stop.
return;
}
this.navCtrl.setRoot('CoreLoginSitePolicyPage', {siteId: siteId});
}
/** /**
* Convenient helper to handle get User Token error. It redirects to change password page if forcepassword is set. * Convenient helper to handle get User Token error. It redirects to change password page if forcepassword is set.
* *

View File

@ -43,6 +43,8 @@ export class CoreEventsProvider {
public static REMOTE_ADDONS_LOADED = 'remote_addons_loaded'; public static REMOTE_ADDONS_LOADED = 'remote_addons_loaded';
public static LOGIN_SITE_CHECKED = 'login_site_checked'; public static LOGIN_SITE_CHECKED = 'login_site_checked';
public static LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; public static LOGIN_SITE_UNCHECKED = 'login_site_unchecked';
public static IAB_LOAD_START = 'inappbrowser_load_start';
public static IAB_EXIT = 'inappbrowser_exit';
logger; logger;
observables = {}; observables = {};

View File

@ -19,6 +19,7 @@ import { InAppBrowser, InAppBrowserObject } from '@ionic-native/in-app-browser';
import { Clipboard } from '@ionic-native/clipboard'; import { Clipboard } from '@ionic-native/clipboard';
import { CoreAppProvider } from '../app'; import { CoreAppProvider } from '../app';
import { CoreDomUtilsProvider } from './dom'; import { CoreDomUtilsProvider } from './dom';
import { CoreEventsProvider } from '../events';
import { CoreLoggerProvider } from '../logger'; import { CoreLoggerProvider } from '../logger';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreLangProvider } from '../lang'; import { CoreLangProvider } from '../lang';
@ -39,7 +40,7 @@ export class CoreUtilsProvider {
constructor(private iab: InAppBrowser, private appProvider: CoreAppProvider, private clipboard: Clipboard, constructor(private iab: InAppBrowser, private appProvider: CoreAppProvider, private clipboard: Clipboard,
private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService, private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService,
private platform: Platform, private langProvider: CoreLangProvider) { private platform: Platform, private langProvider: CoreLangProvider, private eventsProvider: CoreEventsProvider) {
this.logger = logger.getInstance('CoreUtilsProvider'); this.logger = logger.getInstance('CoreUtilsProvider');
} }
@ -249,7 +250,7 @@ export class CoreUtilsProvider {
* *
* @param {boolean} [closeAll] Desktop only. True to close all secondary windows, false to close only the "current" one. * @param {boolean} [closeAll] Desktop only. True to close all secondary windows, false to close only the "current" one.
*/ */
closeInAppBrowser(closeAll: boolean) : void { closeInAppBrowser(closeAll?: boolean) : void {
if (this.iabInstance) { if (this.iabInstance) {
this.iabInstance.close(); this.iabInstance.close();
if (closeAll && this.appProvider.isDesktop()) { if (closeAll && this.appProvider.isDesktop()) {
@ -790,6 +791,17 @@ export class CoreUtilsProvider {
} }
this.iabInstance = this.iab.create(url, '_blank', options); this.iabInstance = this.iab.create(url, '_blank', options);
// Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1.
let loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => {
this.eventsProvider.trigger(CoreEventsProvider.IAB_LOAD_START, event);
});
let exitSubscription = this.iabInstance.on('exit').subscribe((event) => {
loadStartSubscription.unsubscribe();
exitSubscription.unsubscribe();
this.eventsProvider.trigger(CoreEventsProvider.IAB_EXIT, event);
});
return this.iabInstance; return this.iabInstance;
} }