Completed
Push — 8.x-1.x ( e3b4ed...18ef99 )
by Janez
03:17
created

EntityReference::updateWidgetCallback()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 6
Ratio 40 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
dl 6
loc 15
rs 8.8571
c 3
b 0
f 1
cc 5
eloc 7
nc 3
nop 2
1
<?php
2
3
/**
4
 * @file
5
 * Contains \Drupal\entity_browser\Plugin\Field\FieldWidget\EntityReference.
6
 */
7
8
namespace Drupal\entity_browser\Plugin\Field\FieldWidget;
9
10
use Drupal\Component\Utility\Html;
11
use Drupal\Component\Utility\NestedArray;
12
use Drupal\Core\Entity\ContentEntityInterface;
13
use Drupal\Core\Entity\EntityManagerInterface;
14
use Drupal\Core\Field\FieldDefinitionInterface;
15
use Drupal\Core\Field\FieldItemListInterface;
16
use Drupal\Core\Field\FieldStorageDefinitionInterface;
17
use Drupal\Core\Field\WidgetBase;
18
use Drupal\Core\Form\FormStateInterface;
19
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
20
use Drupal\Core\Url;
21
use Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint;
22
use Drupal\entity_browser\Events\Events;
23
use Drupal\entity_browser\Events\RegisterJSCallbacks;
24
use Drupal\entity_browser\FieldWidgetDisplayManager;
25
use Symfony\Component\DependencyInjection\ContainerInterface;
26
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
27
use Symfony\Component\Validator\ConstraintViolation;
28
use Symfony\Component\Validator\ConstraintViolationListInterface;
29
30
/**
31
 * Plugin implementation of the 'entity_reference' widget for entity browser.
32
33
 * @FieldWidget(
34
 *   id = "entity_browser_entity_reference",
35
 *   label = @Translation("Entity browser"),
36
 *   description = @Translation("Uses entity browser to select entities."),
37
 *   multiple_values = TRUE,
38
 *   field_types = {
39
 *     "entity_reference"
40
 *   }
41
 * )
42
 */
43
class EntityReference extends WidgetBase implements ContainerFactoryPluginInterface {
44
45
  /**
46
   * Entity manager service
47
   *
48
   * @var \Drupal\Core\Entity\EntityManagerInterface
49
   */
50
  protected $entityManager;
51
52
  /**
53
   * Field widget display plugin manager.
54
   *
55
   * @var \Drupal\entity_browser\FieldWidgetDisplayManager
56
   */
57
  protected $fieldDisplayManager;
58
59
  /**
60
   * The depth of the delete button.
61
   *
62
   * This property exists so it can be changed if subclasses
63
   *
64
   * @var int
65
   */
66
  protected static $deleteDepth = 4;
67
68
  /**
69
   * Constructs widget plugin.
70
   *
71
   * @param string $plugin_id
72
   *   The plugin_id for the plugin instance.
73
   * @param mixed $plugin_definition
74
   *   The plugin implementation definition.
75
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
76
   *   The definition of the field to which the widget is associated.
77
   * @param array $settings
78
   *   The widget settings.
79
   * @param array $third_party_settings
80
   *   Any third party settings.
81
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
82
   *   Entity manager service.
83
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
84
   *   Event dispatcher.
85
   * @param \Drupal\entity_browser\FieldWidgetDisplayManager $field_display_manager
86
   *   Field widget display plugin manager.
87
   */
88
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityManagerInterface $entity_manager, EventDispatcherInterface $event_dispatcher, FieldWidgetDisplayManager $field_display_manager) {
89
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
90
    $this->entityManager = $entity_manager;
91
    $this->fieldDisplayManager = $field_display_manager;
92
  }
93
94
  /**
95
   * {@inheritdoc}
96
   */
97
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
98
    return new static(
99
      $plugin_id,
100
      $plugin_definition,
101
      $configuration['field_definition'],
102
      $configuration['settings'],
103
      $configuration['third_party_settings'],
104
      $container->get('entity.manager'),
105
      $container->get('event_dispatcher'),
106
      $container->get('plugin.manager.entity_browser.field_widget_display')
107
    );
108
  }
109
110
  /**
111
   * {@inheritdoc}
112
   */
113
  public static function defaultSettings() {
114
    return array(
115
      'entity_browser' => NULL,
116
      'open' => FALSE,
117
      'field_widget_display' => NULL,
118
      'field_widget_edit' => TRUE,
119
      'field_widget_remove' => TRUE,
120
      'field_widget_display_settings' => [],
121
    ) + parent::defaultSettings();
122
  }
123
124
  /**
125
   * {@inheritdoc}
126
   */
127
  public function settingsForm(array $form, FormStateInterface $form_state) {
128
    $element = parent::settingsForm($form, $form_state);
129
130
    $browsers = [];
131
    /** @var \Drupal\entity_browser\EntityBrowserInterface $browser */
132
    foreach ($this->entityManager->getStorage('entity_browser')->loadMultiple() as $browser) {
133
      $browsers[$browser->id()] = $browser->label();
134
    }
135
136
    $element['entity_browser'] = [
137
      '#title' => t('Entity browser'),
138
      '#type' => 'select',
139
      '#default_value' => $this->getSetting('entity_browser'),
140
      '#options' => $browsers,
141
    ];
142
143
    $target_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
144
    $entity_type = \Drupal::entityTypeManager()->getStorage($target_type)->getEntityType();
145
146
    $displays = [];
147
    foreach ($this->fieldDisplayManager->getDefinitions() as $id => $definition) {
148
      if ($this->fieldDisplayManager->createInstance($id)->isApplicable($entity_type)) {
149
        $displays[$id] = $definition['label'];
150
      }
151
    }
152
153
    $id = Html::getUniqueId('field-' . $this->fieldDefinition->getName() . '-display-settings-wrapper');
154
    $element['field_widget_display'] = [
155
      '#title' => t('Entity display plugin'),
156
      '#type' => 'select',
157
      '#default_value' => $this->getSetting('field_widget_display'),
158
      '#options' => $displays,
159
      '#ajax' => [
160
        'callback' => array($this, 'updateSettingsAjax'),
161
        'wrapper' => $id,
162
      ],
163
    ];
164
165
    $element['field_widget_edit'] = [
166
      '#title' => t('Display Edit button'),
167
      '#type' => 'checkbox',
168
      '#default_value' => $this->getSetting('field_widget_edit')
169
    ];
170
171
    $element['field_widget_remove'] = [
172
      '#title' => t('Display Remove button'),
173
      '#type' => 'checkbox',
174
      '#default_value' => $this->getSetting('field_widget_remove')
175
    ];
176
177
    $element['open'] = [
178
      '#title' => t('Show widget details as open by default'),
179
      '#type' => 'checkbox',
180
      '#default_value' => $this->getSetting('open')
181
    ];
182
183
    $element['field_widget_display_settings'] = [
184
      '#type' => 'fieldset',
185
      '#title' => t('Entity display plugin configuration'),
186
      '#tree' => TRUE,
187
      '#prefix' => '<div id="' . $id . '">',
188
      '#suffix' => '</div>',
189
    ];
190
191
    if ($this->getSetting('field_widget_display')) {
192
      $element['field_widget_display_settings'] += $this->fieldDisplayManager
193
        ->createInstance(
194
          $form_state->getValue(
195
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display'],
196
            $this->getSetting('field_widget_display')
197
          ),
198
          $form_state->getValue(
199
            ['fields', $this->fieldDefinition->getName(), 'settings_edit_form', 'settings', 'field_widget_display_settings'],
200
            $this->getSetting('field_widget_display_settings')
201
          ) + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
202
        )
203
        ->settingsForm($form, $form_state);
204
    }
205
206
    return $element;
207
  }
208
209
  /**
210
   * Ajax callback that updates field widget display settings fieldset.
211
   */
212
  public function updateSettingsAjax(array $form, FormStateInterface $form_state) {
213
    return $form['fields'][$this->fieldDefinition->getName()]['plugin']['settings_edit_form']['settings']['field_widget_display_settings'];
214
  }
215
216
  /**
217
   * {@inheritdoc}
218
   */
219
  public function settingsSummary() {
220
    $summary = [];
221
    $entity_browser_id = $this->getSetting('entity_browser');
222
    $field_widget_display = $this->getSetting('field_widget_display');
223
224
    if (empty($entity_browser_id)) {
225
      return [t('No entity browser selected.')];
226
    }
227
    else {
228
      if ($browser = $this->entityManager->getStorage('entity_browser')->load($entity_browser_id)) {
229
        $summary[] = t('Entity browser: @browser', ['@browser' => $browser->label()]);
230
      } else {
231
        drupal_set_message(t('Missing entity browser!'), 'error');
232
        return [t('Missing entity browser!')];
233
      }
234
    }
235
236
    if (!empty($field_widget_display)) {
237
      $plugin = $this->fieldDisplayManager->getDefinition($field_widget_display);
238
      $summary[] = t('Entity display: @name', ['@name' => $plugin['label']]);
239
    }
240
241
    return $summary;
242
  }
243
244
  /**
245
   * {@inheritdoc}
246
   */
247
  public function flagErrors(FieldItemListInterface $items, ConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
248
    if ($violations->count() > 0) {
249
      /** @var \Symfony\Component\Validator\ConstraintViolation $violation */
250
      foreach ($violations as $offset => $violation) {
251
        // The value of the required field is checked through the "not null"
252
        // constraint, whose message is not very useful. We override it here for
253
        // better UX.
254
        if ($violation->getConstraint() instanceof NotNullConstraint) {
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Validation\P...raint\NotNullConstraint does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
255
          $violations->set($offset, new ConstraintViolation(
256
            $this->t('@name field is required.', ['@name' => $items->getFieldDefinition()->getLabel()]),
257
            '',
258
            [],
259
            $violation->getRoot(),
260
            $violation->getPropertyPath(),
261
            $violation->getInvalidValue(),
262
            $violation->getPlural(),
263
            $violation->getCode(),
264
            $violation->getConstraint(),
265
            $violation->getCause()
266
          ));
267
        }
268
      }
269
    }
270
271
    parent::flagErrors($items, $violations, $form, $form_state);
272
  }
273
274
  /**
275
   * Returns a key used to store the previously loaded entity.
276
   *
277
   * @param \Drupal\Core\Field\FieldItemListInterface $items
278
   *   The field items.
279
   *
280
   * @return string
281
   *   A key for form state storage.
282
   */
283
  protected function getFormStateKey(FieldItemListInterface $items) {
284
    return $items->getEntity()->uuid() . ':' . $items->getFieldDefinition()->getName();
285
  }
286
287
  /**
288
   * {@inheritdoc}
289
   */
290
  function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
291
    $entity_type = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type');
292
    $entity_storage = $this->entityManager->getStorage($entity_type);
293
294
    $ids = [];
295
    $entities = [];
296
297
    // Determine if we're submitting and if submit came from this widget.
298
    $is_relevant_submit = FALSE;
299
    if (($trigger = $form_state->getTriggeringElement())) {
300
      // Can be triggered by hidden target_id element or "Remove" button.
301
      if (end($trigger['#parents']) === 'target_id' || (end($trigger['#parents']) === 'remove_button')) {
302
        $is_relevant_submit = TRUE;
303
304
        // In case there are more instances of this widget on the same page we
305
        // need to check if submit came from this instance.
306
        $field_name_key = end($trigger['#parents']) === 'target_id' ? 2 : static::$deleteDepth + 1;
307
        $field_name_key = sizeof($trigger['#parents']) - $field_name_key;
308
        $is_relevant_submit &= ($trigger['#parents'][$field_name_key] === $this->fieldDefinition->getName()) &&
309
          (array_slice($trigger['#parents'], 0, count($element['#field_parents'])) == $element['#field_parents']);
310
      }
311
    };
312
313
    if ($is_relevant_submit) {
314
      // Submit was triggered by hidden "target_id" element when entities were
315
      // added via entity browser.
316 View Code Duplication
      if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
317
        $parents = $trigger['#parents'];
318
      }
319
      // Submit was triggered by one of the "Remove" buttons. We need to walk
320
      // few levels up to read value of "target_id" element.
321
      elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], $this->fieldDefinition->getName() . '_remove_') === 0) {
322
        $parents = array_merge(array_slice($trigger['#parents'], 0, -static::$deleteDepth), ['target_id']);
323
      }
324
325
      if (isset($parents) && $value = $form_state->getValue($parents)) {
326
        $ids = explode(' ', $value);
327
        $entities = $entity_storage->loadMultiple($ids);
328
      }
329
    }
330
    // IDs from a previous request might be saved in the form state.
331
    elseif ($form_state->has(['entity_browser_widget', $this->getFormStateKey($items)])) {
332
      $ids = $form_state->get(['entity_browser_widget', $this->getFormStateKey($items)]);
333
      $entities = $entity_storage->loadMultiple($ids);
334
    }
335
    // We are loading for for the first time so we need to load any existing
336
    // values that might already exist on the entity. Also, remove any leftover
337
    // data from removed entity references.
338
    else {
339
      foreach ($items as $item) {
340
        if (isset($item->target_id)) {
341
          $entity = $entity_storage->load($item->target_id);
342
          if (!empty($entity)) {
343
            $entities[$item->target_id] = $entity;
344
          }
345
        }
346
      }
347
      $ids = array_keys($entities);
348
    }
349
    $ids = array_filter($ids);
350
    // We store current entity IDs as we might need them in future requests. If
351
    // some other part of the form triggers an AJAX request with #limit_validation_errors
352
    // we won't have access to the value of the target_id element and won't be
353
    // able to build the form as a result of that. This will cause missing
354
    // submit (Remove, Edit, ...) elements, which might result in unpredictable
355
    // results.
356
    $form_state->set(['entity_browser_widget', $this->getFormStateKey($items)], $ids);
357
358
    $hidden_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName() . '-target-id');
359
    $details_id = Html::getUniqueId('edit-' . $this->fieldDefinition->getName());
360
    /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */
361
    $entity_browser = $this->entityManager->getStorage('entity_browser')->load($this->getSetting('entity_browser'));
362
363
    $element += [
364
      '#id' => $details_id,
365
      '#type' => 'details',
366
      '#open' => !empty($ids) || $this->getSetting('open'),
367
      '#required' => $this->fieldDefinition->isRequired(),
368
      'target_id' => [
369
        '#type' => 'hidden',
370
        '#id' => $hidden_id,
371
        // We need to repeat ID here as it is otherwise skipped when rendering.
372
        '#attributes' => ['id' => $hidden_id],
373
        '#default_value' => $ids,
374
        // #ajax is officially not supported for hidden elements but if we
375
        // specify event manually it works.
376
        '#ajax' => [
377
          'callback' => [get_class($this), 'updateWidgetCallback'],
378
          'wrapper' => $details_id,
379
          'event' => 'entity_browser_value_updated',
380
        ],
381
      ],
382
    ];
383
384
    $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality();
385
    if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED || count($ids) < $cardinality) {
386
      $entity_browser_uuid = sha1(implode('-', array_merge($form['#parents'], [$this->fieldDefinition->getName(), $delta])));
387
      $entity_browser_display = $entity_browser->getDisplay();
388
      $entity_browser_display->setUuid($entity_browser_uuid);
389
      $element['entity_browser'] = $entity_browser_display->displayEntityBrowser($form_state);
390
      $element['#attached']['library'][] = 'entity_browser/entity_reference';
391
      $element['#attached']['drupalSettings']['entity_browser'] = [
392
        $entity_browser->getDisplay()->getUuid() => [
393
          'cardinality' => $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(),
394
          'selector' => '#'.$element['target_id']['#attributes']['id'],
395
        ]
396
      ];
397
    }
398
399
    $field_parents = $element['#field_parents'];
400
401
    $element['current'] = $this->displayCurrentSelection($details_id, $field_parents, $entities);
402
403
    return $element;
404
  }
405
406
  /**
407
   * {@inheritdoc}
408
   */
409
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
410
    $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id']));
411
    $return = [];
412
    foreach ($ids as $id) {
413
      $return[]['target_id'] = $id;
414
    }
415
416
    return $return;
417
  }
418
419
  /**
420
   * AJAX form callback.
421
   */
422
  public static function updateWidgetCallback(array &$form, FormStateInterface $form_state) {
423
    $trigger = $form_state->getTriggeringElement();
424
    // AJAX requests can be triggered by hidden "target_id" element when entities
425
    // are added or by one of the "Remove" buttons. Depending on that we need to
426
    // figure out where root of the widget is in the form structure and use this
427
    // information to return correct part of the form.
428 View Code Duplication
    if (!empty($trigger['#ajax']['event']) && $trigger['#ajax']['event'] == 'entity_browser_value_updated') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
429
      $parents = array_slice($trigger['#array_parents'], 0, -1);
430
    }
431
    elseif ($trigger['#type'] == 'submit' && strpos($trigger['#name'], '_remove_')) {
432
      $parents = array_slice($trigger['#array_parents'], 0, -static::$deleteDepth);
433
    }
434
435
    return NestedArray::getValue($form, $parents);
0 ignored issues
show
Bug introduced by
The variable $parents does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
436
  }
437
438
  /**
439
   * {@inheritdoc}
440
   */
441
  public function errorElement(array $element, \Symfony\Component\Validator\ConstraintViolationInterface $violation, array $form, \Drupal\Core\Form\FormStateInterface $form_state) {
442
    if (($trigger = $form_state->getTriggeringElement())) {
443
      // Can be triggered by "Remove" button.
444
      if (end($trigger['#parents']) === 'remove_button') {
445
        return FALSE;
446
      }
447
    }
448
  }
449
450
  /**
451
   * Submit callback for remove buttons.
452
   */
453
  public static function removeItemSubmit(&$form, FormStateInterface $form_state) {
454
    $triggering_element = $form_state->getTriggeringElement();
455
    if (!empty($triggering_element['#attributes']['data-entity-id'])) {
456
      $id = $triggering_element['#attributes']['data-entity-id'];
457
      $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
458
      $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
459
460
      // Find and remove correct entity.
461
      $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
462
      $values = array_filter(
463
        $values,
464
        function($item) use ($id) { return $item != $id; }
465
      );
466
      $values = implode(' ', $values);
467
468
      // Set new value for this widget.
469
      $target_id_element = &NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
470
      $form_state->setValueForElement($target_id_element, $values);
471
      NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $values);
472
473
      // Rebuild form.
474
      $form_state->setRebuild();
475
    }
476
  }
477
478
  /**
479
   * Builds the render array for displaying the current results.
480
   *
481
   * @param string $details_id
482
   *   The ID for the details element.
483
   * @param string[] $field_parents
484
   *   Field parents.
485
   * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities
486
   *
487
   * @return array
488
   *   The render array for the current selection.
489
   */
490
  protected function displayCurrentSelection($details_id, $field_parents, $entities) {
491
492
    $field_widget_display = $this->fieldDisplayManager->createInstance(
493
      $this->getSetting('field_widget_display'),
494
      $this->getSetting('field_widget_display_settings') + ['entity_type' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('target_type')]
495
    );
496
497
    return [
498
      '#theme_wrappers' => ['container'],
499
      '#attributes' => ['class' => ['entities-list']],
500
      'items' => array_map(
501
        function (ContentEntityInterface $entity) use ($field_widget_display, $details_id, $field_parents) {
502
          $display = $field_widget_display->view($entity);
503
          if (is_string($display)) {
504
            $display = ['#markup' => $display];
505
          }
506
          return [
507
            '#theme_wrappers' => ['container'],
508
            '#attributes' => [
509
              'class' => ['item-container', Html::getClass($field_widget_display->getPluginId())],
510
              'data-entity-id' => $entity->id()
511
            ],
512
            'display' => $display,
513
            'remove_button' => [
514
              '#type' => 'submit',
515
              '#value' => $this->t('Remove'),
516
              '#ajax' => [
517
                'callback' => [get_class($this), 'updateWidgetCallback'],
518
                'wrapper' => $details_id,
519
              ],
520
              '#submit' => [[get_class($this), 'removeItemSubmit']],
521
              '#name' => $this->fieldDefinition->getName() . '_remove_' . $entity->id(),
522
              '#limit_validation_errors' => [array_merge($field_parents, [$this->fieldDefinition->getName()])],
523
              '#attributes' => ['data-entity-id' => $entity->id()],
524
              '#access' => (bool) $this->getSetting('field_widget_remove')
525
            ],
526
            'edit_button' => [
527
              '#type' => 'submit',
528
              '#value' => $this->t('Edit'),
529
              '#ajax' => [
530
                'url' => Url::fromRoute(
531
                  'entity_browser.edit_form', [
532
                  'entity_type' => $entity->getEntityTypeId(),
533
                  'entity' => $entity->id()
534
                ]
535
                )
536
              ],
537
              '#access' => (bool) $this->getSetting('field_widget_edit')
538
            ]
539
          ];
540
        },
541
        $entities
542
      ),
543
    ];
544
  }
545
}
546