From 92e91931578256b94c1de9657a328a8c187020d8 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 18 May 2018 12:10:05 +0200 Subject: [PATCH] MOBILE-2381 core: Fix getMimeTypeFromUrl --- .../login/pages/site-policy/site-policy.ts | 5 +- src/providers/filepool.ts | 2 +- src/providers/utils/mimetype.ts | 23 -------- src/providers/utils/utils.ts | 58 ++++++++++++------- src/providers/ws.ts | 50 +++++++++++----- 5 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/core/login/pages/site-policy/site-policy.ts b/src/core/login/pages/site-policy/site-policy.ts index c63026f8c..30b56e58f 100644 --- a/src/core/login/pages/site-policy/site-policy.ts +++ b/src/core/login/pages/site-policy/site-policy.ts @@ -17,6 +17,7 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; +import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreLoginHelperProvider } from '../../providers/helper'; import { CoreSite } from '@classes/site'; @@ -36,7 +37,7 @@ export class CoreLoginSitePolicyPage { protected currentSite: CoreSite; constructor(private navCtrl: NavController, navParams: NavParams, private loginHelper: CoreLoginHelperProvider, - private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, + private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private mimeUtils: CoreMimetypeUtilsProvider) { this.siteId = navParams.get('siteId'); } @@ -77,7 +78,7 @@ export class CoreLoginSitePolicyPage { this.sitePolicy = sitePolicy; // Try to get the mime type. - return this.mimeUtils.getMimeTypeFromUrl(sitePolicy).then((mimeType) => { + return this.utils.getMimeTypeFromUrl(sitePolicy).then((mimeType) => { const extension = this.mimeUtils.getExtension(mimeType, sitePolicy); this.showInline = extension == 'html' || extension == 'htm'; }).catch(() => { diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts index e92f148c7..8a350fc0a 100644 --- a/src/providers/filepool.ts +++ b/src/providers/filepool.ts @@ -2734,7 +2734,7 @@ export class CoreFilepoolProvider { return Promise.resolve(); } - return this.mimeUtils.getMimeTypeFromUrl(url).then((mimetype) => { + return this.utils.getMimeTypeFromUrl(url).then((mimetype) => { // If the file is streaming (audio or video) we reject. if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { return Promise.reject(null); diff --git a/src/providers/utils/mimetype.ts b/src/providers/utils/mimetype.ts index af20c6303..a1bce44ce 100644 --- a/src/providers/utils/mimetype.ts +++ b/src/providers/utils/mimetype.ts @@ -28,7 +28,6 @@ export class CoreMimetypeUtilsProvider { protected mimeToExt = {}; // Object to map mimetypes -> extensions. protected groupsMimeInfo = {}; // Object to hold extensions and mimetypes that belong to a certain "group" (audio, video, ...). protected extensionRegex = /^[a-z0-9]+$/; - protected wsProvider: any = {}; // @todo constructor(http: HttpClient, logger: CoreLoggerProvider, private translate: TranslateService, private textUtils: CoreTextUtilsProvider) { @@ -199,28 +198,6 @@ export class CoreMimetypeUtilsProvider { return 'assets/img/files/folder-64.png'; } - /** - * Get the mimetype of a file given its URL. It'll try to guess it using the URL, if that fails then it'll - * perform a HEAD request to get it. It's done in this order because pluginfile.php can return wrong mimetypes. - * - * @param {string} url The URL of the file. - * @return {Promise} Promise resolved with the mimetype. - */ - getMimeTypeFromUrl(url: string): Promise { - // First check if it can be guessed from the URL. - const extension = this.guessExtensionFromUrl(url), - mimetype = this.getMimeType(extension); - - if (mimetype) { - return Promise.resolve(mimetype); - } - - // Can't be guessed, get the remote mimetype. - return this.wsProvider.getRemoteFileMimeType(url).then((mimetype) => { - return mimetype || ''; - }); - } - /** * Guess the extension of a file from its URL. * This is very weak and unreliable. diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts index e135f4465..8a3600430 100644 --- a/src/providers/utils/utils.ts +++ b/src/providers/utils/utils.ts @@ -25,7 +25,7 @@ import { CoreEventsProvider } from '../events'; import { CoreLoggerProvider } from '../logger'; import { TranslateService } from '@ngx-translate/core'; import { CoreLangProvider } from '../lang'; -import { CoreWSError } from '../ws'; +import { CoreWSProvider, CoreWSError } from '../ws'; /** * Deferred promise. It's similar to the result of $q.defer() in AngularJS. @@ -63,7 +63,8 @@ export class CoreUtilsProvider { constructor(private iab: InAppBrowser, private appProvider: CoreAppProvider, private clipboard: Clipboard, private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService, private platform: Platform, private langProvider: CoreLangProvider, private eventsProvider: CoreEventsProvider, - private fileOpener: FileOpener, private mimetypeUtils: CoreMimetypeUtilsProvider, private webIntent: WebIntent) { + private fileOpener: FileOpener, private mimetypeUtils: CoreMimetypeUtilsProvider, private webIntent: WebIntent, + private wsProvider: CoreWSProvider) { this.logger = logger.getInstance('CoreUtilsProvider'); } @@ -255,6 +256,17 @@ export class CoreUtilsProvider { }); } + /** + * Create a "fake" WS error for local errors. + * + * @param {string} message The message to include in the error. + * @param {boolean} [needsTranslate] If the message needs to be translated. + * @return {CoreWSError} Fake WS error. + */ + createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError { + return this.wsProvider.createFakeWSError(message, needsTranslate); + } + /** * Empties an array without losing its reference. * @@ -536,6 +548,29 @@ export class CoreUtilsProvider { }); } + /** + * Get the mimetype of a file given its URL. It'll try to guess it using the URL, if that fails then it'll + * perform a HEAD request to get it. It's done in this order because pluginfile.php can return wrong mimetypes. + * This function is in here instead of MimetypeUtils to prevent circular dependencies. + * + * @param {string} url The URL of the file. + * @return {Promise} Promise resolved with the mimetype. + */ + getMimeTypeFromUrl(url: string): Promise { + // First check if it can be guessed from the URL. + const extension = this.mimetypeUtils.guessExtensionFromUrl(url), + mimetype = this.mimetypeUtils.getMimeType(extension); + + if (mimetype) { + return Promise.resolve(mimetype); + } + + // Can't be guessed, get the remote mimetype. + return this.wsProvider.getRemoteFileMimeType(url).then((mimetype) => { + return mimetype || ''; + }); + } + /** * Given a list of files, check if there are repeated names. * @@ -606,23 +641,6 @@ export class CoreUtilsProvider { return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1); } - /** - * Create a "fake" WS error for local errors. - * - * @param {string} message The message to include in the error. - * @param {boolean} [needsTranslate] If the message needs to be translated. - * @return {CoreWSError} Fake WS error. - */ - createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError { - if (needsTranslate) { - message = this.translate.instant(message); - } - - return { - message: message - }; - } - /** * Given an error returned by a WS call, check if the error is generated by the app or it has been returned by the WebSwervice. * @@ -781,7 +799,7 @@ export class CoreUtilsProvider { openOnlineFile(url: string): Promise { if (this.platform.is('android')) { // In Android we need the mimetype to open it. - return this.mimetypeUtils.getMimeTypeFromUrl(url).catch(() => { + return this.getMimeTypeFromUrl(url).catch(() => { // Error getting mimetype, return undefined. }).then((mimetype) => { if (!mimetype) { diff --git a/src/providers/ws.ts b/src/providers/ws.ts index 896917693..e735a0db6 100644 --- a/src/providers/ws.ts +++ b/src/providers/ws.ts @@ -22,7 +22,6 @@ import { CoreFileProvider } from './file'; import { CoreLoggerProvider } from './logger'; import { CoreMimetypeUtilsProvider } from './utils/mimetype'; import { CoreTextUtilsProvider } from './utils/text'; -import { CoreUtilsProvider } from './utils/utils'; import { CoreConstants } from '@core/constants'; import { Md5 } from 'ts-md5/dist/md5'; import { CoreInterceptor } from '@classes/interceptor'; @@ -131,7 +130,7 @@ export class CoreWSProvider { protected retryTimeout = 0; constructor(private http: HttpClient, private translate: TranslateService, private appProvider: CoreAppProvider, - private textUtils: CoreTextUtilsProvider, logger: CoreLoggerProvider, private utils: CoreUtilsProvider, + private textUtils: CoreTextUtilsProvider, logger: CoreLoggerProvider, private fileProvider: CoreFileProvider, private fileTransfer: FileTransfer, private commonHttp: Http, private mimeUtils: CoreMimetypeUtilsProvider) { this.logger = logger.getInstance('CoreWSProvider'); @@ -148,14 +147,19 @@ export class CoreWSProvider { * if it fails. */ protected addToRetryQueue(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets): Promise { - const call = { + const call: any = { method: method, siteUrl: siteUrl, ajaxData: ajaxData, preSets: preSets, - deferred: this.utils.promiseDefer(), + deferred: {} }; + call.deferred.promise = new Promise((resolve, reject): void => { + call.deferred.resolve = resolve; + call.deferred.reject = reject; + }); + this.retryCalls.push(call); return call.deferred.promise; @@ -174,9 +178,9 @@ export class CoreWSProvider { let siteUrl; if (!preSets) { - return Promise.reject(this.utils.createFakeWSError('core.unexpectederror', true)); + return Promise.reject(this.createFakeWSError('core.unexpectederror', true)); } else if (!this.appProvider.isOnline()) { - return Promise.reject(this.utils.createFakeWSError('core.networkerrormsg', true)); + return Promise.reject(this.createFakeWSError('core.networkerrormsg', true)); } preSets.typeExpected = preSets.typeExpected || 'object'; @@ -184,8 +188,7 @@ export class CoreWSProvider { preSets.responseExpected = true; } - data = data || {}; - data = this.utils.clone(data); // Clone the data so the changes don't affect the original data. + data = Object.assign({}, data); // Create a new object so the changes don't affect the original data. data.wsfunction = method; data.wstoken = preSets.wsToken; siteUrl = preSets.siteUrl + '/webservice/rest/server.php?moodlewsrestformat=json'; @@ -320,6 +323,23 @@ export class CoreWSProvider { return result; } + /** + * Create a "fake" WS error for local errors. + * + * @param {string} message The message to include in the error. + * @param {boolean} [needsTranslate] If the message needs to be translated. + * @return {CoreWSError} Fake WS error. + */ + createFakeWSError(message: string, needsTranslate?: boolean): CoreWSError { + if (needsTranslate) { + message = this.translate.instant(message); + } + + return { + message: message + }; + } + /** * Downloads a file from Moodle using Cordova File API. * @@ -522,7 +542,7 @@ export class CoreWSProvider { } if (!data) { - return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true)); + return Promise.reject(this.createFakeWSError('core.serverconnection', true)); } else if (typeof data != preSets.typeExpected) { // If responseType is text an string will be returned, parse before returning. if (typeof data == 'string') { @@ -531,7 +551,7 @@ export class CoreWSProvider { if (isNaN(data)) { this.logger.warn(`Response expected type "${preSets.typeExpected}" cannot be parsed to number`); - return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true)); + return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true)); } } else if (preSets.typeExpected == 'boolean') { if (data === 'true') { @@ -541,17 +561,17 @@ export class CoreWSProvider { } else { this.logger.warn(`Response expected type "${preSets.typeExpected}" is not true or false`); - return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true)); + return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true)); } } else { this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`); - return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true)); + return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true)); } } else { this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`); - return Promise.reject(this.utils.createFakeWSError('core.errorinvalidresponse', true)); + return Promise.reject(this.createFakeWSError('core.errorinvalidresponse', true)); } } @@ -565,7 +585,7 @@ export class CoreWSProvider { } if (typeof data.debuginfo != 'undefined') { - return Promise.reject(this.utils.createFakeWSError('Error. ' + data.message)); + return Promise.reject(this.createFakeWSError('Error. ' + data.message)); } return data; @@ -593,7 +613,7 @@ export class CoreWSProvider { return retryPromise; } - return Promise.reject(this.utils.createFakeWSError('core.serverconnection', true)); + return Promise.reject(this.createFakeWSError('core.serverconnection', true)); }); promise = this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);