Merge pull request #1565 from dpalou/MOBILE-2653
MOBILE-2653 iframe: Display warning in offline if needed
This commit is contained in:
		
						commit
						47408c9602
					
				| @ -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,8 +66,21 @@ export class CoreIconComponent implements OnInit { | ||||
| 
 | ||||
|         const attrs = this.element.attributes; | ||||
|         for (let i = attrs.length - 1; i >= 0; i--) { | ||||
|             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); | ||||
|     } | ||||
|  | ||||
| @ -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. | ||||
|      * | ||||
| @ -501,11 +513,8 @@ 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>'); | ||||
| 
 | ||||
|             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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user