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