Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 99
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 / 80
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 99
0.00% covered (danger)
0.00%
0 / 9
756
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 / 10
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
56
 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    $saveIsCurrent = $instance->isPublishedPresent();
91
92    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
93      $buttons[] = $this->htmxEvents->onPublish($this->buildPublishButton(), $instance_d);
94    }
95
96    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
97      $buttons[] = $this->htmxEvents->onReset($this->buildRestoreButton(), $instance_d);
98    }
99
100    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
101      $buttons[] = $this->htmxEvents->onRevert($this->buildRevertButton(), $instance_d);
102    }
103
104    return $buttons;
105  }
106
107  /**
108   * {@inheritdoc}
109   */
110  protected function hasButtons(): array {
111    return [
112      'publish' => [
113        'title' => $this->t('Publish'),
114        'default' => 'label',
115      ],
116      'restore' => [
117        'title' => $this->t('Restore'),
118        'default' => 'icon',
119      ],
120      'revert' => [
121        'title' => $this->t('Revert'),
122        'default' => 'icon',
123      ],
124    ];
125  }
126
127  /**
128   * Check if the display builder is on an entity override.
129   *
130   * @param string $builder_id
131   *   The ID of the builder.
132   *
133   * @return bool
134   *   Returns TRUE if the display builder is on an entity override.
135   */
136  protected function isOverridden(string $builder_id): bool {
137    if (!$this->moduleHandler->moduleExists('display_builder_entity_view')) {
138      return FALSE;
139    }
140
141    $instanceInfos = EntityViewOverride::checkInstanceId($builder_id);
142
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144      return FALSE;
145    }
146
147    // Do not get the profile entity ID from Instance context because the
148    // data stored there is not reliable yet.
149    // See: https://www.drupal.org/project/display_builder/issues/3544545
150    $entity = $this->entityTypeManager->getStorage($instanceInfos['entity_type_id'])
151      ->load($instanceInfos['entity_id']);
152
153    if (!($entity instanceof FieldableEntityInterface)) {
154      return FALSE;
155    }
156
157    $overriddenField = $entity->get($instanceInfos['field_name']);
158
159    if ($overriddenField->isEmpty()) {
160      return FALSE;
161    }
162
163    return TRUE;
164  }
165
166  /**
167   * Builds the publish button.
168   *
169   * @return array
170   *   The publish button render array.
171   */
172  private function buildPublishButton(): array {
173    $button = $this->buildButton(
174      $this->showLabel('publish') ? $this->t('Publish') : '',
175      'publish',
176      $this->showIcon('publish') ? 'upload' : '',
177      $this->t('Publish this display in current state. (shortcut: Shift+P)'), ['shift+p' => $this->t('Publish this display')]
178    );
179    $button['#props']['variant'] = 'primary';
180    $button['#attributes']['outline'] = TRUE;
181
182    return $button;
183  }
184
185  /**
186   * Builds the restore button.
187   *
188   * @return array
189   *   The restore button render array.
190   */
191  private function buildRestoreButton(): array {
192    $button = $this->buildButton(
193      $this->showLabel('restore') ? $this->t('Restore') : '',
194      'restore',
195      $this->showIcon('restore') ? 'arrow-repeat' : '',
196      $this->t('Restore to last saved version')
197    );
198    $button['#props']['variant'] = 'warning';
199    $button['#attributes']['outline'] = TRUE;
200
201    return $button;
202  }
203
204  /**
205   * Builds the revert button.
206   *
207   * @return array
208   *   The revert button render array.
209   */
210  private function buildRevertButton(): array {
211    $button = $this->buildButton(
212      $this->showLabel('revert') ? $this->t('Revert') : '',
213      'revert',
214      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
215      $this->t('Revert to default display (not overridden)')
216    );
217    $button['#props']['variant'] = 'danger';
218    $button['#attributes']['outline'] = TRUE;
219
220    return $button;
221  }
222
223}