Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663

This commit is contained in:
Greg Anderson 2015-10-08 11:40:12 -07:00
parent eb34d130a8
commit f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions

View file

@ -18,6 +18,10 @@ use Drupal\Core\Language\LanguageInterface;
* An associative array containing:
* - editor: An editor object.
* - plugins: A list of plugins.
* - active_buttons: A list of disabled buttons.
* - disabled_buttons: A list of disabled buttons.
* - multiple_buttons: A list of multiple buttons that may be added multiple
* times.
*/
function template_preprocess_ckeditor_settings_toolbar(&$variables) {
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
@ -70,10 +74,10 @@ function template_preprocess_ckeditor_settings_toolbar(&$variables) {
elseif (isset($button['image_alternative'])) {
$value = $button['image_alternative'];
}
elseif (isset($button['image'])) {
elseif (isset($button['image']) || isset($button['image' . $rtl])) {
$value = array(
'#theme' => 'image',
'#uri' => $button['image' . $rtl],
'#uri' => isset($button['image' . $rtl]) ? $button['image' . $rtl] : $button['image'],
'#title' => $button['label'],
'#prefix' => '<a href="#" role="button" title="' . $button['label'] . '" aria-label="' . $button['label'] . '"><span class="cke_button_icon">',
'#suffix' => '</span></a>',
@ -113,6 +117,7 @@ function template_preprocess_ckeditor_settings_toolbar(&$variables) {
$variables['active_buttons'] = array();
foreach ($active_buttons as $row_number => $button_row) {
foreach ($button_groups[$row_number] as $group_name) {
$group_name = (string) $group_name;
$variables['active_buttons'][$row_number][$group_name] = array(
'group_name_class' => Html::getClass($group_name),
'buttons' => array(),

View file

@ -11,6 +11,8 @@ drupal.ckeditor:
- core/drupal.debounce
- core/ckeditor
- editor/drupal.editor
# Ensure to run after core/matchmedia.
- core/matchmedia
drupal.ckeditor.plugins.drupalimagecaption:
version: VERSION

View file

@ -16,19 +16,19 @@ function ckeditor_help($route_name, RouteMatchInterface $route_match) {
case 'help.page.ckeditor':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The CKEditor module provides a highly-accessible, highly-usable visual text editor and adds a toolbar to text fields. Users can use buttons to format content and to create semantically correct and valid HTML. The CKEditor module uses the framework provided by the <a href="!text_editor">Text Editor module</a>. It requires JavaScript to be enabled in the browser. For more information, see <a href="!doc_url">the online documentation for the CKEditor module</a> and the <a href="!cke_url">CKEditor website</a>.', array( '!doc_url' => 'https://www.drupal.org/documentation/modules/ckeditor', '!cke_url' => 'http://ckeditor.com', '!text_editor' => \Drupal::url('help.page', array('name' => 'editor')))) . '</p>';
$output .= '<p>' . t('The CKEditor module provides a highly-accessible, highly-usable visual text editor and adds a toolbar to text fields. Users can use buttons to format content and to create semantically correct and valid HTML. The CKEditor module uses the framework provided by the <a href=":text_editor">Text Editor module</a>. It requires JavaScript to be enabled in the browser. For more information, see the <a href=":doc_url">online documentation for the CKEditor module</a> and the <a href=":cke_url">CKEditor website</a>.', array( ':doc_url' => 'https://www.drupal.org/documentation/modules/ckeditor', ':cke_url' => 'http://ckeditor.com', ':text_editor' => \Drupal::url('help.page', array('name' => 'editor')))) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Enabling CKEditor for individual text formats') . '</dt>';
$output .= '<dd>' . t('CKEditor has to be enabled and configured separately for individual text formats from the <a href="!formats">Text formats and editors page</a> because the filter settings for each text format can be different. For more information, see the <a href="!text_editor">Text Editor help page</a> and <a href="!filter">Filter help page</a>.', array('!formats' => \Drupal::url('filter.admin_overview'), '!text_editor' => \Drupal::url('help.page', array('name' => 'editor')), '!filter' => \Drupal::url('help.page', array('name' => 'filter')))) . '</dd>';
$output .= '<dd>' . t('CKEditor has to be enabled and configured separately for individual text formats from the <a href=":formats">Text formats and editors page</a> because the filter settings for each text format can be different. For more information, see the <a href=":text_editor">Text Editor help page</a> and <a href=":filter">Filter help page</a>.', array(':formats' => \Drupal::url('filter.admin_overview'), ':text_editor' => \Drupal::url('help.page', array('name' => 'editor')), ':filter' => \Drupal::url('help.page', array('name' => 'filter')))) . '</dd>';
$output .= '<dt>' . t('Configuring the toolbar') . '</dt>';
$output .= '<dd>' . t('When CKEditor is chosen from the <em>Text editor</em> drop-down menu, its toolbar configuration is displayed. You can add and remove buttons from the <em>Active toolbar</em> by dragging and dropping them, and additional rows can be added to organize the buttons.') . '</dd>';
$output .= '<dt>' . t('Formatting content') . '</dt>';
$output .= '<dd>' . t('CKEditor only allow users to format content in accordance with the filter configuration of the specific text format. If a text format excludes certain HTML tags, the corresponding toolbar buttons are not displayed to users when they edit a text field in this format. For more information see the <a href="!filter">Filter help page</a>.', array('!filter' => \Drupal::url('help.page', array('name' => 'filter')))) . '</dd>';
$output .= '<dd>' . t('CKEditor only allow users to format content in accordance with the filter configuration of the specific text format. If a text format excludes certain HTML tags, the corresponding toolbar buttons are not displayed to users when they edit a text field in this format. For more information see the <a href=":filter">Filter help page</a>.', array(':filter' => \Drupal::url('help.page', array('name' => 'filter')))) . '</dd>';
$output .= '<dt>' . t('Toggling between formatted text and HTML source') . '</dt>';
$output .= '<dd>' . t('If the <em>Source</em> button is available in the toolbar, users can click this button to disable the visual editor and edit the HTML source directly. After toggling back, the visual editor uses the allowed HTML tags to format the text — independent of whether buttons for these tags are available in the toolbar. If the text format is set to <em>limit the use of HTML tags</em>, then all excluded tags will be stripped out of the HTML source when the user toggles back to the text editor.') . '</dd>';
$output .= '<dt>' . t('Accessibility features') . '</dt>';
$output .= '<dd>' . t('The built in WYSIWYG editor (CKEditor) comes with a number of <a href="!features">accessibility features</a>. CKEditor comes with built in <a href="!shortcuts">keyboard shortcuts</a>, which can be beneficial for both power users and keyboard only users.', array('!features' => 'http://docs.ckeditor.com/#!/guide/dev_a11y', '!shortcuts' => 'http://docs.ckeditor.com/#!/guide/dev_shortcuts')) . '</dd>';
$output .= '<dd>' . t('The built in WYSIWYG editor (CKEditor) comes with a number of <a href=":features">accessibility features</a>. CKEditor comes with built in <a href=":shortcuts">keyboard shortcuts</a>, which can be beneficial for both power users and keyboard only users.', array(':features' => 'http://docs.ckeditor.com/#!/guide/dev_a11y', ':shortcuts' => 'http://docs.ckeditor.com/#!/guide/dev_shortcuts')) . '</dd>';
$output .= '<dt>' . t('Generating accessible content') . '</dt>';
$output .= '<dd>' . t('HTML tables can be created with both table headers as well as caption/summary elements. Alt text is required by default on images added through CKEditor (note that this can be overridden). Semantic HTML5 figure/figcaption are available to add captions to images.') . '</dd>';
$output .= '</dl>';

View file

@ -182,11 +182,16 @@
.ckeditor-buttons li .cke-icon-only {
text-indent: -9999px;
width: 16px;
direction: ltr;
/* Firefox includes the offscreen text in the focus indicator, resulting in a
far too wide focus indicator. This fixes that. */
overflow: hidden;
}
.ckeditor-buttons li .cke_ltr {
direction: ltr;
}
.ckeditor-buttons li .cke_rtl {
direction: rtl;
}
.ckeditor-buttons li a:focus,
.ckeditor-buttons li a:active,
.ckeditor-multiple-buttons li a:focus {

View file

@ -25,9 +25,9 @@
var $configurationForm = $(context).find('.ckeditor-toolbar-configuration').once('ckeditor-configuration');
if ($configurationForm.length) {
var $textarea = $configurationForm
// Hide the textarea that contains the serialized representation of
// the CKEditor configuration.
.find('.form-item-editor-settings-toolbar-button-groups')
// Hide the textarea that contains the serialized representation of the
// CKEditor configuration.
.find('.js-form-item-editor-settings-toolbar-button-groups')
.hide()
// Return the textarea child node from this expression.
.find('textarea');

View file

@ -36,12 +36,6 @@
var label = $('label[for=' + element.getAttribute('id') + ']').html();
format.editorSettings.title = Drupal.t("Rich Text Editor, !label field", {'!label': label});
// CKEditor initializes itself in a read-only state if the 'disabled'
// attribute is set. It does not respect the 'readonly' attribute,
// however, so we set the 'readOnly' configuration property manually in
// that case, for the CKEditor instance that's about to be created.
format.editorSettings.readOnly = element.hasAttribute('readonly');
return !!CKEDITOR.replace(element, format.editorSettings);
},
@ -221,9 +215,10 @@
// Add a consistent dialog class.
var classes = dialogSettings.dialogClass ? dialogSettings.dialogClass.split(' ') : [];
classes.push('editor-dialog');
classes.push('ui-dialog--narrow');
dialogSettings.dialogClass = classes.join(' ');
dialogSettings.autoResize = Drupal.checkWidthBreakpoint(600);
dialogSettings.autoResize = window.matchMedia('(min-width: 600px)').matches;
dialogSettings.width = 'auto';
// Add a "Loading…" message, hide it underneath the CKEditor toolbar,
// create a Drupal.Ajax instance to load the dialog and trigger it.
@ -254,7 +249,7 @@
// Moves the dialog to the top of the CKEDITOR stack.
$(window).on('dialogcreate', function (e, dialog, $element, settings) {
$('.editor-dialog').css("zIndex", CKEDITOR.config.baseFloatZIndex + 1);
$('.ui-dialog--narrow').css("zIndex", CKEDITOR.config.baseFloatZIndex + 1);
});
// Respond to new dialogs that are opened by CKEditor, closing the AJAX loader.

View file

@ -30,13 +30,38 @@
}
// Override requiredContent & allowedContent.
widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid]';
widgetDefinition.allowedContent.img.attributes += ',!data-entity-type,!data-entity-uuid';
// We don't allow <figure>, <figcaption>, <div> or <p> in our downcast.
delete widgetDefinition.allowedContent.figure;
delete widgetDefinition.allowedContent.figcaption;
delete widgetDefinition.allowedContent.div;
delete widgetDefinition.allowedContent.p;
widgetDefinition.requiredContent = new CKEDITOR.style({
element: 'img',
attributes: {
'alt': '',
'src': '',
'width': '',
'height': '',
'data-entity-type': '',
'data-entity-uuid': ''
}
});
var allowedContentDefinition = {
element: 'img',
attributes: {
'!data-entity-type': '',
'!data-entity-uuid': ''
}
};
var imgAttributes = widgetDefinition.allowedContent.img.attributes.split(/\s*,\s*/);
for (var i = 0; i < imgAttributes.length; i++) {
allowedContentDefinition.attributes[imgAttributes[i]] = '';
}
if (widgetDefinition.allowedContent.img.classes) {
allowedContentDefinition.attributes['class'] = widgetDefinition.allowedContent.img.classes.split(/\s*,\s*/).join(' ');
}
if (widgetDefinition.allowedContent.img.styles) {
var imgStyles = widgetDefinition.allowedContent.img.styles.split(/\s*,\s*/);
for (var j = 0; j < imgStyles.length; j++) {
allowedContentDefinition.styles[imgStyles[j]] = '';
}
}
widgetDefinition.allowedContent = new CKEDITOR.style(allowedContentDefinition);
// Override the 'link' part, to completely disable image2's link
// support: http://dev.ckeditor.com/ticket/11341.

View file

@ -51,8 +51,14 @@
}, true);
// Override requiredContent & allowedContent.
widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid,data-align,data-caption]';
widgetDefinition.allowedContent.img.attributes += ',!data-align,!data-caption';
var requiredContent = widgetDefinition.requiredContent.getDefinition();
requiredContent.attributes['data-align'] = '';
requiredContent.attributes['data-caption'] = '';
widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
var allowedContent = widgetDefinition.allowedContent.getDefinition();
allowedContent.attributes['!data-align'] = '';
allowedContent.attributes['!data-caption'] = '';
widgetDefinition.allowedContent = new CKEDITOR.style(allowedContent);
// Override allowedContent setting for the 'caption' nested editable.
// This must match what caption_filter enforces.
@ -63,9 +69,12 @@
// Override downcast(): ensure we *only* output <img>, but also ensure
// we include the data-entity-type, data-entity-uuid, data-align and
// data-caption attributes.
var originalDowncast = widgetDefinition.downcast;
widgetDefinition.downcast = function (element) {
// Find an image element in the one being downcasted (can be itself).
var img = findElementByName(element, 'img');
var img = originalDowncast.call(this, element);
if (!img) {
img = findElementByName(element, 'img');
}
var caption = this.editables.caption;
var captionHtml = caption && caption.getData();
var attrs = img.attributes;
@ -94,6 +103,7 @@
// - <img> tag in a paragraph (non-captioned, centered image),
// - <figure> tag (captioned image).
// We take the same attributes into account as downcast() does.
var originalUpcast = widgetDefinition.upcast;
widgetDefinition.upcast = function (element, data) {
if (element.name !== 'img' || !element.attributes['data-entity-type'] || !element.attributes['data-entity-uuid']) {
return;
@ -103,6 +113,7 @@
return;
}
element = originalUpcast.call(this, element, data);
var attrs = element.attributes;
var retElement = element;
var caption;
@ -210,6 +221,15 @@
// upcasting existing elements (see widgetDefinition.upcast).
if (dialogReturnValues.attributes.hasCaption) {
actualWidget.editables.caption.setAttribute('data-placeholder', placeholderText);
// Some browsers will add a <br> tag to a newly created DOM
// element with no content. Remove this <br> if it is the only
// thing in the caption. Our placeholder support requires the
// element be entirely empty. See filter-caption.css.
var captionElement = actualWidget.editables.caption.$;
if (captionElement.childNodes.length === 1 && captionElement.childNodes.item(0).nodeName === 'BR') {
captionElement.removeChild(captionElement.childNodes.item(0));
}
}
};
};

View file

@ -13,8 +13,19 @@
init: function (editor) {
// Add the commands for link and unlink.
editor.addCommand('drupallink', {
allowedContent: 'a[!href,target]',
requiredContent: 'a[href]',
allowedContent: new CKEDITOR.style({
element: 'a',
attributes: {
'!href': '',
'target': ''
}
}),
requiredContent: new CKEDITOR.style({
element: 'a',
attributes: {
href: ''
}
}),
modes: {wysiwyg: 1},
canUndo: true,
exec: function (editor) {
@ -111,8 +122,19 @@
editor.addCommand('drupalunlink', {
contextSensitive: 1,
startDisabled: 1,
allowedContent: 'a[!href]',
requiredContent: 'a[href]',
allowedContent: new CKEDITOR.style({
element: 'a',
attributes: {
'!href': '',
'target': ''
}
}),
requiredContent: new CKEDITOR.style({
element: 'a',
attributes: {
href: ''
}
}),
exec: function (editor) {
var style = new CKEDITOR.style({element: 'a', type: CKEDITOR.STYLE_INLINE, alwaysRemoveElement: 1});
editor.removeStyle(style);

View file

@ -221,6 +221,11 @@
// the feature that was just added or removed. Not every feature has
// such metadata.
var featureName = this.model.get('buttonsToFeatures')[button.toLowerCase()];
// Features without an associated command do not have a 'feature name' by
// default, so we use the lowercased button name instead.
if (!featureName) {
featureName = button.toLowerCase();
}
var featuresMetadata = this.model.get('featuresMetadata');
if (!featuresMetadata[featureName]) {
featuresMetadata[featureName] = new Drupal.EditorFeature(featureName);
@ -301,7 +306,6 @@
broadcastConfigurationChanges: function ($ckeditorToolbar) {
var view = this;
var hiddenEditorConfig = this.model.get('hiddenEditorConfig');
var featuresMetadata = this.model.get('featuresMetadata');
var getFeatureForButton = this.getFeatureForButton.bind(this);
var getCKEditorFeatures = this.getCKEditorFeatures.bind(this);
$ckeditorToolbar
@ -335,6 +339,7 @@
getCKEditorFeatures(hiddenEditorConfig, function (features) {
// Trigger a standardized text editor configuration event for each
// feature that was modified by the configuration changes.
var featuresMetadata = view.model.get('featuresMetadata');
for (var name in features) {
if (features.hasOwnProperty(name)) {
var feature = features[name];

View file

@ -129,6 +129,9 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
$button = function($name, $direction = 'ltr') {
// In the markup below, we mostly use the name (which may include spaces),
// but in one spot we use it as a CSS class, so strip spaces.
// Note: this uses str_replace() instead of Html::cleanCssIdentifier()
// because we must provide these class names exactly how CKEditor expects
// them in its library, which cleanCssIdentifier() does not do.
$class_name = str_replace(' ', '', $name);
return [
'#type' => 'inline_template',
@ -146,48 +149,59 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
'Bold' => array(
'label' => t('Bold'),
'image_alternative' => $button('bold'),
'image_alternative_rtl' => $button('bold', 'rtl'),
),
'Italic' => array(
'label' => t('Italic'),
'image_alternative' => $button('italic'),
'image_alternative_rtl' => $button('italic', 'rtl'),
),
'Underline' => array(
'label' => t('Underline'),
'image_alternative' => $button('underline'),
'image_alternative_rtl' => $button('underline', 'rtl'),
),
'Strike' => array(
'label' => t('Strike-through'),
'image_alternative' => $button('strike'),
'image_alternative_rtl' => $button('strike', 'rtl'),
),
'Superscript' => array(
'label' => t('Superscript'),
'image_alternative' => $button('super script'),
'image_alternative_rtl' => $button('super script', 'rtl'),
),
'Subscript' => array(
'label' => t('Subscript'),
'image_alternative' => $button('sub script'),
'image_alternative_rtl' => $button('sub script', 'rtl'),
),
// "removeformat" plugin.
'RemoveFormat' => array(
'label' => t('Remove format'),
'image_alternative' => $button('remove format'),
'image_alternative_rtl' => $button('remove format', 'rtl'),
),
// "justify" plugin.
'JustifyLeft' => array(
'label' => t('Align left'),
'image_alternative' => $button('justify left'),
'image_alternative_rtl' => $button('justify left', 'rtl'),
),
'JustifyCenter' => array(
'label' => t('Align center'),
'image_alternative' => $button('justify center'),
'image_alternative_rtl' => $button('justify center', 'rtl'),
),
'JustifyRight' => array(
'label' => t('Align right'),
'image_alternative' => $button('justify right'),
'image_alternative_rtl' => $button('justify right', 'rtl'),
),
'JustifyBlock' => array(
'label' => t('Justify'),
'image_alternative' => $button('justify block'),
'image_alternative_rtl' => $button('justify block', 'rtl'),
),
// "list" plugin.
'BulletedList' => array(
@ -226,11 +240,13 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
'Blockquote' => array(
'label' => t('Blockquote'),
'image_alternative' => $button('blockquote'),
'image_alternative_rtl' => $button('blockquote', 'rtl'),
),
// "horizontalrule" plugin
'HorizontalRule' => array(
'label' => t('Horizontal rule'),
'image_alternative' => $button('horizontal rule'),
'image_alternative_rtl' => $button('horizontal rule', 'rtl'),
),
// "clipboard" plugin.
'Cut' => array(
@ -264,6 +280,7 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
'SpecialChar' => array(
'label' => t('Character map'),
'image_alternative' => $button('special char'),
'image_alternative_rtl' => $button('special char', 'rtl'),
),
'Format' => array(
'label' => t('HTML block format'),
@ -279,6 +296,7 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
'Table' => array(
'label' => t('Table'),
'image_alternative' => $button('table'),
'image_alternative_rtl' => $button('table', 'rtl'),
),
// "showblocks" plugin.
'ShowBlocks' => array(
@ -290,11 +308,13 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
'Source' => array(
'label' => t('Source code'),
'image_alternative' => $button('source'),
'image_alternative_rtl' => $button('source', 'rtl'),
),
// "maximize" plugin.
'Maximize' => array(
'label' => t('Maximize'),
'image_alternative' => $button('maximize'),
'image_alternative_rtl' => $button('maximize', 'rtl'),
),
// No plugin, separator "button" for toolbar builder UI use only.
'-' => array(
@ -496,6 +516,13 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
}
// Tell CKEditor the tag is allowed, along with some tags.
elseif (is_array($attributes)) {
// Set defaults (these will be overridden below if more specific
// values are present).
$allowed[$tag] = array(
'attributes' => FALSE,
'styles' => FALSE,
'classes' => FALSE,
);
// Configure allowed attributes, allowed "style" attribute values and
// allowed "class" attribute values.
// CKEditor only allows specific values for the "class" and "style"
@ -560,6 +587,9 @@ class Internal extends CKEditorPluginBase implements ContainerFactoryPluginInter
}
}
ksort($allowed);
ksort($disallowed);
return array($allowed, $disallowed);
}
}

View file

@ -87,23 +87,23 @@ class CKEditorAdminTest extends WebTestBase {
// Button groups
array(
array(
'name' => t('Formatting'),
'name' => 'Formatting',
'items' => array('Bold', 'Italic',),
),
array(
'name' => t('Links'),
'name' => 'Links',
'items' => array('DrupalLink', 'DrupalUnlink',),
),
array(
'name' => t('Lists'),
'name' => 'Lists',
'items' => array('BulletedList', 'NumberedList',),
),
array(
'name' => t('Media'),
'name' => 'Media',
'items' => array('Blockquote', 'DrupalImage',),
),
array(
'name' => t('Tools'),
'name' => 'Tools',
'items' => array('Source',),
),
),
@ -111,7 +111,7 @@ class CKEditorAdminTest extends WebTestBase {
),
'plugins' => array(),
);
$this->assertIdentical($ckeditor->getDefaultSettings(), $expected_default_settings);
$this->assertIdentical($this->castSafeStrings($ckeditor->getDefaultSettings()), $expected_default_settings);
// Keep the "CKEditor" editor selected and click the "Configure" button.
$this->drupalPostAjaxForm(NULL, $edit, 'editor_configure');
@ -279,7 +279,7 @@ class CKEditorAdminTest extends WebTestBase {
$expected_settings['plugins']['stylescombo']['styles'] = '';
$editor = entity_load('editor', 'amazing_format');
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists now.');
$this->assertIdentical($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
$this->assertIdentical($this->castSafeStrings($expected_settings), $this->castSafeStrings($editor->getSettings()), 'The Editor config entity has the correct settings.');
}
}

View file

@ -107,12 +107,12 @@ class CKEditorLoadingTest extends WebTestBase {
$expected = array('formats' => array('filtered_html' => array(
'format' => 'filtered_html',
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSettings' => $this->castSafeStrings($ckeditor_plugin->getJSSettings($editor)),
'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->assertIdentical($expected, $this->castSafeStrings($settings['editor']), "Text Editor module's JavaScript settings on the page are correct.");
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
$this->assertTrue(count($body) === 1, 'A body field exists.');
$this->assertTrue(count($format_selector) === 1, 'A single text format selector exists on the page.');
@ -138,12 +138,12 @@ class CKEditorLoadingTest extends WebTestBase {
'filtered_html' => array(
'format' => 'filtered_html',
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSettings' => $this->castSafeStrings($ckeditor_plugin->getJSSettings($editor)),
'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->assertIdentical($expected, $this->castSafeStrings($settings['editor']), "Text Editor module's JavaScript settings on the page are correct.");
$this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
$this->assertTrue(in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])), 'CKEditor glue library is present.');
}

View file

@ -54,7 +54,7 @@ class CKEditorTest extends KernelTestBase {
'filter_html' => array(
'status' => 1,
'settings' => array(
'allowed_html' => '<h2> <h3> <h4> <h5> <h6> <p> <br> <strong> <a>',
'allowed_html' => '<h2 id> <h3> <h4> <h5> <h6> <p> <br> <strong> <a href hreflang>',
)
),
),
@ -94,8 +94,10 @@ class CKEditorTest extends KernelTestBase {
'drupallink' => file_create_url('core/modules/ckeditor/js/plugins/drupallink/plugin.js'),
),
);
$expected_config = $this->castSafeStrings($expected_config);
ksort($expected_config);
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for default configuration.');
ksort($expected_config['allowedContent']);
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for default configuration.');
// Customize the configuration: add button, have two contextually enabled
// buttons, and configure a CKEditor plugin setting.
@ -116,18 +118,19 @@ class CKEditorTest extends KernelTestBase {
$expected_config['drupalExternalPlugins']['llama_contextual_and_button'] = file_create_url('core/modules/ckeditor/tests/modules/js/llama_contextual_and_button.js');
$expected_config['contentsCss'][] = file_create_url('core/modules/ckeditor/tests/modules/ckeditor_test.css');
ksort($expected_config);
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
// Change the allowed HTML tags; the "allowedContent" and "format_tags"
// settings for CKEditor should automatically be updated as well.
$format = $editor->getFilterFormat();
$format->filters('filter_html')->settings['allowed_html'] .= '<pre> <h3>';
$format->filters('filter_html')->settings['allowed_html'] .= '<pre> <h1>';
$format->save();
$expected_config['allowedContent']['pre'] = array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE);
$expected_config['allowedContent']['h3'] = array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE);
$expected_config['format_tags'] = 'p;h2;h3;h4;h5;h6;pre';
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
$expected_config['allowedContent']['pre'] = array('attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE);
$expected_config['allowedContent']['h1'] = array('attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE);
$expected_config['format_tags'] = 'p;h1;h2;h3;h4;h5;h6;pre';
ksort($expected_config['allowedContent']);
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
// Disable the filter_html filter: allow *all *tags.
$format->setFilterConfig('filter_html', array('status' => 0));
@ -136,7 +139,7 @@ class CKEditorTest extends KernelTestBase {
$expected_config['allowedContent'] = TRUE;
$expected_config['disallowedContent'] = FALSE;
$expected_config['format_tags'] = 'p;h1;h2;h3;h4;h5;h6;pre';
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
// Enable the filter_test_restrict_tags_and_attributes filter.
$format->setFilterConfig('filter_test_restrict_tags_and_attributes', array(
@ -178,14 +181,17 @@ class CKEditorTest extends KernelTestBase {
),
'a' => array(
'attributes' => 'href,rel,class,target',
'styles' => FALSE,
'classes' => 'external',
),
'span' => array(
'attributes' => 'class,property,rel,style',
'styles' => 'font-size',
'classes' => FALSE,
),
'*' => array(
'attributes' => 'class,data-*',
'styles' => FALSE,
'classes' => 'is-a-hipster-llama,and-more',
),
'del' => array(
@ -205,7 +211,9 @@ class CKEditorTest extends KernelTestBase {
);
$expected_config['format_tags'] = 'p';
ksort($expected_config);
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
ksort($expected_config['allowedContent']);
ksort($expected_config['disallowedContent']);
$this->assertIdentical($expected_config, $this->castSafeStrings($this->ckeditor->getJSSettings($editor)), 'Generated JS settings are correct for customized configuration.');
}
/**
@ -216,7 +224,7 @@ class CKEditorTest extends KernelTestBase {
// Default toolbar.
$expected = $this->getDefaultToolbarConfig();
$this->assertIdentical($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for default toolbar.');
$this->assertIdentical($expected, $this->castSafeStrings($this->ckeditor->buildToolbarJSSetting($editor)), '"toolbar" configuration part of JS settings built correctly for default toolbar.');
// Customize the configuration.
$settings = $editor->getSettings();
@ -224,7 +232,7 @@ class CKEditorTest extends KernelTestBase {
$editor->setSettings($settings);
$editor->save();
$expected[0]['items'][] = 'Strike';
$this->assertIdentical($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for customized toolbar.');
$this->assertIdentical($expected, $this->castSafeStrings($this->ckeditor->buildToolbarJSSetting($editor)), '"toolbar" configuration part of JS settings built correctly for customized toolbar.');
// Enable the editor_test module, customize further.
$this->enableModules(array('ckeditor_test'));
@ -236,7 +244,7 @@ class CKEditorTest extends KernelTestBase {
$editor->save();
$expected[0]['name'] = 'JunkScience';
$expected[0]['items'][] = 'Llama';
$this->assertIdentical($expected, $this->ckeditor->buildToolbarJSSetting($editor), '"toolbar" configuration part of JS settings built correctly for customized toolbar with contrib module-provided CKEditor plugin.');
$this->assertIdentical($expected, $this->castSafeStrings($this->ckeditor->buildToolbarJSSetting($editor)), '"toolbar" configuration part of JS settings built correctly for customized toolbar with contrib module-provided CKEditor plugin.');
}
/**
@ -420,17 +428,18 @@ class CKEditorTest extends KernelTestBase {
}
protected function getDefaultAllowedContentConfig() {
return array(
'h2' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'h3' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'h4' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'h5' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'h6' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'p' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'br' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'strong' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
'a' => array('attributes' => TRUE, 'styles' => FALSE, 'classes' => TRUE),
);
return [
'h2' => ['attributes' => 'id', 'styles' => FALSE, 'classes' => FALSE],
'h3' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'h4' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'h5' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'h6' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'p' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'br' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'strong' => ['attributes' => FALSE, 'styles' => FALSE, 'classes' => FALSE],
'a' => ['attributes' => 'href,hreflang', 'styles' => FALSE, 'classes' => FALSE],
'*' => ['attributes' => 'lang,dir', 'styles' => FALSE, 'classes' => FALSE],
];
}
protected function getDefaultDisallowedContentConfig() {
@ -442,23 +451,23 @@ class CKEditorTest extends KernelTestBase {
protected function getDefaultToolbarConfig() {
return array(
array(
'name' => t('Formatting'),
'name' => 'Formatting',
'items' => array('Bold', 'Italic',),
),
array(
'name' => t('Links'),
'name' => 'Links',
'items' => array('DrupalLink', 'DrupalUnlink',),
),
array(
'name' => t('Lists'),
'name' => 'Lists',
'items' => array('BulletedList', 'NumberedList',),
),
array(
'name' => t('Media'),
'name' => 'Media',
'items' => array('Blockquote', 'DrupalImage',),
),
array(
'name' => t('Tools'),
'name' => 'Tools',
'items' => array('Source',),
),
'/',

View file

@ -0,0 +1,82 @@
<?php
/**
* @file
* Contains \Drupal\ckeditor\Tests\CKEditorToolbarButtonTest.
*/
namespace Drupal\ckeditor\Tests;
use Drupal\filter\Entity\FilterFormat;
use Drupal\editor\Entity\Editor;
use Drupal\simpletest\WebTestBase;
use Drupal\Component\Serialization\Json;
/**
* Tests CKEditor toolbar buttons when the language direction is RTL.
*
* @group ckeditor
*/
class CKEditorToolbarButtonTest extends WebTestBase {
/**
* Modules to enable for this test.
*
* @var array
*/
public static $modules = ['filter', 'editor', 'ckeditor', 'locale'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a text format and associate this with CKEditor.
FilterFormat::create([
'format' => 'full_html',
'name' => 'Full HTML',
'weight' => 1,
'filters' => [],
])->save();
Editor::create([
'format' => 'full_html',
'editor' => 'ckeditor',
])->save();
// Create a new user with admin rights.
$this->admin_user = $this->drupalCreateUser([
'administer languages',
'access administration pages',
'administer site configuration',
'administer filters',
]);
}
/**
* Method tests CKEditor image buttons.
*/
public function testImageButtonDisplay() {
global $base_url;
$this->drupalLogin($this->admin_user);
// Install the Arabic language (which is RTL) and configure as the default.
$edit = [];
$edit['predefined_langcode'] = 'ar';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$edit = ['site_default_language' => 'ar'];
$this->drupalPostForm('admin/config/regional/language', $edit, t('Save configuration'));
// Once the default language is changed, go to the tested text format
// configuration page.
$this->drupalGet('admin/config/content/formats/manage/full_html');
// Check if any image button is loaded in CKEditor json.
$json_encode = function($html) {
return trim(Json::encode($html), '"');
};
$markup = $json_encode($base_url . '/core/modules/ckeditor/js/plugins/drupalimage/image.png');
$this->assertRaw($markup);
}
}

View file

@ -26,7 +26,7 @@
{# Available buttons. #}
<div class="ckeditor-toolbar-available">
<label for="ckeditor-available-buttons">{{ 'Available buttons'|t }}</label>
<ul id="ckeditor-available-buttons" class="ckeditor-buttons" role="form" data-drupal-ckeditor-button-sorting="source">
<ul id="ckeditor-available-buttons" class="ckeditor-buttons clearfix" role="form" data-drupal-ckeditor-button-sorting="source">
{% for disabled_button in disabled_buttons %}
<li{{ disabled_button.attributes.addClass('ckeditor-button') }}>{{ disabled_button.value }}</li>
{% endfor %}