Merge pull request #2985 from dpalou/MOBILE-3896

MOBILE-3896 file: Allow downloading tokenpluginfile linked files
main
Pau Ferrer Ocaña 2021-11-04 14:41:04 +01:00 committed by GitHub
commit 89887b8295
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 18 deletions

View File

@ -19,7 +19,6 @@ import { CoreQuestionHelper } from '@features/question/services/question-helper'
import { CoreFilepool } from '@services/filepool'; import { CoreFilepool } from '@services/filepool';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url';
import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker'; import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
/** /**
@ -142,9 +141,9 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
let imgSrc = this.imgSrc; let imgSrc = this.imgSrc;
const site = CoreSites.getCurrentSite(); const site = CoreSites.getCurrentSite();
if (this.imgSrc && site?.canDownloadFiles() && CoreUrlUtils.isPluginFileUrl(this.imgSrc)) { if (this.imgSrc && site?.canDownloadFiles() && site.isSitePluginFileUrl(this.imgSrc)) {
imgSrc = await CoreFilepool.getSrcByUrl( imgSrc = await CoreFilepool.getSrcByUrl(
site.id!, site.getId(),
this.imgSrc, this.imgSrc,
this.component, this.component,
this.componentId, this.componentId,

View File

@ -1893,6 +1893,35 @@ export class CoreSite {
return this.tokenPluginFileWorksPromise; return this.tokenPluginFileWorksPromise;
} }
/**
* Check if a URL to a file belongs to the site and uses the pluginfileurl or tokenpluginfileurl endpoints.
*
* @param url File URL to check.
* @return Whether it's a site file URL.
*/
isSitePluginFileUrl(url: string): boolean {
const isPluginFileUrl = CoreUrlUtils.isPluginFileUrl(url) || CoreUrlUtils.isTokenPluginFileUrl(url);
if (!isPluginFileUrl) {
return false;
}
return this.containsUrl(url);
}
/**
* Check if a URL to a file belongs to the site and is a theme image file.
*
* @param url File URL to check.
* @return Whether it's a site theme image URL.
*/
isSiteThemeImageUrl(url: string): boolean {
if (!CoreUrlUtils.isThemeImageUrl(url)) {
return false;
}
return this.containsUrl(url);
}
} }
/** /**

View File

@ -213,8 +213,11 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
} }
const site = await CoreSites.getSite(siteId);
const isSiteFile = site.isSitePluginFileUrl(url);
if (!url || !url.match(/^https?:\/\//i) || CoreUrlUtils.isLocalFileUrl(url) || if (!url || !url.match(/^https?:\/\//i) || CoreUrlUtils.isLocalFileUrl(url) ||
(tagName === 'A' && !CoreUrlUtils.isDownloadableUrl(url))) { (tagName === 'A' && !(isSiteFile || site.isSiteThemeImageUrl(url) || CoreUrlUtils.isGravatarUrl(url)))) {
this.logger.debug('Ignoring non-downloadable URL: ' + url); this.logger.debug('Ignoring non-downloadable URL: ' + url);
if (tagName === 'SOURCE') { if (tagName === 'SOURCE') {
@ -225,9 +228,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
throw new CoreError('Non-downloadable URL'); throw new CoreError('Non-downloadable URL');
} }
const site = await CoreSites.getSite(siteId); if (!site.canDownloadFiles() && isSiteFile) {
if (!site.canDownloadFiles() && CoreUrlUtils.isPluginFileUrl(url)) {
this.element.parentElement?.removeChild(this.element); // Remove element since it'll be broken. this.element.parentElement?.removeChild(this.element); // Remove element since it'll be broken.
throw new CoreError('Site doesn\'t allow downloading files.'); throw new CoreError('Site doesn\'t allow downloading files.');
@ -329,7 +330,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
return; return;
} }
let inlineStyles = this.element.getAttribute('style'); let inlineStyles = this.element.getAttribute('style') || '';
if (!inlineStyles) { if (!inlineStyles) {
return; return;
@ -346,7 +347,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
const finalUrl = await CoreFilepool.getUrlByUrl(siteId, url, this.component, this.componentId, 0, true, true); const finalUrl = await CoreFilepool.getUrlByUrl(siteId, url, this.component, this.componentId, 0, true, true);
this.logger.debug('Using URL ' + finalUrl + ' for ' + url + ' in inline styles'); this.logger.debug('Using URL ' + finalUrl + ' for ' + url + ' in inline styles');
inlineStyles = inlineStyles!.replace(new RegExp(url, 'gi'), finalUrl); inlineStyles = inlineStyles.replace(new RegExp(url, 'gi'), finalUrl);
}); });
try { try {

View File

@ -125,6 +125,7 @@ describe('CoreFormatTextDirective', () => {
getId: () => '42', getId: () => '42',
canDownloadFiles: () => true, canDownloadFiles: () => true,
isVersionGreaterEqualThan: () => true, isVersionGreaterEqualThan: () => true,
isSitePluginFileUrl: () => false,
}); });
// @todo this is done because we cannot mock image being loaded, we should find an alternative... // @todo this is done because we cannot mock image being loaded, we should find an alternative...

View File

@ -550,8 +550,9 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
} }
const elements = Array.from(this.editorElement.querySelectorAll('img')); const elements = Array.from(this.editorElement.querySelectorAll('img'));
const site = CoreSites.getCurrentSite();
const siteId = CoreSites.getCurrentSiteId(); const siteId = CoreSites.getCurrentSiteId();
const canDownloadFiles = CoreSites.getCurrentSite()!.canDownloadFiles(); const canDownloadFiles = !site || site.canDownloadFiles();
elements.forEach(async (el) => { elements.forEach(async (el) => {
if (el.getAttribute('data-original-src')) { if (el.getAttribute('data-original-src')) {
// Already treated. // Already treated.
@ -560,8 +561,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
const url = el.src; const url = el.src;
if (!url || !CoreUrlUtils.isDownloadableUrl(url) || if (!url || !CoreUrlUtils.isDownloadableUrl(url) || (!canDownloadFiles && site?.isSitePluginFileUrl(url))) {
(!canDownloadFiles && CoreUrlUtils.isPluginFileUrl(url))) {
// Nothing to treat. // Nothing to treat.
return; return;
} }

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
import { Injectable, EventEmitter } from '@angular/core'; import { Injectable, EventEmitter } from '@angular/core';
import { FileEntry, DirectoryEntry } from '@ionic-native/file/ngx';
import { CoreFile } from '@services/file'; import { CoreFile } from '@services/file';
import { CoreFileHelper } from '@services/file-helper'; import { CoreFileHelper } from '@services/file-helper';
@ -20,7 +21,6 @@ import { CoreFilepool } from '@services/filepool';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreTextUtils } from '@services/utils/text'; import { CoreTextUtils } from '@services/utils/text';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreWSFile } from '@services/ws'; import { CoreWSFile } from '@services/ws';
import { makeSingleton, Translate } from '@singletons'; import { makeSingleton, Translate } from '@singletons';
@ -138,7 +138,7 @@ export class CoreQuestionHelperProvider {
// Search the radio button inside this certainty and add its data to the options array. // Search the radio button inside this certainty and add its data to the options array.
const input = <HTMLInputElement> label.querySelector('input[type="radio"]'); const input = <HTMLInputElement> label.querySelector('input[type="radio"]');
if (input) { if (input) {
question.behaviourCertaintyOptions!.push({ question.behaviourCertaintyOptions?.push({
id: input.id, id: input.id,
name: input.name, name: input.name,
value: input.value, value: input.value,
@ -650,7 +650,7 @@ export class CoreQuestionHelperProvider {
} }
treated[fileUrl] = true; treated[fileUrl] = true;
if (!site.canDownloadFiles() && CoreUrlUtils.isPluginFileUrl(fileUrl)) { if (!site.canDownloadFiles() && site.isSitePluginFileUrl(fileUrl)) {
return; return;
} }
@ -791,7 +791,7 @@ export class CoreQuestionHelperProvider {
} }
// Replace the icon with the font version. // Replace the icon with the font version.
const newIcon: HTMLElement = document.createElement('ion-icon'); const newIcon: HTMLIonIconElement = document.createElement('ion-icon');
if (correct) { if (correct) {
newIcon.setAttribute('name', 'fas-check'); newIcon.setAttribute('name', 'fas-check');

View File

@ -409,7 +409,7 @@ export class CoreUrlUtilsProvider {
* @return Whether the URL is downloadable. * @return Whether the URL is downloadable.
*/ */
isDownloadableUrl(url: string): boolean { isDownloadableUrl(url: string): boolean {
return this.isPluginFileUrl(url) || this.isThemeImageUrl(url) || this.isGravatarUrl(url); return this.isPluginFileUrl(url) || this.isTokenPluginFileUrl(url) || this.isThemeImageUrl(url) || this.isGravatarUrl(url);
} }
/** /**
@ -470,7 +470,17 @@ export class CoreUrlUtilsProvider {
* @return Whether the URL is a pluginfile URL. * @return Whether the URL is a pluginfile URL.
*/ */
isPluginFileUrl(url: string): boolean { isPluginFileUrl(url: string): boolean {
return url?.indexOf('/pluginfile.php') !== -1; return url.indexOf('/pluginfile.php') !== -1;
}
/**
* Returns if a URL is a tokenpluginfile URL.
*
* @param url The URL to test.
* @return Whether the URL is a tokenpluginfile URL.
*/
isTokenPluginFileUrl(url: string): boolean {
return url.indexOf('/tokenpluginfile.php') !== -1;
} }
/** /**