3 namespace Drupal\diff\Form;
5 use Drupal\Component\Utility\Xss;
6 use Drupal\Core\Entity\ContentEntityInterface;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Language\LanguageManagerInterface;
10 use Drupal\diff\DiffEntityComparison;
11 use Drupal\diff\DiffLayoutManager;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Drupal\Core\Form\FormBase;
14 use Drupal\Core\Session\AccountInterface;
15 use Drupal\Core\Datetime\DateFormatter;
16 use Drupal\Core\Form\FormStateInterface;
18 use Drupal\Core\Render\RendererInterface;
21 * Provides a form for revision overview page.
23 class RevisionOverviewForm extends FormBase {
26 * The entity type manager.
28 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
30 protected $entityTypeManager;
33 * The current user service.
35 * @var \Drupal\Core\Session\AccountInterface
37 protected $currentUser;
42 * @var \Drupal\Core\Datetime\DateFormatter
47 * The renderer service.
49 * @var \Drupal\Core\Render\RendererInterface
54 * The language manager.
56 * @var \Drupal\Core\Language\LanguageManagerInterface
58 protected $languageManager;
61 * Wrapper object for simple configuration from diff.settings.yml.
63 * @var \Drupal\Core\Config\ImmutableConfig
68 * The field diff layout plugin manager service.
70 * @var \Drupal\diff\DiffLayoutManager
72 protected $diffLayoutManager;
75 * The diff entity comparison service.
77 * @var \Drupal\diff\DiffEntityComparison
79 protected $entityComparison;
82 * Constructs a RevisionOverviewForm object.
84 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
85 * The entity type manager.
86 * @param \Drupal\Core\Session\AccountInterface $current_user
88 * @param \Drupal\Core\Datetime\DateFormatter $date
90 * @param \Drupal\Core\Render\RendererInterface $renderer
91 * The renderer service.
92 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
93 * The language manager.
94 * @param \Drupal\diff\DiffLayoutManager $diff_layout_manager
95 * The diff layout service.
96 * @param \Drupal\diff\DiffEntityComparison $entity_comparison
97 * The diff entity comparison service.
99 public function __construct(EntityTypeManagerInterface $entity_type_manager, AccountInterface $current_user, DateFormatter $date, RendererInterface $renderer, LanguageManagerInterface $language_manager, DiffLayoutManager $diff_layout_manager, DiffEntityComparison $entity_comparison) {
100 $this->entityTypeManager = $entity_type_manager;
101 $this->currentUser = $current_user;
103 $this->renderer = $renderer;
104 $this->languageManager = $language_manager;
105 $this->config = $this->config('diff.settings');
106 $this->diffLayoutManager = $diff_layout_manager;
107 $this->entityComparison = $entity_comparison;
113 public static function create(ContainerInterface $container) {
115 $container->get('entity_type.manager'),
116 $container->get('current_user'),
117 $container->get('date.formatter'),
118 $container->get('renderer'),
119 $container->get('language_manager'),
120 $container->get('plugin.manager.diff.layout'),
121 $container->get('diff.entity_comparison')
128 public function getFormId() {
129 return 'revision_overview_form';
135 public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
136 $account = $this->currentUser;
137 /** @var \Drupal\node\NodeInterface $node */
138 $langcode = $node->language()->getId();
139 $langname = $node->language()->getName();
140 $languages = $node->getTranslationLanguages();
141 $has_translations = (count($languages) > 1);
142 $node_storage = $this->entityTypeManager->getStorage('node');
143 $type = $node->getType();
145 $pagerLimit = $this->config->get('general_settings.revision_pager_limit');
147 $query = $this->entityTypeManager->getStorage('node')->getQuery()
148 ->condition($node->getEntityType()->getKey('id'), $node->id())
151 ->sort($node->getEntityType()->getKey('revision'), 'DESC')
152 // Access to the content has already been verified. Disable query-level
153 // access checking so that revisions for unpublished content still
157 $vids = array_keys($query);
159 $revision_count = count($vids);
161 $build['#title'] = $has_translations ? $this->t('@langname revisions for %title', [
162 '@langname' => $langname,
163 '%title' => $node->label(),
164 ]) : $this->t('Revisions for %title', [
165 '%title' => $node->label(),
167 $build['nid'] = array(
169 '#value' => $node->id(),
173 $table_header['revision'] = $this->t('Revision');
175 // Allow comparisons only if there are 2 or more revisions.
176 if ($revision_count > 1) {
177 $table_header += array(
178 'select_column_one' => '',
179 'select_column_two' => '',
182 $table_header['operations'] = $this->t('Operations');
184 $rev_revert_perm = $account->hasPermission("revert $type revisions") ||
185 $account->hasPermission('revert all revisions') ||
186 $account->hasPermission('administer nodes');
187 $rev_delete_perm = $account->hasPermission("delete $type revisions") ||
188 $account->hasPermission('delete all revisions') ||
189 $account->hasPermission('administer nodes');
190 $revert_permission = $rev_revert_perm && $node->access('update');
191 $delete_permission = $rev_delete_perm && $node->access('delete');
193 // Contains the table listing the revisions.
194 $build['node_revisions_table'] = array(
196 '#header' => $table_header,
197 '#attributes' => array('class' => array('diff-revisions')),
200 $build['node_revisions_table']['#attached']['library'][] = 'diff/diff.general';
201 $build['node_revisions_table']['#attached']['drupalSettings']['diffRevisionRadios'] = $this->config->get('general_settings.radio_behavior');
203 $default_revision = $node->getRevisionId();
204 // Add rows to the table.
205 foreach ($vids as $key => $vid) {
206 $previous_revision = NULL;
207 if (isset($vids[$key + 1])) {
208 $previous_revision = $node_storage->loadRevision($vids[$key + 1]);
210 /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
211 if ($revision = $node_storage->loadRevision($vid)) {
212 if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
214 '#theme' => 'username',
215 '#account' => $revision->getRevisionAuthor(),
217 $revision_date = $this->date->format($revision->getRevisionCreationTime(), 'short');
218 // Use revision link to link to revisions that are not active.
219 if ($vid != $node->getRevisionId()) {
220 $link = Link::fromTextAndUrl($revision_date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
223 $link = $node->toLink($revision_date);
226 if ($vid == $default_revision) {
228 'revision' => $this->buildRevision($link, $username, $revision, $previous_revision),
231 // Allow comparisons only if there are 2 or more revisions.
232 if ($revision_count > 1) {
234 'select_column_one' => $this->buildSelectColumn('radios_left', $vid, FALSE),
235 'select_column_two' => $this->buildSelectColumn('radios_right', $vid, $vid),
238 $row['operations'] = array(
240 '#markup' => $this->t('Current revision'),
241 '#suffix' => '</em>',
242 '#attributes' => array(
243 'class' => array('revision-current'),
246 $row['#attributes'] = [
247 'class' => ['revision-current'],
251 $route_params = array(
252 'node' => $node->id(),
253 'node_revision' => $vid,
254 'langcode' => $langcode,
257 if ($revert_permission) {
259 'title' => $vid < $node->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'),
260 'url' => $has_translations ?
261 Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) :
262 Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
265 if ($delete_permission) {
266 $links['delete'] = array(
267 'title' => $this->t('Delete'),
268 'url' => Url::fromRoute('node.revision_delete_confirm', $route_params),
272 // Here we don't have to deal with 'only one revision' case because
273 // if there's only one revision it will also be the default one,
274 // entering on the first branch of this if else statement.
276 'revision' => $this->buildRevision($link, $username, $revision, $previous_revision),
277 'select_column_one' => $this->buildSelectColumn('radios_left', $vid,
278 isset($vids[1]) ? $vids[1] : FALSE),
279 'select_column_two' => $this->buildSelectColumn('radios_right', $vid, FALSE),
281 '#type' => 'operations',
286 // Add the row to the table.
287 $build['node_revisions_table'][] = $row;
292 // Allow comparisons only if there are 2 or more revisions.
293 if ($revision_count > 1) {
294 $build['submit'] = array(
296 '#button_type' => 'primary',
297 '#value' => t('Compare selected revisions'),
298 '#attributes' => array(
305 $build['pager'] = array(
308 $build['#attached']['library'][] = 'node/drupal.node.admin';
313 * Set column attributes and return config array.
315 * @param string $name
317 * @param string $return_val
318 * Return value attribute.
319 * @param string $default_val
320 * Default value attribute.
323 * Configuration array.
325 protected function buildSelectColumn($name, $return_val, $default_val) {
328 '#title_display' => 'invisible',
330 '#return_value' => $return_val,
331 '#default_value' => $default_val,
336 * Set and return configuration for revision.
338 * @param \Drupal\Core\Link $link
340 * @param string $username
341 * Username attribute.
342 * @param \Drupal\Core\Entity\ContentEntityInterface $revision
343 * Revision parameter for getRevisionDescription function.
344 * @param \Drupal\Core\Entity\ContentEntityInterface $previous_revision
345 * (optional) Previous revision for getRevisionDescription function.
349 * Configuration for revision.
351 protected function buildRevision(Link $link, $username, ContentEntityInterface $revision, ContentEntityInterface $previous_revision = NULL) {
353 '#type' => 'inline_template',
354 '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}',
356 'date' => $link->toString(),
357 'username' => $this->renderer->renderPlain($username),
359 '#markup' => $this->entityComparison->getRevisionDescription($revision, $previous_revision),
360 '#allowed_tags' => Xss::getAdminTagList(),
369 public function validateForm(array &$form, FormStateInterface $form_state) {
370 $input = $form_state->getUserInput();
372 if (count($form_state->getValue('node_revisions_table')) <= 1) {
373 $form_state->setErrorByName('node_revisions_table', $this->t('Multiple revisions are needed for comparison.'));
375 elseif (!isset($input['radios_left']) || !isset($input['radios_right'])) {
376 $form_state->setErrorByName('node_revisions_table', $this->t('Select two revisions to compare.'));
378 elseif ($input['radios_left'] == $input['radios_right']) {
379 // @todo Radio-boxes selection resets if there are errors.
380 $form_state->setErrorByName('node_revisions_table', $this->t('Select different revisions to compare.'));
387 public function submitForm(array &$form, FormStateInterface $form_state) {
388 $input = $form_state->getUserInput();
389 $vid_left = $input['radios_left'];
390 $vid_right = $input['radios_right'];
391 $nid = $input['nid'];
393 // Always place the older revision on the left side of the comparison
394 // and the newer revision on the right side (however revisions can be
395 // compared both ways if we manually change the order of the parameters).
396 if ($vid_left > $vid_right) {
398 $vid_left = $vid_right;
401 // Builds the redirect Url.
402 $redirect_url = Url::fromRoute(
403 'diff.revisions_diff',
406 'left_revision' => $vid_left,
407 'right_revision' => $vid_right,
408 'filter' => $this->diffLayoutManager->getDefaultLayout(),
411 $form_state->setRedirectUrl($redirect_url);