Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
20.06% covered (danger)
20.06%
72 / 359
26.81% covered (danger)
26.81%
37 / 138
4.70% covered (danger)
4.70%
7 / 149
18.18% covered (danger)
18.18%
4 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiController
20.06% covered (danger)
20.06%
72 / 359
26.81% covered (danger)
26.81%
37 / 138
4.70% covered (danger)
4.70%
7 / 149
18.18% covered (danger)
18.18%
4 / 22
4434.38
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
n/a
0 / 0
n/a
0 / 0
100.00% covered (success)
100.00%
1 / 1
1
 attachToRoot
57.58% covered (warning)
57.58%
19 / 33
78.95% covered (warning)
78.95%
15 / 19
13.33% covered (danger)
13.33%
2 / 15
0.00% covered (danger)
0.00%
0 / 1
49.66
 attachToSlot
46.15% covered (danger)
46.15%
18 / 39
57.89% covered (warning)
57.89%
11 / 19
6.67% covered (danger)
6.67%
1 / 15
0.00% covered (danger)
0.00%
0 / 1
60.03
 get
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 update
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
72
 thirdPartySettingsUpdate
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 paste
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 delete
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 saveAsPreset
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 save
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 restore
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 revert
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
 undo
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 redo
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 clear
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 attachPresetToRoot
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 attachPresetToSlot
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 dispatchDisplayBuilderEvent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateIslandForm
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
 responseMessageError
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 recursiveRefreshNodeId
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 cleanNodeId
83.33% covered (warning)
83.33%
5 / 6
72.73% covered (warning)
72.73%
8 / 11
9.09% covered (danger)
9.09%
1 / 11
0.00% covered (danger)
0.00%
0 / 1
23.78
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Controller;
6
7use Drupal\Component\Datetime\TimeInterface;
8use Drupal\Core\Entity\FieldableEntityInterface;
9use Drupal\Core\Form\FormAjaxException;
10use Drupal\Core\Form\FormState;
11use Drupal\Core\Form\FormStateInterface;
12use Drupal\Core\Plugin\Context\ContextInterface;
13use Drupal\Core\Render\RendererInterface;
14use Drupal\Core\StringTranslation\TranslatableMarkup;
15use Drupal\Core\TempStore\SharedTempStoreFactory;
16use Drupal\display_builder\Event\DisplayBuilderEvents;
17use Drupal\display_builder\InstanceInterface;
18use Drupal\display_builder\IslandPluginManagerInterface;
19use Drupal\display_builder\Plugin\display_builder\Island\ContextualFormPanel;
20use Drupal\display_builder\RenderableBuilderTrait;
21use Drupal\display_builder\SourceWithSlotsInterface;
22use Drupal\display_builder_entity_view\Plugin\display_builder\Buildable\EntityViewOverride;
23use Drupal\ui_patterns\SourcePluginBase;
24use Drupal\ui_patterns\SourcePluginManager;
25use Symfony\Component\DependencyInjection\Attribute\Autowire;
26use Symfony\Component\HttpFoundation\Request;
27use Symfony\Component\HttpFoundation\Session\SessionInterface;
28use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
29
30/**
31 * Returns responses for Display builder routes.
32 */
33class ApiController extends ApiControllerBase implements ApiControllerInterface {
34
35  use RenderableBuilderTrait;
36
37  public function __construct(
38    protected EventDispatcherInterface $eventDispatcher,
39    protected RendererInterface $renderer,
40    protected TimeInterface $time,
41    #[Autowire(service: 'tempstore.shared')]
42    protected SharedTempStoreFactory $sharedTempStoreFactory,
43    protected SessionInterface $session,
44    private IslandPluginManagerInterface $islandPluginManager,
45    #[Autowire(service: 'plugin.manager.ui_patterns_source')]
46    private SourcePluginManager $sourceManager,
47  ) {
48    parent::__construct($eventDispatcher, $renderer, $time, $sharedTempStoreFactory, $session);
49  }
50
51  /**
52   * {@inheritdoc}
53   */
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
58      $preset_id = (string) $request->request->get('preset_id');
59
60      return $this->attachPresetToRoot($display_builder_instance, $preset_id, $position);
61    }
62
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
76      }
77
78      $is_move = TRUE;
79    }
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
83      $node_id = $display_builder_instance->attachToRoot($position, $source_id, $data);
84    }
85    else {
86      $message = '[attachToRoot] Missing content (source_id, node_id or preset_id)';
87      $debug = [
88        'request' => $request->request->all(),
89        'instance' => $display_builder_instance->toArray(),
90      ];
91
92      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
93    }
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
106
107  /**
108   * {@inheritdoc}
109   */
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
115      $preset_id = (string) $request->request->get('preset_id');
116
117      return $this->attachPresetToSlot($display_builder_instance, $preset_id, $parent_id, $slot, $position);
118    }
119
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
136      }
137
138      $is_move = TRUE;
139    }
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
143      $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $source_id, $data);
144    }
145    else {
146      $message = $this->t('[attachToSlot] Missing content (component_id, block_id or node_id)');
147      $debug = [
148        'node_id' => $node_id,
149        'slot' => $slot,
150        'request' => $request->request->all(),
151        'instance' => $display_builder_instance->toArray(),
152      ];
153
154      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
155    }
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
169
170  /**
171   * {@inheritdoc}
172   */
173  public function get(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
174    $this->builder = $display_builder_instance;
175
176    return $this->dispatchDisplayBuilderEvent(
177      DisplayBuilderEvents::ON_ACTIVE,
178      $display_builder_instance->getNode($node_id),
179    );
180  }
181
182  /**
183   * {@inheritdoc}
184   */
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
190      $message = $this->t('[update] Missing payload!');
191      $debug = [
192        'node_id' => $node_id,
193        'request' => $request->request->all(),
194        'body' => $body,
195        'instance' => $display_builder_instance->toArray(),
196      ];
197
198      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
199    }
200
201    // Load the node to properly alter the form data into config data.
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
233    }
234    catch (FormAjaxException $e) {
235      throw $e;
236    }
237    catch (\Exception $e) {
238      $debug = [
239        'node_id' => $node_id,
240        'request' => $request->request->all(),
241        'form' => $form_state->getValues(),
242        'body' => $body,
243        'instance' => $display_builder_instance->toArray(),
244      ];
245
246      return $this->responseMessageError((string) $display_builder_instance->id(), $e->getMessage(), $debug);
247    }
248
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
256      /** @var \Drupal\display_builder\SourceWithSlotsInterface $new_source */
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
282
283  /**
284   * {@inheritdoc}
285   */
286  public function thirdPartySettingsUpdate(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $island_id): array {
287    $body = $request->getPayload()->all();
288
289    if (!isset($body['form_id'])) {
290      $message = $this->t('[thirdPartySettingsUpdate] Missing payload!');
291      $debug = [
292        'node_id' => $node_id,
293        'island_id' => $island_id,
294        'request' => $request->request->all(),
295        'body' => $body,
296        'instance' => $display_builder_instance->toArray(),
297      ];
298
299      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
300    }
301
302    $islandDefinition = $this->islandPluginManager->getDefinition($island_id);
303    // Load the instance to properly alter the form data into config data.
304    $node = $display_builder_instance->getNode($node_id);
305    unset($body['form_build_id'], $body['form_token'], $body['form_id']);
306
307    $form_state = new FormState();
308    // Default values are the existing values from the state.
309    $form_state->addBuildInfo('args', [
310      [
311        'island_id' => $island_id,
312        'builder_id' => (string) $display_builder_instance->id(),
313        'instance' => $node,
314      ],
315      [],
316    ]);
317
318    // phpcs:disable Drupal.Files.LineLength.TooLong
319    // @todo should context be injected for third party settings?
320    // $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
321    // phpcs:enable Drupal.Files.LineLength.TooLong
322    // The body received corresponds to raw form values.
323    // We need to set them in the form state to properly
324    // take them into account.
325    $form_state->setValues($body);
326
327    $formClass = ($islandDefinition['class'])::getFormClass();
328    $values = $this->validateIslandForm($formClass, $form_state);
329    // We update the state with the new data.
330    $display_builder_instance->setThirdPartySettings($node_id, $island_id, $values);
331    $display_builder_instance->save();
332
333    $this->builder = $display_builder_instance;
334    $this->islandId = $island_id;
335
336    return $this->dispatchDisplayBuilderEvent(
337      DisplayBuilderEvents::ON_UPDATE,
338      NULL,
339      $node_id,
340      NULL,
341    );
342  }
343
344  /**
345   * {@inheritdoc}
346   */
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
366        $is_paste_root = TRUE;
367        $display_builder_instance->attachToRoot(0, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
368      }
369      else {
370        $display_builder_instance->attachToSlot($parent_id, $slot_id, (int) $slot_position, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
371      }
372    }
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
383
384  /**
385   * {@inheritdoc}
386   */
387  public function delete(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
388    $parent_id = $display_builder_instance->getParentId($node_id);
389    $display_builder_instance->remove($node_id);
390    $display_builder_instance->save();
391    $this->builder = $display_builder_instance;
392
393    return $this->dispatchDisplayBuilderEvent(
394      DisplayBuilderEvents::ON_DELETE,
395      NULL,
396      $node_id,
397      $parent_id
398    );
399  }
400
401  /**
402   * {@inheritdoc}
403   */
404  public function saveAsPreset(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
405    $label = (string) $this->t('New preset');
406    $data = $display_builder_instance->getNode($node_id);
407    self::cleanNodeId($data);
408
409    $preset_storage = $this->entityTypeManager()->getStorage('pattern_preset');
410    $label = $request->headers->get('hx-prompt', $label) ?: $label;
411    // In HTTP headers, only ASCII is guaranteed to work but historically,
412    // HTTP has allowed header values with the ISO-8859-1 charset.
413    $label = \mb_convert_encoding($label, 'UTF-8', 'ISO-8859-1');
414    $preset = $preset_storage->create([
415      'id' => \uniqid(),
416      'label' => $label,
417      'status' => TRUE,
418      'description' => '',
419      'sources' => $data,
420    ]);
421    $preset->save();
422
423    $this->builder = $display_builder_instance;
424
425    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_PRESET_SAVE);
426  }
427
428  /**
429   * {@inheritdoc}
430   */
431  public function save(Request $request, InstanceInterface $display_builder_instance): array {
432    $display_builder_instance->setSave($display_builder_instance->getCurrentState());
433    $display_builder_instance->save();
434
435    $this->builder = $display_builder_instance;
436
437    return $this->dispatchDisplayBuilderEvent(
438      DisplayBuilderEvents::ON_SAVE,
439      $display_builder_instance->getContexts()
440    );
441  }
442
443  /**
444   * {@inheritdoc}
445   */
446  public function restore(Request $request, InstanceInterface $display_builder_instance): array {
447    $display_builder_instance->restore();
448    $display_builder_instance->save();
449
450    $this->builder = $display_builder_instance;
451
452    // @todo on history change is closest to a data change that we need here
453    // without any instance id. Perhaps we need a new event?
454    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
455  }
456
457  /**
458   * {@inheritdoc}
459   */
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
464      // Do not get the profile entity ID from Instance context because the
465      // data stored there is not reliable yet.
466      // See: https://www.drupal.org/project/display_builder/issues/3544545
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
471        // Remove the saved state as the field values will be deleted.
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
484          && $contexts['view_mode'] instanceof ContextInterface
485        ) {
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
506
507  /**
508   * {@inheritdoc}
509   */
510  public function undo(Request $request, InstanceInterface $display_builder_instance): array {
511    $display_builder_instance->undo();
512    $display_builder_instance->save();
513
514    $this->builder = $display_builder_instance;
515
516    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
517  }
518
519  /**
520   * {@inheritdoc}
521   */
522  public function redo(Request $request, InstanceInterface $display_builder_instance): array {
523    $display_builder_instance->redo();
524    $display_builder_instance->save();
525
526    $this->builder = $display_builder_instance;
527
528    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
529  }
530
531  /**
532   * {@inheritdoc}
533   */
534  public function clear(Request $request, InstanceInterface $display_builder_instance): array {
535    $display_builder_instance->clear();
536    $display_builder_instance->save();
537
538    $this->builder = $display_builder_instance;
539
540    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
541  }
542
543  /**
544   * Attach a pattern preset to root.
545   *
546   * Presets are "resolved" after attachment, so they are never moved around.
547   *
548   * @param \Drupal\display_builder\InstanceInterface $display_builder_instance
549   *   Display builder instance.
550   * @param string $preset_id
551   *   Pattern preset ID.
552   * @param int $position
553   *   Position.
554   *
555   * @return array
556   *   A renderable array.
557   */
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
566      $message = $this->t('[attachToRoot] Missing preset source_id data');
567      $debug = [
568        'preset_id' => $preset_id,
569        'position' => $position,
570        'data' => $data,
571        'instance' => $display_builder_instance->toArray(),
572      ];
573
574      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
575    }
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
590
591  /**
592   * Attach a pattern preset to a slot .
593   *
594   * Presets are "resolved" after attachment, so they are never moved around.
595   *
596   * @param \Drupal\display_builder\InstanceInterface $display_builder_instance
597   *   Display builder instance.
598   * @param string $preset_id
599   *   Pattern preset ID.
600   * @param string $parent_id
601   *   Parent instance ID.
602   * @param string $slot
603   *   Slot.
604   * @param int $position
605   *   Position.
606   *
607   * @return array
608   *   A renderable array.
609   */
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
618      $message = $this->t('[attachToSlot] Missing preset source_id data');
619      $debug = [
620        'preset_id' => $preset_id,
621        'parent_id' => $parent_id,
622        'slot' => $slot,
623        'position' => $position,
624        'data' => $data,
625        'instance' => $display_builder_instance->toArray(),
626      ];
627
628      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
629    }
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
645
646  /**
647   * Dispatches a display builder event.
648   *
649   * @param string $event_id
650   *   The event ID.
651   * @param array|null $data
652   *   The data.
653   * @param string|null $node_id
654   *   Optional instance ID.
655   * @param string|null $parent_id
656   *   Optional parent ID.
657   *
658   * @return array
659   *   A renderable array.
660   */
661  protected function dispatchDisplayBuilderEvent(
662    string $event_id,
663    ?array $data = NULL,
664    ?string $node_id = NULL,
665    ?string $parent_id = NULL,
666  ): array {
667    $event = $this->createEventWithEnabledIsland($event_id, $data, $node_id, $parent_id);
668    $this->saveSseData($event_id);
669
670    return $event->getResult();
671  }
672
673  /**
674   * Validates an island form.
675   *
676   * @param string $formClass
677   *   The form class.
678   * @param \Drupal\Core\Form\FormStateInterface $form_state
679   *   The form state.
680   *
681   * @return array
682   *   The validated values.
683   */
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
692        // We set a fake triggering element to avoid form API error.
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
703        $first_error = \reset($formErrors);
704
705        throw new \Exception((string) $first_error);
706      }
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
711        $first_error = \reset($formErrors);
712
713        throw new \Exception((string) $first_error);
714      }
715    }
716    catch (FormAjaxException $e) {
717      throw $e;
718    }
719    // Those values are the validated values, produced by the form.
720    // with all Form API processing.
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
725      unset($values['form_build_id'], $values['form_token'], $values['form_id']);
726    }
727
728    return $values;
729  }
730
731  /**
732   * Render an error message in the display builder.
733   *
734   * @param string $display_builder_instance_id
735   *   The builder ID.
736   * @param string|\Drupal\Core\StringTranslation\TranslatableMarkup $message
737   *   The error message.
738   * @param array $debug
739   *   The debug code related to the error.
740   *
741   * @return array
742   *   A renderable array.
743   */
744  private function responseMessageError(
745    string $display_builder_instance_id,
746    string|TranslatableMarkup $message,
747    array $debug,
748  ): array {
749    // Reduce verbosity.
750    unset($debug['request']['ajax_page_state']['libraries'], $debug['instance']['contexts'], $debug['instance']['past'], $debug['instance']['future'], $debug['instance']['save']);
751
752    $this->getLogger('display_builder')->error('@message <pre>@debug</pre>', [
753      '@message' => $message,
754      '@debug' => \print_r($debug, TRUE),
755    ]);
756
757    $message = new TranslatableMarkup('Error: @error, check logs for more details.', ['@error' => $message]);
758
759    return $this->buildError($display_builder_instance_id, $message, TRUE);
760  }
761
762  /**
763   * Recursively regenerate the node_id key.
764   *
765   * @param array $array
766   *   The array reference.
767   */
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
770      $array['node_id'] = \bin2hex(\random_bytes(8));
771    }
772
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
779
780  /**
781   * Recursively regenerate the node_id key.
782   *
783   * @param array $array
784   *   The array reference.
785   *
786   * @todo set as utils because clone in ExportForm.php?
787   */
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
801
802}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

ApiController->attachPresetToRoot
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
566      $message = $this->t('[attachToRoot] Missing preset source_id data');
567      $debug = [
568        'preset_id' => $preset_id,
569        'position' => $position,
570        'data' => $data,
571        'instance' => $display_builder_instance->toArray(),
572      ];
573
574      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
566      $message = $this->t('[attachToRoot] Missing preset source_id data');
567      $debug = [
568        'preset_id' => $preset_id,
569        'position' => $position,
570        'data' => $data,
571        'instance' => $display_builder_instance->toArray(),
572      ];
573
574      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
558  protected function attachPresetToRoot(InstanceInterface $display_builder_instance, string $preset_id, int $position): array {
559    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
560
561    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
562    $preset = $presetStorage->load($preset_id);
563    $data = $preset->getSources();
564
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
565    if (!isset($data['source_id']) || !isset($data['source'])) {
 
576    $node_id = $display_builder_instance->attachToRoot($position, $data['source_id'], $data['source']);
577
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
578    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
579      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
580    }
581    $display_builder_instance->save();
582    $this->builder = $display_builder_instance;
583
584    return $this->dispatchDisplayBuilderEvent(
585      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
586      NULL,
587      $node_id,
588    );
589  }
ApiController->attachPresetToSlot
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
618      $message = $this->t('[attachToSlot] Missing preset source_id data');
619      $debug = [
620        'preset_id' => $preset_id,
621        'parent_id' => $parent_id,
622        'slot' => $slot,
623        'position' => $position,
624        'data' => $data,
625        'instance' => $display_builder_instance->toArray(),
626      ];
627
628      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
618      $message = $this->t('[attachToSlot] Missing preset source_id data');
619      $debug = [
620        'preset_id' => $preset_id,
621        'parent_id' => $parent_id,
622        'slot' => $slot,
623        'position' => $position,
624        'data' => $data,
625        'instance' => $display_builder_instance->toArray(),
626      ];
627
628      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
610  protected function attachPresetToSlot(InstanceInterface $display_builder_instance, string $preset_id, string $parent_id, string $slot, int $position): array {
611    $presetStorage = $this->entityTypeManager()->getStorage('pattern_preset');
612
613    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
614    $preset = $presetStorage->load($preset_id);
615    $data = $preset->getSources();
616
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
617    if (!isset($data['source_id']) || !isset($data['source'])) {
 
630    $node_id = $display_builder_instance->attachToSlot($parent_id, $slot, $position, $data['source_id'], $data['source']);
631
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
 
632    foreach ($data['third_party_settings'] ?? [] as $provider => $settings) {
633      $display_builder_instance->setThirdPartySettings($node_id, $provider, $settings ?? []);
634    }
635
636    $display_builder_instance->save();
637    $this->builder = $display_builder_instance;
638
639    return $this->dispatchDisplayBuilderEvent(
640      DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
641      NULL,
642      $node_id,
643    );
644  }
ApiController->attachToRoot
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
58      $preset_id = (string) $request->request->get('preset_id');
59
60      return $this->attachPresetToRoot($display_builder_instance, $preset_id, $position);
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
 
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
 
65    if ($request->request->has('node_id')) {
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
76      }
77
78      $is_move = TRUE;
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
 
65    if ($request->request->has('node_id')) {
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
76      }
77
78      $is_move = TRUE;
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
 
65    if ($request->request->has('node_id')) {
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
76      }
77
78      $is_move = TRUE;
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
 
65    if ($request->request->has('node_id')) {
66      $node_id = (string) $request->request->get('node_id');
67
68      if (!$display_builder_instance->moveToRoot($node_id, $position)) {
69        $message = $this->t('[attachToRoot] moveToRoot failed with invalid data');
70        $debug = [
71          'request' => $request->request->all(),
72          'instance' => $display_builder_instance->toArray(),
73        ];
74
75        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
76      }
77
78      $is_move = TRUE;
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
80    elseif ($request->request->has('source_id')) {
81      $source_id = (string) $request->request->get('source_id');
82      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
94    $display_builder_instance->save();
95
96    $this->builder = $display_builder_instance;
97    // Let's refresh when we add new source to get the placeholder replacement.
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
98    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
99
100    return $this->dispatchDisplayBuilderEvent(
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
101      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
102      NULL,
103      $node_id,
104    );
105  }
54  public function attachToRoot(Request $request, InstanceInterface $display_builder_instance): array {
55    $position = (int) $request->request->get('position', 0);
56
57    if ($request->request->has('preset_id')) {
 
63    $is_move = FALSE;
64
65    if ($request->request->has('node_id')) {
 
80    elseif ($request->request->has('source_id')) {
 
86      $message = '[attachToRoot] Missing content (source_id, node_id or preset_id)';
87      $debug = [
88        'request' => $request->request->all(),
89        'instance' => $display_builder_instance->toArray(),
90      ];
91
92      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
ApiController->attachToSlot
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
115      $preset_id = (string) $request->request->get('preset_id');
116
117      return $this->attachPresetToSlot($display_builder_instance, $preset_id, $parent_id, $slot, $position);
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
 
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
 
123    if ($request->request->has('node_id')) {
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
136      }
137
138      $is_move = TRUE;
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
 
123    if ($request->request->has('node_id')) {
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
136      }
137
138      $is_move = TRUE;
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
 
123    if ($request->request->has('node_id')) {
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
136      }
137
138      $is_move = TRUE;
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
 
123    if ($request->request->has('node_id')) {
124      $node_id = (string) $request->request->get('node_id');
125
126      if (!$display_builder_instance->moveToSlot($node_id, $parent_id, $slot, $position)) {
127        $message = $this->t('[attachToSlot] moveToSlot failed with invalid data');
128        $debug = [
129          'node_id' => $node_id,
130          'slot' => $slot,
131          'request' => $request->request->all(),
132          'instance' => $display_builder_instance->toArray(),
133        ];
134
135        return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
136      }
137
138      $is_move = TRUE;
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
140    elseif ($request->request->has('source_id')) {
141      $source_id = (string) $request->request->get('source_id');
142      $data = $request->request->has('source') ? \json_decode((string) $request->request->get('source'), TRUE) : [];
 
156    $display_builder_instance->save();
157
158    $this->builder = $display_builder_instance;
159    // Let's refresh when we add new source to get the placeholder replacement.
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
 
160    $this->islandId = $is_move ? (string) $request->query->get('from', NULL) : NULL;
161
162    return $this->dispatchDisplayBuilderEvent(
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
 
163      $is_move ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
164      NULL,
165      $node_id,
166      $parent_id,
167    );
168  }
110  public function attachToSlot(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $slot): array {
111    $parent_id = $node_id;
112    $position = (int) $request->request->get('position', 0);
113
114    if ($request->request->has('preset_id')) {
 
120    $is_move = FALSE;
121
122    // First, we update the data state.
123    if ($request->request->has('node_id')) {
 
140    elseif ($request->request->has('source_id')) {
 
146      $message = $this->t('[attachToSlot] Missing content (component_id, block_id or node_id)');
147      $debug = [
148        'node_id' => $node_id,
149        'slot' => $slot,
150        'request' => $request->request->all(),
151        'instance' => $display_builder_instance->toArray(),
152      ];
153
154      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
ApiController->cleanNodeId
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
788  private static function cleanNodeId(array &$array): void {
789    unset($array['node_id']);
790
791    foreach ($array as $key => &$value) {
 
791    foreach ($array as $key => &$value) {
792      if (\is_array($value)) {
793        self::cleanNodeId($value);
794
795        if (isset($value['source_id'], $value['source']['value']) && empty($value['source']['value'])) {
796          unset($array[$key]);
797        }
798      }
799    }
800  }
ApiController->clear
534  public function clear(Request $request, InstanceInterface $display_builder_instance): array {
535    $display_builder_instance->clear();
536    $display_builder_instance->save();
537
538    $this->builder = $display_builder_instance;
539
540    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
541  }
ApiController->delete
387  public function delete(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
388    $parent_id = $display_builder_instance->getParentId($node_id);
389    $display_builder_instance->remove($node_id);
390    $display_builder_instance->save();
391    $this->builder = $display_builder_instance;
392
393    return $this->dispatchDisplayBuilderEvent(
394      DisplayBuilderEvents::ON_DELETE,
395      NULL,
396      $node_id,
397      $parent_id
398    );
399  }
ApiController->dispatchDisplayBuilderEvent
662    string $event_id,
663    ?array $data = NULL,
664    ?string $node_id = NULL,
665    ?string $parent_id = NULL,
666  ): array {
667    $event = $this->createEventWithEnabledIsland($event_id, $data, $node_id, $parent_id);
668    $this->saveSseData($event_id);
669
670    return $event->getResult();
671  }
ApiController->get
173  public function get(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
174    $this->builder = $display_builder_instance;
175
176    return $this->dispatchDisplayBuilderEvent(
177      DisplayBuilderEvents::ON_ACTIVE,
178      $display_builder_instance->getNode($node_id),
179    );
180  }
ApiController->paste
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
365      if ($parent_id === '__root__') {
366        $is_paste_root = TRUE;
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
365      if ($parent_id === '__root__') {
366        $is_paste_root = TRUE;
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
370        $display_builder_instance->attachToSlot($parent_id, $slot_id, (int) $slot_position, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
371      }
372    }
373    $display_builder_instance->save();
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
370        $display_builder_instance->attachToSlot($parent_id, $slot_id, (int) $slot_position, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
371      }
372    }
373    $display_builder_instance->save();
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
365      if ($parent_id === '__root__') {
366        $is_paste_root = TRUE;
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
365      if ($parent_id === '__root__') {
366        $is_paste_root = TRUE;
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
370        $display_builder_instance->attachToSlot($parent_id, $slot_id, (int) $slot_position, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
371      }
372    }
373    $display_builder_instance->save();
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
355      $source_id = $dataToCopy['source_id'];
356      // Use reference to ensure modifications by recursiveRefreshNodeId
357      // persist.
358      $data = &$dataToCopy['source'];
359
360      // Refresh nested node_ids.
361      self::recursiveRefreshNodeId($data);
362
363      // If no parent we are on root.
364      // @todo for duplicate and not parent root seems not detected and copy is inside the slot.
365      if ($parent_id === '__root__') {
 
370        $display_builder_instance->attachToSlot($parent_id, $slot_id, (int) $slot_position, $source_id, $data, $dataToCopy['third_party_settings'] ?? []);
371      }
372    }
373    $display_builder_instance->save();
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
347  public function paste(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $parent_id, string $slot_id, string $slot_position): array {
348    $this->builder = $display_builder_instance;
349    $dataToCopy = $display_builder_instance->getNode($node_id);
350
351    // Keep flag for move or attach to root.
352    $is_paste_root = FALSE;
353
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
354    if (isset($dataToCopy['source_id'], $dataToCopy['source'])) {
 
373    $display_builder_instance->save();
374
375    $this->builder = $display_builder_instance;
376
377    return $this->dispatchDisplayBuilderEvent(
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
 
378      $is_paste_root ? DisplayBuilderEvents::ON_MOVE : DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
379      NULL,
380      $parent_id,
381    );
382  }
ApiController->recursiveRefreshNodeId
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
770      $array['node_id'] = \bin2hex(\random_bytes(8));
771    }
772
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
774      if (\is_array($value)) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
770      $array['node_id'] = \bin2hex(\random_bytes(8));
771    }
772
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
774      if (\is_array($value)) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
770      $array['node_id'] = \bin2hex(\random_bytes(8));
771    }
772
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
770      $array['node_id'] = \bin2hex(\random_bytes(8));
771    }
772
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
774      if (\is_array($value)) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
774      if (\is_array($value)) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
768  private static function recursiveRefreshNodeId(array &$array): void {
769    if (isset($array['node_id'])) {
 
773    foreach ($array as &$value) {
 
773    foreach ($array as &$value) {
774      if (\is_array($value)) {
775        self::recursiveRefreshNodeId($value);
776      }
777    }
778  }
ApiController->redo
522  public function redo(Request $request, InstanceInterface $display_builder_instance): array {
523    $display_builder_instance->redo();
524    $display_builder_instance->save();
525
526    $this->builder = $display_builder_instance;
527
528    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
529  }
ApiController->responseMessageError
745    string $display_builder_instance_id,
746    string|TranslatableMarkup $message,
747    array $debug,
748  ): array {
749    // Reduce verbosity.
750    unset($debug['request']['ajax_page_state']['libraries'], $debug['instance']['contexts'], $debug['instance']['past'], $debug['instance']['future'], $debug['instance']['save']);
751
752    $this->getLogger('display_builder')->error('@message <pre>@debug</pre>', [
753      '@message' => $message,
754      '@debug' => \print_r($debug, TRUE),
755    ]);
756
757    $message = new TranslatableMarkup('Error: @error, check logs for more details.', ['@error' => $message]);
758
759    return $this->buildError($display_builder_instance_id, $message, TRUE);
760  }
ApiController->restore
446  public function restore(Request $request, InstanceInterface $display_builder_instance): array {
447    $display_builder_instance->restore();
448    $display_builder_instance->save();
449
450    $this->builder = $display_builder_instance;
451
452    // @todo on history change is closest to a data change that we need here
453    // without any instance id. Perhaps we need a new event?
454    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
455  }
ApiController->revert
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
486          $viewMode = $contexts['view_mode']->getContextValue();
487          $display_id = "{$instanceInfos['entity_type_id']}.{$entity->bundle()}.{$viewMode}";
488
489          /** @var \Drupal\display_builder\DisplayBuildableInterface|null $display */
490          $display = $this->entityTypeManager()->getStorage('entity_view_display')
491            ->load($display_id);
492
493          $sources = $display->getSources();
494          $display_builder_instance->setNewPresent($sources, 'Revert 2/2: retrieve existing data from config');
495          $display_builder_instance->save();
496        }
497      }
498    }
499
500    $this->builder = $display_builder_instance;
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
472        $display_builder_instance->setNewPresent([], 'Revert 1/2: clear overridden data and save');
473        $display_builder_instance->save();
474        $display_builder_instance->setSave($display_builder_instance->getCurrentState());
475
476        // Clear field value.
477        $field = $entity->get($instanceInfos['field_name']);
478        $field->setValue(NULL);
479        $entity->save();
480
481        $contexts = $display_builder_instance->get('contexts')->first()->getValue();
482
483        if (isset($contexts['view_mode'])
 
484          && $contexts['view_mode'] instanceof ContextInterface
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
467      $entity = $this->entityTypeManager()->getStorage($instanceInfos['entity_type_id'])
468        ->load($instanceInfos['entity_id']);
469
470      if ($entity instanceof FieldableEntityInterface) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
460  public function revert(Request $request, InstanceInterface $display_builder_instance): array {
461    $instanceInfos = EntityViewOverride::checkInstanceId((string) $display_builder_instance->id());
462
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
463    if (isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
 
500    $this->builder = $display_builder_instance;
501
502    // @todo on history change is closest to a data change that we need here
503    // without any instance id. Perhaps we need a new event?
504    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
505  }
ApiController->save
431  public function save(Request $request, InstanceInterface $display_builder_instance): array {
432    $display_builder_instance->setSave($display_builder_instance->getCurrentState());
433    $display_builder_instance->save();
434
435    $this->builder = $display_builder_instance;
436
437    return $this->dispatchDisplayBuilderEvent(
438      DisplayBuilderEvents::ON_SAVE,
439      $display_builder_instance->getContexts()
440    );
441  }
ApiController->saveAsPreset
404  public function saveAsPreset(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
405    $label = (string) $this->t('New preset');
406    $data = $display_builder_instance->getNode($node_id);
407    self::cleanNodeId($data);
408
409    $preset_storage = $this->entityTypeManager()->getStorage('pattern_preset');
410    $label = $request->headers->get('hx-prompt', $label) ?: $label;
411    // In HTTP headers, only ASCII is guaranteed to work but historically,
412    // HTTP has allowed header values with the ISO-8859-1 charset.
413    $label = \mb_convert_encoding($label, 'UTF-8', 'ISO-8859-1');
414    $preset = $preset_storage->create([
415      'id' => \uniqid(),
416      'label' => $label,
417      'status' => TRUE,
418      'description' => '',
419      'sources' => $data,
420    ]);
421    $preset->save();
422
423    $this->builder = $display_builder_instance;
424
425    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_PRESET_SAVE);
426  }
ApiController->thirdPartySettingsUpdate
286  public function thirdPartySettingsUpdate(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $island_id): array {
287    $body = $request->getPayload()->all();
288
289    if (!isset($body['form_id'])) {
 
290      $message = $this->t('[thirdPartySettingsUpdate] Missing payload!');
291      $debug = [
292        'node_id' => $node_id,
293        'island_id' => $island_id,
294        'request' => $request->request->all(),
295        'body' => $body,
296        'instance' => $display_builder_instance->toArray(),
297      ];
298
299      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
286  public function thirdPartySettingsUpdate(Request $request, InstanceInterface $display_builder_instance, string $node_id, string $island_id): array {
287    $body = $request->getPayload()->all();
288
289    if (!isset($body['form_id'])) {
 
302    $islandDefinition = $this->islandPluginManager->getDefinition($island_id);
303    // Load the instance to properly alter the form data into config data.
304    $node = $display_builder_instance->getNode($node_id);
305    unset($body['form_build_id'], $body['form_token'], $body['form_id']);
306
307    $form_state = new FormState();
308    // Default values are the existing values from the state.
309    $form_state->addBuildInfo('args', [
310      [
311        'island_id' => $island_id,
312        'builder_id' => (string) $display_builder_instance->id(),
313        'instance' => $node,
314      ],
315      [],
316    ]);
317
318    // phpcs:disable Drupal.Files.LineLength.TooLong
319    // @todo should context be injected for third party settings?
320    // $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
321    // phpcs:enable Drupal.Files.LineLength.TooLong
322    // The body received corresponds to raw form values.
323    // We need to set them in the form state to properly
324    // take them into account.
325    $form_state->setValues($body);
326
327    $formClass = ($islandDefinition['class'])::getFormClass();
328    $values = $this->validateIslandForm($formClass, $form_state);
329    // We update the state with the new data.
330    $display_builder_instance->setThirdPartySettings($node_id, $island_id, $values);
331    $display_builder_instance->save();
332
333    $this->builder = $display_builder_instance;
334    $this->islandId = $island_id;
335
336    return $this->dispatchDisplayBuilderEvent(
337      DisplayBuilderEvents::ON_UPDATE,
338      NULL,
339      $node_id,
340      NULL,
341    );
342  }
ApiController->undo
510  public function undo(Request $request, InstanceInterface $display_builder_instance): array {
511    $display_builder_instance->undo();
512    $display_builder_instance->save();
513
514    $this->builder = $display_builder_instance;
515
516    return $this->dispatchDisplayBuilderEvent(DisplayBuilderEvents::ON_HISTORY_CHANGE);
517  }
ApiController->update
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
190      $message = $this->t('[update] Missing payload!');
191      $debug = [
192        'node_id' => $node_id,
193        'request' => $request->request->all(),
194        'body' => $body,
195        'instance' => $display_builder_instance->toArray(),
196      ];
197
198      return $this->responseMessageError((string) $display_builder_instance->id(), $message, $debug);
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
205      unset($body['source']['form_build_id'], $body['source']['form_token'], $body['source']['form_id']);
206    }
207
208    if (isset($body['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
209      unset($body['form_build_id'], $body['form_token'], $body['form_id']);
210    }
211    $form_state = new FormState();
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
257      $new_source = $this->sourceManager->createInstance(
258        $node['source_id'],
259        SourcePluginBase::buildConfiguration('slot', $slot_definition, $data, [])
260      );
261      // We keep the slots values (which are not sent by the contextual form)
262      // instead of removing them.
263      $slots = $current_source->getSlotValues();
264
265      foreach ($slots as $slot_id => $slot) {
 
265      foreach ($slots as $slot_id => $slot) {
266        $data['source'] = $new_source->setSlotValue($slot_id, $slot);
267      }
268    }
269
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
185  public function update(Request $request, InstanceInterface $display_builder_instance, string $node_id): array {
186    $this->builder = $display_builder_instance;
187    $body = $request->getPayload()->all();
188
189    if (!isset($body['form_id'])) {
 
202    $node = $display_builder_instance->getNode($node_id);
203
204    if (isset($body['source']['form_build_id'])) {
 
208    if (isset($body['form_build_id'])) {
 
211    $form_state = new FormState();
212    // Default values are the existing values from the state.
213    $form_state->addBuildInfo('args', [
214      [
215        'island_id' => 'contextual_form',
216        'builder_id' => (string) $display_builder_instance->id(),
217        'instance' => $node,
218      ],
219      $display_builder_instance->getContexts(),
220    ]);
221    $form_state->setTemporaryValue('gathered_contexts', $display_builder_instance->getContexts());
222    // The body received corresponds to raw form values.
223    // We need to set them in the form state to properly
224    // take them into account.
225    $form_state->setValues($body);
226
227    $formClass = ContextualFormPanel::getFormClass();
228    $data = [];
229
230    try {
231      $values = $this->validateIslandForm($formClass, $form_state);
232      $data['source'] = $values;
 
249    $slot_definition = ['ui_patterns' => ['type_definition' => $this->sourceManager->getSlotPropType()]];
250    $current_source = $this->sourceManager->createInstance(
251      $node['source_id'],
252      SourcePluginBase::buildConfiguration('slot', $slot_definition, $node, [])
253    );
254
255    if ($current_source instanceof SourceWithSlotsInterface) {
 
270    $display_builder_instance->setSource($node_id, $node['source_id'], $data['source']);
271    $display_builder_instance->save();
272
273    $this->builder = $display_builder_instance;
274    $this->islandId = (string) $request->query->get('from', NULL);
275
276    return $this->dispatchDisplayBuilderEvent(
277      DisplayBuilderEvents::ON_UPDATE,
278      NULL,
279      $node_id,
280    );
281  }
234    catch (FormAjaxException $e) {
 
235      throw $e;
234    catch (FormAjaxException $e) {
 
237    catch (\Exception $e) {
 
239        'node_id' => $node_id,
240        'request' => $request->request->all(),
241        'form' => $form_state->getValues(),
242        'body' => $body,
243        'instance' => $display_builder_instance->toArray(),
244      ];
245
246      return $this->responseMessageError((string) $display_builder_instance->id(), $e->getMessage(), $debug);
ApiController->validateIslandForm
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
703        $first_error = \reset($formErrors);
704
705        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
711        $first_error = \reset($formErrors);
712
713        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
725      unset($values['form_build_id'], $values['form_token'], $values['form_id']);
726    }
727
728    return $values;
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
703        $first_error = \reset($formErrors);
704
705        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
711        $first_error = \reset($formErrors);
712
713        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
725      unset($values['form_build_id'], $values['form_token'], $values['form_id']);
726    }
727
728    return $values;
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
703        $first_error = \reset($formErrors);
704
705        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
711        $first_error = \reset($formErrors);
712
713        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
725      unset($values['form_build_id'], $values['form_token'], $values['form_id']);
726    }
727
728    return $values;
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
693        $form_state->setTriggeringElement([
694          '#type' => 'submit',
695          '#limit_validation_errors' => FALSE,
696          '#value' => (string) $this->t('Submit'),
697        ]);
698      }
699      $form = $formBuilder->buildForm($formClass, $form_state);
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
703        $first_error = \reset($formErrors);
704
705        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
711        $first_error = \reset($formErrors);
712
713        throw new \Exception((string) $first_error);
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
725      unset($values['form_build_id'], $values['form_token'], $values['form_id']);
726    }
727
728    return $values;
 
728    return $values;
729  }
684  private function validateIslandForm(string $formClass, FormStateInterface $form_state): array {
685    /** @var \Drupal\Core\Form\FormBuilder $formBuilder */
686    $formBuilder = $this->formBuilder();
687
688    try {
689      $triggering_element = $form_state->getTriggeringElement();
690
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
691      if (!$triggering_element && !isset($form_state->getValues()['_triggering_element_name'])) {
 
699      $form = $formBuilder->buildForm($formClass, $form_state);
700      $formErrors = $form_state->getErrors();
701
702      if (!empty($formErrors)) {
 
707      $formBuilder->validateForm($formClass, $form, $form_state);
708      $formErrors = $form_state->getErrors();
709
710      if (!empty($formErrors)) {
 
713        throw new \Exception((string) $first_error);
 
721    $values = $form_state->getValues();
722
723    // We clean the values from form API keys.
724    if (isset($values['form_build_id'])) {
 
728    return $values;
729  }
716    catch (FormAjaxException $e) {
 
717      throw $e;