forked from EVOgeek/Vmeda.Online
		
	
						commit
						8728954fba
					
				| @ -1984,6 +1984,7 @@ | |||||||
|   "core.openmodinbrowser": "local_moodlemobileapp", |   "core.openmodinbrowser": "local_moodlemobileapp", | ||||||
|   "core.opensecurityquestion": "local_moodlemobileapp", |   "core.opensecurityquestion": "local_moodlemobileapp", | ||||||
|   "core.opensettings": "local_moodlemobileapp", |   "core.opensettings": "local_moodlemobileapp", | ||||||
|  |   "core.openwith": "local_moodlemobileapp", | ||||||
|   "core.othergroups": "group", |   "core.othergroups": "group", | ||||||
|   "core.pagea": "moodle", |   "core.pagea": "moodle", | ||||||
|   "core.parentlanguage": "langconfig", |   "core.parentlanguage": "langconfig", | ||||||
|  | |||||||
| @ -45,11 +45,16 @@ | |||||||
|         <core-format-text [text]="contentText" [filter]="false"></core-format-text> |         <core-format-text [text]="contentText" [filter]="false"></core-format-text> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <div class="ion-padding" *ngIf="mode == 'external'"> |     <ng-container *ngIf="mode == 'external'"> | ||||||
|         <ion-button expand="block" (click)="open()"> |         <ion-button expand="block" class="ion-margin" (click)="open(openFileAction.OPEN)"> | ||||||
|             <ion-icon name="far-file" slot="start" aria-hidden="true"></ion-icon> |             <ion-icon name="far-file" slot="start" aria-hidden="true"></ion-icon> | ||||||
|             {{ 'addon.mod_resource.openthefile' | translate }} |             {{ 'addon.mod_resource.openthefile' | translate }} | ||||||
|         </ion-button> |         </ion-button> | ||||||
|     </div> | 
 | ||||||
|  |         <ion-button *ngIf="isIOS" 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> | ||||||
|  |     </ng-container> | ||||||
| 
 | 
 | ||||||
| </core-loading> | </core-loading> | ||||||
|  | |||||||
| @ -20,9 +20,10 @@ import { | |||||||
| import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; | import { CoreCourseContentsPage } from '@features/course/pages/contents/contents'; | ||||||
| import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course'; | import { CoreCourse, CoreCourseWSModule } from '@features/course/services/course'; | ||||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
|  | import { CoreApp } from '@services/app'; | ||||||
| import { CoreSites } from '@services/sites'; | import { CoreSites } from '@services/sites'; | ||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils, OpenFileAction } from '@services/utils/utils'; | ||||||
| import { Translate } from '@singletons'; | import { Translate } from '@singletons'; | ||||||
| import { | import { | ||||||
|     AddonModResource, |     AddonModResource, | ||||||
| @ -49,6 +50,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | |||||||
|     contentText = ''; |     contentText = ''; | ||||||
|     displayDescription = true; |     displayDescription = true; | ||||||
|     warning = ''; |     warning = ''; | ||||||
|  |     isIOS = false; | ||||||
|  |     openFileAction = OpenFileAction; | ||||||
| 
 | 
 | ||||||
|     constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { |     constructor(@Optional() courseContentsPage?: CoreCourseContentsPage) { | ||||||
|         super('AddonModResourceIndexComponent', courseContentsPage); |         super('AddonModResourceIndexComponent', courseContentsPage); | ||||||
| @ -61,6 +64,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | |||||||
|         super.ngOnInit(); |         super.ngOnInit(); | ||||||
| 
 | 
 | ||||||
|         this.canGetResource = AddonModResource.isGetResourceWSAvailable(); |         this.canGetResource = AddonModResource.isGetResourceWSAvailable(); | ||||||
|  |         this.isIOS = CoreApp.isIOS(); | ||||||
| 
 | 
 | ||||||
|         await this.loadContent(); |         await this.loadContent(); | ||||||
|         try { |         try { | ||||||
| @ -155,9 +159,10 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | |||||||
|     /** |     /** | ||||||
|      * Opens a file. |      * Opens a file. | ||||||
|      * |      * | ||||||
|  |      * @param iOSOpenFileAction Action to do in iOS. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     async open(): Promise<void> { |     async open(iOSOpenFileAction?: OpenFileAction): Promise<void> { | ||||||
|         let downloadable = await CoreCourseModulePrefetchDelegate.isModuleDownloadable(this.module, this.courseId); |         let downloadable = await CoreCourseModulePrefetchDelegate.isModuleDownloadable(this.module, this.courseId); | ||||||
| 
 | 
 | ||||||
|         if (downloadable) { |         if (downloadable) { | ||||||
| @ -166,7 +171,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource | |||||||
|             downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module); |             downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module); | ||||||
| 
 | 
 | ||||||
|             if (downloadable) { |             if (downloadable) { | ||||||
|                 return AddonModResourceHelper.openModuleFile(this.module, this.courseId); |                 return AddonModResourceHelper.openModuleFile(this.module, this.courseId, { iOSOpenFileAction }); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,6 @@ | |||||||
|     "errorwhileloadingthecontent": "Error while loading the content.", |     "errorwhileloadingthecontent": "Error while loading the content.", | ||||||
|     "modifieddate": "Modified {{$a}}", |     "modifieddate": "Modified {{$a}}", | ||||||
|     "modulenameplural": "Files", |     "modulenameplural": "Files", | ||||||
|     "openthefile": "Open the file", |     "openthefile": "Open", | ||||||
|     "uploadeddate": "Uploaded {{$a}}" |     "uploadeddate": "Uploaded {{$a}}" | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from | |||||||
| import { CoreCourseModule } from '@features/course/services/course-helper'; | import { CoreCourseModule } from '@features/course/services/course-helper'; | ||||||
| import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; | ||||||
| import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; | ||||||
|  | import { CoreFileHelper } from '@services/file-helper'; | ||||||
| import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; | import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; | ||||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| @ -71,6 +72,7 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan | |||||||
|             handlerData.buttons![0].hidden = status !== CoreConstants.DOWNLOADED || |             handlerData.buttons![0].hidden = status !== CoreConstants.DOWNLOADED || | ||||||
|                 AddonModResourceHelper.isDisplayedInIframe(module); |                 AddonModResourceHelper.isDisplayedInIframe(module); | ||||||
|         }; |         }; | ||||||
|  |         const openWithPicker = CoreFileHelper.defaultIsOpenWithPicker(); | ||||||
| 
 | 
 | ||||||
|         const handlerData: CoreCourseModuleHandlerData = { |         const handlerData: CoreCourseModuleHandlerData = { | ||||||
|             icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), |             icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined), | ||||||
| @ -88,8 +90,8 @@ export class AddonModResourceModuleHandlerService implements CoreCourseModuleHan | |||||||
|             updateStatus: updateStatus.bind(this), |             updateStatus: updateStatus.bind(this), | ||||||
|             buttons: [{ |             buttons: [{ | ||||||
|                 hidden: true, |                 hidden: true, | ||||||
|                 icon: 'document', |                 icon: openWithPicker ? 'fas-share-square' : 'fas-file', | ||||||
|                 label: 'addon.mod_resource.openthefile', |                 label: module.name + ': ' + Translate.instant(openWithPicker ? 'core.openwith' : 'addon.mod_resource.openthefile'), | ||||||
|                 action: async (event: Event, module: CoreCourseModule, courseId: number): Promise<void> => { |                 action: async (event: Event, module: CoreCourseModule, courseId: number): Promise<void> => { | ||||||
|                     const hide = await this.hideOpenButton(module, courseId); |                     const hide = await this.hideOpenButton(module, courseId); | ||||||
|                     if (!hide) { |                     if (!hide) { | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ import { CoreSites } from '@services/sites'; | |||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
|  | import { CoreUtilsOpenFileOptions } from '@services/utils/utils'; | ||||||
| import { makeSingleton } from '@singletons'; | import { makeSingleton } from '@singletons'; | ||||||
| import { AddonModResource, AddonModResourceProvider } from './resource'; | import { AddonModResource, AddonModResourceProvider } from './resource'; | ||||||
| 
 | 
 | ||||||
| @ -171,9 +172,10 @@ export class AddonModResourceHelperProvider { | |||||||
|      * |      * | ||||||
|      * @param module Module where to get the contents. |      * @param module Module where to get the contents. | ||||||
|      * @param courseId Course Id, used for completion purposes. |      * @param courseId Course Id, used for completion purposes. | ||||||
|  |      * @param options Options to open the file. | ||||||
|      * @return Resolved when done. |      * @return Resolved when done. | ||||||
|      */ |      */ | ||||||
|     async openModuleFile(module: CoreCourseWSModule, courseId: number): Promise<void> { |     async openModuleFile(module: CoreCourseWSModule, courseId: number, options: CoreUtilsOpenFileOptions = {}): Promise<void> { | ||||||
|         const modal = await CoreDomUtils.showModalLoading(); |         const modal = await CoreDomUtils.showModalLoading(); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
| @ -184,6 +186,8 @@ export class AddonModResourceHelperProvider { | |||||||
|                 AddonModResourceProvider.COMPONENT, |                 AddonModResourceProvider.COMPONENT, | ||||||
|                 module.id, |                 module.id, | ||||||
|                 module.contents, |                 module.contents, | ||||||
|  |                 undefined, | ||||||
|  |                 options, | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             try { |             try { | ||||||
|  | |||||||
| @ -7,11 +7,16 @@ | |||||||
|         <p *ngIf="fileSizeReadable">{{ fileSizeReadable }}</p> |         <p *ngIf="fileSizeReadable">{{ fileSizeReadable }}</p> | ||||||
|         <p *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</p> |         <p *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</p> | ||||||
|     </ion-label> |     </ion-label> | ||||||
|     <div slot="end"> |     <div slot="end" class="flex-row"> | ||||||
|         <core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" |         <core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" | ||||||
|             [canTrustDownload]="!alwaysDownload" (action)="download()" size="small"> |             [canTrustDownload]="!alwaysDownload" (action)="download()" size="small"> | ||||||
|         </core-download-refresh> |         </core-download-refresh> | ||||||
| 
 | 
 | ||||||
|  |         <ion-button fill="clear" *ngIf="isDownloaded && isIOS" (click)="openFile(true)" color="dark" | ||||||
|  |             [title]="openButtonLabel | translate"> | ||||||
|  |             <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon> | ||||||
|  |         </ion-button> | ||||||
|  | 
 | ||||||
|         <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)" |         <ion-button fill="clear" *ngIf="!isDownloading && canDelete" (click)="delete($event)" | ||||||
|             [attr.aria-label]="'core.delete' | translate" color="danger" size="small"> |             [attr.aria-label]="'core.delete' | translate" color="danger" size="small"> | ||||||
|             <ion-icon slot="icon-only" name="fas-trash" aria-hidden="true"></ion-icon> |             <ion-icon slot="icon-only" name="fas-trash" aria-hidden="true"></ion-icon> | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ import { CoreSites } from '@services/sites'; | |||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||||
| import { CoreUrlUtils } from '@services/utils/url'; | import { CoreUrlUtils } from '@services/utils/url'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; | ||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { CoreConstants } from '@/core/constants'; | import { CoreConstants } from '@/core/constants'; | ||||||
| import { CoreEventObserver, CoreEvents } from '@singletons/events'; | import { CoreEventObserver, CoreEvents } from '@singletons/events'; | ||||||
| @ -48,16 +48,21 @@ export class CoreFileComponent implements OnInit, OnDestroy { | |||||||
|     @Output() onDelete: EventEmitter<void>; // Will notify when the delete button is clicked.
 |     @Output() onDelete: EventEmitter<void>; // Will notify when the delete button is clicked.
 | ||||||
| 
 | 
 | ||||||
|     isDownloading?: boolean; |     isDownloading?: boolean; | ||||||
|  |     isDownloaded?: boolean; | ||||||
|     fileIcon?: string; |     fileIcon?: string; | ||||||
|     fileName!: string; |     fileName!: string; | ||||||
|     fileSizeReadable?: string; |     fileSizeReadable?: string; | ||||||
|     state?: string; |     state?: string; | ||||||
|     timemodified!: number; |     timemodified!: number; | ||||||
|  |     isIOS = false; | ||||||
|  |     openButtonIcon = ''; | ||||||
|  |     openButtonLabel = ''; | ||||||
| 
 | 
 | ||||||
|     protected fileUrl!: string; |     protected fileUrl!: string; | ||||||
|     protected siteId?: string; |     protected siteId?: string; | ||||||
|     protected fileSize?: number; |     protected fileSize?: number; | ||||||
|     protected observer?: CoreEventObserver; |     protected observer?: CoreEventObserver; | ||||||
|  |     protected defaultIsOpenWithPicker = false; | ||||||
| 
 | 
 | ||||||
|     constructor() { |     constructor() { | ||||||
|         this.onDelete = new EventEmitter<void>(); |         this.onDelete = new EventEmitter<void>(); | ||||||
| @ -81,6 +86,11 @@ export class CoreFileComponent implements OnInit, OnDestroy { | |||||||
|         this.fileSize = this.file.filesize; |         this.fileSize = this.file.filesize; | ||||||
|         this.fileName = this.file.filename || ''; |         this.fileName = this.file.filename || ''; | ||||||
| 
 | 
 | ||||||
|  |         this.isIOS = CoreApp.isIOS(); | ||||||
|  |         this.defaultIsOpenWithPicker = CoreFileHelper.defaultIsOpenWithPicker(); | ||||||
|  |         this.openButtonIcon = this.defaultIsOpenWithPicker ? 'fas-file' : 'fas-share-square'; | ||||||
|  |         this.openButtonLabel = this.defaultIsOpenWithPicker ? 'core.openfile' : 'core.openwith'; | ||||||
|  | 
 | ||||||
|         if (CoreUtils.isTrueOrOne(this.showSize) && this.fileSize && this.fileSize >= 0) { |         if (CoreUtils.isTrueOrOne(this.showSize) && this.fileSize && this.fileSize >= 0) { | ||||||
|             this.fileSizeReadable = CoreTextUtils.bytesToSize(this.fileSize, 2); |             this.fileSizeReadable = CoreTextUtils.bytesToSize(this.fileSize, 2); | ||||||
|         } |         } | ||||||
| @ -128,20 +138,28 @@ export class CoreFileComponent implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         this.state = state; |         this.state = state; | ||||||
|         this.isDownloading = this.canDownload && state === CoreConstants.DOWNLOADING; |         this.isDownloading = this.canDownload && state === CoreConstants.DOWNLOADING; | ||||||
|  |         this.isDownloaded = this.canDownload && CoreFileHelper.isStateDownloaded(state); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Convenience function to open a file, downloading it if needed. |      * Convenience function to open a file, downloading it if needed. | ||||||
|      * |      * | ||||||
|  |      * @param isOpenButton Whether the open button was clicked. | ||||||
|      * @return Promise resolved when file is opened. |      * @return Promise resolved when file is opened. | ||||||
|      */ |      */ | ||||||
|     protected openFile(): Promise<void> { |     openFile(isOpenButton = false): Promise<void> { | ||||||
|  |         const options: CoreUtilsOpenFileOptions = {}; | ||||||
|  |         if (isOpenButton) { | ||||||
|  |             // Use the non-default method.
 | ||||||
|  |             options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         return CoreFileHelper.downloadAndOpenFile(this.file!, this.component, this.componentId, this.state, (event) => { |         return CoreFileHelper.downloadAndOpenFile(this.file!, this.component, this.componentId, this.state, (event) => { | ||||||
|             if (event && 'calculating' in event && event.calculating) { |             if (event && 'calculating' in event && event.calculating) { | ||||||
|                 // The process is calculating some data required for the download, show the spinner.
 |                 // The process is calculating some data required for the download, show the spinner.
 | ||||||
|                 this.isDownloading = true; |                 this.isDownloading = true; | ||||||
|             } |             } | ||||||
|         }).catch((error) => { |         }, undefined, options).catch((error) => { | ||||||
|             CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); |             CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -152,7 +170,7 @@ export class CoreFileComponent implements OnInit, OnDestroy { | |||||||
|      * @param e Click event. |      * @param e Click event. | ||||||
|      * @param openAfterDownload Whether the file should be opened after download. |      * @param openAfterDownload Whether the file should be opened after download. | ||||||
|      */ |      */ | ||||||
|     async download(e?: Event, openAfterDownload: boolean = false): Promise<void> { |     async download(e?: Event, openAfterDownload = false): Promise<void> { | ||||||
|         e && e.preventDefault(); |         e && e.preventDefault(); | ||||||
|         e && e.stopPropagation(); |         e && e.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| <form (ngSubmit)="changeName(newFileName, $event)" #nameForm> | <form (ngSubmit)="changeName(newFileName, $event)" #nameForm> | ||||||
|     <ion-item class="ion-text-wrap item-file" (click)="fileClicked($event)" button> <!-- [class.item-input]="editMode" --> |     <ion-item class="ion-text-wrap item-file" (click)="openFile($event)" button detail="false"> | ||||||
|         <ion-thumbnail slot="start"> |         <ion-thumbnail slot="start"> | ||||||
|             <img [src]="fileIcon" [alt]="fileExtension" role="presentation" /> |             <img [src]="fileIcon" [alt]="fileExtension" role="presentation" /> | ||||||
|         </ion-thumbnail> |         </ion-thumbnail> | ||||||
| @ -17,19 +17,26 @@ | |||||||
|             (click)="$event.stopPropagation()" core-auto-focus [(ngModel)]="newFileName" *ngIf="editMode"> |             (click)="$event.stopPropagation()" core-auto-focus [(ngModel)]="newFileName" *ngIf="editMode"> | ||||||
|         </ion-input> |         </ion-input> | ||||||
| 
 | 
 | ||||||
|         <div class="buttons" slot="end" *ngIf="manage"> |         <div class="buttons" slot="end"> | ||||||
|             <ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)" |             <ion-button fill="clear" *ngIf="isIOS" (click)="openFile($event, true)" | ||||||
|                 [attr.aria-label]="'core.edit' | translate" color="dark"> |                 [attr.aria-label]="openButtonLabel | translate"> | ||||||
|                 <ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon> |                 <ion-icon slot="icon-only" [name]="openButtonIcon" aria-hidden="true"></ion-icon> | ||||||
|             </ion-button> |             </ion-button> | ||||||
| 
 | 
 | ||||||
|             <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit"> |             <ng-container *ngIf="manage"> | ||||||
|                 <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon> |                 <ion-button *ngIf="!editMode" fill="clear" [core-suppress-events] (onClick)="activateEdit($event)" | ||||||
|             </ion-button> |                     [attr.aria-label]="'core.edit' | translate" color="dark"> | ||||||
|  |                     <ion-icon name="fas-pen" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|  |                 </ion-button> | ||||||
| 
 | 
 | ||||||
|             <ion-button fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger"> |                 <ion-button *ngIf="editMode" fill="clear" [attr.aria-label]="'core.save' | translate" color="success" type="submit"> | ||||||
|                 <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> |                     <ion-icon name="fas-check" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|             </ion-button> |                 </ion-button> | ||||||
|  | 
 | ||||||
|  |                 <ion-button fill="clear" (click)="deleteFile($event)" [attr.aria-label]="'core.delete' | translate" color="danger"> | ||||||
|  |                     <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> | ||||||
|  |                 </ion-button> | ||||||
|  |             </ng-container> | ||||||
|         </div> |         </div> | ||||||
|     </ion-item> |     </ion-item> | ||||||
| </form> | </form> | ||||||
|  | |||||||
| @ -23,8 +23,9 @@ import { CoreDomUtils } from '@services/utils/dom'; | |||||||
| import { CoreMimetypeUtils } from '@services/utils/mimetype'; | import { CoreMimetypeUtils } from '@services/utils/mimetype'; | ||||||
| import { CoreTextUtils } from '@services/utils/text'; | import { CoreTextUtils } from '@services/utils/text'; | ||||||
| import { CoreTimeUtils } from '@services/utils/time'; | import { CoreTimeUtils } from '@services/utils/time'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; | ||||||
| import { CoreForms } from '@singletons/form'; | import { CoreForms } from '@singletons/form'; | ||||||
|  | import { CoreApp } from '@services/app'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Component to handle a local file. Only files inside the app folder can be managed. |  * Component to handle a local file. Only files inside the app folder can be managed. | ||||||
| @ -55,6 +56,11 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|     newFileName = ''; |     newFileName = ''; | ||||||
|     editMode = false; |     editMode = false; | ||||||
|     relativePath?: string; |     relativePath?: string; | ||||||
|  |     isIOS = false; | ||||||
|  |     openButtonIcon = ''; | ||||||
|  |     openButtonLabel = ''; | ||||||
|  | 
 | ||||||
|  |     protected defaultIsOpenWithPicker = false; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Component being initialized. |      * Component being initialized. | ||||||
| @ -76,6 +82,10 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|         this.timemodified = CoreTimeUtils.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort'); |         this.timemodified = CoreTimeUtils.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort'); | ||||||
| 
 | 
 | ||||||
|  |         this.isIOS = CoreApp.isIOS(); | ||||||
|  |         this.defaultIsOpenWithPicker = CoreFileHelper.defaultIsOpenWithPicker(); | ||||||
|  |         this.openButtonIcon = this.defaultIsOpenWithPicker ? 'fas-file' : 'fas-share-square'; | ||||||
|  |         this.openButtonLabel = this.defaultIsOpenWithPicker ? 'core.openfile' : 'core.openwith'; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -95,11 +105,12 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * File clicked. |      * Open file. | ||||||
|      * |      * | ||||||
|      * @param e Click event. |      * @param e Click event. | ||||||
|  |      * @param isOpenButton Whether the open button was clicked. | ||||||
|      */ |      */ | ||||||
|     async fileClicked(e: Event): Promise<void> { |     async openFile(e: Event, isOpenButton = false): Promise<void> { | ||||||
|         if (this.editMode) { |         if (this.editMode) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -107,7 +118,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|         if (CoreUtils.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) { |         if (!isOpenButton && CoreUtils.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) { | ||||||
|             this.onClick.emit(); |             this.onClick.emit(); | ||||||
| 
 | 
 | ||||||
|             return; |             return; | ||||||
| @ -121,7 +132,13 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         CoreUtils.openFile(this.file!.toURL()); |         const options: CoreUtilsOpenFileOptions = {}; | ||||||
|  |         if (isOpenButton) { | ||||||
|  |             // Use the non-default method.
 | ||||||
|  |             options.iOSOpenFileAction = this.defaultIsOpenWithPicker ? OpenFileAction.OPEN : OpenFileAction.OPEN_WITH; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         CoreUtils.openFile(this.file!.toURL(), options); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ | |||||||
| import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper'; | import { CoreColorScheme, CoreZoomLevel } from '@features/settings/services/settings-helper'; | ||||||
| import { CoreSitesDemoSiteData } from '@services/sites'; | import { CoreSitesDemoSiteData } from '@services/sites'; | ||||||
| import envJson from '@/assets/env.json'; | import envJson from '@/assets/env.json'; | ||||||
|  | import { OpenFileAction } from '@services/utils/utils'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Context levels enumeration. |  * Context levels enumeration. | ||||||
| @ -173,6 +174,7 @@ export interface EnvironmentConfig { | |||||||
|     displayqroncredentialscreen?: boolean; |     displayqroncredentialscreen?: boolean; | ||||||
|     displayqronsitescreen?: boolean; |     displayqronsitescreen?: boolean; | ||||||
|     forceOpenLinksIn: 'app' | 'browser'; |     forceOpenLinksIn: 'app' | 'browser'; | ||||||
|  |     iOSDefaultOpenFileAction?: OpenFileAction; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export interface EnvironmentBuild { | export interface EnvironmentBuild { | ||||||
|  | |||||||
| @ -99,7 +99,10 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy { | |||||||
|         this.hasInfo = !!( |         this.hasInfo = !!( | ||||||
|             this.module.description || |             this.module.description || | ||||||
|             (this.showActivityDates && this.module.dates && this.module.dates.length) || |             (this.showActivityDates && this.module.dates && this.module.dates.length) || | ||||||
|             this.module.completiondata |             (this.module.completiondata && | ||||||
|  |                 ((this.showManualCompletion && !this.module.completiondata.isautomatic) || | ||||||
|  |                     (this.showCompletionConditions && this.module.completiondata.isautomatic)) | ||||||
|  |             ) | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         if (this.module.handlerData.showDownloadButton) { |         if (this.module.handlerData.showDownloadButton) { | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ import { Directive, Input, OnInit, ElementRef } from '@angular/core'; | |||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreCourse, CoreCourseModuleContentFile, CoreCourseWSModule } from '@features/course/services/course'; | import { CoreCourse, CoreCourseModuleContentFile, CoreCourseWSModule } from '@features/course/services/course'; | ||||||
| import { CoreCourseHelper } from '@features/course/services/course-helper'; | import { CoreCourseHelper } from '@features/course/services/course-helper'; | ||||||
|  | import { CoreUtilsOpenFileOptions } from '@services/utils/utils'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Directive to allow downloading and open the main file of a module. |  * Directive to allow downloading and open the main file of a module. | ||||||
| @ -36,6 +37,7 @@ export class CoreCourseDownloadModuleMainFileDirective implements OnInit { | |||||||
|     @Input() component?: string; // Component to link the file to.
 |     @Input() component?: string; // Component to link the file to.
 | ||||||
|     @Input() componentId?: string | number; // Component ID to use in conjunction with the component. If not defined, use moduleId.
 |     @Input() componentId?: string | number; // Component ID to use in conjunction with the component. If not defined, use moduleId.
 | ||||||
|     @Input() files?: CoreCourseModuleContentFile[]; // List of files of the module. If not provided, use module.contents.
 |     @Input() files?: CoreCourseModuleContentFile[]; // List of files of the module. If not provided, use module.contents.
 | ||||||
|  |     @Input() options?: CoreUtilsOpenFileOptions = {}; | ||||||
| 
 | 
 | ||||||
|     protected element: HTMLElement; |     protected element: HTMLElement; | ||||||
| 
 | 
 | ||||||
| @ -73,6 +75,8 @@ export class CoreCourseDownloadModuleMainFileDirective implements OnInit { | |||||||
|                     this.component, |                     this.component, | ||||||
|                     componentId, |                     componentId, | ||||||
|                     this.files, |                     this.files, | ||||||
|  |                     undefined, | ||||||
|  |                     this.options, | ||||||
|                 ); |                 ); | ||||||
|             } catch (error) { |             } catch (error) { | ||||||
|                 CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); |                 CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true); | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ import { CoreLogger } from '@singletons/logger'; | |||||||
| import { makeSingleton, Translate } from '@singletons'; | import { makeSingleton, Translate } from '@singletons'; | ||||||
| import { CoreFilepool } from '@services/filepool'; | import { CoreFilepool } from '@services/filepool'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils, CoreUtilsOpenFileOptions } from '@services/utils/utils'; | ||||||
| import { | import { | ||||||
|     CoreCourseAnyCourseData, |     CoreCourseAnyCourseData, | ||||||
|     CoreCourseBasicData, |     CoreCourseBasicData, | ||||||
| @ -656,6 +656,7 @@ export class CoreCourseHelperProvider { | |||||||
|      * @param componentId An ID to use in conjunction with the component. |      * @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 files List of files of the module. If not provided, use module.contents. | ||||||
|      * @param siteId The site ID. If not defined, current site. |      * @param siteId The site ID. If not defined, current site. | ||||||
|  |      * @param options Options to open the file. | ||||||
|      * @return Resolved on success. |      * @return Resolved on success. | ||||||
|      */ |      */ | ||||||
|     async downloadModuleAndOpenFile( |     async downloadModuleAndOpenFile( | ||||||
| @ -665,6 +666,7 @@ export class CoreCourseHelperProvider { | |||||||
|         componentId?: string | number, |         componentId?: string | number, | ||||||
|         files?: CoreCourseModuleContentFile[], |         files?: CoreCourseModuleContentFile[], | ||||||
|         siteId?: string, |         siteId?: string, | ||||||
|  |         options: CoreUtilsOpenFileOptions = {}, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); |         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||||
| 
 | 
 | ||||||
| @ -696,7 +698,7 @@ export class CoreCourseHelperProvider { | |||||||
|         const result = await this.downloadModuleWithMainFileIfNeeded(module, courseId, component || '', componentId, files, siteId); |         const result = await this.downloadModuleWithMainFileIfNeeded(module, courseId, component || '', componentId, files, siteId); | ||||||
| 
 | 
 | ||||||
|         if (CoreUrlUtils.isLocalFileUrl(result.path)) { |         if (CoreUrlUtils.isLocalFileUrl(result.path)) { | ||||||
|             return CoreUtils.openFile(result.path); |             return CoreUtils.openFile(result.path, options); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /* In iOS, if we use the same URL in embedded browser and background download then the download only |         /* In iOS, if we use the same URL in embedded browser and background download then the download only | ||||||
| @ -724,7 +726,7 @@ export class CoreCourseHelperProvider { | |||||||
|                 path = await CoreFilepool.getInternalUrlByUrl(siteId, mainFile.fileurl); |                 path = await CoreFilepool.getInternalUrlByUrl(siteId, mainFile.fileurl); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             await CoreUtils.openFile(path); |             await CoreUtils.openFile(path, options); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -217,6 +217,7 @@ | |||||||
|     "openmodinbrowser": "Open {{$a}} in browser", |     "openmodinbrowser": "Open {{$a}} in browser", | ||||||
|     "opensecurityquestion": "Open security question", |     "opensecurityquestion": "Open security question", | ||||||
|     "opensettings": "Open settings", |     "opensettings": "Open settings", | ||||||
|  |     "openwith": "Open with...", | ||||||
|     "othergroups": "Other groups", |     "othergroups": "Other groups", | ||||||
|     "pagea": "Page {{$a}}", |     "pagea": "Page {{$a}}", | ||||||
|     "parentlanguage": "", |     "parentlanguage": "", | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ import { CoreSites } from '@services/sites'; | |||||||
| import { CoreWS, CoreWSFile } from '@services/ws'; | import { CoreWS, CoreWSFile } from '@services/ws'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreUrlUtils } from '@services/utils/url'; | import { CoreUrlUtils } from '@services/utils/url'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | import { CoreUtils, CoreUtilsOpenFileOptions, OpenFileAction } from '@services/utils/utils'; | ||||||
| import { CoreConstants } from '@/core/constants'; | import { CoreConstants } from '@/core/constants'; | ||||||
| import { CoreError } from '@classes/errors/error'; | import { CoreError } from '@classes/errors/error'; | ||||||
| import { makeSingleton, Translate } from '@singletons'; | import { makeSingleton, Translate } from '@singletons'; | ||||||
| @ -34,6 +34,15 @@ import { CoreNetworkError } from '@classes/errors/network-error'; | |||||||
| @Injectable({ providedIn: 'root' }) | @Injectable({ providedIn: 'root' }) | ||||||
| export class CoreFileHelperProvider { | export class CoreFileHelperProvider { | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if the default behaviour of the app is open file with picker. | ||||||
|  |      * | ||||||
|  |      * @return Boolean. | ||||||
|  |      */ | ||||||
|  |     defaultIsOpenWithPicker(): boolean { | ||||||
|  |         return CoreApp.isIOS() && CoreConstants.CONFIG.iOSDefaultOpenFileAction === OpenFileAction.OPEN_WITH; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Convenience function to open a file, downloading it if needed. |      * Convenience function to open a file, downloading it if needed. | ||||||
|      * |      * | ||||||
| @ -43,6 +52,7 @@ export class CoreFileHelperProvider { | |||||||
|      * @param state The file's state. If not provided, it will be calculated. |      * @param state The file's state. If not provided, it will be calculated. | ||||||
|      * @param onProgress Function to call on progress. |      * @param onProgress Function to call on progress. | ||||||
|      * @param siteId The site ID. If not defined, current site. |      * @param siteId The site ID. If not defined, current site. | ||||||
|  |      * @param options Options to open the file. | ||||||
|      * @return Resolved on success. |      * @return Resolved on success. | ||||||
|      */ |      */ | ||||||
|     async downloadAndOpenFile( |     async downloadAndOpenFile( | ||||||
| @ -52,6 +62,7 @@ export class CoreFileHelperProvider { | |||||||
|         state?: string, |         state?: string, | ||||||
|         onProgress?: CoreFileHelperOnProgress, |         onProgress?: CoreFileHelperOnProgress, | ||||||
|         siteId?: string, |         siteId?: string, | ||||||
|  |         options: CoreUtilsOpenFileOptions = {}, | ||||||
|     ): Promise<void> { |     ): Promise<void> { | ||||||
|         siteId = siteId || CoreSites.getCurrentSiteId(); |         siteId = siteId || CoreSites.getCurrentSiteId(); | ||||||
| 
 | 
 | ||||||
| @ -102,7 +113,7 @@ export class CoreFileHelperProvider { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return CoreUtils.openFile(url); |         return CoreUtils.openFile(url, options); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ import { CoreFileSizeSum } from '@services/plugin-file-delegate'; | |||||||
| import { CoreViewerQRScannerComponent } from '@features/viewer/components/qr-scanner/qr-scanner'; | import { CoreViewerQRScannerComponent } from '@features/viewer/components/qr-scanner/qr-scanner'; | ||||||
| import { CoreCanceledError } from '@classes/errors/cancelederror'; | import { CoreCanceledError } from '@classes/errors/cancelederror'; | ||||||
| import { CoreFileEntry } from '@services/file-helper'; | import { CoreFileEntry } from '@services/file-helper'; | ||||||
|  | import { CoreConstants } from '@/core/constants'; | ||||||
| 
 | 
 | ||||||
| type TreeNode<T> = T & { children: TreeNode<T>[] }; | type TreeNode<T> = T & { children: TreeNode<T>[] }; | ||||||
| 
 | 
 | ||||||
| @ -907,9 +908,10 @@ export class CoreUtilsProvider { | |||||||
|      * Open a file using platform specific method. |      * Open a file using platform specific method. | ||||||
|      * |      * | ||||||
|      * @param path The local path of the file to be open. |      * @param path The local path of the file to be open. | ||||||
|  |      * @param options Options. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     async openFile(path: string): Promise<void> { |     async openFile(path: string, options: CoreUtilsOpenFileOptions = {}): Promise<void> { | ||||||
|         // Convert the path to a native path if needed.
 |         // Convert the path to a native path if needed.
 | ||||||
|         path = CoreFile.unconvertFileSrc(path); |         path = CoreFile.unconvertFileSrc(path); | ||||||
| 
 | 
 | ||||||
| @ -931,7 +933,12 @@ export class CoreUtilsProvider { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             await FileOpener.open(path, mimetype || ''); |             const openFileAction = options.iOSOpenFileAction ?? CoreConstants.CONFIG.iOSDefaultOpenFileAction; | ||||||
|  |             if (CoreApp.isIOS() && openFileAction == OpenFileAction.OPEN_WITH) { | ||||||
|  |                 await FileOpener.showOpenWithDialog(path, mimetype || ''); | ||||||
|  |             } else { | ||||||
|  |                 await FileOpener.open(path, mimetype || ''); | ||||||
|  |             } | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|             this.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype); |             this.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype); | ||||||
|             this.logger.error('Error: ', JSON.stringify(error)); |             this.logger.error('Error: ', JSON.stringify(error)); | ||||||
| @ -1703,3 +1710,18 @@ export type CoreMenuItem<T = number> = { | |||||||
|     label: string; |     label: string; | ||||||
|     value: T | number; |     value: T | number; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Options for opening a file. | ||||||
|  |  */ | ||||||
|  | export type CoreUtilsOpenFileOptions = { | ||||||
|  |     iOSOpenFileAction?: OpenFileAction; // Action to do when opening a file.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Possible default picker actions. | ||||||
|  |  */ | ||||||
|  | export enum OpenFileAction { | ||||||
|  |     OPEN = 'open', | ||||||
|  |     OPEN_WITH = 'open-with', | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user