config = $this->config('diff.settings'); $this->diffLayoutManager = $diff_layout_manager; $this->entityComparison = $entity_comparison; $this->requestStack = $request_stack; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('diff.entity_comparison'), $container->get('plugin.manager.diff.layout'), $container->get('request_stack') ); } /** * Get all the revision ids of given entity id. * * @param \Drupal\Core\Entity\EntityStorageInterface $storage * The entity storage manager. * @param int $entity_id * The entity to find revisions of. * * @return int[] * The revision ids. */ public function getRevisionIds(EntityStorageInterface $storage, $entity_id) { $result = $storage->getQuery() ->allRevisions() ->condition($storage->getEntityType()->getKey('id'), $entity_id) ->accessCheck(FALSE) ->execute(); $result_array = array_keys($result); sort($result_array); return $result_array; } /** * Returns a table which shows the differences between two entity revisions. * * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The route match. * @param \Drupal\Core\Entity\ContentEntityInterface $left_revision * The left revision. * @param \Drupal\Core\Entity\ContentEntityInterface $right_revision * The right revision. * @param string $filter * If $filter == 'raw' raw text is compared (including html tags) * If filter == 'raw-plain' markdown function is applied to the text before comparison. * * @return array * Table showing the diff between the two entity revisions. */ public function compareEntityRevisions(RouteMatchInterface $route_match, ContentEntityInterface $left_revision, ContentEntityInterface $right_revision, $filter) { $entity_type_id = $left_revision->getEntityTypeId(); /** @var \Drupal\Core\Entity\EntityInterface $entity */ $entity = $route_match->getParameter($entity_type_id); $entity_type_id = $entity->getEntityTypeId(); $storage = $this->entityTypeManager()->getStorage($entity_type_id); // Get language from the entity context. $langcode = $entity->language()->getId(); // Get left and right revision in current language. $left_revision = $left_revision->getTranslation($langcode); $right_revision = $right_revision->getTranslation($langcode); $revisions_ids = []; // Filter revisions of current translation and where the translation is // affected. foreach ($this->getRevisionIds($storage, $entity->id()) as $revision_id) { /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ $revision = $storage->loadRevision($revision_id); if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) { $revisions_ids[] = $revision_id; } } $build = [ '#title' => $this->t('Changes to %title', ['%title' => $entity->label()]), 'header' => [ '#prefix' => '
', '#suffix' => '
', ], 'controls' => [ '#prefix' => '
', '#suffix' => '
', ], ]; // Build the navigation links. $build['header']['diff_navigation'] = $this->buildRevisionsNavigation($entity, $revisions_ids, $left_revision->getRevisionId(), $right_revision->getRevisionId(), $filter); // Build the layout filter. $build['controls']['diff_layout'] = [ '#type' => 'item', '#title' => $this->t('Layout'), '#wrapper_attributes' => ['class' => 'diff-controls__item'], 'filter' => $this->buildLayoutNavigation($entity, $left_revision->getRevisionId(), $right_revision->getRevisionId(), $filter), ]; // Perform comparison only if both entity revisions loaded successfully. if ($left_revision != FALSE && $right_revision != FALSE) { // Build the diff comparison with the plugin. if ($plugin = $this->diffLayoutManager->createInstance($filter)) { $build = array_merge_recursive($build, $plugin->build($left_revision, $right_revision, $entity)); $build['diff']['#prefix'] = '
'; $build['diff']['#suffix'] = '
'; $build['diff']['#attributes']['class'][] = 'diff-responsive-table'; } } $build['#attached']['library'][] = 'diff/diff.general'; return $build; } /** * Builds a navigation dropdown button between the layout plugins. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity to be compared. * @param int $left_revision_id * Revision id of the left revision. * @param int $right_revision_id * Revision id of the right revision. * @param string $active_filter * The active filter. * * @return array * The layout filter. */ protected function buildLayoutNavigation(ContentEntityInterface $entity, $left_revision_id, $right_revision_id, $active_filter) { $links = []; $layouts = $this->diffLayoutManager->getPluginOptions(); foreach ($layouts as $key => $value) { $links[$key] = array( 'title' => $value, 'url' => $this->diffRoute($entity, $left_revision_id, $right_revision_id, $key), ); } // Set as the first element the current filter. $filter = $links[$active_filter]; unset($links[$active_filter]); array_unshift($links, $filter); $filter = [ '#type' => 'operations', '#links' => $links, ]; return $filter; } /** * Creates navigation links between the previous changes and the new ones. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity to be compared. * @param array $revision_ids * The revision ids. * @param int $left_revision_id * Revision id of the left revision. * @param int $right_revision_id * Revision id of the right revision. * @param string $filter * The filter. * * @return array * The revision navigation links. */ protected function buildRevisionsNavigation(ContentEntityInterface $entity, array $revision_ids, $left_revision_id, $right_revision_id, $filter) { $revisions_count = count($revision_ids); $layout_options = &drupal_static(__FUNCTION__); if (!isset($layout_options)) { $layout_options = UrlHelper::filterQueryParameters($this->requestStack->getCurrentRequest()->query->all(), ['page']); } // If there are only 2 revision return an empty row. if ($revisions_count == 2) { return []; } else { $left_link = $right_link = ''; $element = [ '#type' => 'item', '#title' => $this->t('Navigation'), '#wrapper_attributes' => ['class' => 'diff-navigation'], ]; $i = 0; // Find the previous revision. while ($left_revision_id > $revision_ids[$i]) { $i += 1; } if ($i != 0) { // Build the left link. $left_link = Link::fromTextAndUrl($this->t('Previous change'), $this->diffRoute($entity, $revision_ids[$i - 1], $left_revision_id, $filter, $layout_options))->toString(); } $element['left'] = [ '#type' => 'markup', '#markup' => $left_link, '#prefix' => '', ]; // Find the next revision. $i = 0; while ($i < $revisions_count && $right_revision_id >= $revision_ids[$i]) { $i += 1; } if ($revisions_count != $i && $revision_ids[$i - 1] != $revision_ids[$revisions_count - 1]) { // Build the right link. $right_link = Link::fromTextAndUrl($this->t('Next change'), $this->diffRoute($entity, $right_revision_id, $revision_ids[$i], $filter, $layout_options))->toString(); } $element['right'] = [ '#type' => 'markup', '#markup' => $right_link, '#prefix' => '', ]; return $element; } } /** * Creates an url object for diff. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity to be compared. * @param int $left_revision_id * Revision id of the left revision. * @param int $right_revision_id * Revision id of the right revision. * @param string $layout * (optional) The filter/layout added to the route. * @param array $layout_options * (optional) The layout options provided by the selected layout. * * @return \Drupal\Core\Url * The URL object. */ public static function diffRoute(ContentEntityInterface $entity, $left_revision_id, $right_revision_id, $layout = NULL, array $layout_options = NULL) { $entity_type_id = $entity->getEntityTypeId(); // @todo Remove the diff.revisions_diff route so we avoid adding extra cases. if ($entity->getEntityTypeId() == 'node') { $route_name = 'diff.revisions_diff'; } else { $route_name = "entity.$entity_type_id.revisions_diff"; } $route_parameters = [ $entity_type_id => $entity->id(), 'left_revision' => $left_revision_id, 'right_revision' => $right_revision_id, ]; if ($layout) { $route_parameters['filter'] = $layout; } $options = []; if ($layout_options) { $options = [ 'query' => $layout_options, ]; } return Url::fromRoute($route_name, $route_parameters, $options); } }