forked from EVOgeek/Vmeda.Online
		
	
						commit
						78c0af8a4b
					
				| @ -1373,6 +1373,7 @@ | |||||||
|   "core.confirmgotabrootdefault": "local_moodlemobileapp", |   "core.confirmgotabrootdefault": "local_moodlemobileapp", | ||||||
|   "core.confirmloss": "local_moodlemobileapp", |   "core.confirmloss": "local_moodlemobileapp", | ||||||
|   "core.confirmopeninbrowser": "local_moodlemobileapp", |   "core.confirmopeninbrowser": "local_moodlemobileapp", | ||||||
|  |   "core.confirmreadfiletoobig": "local_moodlemobileapp", | ||||||
|   "core.considereddigitalminor": "moodle", |   "core.considereddigitalminor": "moodle", | ||||||
|   "core.content": "moodle", |   "core.content": "moodle", | ||||||
|   "core.contenteditingsynced": "local_moodlemobileapp", |   "core.contenteditingsynced": "local_moodlemobileapp", | ||||||
|  | |||||||
| @ -1373,6 +1373,7 @@ | |||||||
|     "core.confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?", |     "core.confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?", | ||||||
|     "core.confirmloss": "Are you sure? All changes will be lost.", |     "core.confirmloss": "Are you sure? All changes will be lost.", | ||||||
|     "core.confirmopeninbrowser": "Do you want to open it in a web browser?", |     "core.confirmopeninbrowser": "Do you want to open it in a web browser?", | ||||||
|  |     "core.confirmreadfiletoobig": "It seems the app previously crashed when trying to read a file as big as this one. Are you sure you want to continue?<br><br>If this file is stored in a repository like Google Drive, please try to download the file to your device first and use the downloaded file.", | ||||||
|     "core.considereddigitalminor": "You are too young to create an account on this site.", |     "core.considereddigitalminor": "You are too young to create an account on this site.", | ||||||
|     "core.content": "Content", |     "core.content": "Content", | ||||||
|     "core.contenteditingsynced": "The content you are editing has been synced.", |     "core.contenteditingsynced": "The content you are editing has been synced.", | ||||||
|  | |||||||
| @ -78,7 +78,7 @@ export class CoreLocalFileComponent implements OnInit { | |||||||
|                 this.size = this.textUtils.bytesToSize(metadata.size, 2); |                 this.size = this.textUtils.bytesToSize(metadata.size, 2); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             this.timemodified = this.timeUtils.userDate(metadata.modificationTime, 'core.strftimedatetimeshort'); |             this.timemodified = this.timeUtils.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort'); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import { CoreFileProvider } from '@providers/file'; | |||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| import { CoreTextUtilsProvider } from '@providers/utils/text'; | import { CoreTextUtilsProvider } from '@providers/utils/text'; | ||||||
| import { CoreTimeUtilsProvider } from '@providers/utils/time'; | import { CoreTimeUtilsProvider } from '@providers/utils/time'; | ||||||
|  | import { MediaFile } from '@ionic-native/media-capture'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page to capture media in browser or desktop. |  * Page to capture media in browser or desktop. | ||||||
| @ -364,13 +365,21 @@ export class CoreEmulatorCaptureMediaPage implements OnInit, OnDestroy { | |||||||
|             if (this.isImage && !this.isCaptureImage) { |             if (this.isImage && !this.isCaptureImage) { | ||||||
|                 this.dismissWithData(fileEntry.toURL()); |                 this.dismissWithData(fileEntry.toURL()); | ||||||
|             } else { |             } else { | ||||||
|                 // The capture plugin returns a MediaFile, not a FileEntry.
 |                 // The capture plugin should return a MediaFile, not a FileEntry. Convert it.
 | ||||||
|                 // The only difference is that it supports a new function that won't be supported in desktop.
 |                 return this.fileProvider.getMetadata(fileEntry).then((metadata) => { | ||||||
|                 fileEntry.getFormatData = (successFn, errorFn): any => { |                     const mediaFile: MediaFile = { | ||||||
|  |                         name: fileEntry.name, | ||||||
|  |                         fullPath: fileEntry.fullPath, | ||||||
|  |                         type: null, | ||||||
|  |                         lastModifiedDate: metadata.modificationTime, | ||||||
|  |                         size: metadata.size, | ||||||
|  |                         getFormatData: (successFn, errorFn): void => { | ||||||
|                             // Nothing to do.
 |                             // Nothing to do.
 | ||||||
|  |                         } | ||||||
|                     }; |                     }; | ||||||
| 
 | 
 | ||||||
|                 this.dismissWithData([fileEntry]); |                     this.dismissWithData([mediaFile]); | ||||||
|  |                 }); | ||||||
|             } |             } | ||||||
|         }).catch((err) => { |         }).catch((err) => { | ||||||
|             this.domUtils.showErrorModal(err); |             this.domUtils.showErrorModal(err); | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import { CoreTimeUtilsProvider } from '@providers/utils/time'; | |||||||
| import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; | import { CoreFileUploaderHandler, CoreFileUploaderHandlerData } from './delegate'; | ||||||
| import { CoreFileUploaderHelperProvider } from './helper'; | import { CoreFileUploaderHelperProvider } from './helper'; | ||||||
| import { CoreFileUploaderProvider } from './fileuploader'; | import { CoreFileUploaderProvider } from './fileuploader'; | ||||||
|  | import { TranslateService } from '@ngx-translate/core'; | ||||||
| /** | /** | ||||||
|  * Handler to upload any type of file. |  * Handler to upload any type of file. | ||||||
|  */ |  */ | ||||||
| @ -28,9 +29,13 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler { | |||||||
|     name = 'CoreFileUploaderFile'; |     name = 'CoreFileUploaderFile'; | ||||||
|     priority = 1200; |     priority = 1200; | ||||||
| 
 | 
 | ||||||
|     constructor(private appProvider: CoreAppProvider, private platform: Platform, private timeUtils: CoreTimeUtilsProvider, |     constructor(protected appProvider: CoreAppProvider, | ||||||
|             private uploaderHelper: CoreFileUploaderHelperProvider, private uploaderProvider: CoreFileUploaderProvider, |             protected platform: Platform, | ||||||
|             private domUtils: CoreDomUtilsProvider) { } |             protected timeUtils: CoreTimeUtilsProvider, | ||||||
|  |             protected uploaderHelper: CoreFileUploaderHelperProvider, | ||||||
|  |             protected uploaderProvider: CoreFileUploaderProvider, | ||||||
|  |             protected domUtils: CoreDomUtilsProvider, | ||||||
|  |             protected translate: TranslateService) { } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Whether or not the handler is enabled on a site level. |      * Whether or not the handler is enabled on a site level. | ||||||
| @ -107,9 +112,8 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler { | |||||||
|                         this.uploaderHelper.uploadFileObject(file, maxSize, upload, allowOffline, fileName).then((result) => { |                         this.uploaderHelper.uploadFileObject(file, maxSize, upload, allowOffline, fileName).then((result) => { | ||||||
|                             this.uploaderHelper.fileUploaded(result); |                             this.uploaderHelper.fileUploaded(result); | ||||||
|                         }).catch((error) => { |                         }).catch((error) => { | ||||||
|                             if (error) { |                             this.domUtils.showErrorModalDefault(error, | ||||||
|                                 this.domUtils.showErrorModal(error); |                                     this.translate.instant('core.fileuploader.errorreadingfile')); | ||||||
|                             } |  | ||||||
|                         }); |                         }); | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ import { Camera, CameraOptions } from '@ionic-native/camera'; | |||||||
| import { TranslateService } from '@ngx-translate/core'; | import { TranslateService } from '@ngx-translate/core'; | ||||||
| import { CoreAppProvider } from '@providers/app'; | import { CoreAppProvider } from '@providers/app'; | ||||||
| import { CoreFileProvider, CoreFileProgressEvent } from '@providers/file'; | import { CoreFileProvider, CoreFileProgressEvent } from '@providers/file'; | ||||||
|  | import { CoreFileHelperProvider } from '@providers/file-helper'; | ||||||
| import { CoreLoggerProvider } from '@providers/logger'; | import { CoreLoggerProvider } from '@providers/logger'; | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| import { CoreTextUtilsProvider } from '@providers/utils/text'; | import { CoreTextUtilsProvider } from '@providers/utils/text'; | ||||||
| @ -36,11 +37,19 @@ export class CoreFileUploaderHelperProvider { | |||||||
|     protected filePickerDeferred: PromiseDefer; |     protected filePickerDeferred: PromiseDefer; | ||||||
|     protected actionSheet: ActionSheet; |     protected actionSheet: ActionSheet; | ||||||
| 
 | 
 | ||||||
|     constructor(logger: CoreLoggerProvider, private appProvider: CoreAppProvider, private translate: TranslateService, |     constructor(logger: CoreLoggerProvider, | ||||||
|             private fileUploaderProvider: CoreFileUploaderProvider, private domUtils: CoreDomUtilsProvider, |             protected appProvider: CoreAppProvider, | ||||||
|             private textUtils: CoreTextUtilsProvider, private fileProvider: CoreFileProvider, private utils: CoreUtilsProvider, |             protected translate: TranslateService, | ||||||
|             private actionSheetCtrl: ActionSheetController, private uploaderDelegate: CoreFileUploaderDelegate, |             protected fileUploaderProvider: CoreFileUploaderProvider, | ||||||
|             private camera: Camera, private platform: Platform) { |             protected domUtils: CoreDomUtilsProvider, | ||||||
|  |             protected textUtils: CoreTextUtilsProvider, | ||||||
|  |             protected fileProvider: CoreFileProvider, | ||||||
|  |             protected utils: CoreUtilsProvider, | ||||||
|  |             protected actionSheetCtrl: ActionSheetController, | ||||||
|  |             protected uploaderDelegate: CoreFileUploaderDelegate, | ||||||
|  |             protected camera: Camera, | ||||||
|  |             protected platform: Platform, | ||||||
|  |             protected fileHelper: CoreFileHelperProvider) { | ||||||
|         this.logger = logger.getInstance('CoreFileUploaderProvider'); |         this.logger = logger.getInstance('CoreFileUploaderProvider'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -99,14 +108,14 @@ export class CoreFileUploaderHelperProvider { | |||||||
|             const filePath = this.textUtils.concatenatePaths(CoreFileProvider.TMPFOLDER, newName); |             const filePath = this.textUtils.concatenatePaths(CoreFileProvider.TMPFOLDER, newName); | ||||||
| 
 | 
 | ||||||
|             // Write the data into the file.
 |             // Write the data into the file.
 | ||||||
|             return this.fileProvider.writeFileDataInFile(file, filePath, (progress: CoreFileProgressEvent) => { |             return this.fileHelper.writeFileDataInFile(file, filePath, (progress: CoreFileProgressEvent) => { | ||||||
|                 this.showProgressModal(modal, 'core.fileuploader.readingfileperc', progress); |                 this.showProgressModal(modal, 'core.fileuploader.readingfileperc', progress); | ||||||
|             }); |             }); | ||||||
|         }).catch((error) => { |         }).catch((error) => { | ||||||
|             this.logger.error('Error reading file to upload.', error); |             this.logger.error('Error reading file to upload.', error); | ||||||
|             modal.dismiss(); |             modal.dismiss(); | ||||||
| 
 | 
 | ||||||
|             return Promise.reject(this.translate.instant('core.fileuploader.errorreadingfile')); |             return Promise.reject(error); | ||||||
|         }).then((fileEntry) => { |         }).then((fileEntry) => { | ||||||
|             modal.dismiss(); |             modal.dismiss(); | ||||||
| 
 | 
 | ||||||
| @ -319,9 +328,7 @@ export class CoreFileUploaderHelperProvider { | |||||||
|                         // Success uploading or picking, return the result.
 |                         // Success uploading or picking, return the result.
 | ||||||
|                         this.fileUploaded(result); |                         this.fileUploaded(result); | ||||||
|                     }).catch((error) => { |                     }).catch((error) => { | ||||||
|                         if (error) { |                         this.domUtils.showErrorModalDefault(error, this.translate.instant('core.fileuploader.errorreadingfile')); | ||||||
|                             this.domUtils.showErrorModal(error); |  | ||||||
|                         } |  | ||||||
|                     }); |                     }); | ||||||
| 
 | 
 | ||||||
|                     // Do not close the action sheet, it will be closed if success.
 |                     // Do not close the action sheet, it will be closed if success.
 | ||||||
|  | |||||||
| @ -43,6 +43,7 @@ | |||||||
|     "confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?", |     "confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?", | ||||||
|     "confirmloss": "Are you sure? All changes will be lost.", |     "confirmloss": "Are you sure? All changes will be lost.", | ||||||
|     "confirmopeninbrowser": "Do you want to open it in a web browser?", |     "confirmopeninbrowser": "Do you want to open it in a web browser?", | ||||||
|  |     "confirmreadfiletoobig": "It seems the app previously crashed when trying to read a file as big as this one. Are you sure you want to continue?<br><br>If this file is stored in a repository like Google Drive, please try to download the file to your device first and use the downloaded file.", | ||||||
|     "considereddigitalminor": "You are too young to create an account on this site.", |     "considereddigitalminor": "You are too young to create an account on this site.", | ||||||
|     "content": "Content", |     "content": "Content", | ||||||
|     "contenteditingsynced": "The content you are editing has been synced.", |     "contenteditingsynced": "The content you are editing has been synced.", | ||||||
|  | |||||||
| @ -15,12 +15,15 @@ | |||||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||||
| import { TranslateService } from '@ngx-translate/core'; | import { TranslateService } from '@ngx-translate/core'; | ||||||
| import { CoreAppProvider } from './app'; | import { CoreAppProvider } from './app'; | ||||||
| import { CoreFileProvider } from './file'; | import { CoreConfigProvider } from './config'; | ||||||
|  | import { CoreFileProvider, CoreFileProgressFunction } from './file'; | ||||||
| import { CoreFilepoolProvider } from './filepool'; | import { CoreFilepoolProvider } from './filepool'; | ||||||
| import { CoreSitesProvider } from './sites'; | import { CoreSitesProvider } from './sites'; | ||||||
| import { CoreWSProvider } from './ws'; | import { CoreWSProvider } from './ws'; | ||||||
|  | import { CoreDomUtilsProvider } from './utils/dom'; | ||||||
| import { CoreUtilsProvider } from './utils/utils'; | import { CoreUtilsProvider } from './utils/utils'; | ||||||
| import { CoreConstants } from '@core/constants'; | import { CoreConstants } from '@core/constants'; | ||||||
|  | import { FileEntry } from '@ionic-native/file'; | ||||||
| import { makeSingleton } from '@singletons/core.singletons'; | import { makeSingleton } from '@singletons/core.singletons'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -29,9 +32,23 @@ import { makeSingleton } from '@singletons/core.singletons'; | |||||||
| @Injectable() | @Injectable() | ||||||
| export class CoreFileHelperProvider { | export class CoreFileHelperProvider { | ||||||
| 
 | 
 | ||||||
|     constructor(private fileProvider: CoreFileProvider, private filepoolProvider: CoreFilepoolProvider, |     // Variables for reading files in chunks.
 | ||||||
|             private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, private translate: TranslateService, |     protected MAX_CHUNK_SIZE_NAME = 'file_max_chunk_size'; | ||||||
|             private utils: CoreUtilsProvider, private wsProvider: CoreWSProvider) { } |     protected READ_CHUNK_ATTEMPT_NAME = 'file_read_chunk_attempt'; | ||||||
|  |     protected maxChunkSize = -1; | ||||||
|  | 
 | ||||||
|  |     constructor(protected fileProvider: CoreFileProvider, | ||||||
|  |             protected filepoolProvider: CoreFilepoolProvider, | ||||||
|  |             protected sitesProvider: CoreSitesProvider, | ||||||
|  |             protected appProvider: CoreAppProvider, | ||||||
|  |             protected translate: TranslateService, | ||||||
|  |             protected utils: CoreUtilsProvider, | ||||||
|  |             protected wsProvider: CoreWSProvider, | ||||||
|  |             protected configProvider: CoreConfigProvider, | ||||||
|  |             protected domUtils: CoreDomUtilsProvider) { | ||||||
|  | 
 | ||||||
|  |         this.initMaxChunkSize(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Convenience function to open a file, downloading it if needed. |      * Convenience function to open a file, downloading it if needed. | ||||||
| @ -240,6 +257,34 @@ export class CoreFileHelperProvider { | |||||||
|         return file.timemodified || 0; |         return file.timemodified || 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Initialize the max chunk size. | ||||||
|  |      * | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async initMaxChunkSize(): Promise<void> { | ||||||
|  |         const sizes = await Promise.all([ | ||||||
|  |             await this.configProvider.get(this.READ_CHUNK_ATTEMPT_NAME, -1), // Check if there is any attempt pending.
 | ||||||
|  |             await this.configProvider.get(this.MAX_CHUNK_SIZE_NAME, -1), // Retrieve current max chunk size from DB.
 | ||||||
|  |         ]); | ||||||
|  | 
 | ||||||
|  |         const attemptSize = sizes[0]; | ||||||
|  |         const maxChunkSize = sizes[1]; | ||||||
|  | 
 | ||||||
|  |         if (attemptSize != -1 && (maxChunkSize == -1 || attemptSize < maxChunkSize)) { | ||||||
|  |             // Store the attempt's size as the max size.
 | ||||||
|  |             this.storeMaxChunkSize(attemptSize); | ||||||
|  |         } else { | ||||||
|  |             // No attempt or the max size is already lower. Keep current max size.
 | ||||||
|  |             this.maxChunkSize = maxChunkSize; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (attemptSize != -1) { | ||||||
|  |             // Clean pending attempt.
 | ||||||
|  |             await this.configProvider.delete(this.READ_CHUNK_ATTEMPT_NAME); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Check if a state is downloaded or outdated. |      * Check if a state is downloaded or outdated. | ||||||
|      * |      * | ||||||
| @ -334,6 +379,99 @@ export class CoreFileHelperProvider { | |||||||
| 
 | 
 | ||||||
|         throw new Error('Couldn\'t determine file size: ' + file.fileurl); |         throw new Error('Couldn\'t determine file size: ' + file.fileurl); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Save max chunk size. | ||||||
|  |      * | ||||||
|  |      * @param size Size to store. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected async storeMaxChunkSize(size: number): Promise<void> { | ||||||
|  |         this.maxChunkSize = size; | ||||||
|  | 
 | ||||||
|  |         await this.configProvider.set(this.MAX_CHUNK_SIZE_NAME, size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Write some file data into a filesystem file. | ||||||
|  |      * It's done in chunks to prevent crashing the app for big files. | ||||||
|  |      * | ||||||
|  |      * @param file The data to write. | ||||||
|  |      * @param path Path where to store the data. | ||||||
|  |      * @param onProgress Function to call on progress. | ||||||
|  |      * @param offset Offset where to start reading from. | ||||||
|  |      * @param append Whether to append the data to the end of the file. | ||||||
|  |      * @return Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     async writeFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction, offset: number = 0, | ||||||
|  |             append?: boolean): Promise<FileEntry> { | ||||||
|  | 
 | ||||||
|  |         offset = offset || 0; | ||||||
|  | 
 | ||||||
|  |         // Get the chunk to read and write.
 | ||||||
|  |         const readWholeFile = offset === 0 && CoreFileProvider.CHUNK_SIZE >= file.size; | ||||||
|  |         const chunk = readWholeFile ? file : file.slice(offset, Math.min(offset + CoreFileProvider.CHUNK_SIZE, file.size)); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             const fileEntry = await this.fileProvider.writeFileDataInFileChunk(chunk, path, append); | ||||||
|  | 
 | ||||||
|  |             offset += CoreFileProvider.CHUNK_SIZE; | ||||||
|  | 
 | ||||||
|  |             onProgress && onProgress({ | ||||||
|  |                 lengthComputable: true, | ||||||
|  |                 loaded: offset, | ||||||
|  |                 total: file.size | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             if (offset >= file.size) { | ||||||
|  |                 // Done, stop.
 | ||||||
|  |                 return fileEntry; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Read the next chunk.
 | ||||||
|  |             return this.writeFileDataInFile(file, path, onProgress, offset, true); | ||||||
|  |         } catch (error) { | ||||||
|  |             if (readWholeFile || !error || error.name != 'NotReadableError') { | ||||||
|  |                 return Promise.reject(error); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Permission error when reading file in chunks. This usually happens with Google Drive files.
 | ||||||
|  |             // Try to read the whole file at once.
 | ||||||
|  |             return this.writeBigFileDataInFile(file, path, onProgress); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Writes a big file data into a filesystem file without using chunks. | ||||||
|  |      * The app can crash when doing this with big files, so this function will try to control the max size that works | ||||||
|  |      * and warn the user if he's trying to upload a file that is too big. | ||||||
|  |      * | ||||||
|  |      * @param file The data to write. | ||||||
|  |      * @param path Path where to store the data. | ||||||
|  |      * @param onProgress Function to call on progress. | ||||||
|  |      * @return Promise resolved with the file. | ||||||
|  |      */ | ||||||
|  |     protected async writeBigFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction): Promise<FileEntry> { | ||||||
|  |         if (this.maxChunkSize != -1 && file.size >= this.maxChunkSize) { | ||||||
|  |             // The file size is bigger than the max allowed size. Ask the user to confirm.
 | ||||||
|  |             await this.domUtils.showConfirm(this.translate.instant('core.confirmreadfiletoobig')); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Store the "attempt".
 | ||||||
|  |         await this.configProvider.set(this.READ_CHUNK_ATTEMPT_NAME, file.size); | ||||||
|  | 
 | ||||||
|  |         // Write the whole file.
 | ||||||
|  |         const fileEntry = await this.fileProvider.writeFileDataInFileChunk(file, path, false); | ||||||
|  | 
 | ||||||
|  |         // Success, remove the attempt and increase the max chunk size if needed.
 | ||||||
|  |         await this.configProvider.delete(this.READ_CHUNK_ATTEMPT_NAME); | ||||||
|  | 
 | ||||||
|  |         if (file.size > this.maxChunkSize) { | ||||||
|  |             await this.storeMaxChunkSize(file.size + 1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return fileEntry; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class CoreFileHelper extends makeSingleton(CoreFileHelperProvider) {} | export class CoreFileHelper extends makeSingleton(CoreFileHelperProvider) {} | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ | |||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||||
| import { Platform } from 'ionic-angular'; | import { Platform } from 'ionic-angular'; | ||||||
| import { File, FileEntry, DirectoryEntry } from '@ionic-native/file'; | import { File, FileEntry, DirectoryEntry, Entry, Metadata } from '@ionic-native/file'; | ||||||
| 
 | 
 | ||||||
| import { CoreAppProvider } from './app'; | import { CoreAppProvider } from './app'; | ||||||
| import { CoreLoggerProvider } from './logger'; | import { CoreLoggerProvider } from './logger'; | ||||||
| @ -44,6 +44,11 @@ export interface CoreFileProgressEvent { | |||||||
|     total?: number; |     total?: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Progress function. | ||||||
|  |  */ | ||||||
|  | export type CoreFileProgressFunction = (event: CoreFileProgressEvent) => void; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Factory to interact with the file system. |  * Factory to interact with the file system. | ||||||
|  */ |  */ | ||||||
| @ -60,14 +65,21 @@ export class CoreFileProvider { | |||||||
|     static SITESFOLDER = 'sites'; |     static SITESFOLDER = 'sites'; | ||||||
|     static TMPFOLDER = 'tmp'; |     static TMPFOLDER = 'tmp'; | ||||||
| 
 | 
 | ||||||
|  |     static CHUNK_SIZE = 10485760; // 10 MB.
 | ||||||
|  | 
 | ||||||
|     protected logger; |     protected logger; | ||||||
|     protected initialized = false; |     protected initialized = false; | ||||||
|     protected basePath = ''; |     protected basePath = ''; | ||||||
|     protected isHTMLAPI = false; |     protected isHTMLAPI = false; | ||||||
|     protected CHUNK_SIZE = 10485760; // 10 MB.
 |  | ||||||
| 
 | 
 | ||||||
|     constructor(logger: CoreLoggerProvider, private platform: Platform, private file: File, private appProvider: CoreAppProvider, |     constructor(logger: CoreLoggerProvider, | ||||||
|             private textUtils: CoreTextUtilsProvider, private zip: Zip, private mimeUtils: CoreMimetypeUtilsProvider) { |             protected platform: Platform, | ||||||
|  |             protected file: File, | ||||||
|  |             protected appProvider: CoreAppProvider, | ||||||
|  |             protected textUtils: CoreTextUtilsProvider, | ||||||
|  |             protected zip: Zip, | ||||||
|  |             protected mimeUtils: CoreMimetypeUtilsProvider) { | ||||||
|  | 
 | ||||||
|         this.logger = logger.getInstance('CoreFileProvider'); |         this.logger = logger.getInstance('CoreFileProvider'); | ||||||
| 
 | 
 | ||||||
|         if (platform.is('android') && !Object.getOwnPropertyDescriptor(FileReader.prototype, 'onloadend')) { |         if (platform.is('android') && !Object.getOwnPropertyDescriptor(FileReader.prototype, 'onloadend')) { | ||||||
| @ -545,7 +557,7 @@ export class CoreFileProvider { | |||||||
| 
 | 
 | ||||||
|             reader.onloadend = (evt): void => { |             reader.onloadend = (evt): void => { | ||||||
|                 const target = <any> evt.target; // Convert to <any> to be able to use non-standard properties.
 |                 const target = <any> evt.target; // Convert to <any> to be able to use non-standard properties.
 | ||||||
|                 if (target.result !== undefined || target.result !== null) { |                 if (target.result !== undefined && target.result !== null) { | ||||||
|                     if (format == CoreFileProvider.FORMATJSON) { |                     if (format == CoreFileProvider.FORMATJSON) { | ||||||
|                         // Convert to object.
 |                         // Convert to object.
 | ||||||
|                         const parsed = this.textUtils.parseJSON(target.result, null); |                         const parsed = this.textUtils.parseJSON(target.result, null); | ||||||
| @ -558,7 +570,7 @@ export class CoreFileProvider { | |||||||
|                     } else { |                     } else { | ||||||
|                         resolve(target.result); |                         resolve(target.result); | ||||||
|                     } |                     } | ||||||
|                 } else if (target.error !== undefined || target.error !== null) { |                 } else if (target.error !== undefined && target.error !== null) { | ||||||
|                     reject(target.error); |                     reject(target.error); | ||||||
|                 } else { |                 } else { | ||||||
|                     reject({ code: null, message: 'READER_ONLOADEND_ERR' }); |                     reject({ code: null, message: 'READER_ONLOADEND_ERR' }); | ||||||
| @ -602,7 +614,7 @@ export class CoreFileProvider { | |||||||
|      * @param append Whether to append the data to the end of the file. |      * @param append Whether to append the data to the end of the file. | ||||||
|      * @return Promise to be resolved when the file is written. |      * @return Promise to be resolved when the file is written. | ||||||
|      */ |      */ | ||||||
|     writeFile(path: string, data: any, append?: boolean): Promise<any> { |     writeFile(path: string, data: any, append?: boolean): Promise<FileEntry> { | ||||||
|         return this.init().then(() => { |         return this.init().then(() => { | ||||||
|             // Remove basePath if it's in the path.
 |             // Remove basePath if it's in the path.
 | ||||||
|             path = this.removeStartingSlash(path.replace(this.basePath, '')); |             path = this.removeStartingSlash(path.replace(this.basePath, '')); | ||||||
| @ -634,17 +646,21 @@ export class CoreFileProvider { | |||||||
|      * @param offset Offset where to start reading from. |      * @param offset Offset where to start reading from. | ||||||
|      * @param append Whether to append the data to the end of the file. |      * @param append Whether to append the data to the end of the file. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|  |      * @deprecated since 3.8.3. Please use CoreFileHelperProvider.writeFileDataInFile instead. | ||||||
|      */ |      */ | ||||||
|     writeFileDataInFile(file: any, path: string, onProgress?: (event: CoreFileProgressEvent) => any, offset: number = 0, |     async writeFileDataInFile(file: Blob, path: string, onProgress?: CoreFileProgressFunction, offset: number = 0, | ||||||
|             append?: boolean): Promise<any> { |             append?: boolean): Promise<FileEntry> { | ||||||
| 
 | 
 | ||||||
|         offset = offset || 0; |         offset = offset || 0; | ||||||
| 
 | 
 | ||||||
|         // Get the chunk to read.
 |         // Get the chunk to read and write.
 | ||||||
|         const blob = file.slice(offset, Math.min(offset + this.CHUNK_SIZE, file.size)); |         const readWholeFile = offset === 0 && CoreFileProvider.CHUNK_SIZE >= file.size; | ||||||
|  |         const chunk = readWholeFile ? file : file.slice(offset, Math.min(offset + CoreFileProvider.CHUNK_SIZE, file.size)); | ||||||
| 
 | 
 | ||||||
|         return this.writeFileDataInFileChunk(blob, path, append).then((fileEntry) => { |         try { | ||||||
|             offset += this.CHUNK_SIZE; |             const fileEntry = await this.writeFileDataInFileChunk(chunk, path, append); | ||||||
|  | 
 | ||||||
|  |             offset += CoreFileProvider.CHUNK_SIZE; | ||||||
| 
 | 
 | ||||||
|             onProgress && onProgress({ |             onProgress && onProgress({ | ||||||
|                 lengthComputable: true, |                 lengthComputable: true, | ||||||
| @ -659,7 +675,15 @@ export class CoreFileProvider { | |||||||
| 
 | 
 | ||||||
|             // Read the next chunk.
 |             // Read the next chunk.
 | ||||||
|             return this.writeFileDataInFile(file, path, onProgress, offset, true); |             return this.writeFileDataInFile(file, path, onProgress, offset, true); | ||||||
|         }); |         } catch (error) { | ||||||
|  |             if (readWholeFile || !error || error.name != 'NotReadableError') { | ||||||
|  |                 return Promise.reject(error); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Permission error when reading file in chunks. This usually happens with Google Drive files.
 | ||||||
|  |             // Try to read the whole file at once.
 | ||||||
|  |             return this.writeFileDataInFileChunk(file, path, false); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -670,7 +694,7 @@ export class CoreFileProvider { | |||||||
|      * @param append Whether to append the data to the end of the file. |      * @param append Whether to append the data to the end of the file. | ||||||
|      * @return Promise resolved when done. |      * @return Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected writeFileDataInFileChunk(chunkData: any, path: string, append?: boolean): Promise<any> { |     writeFileDataInFileChunk(chunkData: Blob, path: string, append?: boolean): Promise<FileEntry> { | ||||||
|         // Read the chunk data.
 |         // Read the chunk data.
 | ||||||
|         return this.readFileData(chunkData, CoreFileProvider.FORMATARRAYBUFFER).then((fileData) => { |         return this.readFileData(chunkData, CoreFileProvider.FORMATARRAYBUFFER).then((fileData) => { | ||||||
|             // Write the data in the file.
 |             // Write the data in the file.
 | ||||||
| @ -1053,7 +1077,7 @@ export class CoreFileProvider { | |||||||
|      * @param fileEntry FileEntry retrieved from getFile or similar. |      * @param fileEntry FileEntry retrieved from getFile or similar. | ||||||
|      * @return Promise resolved with metadata. |      * @return Promise resolved with metadata. | ||||||
|      */ |      */ | ||||||
|     getMetadata(fileEntry: Entry): Promise<any> { |     getMetadata(fileEntry: Entry): Promise<Metadata> { | ||||||
|         if (!fileEntry || !fileEntry.getMetadata) { |         if (!fileEntry || !fileEntry.getMetadata) { | ||||||
|             return Promise.reject(null); |             return Promise.reject(null); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,6 +1,10 @@ | |||||||
| This files describes API changes in the Moodle Mobile app, | This files describes API changes in the Moodle Mobile app, | ||||||
| information provided here is intended especially for developers. | information provided here is intended especially for developers. | ||||||
| 
 | 
 | ||||||
|  | === 3.8.3 === | ||||||
|  | 
 | ||||||
|  | - CoreFileProvider.writeFileDataInFile has been deprecated. Please use CoreFileHelperProvider.writeFileDataInFile instead. | ||||||
|  | 
 | ||||||
| === 3.8.0 === | === 3.8.0 === | ||||||
| 
 | 
 | ||||||
| - CoreDomUtilsProvider.extractDownloadableFilesFromHtml and CoreDomUtilsProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects have been deprecated. Please use CoreFilepoolProvider.extractDownloadableFilesFromHtml and CoreFilepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects. We had to move them to prevent a circular dependency. | - CoreDomUtilsProvider.extractDownloadableFilesFromHtml and CoreDomUtilsProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects have been deprecated. Please use CoreFilepoolProvider.extractDownloadableFilesFromHtml and CoreFilepoolProvider.extractDownloadableFilesFromHtmlAsFakeFileObjects. We had to move them to prevent a circular dependency. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user