MOBILE-3013 core: Support handling root URLs
parent
6dd8786da6
commit
cab44018a6
|
@ -33,7 +33,7 @@ export class AddonModUrlHelperProvider {
|
||||||
*/
|
*/
|
||||||
open(url: string): void {
|
open(url: string): void {
|
||||||
const modal = this.domUtils.showModalLoading();
|
const modal = this.domUtils.showModalLoading();
|
||||||
this.contentLinksHelper.handleLink(url).then((treated) => {
|
this.contentLinksHelper.handleLink(url, undefined, undefined, true, true).then((treated) => {
|
||||||
if (!treated) {
|
if (!treated) {
|
||||||
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
|
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ export class AddonModUrlModuleHandler implements CoreCourseModuleHandler {
|
||||||
handler.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, handler.modName)
|
handler.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, handler.modName)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Check if the URL can be handled by the app. If so, always open it directly.
|
// Check if the URL can be handled by the app. If so, always open it directly.
|
||||||
return handler.contentLinksHelper.canHandleLink(module.contents[0].fileurl, courseId);
|
return handler.contentLinksHelper.canHandleLink(module.contents[0].fileurl, courseId, undefined, true);
|
||||||
}).then((canHandle) => {
|
}).then((canHandle) => {
|
||||||
if (canHandle) {
|
if (canHandle) {
|
||||||
// URL handled by the app, open it directly.
|
// URL handled by the app, open it directly.
|
||||||
|
|
|
@ -65,7 +65,7 @@ export class AddonNotificationsPushClickHandler implements CorePushNotifications
|
||||||
|
|
||||||
// Try to handle the appurl first.
|
// Try to handle the appurl first.
|
||||||
if (notification.customdata && notification.customdata.appurl) {
|
if (notification.customdata && notification.customdata.appurl) {
|
||||||
promise = this.linkHelper.handleLink(notification.customdata.appurl);
|
promise = this.linkHelper.handleLink(notification.customdata.appurl, undefined, undefined, true);
|
||||||
} else {
|
} else {
|
||||||
promise = Promise.resolve(false);
|
promise = Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1370,7 +1370,7 @@ export class CoreSite {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const siteUrl = this.urlUtils.removeProtocolAndWWW(this.siteUrl);
|
const siteUrl = this.textUtils.removeEndingSlash(this.urlUtils.removeProtocolAndWWW(this.siteUrl));
|
||||||
url = this.urlUtils.removeProtocolAndWWW(url);
|
url = this.urlUtils.removeProtocolAndWWW(url);
|
||||||
|
|
||||||
return url.indexOf(siteUrl) == 0;
|
return url.indexOf(siteUrl) == 0;
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { CoreContentLinksDelegate, CoreContentLinksAction } from './delegate';
|
||||||
import { CoreConstants } from '@core/constants';
|
import { CoreConstants } from '@core/constants';
|
||||||
import { CoreConfigConstants } from '../../../configconstants';
|
import { CoreConfigConstants } from '../../../configconstants';
|
||||||
import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
|
import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features regarding content links.
|
* Service that provides some features regarding content links.
|
||||||
|
@ -54,11 +55,27 @@ export class CoreContentLinksHelperProvider {
|
||||||
* @param {string} url URL to handle.
|
* @param {string} url URL to handle.
|
||||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||||
* @param {string} [username] Username to use to filter sites.
|
* @param {string} [username] Username to use to filter sites.
|
||||||
|
* @param {boolean} [checkRoot] Whether to check if the URL is the root URL of a site.
|
||||||
* @return {Promise<boolean>} Promise resolved with a boolean: whether the URL can be handled.
|
* @return {Promise<boolean>} Promise resolved with a boolean: whether the URL can be handled.
|
||||||
*/
|
*/
|
||||||
canHandleLink(url: string, courseId?: number, username?: string): Promise<boolean> {
|
canHandleLink(url: string, courseId?: number, username?: string, checkRoot?: boolean): Promise<boolean> {
|
||||||
|
let promise;
|
||||||
|
|
||||||
|
if (checkRoot) {
|
||||||
|
promise = this.isStoredRootURL(url, username);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((data) => {
|
||||||
|
if (data.site) {
|
||||||
|
// URL is the root of the site, can handle it.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
|
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
|
||||||
return !!this.getFirstValidAction(actions);
|
return !!this.getFirstValidAction(actions);
|
||||||
|
});
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -148,10 +165,16 @@ export class CoreContentLinksHelperProvider {
|
||||||
|
|
||||||
// Wait for the app to be ready.
|
// Wait for the app to be ready.
|
||||||
this.initDelegate.ready().then(() => {
|
this.initDelegate.ready().then(() => {
|
||||||
// Check if the site is stored.
|
// Check if it's the root URL.
|
||||||
return this.sitesProvider.getSiteIdsFromUrl(url, false, username);
|
return this.isStoredRootURL(url, username);
|
||||||
}).then((siteIds) => {
|
}).then((data) => {
|
||||||
if (siteIds.length) {
|
|
||||||
|
if (data.site) {
|
||||||
|
// Root URL.
|
||||||
|
modal.dismiss();
|
||||||
|
|
||||||
|
return this.handleRootURL(data.site, false);
|
||||||
|
} else if (data.hasSites) {
|
||||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||||
|
|
||||||
return this.handleLink(url, username).then((treated) => {
|
return this.handleLink(url, username).then((treated) => {
|
||||||
|
@ -161,11 +184,13 @@ export class CoreContentLinksHelperProvider {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Get the site URL.
|
// Get the site URL.
|
||||||
const siteUrl = this.contentLinksDelegate.getSiteUrl(url);
|
let siteUrl = this.contentLinksDelegate.getSiteUrl(url),
|
||||||
if (!siteUrl) {
|
urlToOpen = url;
|
||||||
this.domUtils.showErrorModal('core.login.invalidsite', true);
|
|
||||||
|
|
||||||
return;
|
if (!siteUrl) {
|
||||||
|
// Site URL not found, use the original URL since it could be the root URL of the site.
|
||||||
|
siteUrl = url;
|
||||||
|
urlToOpen = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that site exists.
|
// Check that site exists.
|
||||||
|
@ -176,7 +201,7 @@ export class CoreContentLinksHelperProvider {
|
||||||
pageParams = {
|
pageParams = {
|
||||||
siteUrl: result.siteUrl,
|
siteUrl: result.siteUrl,
|
||||||
username: username,
|
username: username,
|
||||||
urlToOpen: url,
|
urlToOpen: urlToOpen,
|
||||||
siteConfig: result.config
|
siteConfig: result.config
|
||||||
};
|
};
|
||||||
let promise,
|
let promise,
|
||||||
|
@ -210,14 +235,12 @@ export class CoreContentLinksHelperProvider {
|
||||||
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
||||||
result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
|
result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
|
||||||
} else if (!hasSitePluginsLoaded) {
|
} else if (!hasSitePluginsLoaded) {
|
||||||
this.appProvider.getRootNavController().setRoot(pageName, pageParams);
|
return this.loginHelper.goToNoSitePage(undefined, pageName, pageParams);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error) {
|
this.domUtils.showErrorModalDefault(error, this.translate.instant('core.login.invalidsite'));
|
||||||
this.domUtils.showErrorModal(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
|
@ -234,9 +257,28 @@ export class CoreContentLinksHelperProvider {
|
||||||
* @param {string} [username] Username related with the URL. E.g. in 'http://myuser@m.com', url would be 'http://m.com' and
|
* @param {string} [username] Username related with the URL. E.g. in 'http://myuser@m.com', url would be 'http://m.com' and
|
||||||
* the username 'myuser'. Don't use it if you don't want to filter by username.
|
* the username 'myuser'. Don't use it if you don't want to filter by username.
|
||||||
* @param {NavController} [navCtrl] Nav Controller to use to navigate.
|
* @param {NavController} [navCtrl] Nav Controller to use to navigate.
|
||||||
|
* @param {boolean} [checkRoot] Whether to check if the URL is the root URL of a site.
|
||||||
|
* @param {boolean} [openBrowserRoot] Whether to open in browser if it's root URL and it belongs to current site.
|
||||||
* @return {Promise<boolean>} Promise resolved with a boolean: true if URL was treated, false otherwise.
|
* @return {Promise<boolean>} Promise resolved with a boolean: true if URL was treated, false otherwise.
|
||||||
*/
|
*/
|
||||||
handleLink(url: string, username?: string, navCtrl?: NavController): Promise<boolean> {
|
handleLink(url: string, username?: string, navCtrl?: NavController, checkRoot?: boolean, openBrowserRoot?: boolean)
|
||||||
|
: Promise<boolean> {
|
||||||
|
let promise;
|
||||||
|
|
||||||
|
if (checkRoot) {
|
||||||
|
promise = this.isStoredRootURL(url, username);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((data) => {
|
||||||
|
if (data.site) {
|
||||||
|
// URL is the root of the site.
|
||||||
|
this.handleRootURL(data.site, openBrowserRoot);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the link should be treated by some component/addon.
|
// Check if the link should be treated by some component/addon.
|
||||||
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
|
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
|
||||||
const action = this.getFirstValidAction(actions);
|
const action = this.getFirstValidAction(actions);
|
||||||
|
@ -271,5 +313,63 @@ export class CoreContentLinksHelperProvider {
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a root URL of a site.
|
||||||
|
*
|
||||||
|
* @param {CoreSite} site Site to handle.
|
||||||
|
* @param {boolean} [openBrowserRoot] Whether to open in browser if it's root URL and it belongs to current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
handleRootURL(site: CoreSite, openBrowserRoot?: boolean): Promise<any> {
|
||||||
|
const currentSite = this.sitesProvider.getCurrentSite();
|
||||||
|
|
||||||
|
if (currentSite && currentSite.getURL() == site.getURL()) {
|
||||||
|
// Already logged in.
|
||||||
|
if (openBrowserRoot) {
|
||||||
|
return site.openInBrowserWithAutoLogin(site.getURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
// Login in the site.
|
||||||
|
return this.loginHelper.redirect('', {}, site.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a URL is the root URL of any of the stored sites. If so, return the site ID.
|
||||||
|
*
|
||||||
|
* @param {string} url URL to check.
|
||||||
|
* @param {string} username Username to check.
|
||||||
|
* @return {Promise<{site: CoreSite, hasSites: boolean}>} Promise resolved with site and whether there is any site to treat
|
||||||
|
* the URL. Site will be undefined if it isn't the root URL of any stored site.
|
||||||
|
*/
|
||||||
|
isStoredRootURL(url: string, username: string): Promise<{site: CoreSite, hasSites: boolean}> {
|
||||||
|
// Check if the site is stored.
|
||||||
|
return this.sitesProvider.getSiteIdsFromUrl(url, true, username).then((siteIds) => {
|
||||||
|
const result = {
|
||||||
|
hasSites: siteIds.length > 0,
|
||||||
|
site: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
if (result.hasSites) {
|
||||||
|
// If more than one site is returned it usually means there are different users stored. Use any of them.
|
||||||
|
return this.sitesProvider.getSite(siteIds[0]).then((site) => {
|
||||||
|
const siteUrl = this.textUtils.removeEndingSlash(this.urlUtils.removeProtocolAndWWW(site.getURL())),
|
||||||
|
treatedUrl = this.textUtils.removeEndingSlash(this.urlUtils.removeProtocolAndWWW(url));
|
||||||
|
|
||||||
|
if (siteUrl == treatedUrl) {
|
||||||
|
result.site = site;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class CoreLoginInitPage {
|
||||||
this.initDelegate.ready().then(() => {
|
this.initDelegate.ready().then(() => {
|
||||||
// Check if there was a pending redirect.
|
// Check if there was a pending redirect.
|
||||||
const redirectData = this.appProvider.getRedirect();
|
const redirectData = this.appProvider.getRedirect();
|
||||||
if (redirectData.siteId && redirectData.page) {
|
if (redirectData.siteId) {
|
||||||
// Unset redirect data.
|
// Unset redirect data.
|
||||||
this.appProvider.storeRedirect('', '', '');
|
this.appProvider.storeRedirect('', '', '');
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ export class CoreLoginInitPage {
|
||||||
return this.loadPage();
|
return this.loadPage();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// No site to load, just open the state.
|
// No site to load, open the page.
|
||||||
return this.navCtrl.setRoot(redirectData.page, redirectData.params, { animate: false });
|
return this.loginHelper.goToNoSitePage(this.navCtrl, redirectData.page, redirectData.params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -416,6 +416,43 @@ export class CoreLoginHelperProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a page that doesn't belong to any site.
|
||||||
|
*
|
||||||
|
* @param {NavController} [navCtrl] Nav Controller.
|
||||||
|
* @param {string} [page] Page to open.
|
||||||
|
* @param {any} [params] Params of the page.
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
goToNoSitePage(navCtrl: NavController, page: string, params?: any): Promise<any> {
|
||||||
|
navCtrl = navCtrl || this.appProvider.getRootNavController();
|
||||||
|
|
||||||
|
if (page == 'CoreLoginSitesPage') {
|
||||||
|
// Just open the page as root.
|
||||||
|
return navCtrl.setRoot(page, params);
|
||||||
|
} else {
|
||||||
|
// Check if there is any site stored.
|
||||||
|
return this.sitesProvider.hasSites().then((hasSites) => {
|
||||||
|
if (hasSites) {
|
||||||
|
// There are sites stored, open sites page first to be able to go back.
|
||||||
|
navCtrl.setRoot('CoreLoginSitesPage');
|
||||||
|
|
||||||
|
return navCtrl.push(page, page, {animate: false});
|
||||||
|
} else {
|
||||||
|
if (page != 'CoreLoginSitePage') {
|
||||||
|
// Open the new site page to be able to go back.
|
||||||
|
navCtrl.setRoot('CoreLoginSitePage');
|
||||||
|
|
||||||
|
return navCtrl.push(page, page, {animate: false});
|
||||||
|
} else {
|
||||||
|
// Just open the page as root.
|
||||||
|
return navCtrl.setRoot(page, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Go to the initial page of a site depending on 'userhomepage' setting.
|
* Go to the initial page of a site depending on 'userhomepage' setting.
|
||||||
*
|
*
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class CoreLinkDirective implements OnInit {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (this.utils.isTrueOrOne(this.capture)) {
|
if (this.utils.isTrueOrOne(this.capture)) {
|
||||||
this.contentLinksHelper.handleLink(href, undefined, navCtrl).then((treated) => {
|
this.contentLinksHelper.handleLink(href, undefined, navCtrl, true, true).then((treated) => {
|
||||||
if (!treated) {
|
if (!treated) {
|
||||||
this.navigate(href);
|
this.navigate(href);
|
||||||
}
|
}
|
||||||
|
|
|
@ -551,6 +551,24 @@ export class CoreTextUtilsProvider {
|
||||||
return typeof defaultValue != 'undefined' ? defaultValue : json;
|
return typeof defaultValue != 'undefined' ? defaultValue : json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove ending slash from a path or URL.
|
||||||
|
*
|
||||||
|
* @param {string} text Text to treat.
|
||||||
|
* @return {string} Treated text.
|
||||||
|
*/
|
||||||
|
removeEndingSlash(text: string): string {
|
||||||
|
if (!text) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.slice(-1) == '/') {
|
||||||
|
return text.substr(0, text.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace all characters that cause problems with files in Android and iOS.
|
* Replace all characters that cause problems with files in Android and iOS.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue