3 namespace Drupal\diff\Form;
5 use Drupal\Component\Utility\Xss;
6 use Drupal\Core\Entity\Query\QueryFactory;
7 use Drupal\Core\Entity\ContentEntityInterface;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Language\LanguageManagerInterface;
11 use Drupal\diff\DiffEntityComparison;
12 use Drupal\diff\DiffLayoutManager;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Drupal\Core\Form\FormBase;
15 use Drupal\Core\Session\AccountInterface;
16 use Drupal\Core\Datetime\DateFormatter;
17 use Drupal\Core\Form\FormStateInterface;
19 use Drupal\Core\Render\RendererInterface;
22 * Provides a form for revision overview page.
24 class RevisionOverviewForm extends FormBase {
27 * The entity type manager.
29 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
31 protected $entityTypeManager;
34 * The current user service.
36 * @var \Drupal\Core\Session\AccountInterface
38 protected $currentUser;
43 * @var \Drupal\Core\Datetime\DateFormatter
48 * The renderer service.
50 * @var \Drupal\Core\Render\RendererInterface
55 * The language manager.
57 * @var \Drupal\Core\Language\LanguageManagerInterface
59 protected $languageManager;
62 * Wrapper object for simple configuration from diff.settings.yml.
64 * @var \Drupal\Core\Config\ImmutableConfig
69 * The field diff layout plugin manager service.
71 * @var \Drupal\diff\DiffLayoutManager
73 protected $diffLayoutManager;
76 * The diff entity comparison service.
78 * @var \Drupal\diff\DiffEntityComparison
80 protected $entityComparison;
83 * The entity query factory service.
85 * @var \Drupal\Core\Entity\Query\QueryFactory
87 protected $entityQuery;
90 * Constructs a RevisionOverviewForm object.
92 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
93 * The entity type manager.
94 * @param \Drupal\Core\Session\AccountInterface $current_user
96 * @param \Drupal\Core\Datetime\DateFormatter $date
98 * @param \Drupal\Core\Render\RendererInterface $renderer
99 * The renderer service.
100 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
101 * The language manager.
102 * @param \Drupal\diff\DiffLayoutManager $diff_layout_manager
103 * The diff layout service.
104 * @param \Drupal\diff\DiffEntityComparison $entity_comparison
105 * The diff entity comparison service.
106 * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
107 * The entity query factory.
109 public function __construct(EntityTypeManagerInterface $entity_type_manager, AccountInterface $current_user, DateFormatter $date, RendererInterface $renderer, LanguageManagerInterface $language_manager, DiffLayoutManager $diff_layout_manager, DiffEntityComparison $entity_comparison, QueryFactory $entity_query) {
110 $this->entityTypeManager = $entity_type_manager;
111 $this->currentUser = $current_user;
113 $this->renderer = $renderer;
114 $this->languageManager = $language_manager;
115 $this->config = $this->config('diff.settings');
116 $this->diffLayoutManager = $diff_layout_manager;
117 $this->entityComparison = $entity_comparison;
118 $this->entityQuery = $entity_query;
124 public static function create(ContainerInterface $container) {
126 $container->get('entity_type.manager'),
127 $container->get('current_user'),
128 $container->get('date.formatter'),
129 $container->get('renderer'),
130 $container->get('language_manager'),
131 $container->get('plugin.manager.diff.layout'),
132 $container->get('diff.entity_comparison'),
133 $container->get('entity.query')
140 public function getFormId() {
141 return 'revision_overview_form';
147 public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
148 $account = $this->currentUser;
149 /** @var \Drupal\node\NodeInterface $node */
150 $langcode = $node->language()->getId();
151 $langname = $node->language()->getName();
152 $languages = $node->getTranslationLanguages();
153 $has_translations = (count($languages) > 1);
154 $node_storage = $this->entityTypeManager->getStorage('node');
155 $type = $node->getType();
157 $pagerLimit = $this->config->get('general_settings.revision_pager_limit');
159 $query = $this->entityQuery->get('node')
160 ->condition($node->getEntityType()->getKey('id'), $node->id())
163 ->sort($node->getEntityType()->getKey('revision'), 'DESC')
165 $vids = array_keys($query);
167 $revision_count = count($vids);
169 $build['#title'] = $has_translations ? $this->t('@langname revisions for %title', [
170 '@langname' => $langname,
171 '%title' => $node->label(),
172 ]) : $this->t('Revisions for %title', [
173 '%title' => $node->label(),
175 $build['nid'] = array(
177 '#value' => $node->id(),
181 $table_header['revision'] = $this->t('Revision');
183 // Allow comparisons only if there are 2 or more revisions.
184 if ($revision_count > 1) {
185 $table_header += array(
186 'select_column_one' => '',
187 'select_column_two' => '',
190 $table_header['operations'] = $this->t('Operations');
192 $rev_revert_perm = $account->hasPermission("revert $type revisions") ||
193 $account->hasPermission('revert all revisions') ||
194 $account->hasPermission('administer nodes');
195 $rev_delete_perm = $account->hasPermission("delete $type revisions") ||
196 $account->hasPermission('delete all revisions') ||
197 $account->hasPermission('administer nodes');
198 $revert_permission = $rev_revert_perm && $node->access('update');
199 $delete_permission = $rev_delete_perm && $node->access('delete');
201 // Contains the table listing the revisions.
202 $build['node_revisions_table'] = array(
204 '#header' => $table_header,
205 '#attributes' => array('class' => array('diff-revisions')),
208 $build['node_revisions_table']['#attached']['library'][] = 'diff/diff.general';
209 $build['node_revisions_table']['#attached']['drupalSettings']['diffRevisionRadios'] = $this->config->get('general_settings.radio_behavior');
211 $default_revision = $node->getRevisionId();
212 // Add rows to the table.
213 foreach ($vids as $key => $vid) {
214 $previous_revision = NULL;
215 if (isset($vids[$key + 1])) {
216 $previous_revision = $node_storage->loadRevision($vids[$key + 1]);
218 /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
219 if ($revision = $node_storage->loadRevision($vid)) {
220 if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
222 '#theme' => 'username',
223 '#account' => $revision->getRevisionAuthor(),
225 $revision_date = $this->date->format($revision->getRevisionCreationTime(), 'short');
226 // Use revision link to link to revisions that are not active.
227 if ($vid != $node->getRevisionId()) {
228 $link = Link::fromTextAndUrl($revision_date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
231 $link = $node->toLink($revision_date);
234 if ($vid == $default_revision) {
236 'revision' => $this->buildRevision($link, $username, $revision, $previous_revision),
239 // Allow comparisons only if there are 2 or more revisions.
240 if ($revision_count > 1) {
242 'select_column_one' => $this->buildSelectColumn('radios_left', $vid, FALSE),
243 'select_column_two' => $this->buildSelectColumn('radios_right', $vid, $vid),
246 $row['operations'] = array(
248 '#markup' => $this->t('Current revision'),
249 '#suffix' => '</em>',
250 '#attributes' => array(
251 'class' => array('revision-current'),
254 $row['#attributes'] = [
255 'class' => ['revision-current'],
259 $route_params = array(
260 'node' => $node->id(),
261 'node_revision' => $vid,
262 'langcode' => $langcode,
265 if ($revert_permission) {
267 'title' => $vid < $node->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'),
268 'url' => $has_translations ?
269 Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) :
270 Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
273 if ($delete_permission) {
274 $links['delete'] = array(
275 'title' => $this->t('Delete'),
276 'url' => Url::fromRoute('node.revision_delete_confirm', $route_params),
280 // Here we don't have to deal with 'only one revision' case because
281 // if there's only one revision it will also be the default one,
282 // entering on the first branch of this if else statement.
284 'revision' => $this->buildRevision($link, $username, $revision, $previous_revision),
285 'select_column_one' => $this->buildSelectColumn('radios_left', $vid,
286 isset($vids[1]) ? $vids[1] : FALSE),
287 'select_column_two' => $this->buildSelectColumn('radios_right', $vid, FALSE),
289 '#type' => 'operations',
294 // Add the row to the table.
295 $build['node_revisions_table'][] = $row;
300 // Allow comparisons only if there are 2 or more revisions.
301 if ($revision_count > 1) {
302 $build['submit'] = array(
304 '#button_type' => 'primary',
305 '#value' => t('Compare selected revisions'),
306 '#attributes' => array(
313 $build['pager'] = array(
316 $build['#attached']['library'][] = 'node/drupal.node.admin';
321 * Set column attributes and return config array.
323 * @param string $name
325 * @param string $return_val
326 * Return value attribute.
327 * @param string $default_val
328 * Default value attribute.
331 * Configuration array.
333 protected function buildSelectColumn($name, $return_val, $default_val) {
336 '#title_display' => 'invisible',
338 '#return_value' => $return_val,
339 '#default_value' => $default_val,
344 * Set and return configuration for revision.
346 * @param \Drupal\Core\Link $link
348 * @param string $username
349 * Username attribute.
350 * @param \Drupal\Core\Entity\ContentEntityInterface $revision
351 * Revision parameter for getRevisionDescription function.
352 * @param \Drupal\Core\Entity\ContentEntityInterface $previous_revision
353 * (optional) Previous revision for getRevisionDescription function.
357 * Configuration for revision.
359 protected function buildRevision(Link $link, $username, ContentEntityInterface $revision, ContentEntityInterface $previous_revision = NULL) {
361 '#type' => 'inline_template',
362 '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}',
364 'date' => $link->toString(),
365 'username' => $this->renderer->renderPlain($username),
367 '#markup' => $this->entityComparison->getRevisionDescription($revision, $previous_revision),
368 '#allowed_tags' => Xss::getAdminTagList(),
377 public function validateForm(array &$form, FormStateInterface $form_state) {
378 $input = $form_state->getUserInput();
380 if (count($form_state->getValue('node_revisions_table')) <= 1) {
381 $form_state->setErrorByName('node_revisions_table', $this->t('Multiple revisions are needed for comparison.'));
383 elseif (!isset($input['radios_left']) || !isset($input['radios_right'])) {
384 $form_state->setErrorByName('node_revisions_table', $this->t('Select two revisions to compare.'));
386 elseif ($input['radios_left'] == $input['radios_right']) {
387 // @todo Radio-boxes selection resets if there are errors.
388 $form_state->setErrorByName('node_revisions_table', $this->t('Select different revisions to compare.'));
395 public function submitForm(array &$form, FormStateInterface $form_state) {
396 $input = $form_state->getUserInput();
397 $vid_left = $input['radios_left'];
398 $vid_right = $input['radios_right'];
399 $nid = $input['nid'];
401 // Always place the older revision on the left side of the comparison
402 // and the newer revision on the right side (however revisions can be
403 // compared both ways if we manually change the order of the parameters).
404 if ($vid_left > $vid_right) {
406 $vid_left = $vid_right;
409 // Builds the redirect Url.
410 $redirect_url = Url::fromRoute(
411 'diff.revisions_diff',
414 'left_revision' => $vid_left,
415 'right_revision' => $vid_right,
416 'filter' => $this->diffLayoutManager->getDefaultLayout(),
419 $form_state->setRedirectUrl($redirect_url);