diff --git a/src/components/iframe/iframe.ts b/src/components/iframe/iframe.ts index 5806412bb..14dc98c54 100644 --- a/src/components/iframe/iframe.ts +++ b/src/components/iframe/iframe.ts @@ -106,17 +106,19 @@ export class CoreIframeComponent implements OnChanges { if (changes.src) { const url = this.urlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue; - if (this.platform.is('ios') && !this.urlUtils.isLocalFileUrl(url)) { + if (this.platform.is('ios') && url && !this.urlUtils.isLocalFileUrl(url)) { // Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView. try { const win = window; const urlParts = CoreUrl.parse(url); - await win.WKWebViewCookies.setCookie({ - name: 'MoodleAppCookieForWKWebView', - value: '1', - domain: urlParts.domain, - }); + if (urlParts.domain) { + await win.WKWebViewCookies.setCookie({ + name: 'MoodleAppCookieForWKWebView', + value: '1', + domain: urlParts.domain, + }); + } } catch (err) { // Ignore errors. this.logger.error('Error setting cookie', err); diff --git a/src/core/fileuploader/providers/file-handler.ts b/src/core/fileuploader/providers/file-handler.ts index d6fabc9fb..257110258 100644 --- a/src/core/fileuploader/providers/file-handler.ts +++ b/src/core/fileuploader/providers/file-handler.ts @@ -74,7 +74,7 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler { if (this.appProvider.isMobile()) { handler.action = (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise => { - return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, mimetypes).then((result) => { + return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, allowOffline, mimetypes).then((result) => { return { treated: true, result: result diff --git a/src/core/fileuploader/providers/helper.ts b/src/core/fileuploader/providers/helper.ts index 15b237a92..d2b4d07c5 100644 --- a/src/core/fileuploader/providers/helper.ts +++ b/src/core/fileuploader/providers/helper.ts @@ -60,9 +60,10 @@ export class CoreFileUploaderHelperProvider { * @param maxSize Max size of the upload. -1 for no max size. * @param upload True if the file should be uploaded, false to return the picked file. * @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported. + * @param allowOffline True to allow uploading in offline. * @return Promise resolved when done. */ - async chooseAndUploadFile(maxSize: number, upload?: boolean, mimetypes?: string[]): Promise { + async chooseAndUploadFile(maxSize: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise { const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined); @@ -76,9 +77,28 @@ export class CoreFileUploaderHelperProvider { result.name = this.getChosenFileNameFromPath(result) || result.name; } - const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true); + // Verify that the mimetype is supported. + const error = this.fileUploaderProvider.isInvalidMimetype(mimetypes, result.name, result.mediaType); - return this.uploadFile(result.uri, maxSize, true, options); + if (error) { + return Promise.reject(error); + } + + if (upload) { + const size = await this.fileProvider.getExternalFileSize(result.uri); + + await this.confirmUploadFile(size, false, allowOffline); + + const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true); + + return this.uploadFile(result.uri, maxSize, true, options); + } else { + const entry = await this.fileProvider.getExternalFile(result.uri); + + entry.name = result.name; // In Android sometimes the file is exported with a different name, use the original one. + + return entry; + } } /** @@ -664,15 +684,17 @@ export class CoreFileUploaderHelperProvider { * @param name Name to use when uploading the file. If not defined, use the file's name. * @return Promise resolved when done. */ - uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise { + async uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise { if (maxSize != -1 && file.size > maxSize) { return this.errorMaxBytes(maxSize, file.name); } - return this.confirmUploadFile(file.size, false, allowOffline).then(() => { - // We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it. - return this.copyAndUploadFile(file, upload, name); - }); + if (upload) { + await this.confirmUploadFile(file.size, false, allowOffline); + } + + // We have the data of the file to be uploaded, but not its URL (needed). Create a copy of the file to upload it. + return this.copyAndUploadFile(file, upload, name); } /** diff --git a/src/providers/file.ts b/src/providers/file.ts index 173ff4311..dad6b1c8f 100644 --- a/src/providers/file.ts +++ b/src/providers/file.ts @@ -696,6 +696,18 @@ export class CoreFileProvider { }); } + /** + * Calculate the size of a file. + * + * @param path Absolute path to the file. + * @return Promise to be resolved when the size is calculated. + */ + async getExternalFileSize(path: string): Promise { + const fileEntry = await this.getExternalFile(path); + + return this.getSize(fileEntry); + } + /** * Removes a file that might be outside the app's folder. * @@ -770,7 +782,7 @@ export class CoreFileProvider { * @return Promise resolved when the entry is moved. */ moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise { - return this.moveFileOrDir(originalPath, newPath, true, destDirExists); + return this.copyOrMoveFileOrDir(originalPath, newPath, true, false, destDirExists); } /** @@ -783,47 +795,7 @@ export class CoreFileProvider { * @return Promise resolved when the entry is moved. */ moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise { - return this.moveFileOrDir(originalPath, newPath, false, destDirExists); - } - - /** - * Move a file/dir. - * - * @param originalPath Path to the file/dir to move. - * @param newPath New path of the file/dir. - * @param isDir Whether it's a dir or a file. - * @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will - * try to create it (slower). - * @return Promise resolved when the entry is moved. - */ - protected moveFileOrDir(originalPath: string, newPath: string, isDir?: boolean, destDirExists?: boolean): Promise { - const moveFn = isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file); - - return this.init().then(() => { - // Remove basePath if it's in the paths. - originalPath = this.removeStartingSlash(originalPath.replace(this.basePath, '')); - newPath = this.removeStartingSlash(newPath.replace(this.basePath, '')); - - const newPathFileAndDir = this.getFileAndDirectoryFromPath(newPath); - - if (newPathFileAndDir.directory && !destDirExists) { - // Create the target directory if it doesn't exist. - return this.createDir(newPathFileAndDir.directory); - } - }).then(() => { - - return moveFn(this.basePath, originalPath, this.basePath, newPath).catch((error) => { - // The move can fail if the path has encoded characters. Try again if that's the case. - const decodedOriginal = decodeURI(originalPath), - decodedNew = decodeURI(newPath); - - if (decodedOriginal != originalPath || decodedNew != newPath) { - return moveFn(this.basePath, decodedOriginal, this.basePath, decodedNew); - } else { - return Promise.reject(error); - } - }); - }); + return this.copyOrMoveFileOrDir(originalPath, newPath, false, false, destDirExists); } /** @@ -836,7 +808,7 @@ export class CoreFileProvider { * @return Promise resolved when the entry is copied. */ copyDir(from: string, to: string, destDirExists?: boolean): Promise { - return this.copyFileOrDir(from, to, true, destDirExists); + return this.copyOrMoveFileOrDir(from, to, true, true, destDirExists); } /** @@ -849,46 +821,61 @@ export class CoreFileProvider { * @return Promise resolved when the entry is copied. */ copyFile(from: string, to: string, destDirExists?: boolean): Promise { - return this.copyFileOrDir(from, to, false, destDirExists); + return this.copyOrMoveFileOrDir(from, to, false, true, destDirExists); } /** - * Copy a file or a directory. + * Copy or move a file or a directory. * * @param from Path to the file/dir to move. * @param to New path of the file/dir. * @param isDir Whether it's a dir or a file. + * @param copy Whether to copy. If false, it will move the file. * @param destDirExists Set it to true if you know the directory where to put the file/dir exists. If false, the function will * try to create it (slower). * @return Promise resolved when the entry is copied. */ - protected copyFileOrDir(from: string, to: string, isDir?: boolean, destDirExists?: boolean): Promise { - const copyFn = isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file); + protected async copyOrMoveFileOrDir(from: string, to: string, isDir?: boolean, copy?: boolean, destDirExists?: boolean) + : Promise { - return this.init().then(() => { - // Paths cannot start with "/". Remove basePath if present. - from = this.removeStartingSlash(from.replace(this.basePath, '')); - to = this.removeStartingSlash(to.replace(this.basePath, '')); + const fileIsInAppFolder = this.isPathInAppFolder(from); - const toFileAndDir = this.getFileAndDirectoryFromPath(to); + if (!fileIsInAppFolder) { + return this.copyOrMoveExternalFile(from, to, copy); + } - if (toFileAndDir.directory && !destDirExists) { - // Create the target directory if it doesn't exist. - return this.createDir(toFileAndDir.directory); + const moveCopyFn = copy ? + (isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file)) : + (isDir ? this.file.moveDir.bind(this.file) : this.file.moveFile.bind(this.file)); + + await this.init(); + + // Paths cannot start with "/". Remove basePath if present. + from = this.removeStartingSlash(from.replace(this.basePath, '')); + to = this.removeStartingSlash(to.replace(this.basePath, '')); + + const toFileAndDir = this.getFileAndDirectoryFromPath(to); + + if (toFileAndDir.directory && !destDirExists) { + // Create the target directory if it doesn't exist. + await this.createDir(toFileAndDir.directory); + } + + try { + const entry = await moveCopyFn(this.basePath, from, this.basePath, to); + + return entry; + } catch (error) { + // The copy can fail if the path has encoded characters. Try again if that's the case. + const decodedFrom = decodeURI(from); + const decodedTo = decodeURI(to); + + if (from != decodedFrom || to != decodedTo) { + return moveCopyFn(this.basePath, decodedFrom, this.basePath, decodedTo); + } else { + return Promise.reject(error); } - }).then(() => { - return copyFn(this.basePath, from, this.basePath, to).catch((error) => { - // The copy can fail if the path has encoded characters. Try again if that's the case. - const decodedFrom = decodeURI(from), - decodedTo = decodeURI(to); - - if (from != decodedFrom || to != decodedTo) { - return copyFn(this.basePath, decodedFrom, this.basePath, decodedTo); - } else { - return Promise.reject(error); - } - }); - }); + } } /** @@ -1281,6 +1268,16 @@ export class CoreFileProvider { return src.replace(CoreConfigConstants.ioswebviewscheme + '://localhost/_app_file_', 'file://'); } + + /** + * Check if a certain path is in the app's folder (basePath). + * + * @param path Path to check. + * @return Whether it's in the app folder. + */ + protected isPathInAppFolder(path: string): boolean { + return !path || !path.match(/^[a-z0-9]+:\/\//i) || path.indexOf(this.basePath) != -1; + } } export class CoreFile extends makeSingleton(CoreFileProvider) {}