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}

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
173    $button = $this->buildButton(
174      $this->showLabel('publish') ? $this->t('Publish') : '',
174      $this->showLabel('publish') ? $this->t('Publish') : '',
174      $this->showLabel('publish') ? $this->t('Publish') : '',
174      $this->showLabel('publish') ? $this->t('Publish') : '',
175      'publish',
176      $this->showIcon('publish') ? 'upload' : '',
176      $this->showIcon('publish') ? 'upload' : '',
176      $this->showIcon('publish') ? 'upload' : '',
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  }
StateButtons->buildRestoreButton
192    $button = $this->buildButton(
193      $this->showLabel('restore') ? $this->t('Restore') : '',
193      $this->showLabel('restore') ? $this->t('Restore') : '',
193      $this->showLabel('restore') ? $this->t('Restore') : '',
193      $this->showLabel('restore') ? $this->t('Restore') : '',
194      'restore',
195      $this->showIcon('restore') ? 'arrow-repeat' : '',
195      $this->showIcon('restore') ? 'arrow-repeat' : '',
195      $this->showIcon('restore') ? 'arrow-repeat' : '',
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  }
StateButtons->buildRevertButton
211    $button = $this->buildButton(
212      $this->showLabel('revert') ? $this->t('Revert') : '',
212      $this->showLabel('revert') ? $this->t('Revert') : '',
212      $this->showLabel('revert') ? $this->t('Revert') : '',
212      $this->showLabel('revert') ? $this->t('Revert') : '',
213      'revert',
214      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
214      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
214      $this->showIcon('revert') ? 'box-arrow-in-down' : '',
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  }
StateButtons->buildStateButtons
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) {
92    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
92    if ($this->isButtonEnabled('publish') && !$saveIsCurrent) {
93      $buttons[] = $this->htmxEvents->onPublish($this->buildPublishButton(), $instance_d);
94    }
95
96    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
96    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
96    if ($this->isButtonEnabled('restore') && !$saveIsCurrent) {
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)) {
100    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
100    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
100    if ($this->isButtonEnabled('revert') && $this->isOverridden($instance_d)) {
101      $buttons[] = $this->htmxEvents->onRevert($this->buildRevertButton(), $instance_d);
102    }
103
104    return $buttons;
104    return $buttons;
105  }
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
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  }
StateButtons->isOverridden
136  protected function isOverridden(string $builder_id): bool {
137    if (!$this->moduleHandler->moduleExists('display_builder_entity_view')) {
138      return FALSE;
141    $instanceInfos = EntityViewOverride::checkInstanceId($builder_id);
142
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
143    if (!isset($instanceInfos['entity_type_id'], $instanceInfos['entity_id'], $instanceInfos['field_name'])) {
144      return FALSE;
150    $entity = $this->entityTypeManager->getStorage($instanceInfos['entity_type_id'])
151      ->load($instanceInfos['entity_id']);
152
153    if (!($entity instanceof FieldableEntityInterface)) {
154      return FALSE;
157    $overriddenField = $entity->get($instanceInfos['field_name']);
158
159    if ($overriddenField->isEmpty()) {
160      return FALSE;
163    return TRUE;
164  }
StateButtons->onPublish
74  public function onPublish(InstanceInterface $instance): array {
75    return $this->reloadWithGlobalData($instance);
76  }