Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
LogsPanel
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 7
420
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
 keyboardShortcuts
0.00% covered (danger)
0.00%
0 / 4
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 / 20
0.00% covered (danger)
0.00%
0 / 6
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
 buildRows
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 buildRow
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
56
 printSaveAlert
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Datetime\DateFormatterInterface;
8use Drupal\Core\StringTranslation\TranslatableMarkup;
9use Drupal\display_builder\Attribute\Island;
10use Drupal\display_builder\DisplayBuilderHelpers;
11use Drupal\display_builder\InstanceInterface;
12use Drupal\display_builder\Island\IslandPluginBase;
13use Drupal\display_builder\Island\IslandReloadEventsTrait;
14use Drupal\display_builder\Island\IslandType;
15use Drupal\display_builder\Plugin\Field\FieldType\HistoryStep;
16use Symfony\Component\DependencyInjection\ContainerInterface;
17
18/**
19 * Logs island plugin implementation.
20 */
21#[Island(
22  id: 'logs',
23  label: new TranslatableMarkup('Logs'),
24  description: new TranslatableMarkup('Logs based on changes history.'),
25  type: IslandType::View,
26  default_region: 'main',
27  icon: 'list-columns-reverse',
28)]
29class LogsPanel extends IslandPluginBase {
30
31  use IslandReloadEventsTrait;
32
33  /**
34   * The date formatter.
35   */
36  protected DateFormatterInterface $dateFormatter;
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->dateFormatter = $container->get('date.formatter');
44
45    return $instance;
46  }
47
48  /**
49   * {@inheritdoc}
50   */
51  public static function keyboardShortcuts(): array {
52    return [
53      'key' => 'o',
54      'help' => t('Show the logs'),
55    ];
56  }
57
58  /**
59   * {@inheritdoc}
60   */
61  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
62    /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $present */
63    $present = $builder->get('present')->first();
64
65    if (!$present) {
66      return [];
67    }
68
69    /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $save */
70    $save = $builder->get('save')->first() ?? NULL;
71    $rows = $this->buildRows($builder);
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($builder->get('past')->getValue(), [$present->getValue()], $builder->get('future')->getValue()), $save) : [],
87    ];
88  }
89
90  /**
91   * {@inheritdoc}
92   */
93  public function onPublish(InstanceInterface $instance): array {
94    return $this->reloadWithGlobalData($instance);
95  }
96
97  /**
98   * Build rows for the logs table.
99   *
100   * @param \Drupal\display_builder\InstanceInterface $builder
101   *   A step with time and log message.
102   *
103   * @return array
104   *   A renderable array representing a table row.
105   */
106  protected function buildRows(InstanceInterface $builder): array {
107    $rows = [];
108    /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $save */
109    $save = $builder->get('save')->first();
110    $past = $builder->getPast();
111
112    foreach ($past as $index => $step) {
113      /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $step */
114      $rows[] = $this->buildRow(-\count($past) + $index, $step, $save);
115    }
116
117    // Present data.
118    $rows[] = $this->buildRow(0, $builder->getCurrent(), $save);
119
120    foreach ($builder->getFuture() as $index => $step) {
121      /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $step */
122      $rows[] = $this->buildRow($index + 1, $step, $save);
123    }
124
125    return $rows;
126  }
127
128  /**
129   * Build a single row for the logs table.
130   *
131   * @param int $index
132   *   The row index.
133   * @param \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $step
134   *   The step data containing time and log message.
135   * @param \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $save
136   *   Saved state.
137   *
138   * @return array
139   *   A renderable array representing a table row.
140   */
141  private function buildRow(int $index, HistoryStep $step, ?HistoryStep $save): array {
142    $user = !empty($step->getUser()) ? $this->entityTypeManager->getStorage('user')->load($step->getUser()) : NULL;
143
144    return [
145      'hash' => $step->getHash(),
146      'data' => [
147        (string) $index,
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
149        $step->getTime() ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->getTime()) : NULL,
150        $user ? $user->getDisplayName() : NULL,
151        $step->getLog() ?? '',
152      ],
153      'style' => ($index === 0) ? 'font-weight: bold;' : '',
154    ];
155  }
156
157  /**
158   * Print an alert if the saved step is not in the history.
159   *
160   * @param array $steps
161   *   All steps: past, present and future.
162   * @param \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $save
163   *   Saved state.
164   *
165   * @return array
166   *   A renderable array.
167   */
168  private function printSaveAlert(array $steps, HistoryStep $save): array {
169    foreach ($steps as $step) {
170      if ($step && ($step['hash'] === $save->getHash())) {
171        return [];
172      }
173    }
174
175    $params = [
176      '%time' => DisplayBuilderHelpers::formatTime($this->dateFormatter, $save->getTime()),
177    ];
178
179    return [
180      '#type' => 'html_tag',
181      '#tag' => 'p',
182      '#value' => $this->t('Saved at %time but not visible in logs', $params),
183    ];
184  }
185
186}

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.

LogsPanel->build
61  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
62    /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $present */
63    $present = $builder->get('present')->first();
64
65    if (!$present) {
66      return [];
70    $save = $builder->get('save')->first() ?? NULL;
71    $rows = $this->buildRows($builder);
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($builder->get('past')->getValue(), [$present->getValue()], $builder->get('future')->getValue()), $save) : [],
86      $save ? $this->printSaveAlert(\array_merge($builder->get('past')->getValue(), [$present->getValue()], $builder->get('future')->getValue()), $save) : [],
85      $table,
85      $table,
86      $save ? $this->printSaveAlert(\array_merge($builder->get('past')->getValue(), [$present->getValue()], $builder->get('future')->getValue()), $save) : [],
87    ];
88  }
LogsPanel->buildRow
141  private function buildRow(int $index, HistoryStep $step, ?HistoryStep $save): array {
142    $user = !empty($step->getUser()) ? $this->entityTypeManager->getStorage('user')->load($step->getUser()) : NULL;
142    $user = !empty($step->getUser()) ? $this->entityTypeManager->getStorage('user')->load($step->getUser()) : NULL;
142    $user = !empty($step->getUser()) ? $this->entityTypeManager->getStorage('user')->load($step->getUser()) : NULL;
142    $user = !empty($step->getUser()) ? $this->entityTypeManager->getStorage('user')->load($step->getUser()) : NULL;
143
144    return [
145      'hash' => $step->getHash(),
146      'data' => [
147        (string) $index,
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
149        $step->getTime() ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->getTime()) : NULL,
149        $step->getTime() ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->getTime()) : NULL,
145      'hash' => $step->getHash(),
145      'hash' => $step->getHash(),
146      'data' => [
147        (string) $index,
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
149        $step->getTime() ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->getTime()) : NULL,
150        $user ? $user->getDisplayName() : NULL,
150        $user ? $user->getDisplayName() : NULL,
145      'hash' => $step->getHash(),
145      'hash' => $step->getHash(),
146      'data' => [
147        (string) $index,
148        ($save && $step->getHash() === $save->getHash()) ? '✅' : '',
149        $step->getTime() ? DisplayBuilderHelpers::formatTime($this->dateFormatter, $step->getTime()) : NULL,
150        $user ? $user->getDisplayName() : NULL,
151        $step->getLog() ?? '',
152      ],
153      'style' => ($index === 0) ? 'font-weight: bold;' : '',
153      'style' => ($index === 0) ? 'font-weight: bold;' : '',
153      'style' => ($index === 0) ? 'font-weight: bold;' : '',
153      'style' => ($index === 0) ? 'font-weight: bold;' : '',
154    ];
155  }
LogsPanel->buildRows
106  protected function buildRows(InstanceInterface $builder): array {
107    $rows = [];
108    /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $save */
109    $save = $builder->get('save')->first();
110    $past = $builder->getPast();
111
112    foreach ($past as $index => $step) {
112    foreach ($past as $index => $step) {
112    foreach ($past as $index => $step) {
112    foreach ($past as $index => $step) {
113      /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $step */
114      $rows[] = $this->buildRow(-\count($past) + $index, $step, $save);
115    }
116
117    // Present data.
118    $rows[] = $this->buildRow(0, $builder->getCurrent(), $save);
119
120    foreach ($builder->getFuture() as $index => $step) {
120    foreach ($builder->getFuture() as $index => $step) {
120    foreach ($builder->getFuture() as $index => $step) {
120    foreach ($builder->getFuture() as $index => $step) {
121      /** @var \Drupal\display_builder\Plugin\Field\FieldType\HistoryStep $step */
122      $rows[] = $this->buildRow($index + 1, $step, $save);
123    }
124
125    return $rows;
126  }
LogsPanel->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->dateFormatter = $container->get('date.formatter');
44
45    return $instance;
46  }
LogsPanel->keyboardShortcuts
53      'key' => 'o',
54      'help' => t('Show the logs'),
55    ];
56  }
LogsPanel->onPublish
93  public function onPublish(InstanceInterface $instance): array {
94    return $this->reloadWithGlobalData($instance);
95  }
LogsPanel->printSaveAlert
168  private function printSaveAlert(array $steps, HistoryStep $save): array {
169    foreach ($steps as $step) {
169    foreach ($steps as $step) {
170      if ($step && ($step['hash'] === $save->getHash())) {
170      if ($step && ($step['hash'] === $save->getHash())) {
170      if ($step && ($step['hash'] === $save->getHash())) {
171        return [];
169    foreach ($steps as $step) {
169    foreach ($steps as $step) {
170      if ($step && ($step['hash'] === $save->getHash())) {
171        return [];
172      }
173    }
174
175    $params = [
176      '%time' => DisplayBuilderHelpers::formatTime($this->dateFormatter, $save->getTime()),
177    ];
178
179    return [
180      '#type' => 'html_tag',
181      '#tag' => 'p',
182      '#value' => $this->t('Saved at %time but not visible in logs', $params),
183    ];
184  }