Merge pull request #2421 from dpalou/MOBILE-3401

Mobile 3401
main
Juan Leyva 2020-06-22 16:00:33 +02:00 committed by GitHub
commit 9269d558fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 83 deletions

View File

@ -106,17 +106,19 @@ export class CoreIframeComponent implements OnChanges {
if (changes.src) { if (changes.src) {
const url = this.urlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue; 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. // Save a "fake" cookie for the iframe's domain to fix a bug in WKWebView.
try { try {
const win = <WKWebViewCookiesWindow> window; const win = <WKWebViewCookiesWindow> window;
const urlParts = CoreUrl.parse(url); const urlParts = CoreUrl.parse(url);
if (urlParts.domain) {
await win.WKWebViewCookies.setCookie({ await win.WKWebViewCookies.setCookie({
name: 'MoodleAppCookieForWKWebView', name: 'MoodleAppCookieForWKWebView',
value: '1', value: '1',
domain: urlParts.domain, domain: urlParts.domain,
}); });
}
} catch (err) { } catch (err) {
// Ignore errors. // Ignore errors.
this.logger.error('Error setting cookie', err); this.logger.error('Error setting cookie', err);

View File

@ -74,7 +74,7 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler {
if (this.appProvider.isMobile()) { if (this.appProvider.isMobile()) {
handler.action = (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> => { handler.action = (maxSize?: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> => {
return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, mimetypes).then((result) => { return this.uploaderHelper.chooseAndUploadFile(maxSize, upload, allowOffline, mimetypes).then((result) => {
return { return {
treated: true, treated: true,
result: result result: result

View File

@ -60,9 +60,10 @@ export class CoreFileUploaderHelperProvider {
* @param maxSize Max size of the upload. -1 for no max size. * @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 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 mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
* @param allowOffline True to allow uploading in offline.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
async chooseAndUploadFile(maxSize: number, upload?: boolean, mimetypes?: string[]): Promise<any> { async chooseAndUploadFile(maxSize: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> {
const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined); const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined);
@ -76,9 +77,28 @@ export class CoreFileUploaderHelperProvider {
result.name = this.getChosenFileNameFromPath(result) || result.name; result.name = this.getChosenFileNameFromPath(result) || result.name;
} }
// Verify that the mimetype is supported.
const error = this.fileUploaderProvider.isInvalidMimetype(mimetypes, result.name, result.mediaType);
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); const options = this.fileUploaderProvider.getFileUploadOptions(result.uri, result.name, result.mediaType, true);
return this.uploadFile(result.uri, maxSize, true, options); 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. * @param name Name to use when uploading the file. If not defined, use the file's name.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> { async uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
if (maxSize != -1 && file.size > maxSize) { if (maxSize != -1 && file.size > maxSize) {
return this.errorMaxBytes(maxSize, file.name); return this.errorMaxBytes(maxSize, file.name);
} }
return this.confirmUploadFile(file.size, false, allowOffline).then(() => { 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. // 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); return this.copyAndUploadFile(file, upload, name);
});
} }
/** /**

View File

@ -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<number> {
const fileEntry = await this.getExternalFile(path);
return this.getSize(fileEntry);
}
/** /**
* Removes a file that might be outside the app's folder. * 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. * @return Promise resolved when the entry is moved.
*/ */
moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> { moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
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. * @return Promise resolved when the entry is moved.
*/ */
moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> { moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
return this.moveFileOrDir(originalPath, newPath, false, destDirExists); return this.copyOrMoveFileOrDir(originalPath, newPath, false, 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<any> {
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);
}
});
});
} }
/** /**
@ -836,7 +808,7 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is copied. * @return Promise resolved when the entry is copied.
*/ */
copyDir(from: string, to: string, destDirExists?: boolean): Promise<any> { copyDir(from: string, to: string, destDirExists?: boolean): Promise<any> {
return this.copyFileOrDir(from, to, true, destDirExists); return this.copyOrMoveFileOrDir(from, to, true, true, destDirExists);
} }
/** /**
@ -849,23 +821,35 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is copied. * @return Promise resolved when the entry is copied.
*/ */
copyFile(from: string, to: string, destDirExists?: boolean): Promise<any> { copyFile(from: string, to: string, destDirExists?: boolean): Promise<any> {
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 from Path to the file/dir to move.
* @param to New path of the file/dir. * @param to New path of the file/dir.
* @param isDir Whether it's a dir or a file. * @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 * @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). * try to create it (slower).
* @return Promise resolved when the entry is copied. * @return Promise resolved when the entry is copied.
*/ */
protected copyFileOrDir(from: string, to: string, isDir?: boolean, destDirExists?: boolean): Promise<any> { protected async copyOrMoveFileOrDir(from: string, to: string, isDir?: boolean, copy?: boolean, destDirExists?: boolean)
const copyFn = isDir ? this.file.copyDir.bind(this.file) : this.file.copyFile.bind(this.file); : Promise<Entry> {
const fileIsInAppFolder = this.isPathInAppFolder(from);
if (!fileIsInAppFolder) {
return this.copyOrMoveExternalFile(from, to, copy);
}
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();
return this.init().then(() => {
// Paths cannot start with "/". Remove basePath if present. // Paths cannot start with "/". Remove basePath if present.
from = this.removeStartingSlash(from.replace(this.basePath, '')); from = this.removeStartingSlash(from.replace(this.basePath, ''));
to = this.removeStartingSlash(to.replace(this.basePath, '')); to = this.removeStartingSlash(to.replace(this.basePath, ''));
@ -874,21 +858,24 @@ export class CoreFileProvider {
if (toFileAndDir.directory && !destDirExists) { if (toFileAndDir.directory && !destDirExists) {
// Create the target directory if it doesn't exist. // Create the target directory if it doesn't exist.
return this.createDir(toFileAndDir.directory); await this.createDir(toFileAndDir.directory);
} }
}).then(() => {
return copyFn(this.basePath, from, this.basePath, to).catch((error) => { 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. // The copy can fail if the path has encoded characters. Try again if that's the case.
const decodedFrom = decodeURI(from), const decodedFrom = decodeURI(from);
decodedTo = decodeURI(to); const decodedTo = decodeURI(to);
if (from != decodedFrom || to != decodedTo) { if (from != decodedFrom || to != decodedTo) {
return copyFn(this.basePath, decodedFrom, this.basePath, decodedTo); return moveCopyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
} else { } else {
return Promise.reject(error); return Promise.reject(error);
} }
}); }
});
} }
/** /**
@ -1281,6 +1268,16 @@ export class CoreFileProvider {
return src.replace(CoreConfigConstants.ioswebviewscheme + '://localhost/_app_file_', 'file://'); 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) {} export class CoreFile extends makeSingleton(CoreFileProvider) {}