EntityEmbedDialog   C
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 805
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 805
rs 5
c 0
b 0
f 0
wmc 68
lcom 1
cbo 1

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getFormId() 0 3 1
A submitForm() 0 1 1
A __construct() 0 8 1
A create() 0 10 1
C buildForm() 0 58 12
B buildSelectStep() 0 77 6
B buildReviewStep() 0 44 1
F buildEmbedStep() 0 143 12
A validateForm() 0 10 3
B validateSelectStep() 0 40 6
A validateEmbedStep() 0 13 3
A updatePluginConfigurationForm() 0 3 1
A submitStep() 0 12 1
A submitSelectStep() 0 23 3
A submitAndShowSelect() 0 3 1
A submitAndShowReview() 0 3 1
A submitAndShowEmbed() 0 3 1
B submitEmbedStep() 0 45 4
A escapeValue() 0 5 2
A getDisplayPluginOptions() 0 10 2
A registerJSCallback() 0 5 2
A loadEntityBrowser() 0 12 3

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
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;
0 ignored issues
show
Documentation Bug introduced by
It seems like $entity_field_manager of type object<Drupal\Core\Entit...yFieldManagerInterface> is incompatible with the declared type object<Drupal\Core\Entity\EntityFieldManager> of property $entityFieldManager.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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
}
836