Commit 6740e953 authored by webchick's avatar webchick

Issue #2166399 by Wim Leers, nod_, clemens.tolboom: Changing CKEditor...

Issue #2166399 by Wim Leers, nod_, clemens.tolboom: Changing CKEditor configurations by changing text formats causes markup to be lost: warn the user when performing this advanced action.
parent 3b5efd7f
......@@ -23,6 +23,7 @@
* @Editor(
* id = "ckeditor",
* label = @Translation("CKEditor"),
* supports_content_filtering = TRUE,
* supports_inline_editing = TRUE
* )
*/
......
......@@ -103,6 +103,7 @@ function testLoading() {
'format' => 'filtered_html',
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => TRUE,
)));
$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.");
......@@ -129,6 +130,7 @@ function testLoading() {
'format' => 'filtered_html',
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => TRUE,
)));
$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.");
......
......@@ -93,6 +93,7 @@ function editor_library_info() {
array('system', 'drupal'),
array('system', 'drupalSettings'),
array('system', 'jquery.once'),
array('system', 'drupal.dialog'),
),
);
......
......@@ -3,10 +3,117 @@
* Attaches behavior for the Editor module.
*/
(function ($, Drupal) {
(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 DOM
* The text area DOM element.
*/
function findFieldForFormatSelector ($formatSelector) {
var field_id = $formatSelector.attr('data-editor-for');
return $('#' + field_id).get(0);
}
/**
* Changes the text editor on the text area for the given text format selector.
*
* @param jQuery $formatSelector
* A text format selector DOM element.
* @param String activeFormatID
* The currently active text format; its associated text editor will be
* detached.
* @param String newFormatID
* The text format we're changing to; its associated text editor will be
* attached.
*/
function changeTextEditor ($formatSelector, activeFormatID, newFormatID) {
var field = findFieldForFormatSelector($formatSelector);
// Detach the current editor (if any) and attach a new editor.
if (drupalSettings.editor.formats[activeFormatID]) {
Drupal.editorDetach(field, drupalSettings.editor.formats[activeFormatID]);
}
activeFormatID = newFormatID;
if (drupalSettings.editor.formats[activeFormatID]) {
Drupal.editorAttach(field, drupalSettings.editor.formats[activeFormatID]);
}
$formatSelector.attr('data-editor-active-text-format', newFormatID);
}
/**
* Handles changes in text format.
*
* @param jQuery.Event event
*/
function onTextFormatChange (event) {
var $select = $(event.target);
var activeFormatID = $select.attr('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.
if (drupalSettings.editor.formats[newFormatID] && drupalSettings.editor.formats[newFormatID].editorSupportsContentFiltering) {
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.', {
'%text_format': $select.find('option:selected').text()
});
var 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: function () {
changeTextEditor($select, activeFormatID, 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();
}
}
],
// Prevent this modal from being closed without the user making a choice
// as per http://stackoverflow.com/a/5438771.
closeOnEscape: false,
create: function () {
$(this).parent().find('.ui-dialog-titlebar-close').remove();
},
beforeClose: false,
close: function (event) {
// Automatically destroy the DOM element that was used for the dialog.
$(event.target).remove();
}
});
confirmationDialog.showModal();
}
else {
changeTextEditor($select, activeFormatID, newFormatID);
}
}
/**
* Initialize an empty object for editors to place their attachment code.
*/
......@@ -22,12 +129,11 @@ Drupal.behaviors.editor = {
return;
}
var $context = $(context);
var behavior = this;
$context.find('.editor').once('editor', function () {
$(context).find('.editor').once('editor', function () {
var $this = $(this);
var activeFormatID = $this.val();
var field = behavior.findFieldForFormatSelector($this);
$this.attr('data-editor-active-text-format', activeFormatID);
var field = findFieldForFormatSelector($this);
// Directly attach this editor, if the text format is enabled.
if (settings.editor.formats[activeFormatID]) {
......@@ -36,23 +142,7 @@ Drupal.behaviors.editor = {
// Attach onChange handler to text format selector element.
if ($this.is('select')) {
$this.on('change.editorAttach', function () {
var newFormatID = $this.val();
// Prevent double-attaching if the change event is triggered manually.
if (newFormatID === activeFormatID) {
return;
}
// Detach the current editor (if any) and attach a new editor.
if (settings.editor.formats[activeFormatID]) {
Drupal.editorDetach(field, settings.editor.formats[activeFormatID]);
}
activeFormatID = newFormatID;
if (settings.editor.formats[activeFormatID]) {
Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
}
});
$this.on('change.editorAttach', onTextFormatChange);
}
// Detach any editor when the containing form is submitted.
$this.parents('form').on('submit', function (event) {
......@@ -81,20 +171,14 @@ Drupal.behaviors.editor = {
editors = $(context).find('.editor').removeOnce('editor');
}
var behavior = this;
editors.each(function () {
var $this = $(this);
var activeFormatID = $this.val();
var field = behavior.findFieldForFormatSelector($this);
var field = findFieldForFormatSelector($this);
if (activeFormatID in settings.editor.formats) {
Drupal.editorDetach(field, settings.editor.formats[activeFormatID], trigger);
}
});
},
findFieldForFormatSelector: function ($formatSelector) {
var field_id = $formatSelector.attr('data-editor-for');
return $('#' + field_id).get(0);
}
};
......@@ -133,4 +217,4 @@ Drupal.editorDetach = function (field, format, trigger) {
}
};
})(jQuery, Drupal);
})(jQuery, Drupal, drupalSettings);
......@@ -32,6 +32,13 @@ class Editor extends Plugin {
*/
public $label;
/**
* Whether the editor supports "allowed content only" filtering.
*
* @var boolean
*/
public $supports_content_filtering;
/**
* Whether the editor supports the inline editing provided by the Edit module.
*
......
......@@ -24,6 +24,8 @@
* - id: The unique, system-wide identifier of the text editor. Typically named
* the same as the editor library.
* - label: The human-readable name of the text editor, translated.
* - supports_content_filtering: Whether the editor supports "allowed content
* only" filtering.
* - supports_inline_editing: Whether the editor supports the inline editing
* provided by the Edit module.
*
......@@ -33,6 +35,7 @@
* @Editor(
* id = "myeditor",
* label = @Translation("My Editor"),
* supports_content_filtering = FALSE,
* supports_inline_editing = FALSE
* )
* @endcode
......
......@@ -72,6 +72,7 @@ public function getAttachments(array $format_ids) {
}
$plugin = $this->createInstance($editor->editor);
$plugin_definition = $plugin->getPluginDefinition();
// Libraries.
$attachments['library'] = array_merge($attachments['library'], $plugin->getLibraries($editor));
......@@ -81,6 +82,7 @@ public function getAttachments(array $format_ids) {
'format' => $format_id,
'editor' => $editor->editor,
'editorSettings' => $plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => $plugin_definition['supports_content_filtering'],
);
}
......
......@@ -96,6 +96,7 @@ public function testLoading() {
'format' => 'full_html',
'editor' => 'unicorn',
'editorSettings' => array('ponyModeEnabled' => TRUE),
'editorSupportsContentFiltering' => TRUE,
)));
$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.");
......@@ -123,6 +124,7 @@ public function testLoading() {
'format' => 'plain_text',
'editor' => 'unicorn',
'editorSettings' => array('ponyModeEnabled' => TRUE),
'editorSupportsContentFiltering' => TRUE,
)));
$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.");
......
......@@ -104,6 +104,7 @@ public function testManager() {
'format' => 'full_html',
'editor' => 'unicorn',
'editorSettings' => $unicorn_plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => TRUE,
)
)))
)
......
......@@ -16,6 +16,7 @@
* @Editor(
* id = "unicorn",
* label = @Translation("Unicorn Editor"),
* supports_content_filtering = TRUE,
* supports_inline_editing = TRUE
* )
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment