forked from EVOgeek/Vmeda.Online
		
	
						commit
						6d0d3248b1
					
				
							
								
								
									
										248
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								package.json
									
									
									
									
									
								
							| @ -33,141 +33,141 @@ | ||||
|     "ionic:build:before": "gulp" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@angular/animations": "^11.0.1", | ||||
|     "@angular/common": "~10.0.0", | ||||
|     "@angular/core": "~10.0.0", | ||||
|     "@angular/forms": "~10.0.0", | ||||
|     "@angular/platform-browser": "~10.0.0", | ||||
|     "@angular/platform-browser-dynamic": "~10.0.0", | ||||
|     "@angular/router": "~10.0.0", | ||||
|     "@ionic-native/badge": "^5.33.0", | ||||
|     "@ionic-native/camera": "^5.33.0", | ||||
|     "@ionic-native/chooser": "^5.33.0", | ||||
|     "@ionic-native/clipboard": "^5.33.0", | ||||
|     "@ionic-native/core": "^5.33.0", | ||||
|     "@ionic-native/device": "^5.33.0", | ||||
|     "@ionic-native/diagnostic": "^5.33.0", | ||||
|     "@ionic-native/file": "^5.33.0", | ||||
|     "@ionic-native/file-opener": "^5.33.0", | ||||
|     "@ionic-native/file-transfer": "^5.33.0", | ||||
|     "@ionic-native/geolocation": "^5.33.0", | ||||
|     "@ionic-native/http": "^5.33.0", | ||||
|     "@ionic-native/in-app-browser": "^5.33.0", | ||||
|     "@ionic-native/ionic-webview": "^5.33.0", | ||||
|     "@ionic-native/keyboard": "^5.33.0", | ||||
|     "@ionic-native/local-notifications": "^5.33.0", | ||||
|     "@ionic-native/media": "^5.33.0", | ||||
|     "@ionic-native/media-capture": "^5.33.0", | ||||
|     "@ionic-native/network": "^5.33.0", | ||||
|     "@ionic-native/push": "^5.33.0", | ||||
|     "@ionic-native/qr-scanner": "^5.33.0", | ||||
|     "@ionic-native/splash-screen": "^5.33.0", | ||||
|     "@ionic-native/sqlite": "^5.33.0", | ||||
|     "@ionic-native/status-bar": "^5.33.0", | ||||
|     "@ionic-native/web-intent": "^5.33.0", | ||||
|     "@ionic-native/zip": "^5.33.0", | ||||
|     "@ionic/angular": "^5.6.6", | ||||
|     "@ngx-translate/core": "^13.0.0", | ||||
|     "@ngx-translate/http-loader": "^6.0.0", | ||||
|     "@types/chart.js": "^2.9.31", | ||||
|     "@angular/animations": "11.0.1", | ||||
|     "@angular/common": "10.0.14", | ||||
|     "@angular/core": "10.0.14", | ||||
|     "@angular/forms": "10.0.14", | ||||
|     "@angular/platform-browser": "10.0.14", | ||||
|     "@angular/platform-browser-dynamic": "10.0.14", | ||||
|     "@angular/router": "10.0.14", | ||||
|     "@ionic-native/badge": "5.33.0", | ||||
|     "@ionic-native/camera": "5.33.0", | ||||
|     "@ionic-native/chooser": "5.33.0", | ||||
|     "@ionic-native/clipboard": "5.33.0", | ||||
|     "@ionic-native/core": "5.33.0", | ||||
|     "@ionic-native/device": "5.33.0", | ||||
|     "@ionic-native/diagnostic": "5.33.0", | ||||
|     "@ionic-native/file": "5.33.0", | ||||
|     "@ionic-native/file-opener": "5.33.0", | ||||
|     "@ionic-native/file-transfer": "5.33.0", | ||||
|     "@ionic-native/geolocation": "5.33.0", | ||||
|     "@ionic-native/http": "5.33.0", | ||||
|     "@ionic-native/in-app-browser": "5.33.0", | ||||
|     "@ionic-native/ionic-webview": "5.33.0", | ||||
|     "@ionic-native/keyboard": "5.33.0", | ||||
|     "@ionic-native/local-notifications": "5.33.0", | ||||
|     "@ionic-native/media": "5.33.0", | ||||
|     "@ionic-native/media-capture": "5.33.0", | ||||
|     "@ionic-native/network": "5.33.0", | ||||
|     "@ionic-native/push": "5.33.0", | ||||
|     "@ionic-native/qr-scanner": "5.33.0", | ||||
|     "@ionic-native/splash-screen": "5.33.0", | ||||
|     "@ionic-native/sqlite": "5.33.0", | ||||
|     "@ionic-native/status-bar": "5.33.0", | ||||
|     "@ionic-native/web-intent": "5.33.0", | ||||
|     "@ionic-native/zip": "5.33.0", | ||||
|     "@ionic/angular": "5.6.6", | ||||
|     "@ngx-translate/core": "13.0.0", | ||||
|     "@ngx-translate/http-loader": "6.0.0", | ||||
|     "@types/chart.js": "2.9.31", | ||||
|     "@types/cordova": "0.0.34", | ||||
|     "@types/cordova-plugin-file-transfer": "^1.6.2", | ||||
|     "@types/dom-mediacapture-record": "^1.0.7", | ||||
|     "chart.js": "^2.9.4", | ||||
|     "com-darryncampbell-cordova-plugin-intent": "^1.3.0", | ||||
|     "cordova": "^10.0.0", | ||||
|     "cordova-android": "^9.1.0", | ||||
|     "cordova-android-support-gradle-release": "^3.0.1", | ||||
|     "cordova-clipboard": "^1.3.0", | ||||
|     "cordova-ios": "^6.2.0", | ||||
|     "cordova-plugin-add-swift-support": "^2.0.2", | ||||
|     "cordova-plugin-advanced-http": "^3.1.0", | ||||
|     "cordova-plugin-badge": "^0.8.8", | ||||
|     "cordova-plugin-camera": "^5.0.1", | ||||
|     "cordova-plugin-chooser": "^1.3.2", | ||||
|     "cordova-plugin-customurlscheme": "^5.0.2", | ||||
|     "cordova-plugin-device": "^2.0.3", | ||||
|     "cordova-plugin-file": "^6.0.2", | ||||
|     "cordova-plugin-file-opener2": "^3.0.5", | ||||
|     "@types/cordova-plugin-file-transfer": "1.6.2", | ||||
|     "@types/dom-mediacapture-record": "1.0.7", | ||||
|     "chart.js": "2.9.4", | ||||
|     "com-darryncampbell-cordova-plugin-intent": "1.3.0", | ||||
|     "cordova": "10.0.0", | ||||
|     "cordova-android": "9.1.0", | ||||
|     "cordova-android-support-gradle-release": "3.0.1", | ||||
|     "cordova-clipboard": "1.3.0", | ||||
|     "cordova-ios": "6.2.0", | ||||
|     "cordova-plugin-add-swift-support": "2.0.2", | ||||
|     "cordova-plugin-advanced-http": "3.1.0", | ||||
|     "cordova-plugin-badge": "0.8.8", | ||||
|     "cordova-plugin-camera": "5.0.1", | ||||
|     "cordova-plugin-chooser": "1.3.2", | ||||
|     "cordova-plugin-customurlscheme": "5.0.2", | ||||
|     "cordova-plugin-device": "2.0.3", | ||||
|     "cordova-plugin-file": "6.0.2", | ||||
|     "cordova-plugin-file-opener2": "3.0.5", | ||||
|     "cordova-plugin-file-transfer": "git+https://github.com/moodlemobile/cordova-plugin-file-transfer.git", | ||||
|     "cordova-plugin-geolocation": "^4.1.0", | ||||
|     "cordova-plugin-globalization": "^1.11.0", | ||||
|     "cordova-plugin-inappbrowser": "^5.0.0", | ||||
|     "cordova-plugin-ionic-keyboard": "^2.2.0", | ||||
|     "cordova-plugin-ionic-webview": "^5.0.0", | ||||
|     "cordova-plugin-geolocation": "4.1.0", | ||||
|     "cordova-plugin-globalization": "1.11.0", | ||||
|     "cordova-plugin-inappbrowser": "5.0.0", | ||||
|     "cordova-plugin-ionic-keyboard": "2.2.0", | ||||
|     "cordova-plugin-ionic-webview": "5.0.0", | ||||
|     "cordova-plugin-local-notification": "git+https://github.com/moodlemobile/cordova-plugin-local-notification.git#moodle", | ||||
|     "cordova-plugin-media": "^5.0.3", | ||||
|     "cordova-plugin-media-capture": "^3.0.3", | ||||
|     "cordova-plugin-network-information": "^2.0.2", | ||||
|     "cordova-plugin-media": "5.0.3", | ||||
|     "cordova-plugin-media-capture": "3.0.3", | ||||
|     "cordova-plugin-network-information": "2.0.2", | ||||
|     "cordova-plugin-qrscanner": "git+https://github.com/moodlemobile/cordova-plugin-qrscanner.git#dist", | ||||
|     "cordova-plugin-screen-orientation": "^3.0.2", | ||||
|     "cordova-plugin-splashscreen": "^6.0.0", | ||||
|     "cordova-plugin-statusbar": "^2.4.3", | ||||
|     "cordova-plugin-whitelist": "^1.3.4", | ||||
|     "cordova-plugin-screen-orientation": "3.0.2", | ||||
|     "cordova-plugin-splashscreen": "6.0.0", | ||||
|     "cordova-plugin-statusbar": "2.4.3", | ||||
|     "cordova-plugin-whitelist": "1.3.4", | ||||
|     "cordova-plugin-wkuserscript": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git", | ||||
|     "cordova-plugin-wkwebview-cookies": "git+https://github.com/moodlemobile/cordova-plugin-wkwebview-cookies.git", | ||||
|     "cordova-plugin-zip": "^3.1.0", | ||||
|     "cordova-sqlite-storage": "^6.0.0", | ||||
|     "cordova-support-google-services": "^1.2.1", | ||||
|     "cordova.plugins.diagnostic": "^5.0.2", | ||||
|     "core-js": "^3.9.1", | ||||
|     "es6-promise-plugin": "^4.2.2", | ||||
|     "jszip": "^3.5.0", | ||||
|     "cordova-plugin-zip": "3.1.0", | ||||
|     "cordova-sqlite-storage": "6.0.0", | ||||
|     "cordova-support-google-services": "1.3.2", | ||||
|     "cordova.plugins.diagnostic": "5.0.2", | ||||
|     "core-js": "3.9.1", | ||||
|     "es6-promise-plugin": "4.2.2", | ||||
|     "jszip": "3.5.0", | ||||
|     "mathjax": "2.7.7", | ||||
|     "moment": "^2.29.0", | ||||
|     "nl.kingsquare.cordova.background-audio": "^1.0.1", | ||||
|     "phonegap-plugin-multidex": "^1.0.0", | ||||
|     "moment": "2.29.0", | ||||
|     "nl.kingsquare.cordova.background-audio": "1.0.1", | ||||
|     "phonegap-plugin-multidex": "1.0.0", | ||||
|     "phonegap-plugin-push": "git+https://github.com/moodlemobile/phonegap-plugin-push.git#moodle-v3", | ||||
|     "rxjs": "~6.5.5", | ||||
|     "ts-md5": "^1.2.7", | ||||
|     "tslib": "^2.0.0", | ||||
|     "zone.js": "~0.10.3" | ||||
|     "rxjs": "6.5.5", | ||||
|     "ts-md5": "1.2.7", | ||||
|     "tslib": "2.0.1", | ||||
|     "zone.js": "0.10.3" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@angular-devkit/architect": "^0.1101.2", | ||||
|     "@angular-devkit/build-angular": "~0.1000.0", | ||||
|     "@angular-eslint/builder": "^4.2.0", | ||||
|     "@angular-eslint/eslint-plugin": "^4.2.0", | ||||
|     "@angular-eslint/eslint-plugin-template": "^4.2.0", | ||||
|     "@angular-eslint/schematics": "^4.2.0", | ||||
|     "@angular-eslint/template-parser": "^4.2.0", | ||||
|     "@angular/cli": "~10.0.5", | ||||
|     "@angular/compiler": "~10.0.0", | ||||
|     "@angular/compiler-cli": "~10.0.0", | ||||
|     "@angular/language-service": "~10.0.0", | ||||
|     "@ionic/angular-toolkit": "^2.3.0", | ||||
|     "@ionic/cli": "^6.14.1", | ||||
|     "@types/faker": "^5.1.3", | ||||
|     "@types/node": "^12.12.64", | ||||
|     "@types/resize-observer-browser": "^0.1.5", | ||||
|     "@types/webpack-env": "^1.16.0", | ||||
|     "@typescript-eslint/eslint-plugin": "^4.22.0", | ||||
|     "@typescript-eslint/parser": "^4.22.0", | ||||
|     "check-es-compat": "^1.1.1", | ||||
|     "@angular-devkit/architect": "0.1101.2", | ||||
|     "@angular-devkit/build-angular": "0.1000.8", | ||||
|     "@angular-eslint/builder": "4.2.0", | ||||
|     "@angular-eslint/eslint-plugin": "4.2.0", | ||||
|     "@angular-eslint/eslint-plugin-template": "4.2.0", | ||||
|     "@angular-eslint/schematics": "4.2.0", | ||||
|     "@angular-eslint/template-parser": "4.2.0", | ||||
|     "@angular/cli": "10.0.8", | ||||
|     "@angular/compiler": "10.0.14", | ||||
|     "@angular/compiler-cli": "10.0.14", | ||||
|     "@angular/language-service": "10.0.14", | ||||
|     "@ionic/angular-toolkit": "2.3.3", | ||||
|     "@ionic/cli": "6.14.1", | ||||
|     "@types/faker": "5.1.3", | ||||
|     "@types/node": "12.12.64", | ||||
|     "@types/resize-observer-browser": "0.1.5", | ||||
|     "@types/webpack-env": "1.16.0", | ||||
|     "@typescript-eslint/eslint-plugin": "4.22.0", | ||||
|     "@typescript-eslint/parser": "4.22.0", | ||||
|     "check-es-compat": "1.1.1", | ||||
|     "cordova-plugin-prevent-override": "git+https://github.com/moodlemobile/cordova-plugin-prevent-override.git", | ||||
|     "eslint": "^7.25.0", | ||||
|     "eslint-config-prettier": "^8.3.0", | ||||
|     "eslint-plugin-header": "^3.1.1", | ||||
|     "eslint-plugin-import": "^2.22.1", | ||||
|     "eslint-plugin-jest": "^24.3.6", | ||||
|     "eslint-plugin-jsdoc": "^32.3.3", | ||||
|     "eslint-plugin-prefer-arrow": "^1.2.3", | ||||
|     "eslint-plugin-promise": "^5.1.0", | ||||
|     "faker": "^5.1.0", | ||||
|     "fs-extra": "^9.1.0", | ||||
|     "eslint": "7.25.0", | ||||
|     "eslint-config-prettier": "8.3.0", | ||||
|     "eslint-plugin-header": "3.1.1", | ||||
|     "eslint-plugin-import": "2.22.1", | ||||
|     "eslint-plugin-jest": "24.3.6", | ||||
|     "eslint-plugin-jsdoc": "32.3.3", | ||||
|     "eslint-plugin-prefer-arrow": "1.2.3", | ||||
|     "eslint-plugin-promise": "5.1.0", | ||||
|     "faker": "5.1.0", | ||||
|     "fs-extra": "9.1.0", | ||||
|     "gulp": "4.0.2", | ||||
|     "gulp-clip-empty-files": "^0.1.2", | ||||
|     "gulp-concat": "^2.6.1", | ||||
|     "gulp-flatten": "^0.4.0", | ||||
|     "gulp-htmlmin": "^5.0.1", | ||||
|     "gulp-rename": "^2.0.0", | ||||
|     "gulp-slash": "^1.1.3", | ||||
|     "jest": "^26.5.0", | ||||
|     "jest-preset-angular": "^8.3.1", | ||||
|     "jsonc-parser": "^2.3.1", | ||||
|     "ts-jest": "^26.4.1", | ||||
|     "ts-node": "~8.3.0", | ||||
|     "typescript": "^3.9.9" | ||||
|     "gulp-clip-empty-files": "0.1.2", | ||||
|     "gulp-concat": "2.6.1", | ||||
|     "gulp-flatten": "0.4.0", | ||||
|     "gulp-htmlmin": "5.0.1", | ||||
|     "gulp-rename": "2.0.0", | ||||
|     "gulp-slash": "1.1.3", | ||||
|     "jest": "26.5.2", | ||||
|     "jest-preset-angular": "8.3.1", | ||||
|     "jsonc-parser": "2.3.1", | ||||
|     "ts-jest": "26.4.1", | ||||
|     "ts-node": "8.3.0", | ||||
|     "typescript": "3.9.9" | ||||
|   }, | ||||
|   "engines": { | ||||
|     "node": ">=12.x" | ||||
| @ -236,6 +236,6 @@ | ||||
|     } | ||||
|   }, | ||||
|   "optionalDependencies": { | ||||
|     "keytar": "^7.2.0" | ||||
|     "keytar": "7.2.0" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1993,6 +1993,7 @@ | ||||
|   "core.percentagenumber": "local_moodlemobileapp", | ||||
|   "core.phone": "moodle", | ||||
|   "core.pictureof": "moodle", | ||||
|   "core.play": "local_moodlemobileapp", | ||||
|   "core.previous": "moodle", | ||||
|   "core.proceed": "moodle", | ||||
|   "core.pulltorefresh": "local_moodlemobileapp", | ||||
|  | ||||
| @ -40,6 +40,8 @@ export class AddonModFolderIndexPage extends CoreCourseModuleMainActivityPage<Ad | ||||
|         super.ngOnInit(); | ||||
|         this.folderInstance = CoreNavigator.getRouteParam<AddonModFolderFolder>('folderInstance'); | ||||
|         this.subfolder = CoreNavigator.getRouteParam<AddonModFolderFolderFormattedData>('subfolder'); | ||||
| 
 | ||||
|         this.title = this.subfolder?.filename || this.module.name; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -259,6 +259,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges | ||||
|                 forum: this.forum, | ||||
|             }, | ||||
|             backdropDismiss: false, | ||||
|             cssClass: 'core-modal-fullscreen', | ||||
|         }); | ||||
| 
 | ||||
|         if (!modalData) { | ||||
|  | ||||
| @ -47,11 +47,18 @@ | ||||
| 
 | ||||
|     <ng-container *ngIf="mode == 'external'"> | ||||
|         <ion-button expand="block" class="ion-margin" (click)="open(openFileAction.OPEN)"> | ||||
|             <ion-icon name="far-file" slot="start" aria-hidden="true"></ion-icon> | ||||
|             {{ 'addon.mod_resource.openthefile' | translate }} | ||||
|             <ng-container *ngIf="isStreamedFile"> | ||||
|                 <ion-icon name="fas-play" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 {{ 'core.play' | translate }} | ||||
|             </ng-container> | ||||
|             <ng-container *ngIf="!isStreamedFile"> | ||||
|                 <ion-icon name="far-file" slot="start" aria-hidden="true"></ion-icon> | ||||
|                 {{ 'addon.mod_resource.openthefile' | translate }} | ||||
|             </ng-container> | ||||
|         </ion-button> | ||||
| 
 | ||||
|         <ion-button *ngIf="isIOS" expand="block" class="ion-margin" (click)="open(openFileAction.OPEN_WITH)"> | ||||
|         <ion-button *ngIf="isIOS && (!shouldOpenInBrowser || !isOnline)" expand="block" class="ion-margin" | ||||
|             (click)="open(openFileAction.OPEN_WITH)"> | ||||
|             <ion-icon name="far-share-square" slot="start" aria-hidden="true"></ion-icon> | ||||
|             {{ 'core.openwith' | translate }} | ||||
|         </ion-button> | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, OnInit, Optional } from '@angular/core'; | ||||
| import { Component, OnDestroy, OnInit, Optional } from '@angular/core'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { | ||||
|     CoreCourseModuleMainResourceComponent, | ||||
| @ -21,10 +21,13 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents | ||||
| import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course'; | ||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreFileHelper } from '@services/file-helper'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUtils, OpenFileAction } from '@services/utils/utils'; | ||||
| import { Translate } from '@singletons'; | ||||
| import { Network, NgZone, Translate } from '@singletons'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| import { | ||||
|     AddonModResource, | ||||
|     AddonModResourceCustomData, | ||||
| @ -40,7 +43,7 @@ import { AddonModResourceHelper } from '../../services/resource-helper'; | ||||
|     selector: 'addon-mod-resource-index', | ||||
|     templateUrl: 'addon-mod-resource-index.html', | ||||
| }) | ||||
| export class AddonModResourceIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit { | ||||
| export class AddonModResourceIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     component = AddonModResourceProvider.COMPONENT; | ||||
| 
 | ||||
| @ -52,19 +55,35 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | ||||
|     warning = ''; | ||||
|     isIOS = false; | ||||
|     openFileAction = OpenFileAction; | ||||
|     isOnline = false; | ||||
|     isStreamedFile = false; | ||||
|     shouldOpenInBrowser = false; | ||||
| 
 | ||||
|     protected onlineObserver?: Subscription; | ||||
| 
 | ||||
|     constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { | ||||
|         super('AddonModResourceIndexComponent', courseContentsPage); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         super.ngOnInit(); | ||||
| 
 | ||||
|         this.canGetResource = AddonModResource.isGetResourceWSAvailable(); | ||||
|         this.isIOS = CoreApp.isIOS(); | ||||
|         this.isOnline = CoreApp.isOnline(); | ||||
| 
 | ||||
|         if (this.isIOS) { | ||||
|             // Refresh online status when changes.
 | ||||
|             this.onlineObserver = Network.onChange().subscribe(() => { | ||||
|                 // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | ||||
|                 NgZone.run(() => { | ||||
|                     this.isOnline = CoreApp.isOnline(); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         await this.loadContent(); | ||||
|         try { | ||||
| @ -76,19 +95,14 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Perform the invalidate content function. | ||||
|      * | ||||
|      * @return Resolved when done. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected async invalidateContent(): Promise<void> { | ||||
|         return AddonModResource.invalidateContent(this.module.id, this.courseId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Download resource contents. | ||||
|      * | ||||
|      * @param refresh Whether we're refreshing data. | ||||
|      * @return Promise resolved when done. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected async fetchContent(refresh?: boolean): Promise<void> { | ||||
|         // Load module contents if needed. Passing refresh is needed to force reloading contents.
 | ||||
| @ -150,6 +164,14 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | ||||
|             } else { | ||||
|                 this.mode = 'external'; | ||||
|                 this.warning = ''; | ||||
| 
 | ||||
|                 if (this.isIOS) { | ||||
|                     this.shouldOpenInBrowser = CoreFileHelper.shouldOpenInBrowser(this.module.contents[0]); | ||||
|                 } | ||||
| 
 | ||||
|                 const mimetype = await CoreUtils.getMimeTypeFromUrl(CoreFileHelper.getFileUrl(this.module.contents[0])); | ||||
| 
 | ||||
|                 this.isStreamedFile = CoreMimetypeUtils.isStreamedMimetype(mimetype); | ||||
|             } | ||||
|         } finally { | ||||
|             this.fillContextMenu(refresh); | ||||
| @ -179,4 +201,12 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | ||||
|         await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module.url!); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     ngOnDestroy(): void { | ||||
|         super.ngOnDestroy(); | ||||
|         this.onlineObserver?.unsubscribe(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
|             [canTrustDownload]="!alwaysDownload" (action)="download()" size="small"> | ||||
|         </core-download-refresh> | ||||
| 
 | ||||
|         <ion-button fill="clear" *ngIf="isDownloaded && isIOS" (click)="openFile(true)" color="dark" | ||||
|         <ion-button fill="clear" *ngIf="isDownloaded && isIOS" (click)="openFile($event, true)" color="dark" | ||||
|             [title]="openButtonLabel | translate"> | ||||
|             <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon> | ||||
|         </ion-button> | ||||
|  | ||||
| @ -144,10 +144,14 @@ export class CoreFileComponent implements OnInit, OnDestroy { | ||||
|     /** | ||||
|      * Convenience function to open a file, downloading it if needed. | ||||
|      * | ||||
|      * @param ev Click event (if any). | ||||
|      * @param isOpenButton Whether the open button was clicked. | ||||
|      * @return Promise resolved when file is opened. | ||||
|      */ | ||||
|     openFile(isOpenButton = false): Promise<void> { | ||||
|     openFile(ev?: Event, isOpenButton = false): Promise<void> { | ||||
|         ev?.preventDefault(); | ||||
|         ev?.stopPropagation(); | ||||
| 
 | ||||
|         const options: CoreUtilsOpenFileOptions = {}; | ||||
|         if (isOpenButton) { | ||||
|             // Use the non-default method.
 | ||||
|  | ||||
| @ -117,7 +117,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { | ||||
|     // eslint-disable-next-line @angular-eslint/no-conflicting-lifecycle
 | ||||
|     async ngOnChanges(changes: Record<string, SimpleChange>): Promise<void> { | ||||
|         // Only compile if text/javascript has changed or the forceCompile flag has been set to true.
 | ||||
|         if (this.text && (changes.text || changes.javascript || | ||||
|         if (this.text !== undefined && (changes.text || changes.javascript || | ||||
|                 (changes.forceCompile && CoreUtils.isTrueOrOne(this.forceCompile)))) { | ||||
| 
 | ||||
|             // Create a new component and a new module.
 | ||||
|  | ||||
| @ -681,21 +681,29 @@ export class CoreCourseHelperProvider { | ||||
|             throw new CoreError(Translate.instant('core.filenotfound')); | ||||
|         } | ||||
| 
 | ||||
|         if (!CoreFileHelper.isOpenableInApp(module.contents[0])) { | ||||
|         const mainFile = files[0]; | ||||
| 
 | ||||
|         if (!CoreFileHelper.isOpenableInApp(mainFile)) { | ||||
|             await CoreFileHelper.showConfirmOpenUnsupportedFile(); | ||||
|         } | ||||
| 
 | ||||
|         const site = await CoreSites.getSite(siteId); | ||||
| 
 | ||||
|         const mainFile = files[0]; | ||||
| 
 | ||||
|         // Check if the file should be opened in browser.
 | ||||
|         if (CoreFileHelper.shouldOpenInBrowser(mainFile)) { | ||||
|             return this.openModuleFileInBrowser(mainFile.fileurl, site, module, courseId, component, componentId, files); | ||||
|             return this.openModuleFileInBrowser(mainFile.fileurl, site, module, courseId, component, componentId, files, options); | ||||
|         } | ||||
| 
 | ||||
|         // File shouldn't be opened in browser. Download the module if it needs to be downloaded.
 | ||||
|         const result = await this.downloadModuleWithMainFileIfNeeded(module, courseId, component || '', componentId, files, siteId); | ||||
|         const result = await this.downloadModuleWithMainFileIfNeeded( | ||||
|             module, | ||||
|             courseId, | ||||
|             component || '', | ||||
|             componentId, | ||||
|             files, | ||||
|             siteId, | ||||
|             options, | ||||
|         ); | ||||
| 
 | ||||
|         if (CoreUrlUtils.isLocalFileUrl(result.path)) { | ||||
|             return CoreUtils.openFile(result.path, options); | ||||
| @ -740,6 +748,7 @@ export class CoreCourseHelperProvider { | ||||
|      * @param component The component to link the files to. | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param files List of files of the module. If not provided, use module.contents. | ||||
|      * @param options Options to open the file. Only used if not opened in browser. | ||||
|      * @return Resolved on success. | ||||
|      */ | ||||
|     protected async openModuleFileInBrowser( | ||||
| @ -750,6 +759,7 @@ export class CoreCourseHelperProvider { | ||||
|         component?: string, | ||||
|         componentId?: string | number, | ||||
|         files?: CoreCourseModuleContentFile[], | ||||
|         options: CoreUtilsOpenFileOptions = {}, | ||||
|     ): Promise<void> { | ||||
|         if (!CoreApp.isOnline()) { | ||||
|             // Not online, get the offline file. It will fail if not found.
 | ||||
| @ -760,7 +770,7 @@ export class CoreCourseHelperProvider { | ||||
|                 throw new CoreNetworkError(); | ||||
|             } | ||||
| 
 | ||||
|             return CoreUtils.openFile(path); | ||||
|             return CoreUtils.openFile(path, options); | ||||
|         } | ||||
| 
 | ||||
|         // Open in browser.
 | ||||
| @ -791,6 +801,7 @@ export class CoreCourseHelperProvider { | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param files List of files of the module. If not provided, use module.contents. | ||||
|      * @param siteId The site ID. If not defined, current site. | ||||
|      * @param options Options to open the file. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async downloadModuleWithMainFileIfNeeded( | ||||
| @ -800,6 +811,7 @@ export class CoreCourseHelperProvider { | ||||
|         componentId?: string | number, | ||||
|         files?: CoreCourseModuleContentFile[], | ||||
|         siteId?: string, | ||||
|         options: CoreUtilsOpenFileOptions = {}, | ||||
|     ): Promise<{ fixedUrl: string; path: string; status?: string }> { | ||||
| 
 | ||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||
| @ -840,7 +852,17 @@ export class CoreCourseHelperProvider { | ||||
|         } | ||||
| 
 | ||||
|         if (!path) { | ||||
|             path = await this.downloadModuleWithMainFile(module, courseId, fixedUrl, files, status, component, componentId, siteId); | ||||
|             path = await this.downloadModuleWithMainFile( | ||||
|                 module, | ||||
|                 courseId, | ||||
|                 fixedUrl, | ||||
|                 files, | ||||
|                 status, | ||||
|                 component, | ||||
|                 componentId, | ||||
|                 siteId, | ||||
|                 options, | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return { | ||||
| @ -862,6 +884,7 @@ export class CoreCourseHelperProvider { | ||||
|      * @param component The component to link the files to. | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @param siteId The site ID. If not defined, current site. | ||||
|      * @param options Options to open the file. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async downloadModuleWithMainFile( | ||||
| @ -873,6 +896,7 @@ export class CoreCourseHelperProvider { | ||||
|         component?: string, | ||||
|         componentId?: string | number, | ||||
|         siteId?: string, | ||||
|         options: CoreUtilsOpenFileOptions = {}, | ||||
|     ): Promise<string> { | ||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
| @ -885,7 +909,7 @@ export class CoreCourseHelperProvider { | ||||
|             throw new CoreNetworkError(); | ||||
|         } | ||||
| 
 | ||||
|         const shouldDownloadFirst = await CoreFilepool.shouldDownloadFileBeforeOpen(fixedUrl, mainFile.filesize); | ||||
|         const shouldDownloadFirst = await CoreFilepool.shouldDownloadFileBeforeOpen(fixedUrl, mainFile.filesize, options); | ||||
| 
 | ||||
|         if (shouldDownloadFirst) { | ||||
|             // Download and then return the local URL.
 | ||||
|  | ||||
| @ -46,7 +46,7 @@ export class CoreSitePluginsMainMenuHomeHandler extends CoreSitePluginsBaseHandl | ||||
|         return { | ||||
|             title: this.title, | ||||
|             class: this.handlerSchema.displaydata?.class, | ||||
|             page: `siteplugins/${this.plugin.component}/${this.handlerSchema.method}/0`, | ||||
|             page: `siteplugins/homecontent/${this.plugin.component}/${this.handlerSchema.method}`, | ||||
|             pageParams: { | ||||
|                 title: this.title, | ||||
|                 initResult: this.initResult, | ||||
|  | ||||
| @ -28,6 +28,13 @@ const routes: Routes = [ | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| const homeRoutes: Routes = [ | ||||
|     { | ||||
|         path: 'siteplugins/homecontent/:component/:method', | ||||
|         loadChildren: () => import('./pages/plugin-page/plugin-page.module').then( m => m.CoreSitePluginsPluginPageModule), | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| const courseIndexRoutes: Routes = [ | ||||
|     { | ||||
|         path: 'siteplugins/:handlerUniqueName', | ||||
| @ -47,7 +54,7 @@ const moduleRoutes: Routes = [ | ||||
|     imports: [ | ||||
|         CoreMainMenuTabRoutingModule.forChild(moduleRoutes.concat(routes)), | ||||
|         CoreCourseIndexRoutingModule.forChild({ children: courseIndexRoutes }), | ||||
|         CoreMainMenuHomeRoutingModule.forChild({ children: routes }), | ||||
|         CoreMainMenuHomeRoutingModule.forChild({ children: homeRoutes }), | ||||
|         CoreSitePluginsComponentsModule, | ||||
|     ], | ||||
|     providers: [ | ||||
|  | ||||
| @ -225,6 +225,7 @@ | ||||
|     "percentagenumber": "{{$a}}%", | ||||
|     "phone": "Phone", | ||||
|     "pictureof": "Picture of {{$a}}", | ||||
|     "play": "Play", | ||||
|     "previous": "Previous", | ||||
|     "proceed": "Proceed", | ||||
|     "pulltorefresh": "Pull to refresh", | ||||
|  | ||||
| @ -73,7 +73,17 @@ export class CoreFileHelperProvider { | ||||
|             await this.showConfirmOpenUnsupportedFile(); | ||||
|         } | ||||
| 
 | ||||
|         let url = await this.downloadFileIfNeeded(file, fileUrl, component, componentId, timemodified, state, onProgress, siteId); | ||||
|         let url = await this.downloadFileIfNeeded( | ||||
|             file, | ||||
|             fileUrl, | ||||
|             component, | ||||
|             componentId, | ||||
|             timemodified, | ||||
|             state, | ||||
|             onProgress, | ||||
|             siteId, | ||||
|             options, | ||||
|         ); | ||||
| 
 | ||||
|         if (!url) { | ||||
|             return; | ||||
| @ -127,6 +137,7 @@ export class CoreFileHelperProvider { | ||||
|      * @param state The file's state. If not provided, it will be calculated. | ||||
|      * @param onProgress Function to call on progress. | ||||
|      * @param siteId The site ID. If not defined, current site. | ||||
|      * @param options Options to open the file. | ||||
|      * @return Resolved with the URL to use on success. | ||||
|      */ | ||||
|     protected async downloadFileIfNeeded( | ||||
| @ -138,6 +149,7 @@ export class CoreFileHelperProvider { | ||||
|         state?: string, | ||||
|         onProgress?: CoreFileHelperOnProgress, | ||||
|         siteId?: string, | ||||
|         options: CoreUtilsOpenFileOptions = {}, | ||||
|     ): Promise<string> { | ||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||
| 
 | ||||
| @ -172,7 +184,7 @@ export class CoreFileHelperProvider { | ||||
|                 onProgress({ calculating: true }); | ||||
|             } | ||||
| 
 | ||||
|             const shouldDownloadFirst = await CoreFilepool.shouldDownloadFileBeforeOpen(fixedUrl, file.filesize || 0); | ||||
|             const shouldDownloadFirst = await CoreFilepool.shouldDownloadFileBeforeOpen(fixedUrl, file.filesize || 0, options); | ||||
|             if (shouldDownloadFirst) { | ||||
|                 // Download the file first.
 | ||||
|                 if (state == CoreConstants.DOWNLOADING) { | ||||
|  | ||||
| @ -26,7 +26,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreTimeUtils } from '@services/utils/time'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| import { CoreUtils, PromiseDefer } from '@services/utils/utils'; | ||||
| import { CoreUtils, CoreUtilsOpenFileOptions, PromiseDefer } from '@services/utils/utils'; | ||||
| import { SQLiteDB } from '@classes/sqlitedb'; | ||||
| import { CoreError } from '@classes/errors/error'; | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| @ -2781,7 +2781,7 @@ export class CoreFilepoolProvider { | ||||
| 
 | ||||
|         const mimetype = await CoreUtils.getMimeTypeFromUrl(url); | ||||
|         // If the file is streaming (audio or video) we reject.
 | ||||
|         if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { | ||||
|         if (CoreMimetypeUtils.isStreamedMimetype(mimetype)) { | ||||
|             throw new CoreError('File is audio or video.'); | ||||
|         } | ||||
|     } | ||||
| @ -2791,6 +2791,7 @@ export class CoreFilepoolProvider { | ||||
|      * | ||||
|      * @param url File online URL. | ||||
|      * @param size File size. | ||||
|      * @param options Options. | ||||
|      * @return Promise resolved with boolean: whether file should be downloaded before opening it. | ||||
|      * @description | ||||
|      * Convenience function to check if a file should be downloaded before opening it. | ||||
| @ -2800,16 +2801,21 @@ export class CoreFilepoolProvider { | ||||
|      *     - The file cannot be streamed. | ||||
|      * If the file is big and can be streamed, the promise returned by this function will be rejected. | ||||
|      */ | ||||
|     async shouldDownloadFileBeforeOpen(url: string, size: number): Promise<boolean> { | ||||
|     async shouldDownloadFileBeforeOpen(url: string, size: number, options: CoreUtilsOpenFileOptions = {}): Promise<boolean> { | ||||
|         if (size >= 0 && size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD) { | ||||
|             // The file is small, download it.
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         if (CoreUtils.shouldOpenWithDialog(options)) { | ||||
|             // Open with dialog needs a local file.
 | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         const mimetype = await CoreUtils.getMimeTypeFromUrl(url); | ||||
| 
 | ||||
|         // If the file is streaming (audio or video), return false.
 | ||||
|         return mimetype.indexOf('video') == -1 && mimetype.indexOf('audio') == -1; | ||||
|         return !CoreMimetypeUtils.isStreamedMimetype(mimetype); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -562,6 +562,16 @@ export class CoreMimetypeUtilsProvider { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Check if a mimetype belongs to a file that can be streamed (audio, video). | ||||
|      * | ||||
|      * @param mimetype Mimetype. | ||||
|      * @return Boolean. | ||||
|      */ | ||||
|     isStreamedMimetype(mimetype: string): boolean { | ||||
|         return mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Remove the extension from a path (if any). | ||||
|      * | ||||
|  | ||||
| @ -933,8 +933,7 @@ export class CoreUtilsProvider { | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const openFileAction = options.iOSOpenFileAction ?? CoreConstants.CONFIG.iOSDefaultOpenFileAction; | ||||
|             if (CoreApp.isIOS() && openFileAction == OpenFileAction.OPEN_WITH) { | ||||
|             if (this.shouldOpenWithDialog(options)) { | ||||
|                 await FileOpener.showOpenWithDialog(path, mimetype || ''); | ||||
|             } else { | ||||
|                 await FileOpener.open(path, mimetype || ''); | ||||
| @ -1652,6 +1651,18 @@ export class CoreUtilsProvider { | ||||
|         return this.wait(0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Given some options, check if a file should be opened with showOpenWithDialog. | ||||
|      * | ||||
|      * @param options Options. | ||||
|      * @return Boolean. | ||||
|      */ | ||||
|     shouldOpenWithDialog(options: CoreUtilsOpenFileOptions = {}): boolean { | ||||
|         const openFileAction = options.iOSOpenFileAction ?? CoreConstants.CONFIG.iOSDefaultOpenFileAction; | ||||
| 
 | ||||
|         return CoreApp.isIOS() && openFileAction == OpenFileAction.OPEN_WITH; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const CoreUtils = makeSingleton(CoreUtilsProvider); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user