MOBILE-2653 iframe: Display warning in offline if needed

main
dpalou 2018-10-11 15:36:27 +02:00
parent 85c3d0fc7c
commit 8f76caad92
6 changed files with 116 additions and 15 deletions

View File

@ -1403,6 +1403,7 @@
"core.more": "moodle",
"core.mygroups": "group",
"core.name": "moodle",
"core.networkerroriframemsg": "local_moodlemobileapp",
"core.networkerrormsg": "local_moodlemobileapp",
"core.never": "moodle",
"core.next": "moodle",

View File

@ -941,6 +941,10 @@ ion-app.app-root {
&.disable-scroll ion-modal .ion-page {
pointer-events: initial;
}
.core-iframe-offline-disabled {
display: none !important;
}
}
@each $color-name, $color-base, $color-contrast in get-colors($colors) {

View File

@ -66,7 +66,20 @@ export class CoreIconComponent implements OnInit {
const attrs = this.element.attributes;
for (let i = attrs.length - 1; i >= 0; i--) {
newElement.setAttribute(attrs[i].name, attrs[i].value);
if (attrs[i].name == 'class') {
// We don't want to override the classes we already added. Add them one by one.
if (attrs[i].value) {
const classes = attrs[i].value.split(' ');
for (let j = 0; j < classes.length; j++) {
if (classes[j]) {
newElement.classList.add(classes[j]);
}
}
}
} else {
newElement.setAttribute(attrs[i].name, attrs[i].value);
}
}
this.element.parentElement.replaceChild(newElement, this.element);
@ -79,12 +92,12 @@ export class CoreIconComponent implements OnInit {
* @return {boolean} If has a value equivalent to true.
*/
isTrueProperty(val: any): boolean {
if (typeof val === 'string') {
val = val.toLowerCase().trim();
if (typeof val === 'string') {
val = val.toLowerCase().trim();
return (val === 'true' || val === 'on' || val === '');
}
return (val === 'true' || val === 'on' || val === '');
}
return !!val;
return !!val;
}
}

View File

@ -153,6 +153,7 @@
"mygroups": "My groups",
"name": "Name",
"nograde": "No grade",
"networkerroriframemsg": "This content is not available offline. Please connect to the internet and try again.",
"networkerrormsg": "There was a problem connecting to the site. Please check your connection and try again.",
"never": "Never",
"next": "Next",

View File

@ -429,6 +429,18 @@ export class CoreDomUtilsProvider {
return parseInt(style[measure], 10) || 0;
}
/**
* Get the HTML code to render a connection warning icon.
*
* @return {string} HTML Code.
*/
getConnectionWarningIconHtml(): string {
return '<div text-center><span class="core-icon-with-badge">' +
'<ion-icon role="img" class="icon fa fa-wifi" aria-label="wifi"></ion-icon>' +
'<ion-icon class="icon fa fa-exclamation-triangle core-icon-badge"></ion-icon>' +
'</span></div>';
}
/**
* Returns width of an element.
*
@ -500,12 +512,9 @@ export class CoreDomUtilsProvider {
*/
private getErrorTitle(message: string): any {
if (message == this.translate.instant('core.networkerrormsg') ||
message == this.translate.instant('core.fileuploader.errormustbeonlinetoupload')) {
return this.sanitizer.bypassSecurityTrustHtml('<div text-center><span class="core-icon-with-badge">' +
'<ion-icon role="img" class="icon fa fa-wifi" aria-label="wifi"></ion-icon>' +
'<ion-icon class="icon fa fa-exclamation-triangle core-icon-badge"></ion-icon>' +
'</span></div>');
message == this.translate.instant('core.fileuploader.errormustbeonlinetoupload')) {
return this.sanitizer.bypassSecurityTrustHtml(this.getConnectionWarningIconHtml());
}
return this.textUtils.decodeHTML(this.translate.instant('core.error'));

View File

@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { Injectable, NgZone } from '@angular/core';
import { Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { Network } from '@ionic-native/network';
import { CoreAppProvider } from '../app';
import { CoreFileProvider } from '../file';
import { CoreLoggerProvider } from '../logger';
import { CoreSitesProvider } from '../sites';
@ -33,10 +36,75 @@ export class CoreIframeUtilsProvider {
constructor(logger: CoreLoggerProvider, private fileProvider: CoreFileProvider, private sitesProvider: CoreSitesProvider,
private urlUtils: CoreUrlUtilsProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider,
private domUtils: CoreDomUtilsProvider, private platform: Platform) {
private domUtils: CoreDomUtilsProvider, private platform: Platform, private appProvider: CoreAppProvider,
private translate: TranslateService, private network: Network, private zone: NgZone) {
this.logger = logger.getInstance('CoreUtilsProvider');
}
/**
* Check if a frame uses an online URL but the app is offline. If it does, the iframe is hidden and a warning is shown.
*
* @param {any} element The frame to check (iframe, embed, ...).
* @param {boolean} [isSubframe] Whether it's a frame inside another frame.
* @return {boolean} True if frame is online and the app is offline, false otherwise.
*/
checkOnlineFrameInOffline(element: any, isSubframe?: boolean): boolean {
const src = element.src || element.data;
if (src && src.match(/^https?:\/\//i) && !this.appProvider.isOnline()) {
if (element.classList.contains('core-iframe-offline-disabled')) {
// Iframe already hidden, stop.
return true;
}
// The frame has an online URL but the app is offline. Show a warning.
const div = document.createElement('div');
div.setAttribute('text-center', '');
div.setAttribute('padding', '');
div.classList.add('core-iframe-offline-warning');
div.innerHTML = (isSubframe ? '' : this.domUtils.getConnectionWarningIconHtml()) +
'<p>' + this.translate.instant('core.networkerroriframemsg') + '</p>';
element.parentElement.insertBefore(div, element);
// Add a class to specify that the iframe is hidden.
element.classList.add('core-iframe-offline-disabled');
if (isSubframe) {
// We cannot apply CSS styles in subframes, just hide the iframe.
element.style.display = 'none';
}
// If the network changes, check it again.
const subscription = this.network.onConnect().subscribe(() => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
this.zone.run(() => {
if (!this.checkOnlineFrameInOffline(element, isSubframe)) {
// Now the app is online, no need to check connection again.
subscription.unsubscribe();
}
});
});
return true;
} else if (element.classList.contains('core-iframe-offline-disabled')) {
// Reload the frame.
element.src = element.src;
element.data = element.data;
// Remove the warning and show the iframe
this.domUtils.removeElement(element.parentElement, 'div.core-iframe-offline-warning');
element.classList.remove('core-iframe-offline-disabled');
if (isSubframe) {
element.style.display = '';
}
}
return false;
}
/**
* Given an element, return the content window and document.
* Please notice that the element should be an iframe, embed or similar.
@ -136,7 +204,7 @@ export class CoreIframeUtilsProvider {
CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => {
const elements = Array.from(contentDocument.querySelectorAll(tag));
elements.forEach((subElement) => {
this.treatFrame(subElement);
this.treatFrame(subElement, true);
});
});
}
@ -147,9 +215,12 @@ export class CoreIframeUtilsProvider {
* Search links (<a>) and open them in browser or InAppBrowser if needed.
*
* @param {any} element Element to treat (iframe, embed, ...).
* @param {boolean} [isSubframe] Whether it's a frame inside another frame.
*/
treatFrame(element: any): void {
treatFrame(element: any, isSubframe?: boolean): 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);
@ -157,6 +228,8 @@ export class CoreIframeUtilsProvider {
this.treatFrameLinks(element, winAndDoc.document);
element.addEventListener('load', () => {
this.checkOnlineFrameInOffline(element, isSubframe);
// Element loaded, redefine window.open and treat links again.
winAndDoc = this.getContentWindowAndDocument(element);
this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document);