Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -51,7 +51,7 @@ function hook_editor_js_settings_alter(array &$settings) {
|
|||
* @param \Drupal\filter\FilterFormatInterface $format
|
||||
* The text format configuration entity. Provides context based upon which
|
||||
* one may want to adjust the filtering.
|
||||
* @param \Drupal\filter\FilterFormatInterface $original_format|null
|
||||
* @param \Drupal\filter\FilterFormatInterface|null $original_format
|
||||
* (optional) The original text format configuration entity (when switching
|
||||
* text formats/editors). Also provides context based upon which one may want
|
||||
* to adjust the filtering.
|
||||
|
|
|
@ -5,6 +5,6 @@ package: Core
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- filter
|
||||
- file
|
||||
- drupal:filter
|
||||
- drupal:file
|
||||
configure: filter.admin_overview
|
||||
|
|
|
@ -245,7 +245,9 @@ function editor_form_filter_admin_format_submit($form, FormStateInterface $form_
|
|||
// Ensure the text format is set: when creating a new text format, this
|
||||
// would equal the empty string.
|
||||
$editor->set('format', $format_id);
|
||||
$editor->setSettings($form_state->getValue(['editor', 'settings']));
|
||||
if ($settings = $form_state->getValue(['editor', 'settings'])) {
|
||||
$editor->setSettings($settings);
|
||||
}
|
||||
$editor->save();
|
||||
}
|
||||
}
|
||||
|
@ -276,7 +278,7 @@ function editor_load($format_id) {
|
|||
* @param \Drupal\filter\FilterFormatInterface|null $format
|
||||
* The text format whose text editor will be used or NULL if the previously
|
||||
* defined text format is now disabled.
|
||||
* @param \Drupal\filter\FilterFormatInterface $original_format|null
|
||||
* @param \Drupal\filter\FilterFormatInterface|null $original_format
|
||||
* (optional) The original text format (i.e. when switching text formats,
|
||||
* $format is the text format that is going to be used, $original_format is
|
||||
* the one that was being used initially, the one that is stored in the
|
||||
|
@ -432,7 +434,7 @@ function editor_entity_revision_delete(EntityInterface $entity) {
|
|||
*
|
||||
* @param array $uuids
|
||||
* An array of file entity UUIDs.
|
||||
* @param EntityInterface $entity
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity whose fields to inspect for file references.
|
||||
*/
|
||||
function _editor_record_file_usage(array $uuids, EntityInterface $entity) {
|
||||
|
@ -452,7 +454,7 @@ function _editor_record_file_usage(array $uuids, EntityInterface $entity) {
|
|||
*
|
||||
* @param array $uuids
|
||||
* An array of file entity UUIDs.
|
||||
* @param EntityInterface $entity
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity whose fields to inspect for file references.
|
||||
* @param $count
|
||||
* The number of references to delete. Should be 1 when deleting a single
|
||||
|
@ -543,7 +545,7 @@ function editor_file_download($uri) {
|
|||
/**
|
||||
* Finds all files referenced (data-entity-uuid) by formatted text fields.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity whose fields to analyze.
|
||||
*
|
||||
* @return array
|
||||
|
|
1038
web/core/modules/editor/js/editor.admin.es6.js
Normal file
1038
web/core/modules/editor/js/editor.admin.es6.js
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
34
web/core/modules/editor/js/editor.dialog.es6.js
Normal file
34
web/core/modules/editor/js/editor.dialog.es6.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @file
|
||||
* AJAX commands used by Editor module.
|
||||
*/
|
||||
|
||||
(function($, Drupal) {
|
||||
/**
|
||||
* Command to save the contents of an editor-provided modal.
|
||||
*
|
||||
* This command does not close the open modal. It should be followed by a
|
||||
* call to `Drupal.AjaxCommands.prototype.closeDialog`. Editors that are
|
||||
* integrated with dialogs must independently listen for an
|
||||
* `editor:dialogsave` event to save the changes into the contents of their
|
||||
* interface.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The Drupal.Ajax object.
|
||||
* @param {object} response
|
||||
* The server response from the ajax request.
|
||||
* @param {Array} response.values
|
||||
* The values that were saved.
|
||||
* @param {number} [status]
|
||||
* The status code from the ajax request.
|
||||
*
|
||||
* @fires event:editor:dialogsave
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.editorDialogSave = function(
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
$(window).trigger('editor:dialogsave', [response.values]);
|
||||
};
|
||||
})(jQuery, Drupal);
|
|
@ -1,34 +1,12 @@
|
|||
/**
|
||||
* @file
|
||||
* AJAX commands used by Editor module.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Command to save the contents of an editor-provided modal.
|
||||
*
|
||||
* This command does not close the open modal. It should be followed by a
|
||||
* call to `Drupal.AjaxCommands.prototype.closeDialog`. Editors that are
|
||||
* integrated with dialogs must independently listen for an
|
||||
* `editor:dialogsave` event to save the changes into the contents of their
|
||||
* interface.
|
||||
*
|
||||
* @param {Drupal.Ajax} [ajax]
|
||||
* The Drupal.Ajax object.
|
||||
* @param {object} response
|
||||
* The server response from the ajax request.
|
||||
* @param {Array} response.values
|
||||
* The values that were saved.
|
||||
* @param {number} [status]
|
||||
* The status code from the ajax request.
|
||||
*
|
||||
* @fires event:editor:dialogsave
|
||||
*/
|
||||
Drupal.AjaxCommands.prototype.editorDialogSave = function (ajax, response, status) {
|
||||
$(window).trigger('editor:dialogsave', [response.values]);
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
||||
})(jQuery, Drupal);
|
345
web/core/modules/editor/js/editor.es6.js
Normal file
345
web/core/modules/editor/js/editor.es6.js
Normal file
|
@ -0,0 +1,345 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches behavior for the Editor module.
|
||||
*/
|
||||
|
||||
(function($, Drupal, drupalSettings) {
|
||||
/**
|
||||
* Finds the text area field associated with the given text format selector.
|
||||
*
|
||||
* @param {jQuery} $formatSelector
|
||||
* A text format selector DOM element.
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
* The text area DOM element, if it was found.
|
||||
*/
|
||||
function findFieldForFormatSelector($formatSelector) {
|
||||
const fieldId = $formatSelector.attr('data-editor-for');
|
||||
// This selector will only find text areas in the top-level document. We do
|
||||
// not support attaching editors on text areas within iframes.
|
||||
return $(`#${fieldId}`).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter away XSS attack vectors when switching text formats.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} originalFormatID
|
||||
* The text format ID of the original text format.
|
||||
* @param {function} callback
|
||||
* A callback to be called (with no parameters) after the field's value has
|
||||
* been XSS filtered.
|
||||
*/
|
||||
function filterXssWhenSwitching(field, format, originalFormatID, callback) {
|
||||
// A text editor that already is XSS-safe needs no additional measures.
|
||||
if (format.editor.isXssSafe) {
|
||||
callback(field, format);
|
||||
}
|
||||
// Otherwise, ensure XSS safety: let the server XSS filter this value.
|
||||
else {
|
||||
$.ajax({
|
||||
url: Drupal.url(`editor/filter_xss/${format.format}`),
|
||||
type: 'POST',
|
||||
data: {
|
||||
value: field.value,
|
||||
original_format_id: originalFormatID,
|
||||
},
|
||||
dataType: 'json',
|
||||
success(xssFilteredValue) {
|
||||
// If the server returns false, then no XSS filtering is needed.
|
||||
if (xssFilteredValue !== false) {
|
||||
field.value = xssFilteredValue;
|
||||
}
|
||||
callback(field, format);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the text editor on a text area.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The text area DOM element.
|
||||
* @param {string} newFormatID
|
||||
* The text format we're changing to; the text editor for the currently
|
||||
* active text format will be detached, and the text editor for the new text
|
||||
* format will be attached.
|
||||
*/
|
||||
function changeTextEditor(field, newFormatID) {
|
||||
const previousFormatID = field.getAttribute(
|
||||
'data-editor-active-text-format',
|
||||
);
|
||||
|
||||
// Detach the current editor (if any) and attach a new editor.
|
||||
if (drupalSettings.editor.formats[previousFormatID]) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
drupalSettings.editor.formats[previousFormatID],
|
||||
);
|
||||
}
|
||||
// When no text editor is currently active, stop tracking changes.
|
||||
else {
|
||||
$(field).off('.editor');
|
||||
}
|
||||
|
||||
// Attach the new text editor (if any).
|
||||
if (drupalSettings.editor.formats[newFormatID]) {
|
||||
const format = drupalSettings.editor.formats[newFormatID];
|
||||
filterXssWhenSwitching(
|
||||
field,
|
||||
format,
|
||||
previousFormatID,
|
||||
Drupal.editorAttach,
|
||||
);
|
||||
}
|
||||
|
||||
// Store the new active format.
|
||||
field.setAttribute('data-editor-active-text-format', newFormatID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in text format.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* The text format change event.
|
||||
*/
|
||||
function onTextFormatChange(event) {
|
||||
const $select = $(event.target);
|
||||
const field = event.data.field;
|
||||
const activeFormatID = field.getAttribute('data-editor-active-text-format');
|
||||
const newFormatID = $select.val();
|
||||
|
||||
// Prevent double-attaching if the change event is triggered manually.
|
||||
if (newFormatID === activeFormatID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When changing to a text format that has a text editor associated
|
||||
// with it that supports content filtering, then first ask for
|
||||
// confirmation, because switching text formats might cause certain
|
||||
// markup to be stripped away.
|
||||
const supportContentFiltering =
|
||||
drupalSettings.editor.formats[newFormatID] &&
|
||||
drupalSettings.editor.formats[newFormatID].editorSupportsContentFiltering;
|
||||
// If there is no content yet, it's always safe to change the text format.
|
||||
const hasContent = field.value !== '';
|
||||
if (hasContent && supportContentFiltering) {
|
||||
const message = Drupal.t(
|
||||
'Changing the text format to %text_format will permanently remove content that is not allowed in that text format.<br><br>Save your changes before switching the text format to avoid losing data.',
|
||||
{
|
||||
'%text_format': $select.find('option:selected').text(),
|
||||
},
|
||||
);
|
||||
const confirmationDialog = Drupal.dialog(`<div>${message}</div>`, {
|
||||
title: Drupal.t('Change text format?'),
|
||||
dialogClass: 'editor-change-text-format-modal',
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: Drupal.t('Continue'),
|
||||
class: 'button button--primary',
|
||||
click() {
|
||||
changeTextEditor(field, newFormatID);
|
||||
confirmationDialog.close();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: Drupal.t('Cancel'),
|
||||
class: 'button',
|
||||
click() {
|
||||
// Restore the active format ID: cancel changing text format. We
|
||||
// cannot simply call event.preventDefault() because jQuery's
|
||||
// change event is only triggered after the change has already
|
||||
// been accepted.
|
||||
$select.val(activeFormatID);
|
||||
confirmationDialog.close();
|
||||
},
|
||||
},
|
||||
],
|
||||
// Prevent this modal from being closed without the user making a choice
|
||||
// as per http://stackoverflow.com/a/5438771.
|
||||
closeOnEscape: false,
|
||||
create() {
|
||||
$(this)
|
||||
.parent()
|
||||
.find('.ui-dialog-titlebar-close')
|
||||
.remove();
|
||||
},
|
||||
beforeClose: false,
|
||||
close(event) {
|
||||
// Automatically destroy the DOM element that was used for the dialog.
|
||||
$(event.target).remove();
|
||||
},
|
||||
});
|
||||
|
||||
confirmationDialog.showModal();
|
||||
} else {
|
||||
changeTextEditor(field, newFormatID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an empty object for editors to place their attachment code.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
Drupal.editors = {};
|
||||
|
||||
/**
|
||||
* Enables editors on text_format elements.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches an editor to an input element.
|
||||
* @prop {Drupal~behaviorDetach} detach
|
||||
* Detaches an editor from an input element.
|
||||
*/
|
||||
Drupal.behaviors.editor = {
|
||||
attach(context, settings) {
|
||||
// If there are no editor settings, there are no editors to enable.
|
||||
if (!settings.editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(context)
|
||||
.find('[data-editor-for]')
|
||||
.once('editor')
|
||||
.each(function() {
|
||||
const $this = $(this);
|
||||
const field = findFieldForFormatSelector($this);
|
||||
|
||||
// Opt-out if no supported text area was found.
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the current active format.
|
||||
const activeFormatID = $this.val();
|
||||
field.setAttribute('data-editor-active-text-format', activeFormatID);
|
||||
|
||||
// Directly attach this text editor, if the text format is enabled.
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
// XSS protection for the current text format/editor is performed on
|
||||
// the server side, so we don't need to do anything special here.
|
||||
Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
|
||||
}
|
||||
// When there is no text editor for this text format, still track
|
||||
// changes, because the user has the ability to switch to some text
|
||||
// editor, otherwise this code would not be executed.
|
||||
$(field).on('change.editor keypress.editor', () => {
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
// Just knowing that the value was changed is enough, stop tracking.
|
||||
$(field).off('.editor');
|
||||
});
|
||||
|
||||
// Attach onChange handler to text format selector element.
|
||||
if ($this.is('select')) {
|
||||
$this.on('change.editorAttach', { field }, onTextFormatChange);
|
||||
}
|
||||
// Detach any editor when the containing form is submitted.
|
||||
$this.parents('form').on('submit', event => {
|
||||
// Do not detach if the event was canceled.
|
||||
if (event.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
// Detach the current editor (if any).
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
settings.editor.formats[activeFormatID],
|
||||
'serialize',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
detach(context, settings, trigger) {
|
||||
let editors;
|
||||
// The 'serialize' trigger indicates that we should simply update the
|
||||
// underlying element with the new text, without destroying the editor.
|
||||
if (trigger === 'serialize') {
|
||||
// Removing the editor-processed class guarantees that the editor will
|
||||
// be reattached. Only do this if we're planning to destroy the editor.
|
||||
editors = $(context)
|
||||
.find('[data-editor-for]')
|
||||
.findOnce('editor');
|
||||
} else {
|
||||
editors = $(context)
|
||||
.find('[data-editor-for]')
|
||||
.removeOnce('editor');
|
||||
}
|
||||
|
||||
editors.each(function() {
|
||||
const $this = $(this);
|
||||
const activeFormatID = $this.val();
|
||||
const field = findFieldForFormatSelector($this);
|
||||
if (field && activeFormatID in settings.editor.formats) {
|
||||
Drupal.editorDetach(
|
||||
field,
|
||||
settings.editor.formats[activeFormatID],
|
||||
trigger,
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches editor behaviors to the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
*
|
||||
* @listens event:change
|
||||
*
|
||||
* @fires event:formUpdated
|
||||
*/
|
||||
Drupal.editorAttach = function(field, format) {
|
||||
if (format.editor) {
|
||||
// Attach the text editor.
|
||||
Drupal.editors[format.editor].attach(field, format);
|
||||
|
||||
// Ensures form.js' 'formUpdated' event is triggered even for changes that
|
||||
// happen within the text editor.
|
||||
Drupal.editors[format.editor].onChange(field, () => {
|
||||
$(field).trigger('formUpdated');
|
||||
|
||||
// Keep track of changes, so we know what to do when switching text
|
||||
// formats and guaranteeing XSS protection.
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches editor behaviors from the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} trigger
|
||||
* Trigger value from the detach behavior.
|
||||
*/
|
||||
Drupal.editorDetach = function(field, format, trigger) {
|
||||
if (format.editor) {
|
||||
Drupal.editors[format.editor].detach(field, format, trigger);
|
||||
|
||||
// Restore the original value if the user didn't make any changes yet.
|
||||
if (field.getAttribute('data-editor-value-is-changed') === 'false') {
|
||||
field.value = field.getAttribute('data-editor-value-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery, Drupal, drupalSettings);
|
244
web/core/modules/editor/js/editor.formattedTextEditor.es6.js
Normal file
244
web/core/modules/editor/js/editor.formattedTextEditor.es6.js
Normal file
|
@ -0,0 +1,244 @@
|
|||
/**
|
||||
* @file
|
||||
* Text editor-based in-place editor for formatted text content in Drupal.
|
||||
*
|
||||
* Depends on editor.module. Works with any (WYSIWYG) editor that implements the
|
||||
* editor.js API, including the optional attachInlineEditor() and onChange()
|
||||
* methods.
|
||||
* For example, assuming that a hypothetical editor's name was "Magical Editor"
|
||||
* and its editor.js API implementation lived at Drupal.editors.magical, this
|
||||
* JavaScript would use:
|
||||
* - Drupal.editors.magical.attachInlineEditor()
|
||||
*/
|
||||
|
||||
(function($, Drupal, drupalSettings, _) {
|
||||
Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend(
|
||||
/** @lends Drupal.quickedit.editors.editor# */ {
|
||||
/**
|
||||
* The text format for this field.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
textFormat: null,
|
||||
|
||||
/**
|
||||
* Indicates whether this text format has transformations.
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
textFormatHasTransformations: null,
|
||||
|
||||
/**
|
||||
* Stores a reference to the text editor object for this field.
|
||||
*
|
||||
* @type {Drupal.quickedit.EditorModel}
|
||||
*/
|
||||
textEditor: null,
|
||||
|
||||
/**
|
||||
* Stores the textual DOM element that is being in-place edited.
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
$textElement: null,
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the editor view.
|
||||
*/
|
||||
initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
|
||||
const metadata = Drupal.quickedit.metadata.get(
|
||||
this.fieldModel.get('fieldID'),
|
||||
'custom',
|
||||
);
|
||||
this.textFormat = drupalSettings.editor.formats[metadata.format];
|
||||
this.textFormatHasTransformations = metadata.formatHasTransformations;
|
||||
this.textEditor = Drupal.editors[this.textFormat.editor];
|
||||
|
||||
// Store the actual value of this field. We'll need this to restore the
|
||||
// original value when the user discards his modifications.
|
||||
const $fieldItems = this.$el.find('.quickedit-field');
|
||||
if ($fieldItems.length) {
|
||||
this.$textElement = $fieldItems.eq(0);
|
||||
} else {
|
||||
this.$textElement = this.$el;
|
||||
}
|
||||
this.model.set('originalValue', this.$textElement.html());
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The text element edited.
|
||||
*/
|
||||
getEditedElement() {
|
||||
return this.$textElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {object} fieldModel
|
||||
* The field model.
|
||||
* @param {string} state
|
||||
* The current state.
|
||||
*/
|
||||
stateChange(fieldModel, state) {
|
||||
const editorModel = this.model;
|
||||
const from = fieldModel.previous('state');
|
||||
const to = state;
|
||||
switch (to) {
|
||||
case 'inactive':
|
||||
break;
|
||||
|
||||
case 'candidate':
|
||||
// Detach the text editor when entering the 'candidate' state from one
|
||||
// of the states where it could have been attached.
|
||||
if (from !== 'inactive' && from !== 'highlighted') {
|
||||
this.textEditor.detach(this.$textElement.get(0), this.textFormat);
|
||||
}
|
||||
// A field model's editor view revert() method is invoked when an
|
||||
// 'active' field becomes a 'candidate' field. But, in the case of
|
||||
// this in-place editor, the content will have been *replaced* if the
|
||||
// text format has transformation filters. Therefore, if we stop
|
||||
// in-place editing this entity, revert explicitly.
|
||||
if (from === 'active' && this.textFormatHasTransformations) {
|
||||
this.revert();
|
||||
}
|
||||
if (from === 'invalid') {
|
||||
this.removeValidationErrors();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'highlighted':
|
||||
break;
|
||||
|
||||
case 'activating':
|
||||
// When transformation filters have been applied to the formatted text
|
||||
// of this field, then we'll need to load a re-formatted version of it
|
||||
// without the transformation filters.
|
||||
if (this.textFormatHasTransformations) {
|
||||
const $textElement = this.$textElement;
|
||||
this._getUntransformedText(untransformedText => {
|
||||
$textElement.html(untransformedText);
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
}
|
||||
// When no transformation filters have been applied: start WYSIWYG
|
||||
// editing immediately!
|
||||
else {
|
||||
// Defer updating the model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(() => {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'active': {
|
||||
const textElement = this.$textElement.get(0);
|
||||
const toolbarView = fieldModel.toolbarView;
|
||||
this.textEditor.attachInlineEditor(
|
||||
textElement,
|
||||
this.textFormat,
|
||||
toolbarView.getMainWysiwygToolgroupId(),
|
||||
toolbarView.getFloatedWysiwygToolgroupId(),
|
||||
);
|
||||
// Set the state to 'changed' whenever the content has changed.
|
||||
this.textEditor.onChange(textElement, htmlText => {
|
||||
editorModel.set('currentValue', htmlText);
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'changed':
|
||||
break;
|
||||
|
||||
case 'saving':
|
||||
if (from === 'invalid') {
|
||||
this.removeValidationErrors();
|
||||
}
|
||||
this.save();
|
||||
break;
|
||||
|
||||
case 'saved':
|
||||
break;
|
||||
|
||||
case 'invalid':
|
||||
this.showValidationErrors();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return {object}
|
||||
* The settings for the quick edit UI.
|
||||
*/
|
||||
getQuickEditUISettings() {
|
||||
return {
|
||||
padding: true,
|
||||
unifiedToolbar: true,
|
||||
fullWidthToolbar: true,
|
||||
popup: false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert() {
|
||||
this.$textElement.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads untransformed text for this field.
|
||||
*
|
||||
* More accurately: it re-filters formatted text to exclude transformation
|
||||
* filters used by the text format.
|
||||
*
|
||||
* @param {function} callback
|
||||
* A callback function that will receive the untransformed text.
|
||||
*
|
||||
* @see \Drupal\editor\Ajax\GetUntransformedTextCommand
|
||||
*/
|
||||
_getUntransformedText(callback) {
|
||||
const fieldID = this.fieldModel.get('fieldID');
|
||||
|
||||
// Create a Drupal.ajax instance to load the form.
|
||||
const textLoaderAjax = Drupal.ajax({
|
||||
url: Drupal.quickedit.util.buildUrl(
|
||||
fieldID,
|
||||
Drupal.url(
|
||||
'editor/!entity_type/!id/!field_name/!langcode/!view_mode',
|
||||
),
|
||||
),
|
||||
submit: { nocssjs: true },
|
||||
});
|
||||
|
||||
// Implement a scoped editorGetUntransformedText AJAX command: calls the
|
||||
// callback.
|
||||
textLoaderAjax.commands.editorGetUntransformedText = function(
|
||||
ajax,
|
||||
response,
|
||||
status,
|
||||
) {
|
||||
callback(response.data);
|
||||
};
|
||||
|
||||
// This will ensure our scoped editorGetUntransformedText AJAX command
|
||||
// gets called.
|
||||
textLoaderAjax.execute();
|
||||
},
|
||||
},
|
||||
);
|
||||
})(jQuery, Drupal, drupalSettings, _);
|
|
@ -1,59 +1,21 @@
|
|||
/**
|
||||
* @file
|
||||
* Text editor-based in-place editor for formatted text content in Drupal.
|
||||
*
|
||||
* Depends on editor.module. Works with any (WYSIWYG) editor that implements the
|
||||
* editor.js API, including the optional attachInlineEditor() and onChange()
|
||||
* methods.
|
||||
* For example, assuming that a hypothetical editor's name was "Magical Editor"
|
||||
* and its editor.js API implementation lived at Drupal.editors.magical, this
|
||||
* JavaScript would use:
|
||||
* - Drupal.editors.magical.attachInlineEditor()
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal, drupalSettings, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.editor# */{
|
||||
|
||||
/**
|
||||
* The text format for this field.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend({
|
||||
textFormat: null,
|
||||
|
||||
/**
|
||||
* Indicates whether this text format has transformations.
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
textFormatHasTransformations: null,
|
||||
|
||||
/**
|
||||
* Stores a reference to the text editor object for this field.
|
||||
*
|
||||
* @type {Drupal.quickedit.EditorModel}
|
||||
*/
|
||||
textEditor: null,
|
||||
|
||||
/**
|
||||
* Stores the textual DOM element that is being in-place edited.
|
||||
*
|
||||
* @type {jQuery}
|
||||
*/
|
||||
$textElement: null,
|
||||
|
||||
/**
|
||||
* @constructs
|
||||
*
|
||||
* @augments Drupal.quickedit.EditorView
|
||||
*
|
||||
* @param {object} options
|
||||
* Options for the editor view.
|
||||
*/
|
||||
initialize: function (options) {
|
||||
initialize: function initialize(options) {
|
||||
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
|
||||
|
||||
var metadata = Drupal.quickedit.metadata.get(this.fieldModel.get('fieldID'), 'custom');
|
||||
|
@ -61,37 +23,18 @@
|
|||
this.textFormatHasTransformations = metadata.formatHasTransformations;
|
||||
this.textEditor = Drupal.editors[this.textFormat.editor];
|
||||
|
||||
// Store the actual value of this field. We'll need this to restore the
|
||||
// original value when the user discards his modifications.
|
||||
var $fieldItems = this.$el.find('.quickedit-field');
|
||||
if ($fieldItems.length) {
|
||||
this.$textElement = $fieldItems.eq(0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.$textElement = this.$el;
|
||||
}
|
||||
this.model.set('originalValue', this.$textElement.html());
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return {jQuery}
|
||||
* The text element edited.
|
||||
*/
|
||||
getEditedElement: function () {
|
||||
getEditedElement: function getEditedElement() {
|
||||
return this.$textElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {object} fieldModel
|
||||
* The field model.
|
||||
* @param {string} state
|
||||
* The current state.
|
||||
*/
|
||||
stateChange: function (fieldModel, state) {
|
||||
stateChange: function stateChange(fieldModel, state) {
|
||||
var editorModel = this.model;
|
||||
var from = fieldModel.previous('state');
|
||||
var to = state;
|
||||
|
@ -100,16 +43,10 @@
|
|||
break;
|
||||
|
||||
case 'candidate':
|
||||
// Detach the text editor when entering the 'candidate' state from one
|
||||
// of the states where it could have been attached.
|
||||
if (from !== 'inactive' && from !== 'highlighted') {
|
||||
this.textEditor.detach(this.$textElement.get(0), this.textFormat);
|
||||
}
|
||||
// A field model's editor view revert() method is invoked when an
|
||||
// 'active' field becomes a 'candidate' field. But, in the case of
|
||||
// this in-place editor, the content will have been *replaced* if the
|
||||
// text format has transformation filters. Therefore, if we stop
|
||||
// in-place editing this entity, revert explicitly.
|
||||
|
||||
if (from === 'active' && this.textFormatHasTransformations) {
|
||||
this.revert();
|
||||
}
|
||||
|
@ -122,42 +59,31 @@
|
|||
break;
|
||||
|
||||
case 'activating':
|
||||
// When transformation filters have been applied to the formatted text
|
||||
// of this field, then we'll need to load a re-formatted version of it
|
||||
// without the transformation filters.
|
||||
if (this.textFormatHasTransformations) {
|
||||
var $textElement = this.$textElement;
|
||||
this._getUntransformedText(function (untransformedText) {
|
||||
$textElement.html(untransformedText);
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
}
|
||||
// When no transformation filters have been applied: start WYSIWYG
|
||||
// editing immediately!
|
||||
else {
|
||||
// Defer updating the model until the current state change has
|
||||
// propagated, to not trigger a nested state change event.
|
||||
_.defer(function () {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
_.defer(function () {
|
||||
fieldModel.set('state', 'active');
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'active':
|
||||
var textElement = this.$textElement.get(0);
|
||||
var toolbarView = fieldModel.toolbarView;
|
||||
this.textEditor.attachInlineEditor(
|
||||
textElement,
|
||||
this.textFormat,
|
||||
toolbarView.getMainWysiwygToolgroupId(),
|
||||
toolbarView.getFloatedWysiwygToolgroupId()
|
||||
);
|
||||
// Set the state to 'changed' whenever the content has changed.
|
||||
this.textEditor.onChange(textElement, function (htmlText) {
|
||||
editorModel.set('currentValue', htmlText);
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
break;
|
||||
{
|
||||
var textElement = this.$textElement.get(0);
|
||||
var toolbarView = fieldModel.toolbarView;
|
||||
this.textEditor.attachInlineEditor(textElement, this.textFormat, toolbarView.getMainWysiwygToolgroupId(), toolbarView.getFloatedWysiwygToolgroupId());
|
||||
|
||||
this.textEditor.onChange(textElement, function (htmlText) {
|
||||
editorModel.set('currentValue', htmlText);
|
||||
fieldModel.set('state', 'changed');
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'changed':
|
||||
break;
|
||||
|
@ -177,55 +103,30 @@
|
|||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return {object}
|
||||
* The sttings for the quick edit UI.
|
||||
*/
|
||||
getQuickEditUISettings: function () {
|
||||
return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: false};
|
||||
getQuickEditUISettings: function getQuickEditUISettings() {
|
||||
return {
|
||||
padding: true,
|
||||
unifiedToolbar: true,
|
||||
fullWidthToolbar: true,
|
||||
popup: false
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
revert: function () {
|
||||
revert: function revert() {
|
||||
this.$textElement.html(this.model.get('originalValue'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads untransformed text for this field.
|
||||
*
|
||||
* More accurately: it re-filters formatted text to exclude transformation
|
||||
* filters used by the text format.
|
||||
*
|
||||
* @param {function} callback
|
||||
* A callback function that will receive the untransformed text.
|
||||
*
|
||||
* @see \Drupal\editor\Ajax\GetUntransformedTextCommand
|
||||
*/
|
||||
_getUntransformedText: function (callback) {
|
||||
_getUntransformedText: function _getUntransformedText(callback) {
|
||||
var fieldID = this.fieldModel.get('fieldID');
|
||||
|
||||
// Create a Drupal.ajax instance to load the form.
|
||||
var textLoaderAjax = Drupal.ajax({
|
||||
url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('editor/!entity_type/!id/!field_name/!langcode/!view_mode')),
|
||||
submit: {nocssjs: true}
|
||||
submit: { nocssjs: true }
|
||||
});
|
||||
|
||||
// Implement a scoped editorGetUntransformedText AJAX command: calls the
|
||||
// callback.
|
||||
textLoaderAjax.commands.editorGetUntransformedText = function (ajax, response, status) {
|
||||
callback(response.data);
|
||||
};
|
||||
|
||||
// This will ensure our scoped editorGetUntransformedText AJAX command
|
||||
// gets called.
|
||||
textLoaderAjax.execute();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, _);
|
||||
})(jQuery, Drupal, drupalSettings, _);
|
|
@ -1,83 +1,68 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches behavior for the Editor module.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal, drupalSettings) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Finds the text area field associated with the given text format selector.
|
||||
*
|
||||
* @param {jQuery} $formatSelector
|
||||
* A text format selector DOM element.
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
* The text area DOM element, if it was found.
|
||||
*/
|
||||
function findFieldForFormatSelector($formatSelector) {
|
||||
var field_id = $formatSelector.attr('data-editor-for');
|
||||
// This selector will only find text areas in the top-level document. We do
|
||||
// not support attaching editors on text areas within iframes.
|
||||
return $('#' + field_id).get(0);
|
||||
var fieldId = $formatSelector.attr('data-editor-for');
|
||||
|
||||
return $('#' + fieldId).get(0);
|
||||
}
|
||||
|
||||
function filterXssWhenSwitching(field, format, originalFormatID, callback) {
|
||||
if (format.editor.isXssSafe) {
|
||||
callback(field, format);
|
||||
} else {
|
||||
$.ajax({
|
||||
url: Drupal.url('editor/filter_xss/' + format.format),
|
||||
type: 'POST',
|
||||
data: {
|
||||
value: field.value,
|
||||
original_format_id: originalFormatID
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function success(xssFilteredValue) {
|
||||
if (xssFilteredValue !== false) {
|
||||
field.value = xssFilteredValue;
|
||||
}
|
||||
callback(field, format);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the text editor on a text area.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The text area DOM element.
|
||||
* @param {string} newFormatID
|
||||
* The text format we're changing to; the text editor for the currently
|
||||
* active text format will be detached, and the text editor for the new text
|
||||
* format will be attached.
|
||||
*/
|
||||
function changeTextEditor(field, newFormatID) {
|
||||
var previousFormatID = field.getAttribute('data-editor-active-text-format');
|
||||
|
||||
// Detach the current editor (if any) and attach a new editor.
|
||||
if (drupalSettings.editor.formats[previousFormatID]) {
|
||||
Drupal.editorDetach(field, drupalSettings.editor.formats[previousFormatID]);
|
||||
}
|
||||
// When no text editor is currently active, stop tracking changes.
|
||||
else {
|
||||
$(field).off('.editor');
|
||||
}
|
||||
} else {
|
||||
$(field).off('.editor');
|
||||
}
|
||||
|
||||
// Attach the new text editor (if any).
|
||||
if (drupalSettings.editor.formats[newFormatID]) {
|
||||
var format = drupalSettings.editor.formats[newFormatID];
|
||||
filterXssWhenSwitching(field, format, previousFormatID, Drupal.editorAttach);
|
||||
}
|
||||
|
||||
// Store the new active format.
|
||||
field.setAttribute('data-editor-active-text-format', newFormatID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in text format.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* The text format change event.
|
||||
*/
|
||||
function onTextFormatChange(event) {
|
||||
var $select = $(event.target);
|
||||
var field = event.data.field;
|
||||
var activeFormatID = field.getAttribute('data-editor-active-text-format');
|
||||
var newFormatID = $select.val();
|
||||
|
||||
// Prevent double-attaching if the change event is triggered manually.
|
||||
if (newFormatID === activeFormatID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When changing to a text format that has a text editor associated
|
||||
// with it that supports content filtering, then first ask for
|
||||
// confirmation, because switching text formats might cause certain
|
||||
// markup to be stripped away.
|
||||
var supportContentFiltering = drupalSettings.editor.formats[newFormatID] && drupalSettings.editor.formats[newFormatID].editorSupportsContentFiltering;
|
||||
// If there is no content yet, it's always safe to change the text format.
|
||||
|
||||
var hasContent = field.value !== '';
|
||||
if (hasContent && supportContentFiltering) {
|
||||
var message = Drupal.t('Changing the text format to %text_format will permanently remove content that is not allowed in that text format.<br><br>Save your changes before switching the text format to avoid losing data.', {
|
||||
|
@ -87,68 +72,43 @@
|
|||
title: Drupal.t('Change text format?'),
|
||||
dialogClass: 'editor-change-text-format-modal',
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: Drupal.t('Continue'),
|
||||
class: 'button button--primary',
|
||||
click: function () {
|
||||
changeTextEditor(field, newFormatID);
|
||||
confirmationDialog.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Drupal.t('Cancel'),
|
||||
class: 'button',
|
||||
click: function () {
|
||||
// Restore the active format ID: cancel changing text format. We
|
||||
// cannot simply call event.preventDefault() because jQuery's
|
||||
// change event is only triggered after the change has already
|
||||
// been accepted.
|
||||
$select.val(activeFormatID);
|
||||
confirmationDialog.close();
|
||||
}
|
||||
buttons: [{
|
||||
text: Drupal.t('Continue'),
|
||||
class: 'button button--primary',
|
||||
click: function click() {
|
||||
changeTextEditor(field, newFormatID);
|
||||
confirmationDialog.close();
|
||||
}
|
||||
],
|
||||
// Prevent this modal from being closed without the user making a choice
|
||||
// as per http://stackoverflow.com/a/5438771.
|
||||
}, {
|
||||
text: Drupal.t('Cancel'),
|
||||
class: 'button',
|
||||
click: function click() {
|
||||
$select.val(activeFormatID);
|
||||
confirmationDialog.close();
|
||||
}
|
||||
}],
|
||||
|
||||
closeOnEscape: false,
|
||||
create: function () {
|
||||
create: function create() {
|
||||
$(this).parent().find('.ui-dialog-titlebar-close').remove();
|
||||
},
|
||||
|
||||
beforeClose: false,
|
||||
close: function (event) {
|
||||
// Automatically destroy the DOM element that was used for the dialog.
|
||||
close: function close(event) {
|
||||
$(event.target).remove();
|
||||
}
|
||||
});
|
||||
|
||||
confirmationDialog.showModal();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
changeTextEditor(field, newFormatID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize an empty object for editors to place their attachment code.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
Drupal.editors = {};
|
||||
|
||||
/**
|
||||
* Enables editors on text_format elements.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches an editor to an input element.
|
||||
* @prop {Drupal~behaviorDetach} detach
|
||||
* Detaches an editor from an input element.
|
||||
*/
|
||||
Drupal.behaviors.editor = {
|
||||
attach: function (context, settings) {
|
||||
// If there are no editor settings, there are no editors to enable.
|
||||
attach: function attach(context, settings) {
|
||||
if (!settings.editor) {
|
||||
return;
|
||||
}
|
||||
|
@ -157,58 +117,44 @@
|
|||
var $this = $(this);
|
||||
var field = findFieldForFormatSelector($this);
|
||||
|
||||
// Opt-out if no supported text area was found.
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the current active format.
|
||||
var activeFormatID = $this.val();
|
||||
field.setAttribute('data-editor-active-text-format', activeFormatID);
|
||||
|
||||
// Directly attach this text editor, if the text format is enabled.
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
// XSS protection for the current text format/editor is performed on
|
||||
// the server side, so we don't need to do anything special here.
|
||||
Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
|
||||
}
|
||||
// When there is no text editor for this text format, still track
|
||||
// changes, because the user has the ability to switch to some text
|
||||
// editor, otherwise this code would not be executed.
|
||||
|
||||
$(field).on('change.editor keypress.editor', function () {
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
// Just knowing that the value was changed is enough, stop tracking.
|
||||
|
||||
$(field).off('.editor');
|
||||
});
|
||||
|
||||
// Attach onChange handler to text format selector element.
|
||||
if ($this.is('select')) {
|
||||
$this.on('change.editorAttach', {field: field}, onTextFormatChange);
|
||||
$this.on('change.editorAttach', { field: field }, onTextFormatChange);
|
||||
}
|
||||
// Detach any editor when the containing form is submitted.
|
||||
|
||||
$this.parents('form').on('submit', function (event) {
|
||||
// Do not detach if the event was canceled.
|
||||
if (event.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
// Detach the current editor (if any).
|
||||
|
||||
if (settings.editor.formats[activeFormatID]) {
|
||||
Drupal.editorDetach(field, settings.editor.formats[activeFormatID], 'serialize');
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
detach: function detach(context, settings, trigger) {
|
||||
var editors = void 0;
|
||||
|
||||
detach: function (context, settings, trigger) {
|
||||
var editors;
|
||||
// The 'serialize' trigger indicates that we should simply update the
|
||||
// underlying element with the new text, without destroying the editor.
|
||||
if (trigger === 'serialize') {
|
||||
// Removing the editor-processed class guarantees that the editor will
|
||||
// be reattached. Only do this if we're planning to destroy the editor.
|
||||
editors = $(context).find('[data-editor-for]').findOnce('editor');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
editors = $(context).find('[data-editor-for]').removeOnce('editor');
|
||||
}
|
||||
|
||||
|
@ -223,96 +169,25 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches editor behaviors to the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
*
|
||||
* @listens event:change
|
||||
*
|
||||
* @fires event:formUpdated
|
||||
*/
|
||||
Drupal.editorAttach = function (field, format) {
|
||||
if (format.editor) {
|
||||
// Attach the text editor.
|
||||
Drupal.editors[format.editor].attach(field, format);
|
||||
|
||||
// Ensures form.js' 'formUpdated' event is triggered even for changes that
|
||||
// happen within the text editor.
|
||||
Drupal.editors[format.editor].onChange(field, function () {
|
||||
$(field).trigger('formUpdated');
|
||||
|
||||
// Keep track of changes, so we know what to do when switching text
|
||||
// formats and guaranteeing XSS protection.
|
||||
field.setAttribute('data-editor-value-is-changed', 'true');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches editor behaviors from the field.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} trigger
|
||||
* Trigger value from the detach behavior.
|
||||
*/
|
||||
Drupal.editorDetach = function (field, format, trigger) {
|
||||
if (format.editor) {
|
||||
Drupal.editors[format.editor].detach(field, format, trigger);
|
||||
|
||||
// Restore the original value if the user didn't make any changes yet.
|
||||
if (field.getAttribute('data-editor-value-is-changed') === 'false') {
|
||||
field.value = field.getAttribute('data-editor-value-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter away XSS attack vectors when switching text formats.
|
||||
*
|
||||
* @param {HTMLElement} field
|
||||
* The textarea DOM element.
|
||||
* @param {object} format
|
||||
* The text format that's being activated, from
|
||||
* drupalSettings.editor.formats.
|
||||
* @param {string} originalFormatID
|
||||
* The text format ID of the original text format.
|
||||
* @param {function} callback
|
||||
* A callback to be called (with no parameters) after the field's value has
|
||||
* been XSS filtered.
|
||||
*/
|
||||
function filterXssWhenSwitching(field, format, originalFormatID, callback) {
|
||||
// A text editor that already is XSS-safe needs no additional measures.
|
||||
if (format.editor.isXssSafe) {
|
||||
callback(field, format);
|
||||
}
|
||||
// Otherwise, ensure XSS safety: let the server XSS filter this value.
|
||||
else {
|
||||
$.ajax({
|
||||
url: Drupal.url('editor/filter_xss/' + format.format),
|
||||
type: 'POST',
|
||||
data: {
|
||||
value: field.value,
|
||||
original_format_id: originalFormatID
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (xssFilteredValue) {
|
||||
// If the server returns false, then no XSS filtering is needed.
|
||||
if (xssFilteredValue !== false) {
|
||||
field.value = xssFilteredValue;
|
||||
}
|
||||
callback(field, format);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery, Drupal, drupalSettings);
|
||||
})(jQuery, Drupal, drupalSettings);
|
|
@ -19,7 +19,7 @@ class Standard extends Xss implements EditorXssFilterInterface {
|
|||
// Apply XSS filtering, but blacklist the <script>, <style>, <link>, <embed>
|
||||
// and <object> tags.
|
||||
// The <script> and <style> tags are blacklisted because their contents
|
||||
// can be malicious (and therefor they are inherently unsafe), whereas for
|
||||
// can be malicious (and therefore they are inherently unsafe), whereas for
|
||||
// all other tags, only their attributes can make them malicious. Since
|
||||
// \Drupal\Component\Utility\Xss::filter() protects against malicious
|
||||
// attributes, we take no blacklisting action.
|
||||
|
@ -41,10 +41,10 @@ class Standard extends Xss implements EditorXssFilterInterface {
|
|||
// also user frustration: what if a text format is configured to allow
|
||||
// <embed>, for example? Then we would strip that tag, even though it is
|
||||
// allowed, thereby causing data loss!
|
||||
// Therefor, we want to be smarter still. We want to take into account which
|
||||
// HTML tags are allowed and forbidden by the text format we're filtering
|
||||
// for, and if we're switching from another text format, we want to take
|
||||
// that format's allowed and forbidden tags into account as well.
|
||||
// Therefore, we want to be smarter still. We want to take into account
|
||||
// which HTML tags are allowed and forbidden by the text format we're
|
||||
// filtering for, and if we're switching from another text format, we want
|
||||
// to take that format's allowed and forbidden tags into account as well.
|
||||
// In other words: we only expect markup allowed in both the original and
|
||||
// the new format to continue to exist.
|
||||
$format_restrictions = $format->getHtmlRestrictions();
|
||||
|
@ -116,7 +116,6 @@ class Standard extends Xss implements EditorXssFilterInterface {
|
|||
return $html;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all allowed tags from a restrictions data structure.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,13 @@ use Drupal\editor\EditorInterface;
|
|||
* @ConfigEntityType(
|
||||
* id = "editor",
|
||||
* label = @Translation("Text Editor"),
|
||||
* label_collection = @Translation("Text Editors"),
|
||||
* label_singular = @Translation("text editor"),
|
||||
* label_plural = @Translation("text editors"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count text editor",
|
||||
* plural = "@count text editors",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\editor\EditorAccessControlHandler",
|
||||
* },
|
||||
|
|
|
@ -15,6 +15,8 @@ use Drupal\Core\Entity\EntityStorageInterface;
|
|||
|
||||
/**
|
||||
* Provides an image dialog for text editors.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class EditorImageDialog extends FormBase {
|
||||
|
||||
|
@ -139,7 +141,7 @@ class EditorImageDialog extends FormBase {
|
|||
}
|
||||
$form['attributes']['alt'] = [
|
||||
'#title' => $this->t('Alternative text'),
|
||||
'#placeholder' => $this->t('Short description for the visually impaired'),
|
||||
'#description' => $this->t('Short description of the image used by screen readers and displayed when the image is not loaded. This is important for accessibility.'),
|
||||
'#type' => 'textfield',
|
||||
'#required' => TRUE,
|
||||
'#required_error' => $this->t('Alternative text is required.<br />(Only in rare cases should this be left empty. To create empty alternative text, enter <code>""</code> — two double quotes without any content).'),
|
||||
|
|
|
@ -12,6 +12,8 @@ use Drupal\Core\Ajax\CloseModalDialogCommand;
|
|||
|
||||
/**
|
||||
* Provides a link dialog for text editors.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class EditorLinkDialog extends FormBase {
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class EditorFileReference extends FilterBase implements ContainerFactoryPluginIn
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
|
|
|
@ -5,5 +5,5 @@ core: 8.x
|
|||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- filter
|
||||
- ckeditor
|
||||
- drupal:filter
|
||||
- drupal:ckeditor
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests administration of text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorAdminTest extends WebTestBase {
|
||||
class EditorAdminTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -53,9 +53,10 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->drupalGet('admin/config/content/formats/manage/filtered_html');
|
||||
|
||||
// Ensure the form field order is correct.
|
||||
$roles_pos = strpos($this->getRawContent(), 'Roles');
|
||||
$editor_pos = strpos($this->getRawContent(), 'Text editor');
|
||||
$filters_pos = strpos($this->getRawContent(), 'Enabled filters');
|
||||
$raw_content = $this->getSession()->getPage()->getContent();
|
||||
$roles_pos = strpos($raw_content, 'Roles');
|
||||
$editor_pos = strpos($raw_content, 'Text editor');
|
||||
$filters_pos = strpos($raw_content, 'Enabled filters');
|
||||
$this->assertTrue($roles_pos < $editor_pos && $editor_pos < $filters_pos, '"Text Editor" select appears in the correct location of the text format configuration UI.');
|
||||
|
||||
// Verify the <select>.
|
||||
|
@ -65,8 +66,8 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 1, 'The Text Editor select is disabled.');
|
||||
$this->assertTrue(count($options) === 1, 'The Text Editor select has only one option.');
|
||||
$this->assertTrue(((string) $options[0]) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertRaw(t('This option is disabled because no modules that provide a text editor are currently enabled.'), 'Description for select present that tells users to install a text editor module.');
|
||||
$this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select present that tells users to install a text editor module.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +87,7 @@ class EditorAdminTest extends WebTestBase {
|
|||
$edit = [
|
||||
'editor[editor]' => '',
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'editor_configure');
|
||||
$this->drupalPostForm(NULL, $edit, 'Configure');
|
||||
$unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]');
|
||||
$this->assertTrue(count($unicorn_setting) === 0, "Unicorn Editor's settings form is no longer present.");
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->container->get('module_installer')->install(['node']);
|
||||
$this->resetAll();
|
||||
// Create a new node type and attach the 'body' field to it.
|
||||
$node_type = NodeType::create(['type' => Unicode::strtolower($this->randomMachineName())]);
|
||||
$node_type = NodeType::create(['type' => mb_strtolower($this->randomMachineName())]);
|
||||
$node_type->save();
|
||||
node_add_body_field($node_type, $this->randomString());
|
||||
|
||||
|
@ -135,7 +136,7 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->drupalLogin($account);
|
||||
|
||||
// The node edit page header.
|
||||
$text = t('<em>Edit @type</em> @title', ['@type' => $node_type->label(), '@title' => $node->label()]);
|
||||
$text = (string) new FormattableMarkup('<em>Edit @type</em> @title', ['@type' => $node_type->label(), '@title' => $node->label()]);
|
||||
|
||||
// Go to node edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
|
@ -193,17 +194,17 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.');
|
||||
$this->assertTrue(count($options) === 2, 'The Text Editor select has two options.');
|
||||
$this->assertTrue(((string) $options[0]) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertTrue(((string) $options[1]) === 'Unicorn Editor', 'Option 2 in the Text Editor select is "Unicorn Editor".');
|
||||
$this->assertTrue(((string) $options[0]['selected']) === 'selected', 'Option 1 ("None") is selected.');
|
||||
$this->assertTrue(($options[0]->getText()) === 'None', 'Option 1 in the Text Editor select is "None".');
|
||||
$this->assertTrue(($options[1]->getText()) === 'Unicorn Editor', 'Option 2 in the Text Editor select is "Unicorn Editor".');
|
||||
$this->assertTrue($options[0]->hasAttribute('selected'), 'Option 1 ("None") is selected.');
|
||||
// Ensure the none option is selected.
|
||||
$this->assertNoRaw(t('This option is disabled because no modules that provide a text editor are currently enabled.'), 'Description for select absent that tells users to install a text editor module.');
|
||||
$this->assertNoRaw('This option is disabled because no modules that provide a text editor are currently enabled.', 'Description for select absent that tells users to install a text editor module.');
|
||||
|
||||
// Select the "Unicorn Editor" editor and click the "Configure" button.
|
||||
$edit = [
|
||||
'editor[editor]' => 'unicorn',
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'editor_configure');
|
||||
$this->drupalPostForm(NULL, $edit, 'Configure');
|
||||
$unicorn_setting = $this->xpath('//input[@name="editor[settings][ponies_too]" and @type="checkbox" and @checked]');
|
||||
$this->assertTrue(count($unicorn_setting), "Unicorn Editor's settings form is present.");
|
||||
|
||||
|
@ -230,7 +231,7 @@ class EditorAdminTest extends WebTestBase {
|
|||
$this->assertTrue(count($select) === 1, 'The Text Editor select exists.');
|
||||
$this->assertTrue(count($select_is_disabled) === 0, 'The Text Editor select is not disabled.');
|
||||
$this->assertTrue(count($options) === 2, 'The Text Editor select has two options.');
|
||||
$this->assertTrue(((string) $options[1]['selected']) === 'selected', 'Option 2 ("Unicorn Editor") is selected.');
|
||||
$this->assertTrue($options[1]->hasAttribute('selected'), 'Option 2 ("Unicorn Editor") is selected.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests loading of text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorLoadingTest extends WebTestBase {
|
||||
class EditorLoadingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -118,7 +118,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => ['width' => '', 'height' => ''],
|
||||
]
|
||||
],
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
|
@ -127,7 +127,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
// - doesn't have access to the full_html text format, so: no text editor.
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list( , $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
list(, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$this->assertFalse($editor_settings_present, 'No Text Editor module settings.');
|
||||
$this->assertFalse($editor_js_present, 'No Text Editor JavaScript.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
|
@ -140,13 +140,17 @@ class EditorLoadingTest extends WebTestBase {
|
|||
$this->drupalLogin($this->privilegedUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$expected = ['formats' => ['full_html' => [
|
||||
'format' => 'full_html',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
]]];
|
||||
$expected = [
|
||||
'formats' => [
|
||||
'full_html' => [
|
||||
'format' => 'full_html',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
|
@ -174,13 +178,17 @@ class EditorLoadingTest extends WebTestBase {
|
|||
$this->drupalLogin($this->untrustedUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$expected = ['formats' => ['plain_text' => [
|
||||
'format' => 'plain_text',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
]]];
|
||||
$expected = [
|
||||
'formats' => [
|
||||
'plain_text' => [
|
||||
'format' => 'plain_text',
|
||||
'editor' => 'unicorn',
|
||||
'editorSettings' => ['ponyModeEnabled' => TRUE],
|
||||
'editorSupportsContentFiltering' => TRUE,
|
||||
'isXssSafe' => FALSE,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
|
@ -194,7 +202,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html']
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html'],
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -202,7 +210,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
// that (s)he is not allowed to use. The editor is still loaded. CKEditor,
|
||||
// for example, supports being loaded in a disabled state.
|
||||
$this->drupalGet('node/1/edit');
|
||||
list( , $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
list(, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck('body');
|
||||
$this->assertTrue($editor_settings_present, 'Text Editor module settings.');
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript.');
|
||||
$this->assertTrue(count($body) === 1, 'A body field exists.');
|
||||
|
@ -226,7 +234,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => ['width' => '', 'height' => ''],
|
||||
]
|
||||
],
|
||||
]);
|
||||
$editor->save();
|
||||
|
||||
|
@ -234,14 +242,14 @@ class EditorLoadingTest extends WebTestBase {
|
|||
$this->drupalCreateNode([
|
||||
'type' => 'page',
|
||||
'field_text' => [
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html']
|
||||
['value' => $this->randomMachineName(32), 'format' => 'full_html'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Assert the unicorn editor works with textfields.
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$this->drupalGet('node/1/edit');
|
||||
list( , $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
list(, $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
|
||||
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
|
||||
$this->assertTrue(count($field) === 1, 'A text field exists.');
|
||||
|
@ -257,7 +265,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
])->save();
|
||||
|
||||
$this->drupalGet('node/1/edit');
|
||||
list( , $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
list(, $editor_settings_present, $editor_js_present, $field, $format_selector) = $this->getThingsToCheck('field-text', 'input');
|
||||
$this->assertFalse($editor_settings_present, "Text Editor module's JavaScript settings are not on the page.");
|
||||
$this->assertFalse($editor_js_present, 'Text Editor JavaScript is not present.');
|
||||
$this->assertTrue(count($field) === 1, 'A text field exists.');
|
||||
|
@ -274,7 +282,7 @@ class EditorLoadingTest extends WebTestBase {
|
|||
// Editor.module's JS settings present.
|
||||
isset($settings['editor']),
|
||||
// Editor.module's JS present.
|
||||
strpos($this->getRawContent(), drupal_get_path('module', 'editor') . '/js/editor.js') !== FALSE,
|
||||
strpos($this->getSession()->getPage()->getContent(), drupal_get_path('module', 'editor') . '/js/editor.js') !== FALSE,
|
||||
// Body field.
|
||||
$this->xpath('//' . $type . '[@id="edit-' . $field_name . '-0-value"]'),
|
||||
// Format selector.
|
|
@ -112,7 +112,7 @@ class EditorPrivateFileReferenceFilterTest extends BrowserTestBase {
|
|||
|
||||
// When the published node is also unpublished, the image should also
|
||||
// become inaccessible to anonymous users.
|
||||
$published_node->setPublished(FALSE)->save();
|
||||
$published_node->setUnpublished()->save();
|
||||
|
||||
$this->drupalGet($published_node->toUrl());
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
|
@ -121,7 +121,7 @@ class EditorPrivateFileReferenceFilterTest extends BrowserTestBase {
|
|||
|
||||
// Disallow anonymous users to view the entity, which then should also
|
||||
// disallow them to view the image.
|
||||
$published_node->setPublished(TRUE)->save();
|
||||
$published_node->setPublished()->save();
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->revokePermission('access content')
|
||||
->save();
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests XSS protection for content creators when using text editors.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorSecurityTest extends WebTestBase {
|
||||
class EditorSecurityTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The sample content to use in all tests.
|
||||
|
@ -93,7 +93,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
@ -108,7 +108,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
@ -128,7 +128,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
'status' => 1,
|
||||
'settings' => [
|
||||
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a> <embed>',
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
@ -158,7 +158,6 @@ class EditorSecurityTest extends WebTestBase {
|
|||
]);
|
||||
$editor->save();
|
||||
|
||||
|
||||
// Create node type.
|
||||
$this->drupalCreateContentType([
|
||||
'type' => 'article',
|
||||
|
@ -210,9 +209,9 @@ class EditorSecurityTest extends WebTestBase {
|
|||
$this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'body' => [
|
||||
['value' => self::$sampleContent, 'format' => $sample['format']]
|
||||
['value' => self::$sampleContent, 'format' => $sample['format']],
|
||||
],
|
||||
'uid' => $sample['author']
|
||||
'uid' => $sample['author'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +283,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
$this->drupalLogin($account);
|
||||
$this->drupalGet('node/' . $case['node_id'] . '/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical($case['value'], (string) $dom_node[0], 'The value was correctly filtered for XSS attack vectors.');
|
||||
$this->assertIdentical($case['value'], $dom_node[0]->getText(), 'The value was correctly filtered for XSS attack vectors.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +305,8 @@ class EditorSecurityTest extends WebTestBase {
|
|||
$expected = [
|
||||
[
|
||||
'node_id' => 1,
|
||||
'value' => self::$sampleContent, // No text editor => no XSS filtering.
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'format' => 'restricted_without_editor',
|
||||
'switch_to' => [
|
||||
'restricted_with_editor' => self::$sampleContentSecured,
|
||||
|
@ -319,7 +319,8 @@ class EditorSecurityTest extends WebTestBase {
|
|||
],
|
||||
[
|
||||
'node_id' => 2,
|
||||
'value' => self::$sampleContentSecured, // Text editor => XSS filtering.
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecured,
|
||||
'format' => 'restricted_with_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
|
@ -333,7 +334,8 @@ class EditorSecurityTest extends WebTestBase {
|
|||
],
|
||||
[
|
||||
'node_id' => 3,
|
||||
'value' => self::$sampleContentSecuredEmbedAllowed, // Text editor => XSS filtering.
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecuredEmbedAllowed,
|
||||
'format' => 'restricted_plus_dangerous_tag_with_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
|
@ -348,7 +350,8 @@ class EditorSecurityTest extends WebTestBase {
|
|||
],
|
||||
[
|
||||
'node_id' => 4,
|
||||
'value' => self::$sampleContent, // No text editor => no XSS filtering.
|
||||
// No text editor => no XSS filtering.
|
||||
'value' => self::$sampleContent,
|
||||
'format' => 'unrestricted_without_editor',
|
||||
'switch_to' => [
|
||||
// No text editor => no XSS filtering.
|
||||
|
@ -364,7 +367,8 @@ class EditorSecurityTest extends WebTestBase {
|
|||
],
|
||||
[
|
||||
'node_id' => 5,
|
||||
'value' => self::$sampleContentSecured, // Text editor => XSS filtering.
|
||||
// Text editor => XSS filtering.
|
||||
'value' => self::$sampleContentSecured,
|
||||
'format' => 'unrestricted_with_editor',
|
||||
'switch_to' => [
|
||||
// From editor, no security filters to security filters, no editor: no
|
||||
|
@ -385,13 +389,15 @@ class EditorSecurityTest extends WebTestBase {
|
|||
// - switch to every other text format/editor
|
||||
// - assert the XSS-filtered values that we get from the server
|
||||
$this->drupalLogin($this->privilegedUser);
|
||||
$cookies = $this->getSessionCookies();
|
||||
|
||||
foreach ($expected as $case) {
|
||||
$this->drupalGet('node/' . $case['node_id'] . '/edit');
|
||||
|
||||
// Verify data- attributes.
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContent, (string) $dom_node[0]['data-editor-value-original'], 'The data-editor-value-original attribute is correctly set.');
|
||||
$this->assertIdentical('false', (string) $dom_node[0]['data-editor-value-is-changed'], 'The data-editor-value-is-changed attribute is correctly set.');
|
||||
$this->assertIdentical(self::$sampleContent, $dom_node[0]->getAttribute('data-editor-value-original'), 'The data-editor-value-original attribute is correctly set.');
|
||||
$this->assertIdentical('false', (string) $dom_node[0]->getAttribute('data-editor-value-is-changed'), 'The data-editor-value-is-changed attribute is correctly set.');
|
||||
|
||||
// Switch to every other text format/editor and verify the results.
|
||||
foreach ($case['switch_to'] as $format => $expected_filtered_value) {
|
||||
|
@ -400,13 +406,25 @@ class EditorSecurityTest extends WebTestBase {
|
|||
'%original_format' => $case['format'],
|
||||
'%format' => $format,
|
||||
]));
|
||||
|
||||
$post = [
|
||||
'value' => self::$sampleContent,
|
||||
'original_format_id' => $case['format'],
|
||||
];
|
||||
$response = $this->drupalPostWithFormat('editor/filter_xss/' . $format, 'json', $post);
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$client = $this->getHttpClient();
|
||||
$response = $client->post($this->buildUrl('/editor/filter_xss/' . $format), [
|
||||
'body' => http_build_query($post),
|
||||
'cookies' => $cookies,
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$json = Json::decode($response->getBody());
|
||||
$this->assertIdentical($json, $expected_filtered_value, 'The value was correctly filtered for XSS attack vectors.');
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +438,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('node/2/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContentSecured, (string) $dom_node[0], 'The value was filtered by the Standard text editor XSS filter.');
|
||||
$this->assertIdentical(self::$sampleContentSecured, $dom_node[0]->getText(), 'The value was filtered by the Standard text editor XSS filter.');
|
||||
|
||||
// Enable editor_test.module's hook_editor_xss_filter_alter() implementation
|
||||
// to alter the text editor XSS filter class being used.
|
||||
|
@ -429,7 +447,7 @@ class EditorSecurityTest extends WebTestBase {
|
|||
// First: the Insecure text editor XSS filter.
|
||||
$this->drupalGet('node/2/edit');
|
||||
$dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]');
|
||||
$this->assertIdentical(self::$sampleContent, (string) $dom_node[0], 'The value was filtered by the Insecure text editor XSS filter.');
|
||||
$this->assertIdentical(self::$sampleContent, $dom_node[0]->getText(), 'The value was filtered by the Insecure text editor XSS filter.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests scaling of inline images.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class EditorUploadImageScaleTest extends WebTestBase {
|
||||
class EditorUploadImageScaleTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -51,9 +55,9 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL
|
||||
'height' => NULL,
|
||||
],
|
||||
]
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Create admin user.
|
||||
|
@ -66,7 +70,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
*/
|
||||
public function testEditorUploadImageScale() {
|
||||
// Generate testing images.
|
||||
$testing_image_list = $this->drupalGetTestFiles('image');
|
||||
$testing_image_list = $this->getTestFiles('image');
|
||||
|
||||
// Case 1: no max dimensions set: uploaded image not scaled.
|
||||
$test_image = $testing_image_list[0];
|
||||
|
@ -78,7 +82,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $image_file_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $image_file_height);
|
||||
$this->assertNoRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
$this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 2: max width smaller than uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[1];
|
||||
|
@ -90,7 +94,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $max_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width));
|
||||
$this->assertRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 3: max height smaller than uploaded image: image scaled down.
|
||||
$test_image = $testing_image_list[2];
|
||||
|
@ -102,7 +106,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height));
|
||||
$this->assertEqual($uploaded_image_file_height, $max_height);
|
||||
$this->assertRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 4: max dimensions greater than uploaded image: image not scaled.
|
||||
$test_image = $testing_image_list[3];
|
||||
|
@ -114,7 +118,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $image_file_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $image_file_height);
|
||||
$this->assertNoRaw(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
$this->assertNoRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', ['%dimensions' => $max_width . 'x' . $max_height]));
|
||||
|
||||
// Case 5: only max width dimension was provided and it was smaller than
|
||||
// uploaded image: image scaled down.
|
||||
|
@ -127,7 +131,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $max_width);
|
||||
$this->assertEqual($uploaded_image_file_height, $uploaded_image_file_height * ($uploaded_image_file_width / $max_width));
|
||||
$this->assertRaw(t('The image was resized to fit within the maximum allowed width of %width pixels.', ['%width' => $max_width]));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed width of %width pixels.', ['%width' => $max_width]));
|
||||
|
||||
// Case 6: only max height dimension was provided and it was smaller than
|
||||
// uploaded image: image scaled down.
|
||||
|
@ -140,7 +144,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
list($uploaded_image_file_width, $uploaded_image_file_height) = $this->uploadImage($test_image->uri);
|
||||
$this->assertEqual($uploaded_image_file_width, $uploaded_image_file_width * ($uploaded_image_file_height / $max_height));
|
||||
$this->assertEqual($uploaded_image_file_height, $max_height);
|
||||
$this->assertRaw(t('The image was resized to fit within the maximum allowed height of %height pixels.', ['%height' => $max_height]));
|
||||
$this->assertRaw((string) new FormattableMarkup('The image was resized to fit within the maximum allowed height of %height pixels.', ['%height' => $max_height]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +192,7 @@ class EditorUploadImageScaleTest extends WebTestBase {
|
|||
*/
|
||||
protected function uploadImage($uri) {
|
||||
$edit = [
|
||||
'files[fid]' => drupal_realpath($uri),
|
||||
'files[fid]' => \Drupal::service('file_system')->realpath($uri),
|
||||
];
|
||||
$this->drupalGet('editor/dialog/image/basic_html');
|
||||
$this->drupalPostForm('editor/dialog/image/basic_html', $edit, t('Upload'));
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\editor\Functional\Rest\EditorResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class EditorHalJsonCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests;
|
||||
namespace Drupal\Tests\editor\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests Quick Edit module integration endpoints.
|
||||
*
|
||||
* @group editor
|
||||
*/
|
||||
class QuickEditIntegrationLoadingTest extends WebTestBase {
|
||||
class QuickEditIntegrationLoadingTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
|
@ -57,8 +57,8 @@ class QuickEditIntegrationLoadingTest extends WebTestBase {
|
|||
0 => [
|
||||
'value' => '<p>Do you also love Drupal?</p><img src="druplicon.png" data-caption="Druplicon" />',
|
||||
'format' => 'filtered_html',
|
||||
]
|
||||
]
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ class QuickEditIntegrationLoadingTest extends WebTestBase {
|
|||
$users = [
|
||||
$this->drupalCreateUser(static::$basicPermissions),
|
||||
$this->drupalCreateUser(array_merge(static::$basicPermissions, ['edit any article content'])),
|
||||
$this->drupalCreateUser(array_merge(static::$basicPermissions, ['access in-place editing']))
|
||||
$this->drupalCreateUser(array_merge(static::$basicPermissions, ['access in-place editing'])),
|
||||
];
|
||||
|
||||
// Now test with each of the 3 users with insufficient permissions.
|
||||
|
@ -84,17 +84,30 @@ class QuickEditIntegrationLoadingTest extends WebTestBase {
|
|||
// Ensure the text is transformed.
|
||||
$this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>');
|
||||
|
||||
$client = $this->getHttpClient();
|
||||
|
||||
// Retrieving the untransformed text should result in an 403 response and
|
||||
// return a different error message depending of the missing permission.
|
||||
$response = $this->drupalPost('editor/' . 'node/1/body/en/full', '', [], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]);
|
||||
$this->assertResponse(403);
|
||||
$response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [
|
||||
'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']),
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
if (!$user->hasPermission('access in-place editing')) {
|
||||
$message = "A fatal error occurred: The 'access in-place editing' permission is required.";
|
||||
$this->assertIdentical(Json::encode(['message' => $message]), $response);
|
||||
$message = "The 'access in-place editing' permission is required.";
|
||||
}
|
||||
else {
|
||||
$this->assertIdentical('{}', $response);
|
||||
$message = '';
|
||||
}
|
||||
|
||||
$body = Json::decode($response->getBody());
|
||||
$this->assertIdentical($message, $body['message']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,10 +121,19 @@ class QuickEditIntegrationLoadingTest extends WebTestBase {
|
|||
|
||||
// Ensure the text is transformed.
|
||||
$this->assertRaw('<p>Do you also love Drupal?</p><figure role="group" class="caption caption-img"><img src="druplicon.png" /><figcaption>Druplicon</figcaption></figure>');
|
||||
$client = $this->getHttpClient();
|
||||
$response = $client->post($this->buildUrl('editor/node/1/body/en/full'), [
|
||||
'query' => http_build_query([MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']),
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
|
||||
$response = $this->drupalPost('editor/' . 'node/1/body/en/full', '', [], ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_ajax']]);
|
||||
$this->assertResponse(200);
|
||||
$ajax_commands = Json::decode($response);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$ajax_commands = Json::decode($response->getBody());
|
||||
$this->assertIdentical(1, count($ajax_commands), 'The untransformed text POST request results in one AJAX command.');
|
||||
$this->assertIdentical('editorGetUntransformedText', $ajax_commands[0]['command'], 'The first AJAX command is an editorGetUntransformedText command.');
|
||||
$this->assertIdentical('<p>Do you also love Drupal?</p><img src="druplicon.png" data-caption="Druplicon" />', $ajax_commands[0]['data'], 'The editorGetUntransformedText command contains the expected data.');
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorJsonCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for Editor entity.
|
||||
*/
|
||||
abstract class EditorResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['ckeditor', 'editor'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'editor';
|
||||
|
||||
/**
|
||||
* The Editor entity.
|
||||
*
|
||||
* @var \Drupal\editor\EditorInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer filters']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Llama" filter format.
|
||||
$llama_format = FilterFormat::create([
|
||||
'name' => 'Llama',
|
||||
'format' => 'llama',
|
||||
'langcode' => 'es',
|
||||
'filters' => [
|
||||
'filter_html' => [
|
||||
'status' => TRUE,
|
||||
'settings' => [
|
||||
'allowed_html' => '<p> <a> <b> <lo>',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$llama_format->save();
|
||||
|
||||
// Create a "Camelids" editor.
|
||||
$camelids = Editor::create([
|
||||
'format' => 'llama',
|
||||
'editor' => 'ckeditor',
|
||||
]);
|
||||
$camelids
|
||||
->setImageUploadSettings([
|
||||
'status' => FALSE,
|
||||
'scheme' => file_default_scheme(),
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => '',
|
||||
'height' => '',
|
||||
],
|
||||
])
|
||||
->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [
|
||||
'config' => [
|
||||
'filter.format.llama',
|
||||
],
|
||||
'module' => [
|
||||
'ckeditor',
|
||||
],
|
||||
],
|
||||
'editor' => 'ckeditor',
|
||||
'format' => 'llama',
|
||||
'image_upload' => [
|
||||
'status' => FALSE,
|
||||
'scheme' => 'public',
|
||||
'directory' => 'inline-images',
|
||||
'max_size' => '',
|
||||
'max_dimensions' => [
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
],
|
||||
],
|
||||
'langcode' => 'en',
|
||||
'settings' => [
|
||||
'toolbar' => [
|
||||
'rows' => [
|
||||
[
|
||||
[
|
||||
'name' => 'Formatting',
|
||||
'items' => [
|
||||
'Bold',
|
||||
'Italic',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Links',
|
||||
'items' => [
|
||||
'DrupalLink',
|
||||
'DrupalUnlink',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Lists',
|
||||
'items' => [
|
||||
'BulletedList',
|
||||
'NumberedList',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Media',
|
||||
'items' => [
|
||||
'Blockquote',
|
||||
'DrupalImage',
|
||||
],
|
||||
],
|
||||
[
|
||||
'name' => 'Tools',
|
||||
'items' => [
|
||||
'Source',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'plugins' => [
|
||||
'language' => [
|
||||
'language_list' => 'un',
|
||||
],
|
||||
],
|
||||
],
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCacheContexts() {
|
||||
// @see ::createEntity()
|
||||
return ['user.permissions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer filters' permission is required.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlAnonTest extends EditorResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlBasicAuthTest extends EditorResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\editor\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class EditorXmlCookieTest extends EditorResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\editor\Tests\Update;
|
||||
namespace Drupal\Tests\editor\Functional\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Editor module database updates.
|
||||
*
|
||||
* @group editor
|
||||
* @group legacy
|
||||
*/
|
||||
class EditorUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
|
@ -16,9 +17,9 @@ class EditorUpdateTest extends UpdatePathTestBase {
|
|||
*/
|
||||
public function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
// Simulate an un-synchronized environment.
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.editor-editor_update_8001.php',
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.editor-editor_update_8001.php',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -46,7 +47,6 @@ class EditorUpdateTest extends UpdatePathTestBase {
|
|||
$this->assertTrue($editor_full_html->get('status'));
|
||||
$this->assertNotIdentical($format_full_html->get('status'), $editor_full_html->get('status'));
|
||||
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
|
@ -46,7 +46,7 @@ class EditorFileReferenceFilterTest extends KernelTestBase {
|
|||
public function testEditorFileReferenceFilter() {
|
||||
$filter = $this->filters['editor_file_reference'];
|
||||
|
||||
$test = function($input) use ($filter) {
|
||||
$test = function ($input) use ($filter) {
|
||||
return $filter->process($input, 'und');
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\Tests\editor\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
@ -25,7 +24,7 @@ class EditorFilterIntegrationTest extends KernelTestBase {
|
|||
public function testTextFormatIntegration() {
|
||||
// Create an arbitrary text format.
|
||||
$format = FilterFormat::create([
|
||||
'format' => Unicode::strtolower($this->randomMachineName()),
|
||||
'format' => mb_strtolower($this->randomMachineName()),
|
||||
'name' => $this->randomString(),
|
||||
]);
|
||||
$format->save();
|
||||
|
|
|
@ -9,7 +9,7 @@ use Drupal\editor\Entity\Editor;
|
|||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\quickedit\MetadataGenerator;
|
||||
use Drupal\Tests\quickedit\Kernel\QuickEditTestBase;
|
||||
use Drupal\quickedit_test\MockEditEntityFieldAccessCheck;
|
||||
use Drupal\quickedit_test\MockQuickEditEntityFieldAccessCheck;
|
||||
use Drupal\editor\EditorController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
|
@ -38,7 +38,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
/**
|
||||
* The metadata generator object to be tested.
|
||||
*
|
||||
* @var \Drupal\quickedit\MetadataGeneratorInterface.php
|
||||
* @var \Drupal\quickedit\MetadataGeneratorInterface
|
||||
*/
|
||||
protected $metadataGenerator;
|
||||
|
||||
|
@ -52,7 +52,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
/**
|
||||
* The access checker object to be used by the metadata generator object.
|
||||
*
|
||||
* @var \Drupal\quickedit\Access\EditEntityFieldAccessCheckInterface
|
||||
* @var \Drupal\quickedit\Access\QuickEditEntityFieldAccessCheckInterface
|
||||
*/
|
||||
protected $accessChecker;
|
||||
|
||||
|
@ -165,7 +165,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
*/
|
||||
public function testMetadata() {
|
||||
$this->editorManager = $this->container->get('plugin.manager.quickedit.editor');
|
||||
$this->accessChecker = new MockEditEntityFieldAccessCheck();
|
||||
$this->accessChecker = new MockQuickEditEntityFieldAccessCheck();
|
||||
$this->editorSelector = $this->container->get('quickedit.editor.selector');
|
||||
$this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager);
|
||||
|
||||
|
@ -221,7 +221,7 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
|
|||
[
|
||||
'command' => 'editorGetUntransformedText',
|
||||
'data' => 'Test',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor');
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\Tests\editor\Unit;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityManager;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
|
@ -22,9 +24,9 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
/**
|
||||
* The entity manager used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The ID of the type of the entity under test.
|
||||
|
@ -66,8 +68,8 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->method('getProvider')
|
||||
->will($this->returnValue('editor'));
|
||||
|
||||
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
$this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
|
||||
$this->entityTypeManager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with($this->entityTypeId)
|
||||
->will($this->returnValue($this->entityType));
|
||||
|
@ -78,10 +80,16 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$entity_manager = new EntityManager();
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('entity.manager', $this->entityManager);
|
||||
$container->set('entity.manager', $entity_manager);
|
||||
$container->set('entity_type.manager', $this->entityTypeManager);
|
||||
$container->set('uuid', $this->uuid);
|
||||
$container->set('plugin.manager.editor', $this->editorPluginManager);
|
||||
// Inject the container into entity.manager so it can defer to
|
||||
// entity_type.manager.
|
||||
$entity_manager->setContainer($container);
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
|
@ -120,7 +128,7 @@ class EditorConfigEntityUnitTest extends UnitTestCase {
|
|||
->with($format_id)
|
||||
->will($this->returnValue($filter_format));
|
||||
|
||||
$this->entityManager->expects($this->once())
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getStorage')
|
||||
->with('filter_format')
|
||||
->will($this->returnValue($storage));
|
||||
|
|
|
@ -364,8 +364,11 @@ class StandardTest extends UnitTestCase {
|
|||
|
||||
// IMG STYLE with expression.
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#IMG_STYLE_with_expression
|
||||
$data[] = ['exp/*<A STYLE=\'no\xss:noxss("*//*");
|
||||
xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
||||
$data[] = [
|
||||
'exp/*<A STYLE=\'no\xss:noxss("*//*");
|
||||
xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>',
|
||||
'exp/*<A>',
|
||||
];
|
||||
|
||||
// STYLE tag (Older versions of Netscape only).
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#STYLE_tag_.28Older_versions_of_Netscape_only.29
|
||||
|
@ -443,7 +446,9 @@ xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
|||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Downlevel-Hidden_block
|
||||
$data[] = ['<!--[if gte IE 4]>
|
||||
<SCRIPT>alert(\'XSS\');</SCRIPT>
|
||||
<![endif]-->', "\n alert('XSS');\n "];
|
||||
<![endif]-->',
|
||||
"\n alert('XSS');\n ",
|
||||
];
|
||||
|
||||
// BASE tag.
|
||||
// @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#BASE_tag
|
||||
|
@ -578,20 +583,20 @@ xss:ex/*XSS*//*/*/pression(alert("XSS"))\'>', 'exp/*<A>'];
|
|||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4">alert(0)',
|
||||
'Disallow only the script tag',
|
||||
['script']
|
||||
['script'],
|
||||
],
|
||||
[
|
||||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown>alert(0)',
|
||||
'Disallow both the script and video tags',
|
||||
['script', 'video']
|
||||
['script', 'video'],
|
||||
],
|
||||
// No real use case for this, but it is an edge case we must ensure works.
|
||||
[
|
||||
'<unknown style="visibility:hidden">Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'<unknown>Pink Fairy Armadillo</unknown><video src="gerenuk.mp4"><script>alert(0)</script>',
|
||||
'Disallow no tags',
|
||||
[]
|
||||
[],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
|
Reference in a new issue