MOBILE-2653 iframe: Display warning in offline if needed
parent
85c3d0fc7c
commit
8f76caad92
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue