Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -58,7 +58,7 @@ function editor_menu_links_discovered_alter(array &$links) {
* module), so that selecting a text format notifies a client-side text editor
* when it should be enabled or disabled.
*
* @see filter_element_info()
* @see \Drupal\filter\Element\TextFormat
*/
function editor_element_info_alter(&$types) {
$types['text_format']['#pre_render'][] = 'element.editor:preRenderTextFormat';
@ -78,7 +78,7 @@ function editor_form_filter_admin_overview_alter(&$form, FormStateInterface $for
$editors = \Drupal::service('plugin.manager.editor')->getDefinitions();
foreach (Element::children($form['formats']) as $format_id) {
$editor = editor_load($format_id);
$editor_name = ($editor && isset($editors[$editor->getEditor()])) ? $editors[$editor->getEditor()]['label'] : drupal_placeholder('—');
$editor_name = ($editor && isset($editors[$editor->getEditor()])) ? $editors[$editor->getEditor()]['label'] : '—';
$editor_column['editor'] = array('#markup' => $editor_name);
$position = array_search('name', array_keys($form['formats'][$format_id])) + 1;
$start = array_splice($form['formats'][$format_id], 0, $position, $editor_column);
@ -254,8 +254,9 @@ function editor_load($format_id) {
*
* @param string $html
* The HTML string that will be passed to the text editor.
* @param \Drupal\filter\FilterFormatInterface $format
* The text format whose text editor will be used.
* @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
* (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
@ -263,18 +264,21 @@ function editor_load($format_id) {
* database when editing).
*
* @return string|false
* FALSE when no XSS filtering needs to be applied (either because no text
* editor is associated with the text format, or because the text editor is
* safe from XSS attacks, or because the text format does not use any XSS
* protection filters), otherwise the XSS filtered string.
* The XSS filtered string or FALSE when no XSS filtering needs to be applied,
* because one of the next conditions might occur:
* - No text editor is associated with the text format,
* - The previously defined text format is now disabled,
* - The text editor is safe from XSS,
* - The text format does not use any XSS protection filters.
*
* @see https://www.drupal.org/node/2099741
*/
function editor_filter_xss($html, FilterFormatInterface $format, FilterFormatInterface $original_format = NULL) {
$editor = editor_load($format->id());
function editor_filter_xss($html, FilterFormatInterface $format = NULL, FilterFormatInterface $original_format = NULL) {
$editor = $format ? editor_load($format->id()) : NULL;
// If no text editor is associated with this text format, then we don't need
// text editor XSS filtering either.
// If no text editor is associated with this text format or the previously
// defined text format is now disabled, then we don't need text editor XSS
// filtering either.
if (!isset($editor)) {
return FALSE;
}

View file

@ -12,6 +12,8 @@
"use strict";
/**
* Editor configuration namespace.
*
* @namespace
*/
Drupal.editorConfiguration = {
@ -132,8 +134,10 @@
* "allowed tag property value" restrictions for this particular tag.
*
* @param {object} feature
* The feature in question.
*
* @return {object}
* The universe generated.
*
* @see findPropertyValueOnTag()
* @see filterStatusAllowsFeature()
@ -187,8 +191,10 @@
* values are defined for all properties: attributes, classes and styles.
*
* @param {object} section
* The section to check.
*
* @return {bool}
* Returns true if the section has empty properties, false otherwise.
*/
function emptyProperties(section) {
return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
@ -200,12 +206,18 @@
* tag.
*
* @param {object} universe
* The universe to check.
* @param {string} tag
* The tag to look for.
* @param {string} property
* The property to check.
* @param {Array} propertyValues
* Values of the property to check.
* @param {bool} allowing
* Whether to update the universe or not.
*
* @return {bool}
* Returns true if found, false otherwise.
*/
function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
// Detect the wildcard case.
@ -226,11 +238,16 @@
* Calls findPropertyValuesOnAllTags for all tags in the universe.
*
* @param {object} universe
* The universe to check.
* @param {string} property
* The property to check.
* @param {Array} propertyValues
* Values of the property to check.
* @param {bool} allowing
* Whether to update the universe or not.
*
* @return {bool}
* Returns true if found, false otherwise.
*/
function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
var atLeastOneFound = false;
@ -249,12 +266,18 @@
* value exists. Returns true if found, false otherwise.
*
* @param {object} universe
* The universe to check.
* @param {string} tag
* The tag to look for.
* @param {string} property
* The property to check.
* @param {string} propertyValue
* The property value to check.
* @param {bool} allowing
* Whether to update the universe or not.
*
* @return {bool}
* Returns true if found, false otherwise.
*/
function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
// If the tag does not exist in the universe, then it definitely can't
@ -303,9 +326,12 @@
* properties are marked as allowed.
*
* @param {object} universe
* The universe to delete from.
* @param {string} tag
* The tag to check.
*
* @return {bool}
* Whether something was deleted from the universe.
*/
function deleteFromUniverseIfAllowed(universe, tag) {
// Detect the wildcard case.
@ -323,8 +349,10 @@
* Calls deleteFromUniverseIfAllowed for all tags in the universe.
*
* @param {object} universe
* The universe to delete from.
*
* @return {bool}
* Whether something was deleted from the universe.
*/
function deleteAllTagsFromUniverseIfAllowed(universe) {
var atLeastOneDeleted = false;
@ -341,9 +369,12 @@
* that exists in the universe.
*
* @param {object} universe
* Universe to check.
* @param {object} filterStatus
* Filter status to use for check.
*
* @return {bool}
* Whether any filter rule forbids something in the universe.
*/
function anyForbiddenFilterRuleMatches(universe, filterStatus) {
var properties = ['attributes', 'styles', 'classes'];
@ -392,7 +423,9 @@
* deleted from the universe.
*
* @param {object} universe
* Universe to delete from.
* @param {object} filterStatus
* The filter status in question.
*/
function markAllowedTagsAndPropertyValues(universe, filterStatus) {
var properties = ['attributes', 'styles', 'classes'];
@ -445,9 +478,12 @@
* that.
*
* @param {object} filterStatus
* The filter status in question.
* @param {object} feature
* The feature requested.
*
* @return {bool}
* Whether the current status of the filter allows specified feature.
*
* @see generateUniverseFromFeatureRequirements()
*/
@ -768,7 +804,10 @@
* }
* }
*
* @return {{
* @return {object}
* An object with the following structure:
* ```
* {
* tags: Array,
* allow: null,
* restrictedTags: {
@ -776,9 +815,10 @@
* allowed: {attributes: Array, styles: Array, classes: Array},
* forbidden: {attributes: Array, styles: Array, classes: Array}
* }
* }}
* }
* ```
*
* @see Drupal.FilterStatus
* @see Drupal.FilterStatus
*/
Drupal.FilterHTMLRule = function () {
return {
@ -853,6 +893,9 @@
* Initializes {@link Drupal.filterConfiguration}.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Gets filter configuration from filter form input.
*/
Drupal.behaviors.initializeFilterConfiguration = {
attach: function (context, settings) {

View file

@ -17,9 +17,13 @@
* 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
*/

View file

@ -51,6 +51,7 @@
* @augments Drupal.quickedit.EditorView
*
* @param {object} options
* Options for the editor view.
*/
initialize: function (options) {
Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
@ -62,7 +63,13 @@
// Store the actual value of this field. We'll need this to restore the
// original value when the user discards his modifications.
this.$textElement = this.$el.find('.field-item').eq(0);
var $fieldItems = this.$el.find('.field__item');
if ($fieldItems.length) {
this.$textElement = $fieldItems.eq(0);
}
else {
this.$textElement = this.$el;
}
this.model.set('originalValue', this.$textElement.html());
},
@ -70,6 +77,7 @@
* @inheritdoc
*
* @return {jQuery}
* The text element edited.
*/
getEditedElement: function () {
return this.$textElement;
@ -79,7 +87,9 @@
* @inheritdoc
*
* @param {object} fieldModel
* The field model.
* @param {string} state
* The current state.
*/
stateChange: function (fieldModel, state) {
var editorModel = this.model;
@ -172,6 +182,7 @@
* @inheritdoc
*
* @return {object}
* The sttings for the quick edit UI.
*/
getQuickEditUISettings: function () {
return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: false};

View file

@ -59,6 +59,7 @@
* Handles changes in text format.
*
* @param {jQuery.Event} event
* The text format change event.
*/
function onTextFormatChange(event) {
var $select = $(event.target);
@ -89,7 +90,7 @@
buttons: [
{
text: Drupal.t('Continue'),
'class': 'button button--primary',
class: 'button button--primary',
click: function () {
changeTextEditor(field, newFormatID);
confirmationDialog.close();
@ -97,11 +98,12 @@
},
{
text: Drupal.t('Cancel'),
'class': 'button',
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.
// 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();
}
@ -138,6 +140,11 @@
* 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) {
@ -161,13 +168,13 @@
// 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.
// 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, other-
// wise this code would not be executed.
// 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.
@ -309,8 +316,8 @@
url: Drupal.url('editor/filter_xss/' + format.format),
type: 'POST',
data: {
'value': field.value,
'original_format_id': originalFormatID
value: field.value,
original_format_id: originalFormatID
},
dataType: 'json',
success: function (xssFilteredValue) {

View file

@ -48,7 +48,7 @@ class EditorController extends ControllerBase {
// Direct text editing is only supported for single-valued fields.
$field = $entity->getTranslation($langcode)->$field_name;
$editable_text = check_markup($field->value, $field->format, $langcode, array(FilterInterface::TYPE_TRANSFORM_REVERSIBLE, FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE));
$response->addCommand(new GetUntransformedTextCommand((string) $editable_text));
$response->addCommand(new GetUntransformedTextCommand($editable_text));
return $response;
}

View file

@ -9,7 +9,6 @@ namespace Drupal\editor\EditorXssFilter;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\filter\FilterFormatInterface;
use Drupal\editor\EditorXssFilterInterface;
@ -114,7 +113,7 @@ class Standard extends Xss implements EditorXssFilterInterface {
// value. There is no need to explicitly decode $node->value, since the
// DOMAttr::value getter returns the decoded value.
$value = Xss::filterAdmin($node->value);
$node->value = SafeMarkup::checkPlain($value);
$node->value = Html::escape($value);
}
$html = Html::serialize($dom);
}

View file

@ -7,6 +7,10 @@
namespace Drupal\editor\Tests;
use Drupal\Component\Utility\Unicode;
use Drupal\filter\Entity\FilterFormat;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\simpletest\WebTestBase;
/**
@ -96,24 +100,88 @@ class EditorAdminTest extends WebTestBase {
* Tests adding a text editor to a new text format.
*/
public function testAddEditorToNewFormat() {
$this->addEditorToNewFormat('monocerus', 'Monocerus');
$this->verifyUnicornEditorConfiguration('monocerus');
}
/**
* Tests format disabling.
*/
public function testDisableFormatWithEditor() {
$formats = ['monocerus' => 'Monocerus', 'tattoo' => 'Tattoo'];
// Install the node module.
$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->save();
node_add_body_field($node_type, $this->randomString());
$permissions = ['administer filters', "edit any {$node_type->id()} content"];
foreach ($formats as $format => $name) {
// Create a format and add an editor to this format.
$this->addEditorToNewFormat($format, $name);
// Add permission for this format.
$permissions[] = "use text format $format";
}
// Create a node having the body format value 'moncerus'.
$node = Node::create([
'type' => $node_type->id(),
'title' => $this->randomString(),
]);
$node->body->value = $this->randomString(100);
$node->body->format = 'monocerus';
$node->save();
// Login as an user able to use both formats and edit nodes of created type.
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
// The node edit page header.
$text = t('<em>Edit @type</em> @title', array('@type' => $node_type->label(), '@title' => $node->label()));
// Go to node edit form.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertRaw($text);
// Disable the format assigned to the 'body' field of the node.
FilterFormat::load('monocerus')->disable()->save();
// Edit again the node.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertRaw($text);
}
/**
* Adds an editor to a new format using the UI.
*
* @param string $format_id
* The format id.
* @param string $format_name
* The format name.
*/
protected function addEditorToNewFormat($format_id, $format_name) {
$this->enableUnicornEditor();
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/config/content/formats/add');
// Configure the text format name.
$edit = array(
'name' => 'Monocerus',
'format' => 'monocerus',
'name' => $format_name,
'format' => $format_id,
);
$edit += $this->selectUnicornEditor();
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
$this->verifyUnicornEditorConfiguration($edit['format']);
}
/**
* Enables the unicorn editor.
*/
protected function enableUnicornEditor() {
\Drupal::service('module_installer')->install(array('editor_test'));
if (!$this->container->get('module_handler')->moduleExists('editor_test')) {
$this->container->get('module_installer')->install(array('editor_test'));
}
}
/**

View file

@ -9,7 +9,6 @@ namespace Drupal\editor\Tests;
use Drupal\Component\Serialization\Json;
use Drupal\simpletest\WebTestBase;
use Drupal\Component\Utility\SafeMarkup;
/**
* Tests XSS protection for content creators when using text editors.
@ -96,7 +95,7 @@ class EditorSecurityTest extends WebTestBase {
'filter_html' => array(
'status' => 1,
'settings' => array(
'allowed_html' => '<h4> <h5> <h6> <p> <br> <strong> <a>',
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
)
),
),
@ -111,7 +110,7 @@ class EditorSecurityTest extends WebTestBase {
'filter_html' => array(
'status' => 1,
'settings' => array(
'allowed_html' => '<h4> <h5> <h6> <p> <br> <strong> <a>',
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
)
),
),
@ -131,7 +130,7 @@ class EditorSecurityTest extends WebTestBase {
'filter_html' => array(
'status' => 1,
'settings' => array(
'allowed_html' => '<h4> <h5> <h6> <p> <br> <strong> <a> <embed>',
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a> <embed>',
)
),
),
@ -388,7 +387,6 @@ class EditorSecurityTest extends WebTestBase {
// Log in as the privileged user, and for every sample, do the following:
// - switch to every other text format/editor
// - assert the XSS-filtered values that we get from the server
$value_original_attribute = SafeMarkup::checkPlain(self::$sampleContent);
$this->drupalLogin($this->privilegedUser);
foreach ($expected as $case) {
$this->drupalGet('node/' . $case['node_id'] . '/edit');

View file

@ -176,7 +176,6 @@ class QuickEditIntegrationTest extends QuickEditTestBase {
'access' => TRUE,
'label' => 'Long text field',
'editor' => 'editor',
'aria' => 'Entity entity_test 1, field Long text field',
'custom' => array(
'format' => 'full_html',
'formatHasTransformations' => FALSE,