Mercurial > hg > isophonics-drupal-site
comparison core/modules/comment/comment.module @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | c2387f117808 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * @file | |
5 * Enables users to comment on published content. | |
6 * | |
7 * When installed, the Comment module creates a field that facilitates a | |
8 * discussion board for each Drupal entity to which a comment field is attached. | |
9 * Users can post comments to discuss a forum topic, story, collaborative | |
10 * book page, user etc. | |
11 */ | |
12 | |
13 use Drupal\comment\CommentInterface; | |
14 use Drupal\comment\Entity\CommentType; | |
15 use Drupal\Core\Entity\FieldableEntityInterface; | |
16 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; | |
17 use Drupal\Core\Entity\Entity\EntityViewMode; | |
18 use Drupal\Core\Entity\EntityInterface; | |
19 use Drupal\Core\Form\FormStateInterface; | |
20 use Drupal\Core\Routing\RouteMatchInterface; | |
21 use Drupal\Core\Entity\Display\EntityViewDisplayInterface; | |
22 use Drupal\Core\Render\Element; | |
23 use Drupal\Core\Url; | |
24 use Drupal\field\FieldConfigInterface; | |
25 use Drupal\field\FieldStorageConfigInterface; | |
26 use Drupal\node\NodeInterface; | |
27 use Drupal\user\RoleInterface; | |
28 | |
29 /** | |
30 * Anonymous posters cannot enter their contact information. | |
31 * | |
32 * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. | |
33 * Use \Drupal\comment\CommentInterface::ANONYMOUS_MAYNOT_CONTACT instead. | |
34 * | |
35 * @see https://www.drupal.org/node/2831620 | |
36 */ | |
37 const COMMENT_ANONYMOUS_MAYNOT_CONTACT = 0; | |
38 | |
39 /** | |
40 * Anonymous posters may leave their contact information. | |
41 * | |
42 * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. | |
43 * Use \Drupal\comment\CommentInterface::ANONYMOUS_MAY_CONTACT instead. | |
44 * | |
45 * @see https://www.drupal.org/node/2831620 | |
46 */ | |
47 const COMMENT_ANONYMOUS_MAY_CONTACT = 1; | |
48 | |
49 /** | |
50 * Anonymous posters are required to leave their contact information. | |
51 * | |
52 * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0. | |
53 * Use \Drupal\comment\CommentInterface::ANONYMOUS_MUST_CONTACT instead. | |
54 * | |
55 * @see https://www.drupal.org/node/2831620 | |
56 */ | |
57 const COMMENT_ANONYMOUS_MUST_CONTACT = 2; | |
58 | |
59 /** | |
60 * The time cutoff for comments marked as read for entity types other node. | |
61 * | |
62 * Comments changed before this time are always marked as read. | |
63 * Comments changed after this time may be marked new, updated, or read, | |
64 * depending on their state for the current user. Defaults to 30 days ago. | |
65 * | |
66 * @todo Remove when https://www.drupal.org/node/1029708 lands. | |
67 */ | |
68 define('COMMENT_NEW_LIMIT', REQUEST_TIME - 30 * 24 * 60 * 60); | |
69 | |
70 /** | |
71 * Implements hook_help(). | |
72 */ | |
73 function comment_help($route_name, RouteMatchInterface $route_match) { | |
74 switch ($route_name) { | |
75 case 'help.page.comment': | |
76 $output = '<h3>' . t('About') . '</h3>'; | |
77 $output .= '<p>' . t('The Comment module allows users to comment on site content, set commenting defaults and permissions, and moderate comments. For more information, see the <a href=":comment">online documentation for the Comment module</a>.', [':comment' => 'https://www.drupal.org/documentation/modules/comment']) . '</p>'; | |
78 $output .= '<h3>' . t('Uses') . '</h3>'; | |
79 $output .= '<dl>'; | |
80 $output .= '<dt>' . t('Enabling commenting') . '</dt>'; | |
81 $output .= '<dd>' . t('Comment functionality can be enabled for any entity sub-type (for example, a <a href=":content-type">content type</a>) by adding a <em>Comments</em> field on its <em>Manage fields page</em>. Adding or removing commenting for an entity through the user interface requires the <a href=":field_ui">Field UI</a> module to be enabled, even though the commenting functionality works without it. For more information on fields and entities, see the <a href=":field">Field module help page</a>.', [':content-type' => (\Drupal::moduleHandler()->moduleExists('node')) ? \Drupal::url('entity.node_type.collection') : '#', ':field' => \Drupal::url('help.page', ['name' => 'field']), ':field_ui' => (\Drupal::moduleHandler()->moduleExists('field_ui')) ? \Drupal::url('help.page', ['name' => 'field_ui']) : '#']) . '</dd>'; | |
82 $output .= '<dt>' . t('Configuring commenting settings') . '</dt>'; | |
83 $output .= '<dd>' . t('Commenting settings can be configured by editing the <em>Comments</em> field on the <em>Manage fields page</em> of an entity type if the <em>Field UI module</em> is enabled. Configuration includes the label of the comments field, the number of comments to be displayed, and whether they are shown in threaded list. Commenting can be be configured as: <em>Open</em> to allow new comments, <em>Closed</em> to view existing comments, but prevent new comments, or <em>Hidden</em> to hide existing comments and prevent new comments. Changing this configuration for an entity type will not change existing entity items.') . '</dd>'; | |
84 $output .= '<dt>' . t('Overriding default settings') . '</dt>'; | |
85 $output .= '<dd>' . t('Users with the appropriate permissions can override the default commenting settings of an entity type when they create an item of that type.') . '</dd>'; | |
86 $output .= '<dt>' . t('Adding comment types') . '</dt>'; | |
87 $output .= '<dd>' . t('Additional <em>comment types</em> can be created per entity sub-type and added on the <a href=":field">Comment types page</a>. If there are multiple comment types available you can select the appropriate one after adding a <em>Comments field</em>.', [':field' => \Drupal::url('entity.comment_type.collection')]) . '</dd>'; | |
88 $output .= '<dt>' . t('Approving and managing comments') . '</dt>'; | |
89 $output .= '<dd>' . t('Comments from users who have the <em>Skip comment approval</em> permission are published immediately. All other comments are placed in the <a href=":comment-approval">Unapproved comments</a> queue, until a user who has permission to <em>Administer comments and comment settings</em> publishes or deletes them. Published comments can be bulk managed on the <a href=":admin-comment">Published comments</a> administration page. When a comment has no replies, it remains editable by its author, as long as the author has <em>Edit own comments</em> permission.', [':comment-approval' => \Drupal::url('comment.admin_approval'), ':admin-comment' => \Drupal::url('comment.admin')]) . '</dd>'; | |
90 $output .= '</dl>'; | |
91 return $output; | |
92 | |
93 case 'entity.comment_type.collection': | |
94 $output = '<p>' . t('This page provides a list of all comment types on the site and allows you to manage the fields, form and display settings for each.') . '</p>'; | |
95 return $output; | |
96 } | |
97 } | |
98 | |
99 /** | |
100 * Entity URI callback. | |
101 */ | |
102 function comment_uri(CommentInterface $comment) { | |
103 return new Url( | |
104 'entity.comment.canonical', | |
105 [ | |
106 'comment' => $comment->id(), | |
107 ], | |
108 ['fragment' => 'comment-' . $comment->id()] | |
109 ); | |
110 } | |
111 | |
112 /** | |
113 * Implements hook_entity_extra_field_info(). | |
114 */ | |
115 function comment_entity_extra_field_info() { | |
116 $return = []; | |
117 foreach (CommentType::loadMultiple() as $comment_type) { | |
118 $return['comment'][$comment_type->id()] = [ | |
119 'form' => [ | |
120 'author' => [ | |
121 'label' => t('Author'), | |
122 'description' => t('Author textfield'), | |
123 'weight' => -2, | |
124 ], | |
125 ], | |
126 ]; | |
127 $return['comment'][$comment_type->id()]['display']['links'] = [ | |
128 'label' => t('Links'), | |
129 'description' => t('Comment operation links'), | |
130 'weight' => 100, | |
131 'visible' => TRUE, | |
132 ]; | |
133 } | |
134 | |
135 return $return; | |
136 } | |
137 | |
138 /** | |
139 * Implements hook_theme(). | |
140 */ | |
141 function comment_theme() { | |
142 return [ | |
143 'comment' => [ | |
144 'render element' => 'elements', | |
145 ], | |
146 'field__comment' => [ | |
147 'base hook' => 'field', | |
148 ], | |
149 ]; | |
150 } | |
151 | |
152 /** | |
153 * Implements hook_ENTITY_TYPE_create() for 'field_config'. | |
154 */ | |
155 function comment_field_config_create(FieldConfigInterface $field) { | |
156 if ($field->getType() == 'comment' && !$field->isSyncing()) { | |
157 // Assign default values for the field. | |
158 $default_value = $field->getDefaultValueLiteral(); | |
159 $default_value += [[]]; | |
160 $default_value[0] += [ | |
161 'status' => CommentItemInterface::OPEN, | |
162 'cid' => 0, | |
163 'last_comment_timestamp' => 0, | |
164 'last_comment_name' => '', | |
165 'last_comment_uid' => 0, | |
166 'comment_count' => 0, | |
167 ]; | |
168 $field->setDefaultValue($default_value); | |
169 } | |
170 } | |
171 | |
172 /** | |
173 * Implements hook_ENTITY_TYPE_update() for 'field_config'. | |
174 */ | |
175 function comment_field_config_update(FieldConfigInterface $field) { | |
176 if ($field->getType() == 'comment') { | |
177 // Comment field settings also affects the rendering of *comment* entities, | |
178 // not only the *commented* entities. | |
179 \Drupal::entityManager()->getViewBuilder('comment')->resetCache(); | |
180 } | |
181 } | |
182 | |
183 /** | |
184 * Implements hook_ENTITY_TYPE_insert() for 'field_storage_config'. | |
185 */ | |
186 function comment_field_storage_config_insert(FieldStorageConfigInterface $field_storage) { | |
187 if ($field_storage->getType() == 'comment') { | |
188 // Check that the target entity type uses an integer ID. | |
189 $entity_type_id = $field_storage->getTargetEntityTypeId(); | |
190 if (!_comment_entity_uses_integer_id($entity_type_id)) { | |
191 throw new \UnexpectedValueException('You cannot attach a comment field to an entity with a non-integer ID field'); | |
192 } | |
193 } | |
194 } | |
195 | |
196 /** | |
197 * Implements hook_ENTITY_TYPE_delete() for 'field_config'. | |
198 */ | |
199 function comment_field_config_delete(FieldConfigInterface $field) { | |
200 if ($field->getType() == 'comment') { | |
201 // Delete all comments that used by the entity bundle. | |
202 $entity_query = \Drupal::entityQuery('comment'); | |
203 $entity_query->condition('entity_type', $field->getEntityTypeId()); | |
204 $entity_query->condition('field_name', $field->getName()); | |
205 $cids = $entity_query->execute(); | |
206 entity_delete_multiple('comment', $cids); | |
207 } | |
208 } | |
209 | |
210 /** | |
211 * Implements hook_node_links_alter(). | |
212 */ | |
213 function comment_node_links_alter(array &$links, NodeInterface $node, array &$context) { | |
214 // Comment links are only added to node entity type for backwards | |
215 // compatibility. Should you require comment links for other entity types you | |
216 // can do so by implementing a new field formatter. | |
217 // @todo Make this configurable from the formatter. See | |
218 // https://www.drupal.org/node/1901110. | |
219 | |
220 $comment_links = \Drupal::service('comment.link_builder')->buildCommentedEntityLinks($node, $context); | |
221 $links += $comment_links; | |
222 } | |
223 | |
224 /** | |
225 * Implements hook_entity_view(). | |
226 */ | |
227 function comment_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) { | |
228 if ($entity instanceof FieldableEntityInterface && $view_mode == 'rss' && $display->getComponent('links')) { | |
229 /** @var \Drupal\comment\CommentManagerInterface $comment_manager */ | |
230 $comment_manager = \Drupal::service('comment.manager'); | |
231 $fields = $comment_manager->getFields($entity->getEntityTypeId()); | |
232 foreach ($fields as $field_name => $detail) { | |
233 if ($entity->hasField($field_name) && $entity->get($field_name)->status != CommentItemInterface::HIDDEN) { | |
234 // Add a comments RSS element which is a URL to the comments of this | |
235 // entity. | |
236 $options = [ | |
237 'fragment' => 'comments', | |
238 'absolute' => TRUE, | |
239 ]; | |
240 $entity->rss_elements[] = [ | |
241 'key' => 'comments', | |
242 'value' => $entity->url('canonical', $options), | |
243 ]; | |
244 } | |
245 } | |
246 } | |
247 } | |
248 | |
249 /** | |
250 * Implements hook_ENTITY_TYPE_view_alter() for node entities. | |
251 */ | |
252 function comment_node_view_alter(array &$build, EntityInterface $node, EntityViewDisplayInterface $display) { | |
253 if (\Drupal::moduleHandler()->moduleExists('history')) { | |
254 $build['#attributes']['data-history-node-id'] = $node->id(); | |
255 } | |
256 } | |
257 | |
258 /** | |
259 * Generates an array for rendering a comment. | |
260 * | |
261 * @param \Drupal\comment\CommentInterface $comment | |
262 * The comment object. | |
263 * @param string $view_mode | |
264 * (optional) View mode; for instance, 'full', 'teaser', etc. Defaults to | |
265 * 'full'. | |
266 * @param string $langcode | |
267 * (optional) A language code to use for rendering. Defaults to the global | |
268 * content language of the current request. | |
269 * | |
270 * @return array | |
271 * An array as expected by drupal_render(). | |
272 * | |
273 * @deprecated in Drupal 8.x and will be removed before Drupal 9.0. | |
274 * Use \Drupal::entityManager()->getViewBuilder('comment')->view(). | |
275 */ | |
276 function comment_view(CommentInterface $comment, $view_mode = 'full', $langcode = NULL) { | |
277 return entity_view($comment, $view_mode, $langcode); | |
278 } | |
279 | |
280 /** | |
281 * Constructs render array from an array of loaded comments. | |
282 * | |
283 * @param \Drupal\comment\CommentInterface[] $comments | |
284 * An array of comments as returned by entity_load_multiple(). | |
285 * @param string $view_mode | |
286 * (optional) View mode; for instance, 'full', 'teaser', etc. Defaults to | |
287 * 'full'. | |
288 * @param string $langcode | |
289 * (optional) A string indicating the language field values are to be shown | |
290 * in. If no language is provided the current content language is used. | |
291 * Defaults to NULL. | |
292 * | |
293 * @return array | |
294 * An array in the format expected by drupal_render(). | |
295 * | |
296 * @deprecated in Drupal 8.x and will be removed before Drupal 9.0. | |
297 * Use \Drupal::entityManager()->getViewBuilder('comment')->viewMultiple(). | |
298 * | |
299 * @see drupal_render() | |
300 */ | |
301 function comment_view_multiple($comments, $view_mode = 'full', $langcode = NULL) { | |
302 return entity_view_multiple($comments, $view_mode, $langcode); | |
303 } | |
304 | |
305 /** | |
306 * Implements hook_form_FORM_ID_alter() for field_ui_field_storage_add_form. | |
307 */ | |
308 function comment_form_field_ui_field_storage_add_form_alter(&$form, FormStateInterface $form_state) { | |
309 $route_match = \Drupal::routeMatch(); | |
310 if ($form_state->get('entity_type_id') == 'comment' && $route_match->getParameter('commented_entity_type')) { | |
311 $form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($route_match->getParameter('commented_entity_type'), $route_match->getParameter('field_name')); | |
312 } | |
313 if (!_comment_entity_uses_integer_id($form_state->get('entity_type_id'))) { | |
314 $optgroup = (string) t('General'); | |
315 // You cannot use comment fields on entity types with non-integer IDs. | |
316 unset($form['add']['new_storage_type']['#options'][$optgroup]['comment']); | |
317 } | |
318 } | |
319 | |
320 /** | |
321 * Implements hook_form_FORM_ID_alter(). | |
322 */ | |
323 function comment_form_field_ui_form_display_overview_form_alter(&$form, FormStateInterface $form_state) { | |
324 $route_match = \Drupal::routeMatch(); | |
325 if ($form['#entity_type'] == 'comment' && $route_match->getParameter('commented_entity_type')) { | |
326 $form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($route_match->getParameter('commented_entity_type'), $route_match->getParameter('field_name')); | |
327 } | |
328 } | |
329 | |
330 /** | |
331 * Implements hook_form_FORM_ID_alter(). | |
332 */ | |
333 function comment_form_field_ui_display_overview_form_alter(&$form, FormStateInterface $form_state) { | |
334 $route_match = \Drupal::routeMatch(); | |
335 if ($form['#entity_type'] == 'comment' && $route_match->getParameter('commented_entity_type')) { | |
336 $form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($route_match->getParameter('commented_entity_type'), $route_match->getParameter('field_name')); | |
337 } | |
338 } | |
339 | |
340 /** | |
341 * Implements hook_entity_storage_load(). | |
342 * | |
343 * @see \Drupal\comment\Plugin\Field\FieldType\CommentItem::propertyDefinitions() | |
344 */ | |
345 function comment_entity_storage_load($entities, $entity_type) { | |
346 // Comments can only be attached to content entities, so skip others. | |
347 if (!\Drupal::entityManager()->getDefinition($entity_type)->entityClassImplements(FieldableEntityInterface::class)) { | |
348 return; | |
349 } | |
350 if (!\Drupal::service('comment.manager')->getFields($entity_type)) { | |
351 // Do not query database when entity has no comment fields. | |
352 return; | |
353 } | |
354 // Load comment information from the database and update the entity's | |
355 // comment statistics properties, which are defined on each CommentItem field. | |
356 $result = \Drupal::service('comment.statistics')->read($entities, $entity_type); | |
357 foreach ($result as $record) { | |
358 // Skip fields that entity does not have. | |
359 if (!$entities[$record->entity_id]->hasField($record->field_name)) { | |
360 continue; | |
361 } | |
362 $comment_statistics = $entities[$record->entity_id]->get($record->field_name); | |
363 $comment_statistics->cid = $record->cid; | |
364 $comment_statistics->last_comment_timestamp = $record->last_comment_timestamp; | |
365 $comment_statistics->last_comment_name = $record->last_comment_name; | |
366 $comment_statistics->last_comment_uid = $record->last_comment_uid; | |
367 $comment_statistics->comment_count = $record->comment_count; | |
368 } | |
369 } | |
370 | |
371 /** | |
372 * Implements hook_entity_insert(). | |
373 */ | |
374 function comment_entity_insert(EntityInterface $entity) { | |
375 // Allow bulk updates and inserts to temporarily disable the | |
376 // maintenance of the {comment_entity_statistics} table. | |
377 if (\Drupal::state()->get('comment.maintain_entity_statistics') && | |
378 $fields = \Drupal::service('comment.manager')->getFields($entity->getEntityTypeId())) { | |
379 \Drupal::service('comment.statistics')->create($entity, $fields); | |
380 } | |
381 } | |
382 | |
383 /** | |
384 * Implements hook_entity_predelete(). | |
385 */ | |
386 function comment_entity_predelete(EntityInterface $entity) { | |
387 // Entities can have non-numeric IDs, but {comment} and | |
388 // {comment_entity_statistics} tables have integer columns for entity ID, and | |
389 // PostgreSQL throws exceptions if you attempt query conditions with | |
390 // mismatched types. So, we need to verify that the ID is numeric (even for an | |
391 // entity type that has an integer ID, $entity->id() might be a string | |
392 // containing a number), and then cast it to an integer when querying. | |
393 if ($entity instanceof FieldableEntityInterface && is_numeric($entity->id())) { | |
394 $entity_query = \Drupal::entityQuery('comment'); | |
395 $entity_query->condition('entity_id', (int) $entity->id()); | |
396 $entity_query->condition('entity_type', $entity->getEntityTypeId()); | |
397 $cids = $entity_query->execute(); | |
398 entity_delete_multiple('comment', $cids); | |
399 \Drupal::service('comment.statistics')->delete($entity); | |
400 } | |
401 } | |
402 | |
403 /** | |
404 * Determines if an entity type is using an integer-based ID definition. | |
405 * | |
406 * @param string $entity_type_id | |
407 * The ID the represents the entity type. | |
408 * | |
409 * @return bool | |
410 * Returns TRUE if the entity type has an integer-based ID definition and | |
411 * FALSE otherwise. | |
412 */ | |
413 function _comment_entity_uses_integer_id($entity_type_id) { | |
414 $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); | |
415 $entity_type_id_key = $entity_type->getKey('id'); | |
416 if ($entity_type_id_key === FALSE) { | |
417 return FALSE; | |
418 } | |
419 $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($entity_type->id()); | |
420 $entity_type_id_definition = $field_definitions[$entity_type_id_key]; | |
421 return $entity_type_id_definition->getType() === 'integer'; | |
422 } | |
423 | |
424 /** | |
425 * Implements hook_node_update_index(). | |
426 */ | |
427 function comment_node_update_index(EntityInterface $node) { | |
428 $index_comments = &drupal_static(__FUNCTION__); | |
429 | |
430 if ($index_comments === NULL) { | |
431 // Do not index in the following three cases: | |
432 // 1. 'Authenticated user' can search content but can't access comments. | |
433 // 2. 'Anonymous user' can search content but can't access comments. | |
434 // 3. Any role can search content but can't access comments and access | |
435 // comments is not granted by the 'authenticated user' role. In this case | |
436 // all users might have both permissions from various roles but it is also | |
437 // possible to set up a user to have only search content and so a user | |
438 // edit could change the security situation so it is not safe to index the | |
439 // comments. | |
440 $index_comments = TRUE; | |
441 $roles = \Drupal::entityManager()->getStorage('user_role')->loadMultiple(); | |
442 $authenticated_can_access = $roles[RoleInterface::AUTHENTICATED_ID]->hasPermission('access comments'); | |
443 foreach ($roles as $rid => $role) { | |
444 if ($role->hasPermission('search content') && !$role->hasPermission('access comments')) { | |
445 if ($rid == RoleInterface::AUTHENTICATED_ID || $rid == RoleInterface::ANONYMOUS_ID || !$authenticated_can_access) { | |
446 $index_comments = FALSE; | |
447 break; | |
448 } | |
449 } | |
450 } | |
451 } | |
452 | |
453 $build = []; | |
454 | |
455 if ($index_comments) { | |
456 foreach (\Drupal::service('comment.manager')->getFields('node') as $field_name => $info) { | |
457 // Skip fields that entity does not have. | |
458 if (!$node->hasField($field_name)) { | |
459 continue; | |
460 } | |
461 $field_definition = $node->getFieldDefinition($field_name); | |
462 $mode = $field_definition->getSetting('default_mode'); | |
463 $comments_per_page = $field_definition->getSetting('per_page'); | |
464 if ($node->get($field_name)->status) { | |
465 $comments = \Drupal::entityManager()->getStorage('comment') | |
466 ->loadThread($node, $field_name, $mode, $comments_per_page); | |
467 if ($comments) { | |
468 $build[] = \Drupal::entityManager()->getViewBuilder('comment')->viewMultiple($comments); | |
469 } | |
470 } | |
471 } | |
472 } | |
473 return \Drupal::service('renderer')->renderPlain($build); | |
474 } | |
475 | |
476 /** | |
477 * Implements hook_cron(). | |
478 */ | |
479 function comment_cron() { | |
480 // Store the maximum possible comments per thread (used for node search | |
481 // ranking by reply count). | |
482 \Drupal::state()->set('comment.node_comment_statistics_scale', 1.0 / max(1, \Drupal::service('comment.statistics')->getMaximumCount('node'))); | |
483 } | |
484 | |
485 /** | |
486 * Implements hook_node_search_result(). | |
487 * | |
488 * Formats a comment count string and returns it, for display with search | |
489 * results. | |
490 */ | |
491 function comment_node_search_result(EntityInterface $node) { | |
492 $comment_fields = \Drupal::service('comment.manager')->getFields('node'); | |
493 $comments = 0; | |
494 $open = FALSE; | |
495 foreach ($comment_fields as $field_name => $info) { | |
496 // Skip fields that entity does not have. | |
497 if (!$node->hasField($field_name)) { | |
498 continue; | |
499 } | |
500 // Do not make a string if comments are hidden. | |
501 $status = $node->get($field_name)->status; | |
502 if (\Drupal::currentUser()->hasPermission('access comments') && $status != CommentItemInterface::HIDDEN) { | |
503 if ($status == CommentItemInterface::OPEN) { | |
504 // At least one comment field is open. | |
505 $open = TRUE; | |
506 } | |
507 $comments += $node->get($field_name)->comment_count; | |
508 } | |
509 } | |
510 // Do not make a string if there are no comment fields, or no comments exist | |
511 // or all comment fields are hidden. | |
512 if ($comments > 0 || $open) { | |
513 return ['comment' => \Drupal::translation()->formatPlural($comments, '1 comment', '@count comments')]; | |
514 } | |
515 } | |
516 | |
517 /** | |
518 * Implements hook_user_cancel(). | |
519 */ | |
520 function comment_user_cancel($edit, $account, $method) { | |
521 switch ($method) { | |
522 case 'user_cancel_block_unpublish': | |
523 $comments = entity_load_multiple_by_properties('comment', ['uid' => $account->id()]); | |
524 foreach ($comments as $comment) { | |
525 $comment->setPublished(CommentInterface::NOT_PUBLISHED); | |
526 $comment->save(); | |
527 } | |
528 break; | |
529 | |
530 case 'user_cancel_reassign': | |
531 /** @var \Drupal\comment\CommentInterface[] $comments */ | |
532 $comments = entity_load_multiple_by_properties('comment', ['uid' => $account->id()]); | |
533 foreach ($comments as $comment) { | |
534 $comment->setOwnerId(0); | |
535 $comment->setAuthorName(\Drupal::config('user.settings')->get('anonymous')); | |
536 $comment->save(); | |
537 } | |
538 break; | |
539 } | |
540 } | |
541 | |
542 /** | |
543 * Implements hook_ENTITY_TYPE_predelete() for user entities. | |
544 */ | |
545 function comment_user_predelete($account) { | |
546 $entity_query = \Drupal::entityQuery('comment'); | |
547 $entity_query->condition('uid', $account->id()); | |
548 $cids = $entity_query->execute(); | |
549 entity_delete_multiple('comment', $cids); | |
550 } | |
551 | |
552 /** | |
553 * Generates a comment preview. | |
554 * | |
555 * @param \Drupal\comment\CommentInterface $comment | |
556 * The comment entity to preview. | |
557 * @param Drupal\Core\Form\FormStateInterface $form_state | |
558 * The current state of the form. | |
559 * | |
560 * @return array | |
561 * An array as expected by drupal_render(). | |
562 */ | |
563 function comment_preview(CommentInterface $comment, FormStateInterface $form_state) { | |
564 $preview_build = []; | |
565 $entity = $comment->getCommentedEntity(); | |
566 | |
567 if (!$form_state->getErrors()) { | |
568 $comment->in_preview = TRUE; | |
569 $comment_build = \Drupal::entityTypeManager()->getViewBuilder('comment')->view($comment); | |
570 $comment_build['#weight'] = -100; | |
571 | |
572 $preview_build['comment_preview'] = $comment_build; | |
573 } | |
574 | |
575 if ($comment->hasParentComment()) { | |
576 $build = []; | |
577 $parent = $comment->getParentComment(); | |
578 if ($parent && $parent->isPublished()) { | |
579 $build = \Drupal::entityTypeManager()->getViewBuilder('comment')->view($parent); | |
580 } | |
581 } | |
582 else { | |
583 // The comment field output includes rendering the parent entity of the | |
584 // thread to which the comment is a reply. The rendered entity output | |
585 // includes the comment reply form, which contains the comment preview and | |
586 // therefore the rendered parent entity. This results in an infinite loop of | |
587 // parent entity output rendering the comment form and the comment form | |
588 // rendering the parent entity. To prevent this infinite loop we temporarily | |
589 // set the value of the comment field on a clone of the entity to hidden | |
590 // before calling entity_view(). That way when the output of the commented | |
591 // entity is rendered, it excludes the comment field output. | |
592 $field_name = $comment->getFieldName(); | |
593 $entity = clone $entity; | |
594 $entity->$field_name->status = CommentItemInterface::HIDDEN; | |
595 $build = entity_view($entity, 'full'); | |
596 } | |
597 | |
598 $preview_build['comment_output_below'] = $build; | |
599 $preview_build['comment_output_below']['#weight'] = 100; | |
600 | |
601 return $preview_build; | |
602 } | |
603 | |
604 /** | |
605 * Implements hook_preprocess_HOOK() for block templates. | |
606 */ | |
607 function comment_preprocess_block(&$variables) { | |
608 if ($variables['configuration']['provider'] == 'comment') { | |
609 $variables['attributes']['role'] = 'navigation'; | |
610 } | |
611 } | |
612 | |
613 /** | |
614 * Prepares variables for comment templates. | |
615 * | |
616 * Default template: comment.html.twig. | |
617 * | |
618 * @param array $variables | |
619 * An associative array containing: | |
620 * - elements: An associative array containing the comment and entity objects. | |
621 * Array keys: #comment, #commented_entity. | |
622 */ | |
623 function template_preprocess_comment(&$variables) { | |
624 /** @var \Drupal\comment\CommentInterface $comment */ | |
625 $comment = $variables['elements']['#comment']; | |
626 $commented_entity = $comment->getCommentedEntity(); | |
627 $variables['comment'] = $comment; | |
628 $variables['commented_entity'] = $commented_entity; | |
629 $variables['threaded'] = $variables['elements']['#comment_threaded']; | |
630 | |
631 $account = $comment->getOwner(); | |
632 $username = [ | |
633 '#theme' => 'username', | |
634 '#account' => $account, | |
635 ]; | |
636 $variables['author'] = \Drupal::service('renderer')->render($username); | |
637 $variables['author_id'] = $comment->getOwnerId(); | |
638 $variables['new_indicator_timestamp'] = $comment->getChangedTime(); | |
639 $variables['created'] = format_date($comment->getCreatedTime()); | |
640 // Avoid calling format_date() twice on the same timestamp. | |
641 if ($comment->getChangedTime() == $comment->getCreatedTime()) { | |
642 $variables['changed'] = $variables['created']; | |
643 } | |
644 else { | |
645 $variables['changed'] = format_date($comment->getChangedTime()); | |
646 } | |
647 | |
648 if (theme_get_setting('features.comment_user_picture')) { | |
649 // To change user picture settings (for instance, image style), edit the | |
650 // 'compact' view mode on the User entity. | |
651 $variables['user_picture'] = user_view($account, 'compact'); | |
652 } | |
653 else { | |
654 $variables['user_picture'] = []; | |
655 } | |
656 | |
657 if (isset($comment->in_preview)) { | |
658 $variables['title'] = \Drupal::l($comment->getSubject(), new Url('<front>')); | |
659 $variables['permalink'] = \Drupal::l(t('Permalink'), new Url('<front>')); | |
660 } | |
661 else { | |
662 $uri = $comment->permalink(); | |
663 $attributes = $uri->getOption('attributes') ?: []; | |
664 $attributes += ['class' => ['permalink'], 'rel' => 'bookmark']; | |
665 $uri->setOption('attributes', $attributes); | |
666 $variables['title'] = \Drupal::l($comment->getSubject(), $uri); | |
667 | |
668 $variables['permalink'] = \Drupal::l(t('Permalink'), $comment->permalink()); | |
669 } | |
670 | |
671 $variables['submitted'] = t('Submitted by @username on @datetime', ['@username' => $variables['author'], '@datetime' => $variables['created']]); | |
672 | |
673 if ($comment->hasParentComment()) { | |
674 // Fetch and store the parent comment information for use in templates. | |
675 $comment_parent = $comment->getParentComment(); | |
676 $account_parent = $comment_parent->getOwner(); | |
677 $variables['parent_comment'] = $comment_parent; | |
678 $username = [ | |
679 '#theme' => 'username', | |
680 '#account' => $account_parent, | |
681 ]; | |
682 $variables['parent_author'] = \Drupal::service('renderer')->render($username); | |
683 $variables['parent_created'] = format_date($comment_parent->getCreatedTime()); | |
684 // Avoid calling format_date() twice on the same timestamp. | |
685 if ($comment_parent->getChangedTime() == $comment_parent->getCreatedTime()) { | |
686 $variables['parent_changed'] = $variables['parent_created']; | |
687 } | |
688 else { | |
689 $variables['parent_changed'] = format_date($comment_parent->getChangedTime()); | |
690 } | |
691 $permalink_uri_parent = $comment_parent->permalink(); | |
692 $attributes = $permalink_uri_parent->getOption('attributes') ?: []; | |
693 $attributes += ['class' => ['permalink'], 'rel' => 'bookmark']; | |
694 $permalink_uri_parent->setOption('attributes', $attributes); | |
695 $variables['parent_title'] = \Drupal::l($comment_parent->getSubject(), $permalink_uri_parent); | |
696 $variables['parent_permalink'] = \Drupal::l(t('Parent permalink'), $permalink_uri_parent); | |
697 $variables['parent'] = t('In reply to @parent_title by @parent_username', | |
698 ['@parent_username' => $variables['parent_author'], '@parent_title' => $variables['parent_title']]); | |
699 } | |
700 else { | |
701 $variables['parent_comment'] = ''; | |
702 $variables['parent_author'] = ''; | |
703 $variables['parent_created'] = ''; | |
704 $variables['parent_changed'] = ''; | |
705 $variables['parent_title'] = ''; | |
706 $variables['parent_permalink'] = ''; | |
707 $variables['parent'] = ''; | |
708 } | |
709 | |
710 // Helpful $content variable for templates. | |
711 foreach (Element::children($variables['elements']) as $key) { | |
712 $variables['content'][$key] = $variables['elements'][$key]; | |
713 } | |
714 | |
715 // Set status to a string representation of comment->status. | |
716 if (isset($comment->in_preview)) { | |
717 $variables['status'] = 'preview'; | |
718 } | |
719 else { | |
720 $variables['status'] = $comment->isPublished() ? 'published' : 'unpublished'; | |
721 } | |
722 | |
723 // Add comment author user ID. Necessary for the comment-by-viewer library. | |
724 $variables['attributes']['data-comment-user-id'] = $comment->getOwnerId(); | |
725 } | |
726 | |
727 /** | |
728 * Prepares variables for comment field templates. | |
729 * | |
730 * Default template: field--comment.html.twig. | |
731 * | |
732 * @param array $variables | |
733 * An associative array containing: | |
734 * - element: An associative array containing render arrays for the list of | |
735 * comments, and the comment form. Array keys: comments, comment_form. | |
736 * | |
737 * @todo Rename to template_preprocess_field__comment() once | |
738 * https://www.drupal.org/node/939462 is resolved. | |
739 */ | |
740 function comment_preprocess_field(&$variables) { | |
741 $element = $variables['element']; | |
742 if ($element['#field_type'] == 'comment') { | |
743 // Provide contextual information. | |
744 $variables['comment_display_mode'] = $element[0]['#comment_display_mode']; | |
745 $variables['comment_type'] = $element[0]['#comment_type']; | |
746 | |
747 // Append additional attributes (eg. RDFa) from the first field item. | |
748 $variables['attributes'] += $variables['items'][0]['attributes']->storage(); | |
749 | |
750 // Create separate variables for the comments and comment form. | |
751 $variables['comments'] = $element[0]['comments']; | |
752 $variables['comment_form'] = $element[0]['comment_form']; | |
753 } | |
754 } | |
755 | |
756 /** | |
757 * Implements hook_ranking(). | |
758 */ | |
759 function comment_ranking() { | |
760 return \Drupal::service('comment.statistics')->getRankingInfo(); | |
761 } | |
762 | |
763 /** | |
764 * Implements hook_ENTITY_TYPE_presave() for entity_view_display entities. | |
765 */ | |
766 function comment_entity_view_display_presave(EntityViewDisplayInterface $display) { | |
767 // Act only on comment view displays being disabled. | |
768 if ($display->isNew() || $display->getTargetEntityTypeId() !== 'comment' || $display->status()) { | |
769 return; | |
770 } | |
771 $storage = \Drupal::entityTypeManager()->getStorage('entity_view_display'); | |
772 if (!$storage->loadUnchanged($display->getOriginalId())->status()) { | |
773 return; | |
774 } | |
775 | |
776 // Disable the comment field formatter when the used view display is disabled. | |
777 foreach ($storage->loadMultiple() as $id => $view_display) { | |
778 $changed = FALSE; | |
779 /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */ | |
780 foreach ($view_display->getComponents() as $field => $component) { | |
781 if (isset($component['type']) && ($component['type'] === 'comment_default')) { | |
782 if ($component['settings']['view_mode'] === $display->getMode()) { | |
783 $view_display->removeComponent($field); | |
784 /** @var \Drupal\Core\Entity\EntityViewModeInterface $mode */ | |
785 $mode = EntityViewMode::load($display->getTargetEntityTypeId() . '.' . $display->getMode()); | |
786 $arguments = [ | |
787 '@id' => $view_display->id(), | |
788 '@name' => $field, | |
789 '@display' => $mode->label(), | |
790 '@mode' => $display->getMode(), | |
791 ]; | |
792 \Drupal::logger('system')->warning("View display '@id': Comment field formatter '@name' was disabled because it is using the comment view display '@display' (@mode) that was just disabled.", $arguments); | |
793 $changed = TRUE; | |
794 } | |
795 } | |
796 } | |
797 if ($changed) { | |
798 $view_display->save(); | |
799 } | |
800 } | |
801 } |