Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/components/iframe/iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <WKWebViewCookiesWindow> 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);
Expand Down
2 changes: 1 addition & 1 deletion src/core/fileuploader/providers/file-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class CoreFileUploaderFileHandler implements CoreFileUploaderHandler {

if (this.appProvider.isMobile()) {
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 {
treated: true,
result: result
Expand Down
38 changes: 30 additions & 8 deletions src/core/fileuploader/providers/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> {
async chooseAndUploadFile(maxSize: number, upload?: boolean, allowOffline?: boolean, mimetypes?: string[]): Promise<any> {

const result = await this.fileChooser.getFile(mimetypes ? mimetypes.join(',') : undefined);

Expand All @@ -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);

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);
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;
}
}

/**
Expand Down Expand Up @@ -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<any> {
async uploadFileObject(file: any, maxSize?: number, upload?: boolean, allowOffline?: boolean, name?: string): Promise<any> {
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);
}

/**
Expand Down
133 changes: 65 additions & 68 deletions src/providers/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -770,7 +782,7 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is moved.
*/
moveDir(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
return this.moveFileOrDir(originalPath, newPath, true, destDirExists);
return this.copyOrMoveFileOrDir(originalPath, newPath, true, false, destDirExists);
}

/**
Expand All @@ -783,47 +795,7 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is moved.
*/
moveFile(originalPath: string, newPath: string, destDirExists?: boolean): Promise<any> {
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<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);
}
});
});
return this.copyOrMoveFileOrDir(originalPath, newPath, false, false, destDirExists);
}

/**
Expand All @@ -836,7 +808,7 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is copied.
*/
copyDir(from: string, to: string, destDirExists?: boolean): Promise<any> {
return this.copyFileOrDir(from, to, true, destDirExists);
return this.copyOrMoveFileOrDir(from, to, true, true, destDirExists);
}

/**
Expand All @@ -849,46 +821,61 @@ export class CoreFileProvider {
* @return Promise resolved when the entry is copied.
*/
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 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<any> {
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<Entry> {

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);
}
}).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);
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));

if (from != decodedFrom || to != decodedTo) {
return copyFn(this.basePath, decodedFrom, this.basePath, decodedTo);
} else {
return Promise.reject(error);
}
});
});
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);
}
}
}

/**
Expand Down Expand Up @@ -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) {}