Paragraphs Editor
ParagraphsEditorWidget.php
Go to the documentation of this file.
1 <?php
2 
4 
18 
19 /**
20  * Plugin implementation of the 'entity_reference_paragraphs_editor' widget.
21  *
22  * @FieldWidget(
23  * id = "entity_reference_paragraphs_editor",
24  * label = @Translation("Paragraphs (Editor)"),
25  * multiple_values = TRUE,
26  * description = @Translation("Editor paragraphs form widget."),
27  * field_types = {
28  * "entity_reference_revisions"
29  * }
30  * )
31  */
32 class ParagraphsEditorWidget extends InlineParagraphsWidget implements ContainerFactoryPluginInterface {
33 
34  /**
35  * The editor field value manager for wrapping items.
36  *
37  * @var \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface
38  */
39  protected $fieldValueManager;
40 
41  /**
42  * The dom processor for preparing and extracting editor content.
43  *
44  * @var \Drupal\dom_processor\DomProcessor\DomProcessorInterface
45  */
46  protected $domProcessor;
47 
48  /**
49  * The plugin manager for bundle selector plugins.
50  *
51  * @var \Drupal\Component\Plugin\PluginManagerInterface
52  */
54 
55  /**
56  * The plugin manager for delivery plugins.
57  *
58  * @var \Drupal\Component\Plugin\PluginManagerInterface
59  */
61 
62  /**
63  * The entity display repository service for getting view modes.
64  *
65  * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
66  */
68 
69  /**
70  * Creates a paragraphs editor field widget.
71  *
72  * @param string $plugin_id
73  * The field widget plugin id.
74  * @param mixed $plugin_definition
75  * The plugin definition.
76  * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
77  * The paragraphs editor field definition.
78  * @param array $settings
79  * The paragraphs editor field widget settings.
80  * @param array $third_party_settings
81  * The third party settings for the widget.
82  * @param \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface $field_value_manager
83  * The field value manager for getting and setting paragraphs editor field
84  * information.
85  * @param \Drupal\dom_processor\DomProcessor\DomProcessorInterface $dom_processor
86  * The DOM processor for reading and writing markup.
87  * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
88  * The view mode manager.
89  * @param array $plugin_managers
90  * The paragraphs editor plugin managers.
91  */
92  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, FieldValueManagerInterface $field_value_manager, DomProcessorInterface $dom_processor, EntityDisplayRepositoryInterface $entity_display_repository, array $plugin_managers) {
93  parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
94  $this->fieldValueManager = $field_value_manager;
95  $this->domProcessor = $dom_processor;
96  $this->entityDisplayRepository = $entity_display_repository;
97  $this->bundleSelectorManager = $plugin_managers['bundle_selector'];
98  $this->deliveryProviderManager = $plugin_managers['delivery_provider'];
99  }
100 
101  /**
102  * {@inheritdoc}
103  */
104  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
105  $plugin_managers = [];
106  foreach ($container->getParameter('paragraphs_editor.plugin_managers') as $name => $def) {
107  $plugin_managers[$name] = $container->get($def->id);
108  }
109  return new static(
110  $plugin_id,
111  $plugin_definition,
112  $configuration['field_definition'],
113  $configuration['settings'],
114  $configuration['third_party_settings'],
115  $container->get('paragraphs_editor.field_value.manager'),
116  $container->get('dom_processor.dom_processor'),
117  $container->get('entity_display.repository'),
118  $plugin_managers
119  );
120  }
121 
122  /**
123  * {@inheritdoc}
124  */
125  public static function defaultSettings() {
126  return [
127  'title' => t('Paragraph'),
128  'bundle_selector' => 'list',
129  'delivery_provider' => 'modal',
130  'view_mode' => 'default',
131  'prerender_count' => '10',
132  ];
133  }
134 
135  /**
136  * {@inheritdoc}
137  */
138  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
139  $editable_data = $this->process('load', $items, $form_state);
140 
141  // Pretty much all the important parts are generated by the DOM processor.
142  // Offloading things like '#attached' and '#attributes' to the processor
143  // allows it to do things like dynamically load javascript for rendered
144  // entities.
145  return [
146  'markup' => $element + [
147  '#type' => 'text_format',
148  '#format' => $editable_data->get('filter_format'),
149  '#default_value' => $editable_data->get('markup'),
150  '#rows' => 100,
151  '#attributes' => $editable_data->get('attributes'),
152  '#attached' => [
153  'library' => $editable_data->get('libraries'),
154  'drupalSettings' => $editable_data->get('drupalSettings'),
155  ],
156  '#allowed_formats' => [$editable_data->get('filter_format')],
157  ],
158  'context_id' => [
159  '#type' => 'hidden',
160  '#default_value' => $editable_data->get('context_id'),
161  ],
162  ];
163  }
164 
165  /**
166  * {@inheritdoc}
167  */
168  public function settingsForm(array $form, FormStateInterface $form_state) {
169  $elements = [];
170 
171  $elements['title'] = [
172  '#type' => 'textfield',
173  '#title' => $this->t('Paragraph Title'),
174  '#description' => $this->t('Label to appear as title on the button "Insert [title]. This label is translatable.'),
175  '#default_value' => $this->getSetting('title'),
176  '#required' => TRUE,
177  ];
178 
179  $options = [];
180  foreach ($this->bundleSelectorManager->getDefinitions() as $plugin) {
181  $options[$plugin['id']] = $plugin['title'];
182  }
183 
184  $elements['bundle_selector'] = [
185  '#type' => 'select',
186  '#title' => $this->t('Bundle Selection Handler'),
187  '#description' => $this->t('The bundle selector form plugin that will be used to allow users to insert paragraph items.'),
188  '#options' => $options,
189  '#default_value' => $this->getSetting('bundle_selector'),
190  '#required' => TRUE,
191  ];
192 
193  $options = [];
194  foreach ($this->deliveryProviderManager->getDefinitions() as $plugin) {
195  $options[$plugin['id']] = $plugin['title'];
196  }
197 
198  $elements['delivery_provider'] = [
199  '#type' => 'select',
200  '#title' => $this->t('Delivery Handler'),
201  '#description' => $this->t('The delivery plugin that controls the user experience for how forms are delivered.'),
202  '#options' => $options,
203  '#default_value' => $this->getSetting('delivery_provider'),
204  '#required' => TRUE,
205  ];
206 
207  $elements['view_mode'] = [
208  '#type' => 'select',
209  '#title' => 'Editor View Mode',
210  '#description' => $this->t('The view mode that will be used to render embedded entities.'),
211  '#options' => $this->entityDisplayRepository->getViewModeOptions('paragraph'),
212  '#default_value' => $this->getSetting('prerender_count'),
213  '#required' => TRUE,
214  ];
215 
216  $options = [0 => $this->t('None')];
217  for ($i = 5; $i <= 50; $i += 5) {
218  $options[$i] = $i;
219  }
220  $options[-1] = $this->t('All');
221  $elements['prerender_count'] = [
222  '#type' => 'select',
223  '#title' => 'Maximum Pre-Render Items',
224  '#description' => $this->t("The maximum number of embedded paragraphs to render before an editor is initialized. Additional entities will be rendered via ajax on demand, and won't be available to edit until their respective ajax calls finish."),
225  '#options' => $options,
226  '#default_value' => $this->getSetting('prerender_count'),
227  '#required' => TRUE,
228  ];
229 
230  return $elements;
231  }
232 
233  /**
234  * {@inheritdoc}
235  */
236  public function settingsSummary() {
237  $bundle_selector = $this->bundleSelectorManager->getDefinition($this->getSetting('bundle_selector'));
238  $delivery_provider = $this->deliveryProviderManager->getDefinition($this->getSetting('delivery_provider'));
239  $prerender_count = $this->getSetting('prerender_count');
240  if ($prerender_count == '-1') {
241  $prerender_count = 'All';
242  }
243  elseif ($prerender_count == '0') {
244  $prerender_count = 'None';
245  }
246  $summary = [];
247  $summary[] = $this->t('Title: @title', ['@title' => $this->getSetting('title')]);
248  $summary[] = $this->t('Bundle Selector: @bundle_selector', ['@bundle_selector' => $bundle_selector['title']]);
249  $summary[] = $this->t('Delivery Provider: @delivery_provider', ['@delivery_provider' => $delivery_provider['title']]);
250  $summary[] = $this->t('View Mode: @mode', ['@mode' => $this->getSetting('view_mode')]);
251  $summary[] = $this->t('Maximum Pre-Render Items: @prerender_count', ['@prerender_count' => $prerender_count]);
252  return $summary;
253  }
254 
255  /**
256  * {@inheritdoc}
257  */
258  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
259  $field_name = $this->getFieldConfig()->getName();
260  $path = array_merge($form['#parents'], [$field_name]);
261  $values = NestedArray::getValue($form_state->getValues(), $path);
262  $this->process('update', $items, $form_state, $values['markup']['format'], $values['markup']['value'], $values['context_id']);
263  }
264 
265  /**
266  * {@inheritdoc}
267  */
268  public static function isApplicable(FieldDefinitionInterface $field_definition) {
269  return \Drupal::service('paragraphs_editor.field_value.manager')->isParagraphsEditorField($field_definition);
270  }
271 
272  /**
273  * {@inheritdoc}
274  */
275  protected function mergeDefaults() {
276  $this->settings += $this->getFieldConfig()->getThirdPartySettings('paragraphs_editor');
277  $this->settings += static::defaultSettings();
278  $this->defaultSettingsMerged = TRUE;
279  }
280 
281  /**
282  * Passes markup through the paragraphs_editor DOM processor.
283  *
284  * @param string $variant
285  * The DOM Processor plugin variant to run:
286  * - 'load' is used to take saved markup and make it editable.
287  * - 'update' is used to take editor markup and make it saveable.
288  * @param \Drupal\Core\Field\FieldItemListInterface $items
289  * The field items that will receive savable entities, or serve loadable
290  * entities. Note that neither of these operations perform entity saves.
291  * @param \Drupal\Core\Form\FormStateInterface $form_state
292  * The form state for the form that the field widget belongs to.
293  * @param string|null $format
294  * The default filter format name to apply to created text entities.
295  * @param string|null $markup
296  * The markup to be processed. Defaults to the markup inside the text
297  * entity.
298  * @param string|null $context_id
299  * The id of the root editing context to pull edits from.
300  *
301  * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorPreparer
302  * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorDecorator
303  * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorExtractor
304  *
305  * @return \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface
306  * See the ParagraphsEditorDecorator and ParagraphsEditorExtractor DOM
307  * Processor plugins for more information.
308  */
309  protected function process($variant, FieldItemListInterface $items, FormStateInterface $form_state, $format = NULL, $markup = NULL, $context_id = NULL) {
310  $field_value_wrapper = $this->fieldValueManager->wrapItems($items);
311 
312  if (!isset($markup)) {
313  $markup = $field_value_wrapper->getMarkup();
314  }
315 
316  if (!isset($format)) {
317  $format = $field_value_wrapper->getFormat();
318  }
319 
320  if (empty($format)) {
321  $format = $this->getFieldConfig()->getThirdPartySetting('paragraphs_editor', 'filter_format');
322  }
323 
324  // Ensure that we can get an entity to savethe updates to.
325  $form_object = $form_state->getFormObject();
326  if (!$form_object instanceof EntityFormInterface) {
327  throw new \Exception('Could not locate entity to save changes to in paragraphs editor widget.');
328  }
329 
330  // Check revisioning status.
331  $entity = $form_object->getEntity();
332  $new_revision = FALSE;
333  if ($entity instanceof RevisionableInterface) {
334  if ($entity->isNewRevision()) {
335  $new_revision = TRUE;
336  }
337  elseif ($entity->getEntityType()->hasKey('revision') && $form_state->getValue('revision')) {
338  $new_revision = TRUE;
339  }
340  }
341 
342  return $this->domProcessor->process($markup, 'paragraphs_editor', $variant, [
343  'field' => [
344  'items' => $items,
345  'context_id' => $context_id,
346  'is_mutable' => TRUE,
347  'wrapper' => $field_value_wrapper,
348  ],
349  'owner' => [
350  'entity' => $entity,
351  'new_revision' => $new_revision,
352  ],
353  'langcode' => $form_state->get('langcode'),
354  'settings' => $this->getSettings(),
355  'filter_format' => $format,
356  ]);
357  }
358 
359  /**
360  * Safely gets the field config associated with this widget.
361  *
362  * @return \Drupal\Core\Field\FieldConfigInterface
363  * The field config object.
364  */
365  protected function getFieldConfig() {
366  return TypeUtility::ensureFieldConfig($this->fieldDefinition);
367  }
368 
369 }
__construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, FieldValueManagerInterface $field_value_manager, DomProcessorInterface $dom_processor, EntityDisplayRepositoryInterface $entity_display_repository, array $plugin_managers)
static ensureFieldConfig(FieldDefinitionInterface $field_definition=NULL)
Definition: TypeUtility.php:39
formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state)
extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state)
process($variant, FieldItemListInterface $items, FormStateInterface $form_state, $format=NULL, $markup=NULL, $context_id=NULL)
static create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition)