Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 26
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 / 56
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 7
240
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 / 16
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 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 / 8
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 / 9
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 printSaveAlert
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 6
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\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 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  default_region: 'main',
26  icon: 'list-columns-reverse',
27)]
28class LogsPanel extends IslandPluginBase {
29
30  use IslandReloadEventsTrait;
31
32  /**
33   * The date formatter.
34   */
35  protected DateFormatterInterface $dateFormatter;
36
37  /**
38   * {@inheritdoc}
39   */
40  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
41    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
42    $instance->dateFormatter = $container->get('date.formatter');
43
44    return $instance;
45  }
46
47  /**
48   * {@inheritdoc}
49   */
50  public static function keyboardShortcuts(): array {
51    return [
52      'key' => 'o',
53      'help' => t('Show the logs'),
54    ];
55  }
56
57  /**
58   * {@inheritdoc}
59   */
60  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
61    $published_hash = $builder->getPublishedHash();
62
63    $table = [
64      '#theme' => 'table',
65      '#header' => [
66        ['data' => $this->t('Step')],
67        ['data' => $this->t('Published')],
68        ['data' => $this->t('Time')],
69        ['data' => $this->t('User')],
70        ['data' => $this->t('Message')],
71      ],
72      '#rows' => $this->buildRows($builder, $published_hash),
73    ];
74
75    return [
76      $table,
77      $published_hash ? $this->printSaveAlert($builder) : [],
78    ];
79  }
80
81  /**
82   * {@inheritdoc}
83   */
84  public function onPublish(InstanceInterface $instance): array {
85    return $this->reloadWithGlobalData($instance);
86  }
87
88  /**
89   * Build rows for the logs table.
90   *
91   * @param \Drupal\display_builder\InstanceInterface $builder
92   *   The instance entity.
93   * @param ?int $published_hash
94   *   The hash of the published content, if any.
95   *
96   * @return array
97   *   A renderable array representing a table row.
98   */
99  protected function buildRows(InstanceInterface $builder, ?int $published_hash): array {
100    $rows = [];
101    // Reindex the array numerically so 0 will always be the 'present' (the
102    // default revision).
103    $past = \array_values($builder->getPast());
104
105    foreach ($past as $index => $step) {
106      /** @var \Drupal\display_builder\InstanceInterface $step */
107      $rows[] = $this->buildRow(-\count($past) + $index, $step, $published_hash);
108    }
109
110    // Present data.
111    $rows[] = $this->buildRow(0, $builder, $published_hash);
112
113    // Reindex the array numerically so 0 will always be the 'present' (the
114    // default revision).
115    foreach (\array_values($builder->getFuture()) as $index => $step) {
116      /** @var \Drupal\display_builder\InstanceInterface $step */
117      $rows[] = $this->buildRow($index + 1, $step, $published_hash);
118    }
119
120    return $rows;
121  }
122
123  /**
124   * Build a single row for the logs table.
125   *
126   * @param int $index
127   *   The row index.
128   * @param \Drupal\display_builder\InstanceInterface $step
129   *   The step data containing time and log message.
130   * @param ?int $published_hash
131   *   The hash of the published content, if any.
132   *
133   * @return array
134   *   A renderable array representing a table row.
135   */
136  private function buildRow(int $index, InstanceInterface $step, ?int $published_hash): array {
137    $hash = $step->getHash();
138
139    return [
140      'hash' => $hash,
141      'data' => [
142        (string) $index,
143        $published_hash && ($hash === $published_hash) ? '✅' : '',
144        DisplayBuilderHelpers::formatTime($this->dateFormatter, (int) $step->getRevisionCreationTime()),
145        $step->getRevisionUser()?->getDisplayName(),
146        $step->getRevisionLogMessage(),
147      ],
148      'style' => ($index === 0) ? 'font-weight: bold;' : '',
149    ];
150  }
151
152  /**
153   * Print an alert if the saved step is not in the history.
154   *
155   * @param \Drupal\display_builder\InstanceInterface $builder
156   *   The instance entity.
157   *
158   * @return array
159   *   A renderable array.
160   */
161  private function printSaveAlert(InstanceInterface $builder): array {
162    $steps = \array_merge($builder->getPast(), [$builder], $builder->getFuture());
163
164    foreach ($steps as $step) {
165      /** @var \Drupal\display_builder\InstanceInterface $step */
166      if ($step->isPublishedPresent()) {
167        return [];
168      }
169    }
170
171    $params = [
172      '%time' => DisplayBuilderHelpers::formatTime($this->dateFormatter, (int) $builder->getPublishedTime()),
173    ];
174
175    return [
176      '#type' => 'html_tag',
177      '#tag' => 'p',
178      '#value' => $this->t('Published at %time but not visible in logs', $params),
179    ];
180  }
181
182}