Completed
Push — 8.x-1.x ( b8baa3...990602 )
by Dave
02:07
created

EntityEmbedDialog   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 775
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 24
Bugs 5 Features 1
Metric Value
wmc 69
c 24
b 5
f 1
lcom 1
cbo 1
dl 0
loc 775
rs 5

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A create() 0 8 1
A getFormId() 0 3 1
C buildForm() 0 59 11
C buildSelectStep() 0 84 7
B buildReviewStep() 0 42 1
A validateForm() 0 10 3
C validateSelectStep() 0 39 7
A validateEmbedStep() 0 11 2
A submitForm() 0 1 1
A updatePluginConfigurationForm() 0 3 1
A submitStep() 0 11 1
A submitSelectStep() 0 22 3
A submitAndShowSelect() 0 3 1
A submitAndShowReview() 0 3 1
A submitAndShowEmbed() 0 3 1
A escapeValue() 0 5 2
A getDisplayPluginOptions() 0 10 2
A registerJSCallback() 0 5 2
A loadEntityBrowser() 0 12 3
F buildEmbedStep() 0 147 13
B submitEmbedStep() 0 38 4

How to fix   Complexity   

Complex Class

Complex classes like EntityEmbedDialog often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityEmbedDialog, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @file
5
 * Contains \Drupal\entity_embed\Form\EntityEmbedDialog.
6
 */
7
8
namespace Drupal\entity_embed\Form;
9
10
use Drupal\Component\Utility\Html;
11
use Drupal\Core\Ajax\AjaxResponse;
12
use Drupal\Core\Ajax\CloseModalDialogCommand;
13
use Drupal\Core\Ajax\HtmlCommand;
14
use Drupal\Core\Entity\EntityInterface;
15
use Drupal\Core\Entity\EntityTypeManagerInterface;
16
use Drupal\Core\Form\FormBase;
17
use Drupal\Core\Form\FormBuilderInterface;
18
use Drupal\Core\Form\FormStateInterface;
19
use Drupal\editor\Ajax\EditorDialogSave;
20
use Drupal\editor\EditorInterface;
21
use Drupal\embed\EmbedButtonInterface;
22
use Drupal\entity_browser\Events\Events;
23
use Drupal\entity_browser\Events\RegisterJSCallbacks;
24
use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager;
25
use Drupal\entity_embed\EntityHelperTrait;
26
use Drupal\Component\Serialization\Json;
27
use Symfony\Component\DependencyInjection\ContainerInterface;
28
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
29
30
/**
31
 * Provides a form to embed entities by specifying data attributes.
32
 */
33
class EntityEmbedDialog extends FormBase {
34
  use EntityHelperTrait;
35
36
  /**
37
   * The form builder.
38
   *
39
   * @var \Drupal\Core\Form\FormBuilderInterface
40
   */
41
  protected $formBuilder;
42
43
  /**
44
   * The entity type manager service.
45
   *
46
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
47
   */
48
  protected $entityTypeManager;
49
50
  /**
51
   * Event dispatcher service.
52
   *
53
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
54
   */
55
  protected $eventDispatcher;
56
57
  /**
58
   * The entity browser.
59
   *
60
   * @var \Drupal\entity_browser\EntityBrowserInterface
61
   */
62
  protected $entityBrowser;
63
64
  /**
65
   * The entity browser settings from the entity embed button.
66
   */
67
  protected $entityBrowserSettings = [];
68
69
  /**
70
   * Constructs a EntityEmbedDialog object.
71
   *
72
   * @param \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager $plugin_manager
73
   *   The Module Handler.
74
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
75
   *   The Form Builder.
76
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
77
   *   The entity type manager service.
78
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
79
   *   Event dispatcher service.
80
   */
81
  public function __construct(EntityEmbedDisplayManager $plugin_manager, FormBuilderInterface $form_builder, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher) {
82
    $this->setDisplayPluginManager($plugin_manager);
83
    $this->formBuilder = $form_builder;
84
    $this->entityTypeManager = $entity_type_manager;
85
    $this->eventDispatcher = $event_dispatcher;
86
  }
87
88
  /**
89
   * {@inheritdoc}
90
   */
91
  public static function create(ContainerInterface $container) {
92
    return new static(
93
      $container->get('plugin.manager.entity_embed.display'),
94
      $container->get('form_builder'),
95
      $container->get('entity_type.manager'),
96
      $container->get('event_dispatcher')
97
    );
98
  }
99
100
  /**
101
   * {@inheritdoc}
102
   */
103
  public function getFormId() {
104
    return 'entity_embed_dialog';
105
  }
106
107
  /**
108
   * {@inheritdoc}
109
   *
110
   * @param \Drupal\editor\EditorInterface $editor
111
   *   The editor to which this dialog corresponds.
112
   * @param \Drupal\embed\EmbedButtonInterface $embed_button
113
   *   The URL button to which this dialog corresponds.
114
   */
115
  public function buildForm(array $form, FormStateInterface $form_state, EditorInterface $editor = NULL, EmbedButtonInterface $embed_button = NULL) {
116
    $values = $form_state->getValues();
117
    $input = $form_state->getUserInput();
118
    // Set embed button element in form state, so that it can be used later in
119
    // validateForm() function.
120
    $form_state->set('embed_button', $embed_button);
121
    $form_state->set('editor', $editor);
122
    // Initialize entity element with form attributes, if present.
123
    $entity_element = empty($values['attributes']) ? array() : $values['attributes'];
124
    $entity_element += empty($input['attributes']) ? array() : $input['attributes'];
125
    // The default values are set directly from \Drupal::request()->request,
126
    // provided by the editor plugin opening the dialog.
127
    if (!$form_state->get('entity_element')) {
128
      $form_state->set('entity_element', isset($input['editor_object']) ? $input['editor_object'] : array());
129
    }
130
    $entity_element += $form_state->get('entity_element');
131
    $entity_element += array(
132
      'data-entity-type' => $embed_button->getTypeSetting('entity_type'),
133
      'data-entity-uuid' => '',
134
      'data-entity-id' => '',
135
      'data-entity-embed-display' => 'entity_reference:entity_reference_entity_view',
136
      'data-entity-embed-settings' => array(),
137
      'data-align' => '',
138
      'data-caption' => '',
139
    );
140
    $form_state->set('entity_element', $entity_element);
141
    $form_state->set('entity', $this->loadEntity($entity_element['data-entity-type'], $entity_element['data-entity-uuid'] ?: $entity_element['data-entity-id']));
142
143
    if (!$form_state->get('step')) {
144
      // If an entity has been selected, then always skip to the embed options.
145
      if ($form_state->get('entity')) {
146
        $form_state->set('step', 'embed');
147
      }
148
      else {
149
        $form_state->set('step', 'select');
150
      }
151
    }
152
153
    $form['#tree'] = TRUE;
154
    $form['#attached']['library'][] = 'editor/drupal.editor.dialog';
155
    $form['#attached']['library'][] = 'entity_embed/drupal.entity_embed.dialog';
156
    $form['#prefix'] = '<div id="entity-embed-dialog-form">';
157
    $form['#suffix'] = '</div>';
158
    $form['#attributes']['class'][] = 'entity-embed-dialog-step--' . $form_state->get('step');
159
160
    $this->loadEntityBrowser($form_state);
161
162
    if ($form_state->get('step') == 'select') {
163
      $form = $this->buildSelectStep($form, $form_state);
164
    }
165
    elseif ($form_state->get('step') == 'review') {
166
      $form = $this->buildReviewStep($form, $form_state);
167
    }
168
    elseif ($form_state->get('step') == 'embed') {
169
      $form = $this->buildEmbedStep($form, $form_state);
170
    }
171
172
    return $form;
173
  }
174
175
  /**
176
   * Form constructor for the entity selection step.
177
   *
178
   * @param array $form
179
   *   An associative array containing the structure of the form.
180
   * @param \Drupal\Core\Form\FormStateInterface $form_state
181
   *   The current state of the form.
182
   *
183
   * @return array
184
   *   The form structure.
185
   */
186
  public function buildSelectStep(array &$form, FormStateInterface $form_state) {
187
    $entity_element = $form_state->get('entity_element');
188
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
189
    $embed_button = $form_state->get('embed_button');
190
    $entity = $form_state->get('entity');
191
192
    $form['attributes']['data-entity-type'] = array(
193
      '#type' => 'value',
194
      '#value' => $entity_element['data-entity-type'],
195
    );
196
197
    $label = $this->t('Label');
198
    // Attempt to display a better label if we can by getting it from
199
    // the label field definition.
200
    $entity_type = $this->entityTypeManager->getDefinition($entity_element['data-entity-type']);
201
    if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface') && $entity_type->hasKey('label')) {
202
      $field_definitions = $this->entityManager()->getBaseFieldDefinitions($entity_type->id());
203
      if (isset($field_definitions[$entity_type->getKey('label')])) {
204
        $label = $field_definitions[$entity_type->getKey('label')]->getLabel();
205
      }
206
    }
207
208
    if ($this->entityBrowser) {
209
      $this->eventDispatcher->addListener(Events::REGISTER_JS_CALLBACKS, [$this, 'registerJSCallback']);
210
211
      $form['attributes']['entity_browser']['#theme_wrappers'] = ['container'];
212
      $form['attributes']['entity_browser']['browser'] = $this->entityBrowser->getDisplay()->displayEntityBrowser($form_state);
213
      $form['attributes']['entity_browser']['entity-id'] = [
214
        '#type' => 'hidden',
215
        '#default_value' => $entity ? $entity->id() : '',
216
        '#attributes' => ['class' => ['eb-target']]
217
      ];
218
      $form['#attached']['library'][] = 'entity_browser/common';
219
      $form['#attached']['drupalSettings']['entity_browser'] = [
220
        $this->entityBrowser->getDisplay()->getUuid() => [
221
          'cardinality' => 1
222
        ]
223
      ];
224
      $form['attributes']['data-entity-id'] = array(
225
        '#type' => 'value',
226
        '#title' => $entity_element['data-entity-id'],
227
      );
228
    }
229
    else {
230
      $form['attributes']['data-entity-id'] = array(
231
        '#type' => 'entity_autocomplete',
232
        '#target_type' => $entity_element['data-entity-type'],
233
        '#title' => $label,
234
        '#default_value' => $entity,
235
        '#required' => TRUE,
236
        '#description' => $this->t('Type label and pick the right one from suggestions. Note that the unique ID will be saved.'),
237
      );
238
      if ($bundles = $embed_button->getTypeSetting('bundles')) {
239
        $form['attributes']['data-entity-id']['#selection_settings']['target_bundles'] = $bundles;
240
      }
241
    }
242
243
    $form['attributes']['data-entity-uuid'] = array(
244
      '#type' => 'value',
245
      '#title' => $entity_element['data-entity-uuid'],
246
    );
247
    $form['actions'] = array(
248
      '#type' => 'actions',
249
    );
250
251
    $form['actions']['save_modal'] = array(
252
      '#type' => 'submit',
253
      '#value' => $this->t('Next'),
254
      '#button_type' => 'primary',
255
      // No regular submit-handler. This form only works via JavaScript.
256
      '#submit' => array(),
257
      '#ajax' => array(
258
        'callback' => '::submitSelectStep',
259
        'event' => 'click',
260
      ),
261
      '#attributes' => [
262
        'class' => [
263
          'js-button-next',
264
        ],
265
      ],
266
    );
267
268
    return $form;
269
  }
270
271
  /**
272
   * Form constructor for the entity review step.
273
   *
274
   * @param array $form
275
   *   An associative array containing the structure of the form.
276
   * @param \Drupal\Core\Form\FormStateInterface $form_state
277
   *   The current state of the form.
278
   *
279
   * @return array
280
   *   The form structure.
281
   */
282
  public function buildReviewStep(array &$form, FormStateInterface $form_state) {
283
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
284
    $entity = $form_state->get('entity');
285
286
    $form['selection'] = [
287
      '#markup' => $entity->label()
288
    ];
289
290
    $form['actions'] = array(
291
      '#type' => 'actions',
292
    );
293
294
    $form['actions']['back'] = array(
295
      '#type' => 'submit',
296
      '#value' => $this->t('Replace selection'),
297
      // No regular submit-handler. This form only works via JavaScript.
298
      '#submit' => array(),
299
      '#ajax' => array(
300
        'callback' => '::submitAndShowSelect',
301
        'event' => 'click',
302
      ),
303
    );
304
305
    $form['actions']['save_modal'] = array(
306
      '#type' => 'submit',
307
      '#value' => $this->t('Next'),
308
      '#button_type' => 'primary',
309
      // No regular submit-handler. This form only works via JavaScript.
310
      '#submit' => array(),
311
      '#ajax' => array(
312
        'callback' => '::submitAndShowEmbed',
313
        'event' => 'click',
314
      ),
315
      '#attributes' => [
316
        'class' => [
317
          'js-button-next',
318
        ],
319
      ],
320
    );
321
322
    return $form;
323
  }
324
325
  /**
326
   * Form constructor for the entity embedding step.
327
   *
328
   * @param array $form
329
   *   An associative array containing the structure of the form.
330
   * @param \Drupal\Core\Form\FormStateInterface $form_state
331
   *   The current state of the form.
332
   *
333
   * @return array
334
   *   The form structure.
335
   */
336
  public function buildEmbedStep(array $form, FormStateInterface $form_state) {
337
    $entity_element = $form_state->get('entity_element');
338
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
339
    $embed_button = $form_state->get('embed_button');
340
    /** @var \Drupal\editor\EditorInterface $editor */
341
    $editor = $form_state->get('editor');
342
    $entity = $form_state->get('entity');
343
    $values = $form_state->getValues();
344
345
    $entity_label = '';
346
    try {
347
      $entity_label = $entity->link();
348
    }
349
    catch (\Exception $e) {
350
      // Construct markup of the link to the entity manually if link() fails.
351
      // @see https://www.drupal.org/node/2402533
352
      $entity_label = '<a href="' . $entity->url() . '">' . $entity->label() . '</a>';
353
    }
354
355
    $form['entity'] = array(
356
      '#type' => 'item',
357
      '#title' => $this->t('Selected entity'),
358
      '#markup' => $entity_label,
359
    );
360
    $form['attributes']['data-entity-type'] = array(
361
      '#type' => 'hidden',
362
      '#value' => $entity_element['data-entity-type'],
363
    );
364
    $form['attributes']['data-entity-id'] = array(
365
      '#type' => 'hidden',
366
      '#value' => $entity_element['data-entity-id'],
367
    );
368
    $form['attributes']['data-entity-uuid'] = array(
369
      '#type' => 'hidden',
370
      '#value' => $entity_element['data-entity-uuid'],
371
    );
372
373
    // Build the list of allowed Entity Embed Display plugins.
374
    $display_plugin_options = $this->getDisplayPluginOptions($embed_button, $entity);
375
376
    // If the currently selected display is not in the available options,
377
    // use the first from the list instead. This can happen if an alter
378
    // hook customizes the list based on the entity.
379
    if (!isset($display_plugin_options[$entity_element['data-entity-embed-display']])) {
380
      $entity_element['data-entity-embed-display'] = key($display_plugin_options);
381
    }
382
383
    // The default Entity Embed Display plugin has been deprecated by the
384
    // rendered entity field formatter.
385
    if ($entity_element['data-entity-embed-display'] === 'default') {
386
      $entity_element['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
387
    }
388
389
    $form['attributes']['data-entity-embed-display'] = array(
390
      '#type' => 'select',
391
      '#title' => $this->t('Display as'),
392
      '#options' => $display_plugin_options,
393
      '#default_value' => $entity_element['data-entity-embed-display'],
394
      '#required' => TRUE,
395
      '#ajax' => array(
396
        'callback' => '::updatePluginConfigurationForm',
397
        'wrapper' => 'data-entity-embed-settings-wrapper',
398
        'effect' => 'fade',
399
      ),
400
      // Hide the selection if only one option is available.
401
      '#access' => count($display_plugin_options) > 1,
402
    );
403
    $form['attributes']['data-entity-embed-settings'] = array(
404
      '#type' => 'container',
405
      '#prefix' => '<div id="data-entity-embed-settings-wrapper">',
406
      '#suffix' => '</div>',
407
    );
408
    $form['attributes']['data-embed-button'] = array(
409
      '#type' => 'value',
410
      '#value' => $embed_button->id(),
411
    );
412
    $form['attributes']['data-entity-label'] = array(
413
      '#type' => 'value',
414
      '#value' => $embed_button->label(),
415
    );
416
    $plugin_id = !empty($values['attributes']['data-entity-embed-display']) ? $values['attributes']['data-entity-embed-display'] : $entity_element['data-entity-embed-display'];
417
    if (!empty($plugin_id)) {
418
      if (is_string($entity_element['data-entity-embed-settings'])) {
419
        $entity_element['data-entity-embed-settings'] = Json::decode($entity_element['data-entity-embed-settings']);
420
      }
421
      $display = $this->displayPluginManager()->createInstance($plugin_id, $entity_element['data-entity-embed-settings']);
422
      $display->setContextValue('entity', $entity);
423
      $display->setAttributes($entity_element);
424
      $form['attributes']['data-entity-embed-settings'] += $display->buildConfigurationForm($form, $form_state);
425
    }
426
427
    // When Drupal core's filter_align is being used, the text editor may
428
    // offer the ability to change the alignment.
429
    if (isset($entity_element['data-align']) && $editor->getFilterFormat()->filters('filter_align')->status) {
430
      $form['attributes']['data-align'] = array(
431
        '#title' => $this->t('Align'),
432
        '#type' => 'radios',
433
        '#options' => array(
434
          'none' => $this->t('None'),
435
          'left' => $this->t('Left'),
436
          'center' => $this->t('Center'),
437
          'right' => $this->t('Right'),
438
        ),
439
        '#default_value' => $entity_element['data-align'] === '' ? 'none' : $entity_element['data-align'],
440
        '#wrapper_attributes' => array('class' => array('container-inline')),
441
        '#attributes' => array('class' => array('container-inline')),
442
      );
443
    }
444
445
    // When Drupal core's filter_caption is being used, the text editor may
446
    // offer the ability to add a caption.
447
    if (isset($entity_element['data-caption']) && $editor->getFilterFormat()->filters('filter_caption')->status) {
448
      $form['attributes']['data-caption'] = array(
449
        '#title' => $this->t('Caption'),
450
        '#type' => 'textfield',
451
        '#default_value' => Html::decodeEntities($entity_element['data-caption']),
452
        '#element_validate' => array('::escapeValue'),
453
      );
454
    }
455
456
    $form['actions'] = array(
457
      '#type' => 'actions',
458
    );
459
    $form['actions']['back'] = array(
460
      '#type' => 'submit',
461
      '#value' => $this->t('Back'),
462
      // No regular submit-handler. This form only works via JavaScript.
463
      '#submit' => array(),
464
      '#ajax' => array(
465
        'callback' => !empty($this->entityBrowserSettings['display_review']) ? '::submitAndShowReview' : '::submitAndShowSelect',
466
        'event' => 'click',
467
      ),
468
    );
469
    $form['actions']['save_modal'] = array(
470
      '#type' => 'submit',
471
      '#value' => $this->t('Embed'),
472
      '#button_type' => 'primary',
473
      // No regular submit-handler. This form only works via JavaScript.
474
      '#submit' => array(),
475
      '#ajax' => array(
476
        'callback' => '::submitEmbedStep',
477
        'event' => 'click',
478
      ),
479
    );
480
481
    return $form;
482
  }
483
484
  /**
485
   * {@inheritdoc}
486
   */
487
  public function validateForm(array &$form, FormStateInterface $form_state) {
488
    parent::validateForm($form, $form_state);
489
490
    if ($form_state->get('step') == 'select') {
491
      $this->validateSelectStep($form, $form_state);
492
    }
493
    elseif ($form_state->get('step') == 'embed') {
494
      $this->validateEmbedStep($form, $form_state);
495
    }
496
  }
497
498
  /**
499
   * Form validation handler for the entity selection step.
500
   *
501
   * @param array $form
502
   *   An associative array containing the structure of the form.
503
   * @param \Drupal\Core\Form\FormStateInterface $form_state
504
   *   The current state of the form.
505
   */
506
  public function validateSelectStep(array $form, FormStateInterface $form_state) {
507
    if ($eb_id = $form_state->getValue(['attributes', 'entity_browser', 'entity-id'])) {
508
      $form_state->setValue(['attributes', 'data-entity-id'], $eb_id);
509
    }
510
511
    $values = $form_state->getValues();
512
513
    if ($entity_type = $values['attributes']['data-entity-type']) {
514
      $id = trim($values['attributes']['data-entity-id']);
515
      if ($entity = $this->loadEntity($entity_type, $id)) {
516
        if (!$entity->access('view')) {
517
          $form_state->setError($form['attributes']['data-entity-id'], $this->t('Unable to access @type entity @id.', array('@type' => $entity_type, '@id' => $id)));
518
        }
519
        else {
520
          $form_state->setValueForElement($form['attributes']['data-entity-id'], $entity->id());
521
          if ($uuid = $entity->uuid()) {
522
            $form_state->setValueForElement($form['attributes']['data-entity-uuid'], $uuid);
523
          }
524
          else {
525
            $form_state->setValueForElement($form['attributes']['data-entity-uuid'], '');
526
          }
527
528
          // Ensure that at least one Entity Embed Display plugin is present
529
          // before proceeding to the next step. Rasie an error otherwise.
530
          $embed_button = $form_state->get('embed_button');
531
          $display_plugin_options = $this->getDisplayPluginOptions($embed_button, $entity);
532
          // If no plugin is available after taking the intersection,
533
          // raise error. Also log an exception.
534
          if (empty($display_plugin_options)) {
535
            $form_state->setError($form['attributes']['data-entity-id'], $this->t('No display options available for the selected entity. Please select another entity.'));
536
            $this->logger('entity_embed')->warning('No display options available for "@type:" entity "@id" while embedding using button "@button". Please ensure that at least one Entity Embed Display plugin is allowed for this embed button which is available for this entity.', array('@type' => $entity_type, '@id' => $entity->id(), '@button' => $embed_button->id()));
537
          }
538
        }
539
      }
540
      else {
541
        $form_state->setError($form['attributes']['data-entity-id'], $this->t('Unable to load @type entity @id.', array('@type' => $entity_type, '@id' => $id)));
542
      }
543
    }
544
  }
545
546
  /**
547
   * Form validation handler for the entity embedding step.
548
   *
549
   * @param array $form
550
   *   An associative array containing the structure of the form.
551
   * @param \Drupal\Core\Form\FormStateInterface $form_state
552
   *   The current state of the form.
553
   */
554
  public function validateEmbedStep(array $form, FormStateInterface $form_state) {
555
    // Validate configuration forms for the Entity Embed Display plugin used.
556
    $entity_element = $form_state->getValue('attributes');
557
    $entity = $this->loadEntity($entity_element['data-entity-type'], $entity_element['data-entity-uuid']);
558
    $plugin_id = $entity_element['data-entity-embed-display'];
559
    $plugin_settings = $entity_element['data-entity-embed-settings'] ?: array();
560
    $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_settings);
561
    $display->setContextValue('entity', $entity);
562
    $display->setAttributes($entity_element);
563
    $display->validateConfigurationForm($form, $form_state);
564
  }
565
566
  /**
567
   * {@inheritdoc}
568
   */
569
  public function submitForm(array &$form, FormStateInterface $form_state) {}
570
571
  /**
572
   * Form submission handler to update the plugin configuration form.
573
   *
574
   * @param array $form
575
   *   The form array.
576
   * @param \Drupal\Core\Form\FormStateInterface $form_state
577
   *   The form state.
578
   */
579
  public function updatePluginConfigurationForm(array &$form, FormStateInterface $form_state) {
580
    return $form['attributes']['data-entity-embed-settings'];
581
  }
582
583
  /**
584
   * Form submission handler to to another step of the form.
585
   *
586
   * @param array $form
587
   *   The form array.
588
   * @param \Drupal\Core\Form\FormStateInterface $form_state
589
   *   The form state.
590
   *
591
   * @return \Drupal\Core\Ajax\AjaxResponse
592
   *   The ajax response.
593
   */
594
  public function submitStep(array &$form, FormStateInterface $form_state, $step) {
595
    $response = new AjaxResponse();
596
597
    $form_state->set('step', $step);
598
    $form_state->setRebuild(TRUE);
599
    $rebuild_form = $this->formBuilder->rebuildForm('entity_embed_dialog', $form_state, $form);
600
    unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
601
    $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $rebuild_form));
602
603
    return $response;
604
  }
605
606
  /**
607
   * Form submission handler for the entity selection step.
608
   *
609
   * On success will send the user to the next step of the form to select the
610
   * embed display settings. On form errors, this will rebuild the form and
611
   * display the error messages.
612
   *
613
   * @param array $form
614
   *   The form array.
615
   * @param \Drupal\Core\Form\FormStateInterface $form_state
616
   *   The form state.
617
   *
618
   * @return \Drupal\Core\Ajax\AjaxResponse
619
   *   The ajax response.
620
   */
621
  public function submitSelectStep(array &$form, FormStateInterface $form_state) {
622
    $response = new AjaxResponse();
623
624
    // Display errors in form, if any.
625
    if ($form_state->hasAnyErrors()) {
626
      unset($form['#prefix'], $form['#suffix']);
627
      $form['status_messages'] = array(
628
        '#type' => 'status_messages',
629
        '#weight' => -10,
630
      );
631
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $form));
632
    }
633
    else {
634
      $form_state->set('step', !empty($this->entityBrowserSettings['display_review']) ? 'review' : 'embed');
635
      $form_state->setRebuild(TRUE);
636
      $rebuild_form = $this->formBuilder->rebuildForm('entity_embed_dialog', $form_state, $form);
637
      unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
638
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $rebuild_form));
639
    }
640
641
    return $response;
642
  }
643
644
  /**
645
   * Submit and show select step after submit.
646
   *
647
   * @param array $form
648
   *   The form array.
649
   * @param \Drupal\Core\Form\FormStateInterface $form_state
650
   *   The form state.
651
   *
652
   * @return \Drupal\Core\Ajax\AjaxResponse
653
   *   The ajax response.
654
   */
655
  public function submitAndShowSelect(array &$form, FormStateInterface $form_state) {
656
    return $this->submitStep($form, $form_state, 'select');
657
  }
658
659
  /**
660
   * Submit and show review step after submit.
661
   *
662
   * @param array $form
663
   *   The form array.
664
   * @param \Drupal\Core\Form\FormStateInterface $form_state
665
   *   The form state.
666
   *
667
   * @return \Drupal\Core\Ajax\AjaxResponse
668
   *   The ajax response.
669
   */
670
  public function submitAndShowReview(array &$form, FormStateInterface $form_state) {
671
    return $this->submitStep($form, $form_state, 'review');
672
  }
673
674
  /**
675
   * Submit and show embed step after submit.
676
   *
677
   * @param array $form
678
   *   The form array.
679
   * @param \Drupal\Core\Form\FormStateInterface $form_state
680
   *   The form state.
681
   *
682
   * @return \Drupal\Core\Ajax\AjaxResponse
683
   *   The ajax response.
684
   */
685
  public function submitAndShowEmbed(array $form, FormStateInterface $form_state) {
686
    return $this->submitStep($form, $form_state, 'embed');
687
  }
688
689
  /**
690
   * Form submission handler for the entity embedding step.
691
   *
692
   * On success this will submit the command to save the embedded entity with
693
   * the configured display settings to the WYSIWYG element, and then close the
694
   * modal dialog. On form errors, this will rebuild the form and display the
695
   * error messages.
696
   *
697
   * @param array $form
698
   *   An associative array containing the structure of the form.
699
   * @param FormStateInterface $form_state
700
   *   An associative array containing the current state of the form.
701
   *
702
   * @return \Drupal\Core\Ajax\AjaxResponse
703
   *   The ajax response.
704
   */
705
  public function submitEmbedStep(array &$form, FormStateInterface $form_state) {
706
    $response = new AjaxResponse();
707
708
    // Submit configuration form the selected Entity Embed Display plugin.
709
    $entity_element = $form_state->getValue('attributes');
710
    $entity = $this->loadEntity($entity_element['data-entity-type'], $entity_element['data-entity-uuid']);
711
    $plugin_id = $entity_element['data-entity-embed-display'];
712
    $plugin_settings = $entity_element['data-entity-embed-settings'] ?: array();
713
    $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_settings);
714
    $display->setContextValue('entity', $entity);
715
    $display->setAttributes($entity_element);
716
    $display->submitConfigurationForm($form, $form_state);
717
718
    $values = $form_state->getValues();
719
    // Display errors in form, if any.
720
    if ($form_state->hasAnyErrors()) {
721
      unset($form['#prefix'], $form['#suffix']);
722
      $form['status_messages'] = array(
723
        '#type' => 'status_messages',
724
        '#weight' => -10,
725
      );
726
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $form));
727
    }
728
    else {
729
      // Serialize entity embed settings to JSON string.
730
      if (!empty($values['attributes']['data-entity-embed-settings'])) {
731
        $values['attributes']['data-entity-embed-settings'] = Json::encode($values['attributes']['data-entity-embed-settings']);
732
      }
733
734
      // Allow other modules to alter the values before getting submitted to the WYSIWYG.
735
      $this->moduleHandler()->alter('entity_embed_values', $values, $entity, $display, $form_state);
736
737
      $response->addCommand(new EditorDialogSave($values));
738
      $response->addCommand(new CloseModalDialogCommand());
739
    }
740
741
    return $response;
742
  }
743
744
  /**
745
   * Form element validation handler; Escapes the value an element.
746
   *
747
   * This should be used for any element in the embed form which may contain
748
   * HTML that should be serialized as an attribute element on the embed.
749
   */
750
  public static function escapeValue($element, FormStateInterface $form_state) {
751
    if ($value = trim($element['#value'])) {
752
      $form_state->setValueForElement($element, Html::escape($value));
753
    }
754
  }
755
756
  /**
757
   * Returns the allowed Entity Embed Display plugins given an embed button and
758
   * an entity.
759
   *
760
   * @param \Drupal\embed\EmbedButtonInterface $embed_button
761
   *   The embed button.
762
   * @param \Drupal\Core\Entity\EntityInterface $entity
763
   *   The entity.
764
   *
765
   * @return array
766
   *   List of allowed Entity Embed Display plugins.
767
   */
768
  public function getDisplayPluginOptions(EmbedButtonInterface $embed_button, EntityInterface $entity) {
769
    $plugins = $this->displayPluginManager->getDefinitionOptionsForEntity($entity);
770
771
    if ($allowed_plugins = $embed_button->getTypeSetting('display_plugins')) {
772
      $plugins = array_intersect_key($plugins, array_flip($allowed_plugins));
773
    }
774
775
    natsort($plugins);
776
    return $plugins;
777
  }
778
779
  /**
780
   * Registers JS callback that gets entities from entity browser and updates
781
   * form values accordingly.
782
   */
783
  public function registerJSCallback(RegisterJSCallbacks $event) {
784
    if ($event->getBrowserID() == $this->entityBrowser->id()) {
785
      $event->registerCallback('Drupal.entityEmbedDialog.selectionCompleted');
786
    }
787
  }
788
789
  /**
790
   * Load the current entity browser and its settings from the form state.
791
   *
792
   * @param \Drupal\Core\Form\FormStateInterface $form_state
793
   */
794
  protected function loadEntityBrowser(FormStateInterface $form_state) {
795
    $this->entityBrowser = NULL;
796
    $this->entityBrowserSettings = [];
797
798
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
799
    $embed_button = $form_state->get('embed_button');
800
801
    if ($embed_button && $entity_browser_id = $embed_button->getTypePlugin()->getConfigurationValue('entity_browser')) {
802
      $this->entityBrowser = $this->entityTypeManager->getStorage('entity_browser')->load($entity_browser_id);
803
      $this->entityBrowserSettings = $embed_button->getTypePlugin()->getConfigurationValue('entity_browser_settings');
804
    }
805
  }
806
807
}
808