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}

Branches

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

StateButtons->build
51  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
52    if (!$builder->isPublishable()) {
53      return [];
56    $buttons = $this->buildStateButtons($builder);
57
58    if (empty($buttons)) {
59      return [];
63      '#type' => 'component',
64      '#component' => 'display_builder:button_group',
65      '#slots' => [
66        'buttons' => $buttons,
67      ],
68    ];
69  }
StateButtons->buildPublishButton
174    $button = $this->buildButton(
175      $this->showLabel('publish') ? $this->t('Publish') : '',
175      $this->showLabel('publish') ? $this->t('Publish') : '',
175      $this->showLabel('publish') ? $this->t('Publish') : '',
175      $this->showLabel('publish') ? $this->t('Publish') : '',
176      'publish',
177      $this->showIcon('publish') ? 'upload' : '',
177      $this->showIcon('publish') ? 'upload' : '',
177      $this->showIcon('publish') ? 'upload' : '',
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  }
StateButtons->buildRestoreButton
193    $button = $this->buildButton(
194      $this->showLabel('restore') ? $this->t('Restore') : '',
194      $this->showLabel('restore') ? $this->t('Restore') : '',
194      $this->showLabel('restore') ? $this->t('Restore') : '',
194      $this->showLabel('restore') ? $this->t('Restore') : '',
195      'restore',
196      $this->showIcon('restore') ? 'arrow-repeat' : '',
196      $this->showIcon('restore') ? 'arrow-repeat' : '',
196      $this->showIcon('restore') ? 'arrow-repeat' : '',
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  }
StateButtons->buildRevertButton
212    $button = $this->buildButton(
213      $this->showLabel('revert') ? $this->t('Revert') : '',
213      $this->showLabel('revert') ? $this->t('Revert') : '',
213      $this->showLabel('revert') ? $this->t('Revert') : '',
213      $this->showLabel('revert') ? $this->t('Revert') : '',
214      'revert',
215      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
215      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
215      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
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  }
StateButtons->buildStateButtons
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;
91    $saveIsCurrent = $hasSave ? $instance->isPublishedPresent() : FALSE;
91    $saveIsCurrent = $hasSave ? $instance->isPublishedPresent() : FALSE;
91    $saveIsCurrent = $hasSave ? $instance->isPublishedPresent() : FALSE;
92
93    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
93    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
93    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
94      $buttons[] = $this->htmxEvents->onPublish($this->buildPublishButton(), $instance_d);
95    }
96
97    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
97    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
97    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
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)) {
101    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
101    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
101    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
102      $buttons[] = $this->htmxEvents->onRevert($this->buildRevertButton(), $instance_d);
103    }
104
105    return $buttons;
105    return $buttons;
106  }
StateButtons->create
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  }
StateButtons->hasButtons
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  }
StateButtons->isOverridden
137  protected function isOverridden(string $builder_id): bool {
138    if (!$this->moduleHandler->moduleExists('display_builder_entity_view')) {
139      return FALSE;
142    $instanceInfos = EntityViewOverride::checkInstanceId($builder_id);
143
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
145      return FALSE;
151    $entity = $this->entityTypeManager->getStorage($instanceInfos['entity_type_id'])
152      ->load($instanceInfos['entity_id']);
153
154    if (!($entity instanceof FieldableEntityInterface)) {
155      return FALSE;
158    $overriddenField = $entity->get($instanceInfos['field_name']);
159
160    if ($overriddenField->isEmpty()) {
161      return FALSE;
164    return TRUE;
165  }
StateButtons->onPublish
74  public function onPublish(InstanceInterface $instance): array {
75    return $this->reloadWithGlobalData($instance);
76  }