Completed
Pull Request — 8.x-1.x (#218)
by Dave
01:55
created

EntityEmbedDialog::validateSelectStep()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 37
Code Lines 21

Duplication

Lines 6
Ratio 16.22 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 6
loc 37
rs 8.439
cc 6
eloc 21
nc 12
nop 2
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\Ajax\SetDialogTitleCommand;
15
use Drupal\Core\Entity\EntityInterface;
16
use Drupal\Core\Entity\EntityTypeManagerInterface;
17
use Drupal\Core\Form\FormBase;
18
use Drupal\Core\Form\FormBuilderInterface;
19
use Drupal\Core\Form\FormStateInterface;
20
use Drupal\editor\Ajax\EditorDialogSave;
21
use Drupal\editor\EditorInterface;
22
use Drupal\embed\EmbedButtonInterface;
23
use Drupal\entity_browser\Events\Events;
24
use Drupal\entity_browser\Events\RegisterJSCallbacks;
25
use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager;
26
use Drupal\entity_embed\EntityHelperTrait;
27
use Drupal\Component\Serialization\Json;
28
use Symfony\Component\DependencyInjection\ContainerInterface;
29
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
30
31
/**
32
 * Provides a form to embed entities by specifying data attributes.
33
 */
34
class EntityEmbedDialog extends FormBase {
35
  use EntityHelperTrait;
36
37
  /**
38
   * The form builder.
39
   *
40
   * @var \Drupal\Core\Form\FormBuilderInterface
41
   */
42
  protected $formBuilder;
43
44
  /**
45
   * The entity type manager service.
46
   *
47
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
48
   */
49
  protected $entityTypeManager;
50
51
  /**
52
   * Event dispatcher service.
53
   *
54
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
55
   */
56
  protected $eventDispatcher;
57
58
  /**
59
   * The entity browser.
60
   *
61
   * @var \Drupal\entity_browser\EntityBrowserInterface
62
   */
63
  protected $entityBrowser;
64
65
  /**
66
   * The entity browser settings from the entity embed button.
67
   */
68
  protected $entityBrowserSettings = [];
69
70
  /**
71
   * Constructs a EntityEmbedDialog object.
72
   *
73
   * @param \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager $plugin_manager
74
   *   The Module Handler.
75
   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
76
   *   The Form Builder.
77
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
78
   *   The entity type manager service.
79
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
80
   *   Event dispatcher service.
81
   */
82
  public function __construct(EntityEmbedDisplayManager $plugin_manager, FormBuilderInterface $form_builder, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher) {
83
    $this->setDisplayPluginManager($plugin_manager);
84
    $this->formBuilder = $form_builder;
85
    $this->entityTypeManager = $entity_type_manager;
86
    $this->eventDispatcher = $event_dispatcher;
87
  }
88
89
  /**
90
   * {@inheritdoc}
91
   */
92
  public static function create(ContainerInterface $container) {
93
    return new static(
94
      $container->get('plugin.manager.entity_embed.display'),
95
      $container->get('form_builder'),
96
      $container->get('entity_type.manager'),
97
      $container->get('event_dispatcher')
98
    );
99
  }
100
101
  /**
102
   * {@inheritdoc}
103
   */
104
  public function getFormId() {
105
    return 'entity_embed_dialog';
106
  }
107
108
  /**
109
   * {@inheritdoc}
110
   *
111
   * @param \Drupal\editor\EditorInterface $editor
112
   *   The editor to which this dialog corresponds.
113
   * @param \Drupal\embed\EmbedButtonInterface $embed_button
114
   *   The URL button to which this dialog corresponds.
115
   */
116
  public function buildForm(array $form, FormStateInterface $form_state, EditorInterface $editor = NULL, EmbedButtonInterface $embed_button = NULL) {
117
    $values = $form_state->getValues();
118
    $input = $form_state->getUserInput();
119
    // Set embed button element in form state, so that it can be used later in
120
    // validateForm() function.
121
    $form_state->set('embed_button', $embed_button);
122
    $form_state->set('editor', $editor);
123
    // Initialize entity element with form attributes, if present.
124
    $entity_element = empty($values['attributes']) ? array() : $values['attributes'];
125
    $entity_element += empty($input['attributes']) ? array() : $input['attributes'];
126
    // The default values are set directly from \Drupal::request()->request,
127
    // provided by the editor plugin opening the dialog.
128
    if (!$form_state->get('entity_element')) {
129
      $form_state->set('entity_element', isset($input['editor_object']) ? $input['editor_object'] : array());
130
    }
131
    $entity_element += $form_state->get('entity_element');
132
    $entity_element += array(
133
      'data-entity-type' => $embed_button->getTypeSetting('entity_type'),
134
      'data-entity-uuid' => '',
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']));
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
    $form['#title'] = $this->t('Select @type to embed', array('@type' => $entity_type->getLowercaseLabel()));
209
210
    if ($this->entityBrowser) {
211
      $this->eventDispatcher->addListener(Events::REGISTER_JS_CALLBACKS, [$this, 'registerJSCallback']);
212
213
      $form['attributes']['entity_browser']['#theme_wrappers'] = ['container'];
214
      $form['attributes']['entity_browser']['browser'] = $this->entityBrowser->getDisplay()->displayEntityBrowser($form_state);
215
      $form['attributes']['entity_browser']['entity-id'] = [
216
        '#type' => 'hidden',
217
        '#default_value' => $entity ? $entity->id() : '',
218
        '#attributes' => ['class' => ['eb-target']]
219
      ];
220
      $form['#attached']['library'][] = 'entity_browser/common';
221
      $form['#attached']['drupalSettings']['entity_browser'] = [
222
        $this->entityBrowser->getDisplay()->getUuid() => [
223
          'cardinality' => 1
224
        ]
225
      ];
226
      $form['attributes']['data-entity-id'] = array(
227
        '#type' => 'value',
228
        '#title' => $entity_element['data-entity-id'],
229
      );
230
    }
231
    else {
232
      $form['attributes']['data-entity-id'] = array(
233
        '#type' => 'entity_autocomplete',
234
        '#target_type' => $entity_element['data-entity-type'],
235
        '#title' => $label,
236
        '#default_value' => $entity,
237
        '#required' => TRUE,
238
        '#description' => $this->t('Type label and pick the right one from suggestions. Note that the unique ID will be saved.'),
239
      );
240
      if ($bundles = $embed_button->getTypeSetting('bundles')) {
241
        $form['attributes']['data-entity-id']['#selection_settings']['target_bundles'] = $bundles;
242
      }
243
    }
244
245
    $form['attributes']['data-entity-uuid'] = array(
246
      '#type' => 'value',
247
      '#title' => $entity_element['data-entity-uuid'],
248
    );
249
    $form['actions'] = array(
250
      '#type' => 'actions',
251
    );
252
253
    $form['actions']['save_modal'] = array(
254
      '#type' => 'submit',
255
      '#value' => $this->t('Next'),
256
      '#button_type' => 'primary',
257
      // No regular submit-handler. This form only works via JavaScript.
258
      '#submit' => array(),
259
      '#ajax' => array(
260
        'callback' => '::submitSelectStep',
261
        'event' => 'click',
262
      ),
263
      '#attributes' => [
264
        'class' => [
265
          'js-button-next',
266
        ],
267
      ],
268
    );
269
270
    return $form;
271
  }
272
273
  /**
274
   * Form constructor for the entity review step.
275
   *
276
   * @param array $form
277
   *   An associative array containing the structure of the form.
278
   * @param \Drupal\Core\Form\FormStateInterface $form_state
279
   *   The current state of the form.
280
   *
281
   * @return array
282
   *   The form structure.
283
   */
284
  public function buildReviewStep(array &$form, FormStateInterface $form_state) {
285
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
286
    $entity = $form_state->get('entity');
287
288
    $form['#title'] = $this->t('Review selected @type', array('@type' => $entity->getEntityType()->getLowercaseLabel()));
289
290
    $form['selection'] = [
291
      '#markup' => $entity->label()
292
    ];
293
294
    $form['actions'] = array(
295
      '#type' => 'actions',
296
    );
297
298
    $form['actions']['back'] = array(
299
      '#type' => 'submit',
300
      '#value' => $this->t('Replace selection'),
301
      // No regular submit-handler. This form only works via JavaScript.
302
      '#submit' => array(),
303
      '#ajax' => array(
304
        'callback' => '::submitAndShowSelect',
305
        'event' => 'click',
306
      ),
307
    );
308
309
    $form['actions']['save_modal'] = array(
310
      '#type' => 'submit',
311
      '#value' => $this->t('Next'),
312
      '#button_type' => 'primary',
313
      // No regular submit-handler. This form only works via JavaScript.
314
      '#submit' => array(),
315
      '#ajax' => array(
316
        'callback' => '::submitAndShowEmbed',
317
        'event' => 'click',
318
      ),
319
      '#attributes' => [
320
        'class' => [
321
          'js-button-next',
322
        ],
323
      ],
324
    );
325
326
    return $form;
327
  }
328
329
  /**
330
   * Form constructor for the entity embedding step.
331
   *
332
   * @param array $form
333
   *   An associative array containing the structure of the form.
334
   * @param \Drupal\Core\Form\FormStateInterface $form_state
335
   *   The current state of the form.
336
   *
337
   * @return array
338
   *   The form structure.
339
   */
340
  public function buildEmbedStep(array $form, FormStateInterface $form_state) {
341
    $entity_element = $form_state->get('entity_element');
342
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
343
    $embed_button = $form_state->get('embed_button');
344
    /** @var \Drupal\editor\EditorInterface $editor */
345
    $editor = $form_state->get('editor');
346
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
347
    $entity = $form_state->get('entity');
348
    $values = $form_state->getValues();
349
350
    $form['#title'] = $this->t('Embed @type', array('@type' => $entity->getEntityType()->getLowercaseLabel()));
351
352
    $entity_label = '';
353
    try {
354
      $entity_label = $entity->link();
355
    }
356
    catch (\Exception $e) {
357
      // Construct markup of the link to the entity manually if link() fails.
358
      // @see https://www.drupal.org/node/2402533
359
      $entity_label = '<a href="' . $entity->url() . '">' . $entity->label() . '</a>';
360
    }
361
362
    $form['entity'] = array(
363
      '#type' => 'item',
364
      '#title' => $this->t('Selected entity'),
365
      '#markup' => $entity_label,
366
    );
367
    $form['attributes']['data-entity-type'] = array(
368
      '#type' => 'hidden',
369
      '#value' => $entity_element['data-entity-type'],
370
    );
371
    $form['attributes']['data-entity-uuid'] = array(
372
      '#type' => 'hidden',
373
      '#value' => $entity_element['data-entity-uuid'],
374
    );
375
376
    // Build the list of allowed Entity Embed Display plugins.
377
    $display_plugin_options = $this->getDisplayPluginOptions($embed_button, $entity);
378
379
    // If the currently selected display is not in the available options,
380
    // use the first from the list instead. This can happen if an alter
381
    // hook customizes the list based on the entity.
382
    if (!isset($display_plugin_options[$entity_element['data-entity-embed-display']])) {
383
      $entity_element['data-entity-embed-display'] = key($display_plugin_options);
384
    }
385
386
    // The default Entity Embed Display plugin has been deprecated by the
387
    // rendered entity field formatter.
388
    if ($entity_element['data-entity-embed-display'] === 'default') {
389
      $entity_element['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
390
    }
391
392
    $form['attributes']['data-entity-embed-display'] = array(
393
      '#type' => 'select',
394
      '#title' => $this->t('Display as'),
395
      '#options' => $display_plugin_options,
396
      '#default_value' => $entity_element['data-entity-embed-display'],
397
      '#required' => TRUE,
398
      '#ajax' => array(
399
        'callback' => '::updatePluginConfigurationForm',
400
        'wrapper' => 'data-entity-embed-settings-wrapper',
401
        'effect' => 'fade',
402
      ),
403
      // Hide the selection if only one option is available.
404
      '#access' => count($display_plugin_options) > 1,
405
    );
406
    $form['attributes']['data-entity-embed-settings'] = array(
407
      '#type' => 'container',
408
      '#prefix' => '<div id="data-entity-embed-settings-wrapper">',
409
      '#suffix' => '</div>',
410
    );
411
    $form['attributes']['data-embed-button'] = array(
412
      '#type' => 'value',
413
      '#value' => $embed_button->id(),
414
    );
415
    $plugin_id = !empty($values['attributes']['data-entity-embed-display']) ? $values['attributes']['data-entity-embed-display'] : $entity_element['data-entity-embed-display'];
416
    if (!empty($plugin_id)) {
417
      if (is_string($entity_element['data-entity-embed-settings'])) {
418
        $entity_element['data-entity-embed-settings'] = Json::decode($entity_element['data-entity-embed-settings']);
419
      }
420
      $display = $this->displayPluginManager()->createInstance($plugin_id, $entity_element['data-entity-embed-settings']);
421
      $display->setContextValue('entity', $entity);
422
      $display->setAttributes($entity_element);
423
      $form['attributes']['data-entity-embed-settings'] += $display->buildConfigurationForm($form, $form_state);
424
    }
425
426
    // When Drupal core's filter_align is being used, the text editor may
427
    // offer the ability to change the alignment.
428
    if (isset($entity_element['data-align']) && $editor->getFilterFormat()->filters('filter_align')->status) {
429
      $form['attributes']['data-align'] = array(
430
        '#title' => $this->t('Align'),
431
        '#type' => 'radios',
432
        '#options' => array(
433
          'none' => $this->t('None'),
434
          'left' => $this->t('Left'),
435
          'center' => $this->t('Center'),
436
          'right' => $this->t('Right'),
437
        ),
438
        '#default_value' => $entity_element['data-align'] === '' ? 'none' : $entity_element['data-align'],
439
        '#wrapper_attributes' => array('class' => array('container-inline')),
440
        '#attributes' => array('class' => array('container-inline')),
441
      );
442
    }
443
444
    // When Drupal core's filter_caption is being used, the text editor may
445
    // offer the ability to add a caption.
446
    if (isset($entity_element['data-caption']) && $editor->getFilterFormat()->filters('filter_caption')->status) {
447
      $form['attributes']['data-caption'] = array(
448
        '#title' => $this->t('Caption'),
449
        '#type' => 'textfield',
450
        '#default_value' => Html::decodeEntities($entity_element['data-caption']),
451
        '#element_validate' => array('::escapeValue'),
452
      );
453
    }
454
455
    $form['actions'] = array(
456
      '#type' => 'actions',
457
    );
458
    $form['actions']['back'] = array(
459
      '#type' => 'submit',
460
      '#value' => $this->t('Back'),
461
      // No regular submit-handler. This form only works via JavaScript.
462
      '#submit' => array(),
463
      '#ajax' => array(
464
        'callback' => !empty($this->entityBrowserSettings['display_review']) ? '::submitAndShowReview' : '::submitAndShowSelect',
465
        'event' => 'click',
466
      ),
467
    );
468
    $form['actions']['save_modal'] = array(
469
      '#type' => 'submit',
470
      '#value' => $this->t('Embed'),
471
      '#button_type' => 'primary',
472
      // No regular submit-handler. This form only works via JavaScript.
473
      '#submit' => array(),
474
      '#ajax' => array(
475
        'callback' => '::submitEmbedStep',
476
        'event' => 'click',
477
      ),
478
    );
479
480
    return $form;
481
  }
482
483
  /**
484
   * {@inheritdoc}
485
   */
486
  public function validateForm(array &$form, FormStateInterface $form_state) {
487
    parent::validateForm($form, $form_state);
488
489
    if ($form_state->get('step') == 'select') {
490
      $this->validateSelectStep($form, $form_state);
491
    }
492
    elseif ($form_state->get('step') == 'embed') {
493
      $this->validateEmbedStep($form, $form_state);
494
    }
495
  }
496
497
  /**
498
   * Form validation handler for the entity selection step.
499
   *
500
   * @param array $form
501
   *   An associative array containing the structure of the form.
502
   * @param \Drupal\Core\Form\FormStateInterface $form_state
503
   *   The current state of the form.
504
   */
505
  public function validateSelectStep(array $form, FormStateInterface $form_state) {
506
    if ($eb_id = $form_state->getValue(['attributes', 'entity_browser', 'entity-id'])) {
507
      $form_state->setValue(['attributes', 'data-entity-id'], $eb_id);
508
    }
509
510
    $values = $form_state->getValues();
511
512
    $entity_type = $values['attributes']['data-entity-type'];
513
    $id = trim($values['attributes']['data-entity-id']);
514
    if ($entity = $this->loadEntity($entity_type, $id)) {
515
      if (!$entity->access('view')) {
516
        $form_state->setError($form['attributes']['data-entity-id'], $this->t('Unable to access @type entity @id.', array('@type' => $entity_type, '@id' => $id)));
517
      }
518
      else {
519
        if ($uuid = $entity->uuid()) {
520
          $form_state->setValueForElement($form['attributes']['data-entity-uuid'], $uuid);
521
        }
522 View Code Duplication
        else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
523
          $form_state->setError($form['attributes']['data-entity-id'], $this->t('Cannot embed @type entity @id because it does not have a UUID.', array('@type' => $entity_type, '@id' => $id)));
524
        }
525
526
        // Ensure that at least one Entity Embed Display plugin is present
527
        // before proceeding to the next step. Rasie an error otherwise.
528
        $embed_button = $form_state->get('embed_button');
529
        $display_plugin_options = $this->getDisplayPluginOptions($embed_button, $entity);
530
        // If no plugin is available after taking the intersection,
531
        // raise error. Also log an exception.
532
        if (empty($display_plugin_options)) {
533
          $form_state->setError($form['attributes']['data-entity-id'], $this->t('No display options available for the selected entity. Please select another entity.'));
534
          $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()));
535
        }
536
      }
537
    }
538 View Code Duplication
    else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
539
      $form_state->setError($form['attributes']['data-entity-id'], $this->t('Unable to load @type entity @id.', array('@type' => $entity_type, '@id' => $id)));
540
    }
541
  }
542
543
  /**
544
   * Form validation handler for the entity embedding step.
545
   *
546
   * @param array $form
547
   *   An associative array containing the structure of the form.
548
   * @param \Drupal\Core\Form\FormStateInterface $form_state
549
   *   The current state of the form.
550
   */
551
  public function validateEmbedStep(array $form, FormStateInterface $form_state) {
552
    // Validate configuration forms for the Entity Embed Display plugin used.
553
    $entity_element = $form_state->getValue('attributes');
554
    $entity = $this->loadEntity($entity_element['data-entity-type'], $entity_element['data-entity-uuid']);
555
    $plugin_id = $entity_element['data-entity-embed-display'];
556
    $plugin_settings = $entity_element['data-entity-embed-settings'] ?: array();
557
    $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_settings);
558
    $display->setContextValue('entity', $entity);
559
    $display->setAttributes($entity_element);
560
    $display->validateConfigurationForm($form, $form_state);
561
  }
562
563
  /**
564
   * {@inheritdoc}
565
   */
566
  public function submitForm(array &$form, FormStateInterface $form_state) {}
567
568
  /**
569
   * Form submission handler to update the plugin configuration form.
570
   *
571
   * @param array $form
572
   *   The form array.
573
   * @param \Drupal\Core\Form\FormStateInterface $form_state
574
   *   The form state.
575
   */
576
  public function updatePluginConfigurationForm(array &$form, FormStateInterface $form_state) {
577
    return $form['attributes']['data-entity-embed-settings'];
578
  }
579
580
  /**
581
   * Form submission handler to to another step of the form.
582
   *
583
   * @param array $form
584
   *   The form array.
585
   * @param \Drupal\Core\Form\FormStateInterface $form_state
586
   *   The form state.
587
   *
588
   * @return \Drupal\Core\Ajax\AjaxResponse
589
   *   The ajax response.
590
   */
591
  public function submitStep(array &$form, FormStateInterface $form_state, $step) {
592
    $response = new AjaxResponse();
593
594
    $form_state->set('step', $step);
595
    $form_state->setRebuild(TRUE);
596
    $rebuild_form = $this->formBuilder->rebuildForm('entity_embed_dialog', $form_state, $form);
597
    unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
598
    $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $rebuild_form));
599
    $response->addCommand(new SetDialogTitleCommand('', $rebuild_form['#title']));
600
601
    return $response;
602
  }
603
604
  /**
605
   * Form submission handler for the entity selection step.
606
   *
607
   * On success will send the user to the next step of the form to select the
608
   * embed display settings. On form errors, this will rebuild the form and
609
   * display the error messages.
610
   *
611
   * @param array $form
612
   *   The form array.
613
   * @param \Drupal\Core\Form\FormStateInterface $form_state
614
   *   The form state.
615
   *
616
   * @return \Drupal\Core\Ajax\AjaxResponse
617
   *   The ajax response.
618
   */
619
  public function submitSelectStep(array &$form, FormStateInterface $form_state) {
620
    $response = new AjaxResponse();
621
622
    // Display errors in form, if any.
623
    if ($form_state->hasAnyErrors()) {
624
      unset($form['#prefix'], $form['#suffix']);
625
      $form['status_messages'] = array(
626
        '#type' => 'status_messages',
627
        '#weight' => -10,
628
      );
629
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $form));
630
    }
631
    else {
632
      $form_state->set('step', !empty($this->entityBrowserSettings['display_review']) ? 'review' : 'embed');
633
      $form_state->setRebuild(TRUE);
634
      $rebuild_form = $this->formBuilder->rebuildForm('entity_embed_dialog', $form_state, $form);
635
      unset($rebuild_form['#prefix'], $rebuild_form['#suffix']);
636
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $rebuild_form));
637
      $response->addCommand(new SetDialogTitleCommand('', $rebuild_form['#title']));
638
    }
639
640
    return $response;
641
  }
642
643
  /**
644
   * Submit and show select step after submit.
645
   *
646
   * @param array $form
647
   *   The form array.
648
   * @param \Drupal\Core\Form\FormStateInterface $form_state
649
   *   The form state.
650
   *
651
   * @return \Drupal\Core\Ajax\AjaxResponse
652
   *   The ajax response.
653
   */
654
  public function submitAndShowSelect(array &$form, FormStateInterface $form_state) {
655
    return $this->submitStep($form, $form_state, 'select');
656
  }
657
658
  /**
659
   * Submit and show review step after submit.
660
   *
661
   * @param array $form
662
   *   The form array.
663
   * @param \Drupal\Core\Form\FormStateInterface $form_state
664
   *   The form state.
665
   *
666
   * @return \Drupal\Core\Ajax\AjaxResponse
667
   *   The ajax response.
668
   */
669
  public function submitAndShowReview(array &$form, FormStateInterface $form_state) {
670
    return $this->submitStep($form, $form_state, 'review');
671
  }
672
673
  /**
674
   * Submit and show embed step after submit.
675
   *
676
   * @param array $form
677
   *   The form array.
678
   * @param \Drupal\Core\Form\FormStateInterface $form_state
679
   *   The form state.
680
   *
681
   * @return \Drupal\Core\Ajax\AjaxResponse
682
   *   The ajax response.
683
   */
684
  public function submitAndShowEmbed(array $form, FormStateInterface $form_state) {
685
    return $this->submitStep($form, $form_state, 'embed');
686
  }
687
688
  /**
689
   * Form submission handler for the entity embedding step.
690
   *
691
   * On success this will submit the command to save the embedded entity with
692
   * the configured display settings to the WYSIWYG element, and then close the
693
   * modal dialog. On form errors, this will rebuild the form and display the
694
   * error messages.
695
   *
696
   * @param array $form
697
   *   An associative array containing the structure of the form.
698
   * @param FormStateInterface $form_state
699
   *   An associative array containing the current state of the form.
700
   *
701
   * @return \Drupal\Core\Ajax\AjaxResponse
702
   *   The ajax response.
703
   */
704
  public function submitEmbedStep(array &$form, FormStateInterface $form_state) {
705
    $response = new AjaxResponse();
706
707
    // Submit configuration form the selected Entity Embed Display plugin.
708
    $entity_element = $form_state->getValue('attributes');
709
    $entity = $this->loadEntity($entity_element['data-entity-type'], $entity_element['data-entity-uuid']);
710
    $plugin_id = $entity_element['data-entity-embed-display'];
711
    $plugin_settings = $entity_element['data-entity-embed-settings'] ?: array();
712
    $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_settings);
713
    $display->setContextValue('entity', $entity);
714
    $display->setAttributes($entity_element);
715
    $display->submitConfigurationForm($form, $form_state);
716
717
    $values = $form_state->getValues();
718
    // Display errors in form, if any.
719
    if ($form_state->hasAnyErrors()) {
720
      unset($form['#prefix'], $form['#suffix']);
721
      $form['status_messages'] = array(
722
        '#type' => 'status_messages',
723
        '#weight' => -10,
724
      );
725
      $response->addCommand(new HtmlCommand('#entity-embed-dialog-form', $form));
726
    }
727
    else {
728
      // Serialize entity embed settings to JSON string.
729
      if (!empty($values['attributes']['data-entity-embed-settings'])) {
730
        $values['attributes']['data-entity-embed-settings'] = Json::encode($values['attributes']['data-entity-embed-settings']);
731
      }
732
733
      // Allow other modules to alter the values before getting submitted to the WYSIWYG.
734
      $this->moduleHandler()->alter('entity_embed_values', $values, $entity, $display, $form_state);
735
736
      $response->addCommand(new EditorDialogSave($values));
737
      $response->addCommand(new CloseModalDialogCommand());
738
    }
739
740
    return $response;
741
  }
742
743
  /**
744
   * Form element validation handler; Escapes the value an element.
745
   *
746
   * This should be used for any element in the embed form which may contain
747
   * HTML that should be serialized as an attribute element on the embed.
748
   */
749
  public static function escapeValue($element, FormStateInterface $form_state) {
750
    if ($value = trim($element['#value'])) {
751
      $form_state->setValueForElement($element, Html::escape($value));
752
    }
753
  }
754
755
  /**
756
   * Returns the allowed Entity Embed Display plugins given an embed button and
757
   * an entity.
758
   *
759
   * @param \Drupal\embed\EmbedButtonInterface $embed_button
760
   *   The embed button.
761
   * @param \Drupal\Core\Entity\EntityInterface $entity
762
   *   The entity.
763
   *
764
   * @return array
765
   *   List of allowed Entity Embed Display plugins.
766
   */
767
  public function getDisplayPluginOptions(EmbedButtonInterface $embed_button, EntityInterface $entity) {
768
    $plugins = $this->displayPluginManager->getDefinitionOptionsForEntity($entity);
769
770
    if ($allowed_plugins = $embed_button->getTypeSetting('display_plugins')) {
771
      $plugins = array_intersect_key($plugins, array_flip($allowed_plugins));
772
    }
773
774
    natsort($plugins);
775
    return $plugins;
776
  }
777
778
  /**
779
   * Registers JS callback that gets entities from entity browser and updates
780
   * form values accordingly.
781
   */
782
  public function registerJSCallback(RegisterJSCallbacks $event) {
783
    if ($event->getBrowserID() == $this->entityBrowser->id()) {
784
      $event->registerCallback('Drupal.entityEmbedDialog.selectionCompleted');
785
    }
786
  }
787
788
  /**
789
   * Load the current entity browser and its settings from the form state.
790
   *
791
   * @param \Drupal\Core\Form\FormStateInterface $form_state
792
   */
793
  protected function loadEntityBrowser(FormStateInterface $form_state) {
794
    $this->entityBrowser = NULL;
795
    $this->entityBrowserSettings = [];
796
797
    /** @var \Drupal\embed\EmbedButtonInterface $embed_button */
798
    $embed_button = $form_state->get('embed_button');
799
800
    if ($embed_button && $entity_browser_id = $embed_button->getTypePlugin()->getConfigurationValue('entity_browser')) {
801
      $this->entityBrowser = $this->entityTypeManager->getStorage('entity_browser')->load($entity_browser_id);
802
      $this->entityBrowserSettings = $embed_button->getTypePlugin()->getConfigurationValue('entity_browser_settings');
803
    }
804
  }
805
806
}
807