diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e01e009b3..ae7fe3573 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -49,6 +49,7 @@ import { CoreCronDelegate } from '../providers/cron'; import { CoreFileSessionProvider } from '../providers/file-session'; import { CoreFilepoolProvider } from '../providers/filepool'; import { CoreUpdateManagerProvider } from '../providers/update-manager'; +import { CorePluginFileDelegate } from '../providers/plugin-file-delegate'; // For translate loader. AoT requires an exported function for factories. export function createTranslateLoader(http: HttpClient) { @@ -105,6 +106,7 @@ export function createTranslateLoader(http: HttpClient) { CoreFileSessionProvider, CoreFilepoolProvider, CoreUpdateManagerProvider, + CorePluginFileDelegate ] }) export class AppModule { diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index d83065479..1dfb5166b 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -19,6 +19,7 @@ import { CoreEventsProvider } from './events'; import { CoreFileProvider } from './file'; import { CoreInitDelegate } from './init'; import { CoreLoggerProvider } from './logger'; +import { CorePluginFileDelegate } from './plugin-file-delegate'; import { CoreSitesProvider } from './sites'; import { CoreWSProvider } from './ws'; import { CoreMimetypeUtilsProvider } from './utils/mimetype'; @@ -276,7 +277,6 @@ export class CoreFilepoolProvider { new RegExp('(\\?|&)preview=[A-Za-z0-9]+'), new RegExp('(\\?|&)offline=[0-1]', 'g') ]; - protected revisionRegex = new RegExp('/content/([0-9]+)/'); protected queueDeferreds = {}; // To handle file downloads using the queue. protected sizeCache = {}; // A "cache" to store file sizes to prevent performing too many HEAD requests. // Variables to prevent downloading packages/files twice at the same time. @@ -287,7 +287,7 @@ export class CoreFilepoolProvider { private sitesProvider: CoreSitesProvider, private wsProvider: CoreWSProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private eventsProvider: CoreEventsProvider, initDelegate: CoreInitDelegate, - network: Network) { + network: Network, private pluginFileDelegate: CorePluginFileDelegate) { this.logger = logger.getInstance('CoreFilepoolProvider'); this.appDB = this.appProvider.getDB(); @@ -1784,6 +1784,29 @@ export class CoreFilepoolProvider { }); } + /** + * Return the array of arguments of the pluginfile url. + * + * @param {string} url URL to get the args. + * @return {string[]} The args found, undefined if not a pluginfile. + */ + protected getPluginFileArgs(url: string) : string[] { + if (!this.urlUtils.isPluginFileUrl(url)) { + // Not pluginfile, return. + return; + } + + let relativePath = url.substr(url.indexOf('/pluginfile.php') + 16), + args = relativePath.split('/'); + + if (args.length < 3) { + // To be a plugin file it should have at least contextId, Component and Filearea. + return; + } + + return args; + } + /** * Get the deferred object for a file in the queue. * @@ -1862,11 +1885,22 @@ export class CoreFilepoolProvider { * @return {number} Revision number. */ protected getRevisionFromUrl(url: string) : number { - const matches = url.match(this.revisionRegex); + let args = this.getPluginFileArgs(url); + if (!args) { + // Not a pluginfile, no revision will be found. + return 0; + } + + let revisionRegex = this.pluginFileDelegate.getComponentRevisionRegExp(args); + if (!revisionRegex) { + return 0; + } + + let matches = url.match(revisionRegex); if (matches && typeof matches[1] != 'undefined') { return parseInt(matches[1], 10); } - return -1; + return 0; } /** @@ -1959,7 +1993,7 @@ export class CoreFilepoolProvider { filename = 'gravatar_' + this.urlUtils.getLastFileWithoutParams(fileUrl); } else if (this.urlUtils.isThemeImageUrl(fileUrl)) { // Extract user ID. - const matches = fileUrl.match(/clean\/core\/([^\/]*)\//); + const matches = fileUrl.match(/\/core\/([^\/]*)\//); if (matches && matches[1]) { filename = matches[1]; } @@ -2431,7 +2465,13 @@ export class CoreFilepoolProvider { * The revision is used to know if a file has changed. We remove it from the URL to prevent storing a file per revision. */ protected removeRevisionFromUrl(url: string) : string { - return url.replace(this.revisionRegex, '/content/0/'); + let args = this.getPluginFileArgs(url); + if (!args) { + // Not a pluginfile, no revision will be found. + return url; + } + + return this.pluginFileDelegate.removeRevisionFromUrl(url, args); } /** diff --git a/src/providers/plugin-file-delegate.ts b/src/providers/plugin-file-delegate.ts new file mode 100644 index 000000000..e685374e5 --- /dev/null +++ b/src/providers/plugin-file-delegate.ts @@ -0,0 +1,99 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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'; + +export interface CorePluginFileHandler { + name: string; // Name of the handler. + getComponentRevisionRegExp?(args: string[]): RegExp; // Should return the RegExp to match revision on pluginfile url. + getComponentRevisionReplace?(args: string[]): string; // Should return the String to remove the revision on pluginfile url. +}; + +/** + * Delegate to register pluginfile information handlers. + */ +@Injectable() +export class CorePluginFileDelegate { + protected logger; + protected handlers: {[s: string]: CorePluginFileHandler} = {}; + + constructor(logger: CoreLoggerProvider) { + this.logger = logger.getInstance('CorePluginFileDelegate'); + } + + /** + * Get the handler for a certain pluginfile url. + * + * @param {string} pluginType Type of the plugin. + * @return {CorePluginFileHandler} Handler. Undefined if no handler found for the plugin. + */ + protected getPluginHandler(pluginType: string) : CorePluginFileHandler { + if (typeof this.handlers[pluginType] != 'undefined') { + return this.handlers[pluginType]; + } + } + + /** + * Get the RegExp of the component and filearea described in the URL. + * + * @param {string[]} args Arguments of the pluginfile URL defining component and filearea at least. + * @return {RegExp} RegExp to match the revision or undefined if not found. + */ + getComponentRevisionRegExp(args: string[]) : RegExp { + // Get handler based on component (args[1]). + const handler = this.getPluginHandler(args[1]); + + if (handler && handler.getComponentRevisionRegExp) { + return handler.getComponentRevisionRegExp(args); + } + } + + /** + * Register a handler. + * + * @param {CorePluginFileHandler} handler The handler to register. + * @return {boolean} True if registered successfully, false otherwise. + */ + registerHandler(handler: CorePluginFileHandler) : boolean { + if (typeof this.handlers[handler.name] !== 'undefined') { + this.logger.log(`Addon 'handler.name' already registered`); + return false; + } + this.logger.log(`Registered addon 'handler.name'`); + this.handlers[handler.name] = handler; + return true; + } + + /** + * Removes the revision number from a file URL. + * + * @param {string} url URL to be replaced. + * @param {string[]} args Arguments of the pluginfile URL defining component and filearea at least. + * @return {string} Replaced URL without revision. + */ + removeRevisionFromUrl(url: string, args: string[]) : string { + // 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; + } +} \ No newline at end of file