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