core/core.libraries.yml | 4 +- .../ckeditor/js/plugins/drupalimage/plugin.js | 45 +++++++++++++--------- .../js/plugins/drupalimagecaption/plugin.js | 16 +++++--- .../ckeditor/Plugin/CKEditorPlugin/Internal.php | 9 +++++ .../lib/Drupal/ckeditor/Tests/CKEditorTest.php | 4 +- .../Drupal/filter/Plugin/Filter/FilterCaption.php | 5 ++- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/core/core.libraries.yml b/core/core.libraries.yml index a4b04c4..a56f674 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -17,9 +17,7 @@ classList: ckeditor: remote: https://github.com/ckeditor/ckeditor-dev - # @todo Stable release required for Drupal 8.0. - version: 4.4-dev - commit: ae32d4c7755b36f376ad3be8481fb178313bfe58 + version: 4.4.0 js: assets/vendor/ckeditor/ckeditor.js: { preprocess: false } diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js index 77ef300..1ef9124 100644 --- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js @@ -14,7 +14,7 @@ "use strict"; CKEDITOR.plugins.add('drupalimage', { - requires: 'widget,image2', + requires: 'image2', beforeInit: function (editor) { // Override the image2 widget definition to require and handle the @@ -25,32 +25,39 @@ return; } - // Override default features definitions for drupalimage. - CKEDITOR.tools.extend(widgetDefinition.features, { - image: { - requiredContent: 'img[data-editor-file-uuid]' - } - }, true); - // Override requiredContent & allowedContent. - widgetDefinition.requiredContent = 'img[src,alt,data-editor-file-uuid]'; + widgetDefinition.requiredContent = 'img[alt,src,width,height,data-editor-file-uuid]'; widgetDefinition.allowedContent.img.attributes += ',!data-editor-file-uuid'; - - // Override init(): parse the original data-editor-file-uuid attribute. - var originalInit = widgetDefinition.init; - widgetDefinition.init = function () { - originalInit.call(this); - this.setData('data-editor-file-uuid', this.parts.image.getAttribute('data-editor-file-uuid')); - }; + // We don't allow
or
in our downcast. + delete widgetDefinition.allowedContent.figure; + delete widgetDefinition.allowedContent.figcaption; // Override the 'link' part, to completely disable image2's link // support: http://dev.ckeditor.com/ticket/11341. widgetDefinition.parts.link = 'This is a nonsensical selector to disable this functionality completely'; - // Override downcast(): ensure we *only* output , but also ensure - // we include the data-editor-file-uuid attribute. + // Override downcast(): since we only accept in our upcast method, + // the element is already correct. We only need to update the element's + // data-editor-file-uuid attribute. widgetDefinition.downcast = function (element) { element.attributes['data-editor-file-uuid'] = this.data['data-editor-file-uuid']; + }; + + // We want to upcast elements to a DOM structure required by the + // image2 widget; we only accept an tag, and that tag MAY + // have a data-editor-file-uuid attribute. + widgetDefinition.upcast = function (element, data) { + if (element.name !== 'img') { + return; + } + // Don't initialize on pasted fake objects. + else if (element.attributes['data-cke-realelement']) { + return; + } + + // Parse the data-editor-file-uuid attribute. + data['data-editor-file-uuid'] = element.attributes['data-editor-file-uuid']; + return element; }; @@ -193,7 +200,7 @@ if (editor.contextMenu) { editor.addMenuItem('drupalimage', { label: Drupal.t('Image Properties'), - command: 'drupalimage', + command: 'editdrupalimage', group: 'image' }); editor.contextMenu.addListener(function (element, selection) { diff --git a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js index 70bb124..be65efa 100644 --- a/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.js @@ -12,7 +12,7 @@ "use strict"; CKEDITOR.plugins.add('drupalimagecaption', { - requires: 'widget,image2,drupalimage', + requires: 'drupalimage', beforeInit: function (editor) { // Disable default placeholder text that comes with CKEditor's image2 @@ -44,14 +44,14 @@ }, true); // Override requiredContent & allowedContent. - widgetDefinition.requiredContent = 'img[src,alt,data-editor-file-uuid,data-align,data-caption]'; + widgetDefinition.requiredContent = 'img[alt,src,width,height,data-editor-file-uuid,data-align,data-caption]'; widgetDefinition.allowedContent.img.attributes += ',data-align,data-caption'; // Override allowedContent setting for the 'caption' nested editable. // This must match what caption_filter enforces. // @see \Drupal\filter\Plugin\Filter\FilterCaption::process() // @see \Drupal\Component\Utility\Xss::filter() - widgetDefinition.editables.caption.allowedContent = 'a[!href]; em strong cite blockquote code ul ol li dl dt dd'; + widgetDefinition.editables.caption.allowedContent = 'a[!href]; em strong cite code'; // Override downcast(): ensure we *only* output , but also ensure // we include the data-editor-file-uuid, data-align and data-caption @@ -76,19 +76,23 @@ return img; }; - // We want to upcast element to a DOM structure required by the + // We want to upcast elements to a DOM structure required by the // image2 widget. Depending on a case it may be: // - just an tag (non-captioned, not-centered image), // - tag in a paragraph (non-captioned, centered image), // -
tag (captioned image). // We take the same attributes into account as downcast() does. widgetDefinition.upcast = function (element, data) { - if (element.name !== 'img') { + if (element.name !== 'img' || !element.attributes['data-editor-file-uuid']) { + return; + } + // Don't initialize on pasted fake objects. + else if (element.attributes['data-cke-realelement']) { return; } var attrs = element.attributes, - retElement = null; + retElement = element; // We won't need the attributes during editing: we'll use widget.data // to store them (except the caption, which is stored in the DOM). diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php index 69a58a4..fdaefc7 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php @@ -430,6 +430,15 @@ protected function generateACFSettings(Editor $editor) { return $value === FALSE; }); if (count($disallowed_attributes)) { + // No need to blacklist the 'class' or 'style' attributes; CKEditor + // handles them separately (if no specific class or style attribute + // values are allowed, then those attributes are disallowed). + if (isset($disallowed_attributes['class'])) { + unset($disallowed_attributes['class']); + } + if (isset($disallowed_attributes['style'])) { + unset($disallowed_attributes['style']); + } $disallowed[$tag]['attributes'] = implode(',', array_keys($disallowed_attributes)); } if (isset($allowed_attributes['style']) && is_array($allowed_attributes['style'])) { diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php index ad121ee..7381dd0 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php @@ -205,7 +205,7 @@ function testGetJSSettings() { 'classes' => 'dodo', ), '*' => array( - 'attributes' => 'style,on*', + 'attributes' => 'on*', ), ); $expected_config['format_tags'] = 'p'; @@ -387,7 +387,7 @@ protected function getDefaultAllowedContentConfig() { protected function getDefaultDisallowedContentConfig() { return array( - '*' => array('attributes' => 'style,on*'), + '*' => array('attributes' => 'on*'), ); } diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php index b8fe2ef..edcb78f 100644 --- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php +++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php @@ -41,9 +41,10 @@ public function process($text, $langcode, $cache, $cache_id) { if ($node->hasAttribute('data-caption')) { $caption = String::checkPlain($node->getAttribute('data-caption')); $node->removeAttribute('data-caption'); - // Sanitize caption: decode HTML encoding, limit allowed HTML tags. + // Sanitize caption: decode HTML encoding, limit allowed HTML tags; + // only allow inline tags that are allowed by default. $caption = String::decodeEntities($caption); - $caption = Xss::filter($caption); + $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code')); // The caption must be non-empty. if (Unicode::strlen($caption) === 0) { $caption = NULL;