dateFormatter = $date_formatter; $this->renderer = $renderer; $this->entityRepository = $entity_repository; } /** * Displays add content links for available content types. * * Redirects to node/add/[type] if only one content type is available. * * @return array|\Symfony\Component\HttpFoundation\RedirectResponse * A render array for a list of the node types that can be added; however, * if there is only one node type defined for the site, the function * will return a RedirectResponse to the node add page for that one node * type. */ public function addPage() { $definition = $this->entityTypeManager()->getDefinition('node_type'); $build = [ '#theme' => 'node_add_list', '#cache' => [ 'tags' => $this->entityTypeManager()->getDefinition('node_type')->getListCacheTags(), ], ]; $content = []; $types = $this->entityTypeManager()->getStorage('node_type')->loadMultiple(); uasort($types, [$definition->getClass(), 'sort']); // Only use node types the user has access to. foreach ($types as $type) { $access = $this->entityTypeManager()->getAccessControlHandler('node')->createAccess($type->id(), NULL, [], TRUE); if ($access->isAllowed()) { $content[$type->id()] = $type; } $this->renderer->addCacheableDependency($build, $access); } // Bypass the node/add listing if only one content type is available. if (count($content) == 1) { $type = array_shift($content); return $this->redirect('node.add', ['node_type' => $type->id()]); } $build['#content'] = $content; return $build; } /** * Displays a node revision. * * @param \Drupal\node\NodeInterface $node_revision * The node revision. * * @return array * An array suitable for \Drupal\Core\Render\RendererInterface::render(). */ public function revisionShow(NodeInterface $node_revision) { $node_view_controller = new NodeViewController($this->entityTypeManager(), $this->renderer, $this->currentUser(), $this->entityRepository); $page = $node_view_controller->view($node_revision); unset($page['nodes'][$node_revision->id()]['#cache']); return $page; } /** * Page title callback for a node revision. * * @param \Drupal\node\NodeInterface $node_revision * The node revision. * * @return string * The page title. */ public function revisionPageTitle(NodeInterface $node_revision) { return $this->t('Revision of %title from %date', [ '%title' => $node_revision->label(), '%date' => $this->dateFormatter->format($node_revision->getRevisionCreationTime()), ]); } /** * Generates an overview table of older revisions of a node. * * @param \Drupal\node\NodeInterface $node * A node object. * * @return array * An array as expected by \Drupal\Core\Render\RendererInterface::render(). */ public function revisionOverview(NodeInterface $node) { // Always use the latest revision in the current content language to // determine if this node has translations. This supports showing the // correct translation revisions for translations that only have. // non-default revisions. $node = $this->entityRepository->getActive($node->getEntityTypeId(), $node->id()); $langcode = $node->language()->getId(); $language_name = $node->language()->getName(); $languages = $node->getTranslationLanguages(); $has_translations = (count($languages) > 1); $node_storage = $this->entityTypeManager()->getStorage('node'); $build['#title'] = $has_translations ? $this->t('@language_name revisions for %title', ['@language_name' => $language_name, '%title' => $node->label()]) : $this->t('Revisions for %title', ['%title' => $node->label()]); $header = [$this->t('Revision'), $this->t('Operations')]; $rows = []; $current_revision_displayed = FALSE; foreach ($this->getRevisionIds($node, $node_storage) as $vid) { /** @var \Drupal\node\NodeInterface $revision */ $revision = $node_storage->loadRevision($vid); // Only show revisions that are affected by the language that is being // displayed. if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) { $username = [ '#theme' => 'username', '#account' => $revision->getRevisionUser(), ]; // Use revision link to link to revisions that are not active. $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short'); // We treat also the latest translation-affecting revision as current // revision, if it was the default revision, as its values for the // current language will be the same of the current default revision in // this case. $is_current_revision = $revision->isDefaultRevision() || (!$current_revision_displayed && $revision->wasDefaultRevision()); if (!$is_current_revision) { $link = Link::fromTextAndUrl($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]))->toString(); } else { $link = $node->toLink($date)->toString(); $current_revision_displayed = TRUE; } $row = []; $column = [ 'data' => [ '#type' => 'inline_template', '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}
{{ message }}
{% endif %}', '#context' => [ 'date' => $link, 'username' => $this->renderer->renderInIsolation($username), 'message' => ['#markup' => $revision->revision_log->value, '#allowed_tags' => Xss::getHtmlTagList()], ], ], ]; // @todo Simplify once https://www.drupal.org/node/2334319 lands. $this->renderer->addCacheableDependency($column['data'], $username); $row[] = $column; if ($is_current_revision) { $row[] = [ 'data' => [ '#prefix' => '', '#markup' => $this->t('Current revision'), '#suffix' => '', ], ]; $rows[] = [ 'data' => $row, 'class' => ['revision-current'], ]; } else { $links = []; if ($revision->access('revert revision')) { $links['revert'] = [ 'title' => $vid < $node->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'), 'url' => $has_translations ? Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) : Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]), ]; } if ($revision->access('delete revision')) { $links['delete'] = [ 'title' => $this->t('Delete'), 'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]), ]; } $row[] = [ 'data' => [ '#type' => 'operations', '#links' => $links, ], ]; $rows[] = $row; } } } $build['node_revisions_table'] = [ '#theme' => 'table', '#rows' => $rows, '#header' => $header, '#attached' => [ 'library' => ['node/drupal.node.admin'], ], '#attributes' => ['class' => ['node-revision-table']], ]; $build['pager'] = ['#type' => 'pager']; return $build; } /** * The _title_callback for the node.add route. * * @param \Drupal\node\NodeTypeInterface $node_type * The current node. * * @return string * The page title. */ public function addPageTitle(NodeTypeInterface $node_type) { return $this->t('Create @name', ['@name' => $node_type->label()]); } /** * Gets a list of node revision IDs for a specific node. * * @param \Drupal\node\NodeInterface $node * The node entity. * @param \Drupal\node\NodeStorageInterface $node_storage * The node storage handler. * * @return int[] * Node revision IDs (in descending order). */ protected function getRevisionIds(NodeInterface $node, NodeStorageInterface $node_storage) { $result = $node_storage->getQuery() ->accessCheck(TRUE) ->allRevisions() ->condition($node->getEntityType()->getKey('id'), $node->id()) ->sort($node->getEntityType()->getKey('revision'), 'DESC') ->pager(50) ->execute(); return array_keys($result); } }