Newer
Older
Angie Byron
committed
<?php
/**
* @file
* Contains \Drupal\filter\Element\TextFormat.
*/
namespace Drupal\filter\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\RenderElement;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
Angie Byron
committed
/**
* Provides a text format render element.
*
Jess
committed
* Properties:
* - #base_type: The form element #type to use for the 'value' element.
* 'textarea' by default.
* - #format: (optional) The text format ID to preselect. If omitted, the
* default format for the current user will be used.
* - #allowed_formats: (optional) An array of text format IDs that are available
* for this element. If omitted, all text formats that the current user has
* access to will be allowed.
*
* Usage Example:
* @code
* $form['body'] = array(
* '#type' => 'text_format',
* '#title' => 'Body',
* '#format' => 'full_html',
* '#default_value' => '<p>The quick brown fox jumped over the lazy dog.</p>',
* );
* @endcode
*
* @see \Drupal\Core\Render\Element\Textarea
*
* @RenderElement("text_format")
Angie Byron
committed
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
*/
class TextFormat extends RenderElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#process' => array(
array($class, 'processFormat'),
),
'#base_type' => 'textarea',
'#theme_wrappers' => array('text_format_wrapper'),
);
}
/**
* Expands an element into a base element with text format selector attached.
*
* The form element will be expanded into two separate form elements, one
* holding the original element, and the other holding the text format
* selector:
* - value: Holds the original element, having its #type changed to the value
* of #base_type or 'textarea' by default.
* - format: Holds the text format details and the text format selection,
* using the text format ID specified in #format or the user's default
* format by default, if NULL.
*
* The resulting value for the element will be an array holding the value and
* the format. For example, the value for the body element will be:
* @code
* $values = $form_state->getValue('body');
* $values['value'] = 'foo';
* $values['format'] = 'foo';
* @endcode
*
* @param array $element
Jess
committed
* The form element to process. See main class documentation for properties.
Angie Byron
committed
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The form element.
*/
public static function processFormat(&$element, FormStateInterface $form_state, &$complete_form) {
$user = static::currentUser();
// Ensure that children appear as subkeys of this element.
$element['#tree'] = TRUE;
$blacklist = array(
catch
committed
// Make \Drupal::formBuilder()->doBuildForm() regenerate child properties.
Angie Byron
committed
'#parents',
'#id',
'#name',
catch
committed
// Do not copy this #process function to prevent
// \Drupal::formBuilder()->doBuildForm() from recursing infinitely.
Angie Byron
committed
'#process',
Alex Pott
committed
// Ensure #pre_render functions will be run.
'#pre_render',
Angie Byron
committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Description is handled by theme_text_format_wrapper().
'#description',
// Ensure proper ordering of children.
'#weight',
// Properties already processed for the parent element.
'#prefix',
'#suffix',
'#attached',
'#processed',
'#theme_wrappers',
);
// Move this element into sub-element 'value'.
unset($element['value']);
foreach (Element::properties($element) as $key) {
if (!in_array($key, $blacklist)) {
$element['value'][$key] = $element[$key];
}
}
$element['value']['#type'] = $element['#base_type'];
$element['value'] += static::elementInfo()->getInfo($element['#base_type']);
// Make sure the #default_value key is set, so we can use it below.
$element['value'] += array('#default_value' => '');
// Turn original element into a text format wrapper.
$element['#attached']['library'][] = 'filter/drupal.filter';
// Setup child container for the text format widget.
$element['format'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('filter-wrapper')),
);
// Get a list of formats that the current user has access to.
$formats = filter_formats($user);
// Allow the list of formats to be restricted.
if (isset($element['#allowed_formats'])) {
// We do not add the fallback format here to allow the use-case of forcing
// certain text formats to be used for certain text areas. In case the
// fallback format is supposed to be allowed as well, it must be added to
// $element['#allowed_formats'] explicitly.
$formats = array_intersect_key($formats, array_flip($element['#allowed_formats']));
}
if (!isset($element['#format']) && !empty($formats)) {
// If no text format was selected, use the allowed format with the highest
// weight. This is equivalent to calling filter_default_format().
$element['#format'] = reset($formats)->id();
Angie Byron
committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
}
// If #allowed_formats is set, the list of formats must not be modified in
// any way. Otherwise, however, if all of the following conditions are true,
// remove the fallback format from the list of formats:
// 1. The 'always_show_fallback_choice' filter setting has not been activated.
// 2. Multiple text formats are available.
// 3. The fallback format is not the default format.
// The 'always_show_fallback_choice' filter setting is a hidden setting that
// has no UI. It defaults to FALSE.
$config = static::configFactory()->get('filter.settings');
if (!isset($element['#allowed_formats']) && !$config->get('always_show_fallback_choice')) {
$fallback_format = $config->get('fallback_format');
if ($element['#format'] !== $fallback_format && count($formats) > 1) {
unset($formats[$fallback_format]);
}
}
// Prepare text format guidelines.
$element['format']['guidelines'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('filter-guidelines')),
'#weight' => 20,
);
$options = array();
foreach ($formats as $format) {
$options[$format->id()] = $format->label();
$element['format']['guidelines'][$format->id()] = array(
'#theme' => 'filter_guidelines',
'#format' => $format,
);
}
$element['format']['format'] = array(
'#type' => 'select',
'#title' => t('Text format'),
'#options' => $options,
'#default_value' => $element['#format'],
'#access' => count($formats) > 1,
'#weight' => 10,
'#attributes' => array('class' => array('filter-list')),
'#parents' => array_merge($element['#parents'], array('format')),
);
$element['format']['help'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('filter-help')),
'#markup' => \Drupal::l(t('About text formats'), new Url('filter.tips_all', array(), array('attributes' => array('target' => '_blank')))),
Angie Byron
committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
'#weight' => 0,
);
$all_formats = filter_formats();
$format_exists = isset($all_formats[$element['#format']]);
$format_allowed = !isset($element['#allowed_formats']) || in_array($element['#format'], $element['#allowed_formats']);
$user_has_access = isset($formats[$element['#format']]);
$user_is_admin = $user->hasPermission('administer filters');
// If the stored format does not exist or if it is not among the allowed
// formats for this textarea, administrators have to assign a new format.
if ((!$format_exists || !$format_allowed) && $user_is_admin) {
$element['format']['format']['#required'] = TRUE;
$element['format']['format']['#default_value'] = NULL;
// Force access to the format selector (it may have been denied above if
// the user only has access to a single format).
$element['format']['format']['#access'] = TRUE;
}
// Disable this widget, if the user is not allowed to use the stored format,
// or if the stored format does not exist. The 'administer filters'
// permission only grants access to the filter administration, not to all
// formats.
elseif (!$user_has_access || !$format_exists) {
// Overload default values into #value to make them unalterable.
$element['value']['#value'] = $element['value']['#default_value'];
$element['format']['format']['#value'] = $element['format']['format']['#default_value'];
// Prepend #pre_render callback to replace field value with user notice
// prior to rendering.
$element['value'] += array('#pre_render' => array());
array_unshift($element['value']['#pre_render'], 'filter_form_access_denied');
// Cosmetic adjustments.
if (isset($element['value']['#rows'])) {
$element['value']['#rows'] = 3;
}
$element['value']['#disabled'] = TRUE;
$element['value']['#resizable'] = 'none';
// Hide the text format selector and any other child element (such as text
// field's summary).
foreach (Element::children($element) as $key) {
if ($key != 'value') {
$element[$key]['#access'] = FALSE;
}
}
}
return $element;
}
/**
* Wraps the current user.
*
* \Drupal\Core\Session\AccountInterface
*/
protected static function currentUser() {
return \Drupal::currentUser();
}
/**
* Wraps the config factory.
*
* @return \Drupal\Core\Config\ConfigFactoryInterface
*/
protected static function configFactory() {
return \Drupal::configFactory();
}
/**
* Wraps the element info service.
*
* @return \Drupal\Core\Render\ElementInfoManagerInterface
*/
protected static function elementInfo() {
return \Drupal::service('element_info');
}
}