CommentViewBuilder.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. namespace Drupal\comment;
  3. use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
  4. use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
  5. use Drupal\Core\Entity\EntityInterface;
  6. use Drupal\Core\Entity\EntityRepositoryInterface;
  7. use Drupal\Core\Entity\EntityTypeInterface;
  8. use Drupal\Core\Entity\EntityTypeManagerInterface;
  9. use Drupal\Core\Entity\EntityViewBuilder;
  10. use Drupal\Core\Language\LanguageManagerInterface;
  11. use Drupal\Core\Session\AccountInterface;
  12. use Drupal\Core\Theme\Registry;
  13. use Symfony\Component\DependencyInjection\ContainerInterface;
  14. /**
  15. * View builder handler for comments.
  16. */
  17. class CommentViewBuilder extends EntityViewBuilder {
  18. /**
  19. * The current user.
  20. *
  21. * @var \Drupal\Core\Session\AccountInterface
  22. */
  23. protected $currentUser;
  24. /**
  25. * The entity type manager.
  26. *
  27. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  28. */
  29. protected $entityTypeManager;
  30. /**
  31. * Constructs a new CommentViewBuilder.
  32. *
  33. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  34. * The entity type definition.
  35. * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
  36. * The entity repository service.
  37. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
  38. * The language manager.
  39. * @param \Drupal\Core\Session\AccountInterface $current_user
  40. * The current user.
  41. * @param \Drupal\Core\Theme\Registry $theme_registry
  42. * The theme registry.
  43. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
  44. * The entity display repository.
  45. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  46. * The entity type manager.
  47. */
  48. public function __construct(EntityTypeInterface $entity_type, EntityRepositoryInterface $entity_repository, LanguageManagerInterface $language_manager, AccountInterface $current_user, Registry $theme_registry, EntityDisplayRepositoryInterface $entity_display_repository, EntityTypeManagerInterface $entity_type_manager) {
  49. parent::__construct($entity_type, $entity_repository, $language_manager, $theme_registry, $entity_display_repository);
  50. $this->currentUser = $current_user;
  51. $this->entityTypeManager = $entity_type_manager;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. */
  56. public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
  57. return new static(
  58. $entity_type,
  59. $container->get('entity.repository'),
  60. $container->get('language_manager'),
  61. $container->get('current_user'),
  62. $container->get('theme.registry'),
  63. $container->get('entity_display.repository'),
  64. $container->get('entity_type.manager')
  65. );
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. protected function getBuildDefaults(EntityInterface $entity, $view_mode) {
  71. $build = parent::getBuildDefaults($entity, $view_mode);
  72. /** @var \Drupal\comment\CommentInterface $entity */
  73. // Store a threading field setting to use later in self::buildComponents().
  74. $commented_entity = $entity->getCommentedEntity();
  75. $build['#comment_threaded'] =
  76. is_null($commented_entity)
  77. || $commented_entity->getFieldDefinition($entity->getFieldName())
  78. ->getSetting('default_mode') === CommentManagerInterface::COMMENT_MODE_THREADED;
  79. // If threading is enabled, don't render cache individual comments, but do
  80. // keep the cacheability metadata, so it can bubble up.
  81. if ($build['#comment_threaded']) {
  82. unset($build['#cache']['keys']);
  83. }
  84. return $build;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. *
  89. * In addition to modifying the content key on entities, this implementation
  90. * will also set the comment entity key which all comments carry.
  91. *
  92. * @throws \InvalidArgumentException
  93. * Thrown when a comment is attached to an entity that no longer exists.
  94. */
  95. public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
  96. /** @var \Drupal\comment\CommentInterface[] $entities */
  97. if (empty($entities)) {
  98. return;
  99. }
  100. // Pre-load associated users into cache to leverage multiple loading.
  101. $uids = [];
  102. foreach ($entities as $entity) {
  103. $uids[] = $entity->getOwnerId();
  104. }
  105. $this->entityTypeManager->getStorage('user')->loadMultiple(array_unique($uids));
  106. parent::buildComponents($build, $entities, $displays, $view_mode);
  107. // A counter to track the indentation level.
  108. $current_indent = 0;
  109. $attach_history = $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated();
  110. foreach ($entities as $id => $entity) {
  111. if ($build[$id]['#comment_threaded']) {
  112. $comment_indent = count(explode('.', $entity->getThread())) - 1;
  113. if ($comment_indent > $current_indent) {
  114. // Set 1 to indent this comment from the previous one (its parent).
  115. // Set only one extra level of indenting even if the difference in
  116. // depth is higher.
  117. $build[$id]['#comment_indent'] = 1;
  118. $current_indent++;
  119. }
  120. else {
  121. // Set zero if this comment is on the same level as the previous one
  122. // or negative value to point an amount indents to close.
  123. $build[$id]['#comment_indent'] = $comment_indent - $current_indent;
  124. $current_indent = $comment_indent;
  125. }
  126. }
  127. // Commented entities already loaded after self::getBuildDefaults().
  128. $commented_entity = $entity->getCommentedEntity();
  129. // Set defaults if the commented_entity does not exist.
  130. $bundle = $commented_entity ? $commented_entity->bundle() : '';
  131. $is_node = $commented_entity ? $commented_entity->getEntityTypeId() === 'node' : NULL;
  132. $build[$id]['#entity'] = $entity;
  133. $build[$id]['#theme'] = 'comment__' . $entity->getFieldName() . '__' . $bundle;
  134. $display = $displays[$entity->bundle()];
  135. if ($display->getComponent('links')) {
  136. $build[$id]['links'] = [
  137. '#lazy_builder' => [
  138. 'comment.lazy_builders:renderLinks',
  139. [
  140. $entity->id(),
  141. $view_mode,
  142. $entity->language()->getId(),
  143. !empty($entity->in_preview),
  144. ],
  145. ],
  146. '#create_placeholder' => TRUE,
  147. ];
  148. }
  149. if (!isset($build[$id]['#attached'])) {
  150. $build[$id]['#attached'] = [];
  151. }
  152. $build[$id]['#attached']['library'][] = 'comment/drupal.comment-by-viewer';
  153. if ($attach_history && $is_node) {
  154. $build[$id]['#attached']['library'][] = 'comment/drupal.comment-new-indicator';
  155. // Embed the metadata for the comment "new" indicators on this node.
  156. $build[$id]['history'] = [
  157. '#lazy_builder' => ['\Drupal\history\HistoryRenderCallback::lazyBuilder', [$commented_entity->id()]],
  158. '#create_placeholder' => TRUE,
  159. ];
  160. }
  161. }
  162. if ($build[$id]['#comment_threaded']) {
  163. // The final comment must close up some hanging divs.
  164. $build[$id]['#comment_indent_final'] = $current_indent;
  165. }
  166. }
  167. /**
  168. * {@inheritdoc}
  169. */
  170. protected function alterBuild(array &$build, EntityInterface $comment, EntityViewDisplayInterface $display, $view_mode) {
  171. parent::alterBuild($build, $comment, $display, $view_mode);
  172. if (empty($comment->in_preview)) {
  173. $prefix = '';
  174. // Add indentation div or close open divs as needed.
  175. if ($build['#comment_threaded']) {
  176. $prefix .= $build['#comment_indent'] <= 0 ? str_repeat('</div>', abs($build['#comment_indent'])) : "\n" . '<div class="indented">';
  177. }
  178. $build['#prefix'] = $prefix;
  179. // Close all open divs.
  180. if (!empty($build['#comment_indent_final'])) {
  181. $build['#suffix'] = str_repeat('</div>', $build['#comment_indent_final']);
  182. }
  183. }
  184. }
  185. }