Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
HistoryButtons
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 12
756
0.00% covered (danger)
0.00%
0 / 1
 build
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
 onAttachToRoot
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
 onAttachToSlot
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
 onMove
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
 onHistoryChange
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
 onUpdate
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
 onDelete
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
 hasButtons
0.00% covered (danger)
0.00%
0 / 17
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
 rebuild
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 buildUndoButton
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 buildRedoButton
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 buildClearButton
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\StringTranslation\TranslatableMarkup;
8use Drupal\display_builder\Attribute\Island;
9use Drupal\display_builder\InstanceInterface;
10use Drupal\display_builder\IslandPluginToolbarButtonConfigurationBase;
11use Drupal\display_builder\IslandType;
12
13/**
14 * History buttons island plugin implementation.
15 */
16#[Island(
17  id: 'history',
18  enabled_by_default: TRUE,
19  label: new TranslatableMarkup('History'),
20  description: new TranslatableMarkup('Undo and redo changes.'),
21  type: IslandType::Button,
22  default_region: 'end',
23)]
24class HistoryButtons extends IslandPluginToolbarButtonConfigurationBase {
25
26  /**
27   * {@inheritdoc}
28   */
29  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
30    $builder_id = (string) $builder->id();
31    $buttons = [
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
33      $this->isButtonEnabled('redo') ? $this->buildRedoButton($builder, $builder_id) : [],
34      $this->isButtonEnabled('clear') ? $this->buildClearButton($builder, $builder_id) : [],
35    ];
36
37    if (empty(\array_filter($buttons))) {
38      return [];
39    }
40
41    return [
42      '#type' => 'component',
43      '#component' => 'display_builder:button_group',
44      '#slots' => [
45        'buttons' => $buttons,
46      ],
47    ];
48  }
49
50  /**
51   * {@inheritdoc}
52   */
53  public function onAttachToRoot(string $builder_id, string $instance_id): array {
54    return $this->rebuild($builder_id);
55  }
56
57  /**
58   * {@inheritdoc}
59   */
60  public function onAttachToSlot(string $builder_id, string $instance_id, string $parent_id): array {
61    return $this->rebuild($builder_id);
62  }
63
64  /**
65   * {@inheritdoc}
66   */
67  public function onMove(string $builder_id, string $instance_id): array {
68    return $this->rebuild($builder_id);
69  }
70
71  /**
72   * {@inheritdoc}
73   */
74  public function onHistoryChange(string $builder_id): array {
75    return $this->rebuild($builder_id);
76  }
77
78  /**
79   * {@inheritdoc}
80   */
81  public function onUpdate(string $builder_id, string $instance_id): array {
82    return $this->rebuild($builder_id);
83  }
84
85  /**
86   * {@inheritdoc}
87   */
88  public function onDelete(string $builder_id, string $parent_id): array {
89    return $this->rebuild($builder_id);
90  }
91
92  /**
93   * {@inheritdoc}
94   */
95  protected function hasButtons(): array {
96    return [
97      'undo' => [
98        'title' => $this->t('Undo'),
99        'description' => $this->t('Undo action, icon is always visible, label is number of undo.'),
100        'default' => 'icon_label',
101      ],
102      'redo' => [
103        'title' => $this->t('Redo'),
104        'description' => $this->t('Redo action, icon is always visible, label is number of redo.'),
105        'default' => 'icon_label',
106      ],
107      'clear' => [
108        'title' => $this->t('Clear'),
109        'description' => $this->t('A button to clear the logs history (past and future).'),
110        'default' => 'hidden',
111      ],
112    ];
113  }
114
115  /**
116   * Rebuilds the island with the given builder ID.
117   *
118   * @param string $builder_id
119   *   The ID of the builder.
120   *
121   * @return array
122   *   The rebuilt island.
123   */
124  private function rebuild(string $builder_id): array {
125    if (!$this->builder) {
126      // @todo pass \Drupal\display_builder\InstanceInterface object in
127      // parameters instead of loading again.
128      /** @var \Drupal\display_builder\InstanceInterface $builder */
129      $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
130      $this->builder = $builder;
131    }
132
133    return $this->addOutOfBand(
134      $this->build($this->builder),
135      '#' . $this->getHtmlId($builder_id),
136      'innerHTML'
137    );
138  }
139
140  /**
141   * Builds the undo button.
142   *
143   * @param \Drupal\display_builder\InstanceInterface $builder
144   *   The builder instance.
145   * @param string $builder_id
146   *   The builder ID.
147   *
148   * @return array
149   *   The undo button render array.
150   */
151  private function buildUndoButton(InstanceInterface $builder, string $builder_id): array {
152    $past = $builder->getCountPast();
153    $undo = $this->buildButton(
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
155      'undo',
156      'arrow-counterclockwise',
157      $this->t('Undo (shortcut: u)'),
158      ['u' => $this->t('Undo last change')]
159    );
160
161    if (empty($past)) {
162      $undo['#attributes']['disabled'] = 'disabled';
163    }
164
165    return $this->htmxEvents->onUndo($undo, $builder_id);
166  }
167
168  /**
169   * Builds the redo button.
170   *
171   * @param \Drupal\display_builder\InstanceInterface $builder
172   *   The builder instance.
173   * @param string $builder_id
174   *   The builder ID.
175   *
176   * @return array
177   *   The redo button render array.
178   */
179  private function buildRedoButton(InstanceInterface $builder, string $builder_id): array {
180    $future = $builder->getCountFuture();
181    $redo = $this->buildButton(
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
183      'redo',
184      'arrow-clockwise',
185      $this->t('Redo (shortcut: r)'),
186      ['r' => $this->t('Redo last undone change')]
187    );
188
189    if (empty($future)) {
190      $redo['#attributes']['disabled'] = 'disabled';
191    }
192
193    return $this->htmxEvents->onRedo($redo, $builder_id);
194  }
195
196  /**
197   * Builds the clear button.
198   *
199   * @param \Drupal\display_builder\InstanceInterface $builder
200   *   The builder instance.
201   * @param string $builder_id
202   *   The builder ID.
203   *
204   * @return array
205   *   The clear button render array.
206   */
207  private function buildClearButton(InstanceInterface $builder, string $builder_id): array {
208    $past = $builder->getCountPast();
209    $future = $builder->getCountFuture();
210    $clear = $this->buildButton(
211      $this->showLabel('clear') ? $this->t('Clear') : '',
212      'clear',
213      $this->showIcon('clear') ? 'clock-history' : '',
214      $this->t('Clear history (shortcut: Shift+C)'),
215      ['C' => $this->t('Clear all changes history (Shift+C)')]
216    );
217    $clear['#props']['variant'] = 'warning';
218    $clear['#attributes']['outline'] = TRUE;
219
220    if (empty($past) && empty($future)) {
221      $clear['#attributes']['class'] = ['hidden'];
222    }
223
224    return $this->htmxEvents->onClear($clear, $builder_id);
225  }
226
227}

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.

HistoryButtons->build
29  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
30    $builder_id = (string) $builder->id();
31    $buttons = [
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
33      $this->isButtonEnabled('redo') ? $this->buildRedoButton($builder, $builder_id) : [],
33      $this->isButtonEnabled('redo') ? $this->buildRedoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
33      $this->isButtonEnabled('redo') ? $this->buildRedoButton($builder, $builder_id) : [],
34      $this->isButtonEnabled('clear') ? $this->buildClearButton($builder, $builder_id) : [],
34      $this->isButtonEnabled('clear') ? $this->buildClearButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
32      $this->isButtonEnabled('undo') ? $this->buildUndoButton($builder, $builder_id) : [],
33      $this->isButtonEnabled('redo') ? $this->buildRedoButton($builder, $builder_id) : [],
34      $this->isButtonEnabled('clear') ? $this->buildClearButton($builder, $builder_id) : [],
35    ];
36
37    if (empty(\array_filter($buttons))) {
38      return [];
42      '#type' => 'component',
43      '#component' => 'display_builder:button_group',
44      '#slots' => [
45        'buttons' => $buttons,
46      ],
47    ];
48  }
HistoryButtons->buildClearButton
207  private function buildClearButton(InstanceInterface $builder, string $builder_id): array {
208    $past = $builder->getCountPast();
209    $future = $builder->getCountFuture();
210    $clear = $this->buildButton(
211      $this->showLabel('clear') ? $this->t('Clear') : '',
211      $this->showLabel('clear') ? $this->t('Clear') : '',
211      $this->showLabel('clear') ? $this->t('Clear') : '',
211      $this->showLabel('clear') ? $this->t('Clear') : '',
212      'clear',
213      $this->showIcon('clear') ? 'clock-history' : '',
213      $this->showIcon('clear') ? 'clock-history' : '',
213      $this->showIcon('clear') ? 'clock-history' : '',
213      $this->showIcon('clear') ? 'clock-history' : '',
214      $this->t('Clear history (shortcut: Shift+C)'),
215      ['C' => $this->t('Clear all changes history (Shift+C)')]
216    );
217    $clear['#props']['variant'] = 'warning';
218    $clear['#attributes']['outline'] = TRUE;
219
220    if (empty($past) && empty($future)) {
220    if (empty($past) && empty($future)) {
220    if (empty($past) && empty($future)) {
221      $clear['#attributes']['class'] = ['hidden'];
222    }
223
224    return $this->htmxEvents->onClear($clear, $builder_id);
224    return $this->htmxEvents->onClear($clear, $builder_id);
225  }
HistoryButtons->buildRedoButton
179  private function buildRedoButton(InstanceInterface $builder, string $builder_id): array {
180    $future = $builder->getCountFuture();
181    $redo = $this->buildButton(
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
182      ($this->showLabel('redo') && $future) ? (string) $future : '',
183      'redo',
184      'arrow-clockwise',
185      $this->t('Redo (shortcut: r)'),
186      ['r' => $this->t('Redo last undone change')]
187    );
188
189    if (empty($future)) {
190      $redo['#attributes']['disabled'] = 'disabled';
191    }
192
193    return $this->htmxEvents->onRedo($redo, $builder_id);
193    return $this->htmxEvents->onRedo($redo, $builder_id);
194  }
HistoryButtons->buildUndoButton
151  private function buildUndoButton(InstanceInterface $builder, string $builder_id): array {
152    $past = $builder->getCountPast();
153    $undo = $this->buildButton(
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
154      ($this->showLabel('undo') && $past) ? (string) $past : '',
155      'undo',
156      'arrow-counterclockwise',
157      $this->t('Undo (shortcut: u)'),
158      ['u' => $this->t('Undo last change')]
159    );
160
161    if (empty($past)) {
162      $undo['#attributes']['disabled'] = 'disabled';
163    }
164
165    return $this->htmxEvents->onUndo($undo, $builder_id);
165    return $this->htmxEvents->onUndo($undo, $builder_id);
166  }
HistoryButtons->hasButtons
98        'title' => $this->t('Undo'),
99        'description' => $this->t('Undo action, icon is always visible, label is number of undo.'),
100        'default' => 'icon_label',
101      ],
102      'redo' => [
103        'title' => $this->t('Redo'),
104        'description' => $this->t('Redo action, icon is always visible, label is number of redo.'),
105        'default' => 'icon_label',
106      ],
107      'clear' => [
108        'title' => $this->t('Clear'),
109        'description' => $this->t('A button to clear the logs history (past and future).'),
110        'default' => 'hidden',
111      ],
112    ];
113  }
HistoryButtons->onAttachToRoot
53  public function onAttachToRoot(string $builder_id, string $instance_id): array {
54    return $this->rebuild($builder_id);
55  }
HistoryButtons->onAttachToSlot
60  public function onAttachToSlot(string $builder_id, string $instance_id, string $parent_id): array {
61    return $this->rebuild($builder_id);
62  }
HistoryButtons->onDelete
88  public function onDelete(string $builder_id, string $parent_id): array {
89    return $this->rebuild($builder_id);
90  }
HistoryButtons->onHistoryChange
74  public function onHistoryChange(string $builder_id): array {
75    return $this->rebuild($builder_id);
76  }
HistoryButtons->onMove
67  public function onMove(string $builder_id, string $instance_id): array {
68    return $this->rebuild($builder_id);
69  }
HistoryButtons->onUpdate
81  public function onUpdate(string $builder_id, string $instance_id): array {
82    return $this->rebuild($builder_id);
83  }
HistoryButtons->rebuild
124  private function rebuild(string $builder_id): array {
125    if (!$this->builder) {
129      $builder = $this->entityTypeManager->getStorage('display_builder_instance')->load($builder_id);
130      $this->builder = $builder;
131    }
132
133    return $this->addOutOfBand(
133    return $this->addOutOfBand(
134      $this->build($this->builder),
135      '#' . $this->getHtmlId($builder_id),
136      'innerHTML'
137    );
138  }