From 8d64fea2afacf8c19f47e71704d8667d1e66aa96 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 16 Jul 2019 08:22:58 +0200 Subject: [PATCH] MOBILE-3098 iframe: Open in app links inside iframes --- src/components/iframe/iframe.ts | 13 ++++++-- .../siteplugins/components/block/block.ts | 2 +- src/directives/format-text.ts | 21 +++++++----- src/providers/utils/iframe.ts | 33 +++++++++++-------- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/components/iframe/iframe.ts b/src/components/iframe/iframe.ts index 5988289a7..722d9eae6 100644 --- a/src/components/iframe/iframe.ts +++ b/src/components/iframe/iframe.ts @@ -12,11 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; +import { + Component, Input, Output, OnInit, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, Optional +} from '@angular/core'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { NavController } from 'ionic-angular'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreIframeUtilsProvider } from '@providers/utils/iframe'; +import { CoreSplitViewComponent } from '@components/split-view/split-view'; /** */ @@ -38,7 +42,9 @@ export class CoreIframeComponent implements OnInit, OnChanges { protected IFRAME_TIMEOUT = 15000; constructor(logger: CoreLoggerProvider, private iframeUtils: CoreIframeUtilsProvider, private domUtils: CoreDomUtilsProvider, - private sanitizer: DomSanitizer) { + private sanitizer: DomSanitizer, private navCtrl: NavController, + @Optional() private svComponent: CoreSplitViewComponent) { + this.logger = logger.getInstance('CoreIframe'); this.loaded = new EventEmitter(); } @@ -55,7 +61,8 @@ export class CoreIframeComponent implements OnInit, OnChanges { // Show loading only with external URLs. this.loading = !this.src || !!this.src.match(/^https?:\/\//i); - this.iframeUtils.treatFrame(iframe); + const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; + this.iframeUtils.treatFrame(iframe, false, navCtrl); if (this.loading) { iframe.addEventListener('load', () => { diff --git a/src/core/siteplugins/components/block/block.ts b/src/core/siteplugins/components/block/block.ts index d1e927add..807bceba3 100644 --- a/src/core/siteplugins/components/block/block.ts +++ b/src/core/siteplugins/components/block/block.ts @@ -27,7 +27,7 @@ import { CoreBlockDelegate } from '@core/block/providers/delegate'; }) export class CoreSitePluginsBlockComponent extends CoreBlockBaseComponent implements OnChanges { @Input() block: any; - @Input() contextLevel: number; + @Input() contextLevel: string; @Input() instanceId: number; @ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent; diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index 8cfbd3067..489561674 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -352,7 +352,8 @@ export class CoreFormatTextDirective implements OnChanges { this.utils.isTrueOrOne(this.singleLine), undefined, this.highlight); }).then((formatted) => { const div = document.createElement('div'), - canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']); + canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']), + navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; let images, anchors, audios, @@ -405,12 +406,12 @@ export class CoreFormatTextDirective implements OnChanges { }); videos.forEach((video) => { - this.treatVideoFilters(video); + this.treatVideoFilters(video, navCtrl); this.treatMedia(video); }); iframes.forEach((iframe) => { - this.treatIframe(iframe, site, canTreatVimeo); + this.treatIframe(iframe, site, canTreatVimeo, navCtrl); }); // Handle buttons with inner links. @@ -439,7 +440,7 @@ export class CoreFormatTextDirective implements OnChanges { // Handle all kind of frames. frames.forEach((frame: any) => { - this.iframeUtils.treatFrame(frame); + this.iframeUtils.treatFrame(frame, false, navCtrl); }); this.domUtils.handleBootstrapTooltips(div); @@ -508,8 +509,9 @@ export class CoreFormatTextDirective implements OnChanges { * Treat video filters. Currently only treating youtube video using video JS. * * @param {HTMLElement} el Video element. + * @param {NavController} navCtrl NavController to use. */ - protected treatVideoFilters(video: HTMLElement): void { + protected treatVideoFilters(video: HTMLElement, navCtrl: NavController): void { // Treat Video JS Youtube video links and translate them to iframes. if (!video.classList.contains('video-js')) { return; @@ -534,7 +536,7 @@ export class CoreFormatTextDirective implements OnChanges { // Replace video tag by the iframe. video.parentNode.replaceChild(iframe, video); - this.iframeUtils.treatFrame(iframe); + this.iframeUtils.treatFrame(iframe, false, navCtrl); } /** @@ -571,8 +573,9 @@ export class CoreFormatTextDirective implements OnChanges { * @param {HTMLIFrameElement} iframe Iframe to treat. * @param {CoreSite} site Site instance. * @param {boolean} canTreatVimeo Whether Vimeo videos can be treated in the site. + * @param {NavController} navCtrl NavController to use. */ - protected treatIframe(iframe: HTMLIFrameElement, site: CoreSite, canTreatVimeo: boolean): void { + protected treatIframe(iframe: HTMLIFrameElement, site: CoreSite, canTreatVimeo: boolean, navCtrl: NavController): void { const src = iframe.src, currentSite = this.sitesProvider.getCurrentSite(); @@ -583,7 +586,7 @@ export class CoreFormatTextDirective implements OnChanges { currentSite.getAutoLoginUrl(src, false).then((finalUrl) => { iframe.src = finalUrl; - this.iframeUtils.treatFrame(iframe); + this.iframeUtils.treatFrame(iframe, false, navCtrl); }); return; @@ -644,7 +647,7 @@ export class CoreFormatTextDirective implements OnChanges { } } - this.iframeUtils.treatFrame(iframe); + this.iframeUtils.treatFrame(iframe, false, navCtrl); } /** diff --git a/src/providers/utils/iframe.ts b/src/providers/utils/iframe.ts index 6771a6ed1..2952c22b1 100644 --- a/src/providers/utils/iframe.ts +++ b/src/providers/utils/iframe.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Injectable, NgZone } from '@angular/core'; -import { Config, Platform } from 'ionic-angular'; +import { Config, Platform, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { Network } from '@ionic-native/network'; import { CoreAppProvider } from '../app'; @@ -191,8 +191,9 @@ export class CoreIframeUtilsProvider { * @param {any} element Element to treat (iframe, embed, ...). * @param {Window} contentWindow The window of the element contents. * @param {Document} contentDocument The document of the element contents. + * @param {NavController} [navCtrl] NavController to use if a link can be opened in the app. */ - redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document): void { + redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document, navCtrl?: NavController): void { if (contentWindow) { // Intercept window.open. contentWindow.open = (url: string, target: string): Window => { @@ -229,13 +230,18 @@ export class CoreIframeUtilsProvider { this.domUtils.showErrorModal(error); }); } else { - // It's an external link, we will open with browser. Check if we need to auto-login. - if (!this.sitesProvider.isLoggedIn()) { - // Not logged in, cannot auto-login. - this.utils.openInBrowser(url); - } else { - this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); - } + // It's an external link, check if it can be opened in the app. + this.contentLinksHelper.handleLink(url, undefined, navCtrl, true, true).then((treated) => { + if (!treated) { + // Not opened in the app, open with browser. Check if we need to auto-login + if (!this.sitesProvider.isLoggedIn()) { + // Not logged in, cannot auto-login. + this.utils.openInBrowser(url); + } else { + this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); + } + } + }); } // We cannot create new Window objects directly, return null which is a valid return value for Window.open(). @@ -248,7 +254,7 @@ export class CoreIframeUtilsProvider { CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => { const elements = Array.from(contentDocument.querySelectorAll(tag)); elements.forEach((subElement) => { - this.treatFrame(subElement, true); + this.treatFrame(subElement, true, navCtrl); }); }); } @@ -260,14 +266,15 @@ export class CoreIframeUtilsProvider { * * @param {any} element Element to treat (iframe, embed, ...). * @param {boolean} [isSubframe] Whether it's a frame inside another frame. + * @param {NavController} [navCtrl] NavController to use if a link can be opened in the app. */ - treatFrame(element: any, isSubframe?: boolean): void { + treatFrame(element: any, isSubframe?: boolean, navCtrl?: NavController): void { if (element) { this.checkOnlineFrameInOffline(element, isSubframe); let winAndDoc = this.getContentWindowAndDocument(element); // Redefine window.open in this element and sub frames, it might have been loaded already. - this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document); + this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl); // Treat links. this.treatFrameLinks(element, winAndDoc.document); @@ -276,7 +283,7 @@ export class CoreIframeUtilsProvider { // Element loaded, redefine window.open and treat links again. winAndDoc = this.getContentWindowAndDocument(element); - this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document); + this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl); this.treatFrameLinks(element, winAndDoc.document); if (winAndDoc.window) {