CommentLinkBuilder.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. namespace Drupal\comment;
  3. use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
  4. use Drupal\Component\Utility\NestedArray;
  5. use Drupal\Core\Entity\EntityManagerInterface;
  6. use Drupal\Core\Entity\FieldableEntityInterface;
  7. use Drupal\Core\Extension\ModuleHandlerInterface;
  8. use Drupal\Core\Session\AccountInterface;
  9. use Drupal\Core\StringTranslation\StringTranslationTrait;
  10. use Drupal\Core\StringTranslation\TranslationInterface;
  11. use Drupal\Core\Url;
  12. /**
  13. * Defines a class for building markup for comment links on a commented entity.
  14. *
  15. * Comment links include 'log in to post new comment', 'add new comment' etc.
  16. */
  17. class CommentLinkBuilder implements CommentLinkBuilderInterface {
  18. use StringTranslationTrait;
  19. /**
  20. * Current user.
  21. *
  22. * @var \Drupal\Core\Session\AccountInterface
  23. */
  24. protected $currentUser;
  25. /**
  26. * Comment manager service.
  27. *
  28. * @var \Drupal\comment\CommentManagerInterface
  29. */
  30. protected $commentManager;
  31. /**
  32. * Module handler service.
  33. *
  34. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  35. */
  36. protected $moduleHandler;
  37. /**
  38. * The entity manager service.
  39. *
  40. * @var \Drupal\Core\Entity\EntityManagerInterface
  41. */
  42. protected $entityManager;
  43. /**
  44. * Constructs a new CommentLinkBuilder object.
  45. *
  46. * @param \Drupal\Core\Session\AccountInterface $current_user
  47. * Current user.
  48. * @param \Drupal\comment\CommentManagerInterface $comment_manager
  49. * Comment manager service.
  50. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  51. * Module handler service.
  52. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
  53. * String translation service.
  54. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
  55. * The entity manager service.
  56. */
  57. public function __construct(AccountInterface $current_user, CommentManagerInterface $comment_manager, ModuleHandlerInterface $module_handler, TranslationInterface $string_translation, EntityManagerInterface $entity_manager) {
  58. $this->currentUser = $current_user;
  59. $this->commentManager = $comment_manager;
  60. $this->moduleHandler = $module_handler;
  61. $this->stringTranslation = $string_translation;
  62. $this->entityManager = $entity_manager;
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. public function buildCommentedEntityLinks(FieldableEntityInterface $entity, array &$context) {
  68. $entity_links = [];
  69. $view_mode = $context['view_mode'];
  70. if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print' || $view_mode == 'rss') {
  71. // Do not add any links if the entity is displayed for:
  72. // - search indexing.
  73. // - constructing a search result excerpt.
  74. // - print.
  75. // - rss.
  76. return [];
  77. }
  78. $fields = $this->commentManager->getFields($entity->getEntityTypeId());
  79. foreach ($fields as $field_name => $detail) {
  80. // Skip fields that the entity does not have.
  81. if (!$entity->hasField($field_name)) {
  82. continue;
  83. }
  84. $links = [];
  85. $commenting_status = $entity->get($field_name)->status;
  86. if ($commenting_status != CommentItemInterface::HIDDEN) {
  87. // Entity has commenting status open or closed.
  88. $field_definition = $entity->getFieldDefinition($field_name);
  89. if ($view_mode == 'teaser') {
  90. // Teaser view: display the number of comments that have been posted,
  91. // or a link to add new comments if the user has permission, the
  92. // entity is open to new comments, and there currently are none.
  93. if ($this->currentUser->hasPermission('access comments')) {
  94. if (!empty($entity->get($field_name)->comment_count)) {
  95. $links['comment-comments'] = [
  96. 'title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'),
  97. 'attributes' => ['title' => $this->t('Jump to the first comment.')],
  98. 'fragment' => 'comments',
  99. 'url' => $entity->urlInfo(),
  100. ];
  101. if ($this->moduleHandler->moduleExists('history')) {
  102. $links['comment-new-comments'] = [
  103. 'title' => '',
  104. 'url' => Url::fromRoute('<current>'),
  105. 'attributes' => [
  106. 'class' => 'hidden',
  107. 'title' => $this->t('Jump to the first new comment.'),
  108. 'data-history-node-last-comment-timestamp' => $entity->get($field_name)->last_comment_timestamp,
  109. 'data-history-node-field-name' => $field_name,
  110. ],
  111. ];
  112. }
  113. }
  114. }
  115. // Provide a link to new comment form.
  116. if ($commenting_status == CommentItemInterface::OPEN) {
  117. $comment_form_location = $field_definition->getSetting('form_location');
  118. if ($this->currentUser->hasPermission('post comments')) {
  119. $links['comment-add'] = [
  120. 'title' => $this->t('Add new comment'),
  121. 'language' => $entity->language(),
  122. 'attributes' => ['title' => $this->t('Share your thoughts and opinions.')],
  123. 'fragment' => 'comment-form',
  124. ];
  125. if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
  126. $links['comment-add']['url'] = Url::fromRoute('comment.reply', [
  127. 'entity_type' => $entity->getEntityTypeId(),
  128. 'entity' => $entity->id(),
  129. 'field_name' => $field_name,
  130. ]);
  131. }
  132. else {
  133. $links['comment-add'] += ['url' => $entity->urlInfo()];
  134. }
  135. }
  136. elseif ($this->currentUser->isAnonymous()) {
  137. $links['comment-forbidden'] = [
  138. 'title' => $this->commentManager->forbiddenMessage($entity, $field_name),
  139. ];
  140. }
  141. }
  142. }
  143. else {
  144. // Entity in other view modes: add a "post comment" link if the user
  145. // is allowed to post comments and if this entity is allowing new
  146. // comments.
  147. if ($commenting_status == CommentItemInterface::OPEN) {
  148. $comment_form_location = $field_definition->getSetting('form_location');
  149. if ($this->currentUser->hasPermission('post comments')) {
  150. // Show the "post comment" link if the form is on another page, or
  151. // if there are existing comments that the link will skip past.
  152. if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE || (!empty($entity->get($field_name)->comment_count) && $this->currentUser->hasPermission('access comments'))) {
  153. $links['comment-add'] = [
  154. 'title' => $this->t('Add new comment'),
  155. 'attributes' => ['title' => $this->t('Share your thoughts and opinions.')],
  156. 'fragment' => 'comment-form',
  157. ];
  158. if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
  159. $links['comment-add']['url'] = Url::fromRoute('comment.reply', [
  160. 'entity_type' => $entity->getEntityTypeId(),
  161. 'entity' => $entity->id(),
  162. 'field_name' => $field_name,
  163. ]);
  164. }
  165. else {
  166. $links['comment-add']['url'] = $entity->urlInfo();
  167. }
  168. }
  169. }
  170. elseif ($this->currentUser->isAnonymous()) {
  171. $links['comment-forbidden'] = [
  172. 'title' => $this->commentManager->forbiddenMessage($entity, $field_name),
  173. ];
  174. }
  175. }
  176. }
  177. }
  178. if (!empty($links)) {
  179. $entity_links['comment__' . $field_name] = [
  180. '#theme' => 'links__entity__comment__' . $field_name,
  181. '#links' => $links,
  182. '#attributes' => ['class' => ['links', 'inline']],
  183. ];
  184. if ($view_mode == 'teaser' && $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) {
  185. $entity_links['comment__' . $field_name]['#cache']['contexts'][] = 'user';
  186. $entity_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link';
  187. // Embed the metadata for the "X new comments" link (if any) on this
  188. // entity.
  189. $entity_links['comment__' . $field_name]['#attached']['drupalSettings']['history']['lastReadTimestamps'][$entity->id()] = (int) history_read($entity->id());
  190. $new_comments = $this->commentManager->getCountNewComments($entity);
  191. if ($new_comments > 0) {
  192. $page_number = $this->entityManager
  193. ->getStorage('comment')
  194. ->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new_comments, $entity, $field_name);
  195. $query = $page_number ? ['page' => $page_number] : NULL;
  196. $value = [
  197. 'new_comment_count' => (int) $new_comments,
  198. 'first_new_comment_link' => $entity->url('canonical', [
  199. 'query' => $query,
  200. 'fragment' => 'new',
  201. ]),
  202. ];
  203. $parents = ['comment', 'newCommentsLinks', $entity->getEntityTypeId(), $field_name, $entity->id()];
  204. NestedArray::setValue($entity_links['comment__' . $field_name]['#attached']['drupalSettings'], $parents, $value);
  205. }
  206. }
  207. }
  208. }
  209. return $entity_links;
  210. }
  211. }