Passed
Branch feature/linting (1d84f9)
by Christopher
12:48
created

ParagraphsEditorWidget::process()   C

Complexity

Conditions 9
Paths 40

Size

Total Lines 47
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 29
nc 40
nop 6
dl 0
loc 47
ccs 0
cts 39
cp 0
crap 90
rs 5.2941
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\paragraphs_editor\Plugin\Field\FieldWidget;
4
5
use Drupal\Component\Utility\NestedArray;
6
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
7
use Drupal\Core\Entity\RevisionableInterface;
8
use Drupal\Core\Entity\EntityFormInterface;
9
use Drupal\Core\Field\FieldDefinitionInterface;
10
use Drupal\Core\Field\FieldItemListInterface;
11
use Drupal\Core\Form\FormStateInterface;
12
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
13
use Drupal\dom_processor\DomProcessor\DomProcessorInterface;
14
use Drupal\paragraphs\Plugin\Field\FieldWidget\InlineParagraphsWidget;
15
use Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface;
16
use Drupal\paragraphs_editor\Utility\TypeUtility;
17
use Symfony\Component\DependencyInjection\ContainerInterface;
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
   */
53
  protected $bundleSelectorManager;
54
55
  /**
56
   * The plugin manager for delivery plugins.
57
   *
58
   * @var \Drupal\Component\Plugin\PluginManagerInterface
59
   */
60
  protected $deliveryProviderManager;
61
62
  /**
63
   * The entity display repository service for getting view modes.
64
   *
65
   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
66
   */
67
  protected $entityDisplayRepository;
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
      'filter_format' => 'paragraphs_ckeditor',
131
      'view_mode' => 'default',
132
      'prerender_count' => '10',
133
    ];
134
  }
135
136
  /**
137
   * {@inheritdoc}
138
   */
139
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
140
    $editable_data = $this->process('load', $items, $form_state);
141
142
    // Pretty much all the important parts are generated by the DOM processor.
143
    // Offloading things like '#attached' and '#attributes' to the processor
144
    // allows it to do things like dynamically load javascript for rendered
145
    // entities.
146
    return [
147
      'markup' => $element + [
148
        '#type' => 'text_format',
149
        '#format' => $editable_data->get('filter_format'),
150
        '#default_value' => $editable_data->get('markup'),
151
        '#rows' => 100,
152
        '#attributes' => $editable_data->get('attributes'),
153
        '#attached' => [
154
          'library' => $editable_data->get('libraries'),
155
          'drupalSettings' => $editable_data->get('drupalSettings'),
156
        ],
157
        '#allowed_formats' => [$editable_data->get('filter_format')],
158
      ],
159
      'context_id' => [
160
        '#type' => 'hidden',
161
        '#default_value' => $editable_data->get('context_id'),
162
      ],
163
    ];
164
  }
165
166
  /**
167
   * {@inheritdoc}
168
   */
169
  public function settingsForm(array $form, FormStateInterface $form_state) {
170
    $elements = [];
171
172
    $elements['title'] = [
173
      '#type' => 'textfield',
174
      '#title' => $this->t('Paragraph Title'),
175
      '#description' => $this->t('Label to appear as title on the button "Insert [title]. This label is translatable.'),
176
      '#default_value' => $this->getSetting('title'),
177
      '#required' => TRUE,
178
    ];
179
180
    $options = [];
181
    foreach ($this->bundleSelectorManager->getDefinitions() as $plugin) {
182
      $options[$plugin['id']] = $plugin['title'];
183
    }
184
185
    $elements['bundle_selector'] = [
186
      '#type' => 'select',
187
      '#title' => $this->t('Bundle Selection Handler'),
188
      '#description' => $this->t('The bundle selector form plugin that will be used to allow users to insert paragraph items.'),
189
      '#options' => $options,
190
      '#default_value' => $this->getSetting('bundle_selector'),
191
      '#required' => TRUE,
192
    ];
193
194
    $options = [];
195
    foreach ($this->deliveryProviderManager->getDefinitions() as $plugin) {
196
      $options[$plugin['id']] = $plugin['title'];
197
    }
198
199
    $elements['delivery_provider'] = [
200
      '#type' => 'select',
201
      '#title' => $this->t('Delivery Handler'),
202
      '#description' => $this->t('The delivery plugin that controls the user experience for how forms are delivered.'),
203
      '#options' => $options,
204
      '#default_value' => $this->getSetting('delivery_provider'),
205
      '#required' => TRUE,
206
    ];
207
208
    $options = [];
209
    foreach (filter_formats() as $filter_format) {
210
      $options[$filter_format->id()] = $filter_format->label();
211
    }
212
213
    $elements['filter_format'] = [
214
      '#type' => 'select',
215
      '#title' => 'Default Filter Format',
216
      '#description' => $this->t('The default filter format to use for the Editor instance.'),
217
      '#options' => $options,
218
      '#default_value' => $this->getSetting('filter_format'),
219
      '#required' => TRUE,
220
    ];
221
222
    $elements['view_mode'] = [
223
      '#type' => 'select',
224
      '#title' => 'Editor View Mode',
225
      '#description' => $this->t('The view mode that will be used to render embedded entities.'),
226
      '#options' => $this->entityDisplayRepository->getViewModeOptions('paragraph'),
227
      '#default_value' => $this->getSetting('prerender_count'),
228
      '#required' => TRUE,
229
    ];
230
231
    $options = [0 => $this->t('None')];
232
    for ($i = 5; $i <= 50; $i += 5) {
233
      $options[$i] = $i;
234
    }
235
    $options[-1] = $this->t('All');
236
    $elements['prerender_count'] = [
237
      '#type' => 'select',
238
      '#title' => 'Maximum Pre-Render Items',
239
      '#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."),
240
      '#options' => $options,
241
      '#default_value' => $this->getSetting('prerender_count'),
242
      '#required' => TRUE,
243
    ];
244
245
    return $elements;
246
  }
247
248
  /**
249
   * {@inheritdoc}
250
   */
251
  public function settingsSummary() {
252
    $bundle_selector = $this->bundleSelectorManager->getDefinition($this->getSetting('bundle_selector'));
253
    $delivery_provider = $this->deliveryProviderManager->getDefinition($this->getSetting('delivery_provider'));
254
    $prerender_count = $this->getSetting('prerender_count');
255
    if ($prerender_count == '-1') {
256
      $prerender_count = 'All';
257
    }
258
    elseif ($prerender_count == '0') {
259
      $prerender_count = 'None';
260
    }
261
    $summary = [];
262
    $summary[] = $this->t('Title: @title', ['@title' => $this->getSetting('title')]);
263
    $summary[] = $this->t('Bundle Selector: @bundle_selector', ['@bundle_selector' => $bundle_selector['title']]);
264
    $summary[] = $this->t('Delivery Provider: @delivery_provider', ['@delivery_provider' => $delivery_provider['title']]);
265
    $summary[] = $this->t('Default Format: @filter_format', ['@filter_format' => $this->getSetting('filter_format')]);
266
    $summary[] = $this->t('View Mode: @mode', ['@mode' => $this->getSetting('view_mode')]);
267
    $summary[] = $this->t('Maximum Pre-Render Items: @prerender_count', ['@prerender_count' => $prerender_count]);
268
    return $summary;
269
  }
270
271
  /**
272
   * {@inheritdoc}
273
   */
274
  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
275
    $field_name = $this->getFieldConfig()->getName();
276
    $path = array_merge($form['#parents'], [$field_name]);
277
    $values = NestedArray::getValue($form_state->getValues(), $path);
278
    $this->process('update', $items, $form_state, $values['markup']['format'], $values['markup']['value'], $values['context_id']);
279
  }
280
281
  /**
282
   * {@inheritdoc}
283
   */
284
  public static function isApplicable(FieldDefinitionInterface $field_definition) {
285
    return \Drupal::service('paragraphs_editor.field_value.manager')->isParagraphsEditorField($field_definition);
286
  }
287
288
  /**
289
   * {@inheritdoc}
290
   */
291
  protected function mergeDefaults() {
292
    $this->settings += $this->getFieldConfig()->getThirdPartySettings('paragraphs_editor');
293
    $this->settings += static::defaultSettings();
294
    $this->defaultSettingsMerged = TRUE;
295
  }
296
297
  /**
298
   * Passes markup through the paragraphs_editor DOM processor.
299
   *
300
   * @param string $variant
301
   *   The DOM Processor plugin variant to run:
302
   *     - 'load' is used to take saved markup and make it editable.
303
   *     - 'update' is used to take editor markup and make it saveable.
304
   * @param \Drupal\Core\Field\FieldItemListInterface $items
305
   *   The field items that will receive savable entities, or serve loadable
306
   *   entities. Note that neither of these operations perform entity saves.
307
   * @param \Drupal\Core\Form\FormStateInterface $form_state
308
   *   The form state for the form that the field widget belongs to.
309
   * @param string|null $format
310
   *   The default filter format name to apply to created text entities.
311
   * @param string|null $markup
312
   *   The markup to be processed. Defaults to the markup inside the text
313
   *   entity.
314
   * @param string|null $context_id
315
   *   The id of the root editing context to pull edits from.
316
   *
317
   * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorPreparer
318
   * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorDecorator
319
   * @see \Drupal\paragraphs_editor\Plugin\dom_processor\data_processor\ParagraphsEditorExtractor
320
   *
321
   * @return \Drupal\dom_processor\DomProcessor\DomProcessorResultInterface
322
   *   See the ParagraphsEditorDecorator and ParagraphsEditorExtractor DOM
323
   *   Processor plugins for more information.
324
   */
325
  protected function process($variant, FieldItemListInterface $items, FormStateInterface $form_state, $format = NULL, $markup = NULL, $context_id = NULL) {
326
    $field_value_wrapper = $this->fieldValueManager->wrapItems($items);
327
328
    if (!isset($markup)) {
329
      $markup = $field_value_wrapper->getMarkup();
330
    }
331
332
    if (!isset($format)) {
333
      $format = $field_value_wrapper->getFormat();
334
    }
335
336
    if (!$format) {
337
      $format = $this->getSetting('text_format');
338
    }
339
340
    // Ensure that we can get an entity to savethe updates to.
341
    $form_object = $form_state->getFormObject();
342
    if (!$form_object instanceof EntityFormInterface) {
343
      throw new \Exception('Could not locate entity to save changes to in paragraphs editor widget.');
344
    }
345
346
    // Check revisioning status.
347
    $entity = $form_state->getFormObject()->getEntity();
0 ignored issues
show
Bug introduced by
The method getEntity() does not exist on Drupal\Core\Form\FormInterface. It seems like you code against a sub-type of Drupal\Core\Form\FormInterface such as Drupal\Core\Entity\EntityFormInterface or Drupal\Core\Entity\EntityForm or Drupal\Core\Entity\ContentEntityConfirmFormBase or Drupal\Core\Entity\EntityConfirmFormBase. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

347
    $entity = $form_state->getFormObject()->/** @scrutinizer ignore-call */ getEntity();
Loading history...
348
    $new_revision = FALSE;
349
    if ($entity instanceof RevisionableInterface) {
350
      if ($entity->isNewRevision()) {
351
        $new_revision = TRUE;
352
      }
353
      elseif ($entity->getEntityType()->hasKey('revision') && $form_state->getValue('revision')) {
0 ignored issues
show
Bug introduced by
The method getEntityType() does not exist on Drupal\Core\Entity\RevisionableInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Drupal\Core\Entity\RevisionLogInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

353
      elseif ($entity->/** @scrutinizer ignore-call */ getEntityType()->hasKey('revision') && $form_state->getValue('revision')) {
Loading history...
354
        $new_revision = TRUE;
355
      }
356
    }
357
358
    return $this->domProcessor->process($markup, 'paragraphs_editor', $variant, [
359
      'field' => [
360
        'items' => $items,
361
        'context_id' => $context_id,
362
        'is_mutable' => TRUE,
363
        'wrapper' => $field_value_wrapper,
364
      ],
365
      'owner' => [
366
        'entity' => $entity,
367
        'new_revision' => $new_revision,
368
      ],
369
      'langcode' => $form_state->get('langcode'),
370
      'settings' => $this->getSettings(),
371
      'filter_format' => $format,
372
    ]);
373
  }
374
375
  /**
376
   * Safely gets the field config associated with this widget.
377
   *
378
   * @return \Drupal\Core\Field\FieldConfigInterface
379
   *   The field config object.
380
   */
381
  protected function getFieldConfig() {
382
    return TypeUtility::ensureFieldConfig($this->fieldDefinition);
383
  }
384
385
}
386