Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 76 |
|
0.00% |
0 / 49 |
|
0.00% |
0 / 80 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 1 |
| LogsPanel | |
0.00% |
0 / 70 |
|
0.00% |
0 / 49 |
|
0.00% |
0 / 80 |
|
0.00% |
0 / 13 |
812 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| keyboardShortcuts | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| build | |
0.00% |
0 / 23 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
| 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 | |||
| onSave | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| buildRows | |
0.00% |
0 / 7 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
| buildRow | |
0.00% |
0 / 12 |
|
0.00% |
0 / 17 |
|
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
56 | |||
| printSaveAlert | |
0.00% |
0 / 15 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder\Plugin\display_builder\Island; |
| 6 | |
| 7 | use Drupal\Core\Datetime\DateFormatterInterface; |
| 8 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 9 | use Drupal\display_builder\Attribute\Island; |
| 10 | use Drupal\display_builder\DisplayBuilderHelpers; |
| 11 | use Drupal\display_builder\HistoryStep; |
| 12 | use Drupal\display_builder\InstanceInterface; |
| 13 | use Drupal\display_builder\IslandPluginBase; |
| 14 | use Drupal\display_builder\IslandType; |
| 15 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 16 | |
| 17 | /** |
| 18 | * Logs island plugin implementation. |
| 19 | */ |
| 20 | #[Island( |
| 21 | id: 'logs', |
| 22 | label: new TranslatableMarkup('Logs'), |
| 23 | description: new TranslatableMarkup('Logs based on changes history.'), |
| 24 | type: IslandType::View, |
| 25 | icon: 'list-columns-reverse', |
| 26 | )] |
| 27 | class LogsPanel extends IslandPluginBase { |
| 28 | |
| 29 | /** |
| 30 | * The date formatter. |
| 31 | */ |
| 32 | protected DateFormatterInterface $dateFormatter; |
| 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->dateFormatter = $container->get('date.formatter'); |
| 40 | |
| 41 | return $instance; |
| 42 | } |
| 43 | |
| 44 | /** |
| 45 | * {@inheritdoc} |
| 46 | */ |
| 47 | public static function keyboardShortcuts(): array { |
| 48 | return [ |
| 49 | 'o' => t('Show the logs'), |
| 50 | ]; |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * {@inheritdoc} |
| 55 | */ |
| 56 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 57 | $load = $builder->toArray(); |
| 58 | |
| 59 | if (!$load) { |
| 60 | return []; |
| 61 | } |
| 62 | |
| 63 | /** @var \Drupal\display_builder\HistoryStep $present */ |
| 64 | $present = $load['present']; |
| 65 | |
| 66 | if (!$present) { |
| 67 | return []; |
| 68 | } |
| 69 | |
| 70 | $save = $load['save'] ?? NULL; |
| 71 | $rows = $this->buildRows($load['past'], $present, $load['future'], $save); |
| 72 | $table = [ |
| 73 | '#theme' => 'table', |
| 74 | '#header' => [ |
| 75 | ['data' => $this->t('Step')], |
| 76 | ['data' => $this->t('Published')], |
| 77 | ['data' => $this->t('Time')], |
| 78 | ['data' => $this->t('User')], |
| 79 | ['data' => $this->t('Message')], |
| 80 | ], |
| 81 | '#rows' => $rows, |
| 82 | ]; |
| 83 | |
| 84 | return [ |
| 85 | $table, |
| 86 | $save ? $this->printSaveAlert(\array_merge($load['past'], [$present], $load['future']), $save) : [], |
| 87 | ]; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * {@inheritdoc} |
| 92 | */ |
| 93 | public function onAttachToRoot(string $builder_id, string $instance_id): array { |
| 94 | return $this->reloadWithGlobalData($builder_id); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * {@inheritdoc} |
| 99 | */ |
| 100 | public function onAttachToSlot(string $builder_id, string $instance_id, string $parent_id): array { |
| 101 | return $this->reloadWithGlobalData($builder_id); |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * {@inheritdoc} |
| 106 | */ |
| 107 | public function onMove(string $builder_id, string $instance_id): array { |
| 108 | return $this->reloadWithGlobalData($builder_id); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * {@inheritdoc} |
| 113 | */ |
| 114 | public function onHistoryChange(string $builder_id): array { |
| 115 | return $this->reloadWithGlobalData($builder_id); |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * {@inheritdoc} |
| 120 | */ |
| 121 | public function onUpdate(string $builder_id, string $instance_id): array { |
| 122 | return $this->reloadWithGlobalData($builder_id); |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * {@inheritdoc} |
| 127 | */ |
| 128 | public function onDelete(string $builder_id, string $parent_id): array { |
| 129 | return $this->reloadWithGlobalData($builder_id); |
| 130 | } |
| 131 | |
| 132 | /** |
| 133 | * {@inheritdoc} |
| 134 | */ |
| 135 | public function onSave(string $builder_id): array { |
| 136 | return $this->reloadWithGlobalData($builder_id); |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * Build rows for the logs table. |
| 141 | * |
| 142 | * @param \Drupal\display_builder\HistoryStep[] $past |
| 143 | * Steps with time and log message. |
| 144 | * @param \Drupal\display_builder\HistoryStep $present |
| 145 | * A step with time and log message. |
| 146 | * @param \Drupal\display_builder\HistoryStep[] $future |
| 147 | * Steps with time and log message. |
| 148 | * @param \Drupal\display_builder\HistoryStep $save |
| 149 | * Saved state. |
| 150 | * |
| 151 | * @return array |
| 152 | * A renderable array representing a table row. |
| 153 | */ |
| 154 | protected function buildRows(array $past, ?HistoryStep $present, array $future, ?HistoryStep $save): array { |
| 155 | $rows = []; |
| 156 | |
| 157 | foreach (\array_filter($past) as $index => $step) { |
| 158 | $rows[] = $this->buildRow(-\count($past) + $index, $step, $save); |
| 159 | } |
| 160 | |
| 161 | // Present data. |
| 162 | $rows[] = $this->buildRow(0, $present, $save); |
| 163 | |
| 164 | foreach (\array_filter($future) as $index => $step) { |
| 165 | $rows[] = $this->buildRow($index + 1, $step, $save); |
| 166 | } |
| 167 | |
| 168 | return $rows; |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Build a single row for the logs table. |
| 173 | * |
| 174 | * @param int $index |
| 175 | * The row index. |
| 176 | * @param \Drupal\display_builder\HistoryStep $step |
| 177 | * The step data containing time and log message. |
| 178 | * @param \Drupal\display_builder\HistoryStep $save |
| 179 | * Saved state. |
| 180 | * |
| 181 | * @return array |
| 182 | * A renderable array representing a table row. |
| 183 | */ |
| 184 | private function buildRow(int $index, HistoryStep $step, ?HistoryStep $save): array { |
| 185 | $user = !empty($step->user) ? $this->entityTypeManager->getStorage('user')->load($step->user) : NULL; |
| 186 | |
| 187 | return [ |
| 188 | 'hash' => $step->hash, |
| 189 | 'data' => [ |
| 190 | (string) $index, |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 192 | $step->time ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->time) : NULL, |
| 193 | $user ? $user->getDisplayName() : NULL, |
| 194 | $step->log ?? '', |
| 195 | ], |
| 196 | 'style' => ($index === 0) ? 'font-weight: bold;' : '', |
| 197 | ]; |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * Print an alert if the saved step is not in the history. |
| 202 | * |
| 203 | * @param \Drupal\display_builder\HistoryStep[] $steps |
| 204 | * All steps: past, present and future. |
| 205 | * @param \Drupal\display_builder\HistoryStep $save |
| 206 | * Saved state. |
| 207 | * |
| 208 | * @return array |
| 209 | * A renderable array. |
| 210 | */ |
| 211 | private function printSaveAlert(array $steps, HistoryStep $save): array { |
| 212 | $save_found = FALSE; |
| 213 | |
| 214 | foreach ($steps as $step) { |
| 215 | if ($step && $step->hash === $save->hash) { |
| 216 | $save_found = TRUE; |
| 217 | |
| 218 | break; |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | if ($save_found) { |
| 223 | return []; |
| 224 | } |
| 225 | $params = [ |
| 226 | '%time' => DisplayBuilderHelpers::formatTime($this->dateFormatter, $save->time), |
| 227 | ]; |
| 228 | |
| 229 | return [ |
| 230 | '#type' => 'html_tag', |
| 231 | '#tag' => 'p', |
| 232 | '#value' => $this->t('Saved at %time but not visible in logs', $params), |
| 233 | ]; |
| 234 | } |
| 235 | |
| 236 | } |
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.
| 56 | public function build(InstanceInterface $builder, array $data = [], array $options = []): array { |
| 57 | $load = $builder->toArray(); |
| 58 | |
| 59 | if (!$load) { |
| 60 | return []; |
| 64 | $present = $load['present']; |
| 65 | |
| 66 | if (!$present) { |
| 67 | return []; |
| 70 | $save = $load['save'] ?? NULL; |
| 71 | $rows = $this->buildRows($load['past'], $present, $load['future'], $save); |
| 72 | $table = [ |
| 73 | '#theme' => 'table', |
| 74 | '#header' => [ |
| 75 | ['data' => $this->t('Step')], |
| 76 | ['data' => $this->t('Published')], |
| 77 | ['data' => $this->t('Time')], |
| 78 | ['data' => $this->t('User')], |
| 79 | ['data' => $this->t('Message')], |
| 80 | ], |
| 81 | '#rows' => $rows, |
| 82 | ]; |
| 83 | |
| 84 | return [ |
| 85 | $table, |
| 86 | $save ? $this->printSaveAlert(\array_merge($load['past'], [$present], $load['future']), $save) : [], |
| 86 | $save ? $this->printSaveAlert(\array_merge($load['past'], [$present], $load['future']), $save) : [], |
| 85 | $table, |
| 85 | $table, |
| 184 | private function buildRow(int $index, HistoryStep $step, ?HistoryStep $save): array { |
| 185 | $user = !empty($step->user) ? $this->entityTypeManager->getStorage('user')->load($step->user) : NULL; |
| 185 | $user = !empty($step->user) ? $this->entityTypeManager->getStorage('user')->load($step->user) : NULL; |
| 185 | $user = !empty($step->user) ? $this->entityTypeManager->getStorage('user')->load($step->user) : NULL; |
| 185 | $user = !empty($step->user) ? $this->entityTypeManager->getStorage('user')->load($step->user) : NULL; |
| 186 | |
| 187 | return [ |
| 188 | 'hash' => $step->hash, |
| 189 | 'data' => [ |
| 190 | (string) $index, |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 192 | $step->time ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->time) : NULL, |
| 192 | $step->time ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->time) : NULL, |
| 188 | 'hash' => $step->hash, |
| 188 | 'hash' => $step->hash, |
| 189 | 'data' => [ |
| 190 | (string) $index, |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 192 | $step->time ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->time) : NULL, |
| 193 | $user ? $user->getDisplayName() : NULL, |
| 193 | $user ? $user->getDisplayName() : NULL, |
| 188 | 'hash' => $step->hash, |
| 188 | 'hash' => $step->hash, |
| 189 | 'data' => [ |
| 190 | (string) $index, |
| 191 | ($save && $step->hash === $save->hash) ? '✅' : '', |
| 192 | $step->time ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->time) : NULL, |
| 193 | $user ? $user->getDisplayName() : NULL, |
| 194 | $step->log ?? '', |
| 195 | ], |
| 196 | 'style' => ($index === 0) ? 'font-weight: bold;' : '', |
| 196 | 'style' => ($index === 0) ? 'font-weight: bold;' : '', |
| 196 | 'style' => ($index === 0) ? 'font-weight: bold;' : '', |
| 196 | 'style' => ($index === 0) ? 'font-weight: bold;' : '', |
| 154 | protected function buildRows(array $past, ?HistoryStep $present, array $future, ?HistoryStep $save): array { |
| 155 | $rows = []; |
| 156 | |
| 157 | foreach (\array_filter($past) as $index => $step) { |
| 157 | foreach (\array_filter($past) as $index => $step) { |
| 157 | foreach (\array_filter($past) as $index => $step) { |
| 157 | foreach (\array_filter($past) as $index => $step) { |
| 158 | $rows[] = $this->buildRow(-\count($past) + $index, $step, $save); |
| 159 | } |
| 160 | |
| 161 | // Present data. |
| 162 | $rows[] = $this->buildRow(0, $present, $save); |
| 163 | |
| 164 | foreach (\array_filter($future) as $index => $step) { |
| 164 | foreach (\array_filter($future) as $index => $step) { |
| 164 | foreach (\array_filter($future) as $index => $step) { |
| 164 | foreach (\array_filter($future) as $index => $step) { |
| 165 | $rows[] = $this->buildRow($index + 1, $step, $save); |
| 166 | } |
| 167 | |
| 168 | return $rows; |
| 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->dateFormatter = $container->get('date.formatter'); |
| 40 | |
| 41 | return $instance; |
| 49 | 'o' => t('Show the logs'), |
| 93 | public function onAttachToRoot(string $builder_id, string $instance_id): array { |
| 94 | return $this->reloadWithGlobalData($builder_id); |
| 100 | public function onAttachToSlot(string $builder_id, string $instance_id, string $parent_id): array { |
| 101 | return $this->reloadWithGlobalData($builder_id); |
| 128 | public function onDelete(string $builder_id, string $parent_id): array { |
| 129 | return $this->reloadWithGlobalData($builder_id); |
| 114 | public function onHistoryChange(string $builder_id): array { |
| 115 | return $this->reloadWithGlobalData($builder_id); |
| 107 | public function onMove(string $builder_id, string $instance_id): array { |
| 108 | return $this->reloadWithGlobalData($builder_id); |
| 135 | public function onSave(string $builder_id): array { |
| 136 | return $this->reloadWithGlobalData($builder_id); |
| 121 | public function onUpdate(string $builder_id, string $instance_id): array { |
| 122 | return $this->reloadWithGlobalData($builder_id); |
| 211 | private function printSaveAlert(array $steps, HistoryStep $save): array { |
| 212 | $save_found = FALSE; |
| 213 | |
| 214 | foreach ($steps as $step) { |
| 214 | foreach ($steps as $step) { |
| 215 | if ($step && $step->hash === $save->hash) { |
| 215 | if ($step && $step->hash === $save->hash) { |
| 214 | foreach ($steps as $step) { |
| 215 | if ($step && $step->hash === $save->hash) { |
| 216 | $save_found = TRUE; |
| 214 | foreach ($steps as $step) { |
| 215 | if ($step && $step->hash === $save->hash) { |
| 216 | $save_found = TRUE; |
| 217 | |
| 218 | break; |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | if ($save_found) { |
| 223 | return []; |
| 226 | '%time' => DisplayBuilderHelpers::formatTime($this->dateFormatter, $save->time), |
| 227 | ]; |
| 228 | |
| 229 | return [ |
| 230 | '#type' => 'html_tag', |
| 231 | '#tag' => 'p', |
| 232 | '#value' => $this->t('Saved at %time but not visible in logs', $params), |