MOBILE-2272 quiz: Support viewing attachments and inline files
parent
b6ed831b25
commit
3f40127661
|
@ -1861,6 +1861,7 @@
|
|||
"core.mainmenu.help": "moodle",
|
||||
"core.mainmenu.logout": "moodle",
|
||||
"core.mainmenu.website": "local_moodlemobileapp",
|
||||
"core.maxfilesize": "moodle",
|
||||
"core.maxsizeandattachments": "moodle",
|
||||
"core.min": "moodle",
|
||||
"core.mins": "moodle",
|
||||
|
@ -1944,8 +1945,8 @@
|
|||
"core.question.certainty": "qbehaviour_deferredcbm",
|
||||
"core.question.complete": "question",
|
||||
"core.question.correct": "question",
|
||||
"core.question.errorattachmentsnotsupported": "local_moodlemobileapp",
|
||||
"core.question.errorinlinefilesnotsupported": "local_moodlemobileapp",
|
||||
"core.question.errorattachmentsnotsupportedinsite": "local_moodlemobileapp",
|
||||
"core.question.errorembeddedfilesnotsupportedinsite": "local_moodlemobileapp",
|
||||
"core.question.errorquestionnotsupported": "local_moodlemobileapp",
|
||||
"core.question.feedback": "question",
|
||||
"core.question.howtodraganddrop": "local_moodlemobileapp",
|
||||
|
|
|
@ -411,7 +411,7 @@ export class AddonModLessonSyncProvider extends CoreCourseActivitySyncBaseProvid
|
|||
const params = this.urlUtils.extractUrlParams(response.data.reviewlesson.value);
|
||||
if (params && params.pageid) {
|
||||
// The retake can be reviewed, mark it as finished. Don't block the user for this.
|
||||
this.setRetakeFinishedInSync(lessonId, retake.retake, params.pageid, siteId);
|
||||
this.setRetakeFinishedInSync(lessonId, retake.retake, Number(params.pageid), siteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
</ion-item>
|
||||
|
||||
<!-- Textarea. -->
|
||||
<ion-item *ngIf="question.textarea && !question.hasDraftFiles">
|
||||
<!-- "Format" hidden input -->
|
||||
<ion-item *ngIf="question.textarea && (!question.hasDraftFiles || uploadFilesSupported)">
|
||||
<!-- "Format" and draftid hidden inputs -->
|
||||
<input item-content *ngIf="question.formatInput" type="hidden" [name]="question.formatInput.name" [value]="question.formatInput.value" >
|
||||
<input item-content *ngIf="question.answerDraftIdInput" type="hidden" [name]="question.answerDraftIdInput.name" [value]="question.answerDraftIdInput.value" >
|
||||
<!-- Plain text textarea. -->
|
||||
<ion-textarea *ngIf="question.isPlainText" class="core-question-textarea" [ngClass]='{"core-monospaced": question.isMonospaced}' placeholder="{{ 'core.question.answer' | translate }}" [attr.name]="question.textarea.name" aria-multiline="true" [ngModel]="question.textarea.text"></ion-textarea>
|
||||
<!-- Rich text editor. -->
|
||||
|
@ -15,22 +16,29 @@
|
|||
</ion-item>
|
||||
|
||||
<!-- Draft files not supported. -->
|
||||
<ng-container *ngIf="question.textarea && question.hasDraftFiles">
|
||||
<ng-container *ngIf="question.textarea && question.hasDraftFiles && !uploadFilesSupported">
|
||||
<ion-item text-wrap class="core-danger-item">
|
||||
<p class="core-question-warning">{{ 'core.question.errorinlinefilesnotsupported' | translate }}</p>
|
||||
<p class="core-question-warning">{{ 'core.question.errorinlinefilesnotsupportedinsite' | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="question.textarea.text" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"></core-format-text></p>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Attachments not supported in the app yet. -->
|
||||
<ion-item text-wrap *ngIf="question.allowsAttachments" class="core-danger-item">
|
||||
<p class="core-question-warning">{{ 'core.question.errorattachmentsnotsupported' | translate }}</p>
|
||||
</ion-item>
|
||||
<!-- Attachments. -->
|
||||
<ng-container *ngIf="question.allowsAttachments">
|
||||
<core-attachments *ngIf="uploadFilesSupported" [files]="attachments" [component]="component" [componentId]="componentId" [maxSize]="question.attachmentsMaxBytes" [maxSubmissions]="question.attachmentsMaxFiles"></core-attachments>
|
||||
|
||||
<input item-content *ngIf="uploadFilesSupported" type="hidden" [name]="question.attachmentsDraftIdInput.name" [value]="question.attachmentsDraftIdInput.value" >
|
||||
|
||||
<!-- Attachments not supported in this site. -->
|
||||
<ion-item text-wrap *ngIf="!uploadFilesSupported" class="core-danger-item">
|
||||
<p class="core-question-warning">{{ 'core.question.errorattachmentsnotsupportedinsite' | translate }}</p>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<!-- Answer to the question and attachments (reviewing). -->
|
||||
<ion-item text-wrap *ngIf="!question.textarea && (question.answer || (!question.attachments.length && !question.allowsAttachments))">
|
||||
<ion-item text-wrap *ngIf="!question.textarea && (question.answer || question.answer == '')">
|
||||
<p><core-format-text [ngClass]='{"core-monospaced": question.isMonospaced}' [component]="component" [componentId]="componentId" [text]="question.answer" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"></core-format-text></p>
|
||||
</ion-item>
|
||||
|
||||
|
|
|
@ -14,8 +14,11 @@
|
|||
|
||||
import { Component, OnInit, Injector } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSites } from '@providers/sites';
|
||||
import { CoreWSExternalFile } from '@providers/ws';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { FormControl, FormBuilder } from '@angular/forms';
|
||||
import { CoreFileSession } from '@providers/file-session';
|
||||
|
||||
/**
|
||||
* Component to render an essay question.
|
||||
|
@ -28,6 +31,9 @@ export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent implemen
|
|||
|
||||
protected formControl: FormControl;
|
||||
|
||||
attachments: CoreWSExternalFile[];
|
||||
uploadFilesSupported: boolean;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, injector: Injector, protected fb: FormBuilder) {
|
||||
super(logger, 'AddonQtypeEssayComponent', injector);
|
||||
}
|
||||
|
@ -36,8 +42,14 @@ export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent implemen
|
|||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.uploadFilesSupported = CoreSites.instance.getCurrentSite().isVersionGreaterEqualThan('3.10');
|
||||
this.initEssayComponent();
|
||||
|
||||
this.formControl = this.fb.control(this.question.textarea && this.question.textarea.text);
|
||||
|
||||
if (this.question.allowsAttachments && this.uploadFilesSupported) {
|
||||
this.attachments = Array.from(this.questionHelper.getResponseFileAreaFiles(this.question, 'attachments'));
|
||||
CoreFileSession.instance.setFiles(this.component, this.componentId + '_' + this.question.id, this.attachments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { CoreSites } from '@providers/sites';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
|
@ -68,11 +69,11 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
|||
|
||||
if (element.querySelector('div[id*=filemanager]')) {
|
||||
// The question allows attachments. Since the app cannot attach files yet we will prevent submitting the question.
|
||||
return 'core.question.errorattachmentsnotsupported';
|
||||
return 'core.question.errorattachmentsnotsupportedinsite';
|
||||
}
|
||||
|
||||
if (this.questionHelper.hasDraftFileUrls(element.innerHTML)) {
|
||||
return 'core.question.errorinlinefilesnotsupported';
|
||||
return 'core.question.errorinlinefilesnotsupportedinsite';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,13 +140,21 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Return a promise resolved when done if async, void if sync.
|
||||
*/
|
||||
prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise<any> {
|
||||
async prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): Promise<void> {
|
||||
const element = this.domUtils.convertToElement(question.html);
|
||||
|
||||
// Search the textarea to get its name.
|
||||
const textarea = <HTMLTextAreaElement> element.querySelector('textarea[name*=_answer]');
|
||||
|
||||
if (textarea && typeof answers[textarea.name] != 'undefined') {
|
||||
if (this.questionHelper.hasDraftFileUrls(question.html) && question.responsefileareas) {
|
||||
// Restore draftfile URLs.
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
answers[textarea.name] = this.textUtils.restoreDraftfileUrls(site.getURL(), answers[textarea.name],
|
||||
question.html, this.questionHelper.getResponseFileAreaFiles(question, 'answer'));
|
||||
}
|
||||
|
||||
// Add some HTML to the text if needed.
|
||||
answers[textarea.name] = this.textUtils.formatHtmlLines(answers[textarea.name]);
|
||||
}
|
||||
|
|
|
@ -1945,8 +1945,8 @@
|
|||
"core.question.certainty": "Certainty",
|
||||
"core.question.complete": "Complete",
|
||||
"core.question.correct": "Correct",
|
||||
"core.question.errorattachmentsnotsupported": "The application doesn't support attaching files to answers yet.",
|
||||
"core.question.errorinlinefilesnotsupported": "The application doesn't support editing inline files yet.",
|
||||
"core.question.errorattachmentsnotsupportedinsite": "Your site doesn't support attaching files to answers yet.",
|
||||
"core.question.errorinlinefilesnotsupportedinsite": "Your site doesn't support editing inline files yet.",
|
||||
"core.question.errorquestionnotsupported": "This question type is not supported by the app: {{$a}}.",
|
||||
"core.question.feedback": "Feedback",
|
||||
"core.question.howtodraganddrop": "Tap to select then tap to drop.",
|
||||
|
|
|
@ -221,13 +221,16 @@ export class CoreSite {
|
|||
|
||||
// Versions of Moodle releases.
|
||||
protected MOODLE_RELEASES = {
|
||||
3.1: 2016052300,
|
||||
3.2: 2016120500,
|
||||
3.3: 2017051503,
|
||||
3.4: 2017111300,
|
||||
3.5: 2018051700,
|
||||
3.6: 2018120300,
|
||||
3.7: 2019052000
|
||||
'3.1': 2016052300,
|
||||
'3.2': 2016120500,
|
||||
'3.3': 2017051503,
|
||||
'3.4': 2017111300,
|
||||
'3.5': 2018051700,
|
||||
'3.6': 2018120300,
|
||||
'3.7': 2019052000,
|
||||
'3.8': 2019111800,
|
||||
'3.9': 2020061500,
|
||||
'3.10': 2020092400, // @todo: Replace with the right value once 3.10 is released.
|
||||
};
|
||||
static MINIMUM_MOODLE_VERSION = '3.1';
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ ion-app.app-root core-rich-text-editor {
|
|||
img {
|
||||
@include padding(null, null, null, 2px);
|
||||
max-width: 95%;
|
||||
width: auto;
|
||||
}
|
||||
&:empty:before {
|
||||
content: attr(data-placeholder-text);
|
||||
|
|
|
@ -407,7 +407,7 @@ export class CoreGradesHelperProvider {
|
|||
if (matches && matches.length) {
|
||||
const hrefParams = this.urlUtils.extractUrlParams(matches[1]);
|
||||
|
||||
return hrefParams && hrefParams.id == moduleId;
|
||||
return hrefParams && Number(hrefParams.id) == moduleId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1152,7 +1152,7 @@ export class CoreLoginHelperProvider {
|
|||
const providerToUse = identityProviders.find((provider) => {
|
||||
const params = this.urlUtils.extractUrlParams(provider.url);
|
||||
|
||||
return params.id == currentSite.getOAuthId();
|
||||
return Number(params.id) == currentSite.getOAuthId();
|
||||
});
|
||||
|
||||
if (providerToUse) {
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
|
||||
import { Input, Output, EventEmitter, Injector, ElementRef } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreSites } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUrlUtils } from '@providers/utils/url';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
|
@ -162,6 +164,8 @@ export class CoreQuestionBaseComponent {
|
|||
const input = questionEl.querySelector('input[type="text"][name*=answer]');
|
||||
this.question.optionsFirst =
|
||||
questionEl.innerHTML.indexOf(input.outerHTML) > questionEl.innerHTML.indexOf(radios[0].outerHTML);
|
||||
|
||||
return questionEl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,27 +205,38 @@ export class CoreQuestionBaseComponent {
|
|||
const questionEl = this.initComponent();
|
||||
|
||||
if (questionEl) {
|
||||
// First search the textarea.
|
||||
const textarea = <HTMLTextAreaElement> questionEl.querySelector('textarea[name*=_answer]');
|
||||
const answerDraftIdInput = <HTMLInputElement> questionEl.querySelector('input[name*="_answer:itemid"]');
|
||||
|
||||
this.question.allowsAttachments = !!questionEl.querySelector('div[id*=filemanager]');
|
||||
this.question.allowsAnswerFiles = !!answerDraftIdInput;
|
||||
this.question.isMonospaced = !!questionEl.querySelector('.qtype_essay_monospaced');
|
||||
this.question.isPlainText = this.question.isMonospaced || !!questionEl.querySelector('.qtype_essay_plain');
|
||||
this.question.hasDraftFiles = this.questionHelper.hasDraftFileUrls(questionEl.innerHTML);
|
||||
this.question.hasDraftFiles = this.question.allowsAnswerFiles &&
|
||||
this.questionHelper.hasDraftFileUrls(questionEl.innerHTML);
|
||||
|
||||
if (!textarea) {
|
||||
// Textarea not found, we might be in review. Search the answer and the attachments.
|
||||
if (!textarea && !this.question.allowsAttachments) {
|
||||
// Textarea and filemanager not found, we might be in review. Search the answer and the attachments.
|
||||
this.question.answer = this.domUtils.getContentsOfElement(questionEl, '.qtype_essay_response');
|
||||
this.question.attachments = this.questionHelper.getQuestionAttachmentsFromHtml(
|
||||
this.domUtils.getContentsOfElement(questionEl, '.attachments'));
|
||||
} else {
|
||||
// Textarea found.
|
||||
const input = <HTMLInputElement> questionEl.querySelector('input[type="hidden"][name*=answerformat]'),
|
||||
content = textarea.innerHTML;
|
||||
|
||||
return questionEl;
|
||||
}
|
||||
|
||||
if (textarea) {
|
||||
const input = <HTMLInputElement> questionEl.querySelector('input[type="hidden"][name*=answerformat]');
|
||||
let content = this.textUtils.decodeHTML(textarea.innerHTML || '');
|
||||
|
||||
if (this.question.hasDraftFiles && this.question.responsefileareas) {
|
||||
content = this.textUtils.replaceDraftfileUrls(CoreSites.instance.getCurrentSite().getURL(), content,
|
||||
this.questionHelper.getResponseFileAreaFiles(this.question, 'answer')).text;
|
||||
}
|
||||
|
||||
this.question.textarea = {
|
||||
id: textarea.id,
|
||||
name: textarea.name,
|
||||
text: content ? this.textUtils.decodeHTML(content) : ''
|
||||
text: content,
|
||||
};
|
||||
|
||||
if (input) {
|
||||
|
@ -231,6 +246,38 @@ export class CoreQuestionBaseComponent {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (answerDraftIdInput) {
|
||||
this.question.answerDraftIdInput = {
|
||||
name: answerDraftIdInput.name,
|
||||
value: Number(answerDraftIdInput.value),
|
||||
};
|
||||
}
|
||||
|
||||
if (this.question.allowsAttachments) {
|
||||
const attachmentsInput = <HTMLInputElement> questionEl.querySelector('.attachments input[name*=_attachments]');
|
||||
const objectElement = <HTMLObjectElement> questionEl.querySelector('.attachments object');
|
||||
const fileManagerUrl = objectElement && objectElement.data;
|
||||
|
||||
if (attachmentsInput) {
|
||||
this.question.attachmentsDraftIdInput = {
|
||||
name: attachmentsInput.name,
|
||||
value: Number(attachmentsInput.value),
|
||||
};
|
||||
}
|
||||
|
||||
if (fileManagerUrl) {
|
||||
const params = CoreUrlUtils.instance.extractUrlParams(fileManagerUrl);
|
||||
const maxBytes = Number(params.maxbytes);
|
||||
const areaMaxBytes = Number(params.areamaxbytes);
|
||||
|
||||
this.question.attachmentsMaxFiles = Number(params.maxfiles);
|
||||
this.question.attachmentsMaxBytes = maxBytes === -1 || areaMaxBytes === -1 ?
|
||||
Math.max(maxBytes, areaMaxBytes) : Math.min(maxBytes, areaMaxBytes);
|
||||
}
|
||||
}
|
||||
|
||||
return questionEl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,6 +317,8 @@ export class CoreQuestionBaseComponent {
|
|||
|
||||
// Set the question text.
|
||||
this.question.text = content.innerHTML;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
"certainty": "Certainty",
|
||||
"complete": "Complete",
|
||||
"correct": "Correct",
|
||||
"errorattachmentsnotsupported": "The application doesn't support attaching files to answers yet.",
|
||||
"errorinlinefilesnotsupported": "The application doesn't support editing inline files yet.",
|
||||
"errorattachmentsnotsupportedinsite": "Your site doesn't support attaching files to answers yet.",
|
||||
"errorinlinefilesnotsupportedinsite": "Your site doesn't support editing inline files yet.",
|
||||
"errorquestionnotsupported": "This question type is not supported by the app: {{$a}}.",
|
||||
"feedback": "Feedback",
|
||||
"howtodraganddrop": "Tap to select then tap to drop.",
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Injectable, EventEmitter } from '@angular/core';
|
|||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreWSExternalFile } from '@providers/ws';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
|
@ -421,6 +422,25 @@ export class CoreQuestionHelperProvider {
|
|||
return state ? state.class : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the files of a certain response file area.
|
||||
*
|
||||
* @param question Question.
|
||||
* @param areaName Name of the area, e.g. 'attachments'.
|
||||
* @return List of files.
|
||||
*/
|
||||
getResponseFileAreaFiles(question: any, areaName: string): CoreWSExternalFile[] {
|
||||
if (!question.responsefileareas) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const area = question.responsefileareas.find((area) => {
|
||||
return area.area == areaName;
|
||||
});
|
||||
|
||||
return area && area.files || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation error message from a question HTML if it's there.
|
||||
*
|
||||
|
|
|
@ -19,6 +19,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { CoreLangProvider } from '../lang';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
import { CoreApp } from '../app';
|
||||
import { CoreWSExternalFile } from '../ws';
|
||||
|
||||
/**
|
||||
* Different type of errors the app can treat.
|
||||
|
@ -699,6 +700,60 @@ export class CoreTextUtilsProvider {
|
|||
return text.replace(/(?:\r\n|\r|\n)/g, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace draftfile URLs with the equivalent pluginfile URL.
|
||||
*
|
||||
* @param siteUrl URL of the site.
|
||||
* @param text Text to treat, including draftfile URLs.
|
||||
* @param files List of files of the area, using pluginfile URLs.
|
||||
* @return Treated text and map with the replacements.
|
||||
*/
|
||||
replaceDraftfileUrls(siteUrl: string, text: string, files: CoreWSExternalFile[])
|
||||
: {text: string, replaceMap?: {[url: string]: string}} {
|
||||
|
||||
if (!text || !files || !files.length) {
|
||||
return {text};
|
||||
}
|
||||
|
||||
const draftfileUrl = this.concatenatePaths(siteUrl, 'draftfile.php');
|
||||
const matches = text.match(new RegExp(this.escapeForRegex(draftfileUrl) + '[^\'" ]+', 'ig'));
|
||||
|
||||
if (!matches || !matches.length) {
|
||||
return {text};
|
||||
}
|
||||
|
||||
// Index the pluginfile URLs by file name.
|
||||
const pluginfileMap: {[name: string]: string} = {};
|
||||
files.forEach((file) => {
|
||||
pluginfileMap[file.filename] = file.fileurl;
|
||||
});
|
||||
|
||||
// Replace each draftfile with the corresponding pluginfile URL.
|
||||
const replaceMap: {[url: string]: string} = {};
|
||||
matches.forEach((url) => {
|
||||
if (replaceMap[url]) {
|
||||
// URL already treated, same file embedded more than once.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the filename from the URL.
|
||||
let filename = url.substr(url.lastIndexOf('/') + 1);
|
||||
if (filename.indexOf('?') != -1) {
|
||||
filename = filename.substr(0, filename.indexOf('?'));
|
||||
}
|
||||
|
||||
if (pluginfileMap[filename]) {
|
||||
replaceMap[url] = pluginfileMap[filename];
|
||||
text = text.replace(new RegExp(this.escapeForRegex(url), 'g'), pluginfileMap[filename]);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
text,
|
||||
replaceMap,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace @@PLUGINFILE@@ wildcards with the real URL in a text.
|
||||
*
|
||||
|
@ -717,6 +772,36 @@ export class CoreTextUtilsProvider {
|
|||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore original draftfile URLs.
|
||||
*
|
||||
* @param text Text to treat, including pluginfile URLs.
|
||||
* @param replaceMap Map of the replacements that were done.
|
||||
* @return Treated text.
|
||||
*/
|
||||
restoreDraftfileUrls(siteUrl: string, treatedText: string, originalText: string, files: CoreWSExternalFile[]): string {
|
||||
if (!treatedText || !files || !files.length) {
|
||||
return treatedText;
|
||||
}
|
||||
|
||||
const draftfileUrl = this.concatenatePaths(siteUrl, 'draftfile.php');
|
||||
const draftfileUrlRegexPrefix = this.escapeForRegex(draftfileUrl) + '/[^/]+/[^/]+/[^/]+/[^/]+/';
|
||||
|
||||
files.forEach((file) => {
|
||||
// Search the draftfile URL in the original text.
|
||||
const matches = originalText.match(new RegExp(
|
||||
draftfileUrlRegexPrefix + this.escapeForRegex(file.filename) + '[^\'" ]*', 'i'));
|
||||
|
||||
if (!matches || !matches[0]) {
|
||||
return; // Original URL not found, skip.
|
||||
}
|
||||
|
||||
treatedText = treatedText.replace(new RegExp(this.escapeForRegex(file.fileurl), 'g'), matches[0]);
|
||||
});
|
||||
|
||||
return treatedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace pluginfile URLs with @@PLUGINFILE@@ wildcards.
|
||||
*
|
||||
|
|
|
@ -114,10 +114,10 @@ export class CoreUrlUtilsProvider {
|
|||
* @param url URL to treat.
|
||||
* @return Object with the params.
|
||||
*/
|
||||
extractUrlParams(url: string): any {
|
||||
extractUrlParams(url: string): {[name: string]: string} {
|
||||
const regex = /[?&]+([^=&]+)=?([^&]*)?/gi,
|
||||
subParamsPlaceholder = '@@@SUBPARAMS@@@',
|
||||
params: any = {},
|
||||
params: {[name: string]: string} = {},
|
||||
urlAndHash = url.split('#'),
|
||||
questionMarkSplit = urlAndHash[0].split('?');
|
||||
let subParams;
|
||||
|
|
Loading…
Reference in New Issue