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 { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url';
import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
/**
@ -142,9 +141,9 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
let imgSrc = this.imgSrc;
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(
site.id!,
site.getId(),
this.imgSrc,
this.component,
this.componentId,

View File

@ -1893,6 +1893,35 @@ export class CoreSite {
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) ||
(tagName === 'A' && !CoreUrlUtils.isDownloadableUrl(url))) {
(tagName === 'A' && !(isSiteFile || site.isSiteThemeImageUrl(url) || CoreUrlUtils.isGravatarUrl(url)))) {
this.logger.debug('Ignoring non-downloadable URL: ' + url);
if (tagName === 'SOURCE') {
@ -225,9 +228,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
throw new CoreError('Non-downloadable URL');
}
const site = await CoreSites.getSite(siteId);
if (!site.canDownloadFiles() && CoreUrlUtils.isPluginFileUrl(url)) {
if (!site.canDownloadFiles() && isSiteFile) {
this.element.parentElement?.removeChild(this.element); // Remove element since it'll be broken.
throw new CoreError('Site doesn\'t allow downloading files.');
@ -329,7 +330,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
return;
}
let inlineStyles = this.element.getAttribute('style');
let inlineStyles = this.element.getAttribute('style') || '';
if (!inlineStyles) {
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);
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 {

View File

@ -125,6 +125,7 @@ describe('CoreFormatTextDirective', () => {
getId: () => '42',
canDownloadFiles: () => true,
isVersionGreaterEqualThan: () => true,
isSitePluginFileUrl: () => false,
});
// @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 site = CoreSites.getCurrentSite();
const siteId = CoreSites.getCurrentSiteId();
const canDownloadFiles = CoreSites.getCurrentSite()!.canDownloadFiles();
const canDownloadFiles = !site || site.canDownloadFiles();
elements.forEach(async (el) => {
if (el.getAttribute('data-original-src')) {
// Already treated.
@ -560,8 +561,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
const url = el.src;
if (!url || !CoreUrlUtils.isDownloadableUrl(url) ||
(!canDownloadFiles && CoreUrlUtils.isPluginFileUrl(url))) {
if (!url || !CoreUrlUtils.isDownloadableUrl(url) || (!canDownloadFiles && site?.isSitePluginFileUrl(url))) {
// Nothing to treat.
return;
}

View File

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

View File

@ -409,7 +409,7 @@ export class CoreUrlUtilsProvider {
* @return Whether the URL is downloadable.
*/
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.
*/
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;
}
/**