Merge pull request #2985 from dpalou/MOBILE-3896
MOBILE-3896 file: Allow downloading tokenpluginfile linked filesmain
commit
89887b8295
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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...
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue