MOBILE-2381 core: Fix getMimeTypeFromUrl

main
Dani Palou 2018-05-18 12:10:05 +02:00
parent e3b9e9e8f8
commit 92e9193157
5 changed files with 77 additions and 61 deletions

View File

@ -17,6 +17,7 @@ import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreLoginHelperProvider } from '../../providers/helper'; import { CoreLoginHelperProvider } from '../../providers/helper';
import { CoreSite } from '@classes/site'; import { CoreSite } from '@classes/site';
@ -36,7 +37,7 @@ export class CoreLoginSitePolicyPage {
protected currentSite: CoreSite; protected currentSite: CoreSite;
constructor(private navCtrl: NavController, navParams: NavParams, private loginHelper: CoreLoginHelperProvider, 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) { private mimeUtils: CoreMimetypeUtilsProvider) {
this.siteId = navParams.get('siteId'); this.siteId = navParams.get('siteId');
} }
@ -77,7 +78,7 @@ export class CoreLoginSitePolicyPage {
this.sitePolicy = sitePolicy; this.sitePolicy = sitePolicy;
// Try to get the mime type. // 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); const extension = this.mimeUtils.getExtension(mimeType, sitePolicy);
this.showInline = extension == 'html' || extension == 'htm'; this.showInline = extension == 'html' || extension == 'htm';
}).catch(() => { }).catch(() => {

View File

@ -2734,7 +2734,7 @@ export class CoreFilepoolProvider {
return Promise.resolve(); 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 the file is streaming (audio or video) we reject.
if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) { if (mimetype.indexOf('video') != -1 || mimetype.indexOf('audio') != -1) {
return Promise.reject(null); return Promise.reject(null);

View File

@ -28,7 +28,6 @@ export class CoreMimetypeUtilsProvider {
protected mimeToExt = {}; // Object to map mimetypes -> extensions. protected mimeToExt = {}; // Object to map mimetypes -> extensions.
protected groupsMimeInfo = {}; // Object to hold extensions and mimetypes that belong to a certain "group" (audio, video, ...). protected groupsMimeInfo = {}; // Object to hold extensions and mimetypes that belong to a certain "group" (audio, video, ...).
protected extensionRegex = /^[a-z0-9]+$/; protected extensionRegex = /^[a-z0-9]+$/;
protected wsProvider: any = {}; // @todo
constructor(http: HttpClient, logger: CoreLoggerProvider, private translate: TranslateService, constructor(http: HttpClient, logger: CoreLoggerProvider, private translate: TranslateService,
private textUtils: CoreTextUtilsProvider) { private textUtils: CoreTextUtilsProvider) {
@ -199,28 +198,6 @@ export class CoreMimetypeUtilsProvider {
return 'assets/img/files/folder-64.png'; 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<string>} Promise resolved with the mimetype.
*/
getMimeTypeFromUrl(url: string): Promise<string> {
// 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. * Guess the extension of a file from its URL.
* This is very weak and unreliable. * This is very weak and unreliable.

View File

@ -25,7 +25,7 @@ import { CoreEventsProvider } from '../events';
import { CoreLoggerProvider } from '../logger'; import { CoreLoggerProvider } from '../logger';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreLangProvider } from '../lang'; 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. * 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, constructor(private iab: InAppBrowser, private appProvider: CoreAppProvider, private clipboard: Clipboard,
private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService, private domUtils: CoreDomUtilsProvider, logger: CoreLoggerProvider, private translate: TranslateService,
private platform: Platform, private langProvider: CoreLangProvider, private eventsProvider: CoreEventsProvider, 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'); 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. * 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<string>} Promise resolved with the mimetype.
*/
getMimeTypeFromUrl(url: string): Promise<string> {
// 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. * 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); 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. * 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<void> { openOnlineFile(url: string): Promise<void> {
if (this.platform.is('android')) { if (this.platform.is('android')) {
// In Android we need the mimetype to open it. // 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. // Error getting mimetype, return undefined.
}).then((mimetype) => { }).then((mimetype) => {
if (!mimetype) { if (!mimetype) {

View File

@ -22,7 +22,6 @@ import { CoreFileProvider } from './file';
import { CoreLoggerProvider } from './logger'; import { CoreLoggerProvider } from './logger';
import { CoreMimetypeUtilsProvider } from './utils/mimetype'; import { CoreMimetypeUtilsProvider } from './utils/mimetype';
import { CoreTextUtilsProvider } from './utils/text'; import { CoreTextUtilsProvider } from './utils/text';
import { CoreUtilsProvider } from './utils/utils';
import { CoreConstants } from '@core/constants'; import { CoreConstants } from '@core/constants';
import { Md5 } from 'ts-md5/dist/md5'; import { Md5 } from 'ts-md5/dist/md5';
import { CoreInterceptor } from '@classes/interceptor'; import { CoreInterceptor } from '@classes/interceptor';
@ -131,7 +130,7 @@ export class CoreWSProvider {
protected retryTimeout = 0; protected retryTimeout = 0;
constructor(private http: HttpClient, private translate: TranslateService, private appProvider: CoreAppProvider, 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 fileProvider: CoreFileProvider, private fileTransfer: FileTransfer, private commonHttp: Http,
private mimeUtils: CoreMimetypeUtilsProvider) { private mimeUtils: CoreMimetypeUtilsProvider) {
this.logger = logger.getInstance('CoreWSProvider'); this.logger = logger.getInstance('CoreWSProvider');
@ -148,14 +147,19 @@ export class CoreWSProvider {
* if it fails. * if it fails.
*/ */
protected addToRetryQueue(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets): Promise<any> { protected addToRetryQueue(method: string, siteUrl: string, ajaxData: any, preSets: CoreWSPreSets): Promise<any> {
const call = { const call: any = {
method: method, method: method,
siteUrl: siteUrl, siteUrl: siteUrl,
ajaxData: ajaxData, ajaxData: ajaxData,
preSets: preSets, 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); this.retryCalls.push(call);
return call.deferred.promise; return call.deferred.promise;
@ -174,9 +178,9 @@ export class CoreWSProvider {
let siteUrl; let siteUrl;
if (!preSets) { if (!preSets) {
return Promise.reject(this.utils.createFakeWSError('core.unexpectederror', true)); return Promise.reject(this.createFakeWSError('core.unexpectederror', true));
} else if (!this.appProvider.isOnline()) { } 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'; preSets.typeExpected = preSets.typeExpected || 'object';
@ -184,8 +188,7 @@ export class CoreWSProvider {
preSets.responseExpected = true; preSets.responseExpected = true;
} }
data = data || {}; data = Object.assign({}, data); // Create a new object so the changes don't affect the original data.
data = this.utils.clone(data); // Clone the data so the changes don't affect the original data.
data.wsfunction = method; data.wsfunction = method;
data.wstoken = preSets.wsToken; data.wstoken = preSets.wsToken;
siteUrl = preSets.siteUrl + '/webservice/rest/server.php?moodlewsrestformat=json'; siteUrl = preSets.siteUrl + '/webservice/rest/server.php?moodlewsrestformat=json';
@ -320,6 +323,23 @@ export class CoreWSProvider {
return result; 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. * Downloads a file from Moodle using Cordova File API.
* *
@ -522,7 +542,7 @@ export class CoreWSProvider {
} }
if (!data) { 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) { } else if (typeof data != preSets.typeExpected) {
// If responseType is text an string will be returned, parse before returning. // If responseType is text an string will be returned, parse before returning.
if (typeof data == 'string') { if (typeof data == 'string') {
@ -531,7 +551,7 @@ export class CoreWSProvider {
if (isNaN(data)) { if (isNaN(data)) {
this.logger.warn(`Response expected type "${preSets.typeExpected}" cannot be parsed to number`); 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') { } else if (preSets.typeExpected == 'boolean') {
if (data === 'true') { if (data === 'true') {
@ -541,17 +561,17 @@ export class CoreWSProvider {
} else { } else {
this.logger.warn(`Response expected type "${preSets.typeExpected}" is not true or false`); 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 { } else {
this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`); 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 { } else {
this.logger.warn('Response of type "' + typeof data + `" received, expecting "${preSets.typeExpected}"`); 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') { if (typeof data.debuginfo != 'undefined') {
return Promise.reject(this.utils.createFakeWSError('Error. ' + data.message)); return Promise.reject(this.createFakeWSError('Error. ' + data.message));
} }
return data; return data;
@ -593,7 +613,7 @@ export class CoreWSProvider {
return retryPromise; 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); promise = this.setPromiseHttp(promise, 'post', preSets.siteUrl, ajaxData);