diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index db68f43cd8..45fe53901a 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -216,6 +216,117 @@ function comment_entity_view(array &$build, EntityInterface $entity, EntityViewD 'value' => $entity->toUrl('canonical', $options)->toString(), ]; } + elseif ($view_mode == 'teaser') { + // Teaser view: display the number of comments that have been posted, + // or a link to add new comments if the user has permission, the node + // is open to new comments, and there currently are none. + if (user_access('access comments')) { + if (!empty($entity->get($field_name)->comment_count)) { + $links['comment-comments'] = array( + 'title' => format_plural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), + 'attributes' => array('title' => t('Jump to the first comment of this posting.')), + 'fragment' => 'comments', + 'html' => TRUE, + ) + $entity->urlInfo()->toArray(); + if (\Drupal::moduleHandler()->moduleExists('history')) { + $links['comment-new-comments'] = array( + 'title' => '', + 'href' => '', + 'attributes' => array( + 'class' => 'hidden', + 'title' => t('Jump to the first new comment of this posting.'), + 'data-history-node-last-comment-timestamp' => $entity->get($field_name)->last_comment_timestamp, + 'data-history-node-field-name' => $field_name, + ), + 'html' => TRUE, + ); + } + } + } + // Provide a link to new comment form. + if ($commenting_status == CommentItemInterface::OPEN) { + $comment_form_location = $field_definition->getSetting('form_location'); + if (user_access('post comments')) { + $links['comment-add'] = array( + 'title' => t('Add new comment'), + 'language' => $entity->language(), + 'attributes' => array('title' => t('Add a new comment to this page.')), + 'fragment' => 'comment-form', + ); + if ($comment_form_location == COMMENT_FORM_SEPARATE_PAGE) { + $links['comment-add']['route_name'] = 'comment.reply'; + $links['comment-add']['route_parameters'] = array( + 'entity_type' => $entity->getEntityTypeId(), + 'entity_id' => $entity->id(), + 'field_name' => $field_name, + ); + } + else { + $links['comment-add'] += $entity->urlInfo()->toArray(); + } + } + elseif (\Drupal::currentUser()->isAnonymous()) { + $links['comment-forbidden'] = array( + 'title' => \Drupal::service('comment.helper')->forbiddenMessage($entity, $field_name), + 'html' => TRUE, + ); + } + } + } + else { + // Node in other view modes: add a "post comment" link if the user is + // allowed to post comments and if this node is allowing new comments. + if ($commenting_status == CommentItemInterface::OPEN) { + $comment_form_location = $field_definition->getSetting('form_location'); + if (user_access('post comments')) { + // Show the "post comment" link if the form is on another page, or + // if there are existing comments that the link will skip past. + if ($comment_form_location == COMMENT_FORM_SEPARATE_PAGE || (!empty($entity->get($field_name)->comment_count) && user_access('access comments'))) { + $links['comment-add'] = array( + 'title' => t('Add new comment'), + 'attributes' => array('title' => t('Share your thoughts and opinions related to this posting.')), + 'fragment' => 'comment-form', + ); + if ($comment_form_location == COMMENT_FORM_SEPARATE_PAGE) { + $links['comment-add']['route_name'] = 'comment.reply'; + $links['comment-add']['route_parameters'] = array( + 'entity_type' => $entity->getEntityTypeId(), + 'entity_id' => $entity->id(), + 'field_name' => $field_name, + ); + } + else { + $links['comment-add'] += $entity->urlInfo()->toArray(); + } + } + } + elseif (\Drupal::currentUser()->isAnonymous()) { + $links['comment-forbidden'] = array( + 'title' => \Drupal::service('comment.helper')->forbiddenMessage($entity, $field_name), + 'html' => TRUE, + ); + } + } + } + } + + if (!empty($links)) { + $node_links['comment__' . $field_name] = array( + '#theme' => 'links__entity__comment__' . $field_name, + '#links' => $links, + '#attributes' => array('class' => array('links', 'inline')), + ); + if ($view_mode == 'teaser' && \Drupal::moduleHandler()->moduleExists('history') && \Drupal::currentUser()->isAuthenticated()) { + $node_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link'; + + // Embed the metadata for the "X new comments" link (if any) on this node. + $node_links['comment__' . $field_name]['#post_render_cache']['history_attach_timestamp'] = array( + array('node_id' => $entity->id()), + ); + $node_links['comment__' . $field_name]['#post_render_cache']['Drupal\comment\CommentViewBuilder::attachNewCommentsLinkMetadata'] = array( + array('entity_type' => $entity->getEntityTypeId(), 'entity_id' => $entity->id(), 'field_name' => $field_name), + ); + } } } } diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml index 3595854ceb..7fb1911046 100644 --- a/core/modules/comment/comment.services.yml +++ b/core/modules/comment/comment.services.yml @@ -7,7 +7,11 @@ services: comment.manager: class: Drupal\comment\CommentManager - arguments: ['@entity_type.manager', '@config.factory', '@string_translation', '@module_handler', '@current_user', '@entity_field.manager', '@entity_display.repository'] + arguments: ['@entity_type.manager', '@module_handler', '@current_user', '@entity_field.manager', '@entity_display.repository'] + + comment.helper: + class: Drupal\comment\CommentHelper + arguments: ['@entity.manager', '@config.factory', '@string_translation', '@url_generator'] comment.statistics: class: Drupal\comment\CommentStatistics diff --git a/core/modules/comment/src/CommentHelper.php b/core/modules/comment/src/CommentHelper.php new file mode 100644 index 0000000000..a7845e58d9 --- /dev/null +++ b/core/modules/comment/src/CommentHelper.php @@ -0,0 +1,124 @@ +entityManager = $entity_manager; + $this->userConfig = $config_factory->get('user.settings'); + $this->translationManager = $translation_manager; + $this->urlGenerator = $url_generator; + } + + /** + * {@inheritdoc} + */ + public function forbiddenMessage(EntityInterface $entity, $field_name) { + if (!isset($this->authenticatedCanPostComments)) { + // We only output a link if we are certain that users will get the + // permission to post comments by logging in. + $this->authenticatedCanPostComments = $this->entityManager + ->getStorage('user_role') + ->load(DRUPAL_AUTHENTICATED_RID) + ->hasPermission('post comments'); + } + + if ($this->authenticatedCanPostComments) { + // We cannot use drupal_get_destination() because these links + // sometimes appear on /node and taxonomy listing pages. + if ($entity->get($field_name)->getFieldDefinition()->getSetting('form_location') == COMMENT_FORM_SEPARATE_PAGE) { + $destination = array('destination' => 'comment/reply/' . $entity->getEntityTypeId() . '/' . $entity->id() . '/' . $field_name . '#comment-form'); + } + else { + $destination = array('destination' => $entity->getSystemPath() . '#comment-form'); + } + + if ($this->userConfig->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) { + // Users can register themselves. + return $this->t('Log in or register to post comments', array( + '@login' => $this->urlGenerator->generateFromRoute('user.login', array(), array('query' => $destination)), + '@register' => $this->urlGenerator->generateFromRoute('user.register', array(), array('query' => $destination)), + )); + } + else { + // Only admins can add new users, no public registration. + return $this->t('Log in to post comments', array( + '@login' => $this->urlGenerator->generateFromRoute('user.login', array(), array('query' => $destination)), + )); + } + } + return ''; + } + + /** + * Translates a string to the current language or to a given language. + * + * See the t() documentation for details. + */ + protected function t($string, array $args = array(), array $options = array()) { + return $this->translationManager->translate($string, $args, $options); + } + +} diff --git a/core/modules/comment/src/CommentHelperInterface.php b/core/modules/comment/src/CommentHelperInterface.php new file mode 100644 index 0000000000..dd2d282323 --- /dev/null +++ b/core/modules/comment/src/CommentHelperInterface.php @@ -0,0 +1,34 @@ +entityTypeManager = $entity_type_manager; - $this->userConfig = $config_factory->get('user.settings'); $this->stringTranslation = $string_translation; $this->moduleHandler = $module_handler; $this->currentUser = $current_user; @@ -147,51 +128,6 @@ public function addBodyField($comment_type_id) { } } - /** - * {@inheritdoc} - */ - public function forbiddenMessage(EntityInterface $entity, $field_name) { - if (!isset($this->authenticatedCanPostComments)) { - // We only output a link if we are certain that users will get the - // permission to post comments by logging in. - $this->authenticatedCanPostComments = $this->entityTypeManager - ->getStorage('user_role') - ->load(RoleInterface::AUTHENTICATED_ID) - ->hasPermission('post comments'); - } - - if ($this->authenticatedCanPostComments) { - // We cannot use the redirect.destination service here because these links - // sometimes appear on /node and taxonomy listing pages. - if ($entity->get($field_name)->getFieldDefinition()->getSetting('form_location') == CommentItemInterface::FORM_SEPARATE_PAGE) { - $comment_reply_parameters = [ - 'entity_type' => $entity->getEntityTypeId(), - 'entity' => $entity->id(), - 'field_name' => $field_name, - ]; - $destination = ['destination' => Url::fromRoute('comment.reply', $comment_reply_parameters, ['fragment' => 'comment-form'])->toString()]; - } - else { - $destination = ['destination' => $entity->toUrl('canonical', ['fragment' => 'comment-form'])->toString()]; - } - - if ($this->userConfig->get('register') != UserInterface::REGISTER_ADMINISTRATORS_ONLY) { - // Users can register themselves. - return $this->t('Log in or register to post comments', [ - ':login' => Url::fromRoute('user.login', [], ['query' => $destination])->toString(), - ':register' => Url::fromRoute('user.register', [], ['query' => $destination])->toString(), - ]); - } - else { - // Only admins can add new users, no public registration. - return $this->t('Log in to post comments', [ - ':login' => Url::fromRoute('user.login', [], ['query' => $destination])->toString(), - ]); - } - } - return ''; - } - /** * {@inheritdoc} */ @@ -234,4 +170,11 @@ public function getCountNewComments(EntityInterface $entity, $field_name = NULL, return FALSE; } + public function getFieldUIPageTitle($commented_entity_type, $field_name) { + $field_info = $this->getFields($commented_entity_type); + $sample_bundle = reset($field_info[$field_name]['bundles']); + $sample_definition = $this->entityManager->getFieldDefinitions($commented_entity_type, $sample_bundle)[$field_name]; + return String::checkPlain($sample_definition->getLabel()); + } + } diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php index b3fd17e59c..7d40b89075 100644 --- a/core/modules/comment/src/CommentViewBuilder.php +++ b/core/modules/comment/src/CommentViewBuilder.php @@ -180,6 +180,107 @@ public function buildComponents(array &$build, array $entities, array $displays, } } + /** + * #post_render_cache callback; replaces the placeholder with comment links. + * + * Renders the links on a comment. + * + * @param array $element + * The renderable array that contains the to be replaced placeholder. + * @param array $context + * An array with the following keys: + * - comment_entity_id: a comment entity ID + * - view_mode: the view mode in which the comment entity is being viewed + * - langcode: in which language the comment entity is being viewed + * - commented_entity_type: the entity type to which the comment is attached + * - commented_entity_id: the entity ID to which the comment is attached + * - in_preview: whether the comment is currently being previewed + * + * @return array + * A renderable array representing the comment links. + */ + public static function renderLinks(array $element, array $context) { + $callback = '\Drupal\comment\CommentViewBuilder::renderLinks'; + $placeholder = drupal_render_cache_generate_placeholder($callback, $context, $context['token']); + $links = array( + '#theme' => 'links__comment', + '#pre_render' => array('drupal_pre_render_links'), + '#attributes' => array('class' => array('links', 'inline')), + ); + + if (!$context['in_preview']) { + $entity = entity_load('comment', $context['comment_entity_id']); + $commented_entity = entity_load($context['commented_entity_type'], $context['commented_entity_id']); + + $links['comment'] = self::buildLinks($entity, $commented_entity); + + // Allow other modules to alter the comment links. + $hook_context = array( + 'view_mode' => $context['view_mode'], + 'langcode' => $context['langcode'], + 'commented_entity' => $commented_entity + ); + \Drupal::moduleHandler()->alter('comment_links', $links, $entity, $hook_context); + } + $markup = drupal_render($links); + $element['#markup'] = str_replace($placeholder, $markup, $element['#markup']); + + return $element; + } + + /** + * Build the default links (reply, edit, delete …) for a comment. + * + * @param \Drupal\comment\CommentInterface $entity + * The comment object. + * @param \Drupal\Core\Entity\EntityInterface $commented_entity + * The entity to which the comment is attached. + * + * @return array + * An array that can be processed by drupal_pre_render_links(). + */ + protected static function buildLinks(CommentInterface $entity, EntityInterface $commented_entity) { + $links = array(); + $status = $commented_entity->get($entity->getFieldName())->status; + + if ($status == CommentItemInterface::OPEN) { + if ($entity->access('delete')) { + $links['comment-delete'] = array( + 'title' => t('Delete'), + 'href' => "comment/{$entity->id()}/delete", + 'html' => TRUE, + ); + } + + if ($entity->access('update')) { + $links['comment-edit'] = array( + 'title' => t('Edit'), + 'href' => "comment/{$entity->id()}/edit", + 'html' => TRUE, + ); + } + if ($entity->access('create')) { + $links['comment-reply'] = array( + 'title' => t('Reply'), + 'href' => "comment/reply/{$entity->getCommentedEntityTypeId()}/{$entity->getCommentedEntityId()}/{$entity->getFieldName()}/{$entity->id()}", + 'html' => TRUE, + ); + } + if (!$entity->isPublished() && $entity->access('approve')) { + $links['comment-approve'] = array( + 'title' => t('Approve'), + 'route_name' => 'comment.approve', + 'route_parameters' => array('comment' => $entity->id()), + 'html' => TRUE, + ); + } + if (empty($links) && \Drupal::currentUser()->isAnonymous()) { + $links['comment-forbidden']['title'] = \Drupal::service('comment.helper')->forbiddenMessage($commented_entity, $entity->getFieldName()); + $links['comment-forbidden']['html'] = TRUE; + } + } + } + /** * {@inheritdoc} */