ckeditor5-1.0.x-dev/src/Plugin/CKEditor5Plugin/Heading.php
src/Plugin/CKEditor5Plugin/Heading.php
<?php
declare(strict_types=1);
namespace Drupal\ckeditor5\Plugin\CKEditor5Plugin;
use Drupal\ckeditor5\Plugin\CKEditor5PluginDefault;
use Drupal\ckeditor5\Plugin\CKEditor5PluginConfigurableInterface;
use Drupal\ckeditor5\Plugin\CKEditor5PluginManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\editor\EditorInterface;
use Drupal\editor\Entity\Editor;
/**
* CKEditor5 Heading plugin.
*/
class Heading extends CKEditor5PluginDefault implements CKEditor5PluginConfigurableInterface {
/**
* The headings that should be enabled by default.
*
* @var string[]
*/
const DEFAULT_ENABLED_HEADINGS = [
'heading2',
'heading3',
'heading4',
'heading5',
'heading6',
];
/**
* The headings that cannot be disabled.
*
* @var string[]
*/
const ALWAYS_ENABLED_HEADINGS = [
'paragraph',
];
/**
* Computes all valid choices for the "enabled_headings" setting.
*
* @see ckeditor5.schema.yml
*
* @return string[]
* All valid choices.
*/
public static function validChoices() : array {
$cke5_plugin_manager = \Drupal::service('plugin.manager.ckeditor5.plugin');
assert($cke5_plugin_manager instanceof CKEditor5PluginManager);
$definition = $cke5_plugin_manager->getDefinition('ckeditor5heading');
assert($definition['class'] === static::class);
return array_diff(
array_column($definition['plugin_config']['heading']['options'], 'model'),
static::ALWAYS_ENABLED_HEADINGS
);
}
/**
* Gets all enabled headings.
*
* @param \Drupal\editor\EditorInterface $editor
* A configured text editor object.
*
* @return string[]
* The values in the plugins.ckeditor5heading.enabled_headings configuration
* plus the headings that are always enabled.
*/
private static function getEnabledHeadings(EditorInterface $editor) : array {
$settings = $editor->getSettings();
return array_merge(
self::ALWAYS_ENABLED_HEADINGS,
$settings['plugins']['ckeditor5heading']['enabled_headings']
);
}
/**
* {@inheritdoc}
*
* Form for choosing which heading tags are available.
*/
public function settingsForm(array $form, FormStateInterface $form_state, Editor $editor) {
$configured_or_default = isset($editor->getSettings()['plugins']['ckeditor5heading']['enabled_headings'])
? static::getEnabledHeadings($editor)
: self::DEFAULT_ENABLED_HEADINGS;
$form['enabled_headings'] = [
'#type' => 'fieldset',
'#title' => $this->t('Enabled Headings'),
'#description' => $this->t('These are the headings that will appear in the headings dropdown. If a heading is not chosen here, it does not necessarily mean the corresponding tag is disallowed in the text format.'),
];
foreach ($this->getPluginDefinition()['plugin_config']['heading']['options'] as $heading_option) {
$model = $heading_option['model'];
if (in_array($model, self::ALWAYS_ENABLED_HEADINGS, TRUE)) {
continue;
}
// It's safe to use $model as a key: listing the same model twice with
// different properties triggers a schema error in CKEditor 5.
// @see https://ckeditor.com/docs/ckeditor5/latest/framework/guides/support/error-codes.html#error-schema-cannot-register-item-twice
// @see https://ckeditor.com/docs/ckeditor5/latest/features/headings.html#configuring-custom-heading-elements
$form['enabled_headings'][$model] = self::generateCheckboxForHeadingOption($heading_option);
$form['enabled_headings'][$model]['#default_value'] = in_array($model, $configured_or_default, TRUE) ? $model : NULL;
}
$form['enabled_headings']['#element_validate'][] = [self::class, 'omitUncheckedCheckboxes'];
return $form;
}
/**
* #element_validate handler for the "enabled_headings" element in settingsForm().
*/
public static function omitUncheckedCheckboxes(array $element, FormStateInterface $form_state) {
$submitted_values = $form_state->getValue($element['#parents']);
// To comply with the config schema, omit unchecked checkboxes from the form
// values and drop the keys.
$rewritten = array_values(array_filter($submitted_values));
$form_state->setValue($element['#parents'], $rewritten);
}
/**
* Generates checkbox for a CKEditor 5 heading plugin config option.
*
* @param array $heading_option
* A heading option configuration as the CKEditor 5 Heading plugin expects
* in its configuration.
*
* @return array
* The checkbox render array.
*
* @see https://ckeditor.com/docs/ckeditor5/latest/api/module_heading_heading-HeadingConfig.html#member-options
*/
private static function generateCheckboxForHeadingOption(array $heading_option) : array {
// This requires the `title` and `model` properties. The `class` property is
// optional. The `view` property is not used.
assert(array_key_exists('title', $heading_option));
assert(array_key_exists('model', $heading_option));
$checkbox = [
'#type' => 'checkbox',
'#title' => $heading_option['title'],
'#return_value' => $heading_option['model'],
];
if (isset($heading_option['class'])) {
$checkbox['#label_attributes']['class'][] = $heading_option['class'];
$checkbox['#label_attributes']['class'][] = 'ck';
}
return $checkbox;
}
/**
* {@inheritdoc}
*
* Filters the header options to those chosen in editor config.
*/
public function getDynamicPluginConfig(array $static_plugin_config, Editor $editor) {
$enabled_headings = static::getEnabledHeadings($editor);
$all_heading_options = $static_plugin_config['heading']['options'];
$configured_heading_options = array_filter($all_heading_options, function ($option) use ($enabled_headings) {
return in_array($option['model'], $enabled_headings, TRUE);
});
return [
'heading' => [
'options' => array_values($configured_heading_options),
],
];
}
}
