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