Commit 81d60495 authored by Dries's avatar Dries

Issue #1890502 by Wim Leers, quicksketch, jessebeach, nod_, sun, effulgentsia:...

Issue #1890502 by Wim Leers, quicksketch, jessebeach, nod_, sun, effulgentsia: Added CKEditor module to core.
parent c07014d6
<?php
/**
* @file
* Callbacks and theming for the CKEditor toolbar configuration UI.
*/
use Drupal\Core\Template\Attribute;
/**
* Preprocess variables for theme_ckeditor_settings_toolbar().
*/
function template_preprocess_ckeditor_settings_toolbar(&$variables) {
// Simplify the language direction information for toolbar buttons.
$language_interface = language(LANGUAGE_TYPE_INTERFACE);
$variables['language_direction'] = $language_interface->direction ? 'rtl' : 'ltr';
// Create lists of active and disabled buttons.
$editor = $variables['editor'];
$plugins = $variables['plugins'];
$buttons = array();
$variables['multiple_buttons'] = array();
foreach ($plugins as $plugin => $plugin_buttons) {
foreach ($plugin_buttons as $button_name => $button) {
$button['name'] = $button_name;
if (!empty($button['multiple'])) {
$variables['multiple_buttons'][$button_name] = $button;
}
$buttons[$button_name] = $button;
}
}
$variables['active_buttons'] = array();
foreach ($editor->settings['toolbar']['buttons'] as $row_number => $row) {
foreach ($row as $button_name) {
if (isset($buttons[$button_name])) {
$variables['active_buttons'][$row_number][] = $buttons[$button_name];
if (empty($buttons[$button_name]['multiple'])) {
unset($buttons[$button_name]);
}
}
}
}
$variables['disabled_buttons'] = array_diff_key($buttons, $variables['multiple_buttons']);
}
/**
* Displays the toolbar configuration for CKEditor.
*/
function theme_ckeditor_settings_toolbar($variables) {
$editor = $variables['editor'];
$plugins = $variables['plugins'];
$rtl = $variables['language_direction'] === 'rtl' ? '_rtl' : '';
$build_button_item = function($button, $rtl) {
// Value of the button item.
if (isset($button['image_alternative'])) {
$value = $button['image_alternative' . $rtl];
}
elseif (isset($button['image'])) {
$value = theme('image', array('uri' => $button['image' . $rtl], 'title' => $button['label']));
}
else {
$value = '?';
}
// Set additional attribute on the button if it can occur multiple times.
if (!empty($button['multiple'])) {
$button['attributes']['class'][] = 'ckeditor-multiple-button';
}
// Build the button item.
$button_item = array(
'value' => $value,
'data-button-name' => $button['name'],
);
if (!empty($button['attributes'])) {
$button_item = array_merge($button_item, $button['attributes']);
}
return $button_item;
};
// Assemble items to be added to active button rows.
$active_buttons = array();
foreach ($variables['active_buttons'] as $row_number => $row_buttons) {
foreach ($row_buttons as $button) {
$active_buttons[$row_number][] = $build_button_item($button, $rtl);
}
}
// Assemble list of disabled buttons (which are always a single row).
$disabled_buttons = array();
foreach ($variables['disabled_buttons'] as $button) {
$disabled_buttons[] = $build_button_item($button, $rtl);
}
// Assemble list of multiple buttons that may be added multiple times.
$multiple_buttons = array();
foreach ($variables['multiple_buttons'] as $button_name => $button) {
$multiple_buttons[] = $build_button_item($button, $rtl);
}
$print_buttons = function($buttons) {
$output = '';
foreach ($buttons as $button) {
$value = $button['value'];
unset($button['value']);
$attributes = (string) new Attribute($button);
$output .= '<li' . $attributes . '>' . $value . '</li>';
}
return $output;
};
// We don't use theme_item_list() below in case there are no buttons in the
// active or disabled list, as theme_item_list() will not print an empty UL.
$output = '';
$output .= '<fieldset role="form" aria-labelledby="ckeditor-button-configuration ckeditor-button-description">';
$output .= '<legend id="ckeditor-button-configuration">' . t('Toolbar configuration') . '</legend>';
$output .= '<div class="fieldset-wrapper">';
// aria-live region for outputing aural information about the state of the
// configuration.
$output .= '<div id="ckeditor-button-configuration-aria-live" class="element-invisible" aria-live="polite"></div>';
$output .= '<div id="ckeditor-button-description" class="fieldset-description">' . t('Move a button into the <em>Active toolbar</em> to enable it, or into the list of <em>Available buttons</em> to disable it. Use dividers to create button groups. Buttons may be moved with the mouse or keyboard arrow keys.') . '</div>';
$output .= '<div class="ckeditor-toolbar-disabled clearfix">';
$output .= '<div class="ckeditor-toolbar-dividers">';
$output .= '<label id="ckeditor-multiple-label">' . t('Dividers') . '</label>';
$output .= '<ul class="ckeditor-multiple-buttons" role="form" aria-labelledby="ckeditor-multiple-label">';
$output .= $print_buttons($multiple_buttons);
$output .= '</ul>';
$output .= '</div>';
$output .= '<label id="ckeditor-available-buttons">' . t('Available buttons') . '</label>';
$output .= '<ul class="ckeditor-buttons" role="form" aria-labelledby="ckeditor-available-buttons">';
$output .= $print_buttons($disabled_buttons);
$output .= '</ul>';
$output .= '</div>';
$output .= '<label id="ckeditor-active-toolbar">' . t('Active toolbar') . '</label>';
$output .= '<div data-toolbar="active" class="ckeditor-toolbar-active clearfix">';
foreach ($active_buttons as $button_row) {
$output .= '<ul class="ckeditor-buttons" role="form" aria-labelledby="ckeditor-active-toolbar">';
$output .= $print_buttons($button_row);
$output .= '</ul>';
}
if (empty($active_buttons)) {
$output .= '<ul class="ckeditor-buttons">';
$output .= '</ul>';
}
$output .= '<div class="ckeditor-row-controls">';
$output .= '<a href="#" role="button" aria-label="' . t('Remove last button row') . '" class="ckeditor-row-remove" title="' . t('Remove row') . '">-</a>';
$output .= '<a href="#" role="button" aria-label="' . t('Add additional button row') . '" class="ckeditor-row-add" title="' . t('Add row') . '">+</a>';
$output .= '</div>';
$output .= '</div>';
$output .= '</div>';
$output .= '</fieldset>';
return $output;
}
<?php
/**
* @file
* Documentation for CKEditor module APIs.
*/
use Drupal\editor\Plugin\Core\Entity\Editor;
/**
* @addtogroup hooks
* @{
*/
/**
* Modify the list of available CKEditor plugins.
*
* This hook may be used to modify plugin properties after they have been
* specified by other modules.
*
* @param $plugins
* An array of all the existing plugin definitions, passed by reference.
*
* @see CKEditorPluginManager
*/
function hook_ckeditor_plugin_info_alter(array &$plugins) {
$plugins['someplugin']['label'] = t('Better name');
}
/**
* Modify the list of CSS files that will be added to a CKEditor instance.
*
* Modules may use this hook to provide their own custom CSS file without
* providing a CKEditor plugin. This list of CSS files is only used in the
* iframe versions of CKEditor.
*
* Front-end themes (and base themes) can easily specify CSS files to be used in
* iframe instances of CKEditor through an entry in their .info file:
*
* @code
* ckeditor_stylesheets[] = css/ckeditor-iframe.css
* @endcode
*
* @param array &$css
* An array of CSS files, passed by reference. This is a flat list of file
* paths relative to the Drupal root.
* @param $editor
* The text editor object as returned by editor_load(), for which these files
* are being loaded. Based on this information, it is possible to load the
* corresponding text format object as returned by filter_format_load().
*
* @see _ckeditor_theme_css()
*/
function hook_ckeditor_css_alter(array &$css, Editor $editor) {
$css[] = drupal_get_path('module', 'mymodule') . '/css/mymodule-ckeditor.css';
}
/**
* @} End of "addtogroup hooks".
*/
name = CKEditor
description = WYSIWYG editing for rich text fields using CKEditor.
package = Core
core = 8.x
version = VERSION
dependencies[] = editor
<?php
/**
* @file
* Provides integration with the CKEditor WYSIWYG editor.
*/
/**
* Implements hook_library_info().
*/
function ckeditor_library_info() {
$module_path = drupal_get_path('module', 'ckeditor');
$settings = array(
'ckeditor' => array(
'modulePath' => drupal_get_path('module', 'ckeditor'),
),
);
$libraries['drupal.ckeditor'] = array(
'title' => 'Drupal behavior to enable CKEditor on textareas.',
'version' => VERSION,
'js' => array(
$module_path . '/js/ckeditor.js' => array(),
array('data' => $settings, 'type' => 'setting'),
),
'dependencies' => array(
array('system', 'drupal'),
array('ckeditor', 'ckeditor'),
array('editor', 'drupal.editor'),
),
);
$libraries['drupal.ckeditor.admin'] = array(
'title' => 'Drupal behavior for drag-and-drop CKEditor toolbar builder UI.',
'version' => VERSION,
'js' => array(
$module_path . '/js/ckeditor.admin.js' => array(),
),
'css' => array(
$module_path . '/css/ckeditor.admin.css' => array(),
'core/misc/ckeditor/skins/moono/editor.css' => array(),
),
'dependencies' => array(
array('system', 'jquery'),
array('system', 'drupal'),
array('system', 'drupalSettings'),
array('system', 'jquery.once'),
array('system', 'jquery.ui.sortable'),
array('system', 'jquery.ui.draggable'),
),
);
$libraries['drupal.ckeditor.stylescombo.admin'] = array(
'title' => 'Only show the "stylescombo" plugin settings when its button is enabled.',
'version' => VERSION,
'js' => array(
$module_path . '/js/ckeditor.stylescombo.admin.js' => array(),
),
'dependencies' => array(
array('system', 'jquery'),
array('system', 'drupal'),
array('system', 'jquery.once'),
array('system', 'drupal.vertical-tabs'),
),
);
$libraries['ckeditor'] = array(
'title' => 'Loads the main CKEditor library.',
'version' => '4.0.1',
'js' => array(
'core/misc/ckeditor/ckeditor.js' => array(),
),
);
return $libraries;
}
/**
* Implements hook_theme().
*/
function ckeditor_theme() {
return array(
'ckeditor_settings_toolbar' => array(
'file' => 'ckeditor.admin.inc',
'variables' => array('editor' => NULL, 'plugins' => NULL),
),
);
}
/**
* Retrieves the default theme's CKEditor stylesheets defined in the .info file.
*
* Themes may specify iframe-specific CSS files for use with CKEditor by
* including a "ckeditor_stylesheets" key in the theme .info file.
*
* @code
* ckeditor_stylesheets[] = css/ckeditor-iframe.css
* @endcode
*/
function _ckeditor_theme_css($theme = NULL) {
$css = array();
if (!isset($theme)) {
$theme = variable_get('theme_default');
}
if ($theme_path = drupal_get_path('theme', $theme)) {
$info = system_get_info('theme', $theme);
if (isset($info['ckeditor_stylesheets'])) {
$css = $info['ckeditor_stylesheets'];
foreach ($css as $key => $path) {
$css[$key] = $theme_path . '/' . $path;
}
}
if (isset($info['base theme'])) {
$css = array_merge(_ckeditor_theme_css($info['base theme'], $css));
}
}
return $css;
}
/**
* CSS added to iframe-based instances only.
*/
body {
font-family: Arial, Verdana, sans-serif;
font-size: 12px;
color: #222;
background-color: #fff;
margin: 8px;
}
ol, ul, dl {
/* IE7: reset rtl list margin. (CKEditor issue #7334) */
*margin-right: 0px;
/* Preserved spaces for list items with text direction other than the list.
* (CKEditor issues #6249,#8049) */
padding: 0 40px;
}
/**
* @file
* Styles for configuration of CKEditor module for right-to-left languages.
*/
.ckeditor-toolbar-active > ul {
clear: right;
float: right;
}
ul.ckeditor-buttons li {
float: right;
}
ul.ckeditor-buttons li:first-child a {
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
ul.ckeditor-buttons li:last-child a {
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
ul.ckeditor-multiple-buttons {
float: right;
}
ul.ckeditor-multiple-buttons li {
float: right;
}
.ckeditor-row-controls {
float: left;
text-align: left;
}
/**
* @file
* Styles for configuration of CKEditor module.
*
* Many of these styles are adapted directly from the default CKEditor theme
* "moono".
*/
.ckeditor-toolbar-active {
border: 1px solid #b6b6b6;
padding: 6px 8px 2px;
box-shadow: 0 1px 0 white inset;
background: #cfd1cf;
background-image: -webkit-gradient(linear, left top, left bottom, from(whiteSmoke), to(#cfd1cf));
background-image: -moz-linear-gradient(top, whiteSmoke, #cfd1cf);
background-image: -o-linear-gradient(top, whiteSmoke, #cfd1cf);
background-image: -ms-linear-gradient(top, whiteSmoke, #cfd1cf);
background-image: linear-gradient(top, whiteSmoke, #cfd1cf);
filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr='#fff5f5f5', endColorstr='#ffcfd1cf');
margin: 5px 0;
overflow: nowrap;
}
.ckeditor-toolbar-active > ul {
clear: left; /* LTR */
float: left; /* LTR */
}
#ckeditor-button-description {
margin-bottom: 1em;
}
.ckeditor-toolbar-dividers {
float: right;
}
.ckeditor-toolbar-disabled ul.ckeditor-buttons {
border: 0;
}
.ckeditor-toolbar-disabled ul.ckeditor-buttons li {
margin: 2px;
}
.ckeditor-toolbar-disabled ul.ckeditor-buttons li a,
ul.ckeditor-buttons {
border: 1px solid #a6a6a6;
border-bottom-color: #979797;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 0 2px rgba(255, 255, 255, .15) inset, 0 1px 0 rgba(255, 255, 255, .15) inset;
}
ul.ckeditor-buttons {
min-height: 26px;
min-width: 26px;
list-style: none;
padding: 0;
margin: 0 6px 5px 0;
border: 1px solid #a6a6a6;
border-bottom-color: #979797;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 0 2px rgba(255, 255, 255, .15) inset, 0 1px 0 rgba(255, 255, 255, .15) inset;
}
ul.ckeditor-buttons li {
display: inline-block;
padding: 0;
margin: 0;
float: left; /* LTR */
}
ul.ckeditor-buttons li a {
position: relative;
display: block;
height: 18px;
padding: 4px 6px;
cursor: move;
border: 0;
white-space: nowrap;
text-decoration: none;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
color: #474747;
background: #e4e4e4;
background-image: -webkit-gradient(linear,left top,left bottom,from(white),to(#e4e4e4));
background-image: -moz-linear-gradient(top,white,#e4e4e4);
background-image: -webkit-linear-gradient(top,white,#e4e4e4);
background-image: -o-linear-gradient(top,white,#e4e4e4);
background-image: -ms-linear-gradient(top,white,#e4e4e4);
background-image: linear-gradient(top,white,#e4e4e4);
filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0,startColorstr='#ffffffff',endColorstr='#ffe4e4e4');
}
ul.ckeditor-buttons li .cke-icon-only {
text-indent: -9999px;
width: 16px;
}
ul.ckeditor-buttons li a:focus {
z-index: 11; /* Ensure focused buttons show their outline on all sides. */
}
ul.ckeditor-buttons li:first-child a {
border-top-left-radius: 2px; /* LTR */
border-bottom-left-radius: 2px; /* LTR */
}
ul.ckeditor-buttons li:last-child a {
border-top-right-radius: 2px; /* LTR */
border-bottom-right-radius: 2px; /* LTR */
}
ul.ckeditor-buttons li.ckeditor-button-placeholder a {
background: #333;
opacity: 0.3;
}
ul.ckeditor-multiple-buttons {
padding: 1px 2px;
margin: 5px;
list-style: none;
float: left; /* LTR */
}
ul.ckeditor-multiple-buttons li {
display: inline-block;
float: left; /* LTR */
margin: 0;
padding: 0;
}
ul.ckeditor-multiple-buttons li a {
cursor: move;
display: inline-block;
height: 18px;
margin: 0;
padding: 2px 0;
}
ul.ckeditor-buttons li.ckeditor-group-button-separator,
ul.ckeditor-multiple-buttons li.ckeditor-group-button-separator {
margin: -1px -3px -2px;
}
ul.ckeditor-buttons li.ckeditor-group-button-separator a,
ul.ckeditor-multiple-buttons li.ckeditor-group-button-separator a {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAdCAMAAABG4xbVAAAAhFBMVEUAAACmpqampqampqb////l5eX////5+fmmpqatra2urq6vr6+1tbW2tra4uLi6urq8vLzb29ve3t7i4uLl5eXn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz+/v7qIQO+AAAACHRSTlMATVmAi8XM29MuWToAAABjSURBVBiVrc5BCoAwDETRMKhtRBduev9LKm1xjItWRBBE6Nt9QkIwOTcUzk0Imi8aoMssxbgoTHMtqsFMLta0vPh2N49HyfdelPg6k9uvX/a+Bmggt1qJRNzQFVgjEnkUZDoBmH57VSypjg4AAAAASUVORK5CYII=) no-repeat center center;
width: 13px;
padding: 0;
height: 29px;
position: relative;
z-index: 10;
}
ul.ckeditor-buttons li.ckeditor-button-separator a {
background: #e4e4e4;
background-image: -webkit-linear-gradient(#e4e4e4, #b4b4b4);
background-image: linear-gradient(#e4e4e4, #b4b4b4);
height: 24px;
margin: 1px 0 0;
padding: 0;
position: relative;
width: 1px;
z-index: 10;
}
ul.ckeditor-multiple-buttons li.ckeditor-button-separator a {
width: 2px;
padding: 0;
height: 26px;
margin: 0 10px;
}
.ckeditor-separator {
background-color: silver;
background-color: rgba(0, 0, 0, .2);
margin: 5px 0;
height: 18px;
width: 1px;
display: block;
-webkit-box-shadow: 1px 0 1px rgba(255, 255, 255, .5);
-moz-box-shadow: 1px 0 1px rgba(255,255,255,.5);
box-shadow: 1px 0 1px rgba(255, 255, 255, .5)
}
.ckeditor-button-arrow {
width: 0;
text-align: center;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 3px solid #333;
display: inline-block;
margin: 0 4px 2px;
}
.ckeditor-row-controls {
float: right; /* LTR */
font-size: 18px;
width: 40px;
text-align: right; /* LTR */
}
.ckeditor-row-controls a {
display: inline-block;
padding: 6px 2px;
height: 16px;
width: 16px;
line-height: 0.9;
font-weight: bold;
color: #333;
text-decoration: none;
}
This diff is collapsed.
(function (Drupal, CKEDITOR) {
"use strict";
Drupal.editors.ckeditor = {
attach: function (element, format) {
var externalPlugins = format.editorSettings.externalPlugins;
// Register and load additional CKEditor plugins as necessary.
if (externalPlugins) {
for (var pluginName in externalPlugins) {
if (externalPlugins.hasOwnProperty(pluginName)) {
CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
}
}
delete format.editorSettings.drupalExternalPlugins;
}
return !!CKEDITOR.replace(element, format.editorSettings);
},
detach: function (element, format, trigger) {
var editor = CKEDITOR.dom.element.get(element).getEditor();
if (editor) {
if (trigger === 'serialize') {
editor.updateElement();
}
else {
editor.destroy();
}
}
return !!editor;
}
};
})(Drupal, CKEDITOR);
(function ($, Drupal) {
"use strict";
/**
* Shows the "stylescombo" plugin settings only when the button is enabled.
*/
Drupal.behaviors.ckeditorStylesComboSettingsVisibility = {
attach: function (context) {
var $stylesComboVerticalTab = $('#edit-editor-settings-plugins-stylescombo').data('verticalTab');
// Hide if the "Styles" button is disabled.
if ($('.ckeditor-toolbar-disabled li[data-button-name="Styles"]').length === 1) {
$stylesComboVerticalTab.tabHide();
}
// React to added/removed toolbar buttons.
$(context)
.find('.ckeditor-toolbar-active')
.on('CKEditorToolbarChanged', function (e, action, button) {
if (button === 'Styles') {
if (action === 'added') {
$stylesComboVerticalTab.tabShow();
}
else {
$stylesComboVerticalTab.tabHide();
}
}
});
}
};
/**
* Provides the summary for the "stylescombo" plugin settings vertical tab.
*/
Drupal.behaviors.ckeditorStylesComboSettingsSummary = {
attach: function () {
$('#edit-editor-settings-plugins-stylescombo').drupalSetSummary(function (context) {
var styles = $.trim($('#edit-editor-settings-plugins-stylescombo-styles').val());
if (styles.length === 0) {
return Drupal.t('No styles configured');
}
else {
var count = $.trim(styles).split("\n").length;
return Drupal.t('@count styles configured', { '@count': count});
}
});
}
};
})(jQuery, Drupal);
<?php
/**
* @file
* Contains \Drupal\ckeditor\Plugin\CKEditorPluginBase.
*/
namespace Drupal\ckeditor;
use Drupal\Component\Plugin\PluginBase;
use Drupal\editor\Plugin\Core\Entity\Editor;
/**
* Defines a base CKEditor plugin implementation.
*
* No other CKEditor plugins can be internal, unless a different CKEditor build
* than the one provided by Drupal core is used. Most CKEditor plugins don't
* need to provide additional settings forms.
*
* This base assumes that your plugin has buttons that you want to be enabled
* through the toolbar builder UI. It is still possible to also implement the
* CKEditorPluginContextualInterface (for contextual enabling) and
*