Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
6.25% covered (danger)
6.25%
1 / 16
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiControllerBase
6.25% covered (danger)
6.25%
1 / 16
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
33.33% covered (danger)
33.33%
1 / 3
17.18
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
 createEventWithEnabledIsland
0.00% covered (danger)
0.00%
0 / 6
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
 saveSseData
0.00% covered (danger)
0.00%
0 / 9
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
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Controller;
6
7use Drupal\Component\Datetime\TimeInterface;
8use Drupal\Core\Controller\ControllerBase;
9use Drupal\Core\Render\RendererInterface;
10use Drupal\Core\TempStore\SharedTempStoreFactory;
11use Drupal\display_builder\Event\DisplayBuilderEvent;
12use Drupal\display_builder\Event\DisplayBuilderEvents;
13use Drupal\display_builder\InstanceInterface;
14use Drupal\display_builder\ProfileInterface;
15use Symfony\Component\DependencyInjection\Attribute\Autowire;
16use Symfony\Component\HttpFoundation\Session\SessionInterface;
17use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
18
19/**
20 * Returns responses for Display builder routes.
21 */
22abstract class ApiControllerBase extends ControllerBase {
23
24  public const string SSE_COLLECTION = 'display_builder_sse';
25
26  /**
27   * The list of DB events which triggers SSE refresh.
28   */
29  public const array SSE_EVENTS = [
30    DisplayBuilderEvents::ON_ATTACH_TO_ROOT,
31    DisplayBuilderEvents::ON_ATTACH_TO_SLOT,
32    DisplayBuilderEvents::ON_DELETE,
33    DisplayBuilderEvents::ON_HISTORY_CHANGE,
34    DisplayBuilderEvents::ON_MOVE,
35    DisplayBuilderEvents::ON_PRESET_SAVE,
36    DisplayBuilderEvents::ON_SAVE,
37    DisplayBuilderEvents::ON_UPDATE,
38  ];
39
40  /**
41   * The Display Builder instance triggering the action.
42   */
43  protected InstanceInterface $builder;
44
45  /**
46   * Plugin ID of the island triggering the HTMX event.
47   *
48   * If not NULL, the island will be skipped from the event dispatch. Useful to
49   * avoid swapping the content of an island which is already in the expected
50   * state. For examples, if we move an instance in Builder, Layers or Tree
51   * panels, if we change the settings in InstanceForm.
52   *
53   * @see \Drupal\display_builder\Event\DisplayBuilderEventsSubscriber::dispatchToIslands()
54   * @see \Drupal\display_builder\HtmxEvents
55   */
56  protected ?string $islandId = NULL;
57
58  /**
59   * The lazy loaded display builder.
60   */
61  protected ?ProfileInterface $displayBuilder = NULL;
62
63  public function __construct(
64    protected EventDispatcherInterface $eventDispatcher,
65    protected RendererInterface $renderer,
66    protected TimeInterface $time,
67    #[Autowire(service: 'tempstore.shared')]
68    protected SharedTempStoreFactory $sharedTempStoreFactory,
69    protected SessionInterface $session,
70  ) {}
71
72  /**
73   * Creates a display builder event with enabled islands only.
74   *
75   * Use a cache to avoid loading all the builder configuration.
76   *
77   * @param string $event_id
78   *   The event ID.
79   * @param array|null $data
80   *   The data.
81   * @param string|null $node_id
82   *   Optional Instance entity ID.
83   * @param string|null $parent_id
84   *   Optional parent ID.
85   *
86   * @return \Drupal\display_builder\Event\DisplayBuilderEvent
87   *   The event.
88   */
89  protected function createEventWithEnabledIsland($event_id, $data, $node_id, $parent_id): DisplayBuilderEvent {
90    $builder_id = (string) $this->builder->id();
91
92    $island_configuration = $this->builder->getProfile()->getIslandConfigurations();
93    $island_enabled = $this->builder->getProfile()->getEnabledIslands();
94
95    $event = new DisplayBuilderEvent($builder_id, $island_enabled, $island_configuration, $data, $node_id, $parent_id, $this->islandId);
96    $this->eventDispatcher->dispatch($event, $event_id);
97
98    return $event;
99  }
100
101  /**
102   * Save data for SSE.
103   *
104   * @param string $event_id
105   *   The event ID.
106   */
107  protected function saveSseData(string $event_id): void {
108    if (!\in_array($event_id, $this::SSE_EVENTS, TRUE)) {
109      return;
110    }
111
112    $state = [
113      'sessionId' => $this->session->getId(),
114      'timestamp' => $this->time->getRequestTime(),
115      // instanceId here is the ID of a display_builder_instance entity.
116      'instanceId' => (string) $this->builder->id(),
117    ];
118    $collection = $this->sharedTempStoreFactory->get($this::SSE_COLLECTION);
119    $collection->set(\sprintf('%s_latest', (string) $this->builder->id()), $state);
120  }
121
122}