NodeNewComments.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace Drupal\comment\Plugin\views\field;
  3. use Drupal\Core\Database\Connection;
  4. use Drupal\comment\CommentInterface;
  5. use Drupal\Core\Entity\EntityFieldManagerInterface;
  6. use Drupal\Core\Entity\EntityTypeManagerInterface;
  7. use Drupal\Core\Form\FormStateInterface;
  8. use Drupal\node\Entity\Node;
  9. use Drupal\views\Plugin\views\field\NumericField;
  10. use Drupal\views\Plugin\views\display\DisplayPluginBase;
  11. use Drupal\views\ResultRow;
  12. use Drupal\views\ViewExecutable;
  13. use Symfony\Component\DependencyInjection\ContainerInterface;
  14. /**
  15. * Field handler to display the number of new comments.
  16. *
  17. * @ingroup views_field_handlers
  18. *
  19. * @ViewsField("node_new_comments")
  20. */
  21. class NodeNewComments extends NumericField {
  22. /**
  23. * {@inheritdoc}
  24. */
  25. public function usesGroupBy() {
  26. return FALSE;
  27. }
  28. /**
  29. * Database Service Object.
  30. *
  31. * @var \Drupal\Core\Database\Connection
  32. */
  33. protected $database;
  34. /**
  35. * The entity type manager.
  36. *
  37. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  38. */
  39. protected $entityTypeManager;
  40. /**
  41. * The entity field manager.
  42. *
  43. * @var \Drupal\Core\Entity\EntityFieldManagerInterface
  44. */
  45. protected $entityFieldManager;
  46. /**
  47. * Constructs a \Drupal\comment\Plugin\views\field\NodeNewComments object.
  48. *
  49. * @param array $configuration
  50. * A configuration array containing information about the plugin instance.
  51. * @param string $plugin_id
  52. * The plugin_id for the plugin instance.
  53. * @param mixed $plugin_definition
  54. * The plugin implementation definition.
  55. * @param \Drupal\Core\Database\Connection $database
  56. * Database Service Object.
  57. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  58. * The entity type manager service.
  59. * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
  60. * The entity field manager service.
  61. */
  62. public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityTypeManagerInterface $entity_type_manager = NULL, EntityFieldManagerInterface $entity_field_manager = NULL) {
  63. parent::__construct($configuration, $plugin_id, $plugin_definition);
  64. $this->database = $database;
  65. if (!$entity_type_manager) {
  66. @trigger_error("Not passing the entity type manager to the NodeNewComments constructor is deprecated in drupal:8.8.0 and will be required in drupal:9.0.0. @see https://www.drupal.org/node/3047897", E_USER_DEPRECATED);
  67. $entity_type_manager = \Drupal::entityTypeManager();
  68. }
  69. if (!$entity_field_manager) {
  70. @trigger_error("Not passing the entity type manager to the NodeNewComments constructor is deprecated in drupal:8.8.0 and will be required in drupal:9.0.0. @see https://www.drupal.org/node/3047897", E_USER_DEPRECATED);
  71. $entity_field_manager = \Drupal::service('entity_field.manager');
  72. }
  73. $this->entityTypeManager = $entity_type_manager;
  74. $this->entityFieldManager = $entity_field_manager;
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  80. return new static(
  81. $configuration,
  82. $plugin_id,
  83. $plugin_definition,
  84. $container->get('database'),
  85. $container->get('entity_type.manager'),
  86. $container->get('entity_field.manager')
  87. );
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
  93. parent::init($view, $display, $options);
  94. $this->additional_fields['entity_id'] = 'nid';
  95. $this->additional_fields['type'] = 'type';
  96. $this->additional_fields['comment_count'] = ['table' => 'comment_entity_statistics', 'field' => 'comment_count'];
  97. }
  98. /**
  99. * {@inheritdoc}
  100. */
  101. protected function defineOptions() {
  102. $options = parent::defineOptions();
  103. $options['link_to_comment'] = ['default' => TRUE];
  104. return $options;
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. public function buildOptionsForm(&$form, FormStateInterface $form_state) {
  110. $form['link_to_comment'] = [
  111. '#title' => $this->t('Link this field to new comments'),
  112. '#description' => $this->t("Enable to override this field's links."),
  113. '#type' => 'checkbox',
  114. '#default_value' => $this->options['link_to_comment'],
  115. ];
  116. parent::buildOptionsForm($form, $form_state);
  117. }
  118. /**
  119. * {@inheritdoc}
  120. */
  121. public function query() {
  122. $this->ensureMyTable();
  123. $this->addAdditionalFields();
  124. $this->field_alias = $this->table . '_' . $this->field;
  125. }
  126. /**
  127. * {@inheritdoc}
  128. */
  129. public function preRender(&$values) {
  130. $user = \Drupal::currentUser();
  131. if ($user->isAnonymous() || empty($values)) {
  132. return;
  133. }
  134. $nids = [];
  135. $ids = [];
  136. foreach ($values as $id => $result) {
  137. $nids[] = $result->{$this->aliases['nid']};
  138. $values[$id]->{$this->field_alias} = 0;
  139. // Create a reference so we can find this record in the values again.
  140. if (empty($ids[$result->{$this->aliases['nid']}])) {
  141. $ids[$result->{$this->aliases['nid']}] = [];
  142. }
  143. $ids[$result->{$this->aliases['nid']}][] = $id;
  144. }
  145. if ($nids) {
  146. $result = $this->database->query("SELECT n.nid, COUNT(c.cid) AS num_comments FROM {node} n INNER JOIN {comment_field_data} c ON n.nid = c.entity_id AND c.entity_type = 'node' AND c.default_langcode = 1
  147. LEFT JOIN {history} h ON h.nid = n.nid AND h.uid = :h_uid WHERE n.nid IN ( :nids[] )
  148. AND c.changed > GREATEST(COALESCE(h.timestamp, :timestamp1), :timestamp2) AND c.status = :status GROUP BY n.nid", [
  149. ':status' => CommentInterface::PUBLISHED,
  150. ':h_uid' => $user->id(),
  151. ':nids[]' => $nids,
  152. ':timestamp1' => HISTORY_READ_LIMIT,
  153. ':timestamp2' => HISTORY_READ_LIMIT,
  154. ]);
  155. foreach ($result as $node) {
  156. foreach ($ids[$node->nid] as $id) {
  157. $values[$id]->{$this->field_alias} = $node->num_comments;
  158. }
  159. }
  160. }
  161. }
  162. /**
  163. * Prepares the link to the first new comment.
  164. *
  165. * @param string $data
  166. * The XSS safe string for the link text.
  167. * @param \Drupal\views\ResultRow $values
  168. * The values retrieved from a single row of a view's query result.
  169. *
  170. * @return string
  171. * Returns a string for the link text.
  172. */
  173. protected function renderLink($data, ResultRow $values) {
  174. if (!empty($this->options['link_to_comment']) && $data !== NULL && $data !== '') {
  175. $node_type = $this->getValue($values, 'type');
  176. $node = Node::create([
  177. 'nid' => $this->getValue($values, 'nid'),
  178. 'type' => $node_type,
  179. ]);
  180. // Because there is no support for selecting a specific comment field to
  181. // reference, we arbitrarily use the first such field name we find.
  182. // @todo Provide a means for selecting the comment field.
  183. // https://www.drupal.org/node/2594201
  184. $field_map = $this->entityFieldManager->getFieldMapByFieldType('comment');
  185. $comment_field_name = 'comment';
  186. foreach ($field_map['node'] as $field_name => $field_data) {
  187. foreach ($field_data['bundles'] as $bundle_name) {
  188. if ($node_type == $bundle_name) {
  189. $comment_field_name = $field_name;
  190. break 2;
  191. }
  192. }
  193. }
  194. $page_number = $this->entityTypeManager->getStorage('comment')
  195. ->getNewCommentPageNumber($this->getValue($values, 'comment_count'), $this->getValue($values), $node, $comment_field_name);
  196. $this->options['alter']['make_link'] = TRUE;
  197. $this->options['alter']['url'] = $node->toUrl();
  198. $this->options['alter']['query'] = $page_number ? ['page' => $page_number] : NULL;
  199. $this->options['alter']['fragment'] = 'new';
  200. }
  201. return $data;
  202. }
  203. /**
  204. * {@inheritdoc}
  205. */
  206. public function render(ResultRow $values) {
  207. $value = $this->getValue($values);
  208. if (!empty($value)) {
  209. return $this->renderLink(parent::render($values), $values);
  210. }
  211. else {
  212. $this->options['alter']['make_link'] = FALSE;
  213. }
  214. }
  215. }