MOBILE-2995 qr: Improve handling of custom URL scheme links

main
Dani Palou 2019-10-17 11:37:41 +02:00
parent c3f39abafa
commit f9df95c727
6 changed files with 99 additions and 46 deletions

View File

@ -1269,14 +1269,17 @@ export class CoreCourseHelperProvider {
// Get the module.
return this.courseProvider.getModule(moduleId, courseId, sectionId, false, false, siteId, modName);
}).then((module) => {
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId, false);
if (navCtrl && module.handlerData && module.handlerData.action) {
if (navCtrl && this.sitesProvider.isLoggedIn() && this.sitesProvider.getCurrentSiteId() == site.getId()) {
// If the link handler for this module passed through navCtrl, we can use the module's handler to navigate cleanly.
// Otherwise, we will redirect below.
modal.dismiss();
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId, false);
return module.handlerData.action(new Event('click'), navCtrl, module, courseId, undefined, modParams);
if (module.handlerData && module.handlerData.action) {
modal.dismiss();
return module.handlerData.action(new Event('click'), navCtrl, module, courseId, undefined, modParams);
}
}
this.logger.warn('navCtrl was not passed to navigateToModule by the link handler for ' + module.modname);

View File

@ -17,6 +17,7 @@ import { IonicPage, NavController, ModalController, AlertController, NavParams }
import { CoreAppProvider } from '@providers/app';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider, CoreSiteCheckResponse, CoreLoginSiteInfo } from '@providers/sites';
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreUrlUtilsProvider } from '@providers/utils/url';
@ -72,7 +73,8 @@ export class CoreLoginSitePage {
protected domUtils: CoreDomUtilsProvider,
protected eventsProvider: CoreEventsProvider,
protected translate: TranslateService,
protected utils: CoreUtilsProvider) {
protected utils: CoreUtilsProvider,
private urlSchemesProvider: CoreCustomURLSchemesProvider) {
this.showKeyboard = !!navParams.get('showKeyboard');
this.showScanQR = this.utils.canScanQR();
@ -365,9 +367,14 @@ export class CoreLoginSitePage {
// Scan for a QR code.
this.utils.scanQR().then((text) => {
if (text) {
this.siteForm.controls.siteUrl.setValue(text);
if (this.urlSchemesProvider.isCustomURL(text)) {
this.urlSchemesProvider.handleCustomURL(text);
} else {
// Not a custom URL scheme, put the text in the field.
this.siteForm.controls.siteUrl.setValue(text);
this.connect(new Event('click'), text);
this.connect(new Event('click'), text);
}
}
});
}

View File

@ -16,6 +16,7 @@ import { Component, OnDestroy } from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites';
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/delegate';
@ -50,16 +51,17 @@ export class CoreMainMenuMorePage implements OnDestroy {
protected langObserver;
protected updateSiteObserver;
constructor(private menuDelegate: CoreMainMenuDelegate,
private sitesProvider: CoreSitesProvider,
private navCtrl: NavController,
private mainMenuProvider: CoreMainMenuProvider,
constructor(protected menuDelegate: CoreMainMenuDelegate,
protected sitesProvider: CoreSitesProvider,
protected navCtrl: NavController,
protected mainMenuProvider: CoreMainMenuProvider,
eventsProvider: CoreEventsProvider,
private loginHelper: CoreLoginHelperProvider,
private utils: CoreUtilsProvider,
private linkHelper: CoreContentLinksHelperProvider,
private textUtils: CoreTextUtilsProvider,
private translate: TranslateService) {
protected loginHelper: CoreLoginHelperProvider,
protected utils: CoreUtilsProvider,
protected linkHelper: CoreContentLinksHelperProvider,
protected textUtils: CoreTextUtilsProvider,
protected urlSchemesProvider: CoreCustomURLSchemesProvider,
protected translate: TranslateService) {
this.langObserver = eventsProvider.on(CoreEventsProvider.LANGUAGE_CHANGED, this.loadSiteInfo.bind(this));
this.updateSiteObserver = eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.loadSiteInfo.bind(this),
@ -175,8 +177,10 @@ export class CoreMainMenuMorePage implements OnDestroy {
// Scan for a QR code.
this.utils.scanQR().then((text) => {
if (text) {
// Check if it's a URL. We basically check it has a protocol and doesn't include any space.
if (/^[^:]{2,}:\/\/[^ ]+$/i.test(text)) {
if (this.urlSchemesProvider.isCustomURL(text)) {
// Is a custom URL scheme, handle it.
this.urlSchemesProvider.handleCustomURL(text);
} else if (/^[^:]{2,}:\/\/[^ ]+$/i.test(text)) { // Check if it's a URL.
// Check if the app can handle the URL.
this.linkHelper.handleLink(text, undefined, this.navCtrl, true, true).then((treated) => {
if (!treated) {

View File

@ -35,6 +35,7 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreFilterProvider, CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
import { CoreFilterDelegate } from '@core/filter/providers/delegate';
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
/**
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
@ -87,13 +88,14 @@ export class CoreFormatTextDirective implements OnChanges {
protected contentLinksHelper: CoreContentLinksHelperProvider,
@Optional() protected navCtrl: NavController,
@Optional() protected content: Content, @Optional()
protected svComponent: CoreSplitViewComponent,
@Optional() protected svComponent: CoreSplitViewComponent,
protected iframeUtils: CoreIframeUtilsProvider,
protected eventsProvider: CoreEventsProvider,
protected filterProvider: CoreFilterProvider,
protected filterHelper: CoreFilterHelperProvider,
protected filterDelegate: CoreFilterDelegate,
protected viewContainerRef: ViewContainerRef,
protected urlSchemesProvider: CoreCustomURLSchemesProvider
) {
this.element = element.nativeElement;
@ -467,7 +469,7 @@ export class CoreFormatTextDirective implements OnChanges {
anchors.forEach((anchor) => {
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
const linkDir = new CoreLinkDirective(anchor, this.domUtils, this.utils, this.sitesProvider, this.urlUtils,
this.contentLinksHelper, this.navCtrl, this.content, this.svComponent, this.textUtils);
this.contentLinksHelper, this.navCtrl, this.content, this.svComponent, this.textUtils, this.urlSchemesProvider);
linkDir.capture = true;
linkDir.ngOnInit();

View File

@ -19,9 +19,9 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUrlUtilsProvider } from '@providers/utils/url';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreConfigConstants } from '../configconstants';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreCustomURLSchemesProvider } from '@providers/urlschemes';
/**
* Directive to open a link in external browser.
@ -39,11 +39,17 @@ export class CoreLinkDirective implements OnInit {
protected element: HTMLElement;
constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider,
private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
private contentLinksHelper: CoreContentLinksHelperProvider, @Optional() private navCtrl: NavController,
@Optional() private content: Content, @Optional() private svComponent: CoreSplitViewComponent,
private textUtils: CoreTextUtilsProvider) {
constructor(element: ElementRef,
protected domUtils: CoreDomUtilsProvider,
protected utils: CoreUtilsProvider,
protected sitesProvider: CoreSitesProvider,
protected urlUtils: CoreUrlUtilsProvider,
protected contentLinksHelper: CoreContentLinksHelperProvider,
@Optional() protected navCtrl: NavController,
@Optional() protected content: Content,
@Optional() protected svComponent: CoreSplitViewComponent,
protected textUtils: CoreTextUtilsProvider,
protected urlSchemesProvider: CoreCustomURLSchemesProvider) {
// This directive can be added dynamically. In that case, the first param is the anchor HTMLElement.
this.element = element.nativeElement || element;
}
@ -90,7 +96,6 @@ export class CoreLinkDirective implements OnInit {
* @param href HREF to be opened.
*/
protected navigate(href: string): void {
const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link=';
if (this.urlUtils.isLocalFileUrl(href)) {
// We have a local file.
@ -107,10 +112,8 @@ export class CoreLinkDirective implements OnInit {
// Look for id or name.
this.domUtils.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
}
} else if (href.indexOf(contentLinksScheme) === 0) {
// Link should be treated by Custom URL Scheme. Encode the right part, otherwise ':' is removed in iOS.
href = contentLinksScheme + encodeURIComponent(href.replace(contentLinksScheme, ''));
this.utils.openInBrowser(href);
} else if (this.urlSchemesProvider.isCustomURL(href)) {
this.urlSchemesProvider.handleCustomURL(href);
} else {
// It's an external link, we will open with browser. Check if we need to auto-login.

View File

@ -54,12 +54,19 @@ export class CoreCustomURLSchemesProvider {
protected logger;
protected lastUrls = {};
constructor(logger: CoreLoggerProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider,
private loginHelper: CoreLoginHelperProvider, private linksHelper: CoreContentLinksHelperProvider,
private initDelegate: CoreInitDelegate, private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider,
private sitesProvider: CoreSitesProvider, private textUtils: CoreTextUtilsProvider,
private linksDelegate: CoreContentLinksDelegate, private translate: TranslateService,
private sitePluginsProvider: CoreSitePluginsProvider) {
constructor(logger: CoreLoggerProvider,
protected appProvider: CoreAppProvider,
protected utils: CoreUtilsProvider,
protected loginHelper: CoreLoginHelperProvider,
protected linksHelper: CoreContentLinksHelperProvider,
protected initDelegate: CoreInitDelegate,
protected domUtils: CoreDomUtilsProvider,
protected urlUtils: CoreUrlUtilsProvider,
protected sitesProvider: CoreSitesProvider,
protected textUtils: CoreTextUtilsProvider,
protected linksDelegate: CoreContentLinksDelegate,
protected translate: TranslateService,
protected sitePluginsProvider: CoreSitePluginsProvider) {
this.logger = logger.getInstance('CoreCustomURLSchemesProvider');
}
@ -282,8 +289,7 @@ export class CoreCustomURLSchemesProvider {
* @return Promise resolved with the data.
*/
protected getCustomURLData(url: string): Promise<CoreCustomURLSchemesParams> {
const urlScheme = CoreConfigConstants.customurlscheme + '://';
if (url.indexOf(urlScheme) == -1) {
if (!this.isCustomURL(url)) {
return Promise.reject(null);
}
@ -291,7 +297,7 @@ export class CoreCustomURLSchemesProvider {
this.logger.debug('Treating custom URL scheme: ' + url);
// Delete the sso scheme from the URL.
url = url.replace(urlScheme, '');
url = this.removeCustomURLScheme(url);
// Detect if there's a user specified.
const username = this.urlUtils.getUsernameFromUrl(url);
@ -344,8 +350,7 @@ export class CoreCustomURLSchemesProvider {
* @return Promise resolved with the data.
*/
protected getCustomURLLinkData(url: string): Promise<CoreCustomURLSchemesParams> {
const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link=';
if (url.indexOf(contentLinksScheme) == -1) {
if (!this.isCustomURLLink(url)) {
return Promise.reject(null);
}
@ -353,7 +358,7 @@ export class CoreCustomURLSchemesProvider {
this.logger.debug('Treating custom URL scheme with link param: ' + url);
// Delete the sso scheme from the URL.
url = url.replace(contentLinksScheme, '');
url = this.removeCustomURLLinkScheme(url);
// Detect if there's a user specified.
const username = this.urlUtils.getUsernameFromUrl(url);
@ -408,8 +413,7 @@ export class CoreCustomURLSchemesProvider {
* @return Promise resolved with the data.
*/
protected getCustomURLTokenData(url: string): Promise<CoreCustomURLSchemesParams> {
const ssoScheme = CoreConfigConstants.customurlscheme + '://token=';
if (url.indexOf(ssoScheme) == -1) {
if (!this.isCustomURLToken(url)) {
return Promise.reject(null);
}
@ -428,7 +432,7 @@ export class CoreCustomURLSchemesProvider {
this.logger.debug('App launched by URL with an SSO');
// Delete the sso scheme from the URL.
url = url.replace(ssoScheme, '');
url = this.removeCustomURLTokenScheme(url);
// 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.
@ -488,6 +492,36 @@ export class CoreCustomURLSchemesProvider {
return url.indexOf(CoreConfigConstants.customurlscheme + '://token=') != -1;
}
/**
* Remove the scheme from a custom URL.
*
* @param url URL to treat.
* @return URL without scheme.
*/
removeCustomURLScheme(url: string): string {
return url.replace(CoreConfigConstants.customurlscheme + '://', '');
}
/**
* Remove the scheme and the "link=" prefix from a link custom URL.
*
* @param url URL to treat.
* @return URL without scheme and prefix.
*/
removeCustomURLLinkScheme(url: string): string {
return url.replace(CoreConfigConstants.customurlscheme + '://link=', '');
}
/**
* Remove the scheme and the "token=" prefix from a token custom URL.
*
* @param url URL to treat.
* @return URL without scheme and prefix.
*/
removeCustomURLTokenScheme(url: string): string {
return url.replace(CoreConfigConstants.customurlscheme + '://token=', '');
}
}
export class CoreCustomURLSchemes extends makeSingleton(CoreCustomURLSchemesProvider) {}