MOBILE-2261 core: Implement plugin file delegate

main
Dani Palou 2017-12-13 16:15:54 +01:00
parent 93568a9c97
commit 0519875949
3 changed files with 147 additions and 6 deletions

View File

@ -49,6 +49,7 @@ import { CoreCronDelegate } from '../providers/cron';
import { CoreFileSessionProvider } from '../providers/file-session'; import { CoreFileSessionProvider } from '../providers/file-session';
import { CoreFilepoolProvider } from '../providers/filepool'; import { CoreFilepoolProvider } from '../providers/filepool';
import { CoreUpdateManagerProvider } from '../providers/update-manager'; import { CoreUpdateManagerProvider } from '../providers/update-manager';
import { CorePluginFileDelegate } from '../providers/plugin-file-delegate';
// For translate loader. AoT requires an exported function for factories. // For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient) {
@ -105,6 +106,7 @@ export function createTranslateLoader(http: HttpClient) {
CoreFileSessionProvider, CoreFileSessionProvider,
CoreFilepoolProvider, CoreFilepoolProvider,
CoreUpdateManagerProvider, CoreUpdateManagerProvider,
CorePluginFileDelegate
] ]
}) })
export class AppModule { export class AppModule {

View File

@ -19,6 +19,7 @@ import { CoreEventsProvider } from './events';
import { CoreFileProvider } from './file'; import { CoreFileProvider } from './file';
import { CoreInitDelegate } from './init'; import { CoreInitDelegate } from './init';
import { CoreLoggerProvider } from './logger'; import { CoreLoggerProvider } from './logger';
import { CorePluginFileDelegate } from './plugin-file-delegate';
import { CoreSitesProvider } from './sites'; import { CoreSitesProvider } from './sites';
import { CoreWSProvider } from './ws'; import { CoreWSProvider } from './ws';
import { CoreMimetypeUtilsProvider } from './utils/mimetype'; import { CoreMimetypeUtilsProvider } from './utils/mimetype';
@ -276,7 +277,6 @@ export class CoreFilepoolProvider {
new RegExp('(\\?|&)preview=[A-Za-z0-9]+'), new RegExp('(\\?|&)preview=[A-Za-z0-9]+'),
new RegExp('(\\?|&)offline=[0-1]', 'g') new RegExp('(\\?|&)offline=[0-1]', 'g')
]; ];
protected revisionRegex = new RegExp('/content/([0-9]+)/');
protected queueDeferreds = {}; // To handle file downloads using the queue. protected queueDeferreds = {}; // To handle file downloads using the queue.
protected sizeCache = {}; // A "cache" to store file sizes to prevent performing too many HEAD requests. 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. // 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 sitesProvider: CoreSitesProvider, private wsProvider: CoreWSProvider, private textUtils: CoreTextUtilsProvider,
private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider, private urlUtils: CoreUrlUtilsProvider,
private timeUtils: CoreTimeUtilsProvider, private eventsProvider: CoreEventsProvider, initDelegate: CoreInitDelegate, private timeUtils: CoreTimeUtilsProvider, private eventsProvider: CoreEventsProvider, initDelegate: CoreInitDelegate,
network: Network) { network: Network, private pluginFileDelegate: CorePluginFileDelegate) {
this.logger = logger.getInstance('CoreFilepoolProvider'); this.logger = logger.getInstance('CoreFilepoolProvider');
this.appDB = this.appProvider.getDB(); 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. * Get the deferred object for a file in the queue.
* *
@ -1862,11 +1885,22 @@ export class CoreFilepoolProvider {
* @return {number} Revision number. * @return {number} Revision number.
*/ */
protected getRevisionFromUrl(url: string) : 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') { if (matches && typeof matches[1] != 'undefined') {
return parseInt(matches[1], 10); return parseInt(matches[1], 10);
} }
return -1; return 0;
} }
/** /**
@ -1959,7 +1993,7 @@ export class CoreFilepoolProvider {
filename = 'gravatar_' + this.urlUtils.getLastFileWithoutParams(fileUrl); filename = 'gravatar_' + this.urlUtils.getLastFileWithoutParams(fileUrl);
} else if (this.urlUtils.isThemeImageUrl(fileUrl)) { } else if (this.urlUtils.isThemeImageUrl(fileUrl)) {
// Extract user ID. // Extract user ID.
const matches = fileUrl.match(/clean\/core\/([^\/]*)\//); const matches = fileUrl.match(/\/core\/([^\/]*)\//);
if (matches && matches[1]) { if (matches && matches[1]) {
filename = 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. * 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 { 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);
} }
/** /**

View File

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