2019-09-26 12:43:29 +02:00
|
|
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
2017-12-13 16:15:54 +01:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
import { CoreLoggerProvider } from './logger';
|
2019-11-05 13:00:05 +01:00
|
|
|
import { CoreWSExternalFile } from '@providers/ws';
|
2019-11-25 15:05:49 +01:00
|
|
|
import { FileEntry } from '@ionic-native/file';
|
2017-12-13 16:15:54 +01:00
|
|
|
|
2018-01-15 08:22:00 +01:00
|
|
|
/**
|
|
|
|
* Interface that all plugin file handlers must implement.
|
|
|
|
*/
|
2017-12-13 16:15:54 +01:00
|
|
|
export interface CorePluginFileHandler {
|
2018-01-15 08:22:00 +01:00
|
|
|
/**
|
2018-06-01 12:32:22 +02:00
|
|
|
* A name to identify the handler.
|
2018-01-15 08:22:00 +01:00
|
|
|
*/
|
|
|
|
name: string;
|
|
|
|
|
2018-06-01 12:32:22 +02:00
|
|
|
/**
|
|
|
|
* The "component" of the handler. It should match the "component" of pluginfile URLs.
|
2019-11-05 13:00:05 +01:00
|
|
|
* It is used to treat revision from URLs.
|
2018-06-01 12:32:22 +02:00
|
|
|
*/
|
2019-11-05 13:00:05 +01:00
|
|
|
component?: string;
|
2018-06-01 12:32:22 +02:00
|
|
|
|
2018-01-15 08:22:00 +01:00
|
|
|
/**
|
|
|
|
* Return the RegExp to match the revision on pluginfile URLs.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
|
|
|
* @return RegExp to match the revision on pluginfile URLs.
|
2018-01-15 08:22:00 +01:00
|
|
|
*/
|
|
|
|
getComponentRevisionRegExp?(args: string[]): RegExp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Should return the string to remove the revision on pluginfile url.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
|
|
|
* @return String to remove the revision on pluginfile url.
|
2018-01-15 08:22:00 +01:00
|
|
|
*/
|
|
|
|
getComponentRevisionReplace?(args: string[]): string;
|
2019-11-05 13:00:05 +01:00
|
|
|
|
2019-11-25 16:08:58 +01:00
|
|
|
/**
|
|
|
|
* React to a file being deleted.
|
|
|
|
*
|
|
|
|
* @param fileUrl The file URL used to download the file.
|
|
|
|
* @param path The path of the deleted file.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
fileDeleted?(fileUrl: string, path: string, siteId?: string): Promise<any>;
|
|
|
|
|
2019-12-04 13:37:00 +01:00
|
|
|
/**
|
|
|
|
* Check whether a file can be downloaded. If so, return the file to download.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with the file to use. Rejected if cannot download.
|
|
|
|
*/
|
|
|
|
getDownloadableFile?(file: CoreWSExternalFile, siteId?: string): Promise<CoreWSExternalFile>;
|
|
|
|
|
2019-11-05 13:00:05 +01:00
|
|
|
/**
|
|
|
|
* Given an HTML element, get the URLs of the files that should be downloaded and weren't treated by
|
|
|
|
* CoreDomUtilsProvider.extractDownloadableFilesFromHtml.
|
|
|
|
*
|
|
|
|
* @param container Container where to get the URLs from.
|
|
|
|
* @return {string[]} List of URLs.
|
|
|
|
*/
|
2019-12-04 13:37:00 +01:00
|
|
|
getDownloadableFilesFromHTML?(container: HTMLElement): string[];
|
2019-11-05 13:00:05 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a file size.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with the size.
|
|
|
|
*/
|
|
|
|
getFileSize?(file: CoreWSExternalFile, siteId?: string): Promise<number>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @return Whether the file should be treated by this handler.
|
|
|
|
*/
|
|
|
|
shouldHandleFile?(file: CoreWSExternalFile): boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Treat a downloaded file.
|
|
|
|
*
|
|
|
|
* @param fileUrl The file URL used to download the file.
|
|
|
|
* @param file The file entry of the downloaded file.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string): Promise<any>;
|
2018-01-29 10:05:20 +01:00
|
|
|
}
|
2017-12-13 16:15:54 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Delegate to register pluginfile information handlers.
|
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class CorePluginFileDelegate {
|
|
|
|
protected logger;
|
2018-01-29 10:05:20 +01:00
|
|
|
protected handlers: { [s: string]: CorePluginFileHandler } = {};
|
2017-12-13 16:15:54 +01:00
|
|
|
|
|
|
|
constructor(logger: CoreLoggerProvider) {
|
|
|
|
this.logger = logger.getInstance('CorePluginFileDelegate');
|
|
|
|
}
|
|
|
|
|
2019-12-04 13:37:00 +01:00
|
|
|
/**
|
|
|
|
* React to a file being deleted.
|
|
|
|
*
|
|
|
|
* @param fileUrl The file URL used to download the file.
|
|
|
|
* @param path The path of the deleted file.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
fileDeleted(fileUrl: string, path: string, siteId?: string): Promise<any> {
|
|
|
|
const handler = this.getHandlerForFile({fileurl: fileUrl});
|
|
|
|
|
|
|
|
if (handler && handler.fileDeleted) {
|
|
|
|
return handler.fileDeleted(fileUrl, path, siteId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:00:05 +01:00
|
|
|
/**
|
|
|
|
* Check whether a file can be downloaded. If so, return the file to download.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with the file to use. Rejected if cannot download.
|
|
|
|
*/
|
2019-12-04 13:37:00 +01:00
|
|
|
getDownloadableFile(file: CoreWSExternalFile, siteId?: string): Promise<CoreWSExternalFile> {
|
2019-11-05 13:00:05 +01:00
|
|
|
const handler = this.getHandlerForFile(file);
|
|
|
|
|
2019-12-04 13:37:00 +01:00
|
|
|
return this.getHandlerDownloadableFile(file, handler, siteId);
|
2019-11-05 13:00:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether a file can be downloaded. If so, return the file to download.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @param handler The handler to use.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with the file to use. Rejected if cannot download.
|
|
|
|
*/
|
2019-12-04 13:37:00 +01:00
|
|
|
protected getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string)
|
2019-11-05 13:00:05 +01:00
|
|
|
: Promise<CoreWSExternalFile> {
|
|
|
|
|
2019-12-04 13:37:00 +01:00
|
|
|
if (handler && handler.getDownloadableFile) {
|
|
|
|
return handler.getDownloadableFile(file, siteId).then((newFile) => {
|
2019-11-05 13:00:05 +01:00
|
|
|
return newFile || file;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve(file);
|
|
|
|
}
|
|
|
|
|
2017-12-13 16:15:54 +01:00
|
|
|
/**
|
|
|
|
* Get the handler for a certain pluginfile url.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param component Component of the plugin.
|
|
|
|
* @return Handler. Undefined if no handler found for the plugin.
|
2017-12-13 16:15:54 +01:00
|
|
|
*/
|
2018-06-01 12:32:22 +02:00
|
|
|
protected getPluginHandler(component: string): CorePluginFileHandler {
|
|
|
|
if (typeof this.handlers[component] != 'undefined') {
|
|
|
|
return this.handlers[component];
|
2017-12-13 16:15:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the RegExp of the component and filearea described in the URL.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
|
|
|
* @return RegExp to match the revision or undefined if not found.
|
2017-12-13 16:15:54 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getComponentRevisionRegExp(args: string[]): RegExp {
|
2017-12-13 16:15:54 +01:00
|
|
|
// Get handler based on component (args[1]).
|
|
|
|
const handler = this.getPluginHandler(args[1]);
|
|
|
|
|
|
|
|
if (handler && handler.getComponentRevisionRegExp) {
|
|
|
|
return handler.getComponentRevisionRegExp(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:00:05 +01:00
|
|
|
/**
|
|
|
|
* Given an HTML element, get the URLs of the files that should be downloaded and weren't treated by
|
|
|
|
* CoreDomUtilsProvider.extractDownloadableFilesFromHtml.
|
|
|
|
*
|
|
|
|
* @param container Container where to get the URLs from.
|
|
|
|
* @return List of URLs.
|
|
|
|
*/
|
2019-12-04 13:37:00 +01:00
|
|
|
getDownloadableFilesFromHTML(container: HTMLElement): string[] {
|
2019-11-05 13:00:05 +01:00
|
|
|
let files = [];
|
|
|
|
|
|
|
|
for (const component in this.handlers) {
|
|
|
|
const handler = this.handlers[component];
|
|
|
|
|
2019-12-04 13:37:00 +01:00
|
|
|
if (handler && handler.getDownloadableFilesFromHTML) {
|
|
|
|
files = files.concat(handler.getDownloadableFilesFromHTML(container));
|
2019-11-05 13:00:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sum the filesizes from a list of files checking if the size will be partial or totally calculated.
|
|
|
|
*
|
|
|
|
* @param files List of files to sum its filesize.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with file size and a boolean to indicate if it is the total size or only partial.
|
|
|
|
*/
|
|
|
|
getFilesSize(files: CoreWSExternalFile[], siteId?: string): Promise<{ size: number, total: boolean }> {
|
|
|
|
const promises = [],
|
|
|
|
result = {
|
|
|
|
size: 0,
|
|
|
|
total: true
|
|
|
|
};
|
|
|
|
|
|
|
|
files.forEach((file) => {
|
|
|
|
promises.push(this.getFileSize(file, siteId).then((size) => {
|
|
|
|
if (typeof size == 'undefined') {
|
|
|
|
// We don't have the file size, cannot calculate its total size.
|
|
|
|
result.total = false;
|
|
|
|
} else {
|
|
|
|
result.size += size;
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
return result;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a file size.
|
|
|
|
*
|
|
|
|
* @param file The file data.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved with the size.
|
|
|
|
*/
|
|
|
|
getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
|
|
|
|
const handler = this.getHandlerForFile(file);
|
|
|
|
|
|
|
|
// First of all check if file can be downloaded.
|
2019-12-04 13:37:00 +01:00
|
|
|
return this.getHandlerDownloadableFile(file, handler, siteId).then((file) => {
|
|
|
|
if (!file) {
|
2019-11-05 13:00:05 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handler && handler.getFileSize) {
|
|
|
|
return handler.getFileSize(file, siteId).catch(() => {
|
|
|
|
return file.filesize;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve(file.filesize);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a handler to treat a certain file.
|
|
|
|
*
|
|
|
|
* @param file File data.
|
|
|
|
* @return Handler.
|
|
|
|
*/
|
|
|
|
protected getHandlerForFile(file: CoreWSExternalFile): CorePluginFileHandler {
|
|
|
|
for (const component in this.handlers) {
|
|
|
|
const handler = this.handlers[component];
|
|
|
|
|
|
|
|
if (handler && handler.shouldHandleFile && handler.shouldHandleFile(file)) {
|
|
|
|
return handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 16:15:54 +01:00
|
|
|
/**
|
|
|
|
* Register a handler.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param handler The handler to register.
|
|
|
|
* @return True if registered successfully, false otherwise.
|
2017-12-13 16:15:54 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
registerHandler(handler: CorePluginFileHandler): boolean {
|
2019-11-05 13:00:05 +01:00
|
|
|
if (typeof this.handlers[handler.component || handler.name] !== 'undefined') {
|
2018-06-01 12:32:22 +02:00
|
|
|
this.logger.log(`Handler '${handler.component}' already registered`);
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2017-12-13 16:15:54 +01:00
|
|
|
return false;
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-06-01 12:32:22 +02:00
|
|
|
this.logger.log(`Registered handler '${handler.component}'`);
|
2019-11-05 13:00:05 +01:00
|
|
|
this.handlers[handler.component || handler.name] = handler;
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2017-12-13 16:15:54 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the revision number from a file URL.
|
|
|
|
*
|
2019-09-10 16:48:56 +02:00
|
|
|
* @param url URL to be replaced.
|
|
|
|
* @param args Arguments of the pluginfile URL defining component and filearea at least.
|
|
|
|
* @return Replaced URL without revision.
|
2017-12-13 16:15:54 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
removeRevisionFromUrl(url: string, args: string[]): string {
|
2017-12-13 16:15:54 +01:00
|
|
|
// Get handler based on component (args[1]).
|
|
|
|
const handler = this.getPluginHandler(args[1]);
|
|
|
|
|
|
|
|
if (handler && handler.getComponentRevisionRegExp && handler.getComponentRevisionReplace) {
|
|
|
|
const revisionRegex = handler.getComponentRevisionRegExp(args);
|
|
|
|
if (revisionRegex) {
|
|
|
|
return url.replace(revisionRegex, handler.getComponentRevisionReplace(args));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
2019-11-05 13:00:05 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Treat a downloaded file.
|
|
|
|
*
|
|
|
|
* @param fileUrl The file URL used to download the file.
|
|
|
|
* @param file The file entry of the downloaded file.
|
|
|
|
* @param siteId Site ID. If not defined, current site.
|
|
|
|
* @return Promise resolved when done.
|
|
|
|
*/
|
|
|
|
treatDownloadedFile(fileUrl: string, file: FileEntry, siteId?: string): Promise<any> {
|
|
|
|
const handler = this.getHandlerForFile({fileurl: fileUrl});
|
|
|
|
|
2019-11-25 16:08:58 +01:00
|
|
|
if (handler && handler.treatDownloadedFile) {
|
2019-11-05 13:00:05 +01:00
|
|
|
return handler.treatDownloadedFile(fileUrl, file, siteId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
}
|