diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install index 64aa6bd..1f9cbc4 100644 --- a/core/modules/comment/comment.install +++ b/core/modules/comment/comment.install @@ -32,6 +32,23 @@ function comment_install() { } /** + * Implements hook_modules_installed(). + */ +function comment_modules_installed($modules) { + // Book was just installed, or Comment was just installed while Book + // is (already/newly) installed too. + if (in_array('book', $modules) + || (in_array('comment', $modules) && \Drupal::moduleHandler()->moduleExists('book'))) { + foreach (\Drupal::service('comment.manager')->getFields('node') as $field_name => $detail) { + foreach ($detail['bundles'] as $bundle) { + $display = entity_get_display('node', $bundle, 'print'); + $display->removeComponent($field_name)->save(); + } + } + } +} + +/** * Implements hook_schema(). */ function comment_schema() { diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 0ee6f4c..13575d9 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -12,7 +12,6 @@ use Drupal\comment\CommentInterface; use Drupal\comment\Entity\Comment; -use Drupal\comment\CommentManagerInterface; use Drupal\comment\Entity\CommentType; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; @@ -215,14 +214,12 @@ function comment_entity_build_defaults_alter(array &$build, EntityInterface $ent } /** - * Implements hook_node_links_alter(). + * Implements hook_preprocess_hook() for theme_node(). */ function comment_node_links_alter(array &$node_links, NodeInterface $node, array &$context) { // Comment links are only added to node entity type for backwards // compatibility. Should you require comment links for other entity types you // can do so by implementing a new field formatter. - // @todo Make this configurable from the formatter see - // http://drupal.org/node/1901110 $links = \Drupal::service('comment.link_builder')->buildCommentedEntityLinks($node, $context); $node_links += $links; @@ -473,17 +470,18 @@ function comment_node_update_index(EntityInterface $node, $langcode) { $build = array(); if ($index_comments) { + $node_base_defs = \Drupal::entityManager()->getFieldStorageDefinitions($node->getEntityTypeId()); foreach (\Drupal::service('comment.manager')->getFields('node') as $field_name => $info) { // Skip fields that entity does not have. if (!$node->hasField($field_name)) { continue; } - $field_definition = $node->getFieldDefinition($field_name); - $mode = $field_definition->getSetting('default_mode'); - $comments_per_page = $field_definition->getSetting('per_page'); if ($node->get($field_name)->status) { + $comment_type = CommentType::load($node_base_defs[$field_name]->getSetting('comment_type')); + $display_settings = entity_get_display($node->getEntityTypeId(), $node->bundle(), 'default') + ->getComponent($field_name); $comments = \Drupal::entityManager()->getStorage('comment') - ->loadThread($node, $field_name, $mode, $comments_per_page); + ->loadThread($node, $field_name, $comment_type->getThreadingMode(), $display_settings['settings']['per_page']); if ($comments) { comment_prepare_thread($comments); $build[] = \Drupal::entityManager()->getViewBuilder('comment')->viewMultiple($comments); @@ -812,7 +810,7 @@ function comment_preprocess_field(&$variables) { $element = $variables['element']; if ($element['#field_type'] == 'comment') { // Provide contextual information. - $variables['comment_display_mode'] = $element[0]['#comment_display_mode']; + $variables['comment_threading_mode'] = $element[0]['#comment_threading_mode']; $variables['comment_type'] = $element[0]['#comment_type']; // Adjust a comment field's attributes. diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml index 239cdb2..b67b08f 100644 --- a/core/modules/comment/comment.services.yml +++ b/core/modules/comment/comment.services.yml @@ -20,4 +20,4 @@ services: comment.link_builder: class: Drupal\comment\CommentLinkBuilder - arguments: ['@current_user', '@comment.manager', '@module_handler', '@string_translation'] + arguments: ['@current_user', '@entity.manager', '@comment.manager', '@module_handler', '@string_translation'] diff --git a/core/modules/comment/config/schema/comment.schema.yml b/core/modules/comment/config/schema/comment.schema.yml index b5bcd86..36bea18 100644 --- a/core/modules/comment/config/schema/comment.schema.yml +++ b/core/modules/comment/config/schema/comment.schema.yml @@ -59,6 +59,9 @@ comment.type.*: description: type: text label: 'Description' + threading_mode: + type: integer + label: 'Threading' field.comment.storage_settings: type: sequence @@ -97,18 +100,9 @@ field.comment.field_settings: type: mapping label: 'Comment settings' mapping: - default_mode: - type: integer - label: 'Threading' - per_page: - type: integer - label: 'Comments per page' anonymous: type: integer label: 'Mode' - form_location: - type: boolean - label: ' Allow comment title' preview: type: integer label: 'Preview comment' diff --git a/core/modules/comment/js/node-new-comments-link.js b/core/modules/comment/js/node-new-comments-link.js index e1e10b2..9b72498 100644 --- a/core/modules/comment/js/node-new-comments-link.js +++ b/core/modules/comment/js/node-new-comments-link.js @@ -40,9 +40,12 @@ return; } + var perPage = $placeholder.closest('[data-comment-per-page]').attr('data-comment-per-page'); + var threadingMode = $placeholder.closest('[data-comment-threading-mode]').attr('data-comment-threading-mode'); + // Perform an AJAX request to retrieve node read timestamps. Drupal.history.fetchTimestamps(nodeIDs, function () { - processNodeNewCommentLinks($placeholders); + processNodeNewCommentLinks($placeholders, perPage, threadingMode); }); } }; @@ -71,7 +74,7 @@ .end().show(); } - function processNodeNewCommentLinks($placeholders) { + function processNodeNewCommentLinks($placeholders, perPage, threadingMode) { // Figure out which placeholders need the "x new comments" links. var $placeholdersToUpdate = {}; var fieldName = 'comment'; @@ -121,7 +124,7 @@ $.ajax({ url: Drupal.url('comments/render_new_comments_node_links'), type: 'POST', - data: { 'node_ids[]': nodeIDs, 'field_name': fieldName }, + data: { 'node_ids[]': nodeIDs, 'field_name': fieldName, 'per_page': perPage, 'threading_mode': threadingMode }, dataType: 'json', success: render }); diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php index 35e033d..7901bda 100644 --- a/core/modules/comment/src/CommentForm.php +++ b/core/modules/comment/src/CommentForm.php @@ -384,10 +384,13 @@ public function save(array $form, FormStateInterface $form_state) { else { drupal_set_message($this->t('Your comment has been posted.')); } - $query = array(); // Find the current display page for this comment. - $field_definition = $this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle())[$field_name]; - $page = $this->entityManager->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page')); + $display_settings = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'default') + ->getComponent($field_name); + $field_definition = $this->entityManager->getFieldStorageDefinitions($entity->getEntityTypeId())[$field_name]; + $comment_type = $this->entityManager->getStorage('comment_type')->load($field_definition->getSetting('comment_type')); + $page = $this->entityManager->getStorage('comment')->getDisplayOrdinal($comment, $comment_type->getThreadingMode(), $display_settings['settings']['per_page']); + $query = array(); if ($page > 0) { $query['page'] = $page; } diff --git a/core/modules/comment/src/CommentLinkBuilder.php b/core/modules/comment/src/CommentLinkBuilder.php index f689b1a..168cb03 100644 --- a/core/modules/comment/src/CommentLinkBuilder.php +++ b/core/modules/comment/src/CommentLinkBuilder.php @@ -9,11 +9,14 @@ use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\Url; +use Symfony\Component\HttpFoundation\Request; + /** * Defines a class for building markup for comment links on a commented entity. @@ -32,6 +35,13 @@ class CommentLinkBuilder implements CommentLinkBuilderInterface { protected $currentUser; /** + * Entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** * Comment manager service. * * @var \Drupal\comment\CommentManagerInterface @@ -57,8 +67,9 @@ class CommentLinkBuilder implements CommentLinkBuilderInterface { * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * String translation service. */ - public function __construct(AccountInterface $current_user, CommentManagerInterface $comment_manager, ModuleHandlerInterface $module_handler, TranslationInterface $string_translation) { + public function __construct(AccountInterface $current_user, EntityManagerInterface $entity_manager, CommentManagerInterface $comment_manager, ModuleHandlerInterface $module_handler, TranslationInterface $string_translation) { $this->currentUser = $current_user; + $this->entityManager = $entity_manager; $this->commentManager = $comment_manager; $this->moduleHandler = $module_handler; $this->stringTranslation = $string_translation; @@ -88,7 +99,7 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra $commenting_status = $entity->get($field_name)->status; if ($commenting_status != CommentItemInterface::HIDDEN) { // Entity has commenting status open or closed. - $field_definition = $entity->getFieldDefinition($field_name); + if ($view_mode == 'rss') { // Add a comments RSS element which is a URL to the comments of this // entity. @@ -101,86 +112,118 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra 'value' => $entity->url('canonical', $options), ); } - 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 - // entity is open to new comments, and there currently are none. - if ($this->currentUser->hasPermission('access comments')) { - if (!empty($entity->get($field_name)->comment_count)) { - $links['comment-comments'] = array( - 'title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), - 'attributes' => array('title' => $this->t('Jump to the first comment of this posting.')), - 'fragment' => 'comments', - 'url' => $entity->urlInfo(), + else { + $field_definition = $this->entityManager->getFieldStorageDefinitions($entity->getEntityTypeId())[$field_name]; + $comment_type = $this->entityManager->getStorage('comment_type')->load($field_definition->getSetting('comment_type')); + + // We create links to the first new comment using the standard entity + // urlInfo, i.e. the full/default view mode. + $display = $this->entityManager->getStorage('entity_view_display') + ->load($entity->getEntityTypeId() . '.' . $entity->bundle() . '.full'); + if (!$display) { + $display = $this->entityManager->getStorage('entity_view_display') + ->load($entity->getEntityTypeId() . '.' . $entity->bundle() . '.default'); + } + $formatter_options_full = $display + ? $display->getComponent($field_name): NULL; + + // Add the number of comments that have been posted and a link to the + // first new comment, if they are displayed on 'full' mode and the + // user has permission. Historically this is done for teaser only. + if ($view_mode == 'teaser' && $formatter_options_full + && !empty($entity->get($field_name)->comment_count) + && $this->currentUser->hasPermission('access comments')) { + // Comments can theoretically be displayed inside a teaser, but even + // then we still choose to link to the 'full page' comments. + $links['comment-comments'] = array( + 'title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), + 'attributes' => array('title' => $this->t('Jump to the first comment of this posting.')), + 'fragment' => 'comments', + 'url' => $entity->urlInfo(), + ); + if ($this->moduleHandler->moduleExists('history')) { + $links['comment-new-comments'] = array( + 'title' => '', + 'url' => Url::fromRoute(''), + 'attributes' => array( + 'class' => 'hidden', + 'title' => $this->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, + 'data-comment-threading-mode' => $comment_type->getThreadingMode(), + 'data-comment-per-page' => $formatter_options_full['settings']['per_page'], + ), ); - if ($this->moduleHandler->moduleExists('history')) { - $links['comment-new-comments'] = array( - 'title' => '', - 'url' => Url::fromRoute(''), - 'attributes' => array( - 'class' => 'hidden', - 'title' => $this->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, - ), - ); - } } } - // Provide a link to new comment form. + + // Add a "add new comment" link if this entity is allowing new + // comments and the user has permission. if ($commenting_status == CommentItemInterface::OPEN) { - $comment_form_location = $field_definition->getSetting('form_location'); if ($this->currentUser->hasPermission('post comments')) { - $links['comment-add'] = array( - 'title' => $this->t('Add new comment'), - 'language' => $entity->language(), - 'attributes' => array('title' => $this->t('Add a new comment to this page.')), - 'fragment' => 'comment-form', - ); - if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { - $links['comment-add']['url'] = Url::fromRoute('comment.reply', [ - 'entity_type' => $entity->getEntityTypeId(), - 'entity' => $entity->id(), - 'field_name' => $field_name, - ]); + + // If our own view mode has visible comments, we determine the + // link for the comment-add form using its formatter settings. + // If not, we use the formatter settings for 'full' view mode, + // which means that the comment-add link for a teaser can link + // to a full node page. (This is default behavior.) + $display = $this->entityManager->getStorage('entity_view_display') + ->load($entity->getEntityTypeId() . '.' . $entity->bundle() . '.' . $view_mode); + if (!$display) { + $display = $this->entityManager->getStorage('entity_view_display') + ->load($entity->getEntityTypeId() . '.' . $entity->bundle() . '.default'); } - else { - $links['comment-add'] += ['url' => $entity->urlInfo()]; + $formatter_options = $display + ? $display->getComponent($field_name) : NULL; + $fallback_to_full = !$formatter_options && $view_mode != 'full'; + if ($fallback_to_full) { + $formatter_options = $formatter_options_full; } - } - elseif ($this->currentUser->isAnonymous()) { - $links['comment-forbidden'] = array( - 'title' => $this->commentManager->forbiddenMessage($entity, $field_name), - 'html' => TRUE, - ); - } - } - } - else { - // Entity in other view modes: add a "post comment" link if the user - // is allowed to post comments and if this entity is allowing new - // comments. - if ($commenting_status == CommentItemInterface::OPEN) { - $comment_form_location = $field_definition->getSetting('form_location'); - if ($this->currentUser->hasPermission('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 == CommentItemInterface::FORM_SEPARATE_PAGE || (!empty($entity->get($field_name)->comment_count) && $this->currentUser->hasPermission('access comments'))) { + + // Show "add comment" link, unless the form is directly below it + // (that is, if there are no comments displayed in between). + if ($fallback_to_full + || !$formatter_options + || $formatter_options['settings']['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE + || (!empty($entity->get($field_name)->comment_count) + && $this->currentUser->hasPermission('access comments'))) { $links['comment-add'] = array( 'title' => $this->t('Add new comment'), + 'language' => $entity->language(), 'attributes' => array('title' => $this->t('Share your thoughts and opinions related to this posting.')), 'fragment' => 'comment-form', ); - if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) { + if (!$formatter_options + || $formatter_options['settings']['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE) { + // Link to comment reply form on separate page. $links['comment-add']['url'] = Url::fromRoute('comment.reply', [ 'entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name, ]); } - else { + elseif ($fallback_to_full) { + // We need to link to the full node and are not yet on it. $links['comment-add']['url'] = $entity->urlInfo(); } + else { + // Link to the same page; we may or may not be displaying in + // 'full' view mode. + // @todo check the following code. Can we generate just a + // href="#comment-form" somehow? + // '' won't work if you are viewing the front page, + // because the URL will be expanded to the alias (e.g. 'node') + // which will be a new request, because you are viewing '/'. + if (\Drupal::hasRequest()) { + $request = \Drupal::request(); + } + else { + // Necessary for unit tests. + $request = Request::createFromGlobals(); + } + $links['comment-add']['url'] = $request->getPathInfo() == '/' + ? Url::fromRoute('') : Url::fromRoute(''); + } } } elseif ($this->currentUser->isAnonymous()) { @@ -190,30 +233,35 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra ); } } - } - } - if (!empty($links)) { - $entity_links['comment__' . $field_name] = array( - '#theme' => 'links__entity__comment__' . $field_name, - '#links' => $links, - '#attributes' => array('class' => array('links', 'inline')), - ); - if ($view_mode == 'teaser' && $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) { - $entity_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link'; + if (!empty($links)) { + $entity_links['comment__' . $field_name] = array( + '#theme' => 'links__entity__comment__' . $field_name, + '#links' => $links, + '#attributes' => array('class' => array('links', 'inline')), + ); + if ($view_mode == 'teaser' + && $this->moduleHandler->moduleExists('history') + && $this->currentUser->isAuthenticated() + && $formatter_options_full) { + $entity_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 - // entity. - $entity_links['comment__' . $field_name]['#post_render_cache']['history_attach_timestamp'] = array( - array('node_id' => $entity->id()), - ); - $entity_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, - ), - ); + // Embed the metadata for the "X new comments" link (if any) on + // this entity. + $entity_links['comment__' . $field_name]['#post_render_cache']['history_attach_timestamp'] = array( + array('node_id' => $entity->id()), + ); + $entity_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, + 'threading_mode' => $comment_type->getThreadingMode(), + 'per_page' => $formatter_options_full['settings']['per_page'], + ), + ); + } + } } } } diff --git a/core/modules/comment/src/CommentManager.php b/core/modules/comment/src/CommentManager.php index 0094a01..c29f894 100644 --- a/core/modules/comment/src/CommentManager.php +++ b/core/modules/comment/src/CommentManager.php @@ -7,6 +7,7 @@ namespace Drupal\comment; +use Drupal\comment\Plugin\Field\FieldFormatter\CommentDefaultFormatter; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; @@ -130,6 +131,8 @@ public function addDefaultField($entity_type, $bundle, $field_name = 'comment', '%entity_type' => $entity_type, ))); } + $comment_type->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); + $comment_type->save(); } else { // Silently create the comment-type for the calling code. @@ -138,6 +141,7 @@ public function addDefaultField($entity_type, $bundle, $field_name = 'comment', 'label' => Unicode::ucfirst($comment_type_id), 'target_entity_type_id' => $entity_type, 'description' => 'Default comment field', + 'threading_mode' => CommentTypeInterface::THREADING_MODE_THREADED, ))->save(); } // Make sure the field doesn't already exist. @@ -198,6 +202,10 @@ public function addDefaultField($entity_type, $bundle, $field_name = 'comment', 'label' => 'above', 'type' => 'comment_default', 'weight' => 20, + 'settings' => array( + 'per_page' => 50, + 'form_location' => CommentItemInterface::FORM_BELOW, + ) )) ->save(); // The comment field should be hidden in all other view displays. diff --git a/core/modules/comment/src/CommentManagerInterface.php b/core/modules/comment/src/CommentManagerInterface.php index 0f00351..8e9f782 100644 --- a/core/modules/comment/src/CommentManagerInterface.php +++ b/core/modules/comment/src/CommentManagerInterface.php @@ -17,16 +17,6 @@ interface CommentManagerInterface { /** - * Comments are displayed in a flat list - expanded. - */ - const COMMENT_MODE_FLAT = 0; - - /** - * Comments are displayed as a threaded list - expanded. - */ - const COMMENT_MODE_THREADED = 1; - - /** * Utility function to return an array of comment fields. * * @param string $entity_type_id diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php index 2cbaed7..5b93225 100644 --- a/core/modules/comment/src/CommentStorage.php +++ b/core/modules/comment/src/CommentStorage.php @@ -107,7 +107,7 @@ public function getDisplayOrdinal(CommentInterface $comment, $comment_mode, $div $query->condition('c1.status', CommentInterface::PUBLISHED); } - if ($comment_mode == CommentManagerInterface::COMMENT_MODE_FLAT) { + if ($comment_mode == CommentTypeInterface::THREADING_MODE_FLAT) { // For rendering flat comments, cid is used for ordering comments due to // unpredictable behavior with timestamp, so we make the same assumption // here. @@ -131,15 +131,13 @@ public function getDisplayOrdinal(CommentInterface $comment, $comment_mode, $div /** * {@inheritdoc} */ - public function getNewCommentPageNumber($total_comments, $new_comments, FieldableEntityInterface $entity, $field_name = 'comment') { - $field = $entity->getFieldDefinition($field_name); - $comments_per_page = $field->getSetting('per_page'); + public function getNewCommentPageNumber($total_comments, $new_comments, FieldableEntityInterface $entity, $mode = CommentTypeInterface::THREADING_MODE_THREADED, $comments_per_page = 50, $field_name = 'comment') { if ($total_comments <= $comments_per_page) { // Only one page of comments. $count = 0; } - elseif ($field->getSetting('default_mode') == CommentManagerInterface::COMMENT_MODE_FLAT) { + elseif ($mode == CommentTypeInterface::THREADING_MODE_FLAT) { // Flat comments. $count = $total_comments - $new_comments; } @@ -296,7 +294,7 @@ public function loadThread(EntityInterface $entity, $field_name, $mode, $comment $query->condition('c.status', CommentInterface::PUBLISHED); $count_query->condition('c.status', CommentInterface::PUBLISHED); } - if ($mode == CommentManagerInterface::COMMENT_MODE_FLAT) { + if ($mode == CommentTypeInterface::THREADING_MODE_FLAT) { $query->orderBy('c.cid', 'ASC'); } else { diff --git a/core/modules/comment/src/CommentStorageInterface.php b/core/modules/comment/src/CommentStorageInterface.php index 5be4776..aa0dfe0 100644 --- a/core/modules/comment/src/CommentStorageInterface.php +++ b/core/modules/comment/src/CommentStorageInterface.php @@ -48,13 +48,18 @@ public function getMaxThreadPerThread(CommentInterface $comment); * The number of new comments that the entity has. * @param \Drupal\Core\Entity\FieldableEntityInterface $entity * The entity to which the comments belong. + * @param int $mode + * (optional) The threading mode: CommentTypeInterface::THREADING_MODE_FLAT + * or CommentTypeInterface::THREADING_MODE_THREADED (the default). + * @param int $comments_per_page + * (optional) The amount of comments to display per page. Defaults to 50. * @param string $field_name * The field name on the entity to which comments are attached. * * @return array|null * The page number where first new comment appears. (First page returns 0.) */ - public function getNewCommentPageNumber($total_comments, $new_comments, FieldableEntityInterface $entity, $field_name = 'comment'); + public function getNewCommentPageNumber($total_comments, $new_comments, FieldableEntityInterface $entity, $mode = CommentTypeInterface::THREADING_MODE_THREADED, $comments_per_page = 50, $field_name = 'comment'); /** * Gets the display ordinal or page number for a comment. @@ -62,8 +67,8 @@ public function getNewCommentPageNumber($total_comments, $new_comments, Fieldabl * @param \Drupal\comment\CommentInterface $comment * The comment to use as a reference point. * @param int $comment_mode - * The comment display mode: CommentManagerInterface::COMMENT_MODE_FLAT or - * CommentManagerInterface::COMMENT_MODE_THREADED. + * The threading mode: CommentTypeInterface::THREADING_MODE_FLAT or + * CommentTypeInterface::THREADING_MODE_THREADED. * @param int $divisor * Defaults to 1, which returns the display ordinal for a comment. If the * number of comments per page is provided, the returned value will be the @@ -93,8 +98,8 @@ public function getChildCids(array $comments); * @param string $field_name * The field_name whose comment(s) needs rendering. * @param int $mode - * The comment display mode: CommentManagerInterface::COMMENT_MODE_FLAT or - * CommentManagerInterface::COMMENT_MODE_THREADED. + * The threading mode: CommentTypeInterface::THREADING_MODE_FLAT or + * CommentTypeInterface::THREADING_MODE_THREADED. * @param int $comments_per_page * (optional) The amount of comments to display per page. * Defaults to 0, which means show all comments. diff --git a/core/modules/comment/src/CommentTypeForm.php b/core/modules/comment/src/CommentTypeForm.php index 8e73652..b06b888 100644 --- a/core/modules/comment/src/CommentTypeForm.php +++ b/core/modules/comment/src/CommentTypeForm.php @@ -63,6 +63,7 @@ public function __construct(EntityManagerInterface $entity_manager, LoggerInterf public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); + /** @var $comment_type CommentTypeInterface */ $comment_type = $this->entity; $form['label'] = array( @@ -114,6 +115,13 @@ public function form(array $form, FormStateInterface $form_state) { ); } + $form['threading_mode'] = array( + '#type' => 'checkbox', + '#title' => t('Threading'), + '#default_value' => $comment_type->getThreadingMode(), + '#description' => t('Show comment replies in a threaded list.'), + ); + if ($this->moduleHandler->moduleExists('content_translation')) { $form['language'] = array( '#type' => 'details', diff --git a/core/modules/comment/src/CommentTypeInterface.php b/core/modules/comment/src/CommentTypeInterface.php index 2ccc0c1..b7015a7 100644 --- a/core/modules/comment/src/CommentTypeInterface.php +++ b/core/modules/comment/src/CommentTypeInterface.php @@ -15,6 +15,16 @@ interface CommentTypeInterface extends ConfigEntityInterface { /** + * Comments are displayed as a flat list. + */ + const THREADING_MODE_FLAT = 0; + + /** + * Comments are displayed as a threaded list. + */ + const THREADING_MODE_THREADED = 1; + + /** * Returns the comment type description. * * @return string @@ -40,4 +50,23 @@ public function setDescription($description); */ public function getTargetEntityTypeId(); + /** + * Gets the threading mode for this comment type. + * + * @return int + * The comment threading mode (CommentTypeInterface::THREADING_MODE_FLAT or + * CommentTypeInterface::THREADING_MODE_THREADED) + */ + public function getThreadingMode(); + + /** + * Sets the threading mode for this comment type. + * + * @param int $mode + * The threading mode. + * + * @return $this + */ + public function setThreadingMode($mode); + } diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php index cd9a5b9..fe879ee 100644 --- a/core/modules/comment/src/CommentViewBuilder.php +++ b/core/modules/comment/src/CommentViewBuilder.php @@ -15,7 +15,6 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityViewBuilder; -use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -74,9 +73,13 @@ public function __construct(EntityTypeInterface $entity_type, EntityManagerInter protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { $build = parent::getBuildDefaults($entity, $view_mode, $langcode); + // Threading mode setting is stored in comment_type config; the name of the + // specific comment type is stored in our field definition. + $field_definition = $this->entityManager->getFieldStorageDefinitions($entity->getCommentedEntity()->getEntityTypeId())[$entity->getFieldName()]; + $comment_type = $this->entityManager->getStorage('comment_type')->load($field_definition->getSetting('comment_type')); // If threading is enabled, don't render cache individual comments, but do // keep the cache tags, so they can bubble up. - if ($entity->getCommentedEntity()->getFieldDefinition($entity->getFieldName())->getSetting('default_mode') === CommentManagerInterface::COMMENT_MODE_THREADED) { + if ($comment_type->getThreadingMode() === CommentTypeInterface::THREADING_MODE_THREADED) { $cache_tags = $build['#cache']['tags']; $build['#cache'] = []; $build['#cache']['tags'] = $cache_tags; @@ -309,9 +312,10 @@ protected function alterBuild(array &$build, EntityInterface $comment, EntityVie if (empty($comment->in_preview)) { $prefix = ''; $commented_entity = $comment->getCommentedEntity(); - $field_definition = $this->entityManager->getFieldDefinitions($commented_entity->getEntityTypeId(), $commented_entity->bundle())[$comment->getFieldName()]; + $field_definition = $this->entityManager->getFieldStorageDefinitions($commented_entity->getEntityTypeId())[$comment->getFieldName()]; + $comment_type = $this->entityManager->getStorage('comment_type')->load($field_definition->getSetting('comment_type')); $is_threaded = isset($comment->divs) - && $field_definition->getSetting('default_mode') == CommentManagerInterface::COMMENT_MODE_THREADED; + && $comment_type->getThreadingMode() == CommentTypeInterface::THREADING_MODE_THREADED; // Add indentation div or close open divs as needed. if ($is_threaded) { @@ -360,7 +364,7 @@ public static function attachNewCommentsLinkMetadata(array $element, array $cont $field_name = $context['field_name']; $page_number = \Drupal::entityManager() ->getStorage('comment') - ->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new, $entity); + ->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new, $entity, $context['threading_mode'], $context['per_page']); $query = $page_number ? array('page' => $page_number) : NULL; // Attach metadata. diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php index 4af3f05..245e13c 100644 --- a/core/modules/comment/src/Controller/CommentController.php +++ b/core/modules/comment/src/Controller/CommentController.php @@ -9,6 +9,7 @@ use Drupal\comment\CommentInterface; use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Entity\EntityInterface; @@ -123,10 +124,12 @@ public function commentPermalink(Request $request, CommentInterface $comment) { if (!$entity->access('view')) { throw new AccessDeniedHttpException(); } - $field_definition = $this->entityManager()->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle())[$comment->getFieldName()]; - + $field_definition = $this->entityManager()->getFieldStorageDefinitions($entity->getEntityTypeId())[$comment->getFieldName()]; + $comment_type = $this->entityManager()->getStorage('comment_type')->load($field_definition->getSetting('comment_type')); + $display_settings = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'default') + ->getComponent($comment->getFieldName()); // Find the current display page for this comment. - $page = $this->entityManager()->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page')); + $page = $this->entityManager()->getStorage('comment')->getDisplayOrdinal($comment, $comment_type->getThreadingMode(), $display_settings['settings']['per_page']); // @todo: Cleaner sub request handling. $redirect_request = Request::create($entity->getSystemPath(), 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all()); $redirect_request->query->set('page', $page); @@ -289,18 +292,20 @@ public function renderNewCommentsNodeLinks(Request $request) { $nids = $request->request->get('node_ids'); $field_name = $request->request->get('field_name'); - if (!isset($nids)) { + $per_page = $request->request->get('per_page'); + $threading_mode = $request->request->get('threading_mode'); + if (!isset($nids) || !in_array($threading_mode, array(CommentTypeInterface::THREADING_MODE_THREADED, CommentTypeInterface::THREADING_MODE_FLAT))) { throw new NotFoundHttpException(); } // Only handle up to 100 nodes. $nids = array_slice($nids, 0, 100); $links = array(); - foreach ($nids as $nid) { - $node = $this->entityManager->getStorage('node')->load($nid); + $nodes = $this->entityManager->getStorage('node')->loadMultiple($nids); + foreach ($nodes as $nid => $node) { $new = $this->commentManager->getCountNewComments($node); $page_number = $this->entityManager()->getStorage('comment') - ->getNewCommentPageNumber($node->{$field_name}->comment_count, $new, $node); + ->getNewCommentPageNumber($node->{$field_name}->comment_count, $new, $node, $threading_mode, $per_page); $query = $page_number ? array('page' => $page_number) : NULL; $links[$nid] = array( 'new_comment_count' => (int) $new, diff --git a/core/modules/comment/src/Entity/CommentType.php b/core/modules/comment/src/Entity/CommentType.php index 3a1b3a8..d32ed36 100644 --- a/core/modules/comment/src/Entity/CommentType.php +++ b/core/modules/comment/src/Entity/CommentType.php @@ -71,6 +71,13 @@ class CommentType extends ConfigEntityBundleBase implements CommentTypeInterface protected $target_entity_type_id; /** + * The comment threading mode (flat or threaded). + * + * @var int + */ + protected $threading_mode; + + /** * {@inheritdoc} */ public function getDescription() { @@ -95,6 +102,22 @@ public function getTargetEntityTypeId() { /** * {@inheritdoc} */ + public function getThreadingMode() { + return isset($this->threading_mode) ? $this->threading_mode : + CommentTypeInterface::THREADING_MODE_THREADED; + } + + /** + * {@inheritdoc} + */ + public function setThreadingMode($mode) { + $this->threading_mode = $mode; + return $this; + } + + /** + * {@inheritdoc} + */ public function postSave(EntityStorageInterface $storage, $update = TRUE) { parent::postSave($storage, $update); if (!$update && !$this->isSyncing()) { diff --git a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php index 02c3107..7364a7f 100644 --- a/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php +++ b/core/modules/comment/src/Plugin/Field/FieldFormatter/CommentDefaultFormatter.php @@ -7,19 +7,15 @@ namespace Drupal\comment\Plugin\Field\FieldFormatter; -use Drupal\comment\CommentManagerInterface; -use Drupal\comment\CommentStorageInterface; -use Drupal\comment\Entity\Comment; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\EntityViewBuilderInterface; use Drupal\Core\Entity\EntityFormBuilderInterface; use Drupal\Core\Field\FieldItemListInterface; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FormatterBase; +use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Session\AccountInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -45,6 +41,8 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP public static function defaultSettings() { return array( 'pager_id' => 0, + 'per_page' => 50, + 'form_location' => CommentItemInterface::FORM_BELOW, ) + parent::defaultSettings(); } @@ -97,7 +95,9 @@ public static function create(ContainerInterface $container, array $configuratio $configuration['third_party_settings'], $container->get('current_user'), $container->get('entity.manager'), - $container->get('entity.form_builder') + $container->get('entity.form_builder'), + $container->get('module_handler'), + $container->get('comment.manager') ); } @@ -151,7 +151,8 @@ public function viewElements(FieldItemListInterface $items) { // comment_node_update_index() instead of by this formatter, so don't // return anything if the view mode is search_index or search_result. !in_array($this->viewMode, array('search_result', 'search_index'))) { - $comment_settings = $this->getFieldSettings(); + + $comment_type = $this->entityManager->getStorage('comment_type')->load($this->getFieldSetting('comment_type')); // Only attempt to render comments if the entity has visible comments. // Unpublished comments are not included in @@ -163,8 +164,8 @@ public function viewElements(FieldItemListInterface $items) { $output['comments']['#cache']['tags'] = $this->entityManager->getDefinition('comment')->getListCacheTags(); if ($entity->get($field_name)->comment_count || $this->currentUser->hasPermission('administer comments')) { - $mode = $comment_settings['default_mode']; - $comments_per_page = $comment_settings['per_page']; + $mode = $comment_type->getThreadingMode(); + $comments_per_page = $this->getSetting('per_page'); $comments = $this->storage->loadThread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id')); if ($comments) { comment_prepare_thread($comments); @@ -180,7 +181,7 @@ public function viewElements(FieldItemListInterface $items) { // Append comment form if the comments are open and the form is set to // display below the entity. Do not show the form for the print view mode. - if ($status == CommentItemInterface::OPEN && $comment_settings['form_location'] == CommentItemInterface::FORM_BELOW && $this->viewMode != 'print') { + if ($status == CommentItemInterface::OPEN && $this->getSetting('form_location') == CommentItemInterface::FORM_BELOW && $this->viewMode != 'print') { // Only show the add comment form if the user has permission. if ($this->currentUser->hasPermission('post comments')) { // All users in the "anonymous" role can use the same form: it is fine @@ -219,7 +220,7 @@ public function viewElements(FieldItemListInterface $items) { $elements[] = $output + array( '#comment_type' => $this->getFieldSetting('comment_type'), - '#comment_display_mode' => $this->getFieldSetting('default_mode'), + '#comment_threading_mode' => $comment_type->getThreadingMode(), 'comments' => array(), 'comment_form' => array(), ); @@ -233,6 +234,20 @@ public function viewElements(FieldItemListInterface $items) { */ public function settingsForm(array $form, FormStateInterface $form_state) { $element = array(); + $element['per_page'] = array( + '#type' => 'number', + '#title' => t('Comments per page'), + '#default_value' => $this->getSetting('per_page'), + '#required' => TRUE, + '#min' => 10, + '#max' => 1000, + '#step' => 10, + ); + $element['form_location'] = array( + '#type' => 'checkbox', + '#title' => t('Show reply form on the same page as comments'), + '#default_value' => $this->getSetting('form_location'), + ); $element['pager_id'] = array( '#type' => 'select', '#title' => $this->t('Pager ID'), @@ -247,13 +262,17 @@ public function settingsForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function settingsSummary() { - // Only show a summary if we're using a non-standard pager id. + $variables = array( + '@id' => $this->getSetting('pager_id'), + '@form' => $this->getSetting('form_location') ? $this->t('inline form') : $this->t('form on separate page'), + '@per_page' => $this->getSetting('per_page'), + ); if ($this->getSetting('pager_id')) { - return array($this->t('Pager ID: @id', array( - '@id' => $this->getSetting('pager_id'), - ))); + // Only include pager details in summary if we're using a non-standard + // pager id. + return array($this->t('Showing @per_page comments with @form, using pager ID @id', $variables)); } - return array(); + return array($this->t('Showing @per_page comments with @form', $variables)); } } diff --git a/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php b/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php index 93274a5..34d1269 100644 --- a/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php +++ b/core/modules/comment/src/Plugin/Field/FieldType/CommentItem.php @@ -42,9 +42,6 @@ public static function defaultStorageSettings() { */ public static function defaultFieldSettings() { return array( - 'default_mode' => CommentManagerInterface::COMMENT_MODE_THREADED, - 'per_page' => 50, - 'form_location' => CommentItemInterface::FORM_BELOW, 'anonymous' => COMMENT_ANONYMOUS_MAYNOT_CONTACT, 'preview' => DRUPAL_OPTIONAL, ) + parent::defaultFieldSettings(); @@ -114,21 +111,6 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { 'library' => array('comment/drupal.comment'), ), ); - $element['comment']['default_mode'] = array( - '#type' => 'checkbox', - '#title' => t('Threading'), - '#default_value' => $settings['default_mode'], - '#description' => t('Show comment replies in a threaded list.'), - ); - $element['comment']['per_page'] = array( - '#type' => 'number', - '#title' => t('Comments per page'), - '#default_value' => $settings['per_page'], - '#required' => TRUE, - '#min' => 10, - '#max' => 1000, - '#step' => 10, - ); $element['comment']['anonymous'] = array( '#type' => 'select', '#title' => t('Anonymous commenting'), @@ -140,11 +122,6 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { ), '#access' => $anonymous_user->hasPermission('post comments'), ); - $element['comment']['form_location'] = array( - '#type' => 'checkbox', - '#title' => t('Show reply form on the same page as comments'), - '#default_value' => $settings['form_location'], - ); $element['comment']['preview'] = array( '#type' => 'radios', '#title' => t('Preview comment'), diff --git a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php index 8db37c5..98cd059 100644 --- a/core/modules/comment/src/Plugin/views/field/NodeNewComments.php +++ b/core/modules/comment/src/Plugin/views/field/NodeNewComments.php @@ -90,6 +90,21 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { '#type' => 'checkbox', '#default_value' => $this->options['link_to_comment'], ); + $form['threading_mode'] = array( + '#type' => 'checkbox', + '#title' => t('Threading'), + '#default_value' => $this->options['threading_mode'], + '#description' => t('Comment replies use a threaded list.'), + ); + $element['per_page'] = array( + '#type' => 'number', + '#title' => t('Comments per page'), + '#default_value' => $this->options['per_page'], + '#required' => TRUE, + '#min' => 10, + '#max' => 1000, + '#step' => 10, + ); parent::buildOptionsForm($form, $form_state); } @@ -153,7 +168,7 @@ protected function renderLink($data, ResultRow $values) { 'type' => $this->getValue($values, 'type'), )); $page_number = \Drupal::entityManager()->getStorage('comment') - ->getNewCommentPageNumber($this->getValue($values, 'comment_count'), $this->getValue($values), $node); + ->getNewCommentPageNumber($this->getValue($values, 'comment_count'), $this->getValue($values), $node, $this->options['threading_mode'], $this->options['per_page']); $this->options['alter']['make_link'] = TRUE; $this->options['alter']['path'] = 'node/' . $node->id(); $this->options['alter']['query'] = $page_number ? array('page' => $page_number) : NULL; diff --git a/core/modules/comment/src/Tests/CommentCacheTagsTest.php b/core/modules/comment/src/Tests/CommentCacheTagsTest.php index 5da5555..ce4ce59 100644 --- a/core/modules/comment/src/Tests/CommentCacheTagsTest.php +++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php @@ -7,9 +7,9 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; +use Drupal\comment\Entity\CommentType; use Drupal\Core\Entity\EntityInterface; -use Drupal\field\Entity\FieldConfig; use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase; use Drupal\user\Entity\Role; @@ -50,9 +50,9 @@ protected function createEntity() { \Drupal::service('comment.manager')->addDefaultField('entity_test', 'bar', 'comment'); // Display comments in a flat list; threaded comments are not render cached. - $field = FieldConfig::loadByName('entity_test', 'bar', 'comment'); - $field->settings['default_mode'] = CommentManagerInterface::COMMENT_MODE_FLAT; - $field->save(); + $comment_type = CommentType::load('comment'); + $comment_type->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); + $comment_type->save(); // Create a "Camelids" test entity. $entity_test = entity_create('entity_test', array( diff --git a/core/modules/comment/src/Tests/CommentInterfaceTest.php b/core/modules/comment/src/Tests/CommentInterfaceTest.php index b174492..e107aee 100644 --- a/core/modules/comment/src/Tests/CommentInterfaceTest.php +++ b/core/modules/comment/src/Tests/CommentInterfaceTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\comment\Entity\Comment; @@ -27,7 +27,7 @@ function testCommentInterface() { $this->setCommentPreview(DRUPAL_DISABLED); $this->setCommentForm(TRUE); $this->setCommentSubject(FALSE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $this->drupalLogout(); // Post comment #1 without subject or preview. diff --git a/core/modules/comment/src/Tests/CommentLinksTest.php b/core/modules/comment/src/Tests/CommentLinksTest.php index 34b8b41..773e7ea8 100644 --- a/core/modules/comment/src/Tests/CommentLinksTest.php +++ b/core/modules/comment/src/Tests/CommentLinksTest.php @@ -7,10 +7,9 @@ namespace Drupal\comment\Tests; +use Drupal\comment\CommentInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Core\Language\LanguageInterface; -use Drupal\comment\CommentInterface; -use Drupal\entity\Entity\EntityViewDisplay; /** * Basic comment links tests to ensure markup present. @@ -76,7 +75,7 @@ public function testCommentLinks() { $this->comment = $comment; // Change comment settings. - $this->setCommentSettings('form_location', CommentItemInterface::FORM_BELOW, 'Set comment form location'); + $this->setCommentFormatterSettings('form_location', CommentItemInterface::FORM_BELOW, 'Set comment form location'); $this->setCommentAnonymous(TRUE); $this->node->comment = CommentItemInterface::OPEN; $this->node->save(); diff --git a/core/modules/comment/src/Tests/CommentNewIndicatorTest.php b/core/modules/comment/src/Tests/CommentNewIndicatorTest.php index d057415..0be4f01 100644 --- a/core/modules/comment/src/Tests/CommentNewIndicatorTest.php +++ b/core/modules/comment/src/Tests/CommentNewIndicatorTest.php @@ -109,6 +109,8 @@ public function testCommentNewCommentsIndicator() { // perform an HTTP request to render the "new comments" node link. $this->assertIdentical(1, count($this->xpath('//*[@data-history-node-last-comment-timestamp="' . $comment->getChangedTime() . '"]')), 'data-history-node-last-comment-timestamp attribute is set to the correct value.'); $this->assertIdentical(1, count($this->xpath('//*[@data-history-node-field-name="comment"]')), 'data-history-node-field-name attribute is set to the correct value.'); + $this->assertIdentical(1, count($this->xpath('//*[@data-comment-per-page="50"]')), 'data-comment-per-page attribute is set to the correct value.'); + $this->assertIdentical(1, count($this->xpath('//*[@data-comment-threading-mode="1"]')), 'data-comment-threading-mode is set to the correct value.'); $response = $this->renderNewCommentsNodeLinks(array($this->node->id())); $this->assertResponse(200); $json = Json::decode($response); diff --git a/core/modules/comment/src/Tests/CommentNodeAccessTest.php b/core/modules/comment/src/Tests/CommentNodeAccessTest.php index 4c50180..28e76d7 100644 --- a/core/modules/comment/src/Tests/CommentNodeAccessTest.php +++ b/core/modules/comment/src/Tests/CommentNodeAccessTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; /** * Tests comments with node access. @@ -54,7 +54,7 @@ function testThreadedCommentView() { $this->setCommentPreview(DRUPAL_DISABLED); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $this->drupalLogout(); // Post comment. diff --git a/core/modules/comment/src/Tests/CommentPagerTest.php b/core/modules/comment/src/Tests/CommentPagerTest.php index c3fa58e..357648a 100644 --- a/core/modules/comment/src/Tests/CommentPagerTest.php +++ b/core/modules/comment/src/Tests/CommentPagerTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\Component\Utility\String; use Drupal\node\Entity\Node; @@ -35,7 +35,7 @@ function testCommentPaging() { $comments[] = $this->postComment($node, $this->randomMachineName(), $this->randomMachineName(), TRUE); $comments[] = $this->postComment($node, $this->randomMachineName(), $this->randomMachineName(), TRUE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_FLAT, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); // Set comments to one per page so that we are able to test paging without // needing to insert large numbers of comments. @@ -75,7 +75,7 @@ function testCommentPaging() { // If we switch to threaded mode, the replies on the oldest comment // should be bumped to the first page and comment 6 should be bumped // to the second page. - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Switched to threaded mode.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED, 'Switched to threaded mode.'); $this->drupalGet('node/' . $node->id(), array('query' => array('page' => 0))); $this->assertTrue($this->commentExists($reply, TRUE), 'In threaded mode, reply appears on page 1.'); $this->assertFalse($this->commentExists($comments[1]), 'In threaded mode, comment 2 has been bumped off of page 1.'); @@ -135,7 +135,7 @@ function testCommentOrderingThreading() { // - 2 // - 5 - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_FLAT, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); $expected_order = array( 0, @@ -149,7 +149,7 @@ function testCommentOrderingThreading() { $this->drupalGet('node/' . $node->id()); $this->assertCommentOrder($comments, $expected_order); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Switched to threaded mode.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED, 'Switched to threaded mode.'); $expected_order = array( 0, @@ -230,7 +230,7 @@ function testCommentNewPageIndicator() { // - 2 // - 5 - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_FLAT, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); $expected_pages = array( 1 => 5, // Page of comment 5 @@ -244,11 +244,11 @@ function testCommentNewPageIndicator() { $node = Node::load($node->id()); foreach ($expected_pages as $new_replies => $expected_page) { $returned_page = \Drupal::entityManager()->getStorage('comment') - ->getNewCommentPageNumber($node->get('comment')->comment_count, $new_replies, $node); + ->getNewCommentPageNumber($node->get('comment')->comment_count, $new_replies, $node, CommentTypeInterface::THREADING_MODE_FLAT, 1); $this->assertIdentical($expected_page, $returned_page, format_string('Flat mode, @new replies: expected page @expected, returned page @returned.', array('@new' => $new_replies, '@expected' => $expected_page, '@returned' => $returned_page))); } - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Switched to threaded mode.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED, 'Switched to threaded mode.'); $expected_pages = array( 1 => 5, // Page of comment 5 @@ -263,7 +263,7 @@ function testCommentNewPageIndicator() { $node = Node::load($node->id()); foreach ($expected_pages as $new_replies => $expected_page) { $returned_page = \Drupal::entityManager()->getStorage('comment') - ->getNewCommentPageNumber($node->get('comment')->comment_count, $new_replies, $node); + ->getNewCommentPageNumber($node->get('comment')->comment_count, $new_replies, $node, CommentTypeInterface::THREADING_MODE_THREADED, 1); $this->assertEqual($expected_page, $returned_page, format_string('Threaded mode, @new replies: expected page @expected, returned page @returned.', array('@new' => $new_replies, '@expected' => $expected_page, '@returned' => $returned_page))); } } @@ -290,16 +290,16 @@ function testTwoPagers() { $account = $this->drupalCreateUser(array('administer node display')); $this->drupalLogin($account); $this->drupalGet('admin/structure/types/manage/article/display'); - $this->assertNoText(t('Pager ID: @id', array('@id' => 0)), 'No summary for standard pager'); - $this->assertText(t('Pager ID: @id', array('@id' => 1))); + $this->assertNoText(t(' using pager ID @id', array('@id' => 0)), 'No summary for standard pager'); + $this->assertText(t(' using pager ID @id', array('@id' => 1))); $this->drupalPostAjaxForm(NULL, array(), 'comment_settings_edit'); // Change default pager to 2. $this->drupalPostForm(NULL, array('fields[comment][settings_edit_form][settings][pager_id]' => 2), t('Save')); - $this->assertText(t('Pager ID: @id', array('@id' => 2))); + $this->assertText(t(' using pager ID @id', array('@id' => 2))); // Revert the changes back. $this->drupalPostAjaxForm(NULL, array(), 'comment_settings_edit'); $this->drupalPostForm(NULL, array('fields[comment][settings_edit_form][settings][pager_id]' => 0), t('Save')); - $this->assertNoText(t('Pager ID: @id', array('@id' => 0)), 'No summary for standard pager'); + $this->assertNoText(t(' using pager ID @id', array('@id' => 0)), 'No summary for standard pager'); $this->drupalLogin($this->admin_user); @@ -310,7 +310,7 @@ function testTwoPagers() { foreach (array('comment', 'comment_2') as $field_name) { $this->setCommentForm(TRUE, $field_name); $this->setCommentPreview(DRUPAL_OPTIONAL, $field_name); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_FLAT, 'Comment paging changed.', $field_name); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); // Set comments to one per page so that we are able to test paging without // needing to insert large numbers of comments. diff --git a/core/modules/comment/src/Tests/CommentPreviewTest.php b/core/modules/comment/src/Tests/CommentPreviewTest.php index 8d188c3..80f652a 100644 --- a/core/modules/comment/src/Tests/CommentPreviewTest.php +++ b/core/modules/comment/src/Tests/CommentPreviewTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\Core\Datetime\DrupalDateTime; use Drupal\comment\Entity\Comment; @@ -36,7 +36,7 @@ function testCommentPreview() { $this->setCommentPreview(DRUPAL_OPTIONAL); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $this->drupalLogout(); // Login as web user and add a signature and a user picture. @@ -79,7 +79,7 @@ function testCommentEditPreviewSave() { $this->setCommentPreview(DRUPAL_OPTIONAL); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $edit = array(); $date = new DrupalDateTime('2008-03-02 17:23'); diff --git a/core/modules/comment/src/Tests/CommentStatisticsTest.php b/core/modules/comment/src/Tests/CommentStatisticsTest.php index ea93f94..923c9ab 100644 --- a/core/modules/comment/src/Tests/CommentStatisticsTest.php +++ b/core/modules/comment/src/Tests/CommentStatisticsTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\comment\Entity\Comment; /** @@ -42,7 +42,7 @@ function testCommentNodeCommentStatistics() { $this->setCommentPreview(DRUPAL_DISABLED); $this->setCommentForm(TRUE); $this->setCommentSubject(FALSE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $this->drupalLogout(); // Checks the initial values of node comment statistics with no comment. diff --git a/core/modules/comment/src/Tests/CommentTestBase.php b/core/modules/comment/src/Tests/CommentTestBase.php index d86e3d4..6eb00d7 100644 --- a/core/modules/comment/src/Tests/CommentTestBase.php +++ b/core/modules/comment/src/Tests/CommentTestBase.php @@ -81,6 +81,20 @@ protected function setUp() { // Create comment field on article. $this->container->get('comment.manager')->addDefaultField('node', 'article'); + // Set some display options for search results and print pages. + if ($this->container->get('module_handler')->moduleExists('book')) { + $display = entity_get_display('node', 'article', 'print'); + $display->removeComponent('comment'); + $display->save(); + } + if ($this->container->get('module_handler')->moduleExists('search')) { + foreach (array('search_result', 'search_index') as $view_mode) { + $display = entity_get_display('node', 'article', $view_mode); + $display->removeComponent('comment'); + $display->save(); + } + } + // Create a test node authored by the web user. $this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->id())); } @@ -267,7 +281,7 @@ public function setCommentPreview($mode, $field_name = 'comment') { * Defaults to 'comment'. */ public function setCommentForm($enabled, $field_name = 'comment') { - $this->setCommentSettings('form_location', ($enabled ? CommentItemInterface::FORM_BELOW : CommentItemInterface::FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.', $field_name); + $this->setCommentFormatterSettings('form_location', ($enabled ? CommentItemInterface::FORM_BELOW : CommentItemInterface::FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.', $field_name); } /** @@ -293,11 +307,27 @@ function setCommentAnonymous($level) { * Defaults to 'comment'. */ public function setCommentsPerPage($number, $field_name = 'comment') { - $this->setCommentSettings('per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)), $field_name); + $this->setCommentFormatterSettings('per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)), $field_name); + } + + /** + * Sets the threading mode for this comment type. + * + * @param int $mode + * The threading mode. + * @param string $message + * (optional) Status message to display. Defaults to 'Comment threading + * changed.' + */ + public function setThreadingMode($mode, $message = 'Comment threading changed.') { + $comment_type = CommentType::load('comment'); + $comment_type->setThreadingMode($mode); + $comment_type->save(); + $this->pass($message); } /** - * Sets a comment settings variable for the article content type. + * Sets a comment field settings variable for the article content type. * * @param string $name * Name of variable. @@ -318,6 +348,32 @@ public function setCommentSettings($name, $value, $message, $field_name = 'comme } /** + * Sets a display formatter settings variable for the article content type. + * + * @param string $name + * Name of variable. + * @param string $value + * Value of variable. + * @param string $message + * Status message to display. + * @param string $field_name + * (optional) Field name through which the comment should be posted. + * Defaults to 'comment'. + */ + public function setCommentFormatterSettings($name, $value, $message, $field_name = 'comment') { + // Some of our tests (like CommentLinksTest) assume we are working with the + // 'default' display for full node pages. This works (just like default site + // settings do), as long as we never create/save 'full' display. + $display = entity_get_display('node', 'article', 'default'); + $component = $display->getComponent($field_name); + $component['settings'][$name] = $value; + $display->setComponent($field_name, $component); + $display->save(); + // Display status message. + $this->pass($message); + } + + /** * Checks whether the commenter's contact information is displayed. * * @return boolean diff --git a/core/modules/comment/src/Tests/CommentThreadingTest.php b/core/modules/comment/src/Tests/CommentThreadingTest.php index 6fa1197..848ba2c 100644 --- a/core/modules/comment/src/Tests/CommentThreadingTest.php +++ b/core/modules/comment/src/Tests/CommentThreadingTest.php @@ -7,7 +7,7 @@ namespace Drupal\comment\Tests; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; /** * Tests to make sure the comment number increments properly. @@ -24,7 +24,7 @@ function testCommentThreading() { $this->setCommentPreview(DRUPAL_DISABLED); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); - $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); $this->drupalLogout(); // Create a node. diff --git a/core/modules/comment/templates/field--comment.html.twig b/core/modules/comment/templates/field--comment.html.twig index 9aae572..9691012 100644 --- a/core/modules/comment/templates/field--comment.html.twig +++ b/core/modules/comment/templates/field--comment.html.twig @@ -15,7 +15,7 @@ * - comments: List of comments rendered through comment.html.twig. * - content_attributes: HTML attributes for the form title. * - comment_form: The 'Add new comment' form. - * - comment_display_mode: Is the comments are threaded. + * - comment_threading_mode: Is non-zero if the comments are threaded. * - comment_type: The comment type bundle ID for the comment field. * * @see template_preprocess_field() diff --git a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php b/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php index fdcb8f5..ee4e8e7 100644 --- a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php +++ b/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php @@ -8,11 +8,12 @@ namespace Drupal\Tests\comment\Unit; use Drupal\comment\CommentLinkBuilder; +use Drupal\comment\CommentTypeInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Core\Url; -use Drupal\node\NodeInterface; use Drupal\simpletest\TestBase; use Drupal\Tests\UnitTestCase; +use Symfony\Component\HttpFoundation\Request; /** * @coversDefaultClass \Drupal\comment\CommentLinkBuilder @@ -21,6 +22,55 @@ class CommentLinkBuilderTest extends UnitTestCase { /** + * Entity manager mock. + * + * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityManager; + + /** + * Entity storage mock which knows entity_view_display load method/argument. + * + * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityStorage; + + /** + * Entity storage mock which knows comment_type load method/argument. + * + * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $commentTypeStorage; + + /** + * Entity view display mock. + * + * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityViewDisplay; + + /** + * Entity view display mock used for the 'full' display mode. + * + * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityViewDisplayFull; + + /** + * Comment type mock. + * + * @var \Drupal\comment\CommentTypeInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $commentType; + + /** + * Field storage definition mock. + * + * @var \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fieldStorageDefinition; + + /** * Comment manager mock. * * @var \Drupal\comment\CommentManagerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -64,15 +114,44 @@ class CommentLinkBuilderTest extends UnitTestCase { * Prepares mocks for the test. */ protected function setUp() { + $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface'); + $this->entityStorage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); + $this->entityViewDisplay = $this->getMock('Drupal\Core\Entity\Display\EntityViewDisplayInterface'); + $this->entityViewDisplayFull = $this->getMock('Drupal\Core\Entity\Display\EntityViewDisplayInterface'); + $this->commentTypeStorage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); + $this->commentType = $this->getMock('\Drupal\comment\CommentTypeInterface'); + $this->entityViewDisplay = $this->getMock('Drupal\Core\Entity\Display\EntityViewDisplayInterface'); + $this->fieldStorageDefinition = $this->getMock('\Drupal\Core\Field\FieldStorageDefinitionInterface'); $this->commentManager = $this->getMock('\Drupal\comment\CommentManagerInterface'); $this->stringTranslation = $this->getStringTranslationStub(); $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountProxyInterface'); - $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->commentManager, $this->moduleHandler, $this->stringTranslation); + $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->entityManager, $this->commentManager, $this->moduleHandler, $this->stringTranslation); + $this->fieldStorageDefinition->expects($this->any()) + ->method('getSetting') + ->with('comment_type') + ->willReturn('comment'); + $this->entityManager->expects($this->any()) + ->method('getStorage') + ->will($this->returnValueMap(array( + array('entity_view_display', $this->entityStorage), + array('comment_type', $this->commentTypeStorage), + ))); + $this->entityManager->expects($this->any()) + ->method('getFieldStorageDefinitions') + ->with('node') + ->willReturn(array( + // The field name from getFields() + 'comment' => $this->fieldStorageDefinition + )); + $this->commentTypeStorage->expects($this->any()) + ->method('load') + ->willReturn($this->commentType); $this->commentManager->expects($this->any()) ->method('getFields') ->with('node') ->willReturn(array( + // Key is a field name; we're choosing comment (and don't need a value). 'comment' => array(), )); $this->commentManager->expects($this->any()) @@ -86,10 +165,15 @@ protected function setUp() { /** * Test the buildCommentedEntityLinks method. * - * @param \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject $node - * Mock node. - * @param array $context - * Context for the links. + * @param string $view_mode + * The view mode for displaying the node. + * @param bool $node_has_field + * TRUE if the node has the 'comment' field. + * @param int $comment_item_status + * Status value of the 'comment' field in this particular node. + * One of CommentItemInterface::OPEN|HIDDEN|CLOSED + * @param int $comment_count + * Number of comments against the field. * @param bool $has_access_comments * TRUE if the user has 'access comments' permission. * @param bool $history_exists @@ -98,6 +182,13 @@ protected function setUp() { * TRUE if the use has 'post comments' permission. * @param bool $is_anonymous * TRUE if the user is anonymous. + * @param int|null $form_location + * Form location setting for the current view mode. + * One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE or NULL + * @param int|null $form_location_full + * Form location setting for full view mode (will be equal to $form_location + * if $view_mode is 'full'). + * One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE or NULL * @param array $expected * Array of expected links keyed by link ID. Can be either string (link * title) or array of link properties. @@ -106,7 +197,9 @@ protected function setUp() { * * @covers ::buildCommentedEntityLinks() */ - public function testCommentLinkBuilder(NodeInterface $node, $context, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $expected) { + public function testCommentLinkBuilder($view_mode, $node_has_field, $comment_item_status, $comment_count, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $form_location, $form_location_full, $expected) { + $node = $this->getMockNode($node_has_field, $comment_item_status, $comment_count); + $this->moduleHandler->expects($this->any()) ->method('moduleExists') ->with('history') @@ -123,7 +216,72 @@ public function testCommentLinkBuilder(NodeInterface $node, $context, $has_acces $this->currentUser->expects($this->any()) ->method('isAnonymous') ->willReturn($is_anonymous); + + if ($view_mode == 'full') { + $this->entityViewDisplay->expects($this->any()) + ->method('getComponent') + ->willReturn(isset($form_location) + ? array( + 'settings' => array( + 'form_location' => $form_location, + // This only influences the page argument to the new-comments + // url, which we're not testing, so return static value. + 'per_page' => 50 + ) + ) + : NULL); + $this->entityStorage->expects($this->any()) + ->method('load') + ->with($node->getEntityTypeId() . '.' . $node->bundle() . '.full') + ->willReturn($this->entityViewDisplay); + } + else { + // From the field formatter settings for the current display, the class + // should be using 'form_location'. + $this->entityViewDisplay->expects($this->any()) + ->method('getComponent') + ->willReturn(isset($form_location) + ? array( + 'settings' => array( + 'form_location' => $form_location, + 'per_page' => 50 + ) + ) + : NULL); + // From the field formatter settings for the full display, the class + // should be using 'form_location'. + $this->entityViewDisplayFull->expects($this->any()) + ->method('getComponent') + ->willReturn(isset($form_location_full) + ? array( + 'settings' => array( + 'form_location' => $form_location_full, + 'per_page' => 50 + ) + ) + : NULL); + $this->entityStorage->expects($this->any()) + ->method('load') + ->willReturnMap(array( + array( + $node->getEntityTypeId() . '.' . $node->bundle() . '.full', + $this->entityViewDisplayFull + ), + array( + $node->getEntityTypeId() . '.' . $node->bundle() . '.' . $view_mode, + $this->entityViewDisplay + ), + )); + } + // This setting influences the page argument to the 'new comments' url, + // which we're not testing, so return static value. + $this->commentType->expects($this->any()) + ->method('getThreadingMode') + ->willReturn(CommentTypeInterface::THREADING_MODE_THREADED); + + $context = array('view_mode' => $view_mode); $links = $this->commentLinkBuilder->buildCommentedEntityLinks($node, $context); + if (!empty($expected)) { if (!empty($links)) { foreach ($expected as $link => $detail) { @@ -146,14 +304,12 @@ public function testCommentLinkBuilder(NodeInterface $node, $context, $has_acces else { $this->assertSame($links, $expected); } - if ($context['view_mode'] == 'rss' && $node->get('comment')->status) { + if ($view_mode == 'rss' && $node->get('comment')->status) { $found = FALSE; - if ($node->get('comment')->status) { - foreach ($node->rss_elements as $element) { - if ($element['key'] == 'comments') { - $found = TRUE; - break; - } + foreach ($node->rss_elements as $element) { + if ($element['key'] == 'comments') { + $found = TRUE; + break; } } $this->assertTrue($found); @@ -167,23 +323,31 @@ public function getLinkCombinations() { $cases = array(); // No links should be created if the entity doesn't have the field. $cases[] = array( - $this->getMockNode(FALSE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1), - array('view_mode' => 'teaser'), + 'teaser', + FALSE, + CommentItemInterface::OPEN, + 1, TRUE, TRUE, TRUE, TRUE, + CommentItemInterface::FORM_BELOW, + CommentItemInterface::FORM_BELOW, array(), ); foreach (array('search_result', 'search_index', 'print') as $view_mode) { // Nothing should be output in these view modes. $cases[] = array( - $this->getMockNode(TRUE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1), - array('view_mode' => $view_mode), + $view_mode, + TRUE, + CommentItemInterface::OPEN, + 1, TRUE, TRUE, TRUE, TRUE, + CommentItemInterface::FORM_BELOW, + CommentItemInterface::FORM_BELOW, array(), ); } @@ -194,7 +358,22 @@ public function getLinkCombinations() { 'has_access_comments' => array(0, 1), 'history_exists' => array(FALSE, TRUE), 'has_post_comments' => array(0, 1), - 'form_location' => array(CommentItemInterface::FORM_BELOW, CommentItemInterface::FORM_SEPARATE_PAGE), + // form_location is a field formatter setting which is checked on the + // current view mode (see below); NULL is used for "the field is hidden" + // (so no formatter settings) in this mode. + 'form_location' => array( + CommentItemInterface::FORM_BELOW, + CommentItemInterface::FORM_SEPARATE_PAGE, + NULL, + ), + // form_location_full is a field formatter setting which is checked on the + // full view mode; NULL is used for "the field is hidden" (so no formatter + // settings) in full mode. + 'form_location_full' => array( + CommentItemInterface::FORM_BELOW, + CommentItemInterface::FORM_SEPARATE_PAGE, + NULL, + ), 'comments' => array( CommentItemInterface::OPEN, CommentItemInterface::CLOSED, @@ -206,18 +385,34 @@ public function getLinkCombinations() { ); $permutations = TestBase::generatePermutations($combinations); foreach ($permutations as $combination) { + if ($combination['view_mode'] == 'full' + && $combination['form_location'] !== $combination['form_location_full']) { + // Comment field in full mode is hidden AND visible at the same time; + // impossible. + continue; + } + $case = array( - $this->getMockNode(TRUE, $combination['comments'], $combination['form_location'], $combination['comment_count']), - array('view_mode' => $combination['view_mode']), + $combination['view_mode'], + TRUE, + $combination['comments'], + $combination['comment_count'], $combination['has_access_comments'], $combination['history_exists'], $combination['has_post_comments'], $combination['is_anonymous'], + $combination['form_location'], + $combination['form_location_full'], ); $expected = array(); - // When comments are enabled in teaser mode, and comments exist, and the - // user has access - we can output the comment count. - if ($combination['comments'] && $combination['view_mode'] == 'teaser' && $combination['comment_count'] && $combination['has_access_comments']) { + // When comments exist, you're viewing teaser mode and comments are + // visible in 'full' view mode and the user has access, we can output the + // comment count with link. + if ($combination['view_mode'] == 'teaser' + && $combination['form_location_full'] !== NULL + && $combination['comments'] != CommentItemInterface::HIDDEN + && $combination['comment_count'] + && $combination['has_access_comments']) { $expected['comment-comments'] = '1 comment'; // And if history module exists, we can show a 'new comments' link. if ($combination['history_exists']) { @@ -230,13 +425,30 @@ public function getLinkCombinations() { if ($combination['comments'] == CommentItemInterface::OPEN) { // And the user has post-comments permission. if ($combination['has_post_comments']) { - // If the view mode is teaser, or the user can access comments and - // comments exist or the form is on a separate page. - if ($combination['view_mode'] == 'teaser' || ($combination['has_access_comments'] && $combination['comment_count']) || $combination['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE) { + // Show "add comment" link, unless the form is directly below it + // (that is, if there are no comments displayed in between). + if ($combination['form_location'] !== CommentItemInterface::FORM_BELOW + || ($combination['has_access_comments'] + && $combination['comment_count'])) { // There should be a add comment link. $expected['comment-add'] = array('title' => 'Add new comment'); - if ($combination['form_location'] == CommentItemInterface::FORM_BELOW) { - // On the same page. + if ($combination['form_location'] === CommentItemInterface::FORM_BELOW) { + // On the same page, which is not the full node page. + // @todo see CommentLinkBuilder. + if (\Drupal::hasRequest()) { + $request = \Drupal::request(); + } + else { + $request = Request::createFromGlobals(); + } + $links['comment-add']['url'] = $request->getPathInfo() == '/' + ? Url::fromRoute('') : Url::fromRoute(''); + } + elseif ($combination['form_location'] === CommentItemInterface::FORM_BELOW + || ($combination['form_location'] === NULL + && $combination['form_location_full'] === CommentItemInterface::FORM_BELOW)) { + // On the node page. (Linked from the same page or from another + // view mode.) $expected['comment-add']['url'] = Url::fromRoute('node.view'); } else { @@ -246,7 +458,7 @@ public function getLinkCombinations() { } } elseif ($combination['is_anonymous']) { - // Anonymous users get the forbidden message if the can't post + // Anonymous users get the forbidden message if they can't post // comments. $expected['comment-forbidden'] = "Can't let you do that Dave."; } @@ -266,17 +478,15 @@ public function getLinkCombinations() { * TRUE if the node has the 'comment' field. * @param int $comment_status * One of CommentItemInterface::OPEN|HIDDEN|CLOSED - * @param int $form_location - * One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE * @param int $comment_count * Number of comments against the field. * * @return \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject * Mock node for testing. */ - protected function getMockNode($has_field, $comment_status, $form_location, $comment_count) { + protected function getMockNode($has_field, $comment_status, $comment_count) { $node = $this->getMock('\Drupal\node\NodeInterface'); - $node->expects($this->once()) + $node->expects($this->any()) ->method('hasField') ->willReturn($has_field); @@ -294,10 +504,6 @@ protected function getMockNode($has_field, $comment_status, $form_location, $com ->willReturn($field_item); $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface'); - $field_definition->expects($this->any()) - ->method('getSetting') - ->with('form_location') - ->willReturn($form_location); $node->expects($this->any()) ->method('getFieldDefinition') ->with('comment') @@ -312,6 +518,10 @@ protected function getMockNode($has_field, $comment_status, $form_location, $com ->willReturn('node'); $node->expects($this->any()) + ->method('bundle') + ->willReturn('article'); + + $node->expects($this->any()) ->method('id') ->willReturn(1); diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 6605c1d..3ab6db8 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -5,10 +5,11 @@ * Install, update, and uninstall functions for the Forum module. */ +use Drupal\comment\CommentTypeInterface; +use Drupal\comment\Entity\CommentType; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; -use Drupal\comment\CommentManagerInterface; /** * Implements hook_install(). @@ -91,9 +92,9 @@ function forum_install() { Drupal::service('comment.manager')->addDefaultField('node', 'forum', 'comment_forum', CommentItemInterface::OPEN, 'comment_forum'); // Add here because we don't have param in addDefaultField function. - $field = FieldConfig::loadByName('node', 'forum', 'comment_forum'); - $field->settings['default_mode'] = CommentManagerInterface::COMMENT_MODE_FLAT; - $field->save(); + $comment_type = CommentType::load('comment_forum'); + $comment_type->setThreadingMode(CommentTypeInterface::THREADING_MODE_FLAT); + $comment_type->save(); // Hide label for comment field. entity_get_display('node', 'forum', 'default') diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 6b99955..3354fbc 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -6,6 +6,7 @@ */ use Drupal\comment\CommentInterface; +use Drupal\comment\Entity\CommentType; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\Component\Utility\Xss; use Drupal\Core\Entity\EntityInterface; @@ -465,6 +466,10 @@ function forum_theme_suggestions_forums(array $variables) { */ function template_preprocess_forums(&$variables) { $variables['tid'] = $variables['term']->id(); + $display = entity_get_display('node', 'forum', 'full'); + $settings = $display->getComponent('comment_forum')['settings']; + $field_definition = \Drupal::entityManager()->getFieldStorageDefinitions('node')['comment_forum']; + $comment_type = CommentType::load($field_definition->getSetting('comment_type')); if ($variables['forums_defined'] = count($variables['forums']) || count($variables['parents'])) { if (!empty($variables['forums'])) { $variables['forums'] = array( @@ -531,7 +536,7 @@ function template_preprocess_forums(&$variables) { if ($topic->new_replies) { $page_number = \Drupal::entityManager()->getStorage('comment') - ->getNewCommentPageNumber($topic->comment_count, $topic->new_replies, $topic, 'comment_forum'); + ->getNewCommentPageNumber($topic->comment_count, $topic->new_replies, $topic, $comment_type->getThreadingMode(), $settings['per_page'], 'comment_forum'); $query = $page_number ? array('page' => $page_number) : NULL; $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new post in topic %title', '@count new posts in topic %title', array('%title' => $variables['topics'][$id]->label())); $variables['topics'][$id]->new_url = \Drupal::url('entity.node.canonical', ['node' => $topic->id()], ['query' => $query, 'fragment' => 'new']); diff --git a/core/modules/rdf/src/Tests/CommentAttributesTest.php b/core/modules/rdf/src/Tests/CommentAttributesTest.php index 69a5966..882bc50 100644 --- a/core/modules/rdf/src/Tests/CommentAttributesTest.php +++ b/core/modules/rdf/src/Tests/CommentAttributesTest.php @@ -8,7 +8,7 @@ namespace Drupal\rdf\Tests; use Drupal\comment\CommentInterface; -use Drupal\comment\CommentManagerInterface; +use Drupal\comment\CommentTypeInterface; use Drupal\comment\Tests\CommentTestBase; /** @@ -39,7 +39,7 @@ protected function setUp() { $this->setCommentPreview(DRUPAL_OPTIONAL); $this->setCommentForm(TRUE); $this->setCommentSubject(TRUE); - $this->setCommentSettings('comment_default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.'); + $this->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); // Prepares commonly used URIs. $this->base_uri = \Drupal::url('', [], ['absolute' => TRUE]); diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install index c2b5582..87c4845 100644 --- a/core/profiles/standard/standard.install +++ b/core/profiles/standard/standard.install @@ -4,6 +4,8 @@ * Install, update and uninstall functions for the standard installation profile. */ +use Drupal\comment\CommentTypeInterface; +use Drupal\comment\Entity\CommentType; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; /** @@ -25,6 +27,15 @@ function standard_install() { // Add comment field to article node type. \Drupal::service('comment.manager')->addDefaultField('node', 'article', 'comment', CommentItemInterface::OPEN); + $comment_type = CommentType::load('comment'); + $comment_type->setThreadingMode(CommentTypeInterface::THREADING_MODE_THREADED); + $comment_type->save(); + // Set some display options for comments in search results. + foreach (array('search_result', 'search_index') as $view_mode) { + $display = entity_get_display('node', 'article', $view_mode); + $display->removeComponent('comment'); + $display->save(); + } // Hide the comment field in the rss view mode. entity_get_display('node', 'article', 'rss')