Completed
Push — 8.x-1.x ( 08e509...63ce40 )
by Janez
02:20
created

EntityEmbedDialog::validateSelectStep()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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