Commit b6ba35f0 authored by alexpott's avatar alexpott

Issue #2318251 by larowlan: Fixed Make comment links functionality testable...

Issue #2318251 by larowlan: Fixed Make comment links functionality testable and convert CommentLinkTest to PHPUnit.
parent 5a8dddce
......@@ -47,16 +47,6 @@
*/
const COMMENT_ANONYMOUS_MUST_CONTACT = 2;
/**
* Comment form should be displayed on a separate page.
*/
const COMMENT_FORM_SEPARATE_PAGE = 0;
/**
* Comment form should be shown below post or list of comments.
*/
const COMMENT_FORM_BELOW = 1;
/**
* The time cutoff for comments marked as read for entity types other node.
*
......@@ -257,151 +247,8 @@ function comment_node_links_alter(array &$node_links, NodeInterface $node, array
// @todo Make this configurable from the formatter see
// http://drupal.org/node/1901110
$view_mode = $context['view_mode'];
if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print') {
// Do not add any links if the node displayed for:
// - search indexing.
// - constructing a search result excerpt.
// - print.
return;
}
$fields = \Drupal::service('comment.manager')->getFields('node');
$current_user = \Drupal::currentUser();
foreach ($fields as $field_name => $detail) {
// Skip fields that the node does not have.
if (!$node->hasField($field_name)) {
continue;
}
$links = array();
$commenting_status = $node->get($field_name)->status;
if ($commenting_status) {
$field_definition = $node->getFieldDefinition($field_name);
// Node have commenting open or close.
if ($view_mode == 'rss') {
// Add a comments RSS element which is a URL to the comments of this node.
$options = array(
'fragment' => 'comments',
'absolute' => TRUE,
);
$node->rss_elements[] = array(
'key' => 'comments',
'value' => $node->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 node
// is open to new comments, and there currently are none.
if ($current_user->hasPermission('access comments')) {
if (!empty($node->get($field_name)->comment_count)) {
$links['comment-comments'] = array(
'title' => format_plural($node->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,
) + $node->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' => $node->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 ($current_user->hasPermission('post comments')) {
$links['comment-add'] = array(
'title' => t('Add new comment'),
'language' => $node->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' => $node->getEntityTypeId(),
'entity_id' => $node->id(),
'field_name' => $field_name,
);
}
else {
$links['comment-add'] += $node->urlInfo()->toArray();
}
}
elseif (\Drupal::currentUser()->isAnonymous()) {
$links['comment-forbidden'] = array(
'title' => \Drupal::service('comment.manager')->forbiddenMessage($node, $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 ($current_user->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 == COMMENT_FORM_SEPARATE_PAGE || (!empty($node->get($field_name)->comment_count) && $current_user->hasPermission('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' => $node->getEntityTypeId(),
'entity_id' => $node->id(),
'field_name' => $field_name,
);
}
else {
$links['comment-add'] += $node->urlInfo()->toArray();
}
}
}
elseif (\Drupal::currentUser()->isAnonymous()) {
$links['comment-forbidden'] = array(
'title' => \Drupal::service('comment.manager')->forbiddenMessage($node, $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' => $node->id()),
);
$node_links['comment__' . $field_name]['#post_render_cache']['Drupal\comment\CommentViewBuilder::attachNewCommentsLinkMetadata'] = array(
array('entity_type' => $node->getEntityTypeId(), 'entity_id' => $node->id(), 'field_name' => $field_name),
);
}
}
}
$links = \Drupal::service('comment.link_builder')->buildCommentedEntityLinks($node, $context);
$node_links += $links;
}
/**
......
......@@ -18,3 +18,7 @@ services:
comment.post_render_cache:
class: Drupal\comment\CommentPostRenderCache
arguments: ['@entity.manager', '@entity.form_builder']
comment.link_builder:
class: Drupal\comment\CommentLinkBuilder
arguments: ['@current_user', '@comment.manager', '@module_handler', '@string_translation']
<?php
/**
* @file
* Contains \Drupal\comment\CommentLinkBuilder.
*/
namespace Drupal\comment;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Defines a class for building markup for comment links on a commented entity.
*
* Comment links include 'login to post new comment', 'add new comment' etc.
*/
class CommentLinkBuilder implements CommentLinkBuilderInterface {
use StringTranslationTrait;
/**
* Current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Comment manager service.
*
* @var \Drupal\comment\CommentManagerInterface
*/
protected $commentManager;
/**
* Module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new CommentLinkBuilder object.
*
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user.
* @param \Drupal\comment\CommentManagerInterface $comment_manager
* Comment manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* Module handler service.
* @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) {
$this->currentUser = $current_user;
$this->commentManager = $comment_manager;
$this->moduleHandler = $module_handler;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public function buildCommentedEntityLinks(ContentEntityInterface $entity, array &$context) {
$entity_links = array();
$view_mode = $context['view_mode'];
if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print') {
// Do not add any links if the entity is displayed for:
// - search indexing.
// - constructing a search result excerpt.
// - print.
return array();
}
$fields = $this->commentManager->getFields($entity->getEntityTypeId());
foreach ($fields as $field_name => $detail) {
// Skip fields that the entity does not have.
if (!$entity->hasField($field_name)) {
continue;
}
$links = array();
$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.
$options = array(
'fragment' => 'comments',
'absolute' => TRUE,
);
$entity->rss_elements[] = array(
'key' => 'comments',
'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',
) + $entity->urlInfo()->toArray();
if ($this->moduleHandler->moduleExists('history')) {
$links['comment-new-comments'] = array(
'title' => '',
'href' => '',
'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.
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']['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 ($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'))) {
$links['comment-add'] = array(
'title' => $this->t('Add new comment'),
'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) {
$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 ($this->currentUser->isAnonymous()) {
$links['comment-forbidden'] = array(
'title' => $this->commentManager->forbiddenMessage($entity, $field_name),
'html' => TRUE,
);
}
}
}
}
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';
// 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,
),
);
}
}
}
return $entity_links;
}
}
<?php
/**
* @file
* Contains \Drupal\comment\CommentLinkBuilderInterface.
*/
namespace Drupal\comment;
use Drupal\Core\Entity\ContentEntityInterface;
/**
* Defines an interface for building comment links on a commented entity.
*
* Comment links include 'login to post new comment', 'add new comment' etc.
*/
interface CommentLinkBuilderInterface {
/**
* Builds links for the given entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* Entity for which the links are being built.
* @param array $context
* Array of context passed from the entity view builder.
*
* @return array
* Array of entity links.
*/
public function buildCommentedEntityLinks(ContentEntityInterface $entity, array &$context);
}
......@@ -273,7 +273,7 @@ public function forbiddenMessage(EntityInterface $entity, $field_name) {
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) {
if ($entity->get($field_name)->getFieldDefinition()->getSetting('form_location') == CommentItemInterface::FORM_SEPARATE_PAGE) {
$destination = array('destination' => 'comment/reply/' . $entity->getEntityTypeId() . '/' . $entity->id() . '/' . $field_name . '#comment-form');
}
else {
......
......@@ -182,7 +182,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'] == COMMENT_FORM_BELOW && $this->viewMode != 'print') {
if ($status == CommentItemInterface::OPEN && $comment_settings['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
......
......@@ -44,7 +44,7 @@ public static function defaultInstanceSettings() {
return array(
'default_mode' => CommentManagerInterface::COMMENT_MODE_THREADED,
'per_page' => 50,
'form_location' => COMMENT_FORM_BELOW,
'form_location' => CommentItemInterface::FORM_BELOW,
'anonymous' => COMMENT_ANONYMOUS_MAYNOT_CONTACT,
'preview' => DRUPAL_OPTIONAL,
) + parent::defaultInstanceSettings();
......
......@@ -27,4 +27,14 @@ interface CommentItemInterface {
*/
const OPEN = 2;
/**
* Comment form should be displayed on a separate page.
*/
const FORM_SEPARATE_PAGE = 0;
/**
* Comment form should be shown below post or list of comments.
*/
const FORM_BELOW = 1;
}
......@@ -12,12 +12,26 @@
use Drupal\comment\CommentInterface;
/**
* Tests comment links based on environment configurations.
* Basic comment links tests to ensure markup present.
*
* @group comment
*/
class CommentLinksTest extends CommentTestBase {
/**
* Comment being tested.
*
* @var \Drupal\comment\CommentInterface
*/
protected $comment;
/**
* Seen comments, array of comment IDs.
*
* @var array
*/
protected $seen = array();
/**
* Use the main node listing to test rendering on teasers.
*
......@@ -39,9 +53,9 @@ class CommentLinksTest extends CommentTestBase {
* possible conditions and tests the expected appearance of comment links in
* each environment.
*/
function testCommentLinks() {
public function testCommentLinks() {
// Bartik theme alters comment links, so use a different theme.
theme_enable(array('stark'));
\Drupal::service('theme_handler')->enable(array('stark'));
\Drupal::config('system.theme')
->set('default', 'stark')
->save();
......@@ -51,265 +65,52 @@ function testCommentLinks() {
$roles = $this->web_user->getRoles();
entity_delete_multiple('user_role', array(reset($roles)));
// Matrix of possible environmental conditions and configuration settings.
// See setEnvironment() for details.
$conditions = array(
'authenticated' => array(FALSE, TRUE),
'comment count' => array(FALSE, TRUE),
'access comments' => array(0, 1),
'post comments' => array(0, 1),
'form' => array(COMMENT_FORM_BELOW, COMMENT_FORM_SEPARATE_PAGE),
// USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL is irrelevant for this
// test; there is only a difference between open and closed registration.
'user_register' => array(USER_REGISTER_VISITORS, USER_REGISTER_ADMINISTRATORS_ONLY),
// @todo Complete test coverage for:
//'comments' => array(CommentItemInterface::OPEN, CommentItemInterface::CLOSED, CommentInterface::_HIDDEN),
//// COMMENT_ANONYMOUS_MUST_CONTACT is irrelevant for this test.
//'contact ' => array(COMMENT_ANONYMOUS_MAY_CONTACT, COMMENT_ANONYMOUS_MAYNOT_CONTACT),
);
$environments = $this->generatePermutations($conditions);
foreach ($environments as $info) {
$this->assertCommentLinks($info);
}
}
/**
* Re-configures the environment, module settings, and user permissions.
*
* @param $info
* An associative array describing the environment to setup:
* - Environment conditions:
* - authenticated: Boolean whether to test with $this->web_user or
* anonymous.
* - comment count: Boolean whether to test with a new/unread comment on
* $this->node or no comments.
* - Configuration settings:
* - form: COMMENT_FORM_BELOW or COMMENT_FORM_SEPARATE_PAGE.
* - user_register: USER_REGISTER_ADMINISTRATORS_ONLY or
* USER_REGISTER_VISITORS.
* - contact: COMMENT_ANONYMOUS_MAY_CONTACT or
* COMMENT_ANONYMOUS_MAYNOT_CONTACT.
* - comments: CommentItemInterface::OPEN, CommentItemInterface::CLOSED or
* CommentItemInterface::HIDDEN.
* - User permissions:
* These are granted or revoked for the user, according to the
* 'authenticated' flag above. Pass 0 or 1 as parameter values. See
* user_role_change_permissions().
* - access comments
* - post comments
* - skip comment approval
* - edit own comments
*/
function setEnvironment(array $info) {
static $current;
// Apply defaults to initial environment.
if (!isset($current)) {
$current = array(
'authenticated' => FALSE,
'comment count' => FALSE,
'form' => COMMENT_FORM_BELOW,
'user_register' => USER_REGISTER_VISITORS,
'contact' => COMMENT_ANONYMOUS_MAY_CONTACT,
'comments' => CommentItemInterface::OPEN,
'access comments' => 0,
'post comments' => 0,
// Enabled by default, because it's irrelevant for this test.
'skip comment approval' => 1,
'edit own comments' => 0,
);
}
// Complete new environment with current environment.
$info = array_merge($current, $info);
// Change environment conditions.
if ($current['authenticated'] != $info['authenticated']) {
if ($this->loggedInUser) {
$this->drupalLogout();
}
else {
$this->drupalLogin($this->web_user);
}
}
if ($current['comment count'] != $info['comment count']) {
if ($info['comment count']) {
// Create a comment via CRUD API functionality, since
// $this->postComment() relies on actual user permissions.
$comment = entity_create('comment', array(
'cid' => NULL,
'entity_id' => $this->node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'pid' => 0,
'uid' => 0,
'status' => CommentInterface::PUBLISHED,
'subject' => $this->randomMachineName(),
'hostname' => '127.0.0.1',
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
'comment_body' => array(LanguageInterface::LANGCODE_NOT_SPECIFIED => array($this->randomMachineName())),