e873f91f56890d1dbc949fd52b6895c5f8b93f54
[yaffs-website] / web / core / modules / node / src / Controller / NodeController.php
1 <?php
2
3 namespace Drupal\node\Controller;
4
5 use Drupal\Component\Utility\Xss;
6 use Drupal\Core\Controller\ControllerBase;
7 use Drupal\Core\Datetime\DateFormatterInterface;
8 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
9 use Drupal\Core\Render\RendererInterface;
10 use Drupal\Core\Url;
11 use Drupal\node\NodeStorageInterface;
12 use Drupal\node\NodeTypeInterface;
13 use Drupal\node\NodeInterface;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15
16 /**
17  * Returns responses for Node routes.
18  */
19 class NodeController extends ControllerBase implements ContainerInjectionInterface {
20
21   /**
22    * The date formatter service.
23    *
24    * @var \Drupal\Core\Datetime\DateFormatterInterface
25    */
26   protected $dateFormatter;
27
28   /**
29    * The renderer service.
30    *
31    * @var \Drupal\Core\Render\RendererInterface
32    */
33   protected $renderer;
34
35   /**
36    * Constructs a NodeController object.
37    *
38    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
39    *   The date formatter service.
40    * @param \Drupal\Core\Render\RendererInterface $renderer
41    *   The renderer service.
42    */
43   public function __construct(DateFormatterInterface $date_formatter, RendererInterface $renderer) {
44     $this->dateFormatter = $date_formatter;
45     $this->renderer = $renderer;
46   }
47
48   /**
49    * {@inheritdoc}
50    */
51   public static function create(ContainerInterface $container) {
52     return new static(
53       $container->get('date.formatter'),
54       $container->get('renderer')
55     );
56   }
57
58   /**
59    * Displays add content links for available content types.
60    *
61    * Redirects to node/add/[type] if only one content type is available.
62    *
63    * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
64    *   A render array for a list of the node types that can be added; however,
65    *   if there is only one node type defined for the site, the function
66    *   will return a RedirectResponse to the node add page for that one node
67    *   type.
68    */
69   public function addPage() {
70     $build = [
71       '#theme' => 'node_add_list',
72       '#cache' => [
73         'tags' => $this->entityManager()->getDefinition('node_type')->getListCacheTags(),
74       ],
75     ];
76
77     $content = [];
78
79     // Only use node types the user has access to.
80     foreach ($this->entityManager()->getStorage('node_type')->loadMultiple() as $type) {
81       $access = $this->entityManager()->getAccessControlHandler('node')->createAccess($type->id(), NULL, [], TRUE);
82       if ($access->isAllowed()) {
83         $content[$type->id()] = $type;
84       }
85       $this->renderer->addCacheableDependency($build, $access);
86     }
87
88     // Bypass the node/add listing if only one content type is available.
89     if (count($content) == 1) {
90       $type = array_shift($content);
91       return $this->redirect('node.add', ['node_type' => $type->id()]);
92     }
93
94     $build['#content'] = $content;
95
96     return $build;
97   }
98
99   /**
100    * Provides the node submission form.
101    *
102    * @param \Drupal\node\NodeTypeInterface $node_type
103    *   The node type entity for the node.
104    *
105    * @return array
106    *   A node submission form.
107    */
108   public function add(NodeTypeInterface $node_type) {
109     $node = $this->entityManager()->getStorage('node')->create([
110       'type' => $node_type->id(),
111     ]);
112
113     $form = $this->entityFormBuilder()->getForm($node);
114
115     return $form;
116   }
117
118   /**
119    * Displays a node revision.
120    *
121    * @param int $node_revision
122    *   The node revision ID.
123    *
124    * @return array
125    *   An array suitable for drupal_render().
126    */
127   public function revisionShow($node_revision) {
128     $node = $this->entityManager()->getStorage('node')->loadRevision($node_revision);
129     $node = $this->entityManager()->getTranslationFromContext($node);
130     $node_view_controller = new NodeViewController($this->entityManager, $this->renderer, $this->currentUser());
131     $page = $node_view_controller->view($node);
132     unset($page['nodes'][$node->id()]['#cache']);
133     return $page;
134   }
135
136   /**
137    * Page title callback for a node revision.
138    *
139    * @param int $node_revision
140    *   The node revision ID.
141    *
142    * @return string
143    *   The page title.
144    */
145   public function revisionPageTitle($node_revision) {
146     $node = $this->entityManager()->getStorage('node')->loadRevision($node_revision);
147     return $this->t('Revision of %title from %date', ['%title' => $node->label(), '%date' => format_date($node->getRevisionCreationTime())]);
148   }
149
150   /**
151    * Generates an overview table of older revisions of a node.
152    *
153    * @param \Drupal\node\NodeInterface $node
154    *   A node object.
155    *
156    * @return array
157    *   An array as expected by drupal_render().
158    */
159   public function revisionOverview(NodeInterface $node) {
160     $account = $this->currentUser();
161     $langcode = $node->language()->getId();
162     $langname = $node->language()->getName();
163     $languages = $node->getTranslationLanguages();
164     $has_translations = (count($languages) > 1);
165     $node_storage = $this->entityManager()->getStorage('node');
166     $type = $node->getType();
167
168     $build['#title'] = $has_translations ? $this->t('@langname revisions for %title', ['@langname' => $langname, '%title' => $node->label()]) : $this->t('Revisions for %title', ['%title' => $node->label()]);
169     $header = [$this->t('Revision'), $this->t('Operations')];
170
171     $revert_permission = (($account->hasPermission("revert $type revisions") || $account->hasPermission('revert all revisions') || $account->hasPermission('administer nodes')) && $node->access('update'));
172     $delete_permission = (($account->hasPermission("delete $type revisions") || $account->hasPermission('delete all revisions') || $account->hasPermission('administer nodes')) && $node->access('delete'));
173
174     $rows = [];
175     $default_revision = $node->getRevisionId();
176     $current_revision_displayed = FALSE;
177
178     foreach ($this->getRevisionIds($node, $node_storage) as $vid) {
179       /** @var \Drupal\node\NodeInterface $revision */
180       $revision = $node_storage->loadRevision($vid);
181       // Only show revisions that are affected by the language that is being
182       // displayed.
183       if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) {
184         $username = [
185           '#theme' => 'username',
186           '#account' => $revision->getRevisionUser(),
187         ];
188
189         // Use revision link to link to revisions that are not active.
190         $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short');
191
192         // We treat also the latest translation-affecting revision as current
193         // revision, if it was the default revision, as its values for the
194         // current language will be the same of the current default revision in
195         // this case.
196         $is_current_revision = $vid == $default_revision || (!$current_revision_displayed && $revision->wasDefaultRevision());
197         if (!$is_current_revision) {
198           $link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
199         }
200         else {
201           $link = $node->link($date);
202           $current_revision_displayed = TRUE;
203         }
204
205         $row = [];
206         $column = [
207           'data' => [
208             '#type' => 'inline_template',
209             '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}',
210             '#context' => [
211               'date' => $link,
212               'username' => $this->renderer->renderPlain($username),
213               'message' => ['#markup' => $revision->revision_log->value, '#allowed_tags' => Xss::getHtmlTagList()],
214             ],
215           ],
216         ];
217         // @todo Simplify once https://www.drupal.org/node/2334319 lands.
218         $this->renderer->addCacheableDependency($column['data'], $username);
219         $row[] = $column;
220
221         if ($is_current_revision) {
222           $row[] = [
223             'data' => [
224               '#prefix' => '<em>',
225               '#markup' => $this->t('Current revision'),
226               '#suffix' => '</em>',
227             ],
228           ];
229
230           $rows[] = [
231             'data' => $row,
232             'class' => ['revision-current'],
233           ];
234         }
235         else {
236           $links = [];
237           if ($revert_permission) {
238             $links['revert'] = [
239               'title' => $vid < $node->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'),
240               'url' => $has_translations ?
241                 Url::fromRoute('node.revision_revert_translation_confirm', ['node' => $node->id(), 'node_revision' => $vid, 'langcode' => $langcode]) :
242                 Url::fromRoute('node.revision_revert_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
243             ];
244           }
245
246           if ($delete_permission) {
247             $links['delete'] = [
248               'title' => $this->t('Delete'),
249               'url' => Url::fromRoute('node.revision_delete_confirm', ['node' => $node->id(), 'node_revision' => $vid]),
250             ];
251           }
252
253           $row[] = [
254             'data' => [
255               '#type' => 'operations',
256               '#links' => $links,
257             ],
258           ];
259
260           $rows[] = $row;
261         }
262       }
263     }
264
265     $build['node_revisions_table'] = [
266       '#theme' => 'table',
267       '#rows' => $rows,
268       '#header' => $header,
269       '#attached' => [
270         'library' => ['node/drupal.node.admin'],
271       ],
272       '#attributes' => ['class' => 'node-revision-table'],
273     ];
274
275     $build['pager'] = ['#type' => 'pager'];
276
277     return $build;
278   }
279
280   /**
281    * The _title_callback for the node.add route.
282    *
283    * @param \Drupal\node\NodeTypeInterface $node_type
284    *   The current node.
285    *
286    * @return string
287    *   The page title.
288    */
289   public function addPageTitle(NodeTypeInterface $node_type) {
290     return $this->t('Create @name', ['@name' => $node_type->label()]);
291   }
292
293   /**
294    * Gets a list of node revision IDs for a specific node.
295    *
296    * @param \Drupal\node\NodeInterface $node
297    *   The node entity.
298    * @param \Drupal\node\NodeStorageInterface $node_storage
299    *   The node storage handler.
300    *
301    * @return int[]
302    *   Node revision IDs (in descending order).
303    */
304   protected function getRevisionIds(NodeInterface $node, NodeStorageInterface $node_storage) {
305     $result = $node_storage->getQuery()
306       ->allRevisions()
307       ->condition($node->getEntityType()->getKey('id'), $node->id())
308       ->sort($node->getEntityType()->getKey('revision'), 'DESC')
309       ->pager(50)
310       ->execute();
311     return array_keys($result);
312   }
313
314 }