Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\editor;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\editor\Entity\Editor;
|
Chris@0
|
6 use Drupal\filter\Entity\FilterFormat;
|
Chris@0
|
7 use Drupal\Component\Plugin\PluginManagerInterface;
|
Chris@0
|
8 use Drupal\Core\Render\BubbleableMetadata;
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * Defines a service for Text Editor's render elements.
|
Chris@0
|
12 */
|
Chris@0
|
13 class Element {
|
Chris@0
|
14
|
Chris@0
|
15 /**
|
Chris@0
|
16 * The Text Editor plugin manager service.
|
Chris@0
|
17 *
|
Chris@0
|
18 * @var \Drupal\Component\Plugin\PluginManagerInterface
|
Chris@0
|
19 */
|
Chris@0
|
20 protected $pluginManager;
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * Constructs a new Element object.
|
Chris@0
|
24 *
|
Chris@0
|
25 * @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
|
Chris@0
|
26 * The Text Editor plugin manager service.
|
Chris@0
|
27 */
|
Chris@0
|
28 public function __construct(PluginManagerInterface $plugin_manager) {
|
Chris@0
|
29 $this->pluginManager = $plugin_manager;
|
Chris@0
|
30 }
|
Chris@0
|
31
|
Chris@0
|
32 /**
|
Chris@0
|
33 * Additional #pre_render callback for 'text_format' elements.
|
Chris@0
|
34 */
|
Chris@0
|
35 public function preRenderTextFormat(array $element) {
|
Chris@0
|
36 // Allow modules to programmatically enforce no client-side editor by
|
Chris@0
|
37 // setting the #editor property to FALSE.
|
Chris@0
|
38 if (isset($element['#editor']) && !$element['#editor']) {
|
Chris@0
|
39 return $element;
|
Chris@0
|
40 }
|
Chris@0
|
41
|
Chris@0
|
42 // filter_process_format() copies properties to the expanded 'value' child
|
Chris@0
|
43 // element, including the #pre_render property. Skip this text format
|
Chris@0
|
44 // widget, if it contains no 'format'.
|
Chris@0
|
45 if (!isset($element['format'])) {
|
Chris@0
|
46 return $element;
|
Chris@0
|
47 }
|
Chris@0
|
48 $format_ids = array_keys($element['format']['format']['#options']);
|
Chris@0
|
49
|
Chris@0
|
50 // Early-return if no text editor is associated with any of the text formats.
|
Chris@0
|
51 $editors = Editor::loadMultiple($format_ids);
|
Chris@0
|
52 foreach ($editors as $key => $editor) {
|
Chris@0
|
53 $definition = $this->pluginManager->getDefinition($editor->getEditor());
|
Chris@0
|
54 if (!in_array($element['#base_type'], $definition['supported_element_types'])) {
|
Chris@0
|
55 unset($editors[$key]);
|
Chris@0
|
56 }
|
Chris@0
|
57 }
|
Chris@0
|
58 if (count($editors) === 0) {
|
Chris@0
|
59 return $element;
|
Chris@0
|
60 }
|
Chris@0
|
61
|
Chris@0
|
62 // Use a hidden element for a single text format.
|
Chris@0
|
63 $field_id = $element['value']['#id'];
|
Chris@0
|
64 if (!$element['format']['format']['#access']) {
|
Chris@0
|
65 // Use the first (and only) available text format.
|
Chris@0
|
66 $format_id = $format_ids[0];
|
Chris@0
|
67 $element['format']['editor'] = [
|
Chris@0
|
68 '#type' => 'hidden',
|
Chris@0
|
69 '#name' => $element['format']['format']['#name'],
|
Chris@0
|
70 '#value' => $format_id,
|
Chris@0
|
71 '#attributes' => [
|
Chris@0
|
72 'data-editor-for' => $field_id,
|
Chris@0
|
73 ],
|
Chris@0
|
74 ];
|
Chris@0
|
75 }
|
Chris@0
|
76 // Otherwise, attach to text format selector.
|
Chris@0
|
77 else {
|
Chris@0
|
78 $element['format']['format']['#attributes']['class'][] = 'editor';
|
Chris@0
|
79 $element['format']['format']['#attributes']['data-editor-for'] = $field_id;
|
Chris@0
|
80 }
|
Chris@0
|
81
|
Chris@0
|
82 // Hide the text format's filters' guidelines of those text formats that have
|
Chris@0
|
83 // a text editor associated: they're rather useless when using a text editor.
|
Chris@0
|
84 foreach ($editors as $format_id => $editor) {
|
Chris@0
|
85 $element['format']['guidelines'][$format_id]['#access'] = FALSE;
|
Chris@0
|
86 }
|
Chris@0
|
87
|
Chris@0
|
88 // Attach Text Editor module's (this module) library.
|
Chris@0
|
89 $element['#attached']['library'][] = 'editor/drupal.editor';
|
Chris@0
|
90
|
Chris@0
|
91 // Attach attachments for all available editors.
|
Chris@0
|
92 $element['#attached'] = BubbleableMetadata::mergeAttachments($element['#attached'], $this->pluginManager->getAttachments($format_ids));
|
Chris@0
|
93
|
Chris@0
|
94 // Apply XSS filters when editing content if necessary. Some types of text
|
Chris@0
|
95 // editors cannot guarantee that the end user won't become a victim of XSS.
|
Chris@0
|
96 if (!empty($element['value']['#value'])) {
|
Chris@0
|
97 $original = $element['value']['#value'];
|
Chris@0
|
98 $format = FilterFormat::load($element['format']['format']['#value']);
|
Chris@0
|
99
|
Chris@0
|
100 // Ensure XSS-safety for the current text format/editor.
|
Chris@0
|
101 $filtered = editor_filter_xss($original, $format);
|
Chris@0
|
102 if ($filtered !== FALSE) {
|
Chris@0
|
103 $element['value']['#value'] = $filtered;
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 // Only when the user has access to multiple text formats, we must add data-
|
Chris@0
|
107 // attributes for the original value and change tracking, because they are
|
Chris@0
|
108 // only necessary when the end user can switch between text formats/editors.
|
Chris@0
|
109 if ($element['format']['format']['#access']) {
|
Chris@0
|
110 $element['value']['#attributes']['data-editor-value-is-changed'] = 'false';
|
Chris@0
|
111 $element['value']['#attributes']['data-editor-value-original'] = $original;
|
Chris@0
|
112 }
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 return $element;
|
Chris@0
|
116 }
|
Chris@0
|
117
|
Chris@0
|
118 }
|