ParagraphsEditorWidget::isApplicable()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
ccs 0
cts 2
cp 0
crap 2
rs 10
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
      '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
}
370