Issue #2318251 by larowlan: Fixed Make comment links functionality testable and convert CommentLinkTest to PHPUnit.

8.0.x
Alex Pott 2014-08-19 10:52:51 +01:00
parent 5a8dddce9e
commit b6ba35f006
11 changed files with 660 additions and 409 deletions

View File

@ -47,16 +47,6 @@ const COMMENT_ANONYMOUS_MAY_CONTACT = 1;
*/
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;
}
/**

View File

@ -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']

View File

@ -0,0 +1,223 @@
<?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;
}
}

View File

@ -0,0 +1,32 @@
<?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);
}

View File

@ -273,7 +273,7 @@ class CommentManager implements CommentManagerInterface {
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 {

View File

@ -182,7 +182,7 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
// 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

View File

@ -44,7 +44,7 @@ class CommentItem extends FieldItemBase implements CommentItemInterface {
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();

View File

@ -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;
}

View File

@ -12,12 +12,26 @@ use Drupal\Core\Language\LanguageInterface;
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 @@ class CommentLinksTest extends CommentTestBase {
$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())),
));
$comment->save();
$this->comment = $comment;
}
else {
$cids = db_query("SELECT cid FROM {comment}")->fetchCol();
entity_delete_multiple('comment', $cids);
unset($this->comment);
}
}
// 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())),
));
$comment->save();
$this->comment = $comment;
// Change comment settings.
$this->setCommentSettings('form_location', $info['form'], 'Set comment form location');
$this->setCommentAnonymous($info['contact']);
if ($this->node->comment->status != $info['comments']) {
$this->node->comment = $info['comments'];
$this->node->save();
}
// Change user settings.
\Drupal::config('user.settings')->set('register', $info['user_register'])->save();
$this->setCommentSettings('form_location', CommentItemInterface::FORM_BELOW, 'Set comment form location');
$this->setCommentAnonymous(TRUE);
$this->node->comment = CommentItemInterface::OPEN;
$this->node->save();
// Change user permissions.
$rid = ($this->loggedInUser ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID);
$perms = array_intersect_key($info, array('access comments' => 1, 'post comments' => 1, 'skip comment approval' => 1, 'edit own comments' => 1));
user_role_change_permissions($rid, $perms);
// Output verbose debugging information.
// @see \Drupal\simpletest\TestBase::error()
$t_form = array(
COMMENT_FORM_BELOW => 'below',
COMMENT_FORM_SEPARATE_PAGE => 'separate page',
$perms = array(
'access comments' => 1,
'post comments' => 1,
'skip comment approval' => 1,
'edit own comments' => 1,
);
$t_contact = array(
COMMENT_ANONYMOUS_MAY_CONTACT => 'optional',
COMMENT_ANONYMOUS_MAYNOT_CONTACT => 'disabled',
COMMENT_ANONYMOUS_MUST_CONTACT => 'required',
);
$t_comments = array(
CommentItemInterface::OPEN => 'open',
CommentItemInterface::CLOSED => 'closed',
CommentItemInterface::HIDDEN => 'hidden',
);
$verbose = $info;
$verbose['form'] = $t_form[$info['form']];
$verbose['contact'] = $t_contact[$info['contact']];
$verbose['comments'] = $t_comments[$info['comments']];
$message = t('Changed environment:<pre>@verbose</pre>', array(
'@verbose' => var_export($verbose, TRUE),
));
$this->assert('debug', $message, 'Debug');
// Update current environment.
$current = $info;
return $info;
}
/**
* Asserts that comment links appear according to the passed environment.
*
* @param $info
* An associative array describing the environment to pass to
* setEnvironment().
*/
function assertCommentLinks(array $info) {
$info = $this->setEnvironment($info);
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, $perms);
$nid = $this->node->id();
// Assert basic link is output, actual functionality is unit-tested in
// \Drupal\comment\Tests\CommentLinkBuilderTest.
foreach (array('node', "node/$nid") as $path) {
$this->drupalGet($path);
// User is allowed to view comments.
if ($info['access comments']) {
if ($path == '') {
// In teaser view, a link containing the comment count is always
// expected.
if ($info['comment count']) {
$this->assertLink(t('1 comment'));
// For logged in users, a link containing the amount of new/unread
// comments is expected.
// See important note about
// \Drupal::service('comment.manager')->getCountNewComments() below.
if ($this->loggedInUser && isset($this->comment) && !isset($this->comment->seen)) {
$this->assertLink(t('1 new comment'));
$this->comment->seen = TRUE;
}
}
}
}
else {
$this->assertNoLink(t('1 comment'));
$this->assertNoLink(t('1 new comment'));
}
// \Drupal::service('comment.manager')->getCountNewComments() is based on
// node views, so comments are marked as read when a node is viewed,
// regardless of whether we have access to comments.
if ($path == "node/$nid" && $this->loggedInUser && isset($this->comment)) {
$this->comment->seen = TRUE;
}
// User is not allowed to post comments.
if (!$info['post comments']) {
$this->assertNoLink('Add new comment');
// Anonymous users should see a note to log in or register in case
// authenticated users are allowed to post comments.
// @see \Drupal\comment\CommentManagerInterface::forbiddenMessage()
if (!$this->loggedInUser) {
if ($this->web_user->hasPermission('post comments')) {
// The note depends on whether users are actually able to register.
if ($info['user_register'] != USER_REGISTER_ADMINISTRATORS_ONLY) {
$this->assertText('Log in or register to post comments');
}
else {
$this->assertText('Log in to post comments');
}
}
else {
$this->assertNoText('Log in or register to post comments');
$this->assertNoText('Log in to post comments');
}
}
}
// User is allowed to post comments.
else {
$this->assertNoText('Log in or register to post comments');
// "Add new comment" is always expected, except when there are no
// comments or if the user cannot see them.
if ($path == "node/$nid" && $info['form'] == COMMENT_FORM_BELOW && (!$info['comment count'] || !$info['access comments'])) {
$this->assertNoLink('Add new comment');
}
else {
$this->assertLink('Add new comment');
// Verify that the "Add new comment" link points to the correct URL
// based on the comment form location configuration.
if ($info['form'] == COMMENT_FORM_SEPARATE_PAGE) {
$this->assertLinkByHref("comment/reply/node/$nid/comment#comment-form", 0, 'Comment form link destination is on a separate page.');
$this->assertNoLinkByHref("node/$nid#comment-form");
}
else {
$this->assertLinkByHref("node/$nid#comment-form", 0, 'Comment form link destination is on node.');
$this->assertNoLinkByHref("comment/reply/node/$nid/comment#comment-form");
}
}
// Also verify that the comment form appears according to the configured
// location.
if ($path == "node/$nid") {
$elements = $this->xpath('//form[@id=:id]', array(':id' => 'comment-form'));
if ($info['form'] == COMMENT_FORM_BELOW) {
$this->assertTrue(count($elements), 'Comment form found below.');
}
else {
$this->assertFalse(count($elements), 'Comment form not found below.');
}
}
// In teaser view, a link containing the comment count is always
// expected.
if ($path == 'node') {
$this->assertLink(t('1 comment'));
}
$this->assertLink('Add new comment');
}
}

View File

@ -10,6 +10,7 @@ namespace Drupal\comment\Tests;
use Drupal\comment\Entity\CommentType;
use Drupal\comment\Entity\Comment;
use Drupal\comment\CommentInterface;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\field\Entity\FieldInstanceConfig;
use Drupal\simpletest\WebTestBase;
@ -265,7 +266,7 @@ abstract class CommentTestBase extends WebTestBase {
* Defaults to 'comment'.
*/
public function setCommentForm($enabled, $field_name = 'comment') {
$this->setCommentSettings('form_location', ($enabled ? COMMENT_FORM_BELOW : COMMENT_FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.', $field_name);
$this->setCommentSettings('form_location', ($enabled ? CommentItemInterface::FORM_BELOW : CommentItemInterface::FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.', $field_name);
}
/**

View File

@ -0,0 +1,333 @@
<?php
/**
* @file
* Contains \Drupal\comment\Tests\CommentLinkBuilderTest.
*/
namespace Drupal\comment\Tests;
use Drupal\comment\CommentLinkBuilder;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\node\NodeInterface;
use Drupal\simpletest\TestBase;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\comment\CommentLinkBuilder
* @group comment
*/
class CommentLinkBuilderTest extends UnitTestCase {
/**
* Comment manager mock.
*
* @var \Drupal\comment\CommentManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $commentManager;
/**
* String translation mock.
*
* @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $stringTranslation;
/**
* Module handler mock.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* Current user proxy mock.
*
* @var \Drupal\Core\Session\AccountProxyInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $currentUser;
/**
* Timestamp used in test.
*
* @var int
*/
protected $timestamp;
/**
* @var \Drupal\comment\CommentLinkBuilderInterface;
*/
protected $commentLinkBuilder;
/**
* Prepares mocks for the test.
*/
public function setUp() {
$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->commentManager->expects($this->any())
->method('getFields')
->with('node')
->willReturn(array(
'comment' => array(),
));
$this->commentManager->expects($this->any())
->method('forbiddenMessage')
->willReturn("Can't let you do that Dave.");
$this->stringTranslation->expects($this->any())
->method('formatPlural')
->willReturnArgument(1);
}
/**
* Test the buildCommentedEntityLinks method.
*
* @param \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject $node
* Mock node.
* @param array $context
* Context for the links.
* @param bool $has_access_comments
* TRUE if the user has 'access comments' permission.
* @param bool $history_exists
* TRUE if the history module exists.
* @param bool $has_post_comments
* TRUE if the use has 'post comments' permission.
* @param bool $is_anonymous
* TRUE if the user is anonymous.
* @param array $expected
* Array of expected links keyed by link ID. Can be either string (link
* title) or array of link properties.
*
* @dataProvider getLinkCombinations
*
* @covers ::buildCommentedEntityLinks()
*/
public function testCommentLinkBuilder(NodeInterface $node, $context, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $expected) {
$this->moduleHandler->expects($this->any())
->method('moduleExists')
->with('history')
->willReturn($history_exists);
$this->currentUser->expects($this->any())
->method('hasPermission')
->willReturnMap(array(
array('access comments', $has_access_comments),
array('post comments', $has_post_comments),
));
$this->currentUser->expects($this->any())
->method('isAuthenticated')
->willReturn(!$is_anonymous);
$this->currentUser->expects($this->any())
->method('isAnonymous')
->willReturn($is_anonymous);
$links = $this->commentLinkBuilder->buildCommentedEntityLinks($node, $context);
if (!empty($expected)) {
if (!empty($links)) {
foreach ($expected as $link => $detail) {
if (is_array($detail)) {
// Array of link attributes.
foreach ($detail as $key => $value) {
$this->assertEquals($links['comment__comment']['#links'][$link][$key], $value);
}
}
else {
// Just the title.
$this->assertEquals($links['comment__comment']['#links'][$link]['title'], $detail);
}
}
}
else {
$this->fail('Expected links but found none.');
}
}
else {
$this->assertSame($links, $expected);
}
if ($context['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;
}
}
}
$this->assertTrue($found);
}
}
/**
* Data provider for ::testCommentLinkBuilder.
*/
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'),
TRUE,
TRUE,
TRUE,
TRUE,
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),
TRUE,
TRUE,
TRUE,
TRUE,
array(),
);
}
// All other combinations.
$combinations = array(
'is_anonymous' => array(FALSE, TRUE),
'comment_count' => array(0, 1),
'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),
'comments' => array(
CommentItemInterface::OPEN,
CommentItemInterface::CLOSED,
CommentItemInterface::HIDDEN,
),
'view_mode' => array(
'teaser', 'rss', 'full',
),
);
$permutations = TestBase::generatePermutations($combinations);
foreach ($permutations as $combination) {
$case = array(
$this->getMockNode(TRUE, $combination['comments'], $combination['form_location'], $combination['comment_count']),
array('view_mode' => $combination['view_mode']),
$combination['has_access_comments'],
$combination['history_exists'],
$combination['has_post_comments'],
$combination['is_anonymous'],
);
$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']) {
$expected['comment-comments'] = '1 comment';
// And if history module exists, we can show a 'new comments' link.
if ($combination['history_exists']) {
$expected['comment-new-comments'] = '';
}
}
// All view modes other than RSS.
if ($combination['view_mode'] != 'rss') {
// Where commenting is open.
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) {
// 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.
$expected['comment-add']['route_name'] = 'node.view';
}
else {
// On a separate page.
$expected['comment-add']['route_name'] = 'comment.reply';
}
}
}
elseif ($combination['is_anonymous']) {
// Anonymous users get the forbidden message if the can't post
// comments.
$expected['comment-forbidden'] = "Can't let you do that Dave.";
}
}
}
$case[] = $expected;
$cases[] = $case;
}
return $cases;
}
/**
* Builds a mock node based on given scenario.
*
* @param bool $has_field
* 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) {
$node = $this->getMock('\Drupal\node\NodeInterface');
$node->expects($this->once())
->method('hasField')
->willReturn($has_field);
if (empty($this->timestamp)) {
$this->timestamp = time();
}
$field_item = (object) array(
'status' => $comment_status,
'comment_count' => $comment_count,
'last_comment_timestamp' => $this->timestamp,
);
$node->expects($this->any())
->method('get')
->with('comment')
->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')
->willReturn($field_definition);
$node->expects($this->any())
->method('language')
->willReturn('und');
$node->expects($this->any())
->method('getEntityTypeId')
->willReturn('node');
$node->expects($this->any())
->method('id')
->willReturn(1);
$url = $this->getMockBuilder('\Drupal\Core\Url')
->disableOriginalConstructor()
->getMock();
$url->expects($this->any())
->method('toArray')
->willReturn(array('route_name' => 'node.view'));
$node->expects($this->any())
->method('urlInfo')
->willReturn($url);
$node->expects($this->any())
->method('url')
->willReturn(array('route_name' => 'node.view'));
return $node;
}
}