Merge pull request #2273 from dpalou/MOBILE-3332

Mobile 3332
main
Juan Leyva 2020-02-10 18:23:07 +01:00 committed by GitHub
commit e245e50c8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 321 additions and 60 deletions

View File

@ -1619,6 +1619,7 @@
"core.h5p.offlineDialogRetryButtonLabel": "h5p",
"core.h5p.offlineDialogRetryMessage": "h5p",
"core.h5p.offlineSuccessfulSubmit": "h5p",
"core.h5p.offlinedisabled": "local_moodlemobileapp",
"core.h5p.originator": "h5p",
"core.h5p.pd": "h5p",
"core.h5p.pddl": "h5p",

View File

@ -119,6 +119,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
protected fetchContent(refresh?: boolean): Promise<any> {
const promises = [];
let downloadFailed = false;
let downloadFailError;
// Try to get the book data.
promises.push(this.bookProvider.getBook(this.courseId, this.module.id).then((book) => {
@ -129,9 +130,10 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
}));
// Download content. This function also loads module contents if needed.
promises.push(this.prefetchDelegate.download(this.module, this.courseId).catch(() => {
promises.push(this.prefetchDelegate.download(this.module, this.courseId).catch((error) => {
// Mark download as failed but go on since the main files could have been downloaded.
downloadFailed = true;
downloadFailError = error;
if (!this.module.contents.length) {
// Try to load module contents for offline usage.
@ -163,7 +165,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
return this.loadChapter(this.currentChapter).then(() => {
if (downloadFailed && this.appProvider.isOnline()) {
// We could load the main file but the download failed. Show error message.
this.domUtils.showErrorModal('core.errordownloadingsomefiles', true);
this.showErrorDownloadingSomeFiles(downloadFailError);
}
}).catch(() => {
// Ignore errors, they're handled inside the loadChapter function.

View File

@ -76,6 +76,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
*/
protected fetchContent(refresh?: boolean): Promise<any> {
let downloadFailed = false;
let downloadFailError;
const promises = [];
promises.push(this.imscpProvider.getImscp(this.courseId, this.module.id).then((imscp) => {
@ -83,9 +84,10 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
this.dataRetrieved.emit(imscp);
}));
promises.push(this.imscpPrefetch.download(this.module, this.courseId).catch(() => {
promises.push(this.imscpPrefetch.download(this.module, this.courseId).catch((error) => {
// Mark download as failed but go on since the main files could have been downloaded.
downloadFailed = true;
downloadFailError = error;
return this.courseProvider.loadModuleContents(this.module, this.courseId).catch((error) => {
// Error getting module contents, fail.
@ -109,7 +111,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
}).then(() => {
if (downloadFailed && this.appProvider.isOnline()) {
// We could load the main file but the download failed. Show error message.
this.domUtils.showErrorModal('core.errordownloadingsomefiles', true);
this.showErrorDownloadingSomeFiles(downloadFailError);
}
}).finally(() => {

View File

@ -78,11 +78,13 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
*/
protected fetchContent(refresh?: boolean): Promise<any> {
let downloadFailed = false;
let downloadFailError;
// Download content. This function also loads module contents if needed.
return this.pagePrefetch.download(this.module, this.courseId).catch(() => {
return this.pagePrefetch.download(this.module, this.courseId).catch((error) => {
// Mark download as failed but go on since the main files could have been downloaded.
downloadFailed = true;
downloadFailError = error;
}).then(() => {
if (!this.module.contents.length) {
// Try to load module contents for offline usage.
@ -132,7 +134,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
if (downloadFailed && this.appProvider.isOnline()) {
// We could load the main file but the download failed. Show error message.
this.domUtils.showErrorModal('core.errordownloadingsomefiles', true);
this.showErrorDownloadingSomeFiles(downloadFailError);
}
}));

View File

@ -14,6 +14,7 @@
import { Component, Injector } from '@angular/core';
import { CoreAppProvider } from '@providers/app';
import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCourseProvider } from '@core/course/providers/course';
@ -38,10 +39,15 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
contentText: string;
displayDescription = true;
constructor(injector: Injector, private resourceProvider: AddonModResourceProvider, private courseProvider: CoreCourseProvider,
private appProvider: CoreAppProvider, private prefetchHandler: AddonModResourcePrefetchHandler,
private resourceHelper: AddonModResourceHelperProvider, private sitesProvider: CoreSitesProvider,
private utils: CoreUtilsProvider) {
constructor(injector: Injector,
protected resourceProvider: AddonModResourceProvider,
protected courseProvider: CoreCourseProvider,
protected appProvider: CoreAppProvider,
protected prefetchHandler: AddonModResourcePrefetchHandler,
protected resourceHelper: AddonModResourceHelperProvider,
protected sitesProvider: CoreSitesProvider,
protected utils: CoreUtilsProvider,
protected filepoolProvider: CoreFilepoolProvider) {
super(injector);
}
@ -104,10 +110,12 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
if (this.resourceHelper.isDisplayedInIframe(this.module)) {
let downloadFailed = false;
let downloadFailError;
return this.prefetchHandler.download(this.module, this.courseId).catch(() => {
return this.prefetchHandler.download(this.module, this.courseId).catch((error) => {
// Mark download as failed but go on since the main files could have been downloaded.
downloadFailed = true;
downloadFailError = error;
}).then(() => {
return this.resourceHelper.getIframeSrc(this.module).then((src) => {
this.mode = 'iframe';
@ -125,7 +133,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
if (downloadFailed && this.appProvider.isOnline()) {
// We could load the main file but the download failed. Show error message.
this.domUtils.showErrorModal('core.errordownloadingsomefiles', true);
this.showErrorDownloadingSomeFiles(downloadFailError);
}
});
});
@ -147,15 +155,23 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
/**
* Opens a file.
*
* @return Promise resolved when done.
*/
open(): void {
this.prefetchHandler.isDownloadable(this.module, this.courseId).then((downloadable) => {
async open(): Promise<void> {
let downloadable = await this.prefetchHandler.isDownloadable(this.module, this.courseId);
if (downloadable) {
// Check if the main file is downloadle.
// This isn't done in "isDownloadable" to prevent extra WS calls in the course page.
downloadable = await this.resourceHelper.isMainFileDownloadable(this.module);
if (downloadable) {
this.resourceHelper.openModuleFile(this.module, this.courseId);
} else {
// The resource cannot be downloaded, open the activity in browser.
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
return this.resourceHelper.openModuleFile(this.module, this.courseId);
}
});
}
// The resource cannot be downloaded, open the activity in browser.
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(this.module.url);
}
}

View File

@ -20,6 +20,7 @@ import { AddonModResourceProvider } from './resource';
import { CoreSitesProvider } from '@providers/sites';
import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreFileProvider } from '@providers/file';
import { CoreFileHelperProvider } from '@providers/file-helper';
import { CoreAppProvider } from '@providers/app';
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
import { CoreTextUtilsProvider } from '@providers/utils/text';
@ -36,11 +37,17 @@ export class AddonModResourceHelperProvider {
// Display using object tag.
protected DISPLAY_EMBED = 1;
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
private resourceProvider: AddonModResourceProvider, private courseHelper: CoreCourseHelperProvider,
private textUtils: CoreTextUtilsProvider, private mimetypeUtils: CoreMimetypeUtilsProvider,
private fileProvider: CoreFileProvider, private appProvider: CoreAppProvider,
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider) {
constructor(protected courseProvider: CoreCourseProvider,
protected domUtils: CoreDomUtilsProvider,
protected resourceProvider: AddonModResourceProvider,
protected courseHelper: CoreCourseHelperProvider,
protected textUtils: CoreTextUtilsProvider,
protected mimetypeUtils: CoreMimetypeUtilsProvider,
protected fileProvider: CoreFileProvider,
protected appProvider: CoreAppProvider,
protected filepoolProvider: CoreFilepoolProvider,
protected sitesProvider: CoreSitesProvider,
protected fileHelper: CoreFileHelperProvider) {
}
/**
@ -136,6 +143,23 @@ export class AddonModResourceHelperProvider {
return mimetype == 'text/html';
}
/**
* Check if main file of resource is downloadable.
*
* @param module Module instance.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with boolean: whether main file is downloadable.
*/
isMainFileDownloadable(module: any, siteId?: string): Promise<boolean> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const mainFile = module.contents[0];
const fileUrl = this.fileHelper.getFileUrl(mainFile);
const timemodified = this.fileHelper.getFileTimemodified(mainFile);
return this.filepoolProvider.isFileDownloadable(siteId, fileUrl, timemodified);
}
/**
* Check if the resource is a Nextcloud file.
*

View File

@ -1619,6 +1619,7 @@
"core.h5p.offlineDialogRetryButtonLabel": "Retry now",
"core.h5p.offlineDialogRetryMessage": "Retrying in :num....",
"core.h5p.offlineSuccessfulSubmit": "Successfully submitted results.",
"core.h5p.offlinedisabled": "The site doesn't allow downloading H5P packages.",
"core.h5p.originator": "Originator",
"core.h5p.pd": "Public Domain",
"core.h5p.pddl": "Public Domain Dedication and Licence",

View File

@ -20,6 +20,7 @@ import { CoreFileHelperProvider } from '@providers/file-helper';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
import { CoreUrlUtilsProvider } from '@providers/utils/url';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreConstants } from '@core/constants';
@ -57,16 +58,17 @@ export class CoreFileComponent implements OnInit, OnDestroy {
protected timemodified: number;
protected observer;
constructor(private sitesProvider: CoreSitesProvider,
private utils: CoreUtilsProvider,
private domUtils: CoreDomUtilsProvider,
private filepoolProvider: CoreFilepoolProvider,
private appProvider: CoreAppProvider,
private fileHelper: CoreFileHelperProvider,
private mimeUtils: CoreMimetypeUtilsProvider,
private eventsProvider: CoreEventsProvider,
private textUtils: CoreTextUtilsProvider,
private pluginFileDelegate: CorePluginFileDelegate) {
constructor(protected sitesProvider: CoreSitesProvider,
protected utils: CoreUtilsProvider,
protected domUtils: CoreDomUtilsProvider,
protected filepoolProvider: CoreFilepoolProvider,
protected appProvider: CoreAppProvider,
protected fileHelper: CoreFileHelperProvider,
protected mimeUtils: CoreMimetypeUtilsProvider,
protected eventsProvider: CoreEventsProvider,
protected textUtils: CoreTextUtilsProvider,
protected pluginFileDelegate: CorePluginFileDelegate,
protected urlUtils: CoreUrlUtilsProvider) {
this.onDelete = new EventEmitter();
}
@ -104,6 +106,8 @@ export class CoreFileComponent implements OnInit, OnDestroy {
this.observer = this.eventsProvider.on(eventName, () => {
this.calculateState();
});
}).catch(() => {
// File not downloadable.
});
}
}
@ -152,14 +156,14 @@ export class CoreFileComponent implements OnInit, OnDestroy {
return;
}
if (!this.canDownload) {
if (!this.canDownload || !this.state || this.state == CoreConstants.NOT_DOWNLOADABLE) {
// File cannot be downloaded, just open it.
if (this.file.toURL) {
// Local file.
this.utils.openFile(this.file.toURL());
} else if (this.fileUrl) {
if (this.fileUrl.indexOf('http') === 0) {
this.utils.openOnlineFile(this.fileUrl);
this.utils.openOnlineFile(this.urlUtils.unfixPluginfileURL(this.fileUrl));
} else {
this.utils.openFile(this.fileUrl);
}

View File

@ -17,7 +17,7 @@ import { NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTextUtilsProvider, CoreTextErrorObject } from '@providers/utils/text';
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreCourseModuleMainComponent, CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
import { CoreCourseSectionPage } from '@core/course/pages/section/section.ts';
@ -265,6 +265,20 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
this.courseHelper.confirmAndRemoveFiles(this.module, this.courseId);
}
/**
* Show an error occurred while downloading files.
*
* @param error The specific error.
*/
protected showErrorDownloadingSomeFiles(error: string | CoreTextErrorObject): void {
const errorMessage = this.textUtils.buildSeveralParagraphsMessage([
this.translate.instant('core.errordownloadingsomefiles'),
error,
]);
this.domUtils.showErrorModal(errorMessage);
}
/**
* Component being destroyed.
*/

View File

@ -178,8 +178,9 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
error = this.translate.instant('core.courses.notenroled');
}
const body = this.translate.instant('core.twoparagraphs',
{ p1: error, p2: this.translate.instant('core.confirmopeninbrowser') });
const body = this.textUtils.buildSeveralParagraphsMessage(
[error, this.translate.instant('core.confirmopeninbrowser')]);
this.domUtils.showConfirm(body).then(() => {
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url);
}).catch(() => {

View File

@ -73,7 +73,8 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
this.logger = loggerProvider.getInstance('CoreH5PPlayerComponent');
this.site = sitesProvider.getCurrentSite();
this.siteId = this.site.getId();
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
this.siteCanDownload = this.sitesProvider.getCurrentSite().canDownloadFiles() &&
!this.h5pProvider.isOfflineDisabledInSite();
}
/**

View File

@ -64,6 +64,7 @@
"offlineDialogRetryButtonLabel": "Retry now",
"offlineDialogRetryMessage": "Retrying in :num....",
"offlineSuccessfulSubmit": "Successfully submitted results.",
"offlinedisabled": "The site doesn't allow downloading H5P packages.",
"originator": "Originator",
"pd": "Public Domain",
"pddl": "Public Domain Dedication and Licence",

View File

@ -1876,6 +1876,30 @@ export class CoreH5PProvider {
});
}
/**
* Check whether H5P offline is disabled.
*
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with boolean: whether is disabled.
*/
async isOfflineDisabled(siteId?: string): Promise<boolean> {
const site = await this.sitesProvider.getSite(siteId);
return this.isOfflineDisabledInSite(site);
}
/**
* Check whether H5P offline is disabled.
*
* @param site Site instance. If not defined, current site.
* @return Whether is disabled.
*/
isOfflineDisabledInSite(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite();
return site.isFeatureDisabled('NoDelegate_H5POffline');
}
/**
* Performs actions required when a library has been installed.
*

View File

@ -22,6 +22,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreH5PProvider } from './h5p';
import { CoreWSExternalFile } from '@providers/ws';
import { FileEntry } from '@ionic-native/file';
import { TranslateService } from '@ngx-translate/core';
/**
* Handler to treat H5P files.
@ -35,7 +36,8 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
protected textUtils: CoreTextUtilsProvider,
protected utils: CoreUtilsProvider,
protected fileProvider: CoreFileProvider,
protected h5pProvider: CoreH5PProvider) { }
protected h5pProvider: CoreH5PProvider,
protected translate: TranslateService) { }
/**
* React to a file being deleted.
@ -112,6 +114,28 @@ export class CoreH5PPluginFileHandler implements CorePluginFileHandler {
return this.h5pProvider.canGetTrustedH5PFileInSite();
}
/**
* Check if a file is downloadable.
*
* @param file The file data.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with a boolean and a reason why it isn't downloadable if needed.
*/
async isFileDownloadable(file: CoreWSExternalFile, siteId?: string): Promise<{downloadable: boolean, reason?: string}> {
const offlineDisabled = await this.h5pProvider.isOfflineDisabled(siteId);
if (offlineDisabled) {
return {
downloadable: false,
reason: this.translate.instant('core.h5p.offlinedisabled'),
};
} else {
return {
downloadable: true,
};
}
}
/**
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
*

View File

@ -2385,6 +2385,23 @@ export class CoreFilepoolProvider {
});
}
/**
* Check whether a file is downloadable.
*
* @param siteId The site ID.
* @param fileUrl File URL.
* @param timemodified The time this file was modified.
* @param filePath Filepath to download the file to. If defined, no extension will be added.
* @param revision File revision. If not defined, it will be calculated using the URL.
* @return Promise resolved with a boolean: whether a file is downloadable.
*/
async isFileDownloadable(siteId: string, fileUrl: string, timemodified: number = 0, filePath?: string, revision?: number)
: Promise<boolean> {
const state = await this.getFileStateByUrl(siteId, fileUrl, timemodified, filePath, revision);
return state != CoreConstants.NOT_DOWNLOADABLE;
}
/**
* Check if a file is downloading.
*

View File

@ -84,6 +84,15 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
*/
getFileSize?(file: CoreWSExternalFile, siteId?: string): Promise<number>;
/**
* Check if a file is downloadable.
*
* @param file The file data.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with a boolean and a reason why it isn't downloadable if needed.
*/
isFileDownloadable?(file: CoreWSExternalFile, siteId?: string): Promise<CorePluginFileDownloadableResult>;
/**
* Check whether the file should be treated by this handler. It is used in functions where the component isn't used.
*
@ -103,6 +112,21 @@ export interface CorePluginFileHandler extends CoreDelegateHandler {
treatDownloadedFile?(fileUrl: string, file: FileEntry, siteId?: string): Promise<any>;
}
/**
* Data about if a file is downloadable.
*/
export type CorePluginFileDownloadableResult = {
/**
* Whether it's downloadable.
*/
downloadable: boolean;
/**
* If not downloadable, the reason why it isn't.
*/
reason?: string;
};
/**
* Delegate to register pluginfile information handlers.
*/
@ -155,16 +179,22 @@ export class CorePluginFileDelegate extends CoreDelegate {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the file to use. Rejected if cannot download.
*/
protected getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string)
protected async getHandlerDownloadableFile(file: CoreWSExternalFile, handler: CorePluginFileHandler, siteId?: string)
: Promise<CoreWSExternalFile> {
if (handler && handler.getDownloadableFile) {
return handler.getDownloadableFile(file, siteId).then((newFile) => {
return newFile || file;
});
const isDownloadable = await this.isFileDownloadable(file, siteId);
if (!isDownloadable.downloadable) {
throw isDownloadable.reason;
}
return Promise.resolve(file);
if (handler && handler.getDownloadableFile) {
const newFile = await handler.getDownloadableFile(file, siteId);
return newFile || file;
}
return file;
}
/**
@ -240,23 +270,32 @@ export class CorePluginFileDelegate extends CoreDelegate {
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the size.
*/
getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
async getFileSize(file: CoreWSExternalFile, siteId?: string): Promise<number> {
const isDownloadable = await this.isFileDownloadable(file, siteId);
if (!isDownloadable.downloadable) {
return 0;
}
const handler = this.getHandlerForFile(file);
// First of all check if file can be downloaded.
return this.getHandlerDownloadableFile(file, handler, siteId).then((file) => {
if (!file) {
return 0;
}
const downloadableFile = await this.getHandlerDownloadableFile(file, handler, siteId);
if (!downloadableFile) {
return 0;
}
if (handler && handler.getFileSize) {
return handler.getFileSize(file, siteId).catch(() => {
return file.filesize;
});
}
if (handler && handler.getFileSize) {
try {
const size = handler.getFileSize(downloadableFile, siteId);
return Promise.resolve(file.filesize);
});
return size;
} catch (error) {
// Ignore errors.
}
}
return downloadableFile.filesize;
}
/**
@ -275,6 +314,24 @@ export class CorePluginFileDelegate extends CoreDelegate {
}
}
/**
* Check if a file is downloadable.
*
* @param file The file data.
* @param siteId Site ID. If not defined, current site.
* @return Promise with the data.
*/
isFileDownloadable(file: CoreWSExternalFile, siteId?: string): Promise<CorePluginFileDownloadableResult> {
const handler = this.getHandlerForFile(file);
if (handler && handler.isFileDownloadable) {
return handler.isFileDownloadable(file, siteId);
}
// Default to true.
return Promise.resolve({downloadable: true});
}
/**
* Removes the revision number from a file URL.
*

View File

@ -18,6 +18,16 @@ import { ModalController, Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreLangProvider } from '../lang';
/**
* Different type of errors the app can treat.
*/
export type CoreTextErrorObject = {
message?: string;
error?: string;
content?: string;
body?: string;
};
/*
* "Utils" service with helper functions for text.
*/
@ -122,6 +132,38 @@ export class CoreTextUtilsProvider {
return result;
}
/**
* Build a message with several paragraphs.
*
* @param paragraphs List of paragraphs.
* @return Built message.
*/
buildSeveralParagraphsMessage(paragraphs: (string | CoreTextErrorObject)[]): string {
// Filter invalid messages, and convert them to messages in case they're errors.
const messages: string[] = [];
paragraphs.forEach((paragraph) => {
// If it's an error, get its message.
const message = this.getErrorMessageFromError(paragraph);
if (paragraph) {
messages.push(message);
}
});
if (messages.length < 2) {
return messages[0] || '';
}
let builtMessage = messages[0];
for (let i = 1; i < messages.length; i++) {
builtMessage = this.translate.instant('core.twoparagraphs', { p1: builtMessage, p2: messages[i] });
}
return builtMessage;
}
/**
* Convert size in bytes into human readable format
*
@ -449,7 +491,7 @@ export class CoreTextUtilsProvider {
* @param error Error object.
* @return Error message, undefined if not found.
*/
getErrorMessageFromError(error: any): string {
getErrorMessageFromError(error: string | CoreTextErrorObject): string {
if (typeof error == 'string') {
return error;
}

View File

@ -469,4 +469,32 @@ export class CoreUrlUtilsProvider {
return matches && matches[0];
}
/**
* Modifies a pluginfile URL to use the default pluginfile script instead of the webservice one.
*
* @param url The url to be fixed.
* @param siteUrl The URL of the site the URL belongs to.
* @return Modified URL.
*/
unfixPluginfileURL(url: string, siteUrl?: string): string {
if (!url) {
return '';
}
url = url.replace(/&amp;/g, '&');
// It site URL is supplied, check if the URL belongs to the site.
if (siteUrl && url.indexOf(this.textUtils.addEndingSlash(siteUrl)) !== 0) {
return url;
}
// Not a pluginfile URL. Treat webservice/pluginfile case.
url = url.replace(/\/webservice\/pluginfile\.php\//, '/pluginfile.php/');
// Make sure the URL doesn't contain the token.
url.replace(/([?&])token=[^&]*&?/, '$1');
return url;
}
}