NodeController.php

Same filename in other branches
  1. 9 core/modules/node/src/Controller/NodeController.php
  2. 8.9.x core/modules/node/src/Controller/NodeController.php
  3. 11.x core/modules/node/src/Controller/NodeController.php

Namespace

Drupal\node\Controller

File

core/modules/node/src/Controller/NodeController.php

View source
<?php

namespace Drupal\node\Controller;

use Drupal\Component\Utility\Xss;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Drupal\node\NodeStorageInterface;
use Drupal\node\NodeTypeInterface;
use Drupal\node\NodeInterface;

/**
 * Returns responses for Node routes.
 */
class NodeController extends ControllerBase implements ContainerInjectionInterface {
    
    /**
     * The date formatter service.
     *
     * @var \Drupal\Core\Datetime\DateFormatterInterface
     */
    protected $dateFormatter;
    
    /**
     * The renderer service.
     *
     * @var \Drupal\Core\Render\RendererInterface
     */
    protected $renderer;
    
    /**
     * The entity repository service.
     *
     * @var \Drupal\Core\Entity\EntityRepositoryInterface
     */
    protected $entityRepository;
    
    /**
     * Constructs a NodeController object.
     *
     * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
     *   The date formatter service.
     * @param \Drupal\Core\Render\RendererInterface $renderer
     *   The renderer service.
     * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
     *   The entity repository.
     */
    public function __construct(DateFormatterInterface $date_formatter, RendererInterface $renderer, EntityRepositoryInterface $entity_repository) {
        $this->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 %}<p class="revision-log">{{ message }}</p>{% 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' => '<em>',
                            '#markup' => $this->t('Current revision'),
                            '#suffix' => '</em>',
                        ],
                    ];
                    $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);
    }

}

Classes

Title Deprecated Summary
NodeController Returns responses for Node routes.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.