Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 163
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
StateButtons
0.00% covered (danger)
0.00%
0 / 81
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 163
0.00% covered (danger)
0.00%
0 / 9
812
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 3
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
 build
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 onPublish
0.00% covered (danger)
0.00%
0 / 1
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
 buildStateButtons
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 1
72
 hasButtons
0.00% covered (danger)
0.00%
0 / 14
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
 isOverridden
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 buildPublishButton
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 buildRestoreButton
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 buildRevertButton
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Entity\FieldableEntityInterface;
8use Drupal\Core\Extension\ModuleHandlerInterface;
9use Drupal\Core\StringTranslation\TranslatableMarkup;
10use Drupal\display_builder\Attribute\Island;
11use Drupal\display_builder\InstanceInterface;
12use Drupal\display_builder\Island\IslandPluginToolbarButtonConfigurationBase;
13use Drupal\display_builder\Island\IslandReloadEventsTrait;
14use Drupal\display_builder\Island\IslandType;
15use Drupal\display_builder_entity_view\Plugin\display_builder\Buildable\EntityViewOverride;
16use Symfony\Component\DependencyInjection\ContainerInterface;
17
18/**
19 * State buttons island plugin implementation.
20 */
21#[Island(
22  id: 'state',
23  enabled_by_default: TRUE,
24  label: new TranslatableMarkup('State'),
25  description: new TranslatableMarkup('Publish and reset the display.'),
26  type: IslandType::Button,
27  default_region: 'end',
28)]
29class StateButtons extends IslandPluginToolbarButtonConfigurationBase {
30
31  use IslandReloadEventsTrait;
32
33  /**
34   * The module handler.
35   */
36  protected ModuleHandlerInterface $moduleHandler;
37
38  /**
39   * {@inheritdoc}
40   */
41  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
42    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
43    $instance->moduleHandler = $container->get('module_handler');
44
45    return $instance;
46  }
47
48  /**
49   * {@inheritdoc}
50   */
51  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
52    if (!$builder->isPublishable()) {
53      return [];
54    }
55
56    $buttons = $this->buildStateButtons($builder);
57
58    if (empty($buttons)) {
59      return [];
60    }
61
62    return [
63      '#type' => 'component',
64      '#component' => 'display_builder:button_group',
65      '#slots' => [
66        'buttons' => $buttons,
67      ],
68    ];
69  }
70
71  /**
72   * {@inheritdoc}
73   */
74  public function onPublish(InstanceInterface $instance): array {
75    return $this->reloadWithGlobalData($instance);
76  }
77
78  /**
79   * Build state buttons.
80   *
81   * @param \Drupal\display_builder\InstanceInterface $instance
82   *   The current display builder instance.
83   *
84   * @return array
85   *   A renderable array of buttons.
86   */
87  protected function buildStateButtons(InstanceInterface $instance): array {
88    $instance_d = (string) $instance->id();
89    $buttons = [];
90    $hasSave = $instance->isPublished();
91    $saveIsCurrent = $hasSave ? $instance->isPublishedPresent() : FALSE;
92
93    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
94      $buttons[] = $this->htmxEvents->onPublish($this->buildPublishButton(), $instance_d);
95    }
96
97    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
98      $buttons[] = $this->htmxEvents->onReset($this->buildRestoreButton(), $instance_d);
99    }
100
101    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
102      $buttons[] = $this->htmxEvents->onRevert($this->buildRevertButton(), $instance_d);
103    }
104
105    return $buttons;
106  }
107
108  /**
109   * {@inheritdoc}
110   */
111  protected function hasButtons(): array {
112    return [
113      'publish' => [
114        'title' => $this->t('Publish'),
115        'default' => 'label',
116      ],
117      'restore' => [
118        'title' => $this->t('Restore'),
119        'default' => 'icon',
120      ],
121      'revert' => [
122        'title' => $this->t('Revert'),
123        'default' => 'icon',
124      ],
125    ];
126  }
127
128  /**
129   * Check if the display builder is on an entity override.
130   *
131   * @param string $builder_id
132   *   The ID of the builder.
133   *
134   * @return bool
135   *   Returns TRUE if the display builder is on an entity override.
136   */
137  protected function isOverridden(string $builder_id): bool {
138    if (!$this->moduleHandler->moduleExists('display_builder_entity_view')) {
139      return FALSE;
140    }
141
142    $instanceInfos = EntityViewOverride::checkInstanceId($builder_id);
143
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
145      return FALSE;
146    }
147
148    // Do not get the profile entity ID from Instance context because the
149    // data stored there is not reliable yet.
150    // See: https://www.drupal.org/project/display_builder/issues/3544545
151    $entity = $this->entityTypeManager->getStorage($instanceInfos['entity_type_id'])
152      ->load($instanceInfos['entity_id']);
153
154    if (!($entity instanceof FieldableEntityInterface)) {
155      return FALSE;
156    }
157
158    $overriddenField = $entity->get($instanceInfos['field_name']);
159
160    if ($overriddenField->isEmpty()) {
161      return FALSE;
162    }
163
164    return TRUE;
165  }
166
167  /**
168   * Builds the publish button.
169   *
170   * @return array
171   *   The publish button render array.
172   */
173  private function buildPublishButton(): array {
174    $button = $this->buildButton(
175      $this->showLabel('publish') ? $this->t('Publish') : '',
176      'publish',
177      $this->showIcon('publish') ? 'upload' : '',
178      $this->t('Publish this display in current state. (shortcut: P)'), ['P' => $this->t('Publish this display (shift+P)')]
179    );
180    $button['#props']['variant'] = 'primary';
181    $button['#attributes']['outline'] = TRUE;
182
183    return $button;
184  }
185
186  /**
187   * Builds the restore button.
188   *
189   * @return array
190   *   The restore button render array.
191   */
192  private function buildRestoreButton(): array {
193    $button = $this->buildButton(
194      $this->showLabel('restore') ? $this->t('Restore') : '',
195      'restore',
196      $this->showIcon('restore') ? 'arrow-repeat' : '',
197      $this->t('Restore to last saved version')
198    );
199    $button['#props']['variant'] = 'warning';
200    $button['#attributes']['outline'] = TRUE;
201
202    return $button;
203  }
204
205  /**
206   * Builds the revert button.
207   *
208   * @return array
209   *   The revert button render array.
210   */
211  private function buildRevertButton(): array {
212    $button = $this->buildButton(
213      $this->showLabel('revert') ? $this->t('Revert') : '',
214      'revert',
215      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
216      $this->t('Revert to default display (not overridden)')
217    );
218    $button['#props']['variant'] = 'danger';
219    $button['#attributes']['outline'] = TRUE;
220
221    return $button;
222  }
223
224}