Commit 37949fe3 authored by alexpott's avatar alexpott

Issue #731724 by larowlan, andypost, dixon_, tsvenson: Convert comment...

Issue #731724 by larowlan, andypost, dixon_, tsvenson: Convert comment settings into a field to make them work with CMI and non-node entities.
parent fb091cd6
......@@ -234,8 +234,6 @@ class EntityType extends Plugin {
/**
* The prefix for the bundles of this entity type.
*
* For example, the comment bundle is prefixed with 'comment_node_'.
*
* @var string (optional)
*/
public $bundle_prefix;
......
/**
* @file
* Attaches comment behaviors to the entity form.
*/
(function ($) {
"use strict";
Drupal.behaviors.commentFieldsetSummaries = {
attach: function (context) {
var $context = $(context);
$context.find('fieldset.comment-entity-settings-form').drupalSetSummary(function (context) {
return Drupal.checkPlain($(context).find('.form-item-comment input:checked').next('label').text());
});
}
};
})(jQuery);
/**
* @file
* Attaches comment behaviors to the node form.
*/
(function ($) {
"use strict";
Drupal.behaviors.commentDetailsSummaries = {
attach: function (context) {
var $context = $(context);
$context.find('.comment-node-settings-form').drupalSetSummary(function (context) {
return Drupal.checkPlain($(context).find('.form-item-comment input:checked').next('label').text());
});
// Provide the summary for the node type form.
$context.find('.comment-node-type-settings-form').drupalSetSummary(function(context) {
var $context = $(context);
var vals = [];
// Default comment setting.
vals.push($context.find(".form-item-comment select option:selected").text());
// Threading.
var threading = $(context).find(".form-item-comment-default-mode input:checked").next('label').text();
if (threading) {
vals.push(threading);
}
// Comments per page.
var number = $context.find(".form-item-comment-default-per-page select option:selected").val();
vals.push(Drupal.t('@number comments per page', {'@number': number}));
return Drupal.checkPlain(vals.join(', '));
});
}
};
})(jQuery);
......@@ -84,41 +84,53 @@ function comment_admin_overview($form, &$form_state, $arg) {
$query = db_select('comment', 'c')
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
->extend('Drupal\Core\Database\Query\TableSortExtender');
$query->join('node_field_data', 'n', 'n.nid = c.nid');
$query->addTag('node_access');
if (\Drupal::moduleHandler()->moduleExists('node')) {
// Special case to ensure node access works.
$query->leftJoin('node_field_data', 'n', "n.nid = c.entity_id AND c.entity_type = 'node'");
$query->addTag('node_access');
}
$result = $query
->fields('c', array('cid', 'nid', 'subject', 'name', 'changed'))
->fields('c', array('cid', 'subject', 'name', 'changed', 'entity_id', 'entity_type', 'field_id'))
->condition('c.status', $status)
->limit(50)
->orderByHeader($header)
->execute();
$nids = array();
$cids = array();
$entity_ids = array();
$entities = array();
// We collect a sorted list of node_titles during the query to attach to the
// comments later.
// We collect entities grouped by entity_type so we can load them and use
// their labels.
foreach ($result as $row) {
$nids[] = $row->nid;
$entity_ids[$row->entity_type][] = $row->entity_id;
$cids[] = $row->cid;
}
// Ensure all nodes are statically cached so that we do not have to load them
// individually when getting their labels below.
node_load_multiple($nids);
$comments = comment_load_multiple($cids);
// Ensure all entities are statically cached so that we do not have to load
// them individually when getting their labels below.
foreach ($entity_ids as $entity_type => $ids) {
$entities[$entity_type] = entity_load_multiple($entity_type, $ids);
}
$comments = entity_load_multiple('comment', $cids);
// Build a table listing the appropriate comments.
$options = array();
$destination = drupal_get_destination();
foreach ($comments as $comment) {
// Use the first entity label.
$entity = $entities[$comment->entity_type->value][$comment->entity_id->value];
$entity_uri = $entity->uri();
// Remove the first node title from the node_titles array and attach to
// the comment.
$node_title = $comment->nid->entity->label();
$username = array(
'#theme' => 'username',
'#account' => comment_prepare_author($comment),
);
$body = '';
if (!empty($comment->comment_body->value)) {
$body = $comment->comment_body->value;
}
$options[$comment->id()] = array(
'title' => array('data' => array('#title' => $comment->subject->value ?: $comment->id())),
'subject' => array(
......@@ -126,15 +138,17 @@ function comment_admin_overview($form, &$form_state, $arg) {
'#type' => 'link',
'#title' => $comment->subject->value,
'#href' => 'comment/' . $comment->id(),
'#options' => array('attributes' => array('title' => truncate_utf8($comment->comment_body->value, 128)), 'fragment' => 'comment-' . $comment->id()),
'#options' => array('attributes' => array('title' => truncate_utf8($body, 128)), 'fragment' => 'comment-' . $comment->id()),
),
),
'author' => drupal_render($username),
'posted_in' => array(
'data' => array(
'#type' => 'link',
'#title' => $node_title,
'#href' => 'node/' . $comment->nid->target_id,
'#title' => $entity->label(),
'#href' => $entity_uri['path'],
'#options' => $entity_uri['options'],
'#access' => $entity->access('view'),
),
),
'changed' => format_date($comment->changed->value, 'short'),
......
......@@ -34,7 +34,9 @@ function hook_comment_presave(Drupal\comment\Comment $comment) {
*/
function hook_comment_insert(Drupal\comment\Comment $comment) {
// Reindex the node when comments are added.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}
/**
......@@ -45,7 +47,9 @@ function hook_comment_insert(Drupal\comment\Comment $comment) {
*/
function hook_comment_update(Drupal\comment\Comment $comment) {
// Reindex the node when comments are updated.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}
/**
......
......@@ -6,6 +6,5 @@ version: VERSION
core: 8.x
dependencies:
- datetime
- node
- text
configure: admin/content/comment
This diff is collapsed.
This diff is collapsed.
......@@ -46,12 +46,13 @@ comment.confirm_delete:
_entity_access: 'comment.delete'
comment.reply:
path: 'comment/reply/{node}/{pid}'
path: 'comment/reply/{entity_type}/{entity_id}/{field_name}/{pid}'
defaults:
_content: '\Drupal\comment\Controller\CommentController::getReplyForm'
_title: 'Add new comment'
pid: ~
requirements:
_entity_access: 'node.view'
_access: 'TRUE'
comment.new_comments_node_links:
path: '/comments/render_new_comments_node_links'
......@@ -59,3 +60,18 @@ comment.new_comments_node_links:
_controller: '\Drupal\comment\Controller\CommentController::renderNewCommentsNodeLinks'
requirements:
_permission: 'access content'
comment.bundle_list:
path: '/admin/structure/comments'
defaults:
_content: 'Drupal\comment\Controller\AdminController::overviewBundles'
requirements:
_permission: 'administer comments'
comment.bundle:
path: '/admin/structure/comments/manage/{field_name}'
defaults:
_content: 'Drupal\comment\Controller\AdminController::bundleInfo'
_title_callback: 'Drupal\comment\Controller\AdminController::bundleTitle'
requirements:
_access: 'FALSE'
......@@ -3,4 +3,14 @@ services:
class: Drupal\comment\CommentBreadcrumbBuilder
tags:
- { name: breadcrumb_builder, priority: 100 }
arguments: ['@string_translation']
arguments: ['@string_translation', '@entity.manager']
comment.subscriber:
class: Drupal\comment\Routing\RouteSubscriber
arguments: ['@module_handler']
tags:
- { name: event_subscriber }
comment.manager:
class: Drupal\comment\CommentManager
arguments: ['@field.info', '@entity.manager']
......@@ -15,14 +15,14 @@ function comment_token_info() {
'needs-data' => 'comment',
);
// Comment-related tokens for nodes
$node['comment-count'] = array(
// @todo Make this work per field. See http://drupal.org/node/2031903
$entity['comment-count'] = array(
'name' => t("Comment count"),
'description' => t("The number of comments posted on a node."),
'description' => t("The number of comments posted on an entity."),
);
$node['comment-count-new'] = array(
$entity['comment-count-new'] = array(
'name' => t("New comment count"),
'description' => t("The number of comments posted on a node since the reader last viewed it."),
'description' => t("The number of comments posted on an entity since the reader last viewed it."),
);
// Core comment tokens
......@@ -79,9 +79,16 @@ function comment_token_info() {
'description' => t("The comment's parent, if comment threading is active."),
'type' => 'comment',
);
$comment['entity'] = array(
'name' => t("Entity"),
'description' => t("The entity the comment was posted to."),
'type' => 'entity',
);
// Support legacy comment node tokens, since tokes are embedded in user data
// and can't be upgraded directly.
$comment['node'] = array(
'name' => t("Node"),
'description' => t("The node the comment was posted to."),
'description' => t("DEPRECATED: The node the comment was posted to."),
'type' => 'node',
);
$comment['author'] = array(
......@@ -93,8 +100,10 @@ function comment_token_info() {
return array(
'types' => array('comment' => $type),
'tokens' => array(
'node' => $node,
'entity' => $entity,
'comment' => $comment,
// Support deprecated node tokens.
'node' => $entity,
),
);
}
......@@ -177,8 +186,8 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
case 'parent':
if (!empty($comment->pid->target_id)) {
$parent = comment_load($comment->pid->target_id);
$replacements[$original] = $sanitize ? filter_xss($parent->subject) : $parent->subject;
$parent = entity_load('comment', $comment->pid->target_id);
$replacements[$original] = $sanitize ? filter_xss($parent->subject->value) : $parent->subject->value;
}
break;
......@@ -190,17 +199,36 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
$replacements[$original] = format_date($comment->changed->value, 'medium', '', NULL, $langcode);
break;
case 'node':
$node = $comment->nid->entity;
$title = $node->label();
case 'entity':
$entity = entity_load($comment->entity_type->value, $comment->entity_id->value);
$title = $entity->label();
$replacements[$original] = $sanitize ? filter_xss($title) : $title;
break;
case 'node':
// Support legacy comment node tokens, since tokes are embedded in
// user data and can't be upgraded directly.
// @todo Remove in Drupal 9, see https://drupal.org/node/2031901.
if ($comment->entity_type->value == 'node') {
$entity = entity_load($comment->entity_type->value, $comment->entity_id->value);
$title = $entity->label();
$replacements[$original] = $sanitize ? filter_xss($title) : $title;
}
else {
$replacements[$original] = NULL;
}
break;
}
}
// Chained token relationships.
if ($node_tokens = $token_service->findwithPrefix($tokens, 'node')) {
$node = $comment->nid->entity;
if ($entity_tokens = $token_service->findwithPrefix($tokens, 'entity')) {
$entity = entity_load($comment->entity_type->value, $comment->entity_id->value);
$replacements += $token_service->generate($comment->entity_type->value, $entity_tokens, array($comment->entity_type->value => $entity), $options);
}
if (($node_tokens = $token_service->findwithPrefix($tokens, 'node')) && $comment->entity_type->value == 'node') {
$node = entity_load($comment->entity_type->value, $comment->entity_id->value);
$replacements += $token_service->generate('node', $node_tokens, array('node' => $node), $options);
}
......@@ -220,17 +248,25 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
$replacements += $token_service->generate('user', $author_tokens, array('user' => $account), $options);
}
}
elseif ($type == 'node' & !empty($data['node'])) {
$node = $data['node'];
elseif (($type == 'entity' & !empty($data['entity'])) ||
($type == 'node' & !empty($data['node']))) {
$entity = !empty($data['entity']) ? $data['entity'] : $data['node'];
foreach ($tokens as $name => $original) {
switch($name) {
case 'comment-count':
$replacements[$original] = $node->comment_count;
$count = 0;
$fields = array_keys(\Drupal::service('comment.manager')->getFields($entity->entityType()));
$definitions = array_keys($entity->getPropertyDefinitions());
$valid_fields = array_intersect($fields, $definitions);
foreach ($valid_fields as $field_name) {
$count += $entity->get($field_name)->comment_count;
}
$replacements[$original] = $count;
break;
case 'comment-count-new':
$replacements[$original] = comment_num_new($node->id());
$replacements[$original] = comment_num_new($entity->id(), $entity->entityType());
break;
}
}
......
This diff is collapsed.
......@@ -74,10 +74,12 @@ function show($placeholder) {
function processNodeNewCommentLinks($placeholders) {
// Figure out which placeholders need the "x new comments" links.
var $placeholdersToUpdate = {};
var fieldName = 'comment';
var $placeholder;
$placeholders.each(function (index, placeholder) {
$placeholder = $(placeholder);
var timestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
fieldName = $placeholder.attr('data-history-node-field-name');
var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
var lastViewTimestamp = Drupal.history.getLastRead(nodeID);
......@@ -100,7 +102,7 @@ function processNodeNewCommentLinks($placeholders) {
$.ajax({
url: Drupal.url('comments/render_new_comments_node_links'),
type: 'POST',
data: { 'node_ids[]' : nodeIDs },
data: { 'node_ids[]' : nodeIDs, 'field_name' : fieldName },
dataType: 'json',
success: function (results) {
for (var nodeID in results) {
......
......@@ -8,6 +8,7 @@
namespace Drupal\comment;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\StringTranslation\TranslationManager;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
......@@ -23,25 +24,41 @@ class CommentBreadcrumbBuilder implements BreadcrumbBuilderInterface {
*/
protected $translation;
/**
* Stores the Entity manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* Constructs a CommentBreadcrumbBuilder object.
*
* @param \Drupal\Core\StringTranslation\TranslationManager $translation
* The translation manager.
* @param \Drupal\Core\Entity\EntityManager
* The entity manager.
*/
public function __construct(TranslationManager $translation) {
public function __construct(TranslationManager $translation, EntityManager $entity_manager) {
$this->translation = $translation;
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
if (isset($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'comment.reply' && isset($attributes['node'])) {
$node = $attributes['node'];
$uri = $node->uri();
if (isset($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'comment.reply'
&& isset($attributes['entity_type'])
&& isset($attributes['entity_id'])
&& isset($attributes['field_name'])
) {
$breadcrumb[] = l($this->t('Home'), NULL);
$breadcrumb[] = l($node->label(), $uri['path']);
$entity = $this->entityManager
->getStorageController($attributes['entity_type'])
->load($attributes['entity_id']);
$uri = $entity->uri();
$breadcrumb[] = l($entity->label(), $uri['path'], $uri['options']);
return $breadcrumb;
}
}
......
<?php
/**
* @file
* Contains \Drupal\comment\CommentFieldName.
*/
namespace Drupal\comment;
use Drupal\Core\Entity\Plugin\DataType\StringItem;
/**
* The field item for the 'fieldname' field.
*/
class CommentFieldName extends StringItem {
/**
* Definitions of the contained properties.
*
* @see self::getPropertyDefinitions()
*
* @var array
*/
static $propertyDefinitions;
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
if (!isset(static::$propertyDefinitions)) {
static::$propertyDefinitions['value'] = array(
'type' => 'string',
'label' => t('String value'),
'class' => '\Drupal\comment\CommentFieldNameValue',
'computed' => TRUE,
);
}
return static::$propertyDefinitions;
}
}
<?php
/**
* @file
* Contains \Drupal\comment\CommentFieldNameValue.
*/
namespace Drupal\comment;
use Drupal\Core\TypedData\TypedData;
use Drupal\Core\TypedData\ReadOnlyException;
use InvalidArgumentException;
/**
* A computed property for the string value of the field_name field.
*/
class CommentFieldNameValue extends TypedData {
/**
* {@inheritdoc}
*/
public function getValue() {
if (!isset($this->value)) {
if (!isset($this->parent)) {
throw new InvalidArgumentException('Computed properties require context for computation.');
}
$field = $this->parent->getParent();
$entity = $field->getParent();
// Field id is of the form {entity_type}__{field_name}. We set the
// optional limit param to explode() in case the user adds a field with __
// in the name.
$parts = explode('__', $entity->field_id->value, 2);
if ($parts && count($parts) == 2) {
$this->value = end($parts);
}
}
return $this->value;
}
/**
* {@inheritdoc}
*/
public function setValue($value, $notify = TRUE) {
if (isset($value)) {
$this->field_name = $value;
// Also set the field id.
$field = $this->parent->getParent();
$entity = $field->getParent();
$entity->field_id = $entity->entity_type->value . '__' . $value;
}
}
}
......@@ -24,4 +24,5 @@
* UrlGenerator::generateFromPath().
*/
public function permalink();
}
<?php
/**
* @file
* Contains \Drupal\comment\CommentManager.
*/
namespace Drupal\comment;
use Drupal\Component\Utility\String;
use Drupal\field\FieldInfo;
use Drupal\Core\Entity\EntityManager;
/**
* Comment manager contains common functions to manage comment fields.
*/
class CommentManager {
/**
* The field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* Construct the CommentManager object.
*
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager service.
*/
public function __construct(FieldInfo $field_info, EntityManager $entity_manager) {
$this->fieldInfo = $field_info;
$this->entityManager = $entity_manager;
}
/**
* Utility function to return URI of the comment's parent entity.
*
* @param \Drupal\comment\CommentInterface $comment
* The comment entity.
*
* @return array
* An array returned by \Drupal\Core\Entity\EntityInterface::uri().
*/
public function getParentEntityUri(CommentInterface $comment) {
return $this->entityManager
->getStorageController($comment->entity_type->value)
->load($comment->entity_id->value)
->uri();
}
/**
* Utility function to return an array of comment fields.
*
* @param string $entity_type
* The entity type to return fields which are attached on.
*
* @return array
* An array of comment field map definitions, keyed by field name. Each
* value is an array with two entries:
* - type: The field type.
* - bundles: The bundles in which the field appears, as an array with entity
* types as keys and the array of bundle names as values.
*
* @see field_info_field_map()
*/
public function getFields($entity_type = NULL) {
$map = $this->getAllFields();
if (!isset($map[$entity_type])) {
return array();
}
return $map[$entity_type];