Issue #731724 by larowlan, andypost, dixon_, tsvenson: Convert comment settings into a field to make them work with CMI and non-node entities.

8.0.x
Alex Pott 2013-09-27 17:34:47 +02:00
parent fb091cd6b5
commit 37949fe378
140 changed files with 4393 additions and 1674 deletions

View File

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

View File

@ -0,0 +1,19 @@
/**
* @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);

View File

@ -1,40 +0,0 @@
/**
* @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);

View File

@ -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'),

View File

@ -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);
}
}
/**

View File

@ -6,6 +6,5 @@ version: VERSION
core: 8.x
dependencies:
- datetime
- node
- text
configure: admin/content/comment

View File

@ -5,30 +5,26 @@
* Install, update and uninstall functions for the Comment module.
*/
use Drupal\Core\Language\Language;
use Drupal\comment\Plugin\field\field_type\CommentItem;
use Drupal\field\Entity\Field;
/**
* Implements hook_uninstall().
*/
function comment_uninstall() {
// Remove variables.
variable_del('comment_block_count');
$node_types = array_keys(node_type_get_types());
\Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
'Drupal\comment' => DRUPAL_ROOT . '/core/modules/comment/lib',
)));
drupal_classloader_register('comment', 'core/modules/comment');
foreach ($node_types as $node_type) {
variable_del('comment_' . $node_type);
variable_del('comment_anonymous_' . $node_type);
variable_del('comment_controls_' . $node_type);
variable_del('comment_default_mode_' . $node_type);
variable_del('comment_default_order_' . $node_type);
variable_del('comment_default_per_page_' . $node_type);
variable_del('comment_form_location_' . $node_type);
variable_del('comment_preview_' . $node_type);
variable_del('comment_subject_field_' . $node_type);
// Remove the comment fields.
$fields = entity_load_multiple_by_properties('field_entity', array(
'type' => 'comment',
'include_inactive' => TRUE,
'include_deleted' => FALSE,
));
foreach ($fields as $field) {
entity_invoke_bundle_hook('delete', 'comment', $field->entity_type . '__' . $field->name);
$field->delete();
}
// Remove states.
// Remove state setting.
\Drupal::state()->delete('comment.node_comment_statistics_scale');
}
@ -36,48 +32,37 @@ function comment_uninstall() {
* Implements hook_install().
*/
function comment_install() {
// Insert records into the node_comment_statistics for nodes that are missing.
$query = db_select('node_field_data', 'n');
$query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid AND n.default_langcode = 1');
$query->addField('n', 'created', 'last_comment_timestamp');
$query->addField('n', 'uid', 'last_comment_uid');
$query->addField('n', 'nid');
$query->addExpression('0', 'comment_count');
$query->addExpression('NULL', 'last_comment_name');
$query->isNull('ncs.comment_count');
db_insert('node_comment_statistics')
->from($query)
->execute();
// By default, maintain entity statistics for comments.
// @see \Drupal\comment\CommentStorageController::updateEntityStatistics().
\Drupal::state()->set('comment.maintain_entity_statistics', TRUE);
}
/**
* Implements hook_modules_installed().
* Returns comment fields.
*
* Creates comment body fields for node types existing before the Comment module
* is enabled. We use hook_modules_installed() rather than hook_install() so we
* can react to node types of existing modules, and those of modules being
* enabled both before and after the Comment module in the loop of
* \Drupal\Core\Extension\ModuleHandler::install().
* comment_get_comment_fields() cannot be used in maintenance mode because
* comment is disabled and its fields are inactive.
*
* There is a separate comment bundle for each node type to allow for
* per-node-type customization of comment fields. Each one of these bundles
* needs a comment body field instance. A comment bundle is needed even for
* node types whose comments are disabled by default, because individual nodes
* may override that default.
*
* @see comment_node_type_insert()
* @return array
* An array of comment fields keyed by field ID.
*/
function comment_modules_installed($modules) {
// Only react if the Comment module is one of the modules being enabled.
// hook_node_type_insert() is used to create body fields while the comment
// module is enabled.
if (in_array('comment', $modules)) {
// Create comment body fields for each node type, if needed.
foreach (node_type_get_types() as $type => $info) {
_comment_body_field_create($info);
function _comment_get_comment_fields() {
$fields = entity_load_multiple_by_properties('field_entity', array(
'type' => 'comment',
'include_inactive' => TRUE,
'include_deleted' => FALSE,
));
foreach ($fields as &$field) {
$instances = entity_load_multiple_by_properties('field_instance', array(
'field_uuid' => $field->uuid,
'include_inactive' => TRUE,
'include_deleted' => FALSE,
));
foreach ($instances as $instance) {
$field->bundles[$instance->entity_type] = $instance->bundle;
}
}
return $fields;
}
/**
@ -104,12 +89,26 @@ function comment_schema() {
'default' => 0,
'description' => 'The {comment}.cid to which this comment is a reply. If set to 0, this comment is not a reply to an existing comment.',
),
'nid' => array(
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {node}.nid to which this comment is a reply.',
'description' => 'The entity_id of the entity to which this comment is a reply.',
),
'entity_type' => array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node',
'length' => 255,
'description' => 'The entity_type of the entity to which this comment is a reply.',
),
'field_id' => array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node.comment',
'length' => 255,
'description' => 'The field_id of the field that was used to add this comment.',
),
'uid' => array(
'type' => 'int',
@ -186,9 +185,22 @@ function comment_schema() {
),
'indexes' => array(
'comment_status_pid' => array('pid', 'status'),
'comment_num_new' => array('nid', 'status', 'created', 'cid', 'thread'),
'comment_num_new' => array(
'entity_id',
array('entity_type', 32),
array('field_id', 32),
'status',
'created',
'cid',
'thread',
),
'comment_uid' => array('uid'),
'comment_nid_langcode' => array('nid', 'langcode'),
'comment_entity_langcode' => array(
'entity_id',
array('entity_type', 32),
array('field_id', 32),
'langcode',
),
'comment_created' => array('created'),
),
'primary key' => array('cid'),
@ -196,10 +208,6 @@ function comment_schema() {
'uuid' => array('uuid'),
),
'foreign keys' => array(
'comment_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'comment_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
@ -207,15 +215,29 @@ function comment_schema() {
),
);
$schema['node_comment_statistics'] = array(
'description' => 'Maintains statistics of node and comments posts to show "new" and "updated" flags.',
$schema['comment_entity_statistics'] = array(
'description' => 'Maintains statistics of entity and comments posts to show "new" and "updated" flags.',
'fields' => array(
'nid' => array(
'entity_id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {node}.nid for which the statistics are compiled.',
'description' => 'The entity_id of the entity for which the statistics are compiled.',
),
'entity_type' => array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node',
'length' => 255,
'description' => 'The entity_type of the entity to which this comment is a reply.',
),
'field_id' => array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node__comment',
'length' => 255,
'description' => 'The field_id of the field that was used to add this comment.',
),
'cid' => array(
'type' => 'int',
@ -247,20 +269,16 @@ function comment_schema() {
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The total number of comments on this node.',
'description' => 'The total number of comments on this entity.',
),
),
'primary key' => array('nid'),
'primary key' => array('entity_id', array('entity_type', 32), array('field_id', 32)),
'indexes' => array(
'node_comment_timestamp' => array('last_comment_timestamp'),
'last_comment_timestamp' => array('last_comment_timestamp'),
'comment_count' => array('comment_count'),
'last_comment_uid' => array('last_comment_uid'),
),
'foreign keys' => array(
'statistics_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'last_comment_author' => array(
'table' => 'users',
'columns' => array(
@ -278,6 +296,16 @@ function comment_schema() {
* @{
*/
/**
* Implements hook_update_dependencies().
*/
function comment_update_dependencies() {
// Node comment status cannot be turned into fields until after the fields and
// instances are converted to ConfigEntities.
$dependencies['comment'][8006]['field'] = 8003;
return $dependencies;
}
/**
* Renames {comment}.language to {comment}.langcode.
*/
@ -388,9 +416,323 @@ function comment_update_8003(&$sandbox) {
function comment_update_8004() {
update_variables_to_state(array(
'node_cron_comments_scale' => 'comment.node_comment_statistics_scale',
'comment_maintain_node_statistics' => 'comment.maintain_entity_statistics',
));
}
/**
* Update the {comment_node_statistics} and {comment} tables to new structure.
*/
function comment_update_8005(&$sandbox) {
// Drop old indexes.
if (db_index_exists('comment', 'comment_node')) {
// Drop the comment_node foreign key.
db_drop_index('comment', 'comment_node');
}
db_drop_index('comment', 'comment_num_new');
db_drop_index('comment', 'comment_nid_langcode');
// Add the entity_type and field id columns to comment.
db_add_field('comment', 'entity_type', array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node',
'length' => 255,
'description' => 'The entity_type of the entity to which this comment is a reply.',
));
db_add_field('comment', 'field_id', array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node__comment',
'length' => 255,
'description' => 'The field_id of the field that was used to add this comment.',
));
// Rename the nid column to entity_id.
db_change_field('comment', 'nid', 'entity_id', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The entity_id of the entity to which this comment is a reply.',
));
db_add_index('comment', 'comment_num_new', array(
'entity_id',
array('entity_type', 32),
array('field_id', 32),
'status',
'created',
'cid',
'thread'
));
// Add the comment_entity_langcode index.
db_add_index('comment', 'comment_entity_langcode', array(
'entity_id',
array('entity_type', 32),
array('field_id', 32),
'langcode',
));
// We need to drop all indexes to make sure their constrains named properly.
db_drop_primary_key('node_comment_statistics');
if (db_index_exists('node_comment_statistics', 'statistics_node')) {
// Drop the statistics_node foreign key.
db_drop_index('node_comment_statistics', 'statistics_node');
}
db_drop_index('node_comment_statistics', 'node_comment_timestamp');
db_drop_index('node_comment_statistics', 'comment_count');
db_drop_index('node_comment_statistics', 'last_comment_uid');
// Rename {node_comment_statistics} to {comment_entity_statistics}.
db_rename_table('node_comment_statistics', 'comment_entity_statistics');
// Add the entity_type and field id columns to comment_entity_statistics.
db_add_field('comment_entity_statistics', 'entity_type', array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node',
'length' => 255,
'description' => 'The entity_type of the entity to which this comment is a reply.',
));
db_add_field('comment_entity_statistics', 'field_id', array(
'type' => 'varchar',
'not null' => TRUE,
'default' => 'node__comment',
'length' => 255,
'description' => 'The field_id of the field that was used to add this comment.',
));
// Rename the nid column in entity_comment_statistics to entity_id.
db_change_field('comment_entity_statistics', 'nid', 'entity_id', array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'description' => 'The entity_id of the entity to which this comment is a reply.',
));
// Add indexes.
db_add_primary_key('comment_entity_statistics', array('entity_id', array('entity_type', 32), array('field_id',32)));
db_add_index('comment_entity_statistics', 'last_comment_timestamp', array('last_comment_timestamp'));
db_add_index('comment_entity_statistics', 'comment_count', array('comment_count'));
db_add_index('comment_entity_statistics', 'last_comment_uid', array('last_comment_uid'));
}
/**
* Adds comment fields for all node types.
*
* Field instance settings "default_mode", "per_page" and "form_location" are
* preserved to allow migrate contrib modules.
*/
function comment_update_8006(&$sandbox) {
// Entity module update functions are needed to update components of node
// entity display and form display for new comment fields.
module_load_install('entity');
// Loop over defined node_types.
$node_types = array_keys(_update_7000_node_get_types());
foreach ($node_types as $node_type) {
// COMMENT_OPEN
$default_value = update_variable_get('comment_' . $node_type, 2);
// Add a default comment field for existing node comments.
$field_name = 'comment_' . $node_type;
$field = array(
// We need one per content type to match the existing comment bundles.
'id' => 'node.' . $field_name,
'type' => 'comment',
'module' => 'comment',
'name' => $field_name,
'entity_type' => 'node',
);
// Make sure field doesn't already exist.
$index = 0;
// @todo Refactor once https://drupal.org/node/1856972 lands.
while (!\Drupal::config('field.field.node.' . $field['name'])->isNew()) {
// Append a numeric index.
$field['id'] = 'node.' . $field_name . '_' . $index;
$field['name'] = $field_name . '_' . $index;
// Increment index.
$index++;
}
$field_object = new Field($field);
$field['schema'] = CommentItem::schema($field_object);
_update_8003_field_create_field($field);
// @todo Refactor once https://drupal.org/node/1856972 lands.
if (\Drupal::config("field.instance.node.$node_type." . $field['name'])->isNew()) {
// Add the comment field, setting the instance settings to match those for
// the given node_type.
$instance_settings = array(
// COMMENT_MODE_THREADED
'default_mode' => update_variable_get('comment_default_mode_' . $node_type, 1),
'per_page' => update_variable_get('comment_default_per_page_' . $node_type, 50),
// COMMENT_FORM_BELOW
'form_location' => update_variable_get('comment_form_location_' . $node_type, 1),
// COMMENT_ANONYMOUS_MAYNOT_CONTACT
'anonymous' => update_variable_get('comment_anonymous_' . $node_type, 0),
'subject' => update_variable_get('comment_subject_field_' . $node_type, 1),
// DRUPAL_OPTIONAL
'preview' => update_variable_get('comment_preview_' . $node_type, 1),
);
$instance = array(
'id' => "node.$node_type." . $field['name'],
'entity_type' => 'node',
'bundle' => $node_type,
'default_value' => array(array('status' => $default_value)),
'deleted' => '0',
'description' => '',
'label' => 'Comment settings',
'required' => TRUE,
'settings' => $instance_settings,
);
_update_8003_field_create_instance($field, $instance);
}
// Prepare defaults for the default and full view modes.
$display_options_default = array(
'label' => 'hidden',
'type' => 'comment_default',
'settings' => array(),
'weight' => 20,
);
// Assign display settings for the 'default' and 'full' view modes.
$display = _update_8000_entity_get_display('node', $node_type, 'default');
$display->set('content.' . $field['name'], $display_options_default)
->save();
$display = _update_8000_entity_get_display('node', $node_type, 'full');
$display->set('content.' . $field['name'], $display_options_default)
->save();
// Assign widget settings for the 'default' form mode.
$display_options_default = array(
'type' => 'comment_default',
'settings' => array(),
'weight' => 20,
);
$display = _update_8000_entity_get_form_display('node', $node_type, 'default');
$display->set('content.' . $field['name'], $display_options_default)
->save();
// Clean up old variables.
update_variable_del('comment_' . $node_type);
update_variable_del('comment_default_mode_' . $node_type);
update_variable_del('comment_default_per_page_' . $node_type);
update_variable_del('comment_anonymous_' . $node_type);
update_variable_del('comment_subject_field_' . $node_type);
update_variable_del('comment_form_location_' . $node_type);
update_variable_del('comment_preview_' . $node_type);
}
}
/**
* Update existing comment values from node table to field data.
*/
function comment_update_8007(&$sandbox) {
$types = array_keys(_update_7000_node_get_types());
// Load each node type in batch and initialize field values for comment field.
if (!isset($sandbox['progress'])) {
$sandbox['progress'] = 0;
$sandbox['current_nid'] = 0;
// We track all node types here.
$sandbox['node_types'] = $types;
// We start with this node type.
$sandbox['node_type'] = array_shift($sandbox['node_types']);
$sandbox['#finished'] = 1;
$sandbox['max'] = db_query('SELECT COUNT(nid) FROM {node}')->fetchField();
}
// Set the initial values of comment fields for existing nodes. Note that
// contrib modules will need to handle the upgrade path on their own, as
// they are disabled during core upgrade.
// Node table will always exist up until here because in 7.x comment
// depends on node.
$nodes = db_select('node', 'n')
->fields('n', array('nid', 'comment', 'vid', 'langcode'))
->condition('type', $sandbox['node_type'])
->condition('nid', $sandbox['current_nid'], '>')
->range(0, 50)
->orderBy('nid', 'ASC')
->execute()
->fetchAllAssoc('nid');
if (count($nodes) > 0) {
$insert = db_insert('node__comment_' . $sandbox['node_type'])
->fields(array(
'bundle',
'entity_id',
'revision_id',
'langcode',
'delta',
'comment_' . $sandbox['node_type'] . '_status',
));
$revision = db_insert('node_revision__comment_' . $sandbox['node_type'])
->fields(array(
'bundle',
'entity_id',
'revision_id',
'langcode',
'delta',
'comment_' . $sandbox['node_type'] . '_status',
));
// Update the field name to match the node type.
db_update('comment')
->fields(array(
'field_id' => 'node__comment_' . $sandbox['node_type'],
))
->condition('entity_id', array_keys($nodes))
->execute();
foreach ($nodes as $nid => $node) {
$insert->values(array(
'bundle' => $sandbox['node_type'],
'entity_id' => $nid,
'revision_id' => $node->vid,
'langcode' => Language::LANGCODE_NOT_SPECIFIED,
'delta' => 0,
'comment_' . $sandbox['node_type'] . '_status' => $node->comment,
));
$revision->values(array(
'bundle' => $sandbox['node_type'],
'entity_id' => $nid,
'revision_id' => $node->vid,
'langcode' => Language::LANGCODE_NOT_SPECIFIED,
'delta' => 0,
'comment_' . $sandbox['node_type'] . '_status' => $node->comment,
));
$sandbox['progress']++;
$sandbox['current_nid'] = $nid;
}
$insert->execute();
$revision->execute();
// Populate the field name to match the node type.
db_update('comment_entity_statistics')
->fields(array(
'field_id' => 'node__comment_' . $sandbox['node_type'],
))
->condition('entity_id', array_keys($nodes))
->execute();
}
else {
// Move to the next node type.
$sandbox['node_type'] = array_shift($sandbox['node_types']);
// Reset the current nid pointer.
$sandbox['current_nid'] = 0;
}
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
}
/**
* Removes the existing fields.
*
* Data was migrated in comment_update_8007().
*/
function comment_update_8008(&$sandbox) {
// Remove the {node}.comment field.
db_drop_field('node', 'comment');
// Remove the {node_revision}.comment field.
db_drop_field('node_revision', 'comment');
}
/**
* @} End of "addtogroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -98,7 +98,7 @@ function comment_views_data() {
'help' => t('Hostname of user that posted the comment.'),
'field' => array(
'id' => 'standard',
),
),
'filter' => array(
'id' => 'string',
),
@ -115,7 +115,7 @@ function comment_views_data() {
'help' => t('E-mail of user that posted the comment. Will be empty if the author is a registered user.'),
'field' => array(
'id' => 'standard',
),
),
'filter' => array(
'id' => 'string',
),
@ -300,16 +300,11 @@ function comment_views_data() {
),
);
$data['comment']['nid'] = array(
'title' => t('Nid'),
'help' => t('The node ID to which the comment is a reply to.'),
'relationship' => array(
'title' => t('Content'),
'help' => t('The content to which the comment is a reply to.'),
'base' => 'node',
'base field' => 'nid',
$data['comment']['entity_id'] = array(
'title' => t('Entity ID'),
'help' => t('The Entity ID to which the comment is a reply to.'),
'field' => array(
'id' => 'standard',
'label' => t('Content'),
),
'filter' => array(
'id' => 'numeric',
@ -317,11 +312,74 @@ function comment_views_data() {
'argument' => array(
'id' => 'numeric',
),
'field' => array(
'id' => 'numeric',
'sort' => array(
'id' => 'standard',
),
);
$data['comment']['entity_type'] = array(
'title' => t('Entity type'),
'help' => t('The Entity type to which the comment is a reply to.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'string',
),
'sort' => array(
'id' => 'standard',
),
);
$data['comment']['field_id'] = array(
'title' => t('Comment field id'),
'help' => t('The Field id from which the comment originated.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'string',
),
'sort' => array(
'id' => 'standard',
),
);
$entities_info = \Drupal::entityManager()->getDefinitions();
// Provide a relationship for each entity type except comment.
foreach ($entities_info as $type => $entity_info) {
if ($type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
continue;
}
if ($fields = \Drupal::service('comment.manager')->getFields($type)) {
$data['comment'][$type] = array(
'relationship' => array(
'title' => $entity_info['label'],
'help' => t('The @entity_type to which the comment is a reply to.', array('@entity_type' => $entity_info['label'])),
'base' => $entity_info['base_table'],
'base field' => $entity_info['entity_keys']['id'],
'relationship field' => 'entity_id',
'id' => 'standard',
'label' => $entity_info['label'],
'extra' => array(
array(
'field' => 'entity_type',
'value' => $type,
'table' => 'comment'
),
),
),
);
}
}
$data['comment']['uid'] = array(
'title' => t('Author uid'),
'help' => t('If you need more fields than the uid add the comment: author relationship'),
@ -372,18 +430,35 @@ function comment_views_data() {
// Define the base group of this table. Fields that don't have a group defined
// will go into this field by default.
$data['node_comment_statistics']['table']['group'] = t('Content');
$data['comment_entity_statistics']['table']['group'] = t('Comment Statistics');
// Explain how this table joins to others.
$data['node_comment_statistics']['table']['join'] = array(
'node' => array(
'type' => 'INNER',
'left_field' => 'nid',
'field' => 'nid',
),
);
// Provide a relationship for each entity type except comment.
foreach ($entities_info as $type => $entity_info) {
if ($type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
continue;
}
// This relationship does not use the 'field id' column, if the entity has
// multiple comment-fields, then this might introduce duplicates, in which
// case the site-builder should enable aggregation and SUM the comment_count
// field. We cannot create a relationship from the base table to
// {comment_entity_statistics} for each field as multiple joins between
// the same two tables is not supported.
if (\Drupal::service('comment.manager')->getFields($type)) {
$data['comment_entity_statistics']['table']['join'][$entity_info['base_table']] = array(
'type' => 'INNER',
'left_field' => $entity_info['entity_keys']['id'],
'field' => 'entity_id',
'extra' => array(
array(
'field' => 'entity_type',
'value' => $type,
),
),
);
}
}
$data['node_comment_statistics']['last_comment_timestamp'] = array(
$data['comment_entity_statistics']['last_comment_timestamp'] = array(
'title' => t('Last comment time'),
'help' => t('Date and time of when the last comment was posted.'),
'field' => array(
@ -397,22 +472,22 @@ function comment_views_data() {
),
);
$data['node_comment_statistics']['last_comment_name'] = array(
$data['comment_entity_statistics']['last_comment_name'] = array(
'title' => t("Last comment author"),
'help' => t('The name of the author of the last posted comment.'),
'field' => array(
'id' => 'comment_ncs_last_comment_name',
'id' => 'comment_ces_last_comment_name',
'no group by' => TRUE,
),
'sort' => array(
'id' => 'comment_ncs_last_comment_name',
'id' => 'comment_ces_last_comment_name',
'no group by' => TRUE,
),
);
$data['node_comment_statistics']['comment_count'] = array(
$data['comment_entity_statistics']['comment_count'] = array(
'title' => t('Comment count'),
'help' => t('The number of comments a node has.'),
'help' => t('The number of comments an entity has.'),
'field' => array(
'id' => 'numeric',
),
@ -427,28 +502,28 @@ function comment_views_data() {
),
);
$data['node_comment_statistics']['last_updated'] = array(
$data['comment_entity_statistics']['last_updated'] = array(
'title' => t('Updated/commented date'),
'help' => t('The most recent of last comment posted or node updated time.'),
'help' => t('The most recent of last comment posted or entity updated time.'),
'field' => array(
'id' => 'comment_ncs_last_updated',
'id' => 'comment_ces_last_updated',
'no group by' => TRUE,
),
'sort' => array(
'id' => 'comment_ncs_last_updated',
'id' => 'comment_ces_last_updated',
'no group by' => TRUE,
),
'filter' => array(
'id' => 'comment_ncs_last_updated',
'id' => 'comment_ces_last_updated',
),
);
$data['node_comment_statistics']['cid'] = array(
$data['comment_entity_statistics']['cid'] = array(
'title' => t('Last comment CID'),
'help' => t('Display the last comment of a node'),
'help' => t('Display the last comment of an entity'),
'relationship' => array(
'title' => t('Last comment'),
'help' => t('The last comment of a node.'),
'help' => t('The last comment of an entity.'),
'group' => t('Comment'),
'base' => 'comment',
'base field' => 'cid',
@ -457,9 +532,9 @@ function comment_views_data() {
),
);
$data['node_comment_statistics']['last_comment_uid'] = array(
$data['comment_entity_statistics']['last_comment_uid'] = array(
'title' => t('Last comment uid'),
'help' => t('The User ID of the author of the last comment of a node.'),
'help' => t('The User ID of the author of the last comment of an entity.'),
'relationship' => array(
'title' => t('Last comment author'),
'base' => 'users',
@ -478,6 +553,39 @@ function comment_views_data() {
),
);
$data['comment_entity_statistics']['entity_type'] = array(
'title' => t('Entity type'),
'help' => t('The entity type to which the comment is a reply to.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'string',
),
'sort' => array(
'id' => 'standard',
),
);
$data['comment_entity_statistics']['field_id'] = array(
'title' => t('Comment field ID'),
'help' => t('The field ID from which the comment originated.'),
'field' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'string',
),
'argument' => array(
'id' => 'string',
),
'sort' => array(
'id' => 'standard',
),
);
return $data;
}
@ -485,7 +593,8 @@ function comment_views_data() {
* Implements hook_views_data_alter().
*/
function comment_views_data_alter(&$data) {
// new comments
// New comments are only supported for node table because it requires the
// history table.
$data['node']['new_comments'] = array(
'title' => t('New comments'),
'help' => t('The number of new comments on the node.'),
@ -495,59 +604,79 @@ function comment_views_data_alter(&$data) {
),
);
$data['node']['comments_link'] = array(
'field' => array(
'title' => t('Add comment link'),
'help' => t('Display the standard add comment link used on regular nodes, which will only display if the viewing user has access to add a comment.'),
'id' => 'comment_node_link',
),
);
// Provide a integration for each entity type except comment.
foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
if ($entity_type == 'comment' || empty($entity_info['fieldable']) || !isset($entity_info['base_table'])) {
continue;
}
$fields = \Drupal::service('comment.manager')->getFields($entity_type);
$base_table = $entity_info['base_table'];
$args = array('@entity_type' => $entity_type);
$data['node_field_data']['comment'] = array(
'title' => t('Comment status'),
'help' => t('Whether comments are enabled or disabled on the node.'),
'field' => array(
'id' => 'node_comment',
),
'sort' => array(
'id' => 'standard',
),
'filter' => array(
'id' => 'node_comment',
),
);
if ($fields) {
$data[$base_table]['comments_link'] = array(
'field' => array(
'title' => t('Add comment link'),
'help' => t('Display the standard add comment link used on regular @entity_type, which will only display if the viewing user has access to add a comment.', $args),
'id' => 'comment_entity_link',
),
);
$data['node_field_data']['uid_touch'] = array(
'title' => t('User posted or commented'),
'help' => t('Display nodes only if a user posted the node or commented on the node.'),
'argument' => array(
'field' => 'uid',
'name table' => 'users',
'name field' => 'name',
'id' => 'argument_comment_user_uid',
'no group by' => TRUE,
),
'filter' => array(
'field' => 'uid',
'name table' => 'users',
'name field' => 'name',
'id' => 'comment_user_uid',
),
);
$data['node']['cid'] = array(
'title' => t('Comments of the node'),
'help' => t('Relate all comments on the node. This will create 1 duplicate record for every comment. Usually if you need this it is better to create a comment view.'),
'relationship' => array(
'group' => t('Comment'),
'label' => t('Comments'),
'base' => 'comment',
'base field' => 'nid',
'relationship field' => 'nid',
'id' => 'standard',
),
);
if ($entity_info['id'] == 'node') {
// Node properties lives in data_table.
$table = $entity_info['data_table'];
}
else {
$table = $base_table;
}
$data[$table]['uid_touch'] = array(
'title' => t('User posted or commented'),
'help' => t('Display nodes only if a user posted the @entity_type or commented on the @entity_type.', $args),
'argument' => array(
'field' => 'uid',
'name table' => 'users',
'name field' => 'name',
'id' => 'argument_comment_user_uid',
'no group by' => TRUE,
'entity_type' => $entity_type,
'entity_id' => $entity_info['entity_keys']['id'],
),
'filter' => array(
'field' => 'uid',
'name table' => 'users',
'name field' => 'name',
'id' => 'comment_user_uid',
'entity_type' => $entity_type,
'entity_id' => $entity_info['entity_keys']['id'],
),
);
foreach ($fields as $field_name => $field) {
$data[$base_table][$field_name . '_cid'] = array(
'title' => t('Comments of the @entity_type using field: @field_name', $args + array('@field_name' => $field_name)),
'help' => t('Relate all comments on the @entity_type. This will create 1 duplicate record for every comment. Usually if you need this it is better to create a comment view.', $args),
'relationship' => array(
'group' => t('Comment'),
'label' => t('Comments'),
'base' => 'comment',
'base field' => 'entity_id',
'relationship field' => $entity_info['entity_keys']['id'],
'id' => 'standard',
'extra' => array(
array(
'field' => 'entity_type',
'value' => $entity_type,
),
array(
'field' => 'field_id',
'value' => $entity_type . '.' . $field_name,
),
),
),
);
}
}
}
}
/**

View File

@ -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) {

View File

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

View File

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

View File

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

View File

@ -7,39 +7,89 @@
namespace Drupal\comment;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityFormControllerNG;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\FieldInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base for controller for comment forms.
*/
class CommentFormController extends EntityFormControllerNG {
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* The field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('field.info'),
$container->get('current_user')
);
}
/**
* Constructs a new CommentRenderController.
*
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager service.
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(EntityManager $entity_manager, FieldInfo $field_info, AccountInterface $current_user) {
$this->entityManager = $entity_manager;
$this->fieldInfo = $field_info;
$this->currentUser = $current_user;
}
/**
* Overrides Drupal\Core\Entity\EntityFormController::form().
*/
public function form(array $form, array &$form_state) {
global $user;
$comment = $this->entity;
$node = $comment->nid->entity;
$entity = $this->entityManager->getStorageController($comment->entity_type->value)->load($comment->entity_id->value);
$field_name = $comment->field_name->value;
$instance = $this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name);
// Use #comment-form as unique jump target, regardless of node type.
// Use #comment-form as unique jump target, regardless of entity type.
$form['#id'] = drupal_html_id('comment_form');
$form['#theme'] = array('comment_form__node_' . $node->getType(), 'comment_form');
$form['#theme'] = array('comment_form__' . $entity->entityType() . '__' . $entity->bundle() . '__' . $field_name, 'comment_form');
$anonymous_contact = variable_get('comment_anonymous_' . $node->getType(), COMMENT_ANONYMOUS_MAYNOT_CONTACT);
$is_admin = $comment->id() && user_access('administer comments');
$anonymous_contact = $instance->getFieldSetting('anonymous');
$is_admin = $comment->id() && $this->currentUser->hasPermission('administer comments');
if (!$user->isAuthenticated() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
if (!$this->currentUser->isAuthenticated() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
}
// If not replying to a comment, use our dedicated page callback for new
// comments on nodes.
if (!$comment->id() && !$comment->pid->target_id) {
$form['#action'] = url('comment/reply/' . $comment->nid->target_id);
// Comments on entities.
if (!$comment->id() && empty($comment->pid->target_id)) {
$form['#action'] = url('comment/reply/' . $entity->entityType() . '/' . $entity->id() . '/' . $field_name);
}
if (isset($form_state['comment_preview'])) {
@ -51,7 +101,7 @@ class CommentFormController extends EntityFormControllerNG {
if ($is_admin) {
$form['author'] += array(
'#type' => 'details',
'#title' => t('Administration'),
'#title' => $this->t('Administration'),
'#collapsed' => TRUE,
);
}
@ -62,42 +112,42 @@ class CommentFormController extends EntityFormControllerNG {
$status = (isset($comment->status->value) ? $comment->status->value : COMMENT_NOT_PUBLISHED);
$date = (!empty($comment->date) ? $comment->date : DrupalDateTime::createFromTimestamp($comment->created->value));
if (empty($form_state['comment_preview'])) {
$form['#title'] = t('Edit comment %title', array(
$form['#title'] = $this->t('Edit comment %title', array(
'%title' => $comment->subject->value,
));
}
}
else {
if ($user->isAuthenticated()) {
$author = $user->getUsername();
if ($this->currentUser->isAuthenticated()) {
$author = $this->currentUser->getUsername();
}
else {
$author = ($comment->name->value ? $comment->name->value : '');
}
$status = (user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED);
$status = ($this->currentUser->hasPermission('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED);
$date = '';
}
// Add the author name field depending on the current user.
$form['author']['name'] = array(
'#type' => 'textfield',
'#title' => t('Your name'),
'#title' => $this->t('Your name'),
'#default_value' => $author,
'#required' => ($user->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
'#required' => ($this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
'#maxlength' => 60,
'#size' => 30,
);
if ($is_admin) {
$form['author']['name']['#title'] = t('Authored by');
$form['author']['name']['#description'] = t('Leave blank for %anonymous.', array('%anonymous' => \Drupal::config('user.settings')->get('anonymous')));
$form['author']['name']['#title'] = $this->t('Authored by');
$form['author']['name']['#description'] = $this->t('Leave blank for %anonymous.', array('%anonymous' => $this->config('user.settings')->get('anonymous')));
$form['author']['name']['#autocomplete_route_name'] = 'user.autocomplete';
}
elseif ($user->isAuthenticated()) {
elseif ($this->currentUser->isAuthenticated()) {
$form['author']['name']['#type'] = 'item';
$form['author']['name']['#value'] = $form['author']['name']['#default_value'];
$username = array(
'#theme' => 'username',
'#account' => $user,
'#account' => $this->currentUser,
);
$form['author']['name']['#markup'] = drupal_render($username);
}
@ -105,28 +155,28 @@ class CommentFormController extends EntityFormControllerNG {
// Add author e-mail and homepage fields depending on the current user.
$form['author']['mail'] = array(
'#type' => 'email',
'#title' => t('E-mail'),
'#title' => $this->t('E-mail'),
'#default_value' => $comment->mail->value,
'#required' => ($user->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
'#required' => ($this->currentUser->isAnonymous() && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
'#maxlength' => 64,
'#size' => 30,
'#description' => t('The content of this field is kept private and will not be shown publicly.'),
'#access' => $is_admin || ($user->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
'#description' => $this->t('The content of this field is kept private and will not be shown publicly.'),
'#access' => $is_admin || ($this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
);
$form['author']['homepage'] = array(
'#type' => 'url',
'#title' => t('Homepage'),
'#title' => $this->t('Homepage'),
'#default_value' => $comment->homepage->value,
'#maxlength' => 255,
'#size' => 30,
'#access' => $is_admin || ($user->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
'#access' => $is_admin || ($this->currentUser->isAnonymous() && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
);
// Add administrative comment publishing options.
$form['author']['date'] = array(
'#type' => 'datetime',
'#title' => t('Authored on'),
'#title' => $this->t('Authored on'),
'#default_value' => $date,
'#size' => 20,
'#access' => $is_admin,
@ -134,27 +184,27 @@ class CommentFormController extends EntityFormControllerNG {
$form['author']['status'] = array(
'#type' => 'radios',
'#title' => t('Status'),
'#title' => $this->t('Status'),
'#default_value' => $status,
'#options' => array(
COMMENT_PUBLISHED => t('Published'),
COMMENT_NOT_PUBLISHED => t('Not published'),
COMMENT_PUBLISHED => $this->t('Published'),
COMMENT_NOT_PUBLISHED => $this->t('Not published'),
),
'#access' => $is_admin,
);
$form['subject'] = array(
'#type' => 'textfield',
'#title' => t('Subject'),
'#title' => $this->t('Subject'),
'#maxlength' => 64,
'#default_value' => $comment->subject->value,
'#access' => variable_get('comment_subject_field_' . $node->getType(), 1) == 1,
'#access' => $instance->getFieldSetting('subject'),
);
// Used for conditional validation of author fields.
$form['is_anonymous'] = array(
'#type' => 'value',
'#value' => ($comment->id() ? !$comment->uid->target_id : $user->isAnonymous()),
'#value' => ($comment->id() ? !$comment->uid->target_id : $this->currentUser->isAnonymous()),
);
// Make the comment inherit the current content language unless specifically
@ -166,7 +216,7 @@ class CommentFormController extends EntityFormControllerNG {
// Add internal comment properties.
$original = $comment->getUntranslated();
foreach (array('cid', 'pid', 'nid', 'uid', 'node_type', 'langcode') as $key) {
foreach (array('cid', 'pid', 'entity_id', 'entity_type', 'field_id', 'uid', 'langcode') as $key) {
$key_name = key($comment->$key->offsetGet(0)->getPropertyDefinitions());
$form[$key] = array('#type' => 'value', '#value' => $original->$key->{$key_name});
}
@ -180,8 +230,9 @@ class CommentFormController extends EntityFormControllerNG {
protected function actions(array $form, array &$form_state) {
$element = parent::actions($form, $form_state);
$comment = $this->entity;
$node = $comment->nid->entity;
$preview_mode = variable_get('comment_preview_' . $node->getType(), DRUPAL_OPTIONAL);
$entity = $this->entityManager->getStorageController($comment->entity_type->value)->load($comment->entity_id->value);
$instance = $this->fieldInfo->getInstance($comment->entity_type->value, $entity->bundle(), $comment->field_name->value);
$preview_mode = $instance->getFieldSetting('preview');
// No delete action on the comment form.
unset($element['delete']);
@ -191,11 +242,11 @@ class CommentFormController extends EntityFormControllerNG {
// Only show the save button if comment previews are optional or if we are
// already previewing the submission.
$element['submit']['#access'] = ($comment->id() && user_access('administer comments')) || $preview_mode != DRUPAL_REQUIRED || isset($form_state['comment_preview']);
$element['submit']['#access'] = ($comment->id() && $this->currentUser->hasPermission('administer comments')) || $preview_mode != DRUPAL_REQUIRED || isset($form_state['comment_preview']);
$element['preview'] = array(
'#type' => 'submit',
'#value' => t('Preview'),
'#value' => $this->t('Preview'),
'#access' => $preview_mode != DRUPAL_DISABLED,
'#validate' => array(
array($this, 'validate'),
@ -217,15 +268,16 @@ class CommentFormController extends EntityFormControllerNG {
if (!empty($form_state['values']['cid'])) {
// Verify the name in case it is being changed from being anonymous.
$account = user_load_by_name($form_state['values']['name']);
$accounts = $this->entityManager->getStorageController('user')->loadByProperties(array('name' => $form_state['values']['name']));
$account = reset($accounts);
$form_state['values']['uid'] = $account ? $account->id() : 0;
$date = $form_state['values']['date'];
if ($date instanceOf DrupalDateTime && $date->hasErrors()) {
form_set_error('date', t('You have to specify a valid date.'));
form_set_error('date', $this->t('You have to specify a valid date.'));
}
if ($form_state['values']['name'] && !$form_state['values']['is_anonymous'] && !$account) {
form_set_error('name', t('You have to specify a valid author.'));
form_set_error('name', $this->t('You have to specify a valid author.'));
}
}
elseif ($form_state['values']['is_anonymous']) {
@ -233,15 +285,9 @@ class CommentFormController extends EntityFormControllerNG {
// author of this comment was an anonymous user, verify that no registered
// user with this name exists.
if ($form_state['values']['name']) {
$query = db_select('users', 'u');
$query->addField('u', 'uid', 'uid');
$taken = $query
->condition('name', db_like($form_state['values']['name']), 'LIKE')
->countQuery()
->execute()
->fetchField();
if ($taken) {
form_set_error('name', t('The name you used belongs to a registered user.'));
$accounts = $this->entityManager->getStorageController('user')->loadByProperties(array('name' => $form_state['values']['name']));
if (!empty($accounts)) {
form_set_error('name', $this->t('The name you used belongs to a registered user.'));
}
}
}
@ -277,7 +323,7 @@ class CommentFormController extends EntityFormControllerNG {
// If the comment was posted by an anonymous user and no author name was
// required, use "Anonymous" by default.
if ($comment->is_anonymous && (!isset($comment->name->value) || $comment->name->value === '')) {
$comment->name->value = \Drupal::config('user.settings')->get('anonymous');
$comment->name->value = $this->config('user.settings')->get('anonymous');
}
// Validate the comment's subject. If not specified, extract from comment
@ -288,11 +334,11 @@ class CommentFormController extends EntityFormControllerNG {
// 2) Strip out all HTML tags
// 3) Convert entities back to plain-text.
$comment_text = $comment->comment_body->processed;
$comment->subject = truncate_utf8(trim(decode_entities(strip_tags($comment_text))), 29, TRUE);
$comment->subject = Unicode::truncate(trim(String::decodeEntities(strip_tags($comment_text))), 29, TRUE);
// Edge cases where the comment body is populated only by HTML tags will
// require a default subject.
if ($comment->subject->value == '') {
$comment->subject->value = t('(No subject)');
$comment->subject->value = $this->t('(No subject)');
}
}
@ -318,12 +364,14 @@ class CommentFormController extends EntityFormControllerNG {
* Overrides Drupal\Core\Entity\EntityFormController::save().
*/
public function save(array $form, array &$form_state) {
$node = node_load($form_state['values']['nid']);
$entity = entity_load($form_state['values']['entity_type'], $form_state['values']['entity_id']);
$comment = $this->entity;
$field_name = $comment->field_name->value;
$uri = $entity->uri();
if (user_access('post comments') && (user_access('administer comments') || $node->comment->value == COMMENT_NODE_OPEN)) {
if ($this->currentUser->hasPermission('post comments') && ($this->currentUser->hasPermission('administer comments') || $entity->{$field_name}->status == COMMENT_OPEN)) {
// Save the anonymous user information to a cookie for reuse.
if (user_is_anonymous()) {
if ($this->currentUser->isAnonymous()) {
user_cookie_save(array_intersect_key($form_state['values'], array_flip(array('name', 'mail', 'homepage'))));
}
@ -335,31 +383,33 @@ class CommentFormController extends EntityFormControllerNG {
// Explain the approval queue if necessary.
if ($comment->status->value == COMMENT_NOT_PUBLISHED) {
if (!user_access('administer comments')) {
drupal_set_message(t('Your comment has been queued for review by site administrators and will be published after approval.'));
if (!$this->currentUser->hasPermission('administer comments')) {
drupal_set_message($this->t('Your comment has been queued for review by site administrators and will be published after approval.'));
}
}
else {
drupal_set_message(t('Your comment has been posted.'));
drupal_set_message($this->t('Your comment has been posted.'));
}
$query = array();
// Find the current display page for this comment.
$page = comment_get_display_page($comment->id(), $node->getType());
$instance = $this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name);
$page = comment_get_display_page($comment->id(), $instance);
if ($page > 0) {
$query['page'] = $page;
}
// Redirect to the newly posted comment.
$redirect = array('node/' . $node->id(), array('query' => $query, 'fragment' => 'comment-' . $comment->id()));
$redirect = array($uri['path'], array('query' => $query, 'fragment' => 'comment-' . $comment->id()) + $uri['options']);
}
else {
watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject->value), WATCHDOG_WARNING);
drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject->value)), 'error');
// Redirect the user to the node they are commenting on.
$redirect = 'node/' . $node->id();
drupal_set_message($this->t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject->value)), 'error');
// Redirect the user to the entity they are commenting on.
$redirect = $uri['path'];
}
$form_state['redirect'] = $redirect;
// Clear the block and page caches so that anonymous users see the comment
// they have posted.
cache_invalidate_tags(array('content' => TRUE));
Cache::invalidateTags(array('content' => TRUE));
$this->entityManager->getRenderController($entity->entityType())->resetCache(array($entity->id()));
}
}

View File

@ -24,4 +24,5 @@ interface CommentInterface extends ContentEntityInterface, EntityChangedInterfac
* UrlGenerator::generateFromPath().
*/
public function permalink();
}

View File

@ -0,0 +1,244 @@
<?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];
}
/**
* Utility function to return all comment fields.
*/
public function getAllFields() {
$map = $this->fieldInfo->getFieldMap();
// Build a list of comment fields only.
$comment_fields = array();
foreach ($map as $entity_type => $data) {
foreach ($data as $field_name => $field_info) {
if ($field_info['type'] == 'comment') {
$comment_fields[$entity_type][$field_name] = $field_info;
}
}
}
return $comment_fields;
}
/**
* Utility method to add the default comment field to an entity.
*
* Attaches a comment field named 'comment' to the given entity type and
* bundle. Largely replicates the default behaviour in Drupal 7 and earlier.
*
* @param string $entity_type
* The entity type to attach the default comment field to.
* @param string $bundle
* The bundle to attach the default comment field instance to.
* @param string $field_name
* (optional) Field name to use for the comment field. Defaults to 'comment'.
* @param int $default_value
* (optional) Default value, one of COMMENT_HIDDEN, COMMENT_OPEN,
* COMMENT_CLOSED. Defaults to COMMENT_OPEN.
*/
public function addDefaultField($entity_type, $bundle, $field_name = 'comment', $default_value = COMMENT_OPEN) {
// Make sure the field doesn't already exist.
if (!$this->fieldInfo->getField($entity_type, $field_name)) {
// Add a default comment field for existing node comments.
$field = $this->entityManager->getStorageController('field_entity')->create(array(
'entity_type' => $entity_type,
'name' => $field_name,
'type' => 'comment',
'translatable' => '0',
));
// Create the field.
$field->save();
}
// Make sure the instance doesn't already exist.
if (!$this->fieldInfo->getInstance($entity_type, $bundle, $field_name)) {
$instance = $this->entityManager->getStorageController('field_instance')->create(array(
'label' => 'Comment settings',
'description' => '',
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'required' => 1,
'default_value' => array(
array(
'status' => $default_value,
'cid' => 0,
'last_comment_name' => '',
'last_comment_timestamp' => 0,
'last_comment_uid' => 0,
),
),
));
$instance->save();
// Assign widget settings for the 'default' form mode.
entity_get_form_display($entity_type, $bundle, 'default')
->setComponent($field_name, array(
'type' => 'comment_default',
'weight' => 20,
))
->save();
// Set default to display comment list.
entity_get_display($entity_type, $bundle, 'default')
->setComponent($field_name, array(
'label' => 'hidden',
'type' => 'comment_default',
'weight' => 20,
))
->save();
}
$this->addBodyField($entity_type, $field_name);
}
/**
* Creates a comment_body field instance.
*
* @param string $entity_type
* The type of the entity to which the comment field attached.
* @param string $field_name
* Name of the comment field to add comment_body field.
*/
public function addBodyField($entity_type, $field_name) {
// Create the field if needed.
$field = $this->entityManager->getStorageController('field_entity')->load('comment.comment_body');
if (!$field) {
$field = $this->entityManager->getStorageController('field_entity')->create(array(
'name' => 'comment_body',
'type' => 'text_long',
'entity_type' => 'comment',
));
$field->save();
}
// Create the instance if needed, field name defaults to 'comment'.
$comment_bundle = $entity_type . '__' . $field_name;
$field_instance = $this->entityManager
->getStorageController('field_instance')
->load("comment.$comment_bundle.comment_body");
if (!$field_instance) {
// Attaches the body field by default.
$field_instance = $this->entityManager->getStorageController('field_instance')->create(array(
'field_name' => 'comment_body',
'label' => 'Comment',
'entity_type' => 'comment',
'bundle' => $comment_bundle,
'settings' => array('text_processing' => 1),
'required' => TRUE,
));
$field_instance->save();
// Assign widget settings for the 'default' form mode.
entity_get_form_display('comment', $comment_bundle, 'default')
->setComponent('comment_body', array(
'type' => 'text_textarea',
))
->save();
// Assign display settings for the 'default' view mode.
entity_get_display('comment', $comment_bundle, 'default')
->setComponent('comment_body', array(
'label' => 'hidden',
'type' => 'text_default',
'weight' => 0,
))
->save();
}
}
/**
* Builds human readable page title for field_ui management screens.
*
* @param string $field_name
* The comment field for which the overview is to be displayed.
*
* @return string
* The human readable field name.
*/
public function getFieldUIPageTitle($field_name) {
list($entity_type, $field) = explode('__', $field_name, 2);
$field_info = $this->fieldInfo->getField($entity_type, $field);
$bundles = $field_info->getBundles();
$sample_bundle = reset($bundles);
$sample_instance = $this->fieldInfo->getInstance($entity_type, $sample_bundle, $field);
return String::checkPlain($sample_instance->label);
}
}

View File

@ -7,20 +7,80 @@
namespace Drupal\comment;
use Drupal\Core\Entity\EntityControllerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityRenderController;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\entity\Entity\EntityDisplay;
use Drupal\field\FieldInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Render controller for comments.
*/
class CommentRenderController extends EntityRenderController {
class CommentRenderController extends EntityRenderController implements EntityControllerInterface {
/**
* The entity manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* The field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
return new static(
$entity_type,
$container->get('entity.manager'),
$container->get('field.info'),
$container->get('module_handler')
);
}
/**
* Constructs a new CommentRenderController.
*
* @param string $entity_type
* The entity type.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager service.
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
*/
public function __construct($entity_type, EntityManager $entity_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler) {
parent::__construct($entity_type);
$this->entityManager = $entity_manager;
$this->fieldInfo = $field_info;
$this->moduleHandler = $module_handler;
}
/**
* Overrides Drupal\Core\Entity\EntityRenderController::buildContent().
*
* In addition to modifying the content key on entities, this implementation
* will also set the node key which all comments carry.
* will also set the comment entity key which all comments carry.
*
* @throws \InvalidArgumentException
* Thrown when a comment is attached to an entity that no longer exists.
*/
public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) {
$return = array();
@ -33,28 +93,31 @@ class CommentRenderController extends EntityRenderController {
foreach ($entities as $entity) {
$uids[] = $entity->uid->target_id;
}
user_load_multiple(array_unique($uids));
$this->entityManager->getStorageController('user')->loadMultiple(array_unique($uids));
parent::buildContent($entities, $displays, $view_mode, $langcode);
// Load all nodes of all comments at once.
$nids = array();
// Load all the entities that have comments attached.
$commented_entity_ids = array();
$commented_entities = array();
foreach ($entities as $entity) {
$nids[$entity->nid->target_id] = $entity->nid->target_id;
$commented_entity_ids[$entity->entity_type->value][] = $entity->entity_id->value;
}
// Load entities in bulk. This is more performant than using
// $comment->entity_id->value as we can load them in bulk per type.
foreach ($commented_entity_ids as $entity_type => $entity_ids) {
$commented_entities[$entity_type] = $this->entityManager->getStorageController($entity_type)->loadMultiple($entity_ids);
}
$nodes = node_load_multiple($nids);
global $user;
foreach ($entities as $entity) {
if (isset($nodes[$entity->nid->target_id])) {
$node = $nodes[$entity->nid->target_id];
if (isset($commented_entities[$entity->entity_type->value][$entity->entity_id->value])) {
$commented_entity = $commented_entities[$entity->entity_type->value][$entity->entity_id->value];
}
else {
throw new \InvalidArgumentException(t('Invalid node for comment.'));
throw new \InvalidArgumentException(t('Invalid entity for comment.'));
}
$entity->content['#node'] = $node;
$entity->content['#theme'] = 'comment__node_' . $node->bundle();
$entity->content['#entity'] = $entity;
$entity->content['#theme'] = 'comment__' . $entity->field_id->value . '__' . $commented_entity->bundle();
$entity->content['links'] = array(
'#theme' => 'links__comment',
'#pre_render' => array('drupal_pre_render_links'),
@ -63,8 +126,9 @@ class CommentRenderController extends EntityRenderController {
if (empty($entity->in_preview)) {
$entity->content['links'][$this->entityType] = array(
'#theme' => 'links__comment__comment',
// The "node" property is specified to be present, so no need to check.
'#links' => comment_links($entity, $node),
// The "entity" property is specified to be present, so no need to
// check.
'#links' => comment_links($entity, $commented_entity, $entity->field_name->value),
'#attributes' => array('class' => array('links', 'inline')),
);
}
@ -73,7 +137,7 @@ class CommentRenderController extends EntityRenderController {
$entity->content['#attached'] = array();
}
$entity->content['#attached']['library'][] = array('comment', 'drupal.comment-by-viewer');
if (\Drupal::moduleHandler()->moduleExists('history') && $user->isAuthenticated()) {
if ($this->moduleHandler->moduleExists('history') && \Drupal::currentUser()->isAuthenticated()) {
$entity->content['#attached']['library'][] = array('comment', 'drupal.comment-new-indicator');
}
}
@ -86,8 +150,10 @@ class CommentRenderController extends EntityRenderController {
parent::alterBuild($build, $comment, $display, $view_mode, $langcode);
if (empty($comment->in_preview)) {
$prefix = '';
$commented_entity = $this->entityManager->getStorageController($comment->entity_type->value)->load($comment->entity_id->value);
$instance = $this->fieldInfo->getInstance($commented_entity->entityType(), $commented_entity->bundle(), $comment->field_name->value);
$is_threaded = isset($comment->divs)
&& variable_get('comment_default_mode_' . $comment->bundle(), COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED;
&& $instance->getFieldSetting('default_mode') == COMMENT_MODE_THREADED;
// Add indentation div or close open divs as needed.
if ($is_threaded) {

View File

@ -9,6 +9,7 @@ namespace Drupal\comment;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\DatabaseStorageControllerNG;
use Drupal\Core\Entity\EntityChangedInterface;
/**
* Defines the controller class for comments.
@ -28,9 +29,7 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
*/
protected function buildQuery($ids, $revision_id = FALSE) {
$query = parent::buildQuery($ids, $revision_id);
// Specify additional fields from the user and node tables.
$query->innerJoin('node', 'n', 'base.nid = n.nid');
$query->addField('n', 'type', 'node_type');
// Specify additional fields from the user table.
$query->innerJoin('users', 'u', 'base.uid = u.uid');
// @todo: Move to a computed 'name' field instead.
$query->addField('u', 'name', 'registered_name');
@ -42,10 +41,8 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
*/
protected function attachLoad(&$records, $load_revision = FALSE) {
// Prepare standard comment fields.
foreach ($records as $key => $record) {
foreach ($records as &$record) {
$record->name = $record->uid ? $record->registered_name : $record->name;
$record->node_type = 'comment_node_' . $record->node_type;
$records[$key] = $record;
}
parent::attachLoad($records, $load_revision);
}
@ -53,25 +50,36 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
/**
* {@inheritdoc}
*/
public function updateNodeStatistics($nid) {
// Allow bulk updates and inserts to temporarily disable the
// maintenance of the {node_comment_statistics} table.
if (!variable_get('comment_maintain_node_statistics', TRUE)) {
public function updateEntityStatistics(CommentInterface $comment) {
// Allow bulk updates and inserts to temporarily disable the maintenance of
// the {comment_entity_statistics} table.
if (!\Drupal::state()->get('comment.maintain_entity_statistics')) {
return;
}
$count = db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND status = :status', array(
':nid' => $nid,
':status' => COMMENT_PUBLISHED,
))->fetchField();
$query = $this->database->select('comment', 'c');
$query->addExpression('COUNT(cid)');
$count = $query->condition('c.entity_id', $comment->entity_id->value)
->condition('c.entity_type', $comment->entity_type->value)
->condition('c.field_id', $comment->field_id->value)
->condition('c.status', COMMENT_PUBLISHED)
->execute()
->fetchField();
if ($count > 0) {
// Comments exist.
$last_reply = db_query_range('SELECT cid, name, changed, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
':nid' => $nid,
':status' => COMMENT_PUBLISHED,
))->fetchObject();
db_update('node_comment_statistics')
$last_reply = $this->database->select('comment', 'c')
->fields('c', array('cid', 'name', 'changed', 'uid'))
->condition('c.entity_id', $comment->entity_id->value)
->condition('c.entity_type', $comment->entity_type->value)
->condition('c.field_id', $comment->field_id->value)
->condition('c.status', COMMENT_PUBLISHED)
->orderBy('c.created', 'DESC')
->range(0, 1)
->execute()
->fetchObject();
// Use merge here because entity could be created before comment field.
$this->database->merge('comment_entity_statistics')
->fields(array(
'cid' => $last_reply->cid,
'comment_count' => $count,
@ -79,21 +87,32 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
'last_comment_name' => $last_reply->uid ? '' : $last_reply->name,
'last_comment_uid' => $last_reply->uid,
))
->condition('nid', $nid)
->key(array(
'entity_id' => $comment->entity_id->value,
'entity_type' => $comment->entity_type->value,
'field_id' => $comment->field_id->value,
))
->execute();
}
else {
// Comments do not exist.
$node = db_query('SELECT uid, created FROM {node_field_data} WHERE nid = :nid LIMIT 1', array(':nid' => $nid))->fetchObject();
db_update('node_comment_statistics')
$entity = entity_load($comment->entity_type->value, $comment->entity_id->value);
$this->database->update('comment_entity_statistics')
->fields(array(
'cid' => 0,
'comment_count' => 0,
'last_comment_timestamp' => $node->created,
// Use the created date of the entity if it's set, or default to
// REQUEST_TIME.
'last_comment_timestamp' => ($entity instanceof EntityChangedInterface) ? $entity->getChangedTime() : REQUEST_TIME,
'last_comment_name' => '',
'last_comment_uid' => $node->uid,
// @todo Use $entity->getAuthorId() after https://drupal.org/node/2078387
// Get the user ID from the entity if it's set, or default to the
// currently logged in user.
'last_comment_uid' => $entity->getPropertyDefinition('uid') ? $entity->get('uid')->value : \Drupal::currentUser()->id(),
))
->condition('nid', $nid)
->condition('entity_id', $comment->entity_id->value)
->condition('entity_type', $comment->entity_type->value)
->condition('field_id', $comment->field_id->value)
->execute();
}
}
@ -102,17 +121,27 @@ class CommentStorageController extends DatabaseStorageControllerNG implements Co
* {@inheritdoc}
*/
public function getMaxThread(EntityInterface $comment) {
return db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid->target_id))->fetchField();
$query = $this->database->select('comment', 'c')
->condition('entity_id', $comment->entity_id->value)
->condition('field_id', $comment->field_id->value)
->condition('entity_type', $comment->entity_type->value);
$query->addExpression('MAX(thread)', 'thread');
return $query->execute()
->fetchField();
}
/**
* {@inheritdoc}
*/
public function getMaxThreadPerThread(EntityInterface $comment) {
return $this->database->query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
':thread' => rtrim($comment->pid->entity->thread->value, '/') . '.%',
':nid' => $comment->nid->target_id,
))->fetchField();
$query = $this->database->select('comment', 'c')
->condition('entity_id', $comment->entity_id->value)
->condition('field_id', $comment->field_id->value)
->condition('entity_type', $comment->entity_type->value)
->condition('thread', $comment->pid->entity->thread->value . '.%', 'LIKE');
$query->addExpression('MAX(thread)', 'thread');
return $query->execute()
->fetchField();
}
/**

View File

@ -9,6 +9,7 @@ namespace Drupal\comment;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\comment\CommentInterface;
/**
* Defines a common interface for comment entity controller classes.
@ -51,19 +52,19 @@ interface CommentStorageControllerInterface extends EntityStorageControllerInter
/**
* Updates the comment statistics for a given node.
*
* The {node_comment_statistics} table has the following fields:
* - last_comment_timestamp: The timestamp of the last comment for this node,
* or the node created timestamp if no comments exist for the node.
* The {comment_entity_statistics} table has the following fields:
* - last_comment_timestamp: The timestamp of the last comment for the entity,
* or the entity created timestamp if no comments exist for the entity.
* - last_comment_name: The name of the anonymous poster for the last comment.
* - last_comment_uid: The user ID of the poster for the last comment for
* this node, or the node author's user ID if no comments exist for the
* node.
* this entity, or the entity author's user ID if no comments exist for the
* entity.
* - comment_count: The total number of approved/published comments on this
* node.
* entity.
*
* @param $nid
* The node ID.
* @param \Drupal\comment\CommentInterface $comment
* The comment being saved.
*/
public function updateNodeStatistics($nid);
public function updateEntityStatistics(CommentInterface $comment);
}

View File

@ -0,0 +1,227 @@
<?php
/**
* @file
* Contains \Drupal\comment\Controller\AdminController.
*/
namespace Drupal\comment\Controller;
use Drupal\Component\Utility\String;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\comment\CommentManager;
use Drupal\field\FieldInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Returns responses for comment module administrative routes.
*/
class AdminController extends ControllerBase implements ContainerInjectionInterface {
/**
* The field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* The comment manager service.
*
* @var \Drupal\comment\CommentManager
*/
protected $commentManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('field.info'),
$container->get('comment.manager')
);
}
/**
* Constructs an AdminController object.
*
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\comment\CommentManager $comment_manager
* The comment manager service.
*/
public function __construct(FieldInfo $field_info, CommentManager $comment_manager) {
$this->fieldInfo = $field_info;
$this->commentManager = $comment_manager;
}
/**
* Returns an overview of comment fields in use on the site.
*
* @return array
* A renderable array containing a list of comment fields, the entity
* type and bundle combinations on which they are in use and various
* operation links for configuring each field.
*/
public function overviewBundles() {
$header = array(
'field_name' => $this->t('Field name'),
'usage' => array(
'data' => $this->t('Used in'),
'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
),
'type' => $this->t('Type'),
);
// Add a column for field UI operations if the Field UI module is enabled.
$field_ui_enabled = $this->moduleHandler()->moduleExists('field_ui');
if ($field_ui_enabled) {
$header['operations'] = $this->t('Operations');
}
$entity_bundles = $this->entityManager()->getAllBundleInfo();
$entity_types = $this->entityManager()->getDefinitions();
$rows = array();
// Fetch a list of all comment fields.
$fields = $this->commentManager->getAllFields();
foreach ($fields as $entity_type => $data) {
foreach ($data as $field_name => $field_info_map) {
$field_info = $this->fieldInfo->getField($entity_type, $field_name);
// Initialize the row.
$row = array(
'class' => $field_info->get('locked') ? array('field-disabled') : array(''),
);
$bundles = $field_info->getBundles();
$sample_bundle = reset($bundles);
$sample_instance = $this->fieldInfo->getInstance($entity_type, $sample_bundle, $field_name);
$tokens = array(
'@label' => $sample_instance->label,
'@field_name' => $field_name,
);
$row['data']['field_name']['data'] = $field_info->get('locked') ? $this->t('@label (@field_name) (Locked)', $tokens) : $this->t('@label (@field_name)', $tokens);
$row['data']['usage']['data'] = array(
'#theme' => 'item_list',
'#items' => array(),
);
foreach ($field_info_map['bundles'] as $bundle) {
if (isset($entity_bundles[$entity_type][$bundle])) {
// Add the current instance.
if ($field_ui_enabled && ($route_info = $this->entityManager()->getAdminRouteInfo($entity_type, $bundle))) {
$row['data']['usage']['data']['#items'][] = $this->l($entity_bundles[$entity_type][$bundle]['label'], $route_info['route_name'], $route_info['route_parameters']);
}
else {
$row['data']['usage']['data']['#items'][] = $entity_bundles[$entity_type][$bundle]['label'];
}
}
}
$row['data']['type']['data'] = String::checkPlain($entity_types[$entity_type]['label']);
if ($field_ui_enabled) {
if ($this->currentUser()->hasPermission('administer comment fields')) {
$links['fields'] = array(
'title' => $this->t('Manage fields'),
'href' => 'admin/structure/comments/manage/' . $entity_type . '__' . $field_name . '/fields',
'weight' => 5,
);
}
if ($this->currentUser()->hasPermission('administer comment display')) {
$links['display'] = array(
'title' => $this->t('Manage display'),
'href' => 'admin/structure/comments/manage/' . $entity_type . '__' . $field_name . '/display',
'weight' => 10,
);
}
if ($this->currentUser()->hasPermission('administer comment form display')) {
$links['form_display'] = array(
'title' => $this->t('Manage form display'),
'href' => 'admin/structure/comments/manage/' . $entity_type . '__' . $field_name . '/form-display',
'weight' => 10,
);
}
$row['data']['operations']['data'] = array(
'#type' => 'operations',
'#links' => $links,
);
}
$rows[$entity_type . '__' . $field_name] = $row;
}
}
$build['overview'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('No comment forms available.'),
);
$build['#title'] = $this->t('Comment forms');
return $build;
}
/**
* Returns an overview of the entity types a comment field is attached to.
*
* @param string $field_name
* The comment field for which the overview is to be displayed.
*
* @return array
* A renderable array containing the list of entity types and bundle
* combinations on which the comment field is in use.
*/
public function bundleInfo($field_name) {
// Add a link to manage entity fields if the Field UI module is enabled.
$field_ui_enabled = $this->moduleHandler()->moduleExists('field_ui');
// @todo Provide dynamic routing to get entity type and field name.
// https://drupal.org/node/2098011.
list($entity_type, $field) = explode('__', $field_name, 2);
$field_info = $this->fieldInfo->getField($entity_type, $field);
$entity_type_info = $this->entityManager()->getDefinition($entity_type);
$entity_bundle_info = $this->entityManager()->getBundleInfo($entity_type);
$build['usage'] = array(
'#theme' => 'item_list',
'#title' => String::checkPlain($entity_type_info['label']),
'#items' => array(),
);
// Loop over all of bundles to which this comment field is attached.
foreach ($field_info->getBundles() as $bundle) {
// Add the current instance to the list of bundles.
if ($field_ui_enabled && ($route_info = $this->entityManager()->getAdminRouteInfo($entity_type, $bundle))) {
// Add a link to configure the fields on the given bundle and entity
// type combination.
$build['usage']['#items'][] = $this->l($entity_bundle_info[$bundle]['label'], $route_info['route_name'], $route_info['route_parameters']);
}
else {
// Field UI is disabled so fallback to a list of bundle labels
// instead of links to configure fields.
$build['usage']['#items'][] = String::checkPlain($entity_bundle_info[$bundle]['label']);
}
}
return $build;
}
/**
* Route title callback.
*
* @param string $field_name
* The comment field for which the overview is to be displayed.
*
* @return string
* The human readable field name.
*/
public function bundleTitle($field_name) {
return $this->commentManager->getFieldUIPageTitle($field_name);
}
}

View File

@ -8,11 +8,12 @@
namespace Drupal\comment\Controller;
use Drupal\comment\CommentInterface;
use Drupal\comment\Entity\Comment;
use Drupal\comment\CommentManager;
use Drupal\field\FieldInfo;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Node\NodeInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
@ -50,6 +51,20 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
*/
protected $currentUser;
/**
* Field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* The comment manager service.
*
* @var \Drupal\comment\CommentManager
*/
protected $commentManager;
/**
* Constructs a CommentController object.
*
@ -59,12 +74,19 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
* The CSRF token manager service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user service.
* @param \Drupal\field\FieldInfo $field_info
* Field Info service.
* @param \Drupal\comment\CommentManager $comment_manager
* The comment manager service.
*/
public function __construct(HttpKernelInterface $httpKernel, CsrfTokenGenerator $csrf_token, AccountInterface $current_user) {
public function __construct(HttpKernelInterface $httpKernel, CsrfTokenGenerator $csrf_token, AccountInterface $current_user, FieldInfo $field_info, CommentManager $comment_manager) {
$this->httpKernel = $httpKernel;
$this->csrfToken = $csrf_token;
$this->currentUser = $current_user;
$this->fieldInfo = $field_info;
$this->commentManager = $comment_manager;
}
/**
* {@inheritdoc}
*/
@ -72,7 +94,9 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
return new static(
$container->get('http_kernel'),
$container->get('csrf_token'),
$container->get('current_user')
$container->get('current_user'),
$container->get('field.info'),
$container->get('comment.manager')
);
}
@ -130,15 +154,18 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
* The comment listing set to the page on which the comment appears.
*/
public function commentPermalink(Request $request, CommentInterface $comment) {
if ($node = $comment->nid->entity) {
// Check access permissions for the node entity.
if (!$node->access('view')) {
if ($entity = $this->entityManager()->getStorageController($comment->entity_type->value)->load($comment->entity_id->value)) {
// Check access permissions for the entity.
if (!$entity->access('view')) {
throw new AccessDeniedHttpException();
}
$instance = $this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $comment->field_name->value);
// Find the current display page for this comment.
$page = comment_get_display_page($comment->id(), $node->getType());
$page = comment_get_display_page($comment->id(), $instance);
// @todo: Cleaner sub request handling.
$redirect_request = Request::create('/node/' . $node->id(), 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
$uri = $entity->uri();
$redirect_request = Request::create($uri['path'], 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
$redirect_request->query->set('page', $page);
// @todo: Convert the pager to use the request object.
$request->query->set('page', $page);
@ -147,42 +174,77 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
throw new NotFoundHttpException();
}
/**
* Redirects legacy node links to the new path.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* The node object identified by the legacy URL.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* Redirects user to new url.
*/
public function redirectNode(EntityInterface $node) {
$fields = $this->commentManager->getFields('node');
// Legacy nodes only had a single comment field, so use the first comment
// field on the entity.
if (!empty($fields) && ($field_names = array_keys($fields)) && ($field_name = reset($field_names))) {
return new RedirectResponse($this->urlGenerator()->generateFromPath('comment/reply/node/' . $node->id() . '/' . $field_name, array('absolute' => TRUE)));
}
throw new NotFoundHttpException();
}
/**
* Form constructor for the comment reply form.
*
* Both replies on the node itself and replies on other comments are
* supported. To provide context, the node or comment that is being replied on
* will be displayed along the comment reply form.
* The constructor takes care of access permissions and checks whether the
* node still accepts comments.
* There are several cases that have to be handled, including:
* - replies to comments
* - replies to entities
* - attempts to reply to entities that can no longer accept comments
* - respecting access permissions ('access comments', 'post comments', etc.)
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
* @param \Drupal\node\NodeInterface $node
* Every comment belongs to a node. This is that node.
* @param string $entity_type
* Every comment belongs to an entity. This is the type of the entity.
* @param string $entity_id
* Every comment belongs to an entity. This is the ID of the entity.
* @param string $field_name
* The field_name to which the comment belongs.
* @param int $pid
* (optional) Some comments are replies to other comments. In those cases,
* $pid is the parent comment's ID. Defaults to NULL.
* $pid is the parent comment's comment ID. Defaults to NULL.
*
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
* One of the following:
* An associative array containing:
* - An array for rendering the entity or parent comment.
* - comment_entity: If the comment is a reply to the entity.
* - comment_parent: If the comment is a reply to another comment.
* - comment_form: The comment form as a renderable array.
* - An associative array containing:
* - An array for rendering the node or parent comment.
* - comment_node: If the comment is a reply to the node.
* - An array for rendering the entity or parent comment.
* - comment_entity: If the comment is a reply to the entity.
* - comment_parent: If the comment is a reply to another comment.
* - comment_form: The comment form as a renderable array.
* - A redirect response to current node:
* - If user is not authorized to post comments.
* - If parent comment doesn't belong to current node.
* - If parent comment doesn't belong to current entity.
* - If user is not authorized to view comments.
* - If current node comments are disable.
* - If current entity comments are disable.
*/
public function getReplyForm(Request $request, NodeInterface $node, $pid = NULL) {
$uri = $node->uri();
$build = array();
$account = $this->currentUser();
public function getReplyForm(Request $request, $entity_type, $entity_id, $field_name, $pid = NULL) {
$build['#title'] = $this->t('Add new comment');
// Check if entity and field exists.
$fields = $this->commentManager->getFields($entity_type);
if (empty($fields[$field_name]) || !($entity = $this->entityManager()->getStorageController($entity_type)->load($entity_id))) {
throw new NotFoundHttpException();
}
$account = $this->currentUser();
$uri = $entity->uri();
$build = array();
// Check if the user has the proper permissions.
if (!$account->hasPermission('post comments')) {
@ -192,7 +254,8 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
// The user is not just previewing a comment.
if ($request->request->get('op') != $this->t('Preview')) {
if ($node->comment->value != COMMENT_NODE_OPEN) {
$status = $entity->{$field_name}->status;
if ($status != COMMENT_OPEN) {
drupal_set_message($this->t("This discussion is closed: you can't post new comments."), 'error');
return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
}
@ -207,7 +270,7 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
// Load the parent comment.
$comment = $this->entityManager()->getStorageController('comment')->load($pid);
// Check if the parent comment is published and belongs to the current nid.
if (($comment->status->value == COMMENT_NOT_PUBLISHED) || ($comment->nid->target_id != $node->id())) {
if (($comment->status->value == COMMENT_NOT_PUBLISHED) || ($comment->entity_id->value != $entity->id())) {
drupal_set_message($this->t('The comment you are replying to does not exist.'), 'error');
return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
}
@ -215,11 +278,15 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
$build['comment_parent'] = $this->entityManager()->getRenderController('comment')->view($comment);
}
// The comment is in response to a node.
elseif ($account->hasPermission('access content')) {
// Display the node.
$build['comment_node'] = $this->entityManager()->getRenderController('node')->view($node);
unset($build['comment_node']['#cache']);
// The comment is in response to a entity.
elseif ($entity->access('view', $account)) {
// We make sure the field value isn't set so we don't end up with a
// redirect loop.
$entity->{$field_name}->status = COMMENT_HIDDEN;
// Render array of the entity full view mode.
$build['commented_entity'] = $this->entityManager()->getRenderController($entity->entityType())->view($entity, 'full');
unset($build['commented_entity']['#cache']);
$entity->{$field_name}->status = $status;
}
}
else {
@ -228,9 +295,10 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
// Show the actual reply box.
$comment = $this->entityManager()->getStorageController('comment')->create(array(
'nid' => $node->id(),
'entity_id' => $entity->id(),
'pid' => $pid,
'node_type' => 'comment_node_' . $node->getType(),
'entity_type' => $entity->entityType(),
'field_id' => $entity->entityType() . '__' . $field_name,
));
$build['comment_form'] = $this->entityManager()->getForm($comment);
@ -243,7 +311,7 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
* @param \Symfony\Component\HttpFoundation\Request $request
* The request of the page.
*
* @return Symfony\Component\HttpFoundation\JsonResponse
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The JSON response.
*/
public function renderNewCommentsNodeLinks(Request $request) {
@ -252,6 +320,7 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
}
$nids = $request->request->get('node_ids');
$field_name = $request->request->get('field_name');
if (!isset($nids)) {
throw new NotFoundHttpException();
}
@ -261,11 +330,11 @@ class CommentController extends ControllerBase implements ContainerInjectionInte
$links = array();
foreach ($nids as $nid) {
$node = node_load($nid);
$new = comment_num_new($node->id());
$query = comment_new_page_count($node->comment_count, $new, $node);
$new = comment_num_new($node->id(), 'node');
$query = comment_new_page_count($node->{$field_name}->comment_count, $new, $node);
$links[$nid] = array(
'new_comment_count' => (int)$new,
'first_new_comment_link' => url('node/' . $node->id(), array('query' => $query, 'fragment' => 'new')),
'first_new_comment_link' => $this->urlGenerator()->generateFromPath('node/' . $node->id(), array('query' => $query, 'fragment' => 'new')),
);
}

View File

@ -37,14 +37,16 @@ use Drupal\Core\Language\Language;
* fieldable = TRUE,
* translatable = TRUE,
* render_cache = FALSE,
* route_base_path = "admin/structure/types/manage/{bundle}/comment",
* bundle_prefix = "comment_node_",
* route_base_path = "admin/structure/comments/manage/{bundle}",
* entity_keys = {
* "id" = "cid",
* "bundle" = "node_type",
* "bundle" = "field_id",
* "label" = "subject",
* "uuid" = "uuid"
* },
* bundle_keys = {
* "bundle" = "field_id"
* },
* links = {
* "canonical" = "/comment/{comment}",
* "edit-form" = "/comment/{comment}/edit"
@ -56,8 +58,6 @@ class Comment extends EntityNG implements CommentInterface {
/**
* The comment ID.
*
* @todo Rename to 'id'.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $cid;
@ -70,20 +70,32 @@ class Comment extends EntityNG implements CommentInterface {
public $uuid;
/**
* The parent comment ID if this is a reply to a comment.
*
* @todo: Rename to 'parent_id'.
* The parent comment ID if this is a reply to another comment.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $pid;
/**
* The ID of the node to which the comment is attached.
* The entity ID for the entity to which this comment is attached.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $nid;
public $entity_id;
/**
* The entity type of the entity to which this comment is attached.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $entity_type;
/**
* The field to which this comment is attached.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $field_id;
/**
* The comment language code.
@ -168,13 +180,6 @@ class Comment extends EntityNG implements CommentInterface {
*/
public $thread;
/**
* The comment node type.
*
* @var \Drupal\Core\Entity\Field\FieldInterface
*/
public $node_type;
/**
* Initialize the object. Invoked upon construction and wake up.
*/
@ -184,7 +189,8 @@ class Comment extends EntityNG implements CommentInterface {
unset($this->cid);
unset($this->uuid);
unset($this->pid);
unset($this->nid);
unset($this->entity_id);
unset($this->field_id);
unset($this->subject);
unset($this->uid);
unset($this->name);
@ -195,7 +201,7 @@ class Comment extends EntityNG implements CommentInterface {
unset($this->changed);
unset($this->status);
unset($this->thread);
unset($this->node_type);
unset($this->entity_type);
}
/**
@ -205,18 +211,6 @@ class Comment extends EntityNG implements CommentInterface {
return $this->get('cid')->value;
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
parent::preCreate($storage_controller, $values);
if (empty($values['node_type']) && !empty($values['nid'])) {
$node = node_load(is_object($values['nid']) ? $values['nid']->value : $values['nid']);
$values['node_type'] = 'comment_node_' . $node->getType();
}
}
/**
* {@inheritdoc}
*/
@ -228,10 +222,6 @@ class Comment extends EntityNG implements CommentInterface {
if (!isset($this->status->value)) {
$this->status->value = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
}
// Make sure we have a proper bundle name.
if (!isset($this->node_type->value)) {
$this->node_type->value = 'comment_node_' . $this->nid->entity->type;
}
if ($this->isNew()) {
// Add the comment to database. This next section builds the thread field.
// Also see the documentation for comment_view().
@ -288,7 +278,7 @@ class Comment extends EntityNG implements CommentInterface {
// has the lock, just move to the next integer.
do {
$thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
} while (!lock()->acquire("comment:{$this->nid->target_id}:$thread"));
} while (!lock()->acquire("comment:{$this->entity_id->value}:$thread"));
$this->threadLock = $thread;
}
if (empty($this->created->value)) {
@ -315,8 +305,8 @@ class Comment extends EntityNG implements CommentInterface {
parent::postSave($storage_controller, $update);
$this->releaseThreadLock();
// Update the {node_comment_statistics} table prior to executing the hook.
$storage_controller->updateNodeStatistics($this->nid->target_id);
// Update the {comment_entity_statistics} table prior to executing the hook.
$storage_controller->updateEntityStatistics($this);
if ($this->status->value == COMMENT_PUBLISHED) {
module_invoke_all('comment_publish', $this);
}
@ -341,8 +331,8 @@ class Comment extends EntityNG implements CommentInterface {
$child_cids = $storage_controller->getChildCids($entities);
entity_delete_multiple('comment', $child_cids);
foreach ($entities as $entity) {
$storage_controller->updateNodeStatistics($entity->nid->target_id);
foreach ($entities as $id => $entity) {
$storage_controller->updateEntityStatistics($entity);
}
}
@ -350,7 +340,9 @@ class Comment extends EntityNG implements CommentInterface {
* {@inheritdoc}
*/
public function permalink() {
$url['path'] = 'node/' . $this->nid->value;
$entity = entity_load($this->get('entity_type')->value, $this->get('entity_id')->value);
$uri = $entity->uri();
$url['path'] = $uri['path'];
$url['options'] = array('fragment' => 'comment-' . $this->id());
return $url;
@ -377,9 +369,9 @@ class Comment extends EntityNG implements CommentInterface {
'type' => 'entity_reference_field',
'settings' => array('target_type' => 'comment'),
);
$properties['nid'] = array(
'label' => t('Node ID'),
'description' => t('The ID of the node of which this comment is a reply.'),
$properties['entity_id'] = array(
'label' => t('Entity ID'),
'description' => t('The ID of the entity of which this comment is a reply.'),
'type' => 'entity_reference_field',
'settings' => array('target_type' => 'node'),
'required' => TRUE,
@ -444,12 +436,22 @@ class Comment extends EntityNG implements CommentInterface {
'description' => t("The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length."),
'type' => 'string_field',
);
$properties['node_type'] = array(
// @todo: The bundle property should be stored so it's queryable.
'label' => t('Node type'),
'description' => t("The comment node type."),
$properties['entity_type'] = array(
'label' => t('Entity type'),
'description' => t("The entity type to which this comment is attached."),
'type' => 'string_field',
'queryable' => FALSE,
);
$properties['field_id'] = array(
'label' => t('Field ID'),
'description' => t("The comment field id."),
'type' => 'string_field',
);
$properties['field_name'] = array(
'label' => t('Comment field name'),
'description' => t("The field name through which this comment was added."),
'type' => 'string_field',
'computed' => TRUE,
'class' => '\Drupal\comment\CommentFieldName',
);
return $properties;
}
@ -461,4 +463,13 @@ class Comment extends EntityNG implements CommentInterface {
return $this->changed->value;
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
if (empty($values['field_id']) && !empty($values['field_name']) && !empty($values['entity_type'])) {
$values['field_id'] = $values['entity_type'] . '__' . $values['field_name'];
}
}
}

View File

@ -7,15 +7,42 @@
namespace Drupal\comment\Form;
use Drupal\comment\CommentManager;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityNGConfirmFormBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the comment delete confirmation form.
*/
class DeleteForm extends EntityNGConfirmFormBase {
/**
* The comment manager.
*
* @var \Drupal\comment\CommentManager
*/
protected $commentManager;
/**
* Constructs a DeleteForm object.
*
* @param \Drupal\comment\CommentManager $comment_manager
* The comment manager service.
*/
public function __construct(CommentManager $comment_manager) {
$this->commentManager = $comment_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('comment.manager')
);
}
/**
* {@inheritdoc}
*/
@ -30,7 +57,8 @@ class DeleteForm extends EntityNGConfirmFormBase {
$actions = parent::actions($form, $form_state);
// @todo Convert to getCancelRoute() after http://drupal.org/node/1987778.
$actions['cancel']['#href'] = 'node/' . $this->entity->nid->target_id;
$uri = $this->commentManager->getParentEntityUri($this->entity);
$actions['cancel']['#href'] = $uri['path'];
return $actions;
}
@ -66,7 +94,8 @@ class DeleteForm extends EntityNGConfirmFormBase {
// Clear the cache so an anonymous user sees that his comment was deleted.
Cache::invalidateTags(array('content' => TRUE));
$form_state['redirect'] = "node/{$this->entity->nid->target_id}";
$uri = $this->commentManager->getParentEntityUri($this->entity);
$form_state['redirect'] = $uri['path'];
}
}

View File

@ -50,7 +50,7 @@ class CommentSelection extends SelectionBase {
// The Comment module doesn't implement any proper comment access,
// and as a consequence doesn't make sure that comments cannot be viewed
// when the user doesn't have access to the node.
$node_alias = $query->innerJoin('node_field_data', 'n', '%alias.nid = ' . $base_table . '.nid');
$node_alias = $query->innerJoin('node_field_data', 'n', '%alias.nid = ' . $base_table . '.entity_id AND ' . $base_table . ".entity_type = 'node'");
// Pass the query to the node access control.
$this->reAlterQuery($query, 'node_access', $node_alias);

View File

@ -0,0 +1,223 @@
<?php
/**
* @file
* Contains \Drupal\comment\Plugin\field\field_type\CommentItem.
*/
namespace Drupal\comment\Plugin\field\field_type;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\Annotation\FieldType;
use Drupal\field\FieldInterface;
use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase;
/**
* Plugin implementation of the 'comment' field type.
*
* @FieldType(
* id = "comment",
* label = @Translation("Comments"),
* description = @Translation("This field manages configuration and presentation of comments on an entity."),
* instance_settings = {
* "default_mode" = COMMENT_MODE_THREADED,
* "per_page" = 50,
* "form_location" = COMMENT_FORM_BELOW,
* "anonymous" = COMMENT_ANONYMOUS_MAYNOT_CONTACT,
* "subject" = 1,
* "preview" = DRUPAL_OPTIONAL,
* },
* default_widget = "comment_default",
* default_formatter = "comment_default"
* )
*/
class CommentItem extends ConfigFieldItemBase {
/**
* Definitions of the contained properties.
*
* @var array
*/
public static $propertyDefinitions;
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions() {
if (!isset(static::$propertyDefinitions)) {
static::$propertyDefinitions = array(
'status' => array(
'type' => 'integer',
'label' => t('Comment status value'),
),
'cid' => array(
'type' => 'entity_reference_field',
'label' => t('Last comment ID'),
'settings' => array(
'target_type' => 'comment',
),
),
'last_comment_timestamp' => array(
'label' => t('Last comment timestamp'),
'description' => t('The time that the last comment was created.'),
'type' => 'integer',
),
'last_comment_name' => array(
'label' => t('Last comment name'),
'description' => t('The name of the user posting the last comment.'),
'type' => 'string',
),
'last_comment_uid' => array(
'type' => 'entity_reference_field',
'label' => t('Last comment user ID'),
'settings' => array(
'target_type' => 'user',
),
),
'comment_count' => array(
'label' => t('Number of comments'),
'description' => t('The number of comments.'),
'type' => 'integer',
),
);
}
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
return array(
'columns' => array(
'status' => array(
'description' => 'Whether comments are allowed on this entity: 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'indexes' => array(),
'foreign keys' => array(),
);
}
/**
* {@inheritdoc}
*/
public function instanceSettingsForm(array $form, array &$form_state) {
$element = array();
$settings = $this->getFieldSettings();
$entity_type = $this->getEntity()->entityType();
$field_name = $this->getFieldDefinition()->getFieldName();
$element['comment'] = array(
'#type' => 'details',
'#title' => t('Comment form settings'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#bundle' => "{$entity_type}__{$field_name}",
'#process' => array(array(get_class($this), 'processSettingsElement')),
'#attributes' => array(
'class' => array('comment-instance-settings-form'),
),
'#attached' => array(
'library' => array(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' => 'select',
'#title' => t('Comments per page'),
'#default_value' => $settings['per_page'],
'#options' => _comment_per_page(),
);
$element['comment']['anonymous'] = array(
'#type' => 'select',
'#title' => t('Anonymous commenting'),
'#default_value' => $settings['anonymous'],
'#options' => array(
COMMENT_ANONYMOUS_MAYNOT_CONTACT => t('Anonymous posters may not enter their contact information'),
COMMENT_ANONYMOUS_MAY_CONTACT => t('Anonymous posters may leave their contact information'),
COMMENT_ANONYMOUS_MUST_CONTACT => t('Anonymous posters must leave their contact information'),
),
'#access' => drupal_anonymous_user()->hasPermission('post comments'),
);
$element['comment']['subject'] = array(
'#type' => 'checkbox',
'#title' => t('Allow comment title'),
'#default_value' => $settings['subject'],
);
$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'),
'#default_value' => $settings['preview'],
'#options' => array(
DRUPAL_DISABLED => t('Disabled'),
DRUPAL_OPTIONAL => t('Optional'),
DRUPAL_REQUIRED => t('Required'),
),
);
return $element;
}
/**
* {@inheritdoc}
*/
public function __get($name) {
if ($name == 'status' && !isset($this->values[$name])) {
// Get default value from field instance when no data saved in entity.
$field_default_values = $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
return $field_default_values[0]['status'];
}
else {
return parent::__get($name);
}
}
/**
* {@inheritdoc}
*/
public function isEmpty() {
// There is always a value for this field, it is one of COMMENT_OPEN,
// COMMENT_CLOSED or COMMENT_HIDDEN.
return FALSE;
}
/**
* Process callback to add submit handler for instance settings form.
*
* Attaches the required translation entity handlers for the instance which
* correlates one to one with the comment bundle.
*/
public static function processSettingsElement($element) {
// Settings should not be stored as nested.
$parents = $element['#parents'];
array_pop($parents);
$element['#parents'] = $parents;
// Add translation entity handlers.
if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
$comment_form = $element;
$comment_form_state['content_translation']['key'] = 'language_configuration';
$element += content_translation_enable_widget('comment', $element['#bundle'], $comment_form, $comment_form_state);
$element['content_translation']['#parents'] = $element['content_translation']['#array_parents'] = array(
'content_translation'
);
}
return $element;
}
}

View File

@ -0,0 +1,160 @@
<?php
/**
* @file
* Contains \Drupal\comment\Plugin\field\formatter\CommentDefaultFormatter.
*/
namespace Drupal\comment\Plugin\field\formatter;
use Drupal\comment\CommentStorageControllerInterface;
use Drupal\Core\Entity\EntityRenderControllerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\field\Annotation\FieldFormatter;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\Field\FieldInterface;
use Drupal\field\Plugin\Type\Formatter\FormatterBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a default comment formatter.
*
* @FieldFormatter(
* id = "comment_default",
* module = "comment",
* label = @Translation("Comment list"),
* field_types = {
* "comment"
* }
* )
*/
class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
/**
* The comment storage controller.
*
* @var \Drupal\comment\CommentStorageControllerInterface
*/
protected $storageController;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The comment render controller.
*
* @var \Drupal\Core\Entity\EntityRenderControllerInterface
*/
protected $renderController;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['view_mode'],
$container->get('current_user'),
$container->get('entity.manager')->getStorageController('comment'),
$container->get('entity.manager')->getRenderController('comment')
);
}
/**
* Constructs a new CommentDefaultFormatter.
*
* @param string $plugin_id
* The plugin_id for the formatter.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the formatter is associated.
* @param array $settings
* The formatter settings.
* @param string $label
* The formatter label display setting.
* @param string $view_mode
* The view mode.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\comment\CommentStorageControllerInterface
* The comment storage controller.
* @param \Drupal\Core\Entity\EntityRenderControllerInterface
* The comment render controller.
*/
public function __construct($plugin_id, array $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, AccountInterface $current_user, CommentStorageControllerInterface $comment_storage_controller, EntityRenderControllerInterface $comment_render_controller) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode);
$this->renderController = $comment_render_controller;
$this->storageController = $comment_storage_controller;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldInterface $items) {
$elements = array();
$output = array();
$field_name = $this->fieldDefinition->getFieldName();
$entity = $items->getEntity();
$status = $items->status;
if ($status != COMMENT_HIDDEN && empty($entity->in_preview) &&
// Comments are added to the search results and search index by
// 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();
// Only attempt to render comments if the entity has visible comments.
// Unpublished comments are not included in
// $entity->get($field_name)->comment_count, but unpublished comments
// should display if the user is an administrator.
if ((($entity->get($field_name)->comment_count && $this->currentUser->hasPermission('access comments')) ||
$this->currentUser->hasPermission('administer comments'))) {
$mode = $comment_settings['default_mode'];
$comments_per_page = $comment_settings['per_page'];
if ($cids = comment_get_thread($entity, $field_name, $mode, $comments_per_page)) {
$comments = $this->storageController->loadMultiple($cids);
comment_prepare_thread($comments);
$build = $this->renderController->viewMultiple($comments);
$build['pager']['#theme'] = 'pager';
$output['comments'] = $build;
}
}
// Append comment form if the comments are open and the form is set to
// display below the entity.
if ($status == COMMENT_OPEN && $comment_settings['form_location'] == COMMENT_FORM_BELOW) {
// Only show the add comment form if the user has permission.
if ($this->currentUser->hasPermission('post comments')) {
$output['comment_form'] = comment_add($entity, $field_name);
}
}
$elements[] = $output + array(
'#theme' => 'comment_wrapper__' . $entity->entityType() . '__' . $entity->bundle() . '__' . $field_name,
'#entity' => $entity,
'#display_mode' => $this->getFieldSetting('default_mode'),
'comments' => array(),
'comment_form' => array(),
);
}
return $elements;
}
}

View File

@ -0,0 +1,107 @@
<?php
/**
* @file
* Contains \Drupal\comment\Plugin\field\widget\CommentWidget.
*/
namespace Drupal\comment\Plugin\field\widget;
use Drupal\field\Annotation\FieldWidget;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\Field\FieldInterface;
use Drupal\field\Plugin\Type\Widget\WidgetBase;
/**
* Provides a default comment widget.
*
* @FieldWidget(
* id = "comment_default",
* label = @Translation("Comment"),
* field_types = {
* "comment"
* }
* )
*/
class CommentWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldInterface $items, $delta, array $element, array &$form, array &$form_state) {
$field = $this->fieldDefinition;
$entity = $items->getParent();
// Get default value from the field instance.
$field_default_values = $this->fieldDefinition->getFieldDefaultValue($entity);
$status = $items->status;
$element['status'] = array(
'#type' => 'radios',
'#title' => t('Comments'),
'#title_display' => 'invisible',
'#default_value' => $status,
'#options' => array(
COMMENT_OPEN => t('Open'),
COMMENT_CLOSED => t('Closed'),
COMMENT_HIDDEN => t('Hidden'),
),
COMMENT_OPEN => array(
'#description' => t('Users with the "Post comments" permission can post comments.'),
),
COMMENT_CLOSED => array(
'#description' => t('Users cannot post comments, but existing comments will be displayed.'),
),
COMMENT_HIDDEN => array(
'#description' => t('Comments are hidden from view.'),
),
);
// If the entity doesn't have any comments, the "hidden" option makes no
// sense, so don't even bother presenting it to the user unless this is the
// default value widget on the field settings form.
if ($element['#field_parents'] != array('default_value_input') && !$entity->get($field->getFieldName())->comment_count) {
$element['status'][COMMENT_HIDDEN]['#access'] = FALSE;
// Also adjust the description of the "closed" option.
$element['status'][COMMENT_CLOSED]['#description'] = t('Users cannot post comments.');
}
// If the advanced settings tabs-set is available (normally rendered in the
// second column on wide-resolutions), place the field as a details element
// in this tab-set.
if (isset($form['advanced'])) {
$element += array(
'#type' => 'details',
// Collapse this field when the selected value is the same as stored in
// default values for the field instance.
'#collapsed' => ($items->status == $field_default_values[0]['status']),
'#group' => 'advanced',
'#attributes' => array(
'class' => array('comment-' . drupal_html_class($element['#entity_type']) . '-settings-form'),
),
'#attached' => array(
'library' => array('comment', 'drupal.comment'),
),
);
}
return $element;
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, array &$form_state) {
// Add default values for statistics properties because we don't want to
// have them in form.
foreach ($values as &$value) {
$value += array(
'cid' => 0,
'last_comment_timestamp' => 0,
'last_comment_name' => '',
'last_comment_uid' => 0,
'comment_count' => 0,
);
}
return $values;
}
}

View File

@ -85,16 +85,23 @@ class UserUid extends ArgumentPluginBase {
public function query($group_by = FALSE) {
$this->ensureMyTable();
$subselect = $this->database->select('comment', 'c');
$subselect->addField('c', 'cid');
$subselect->condition('c.uid', $this->argument);
$subselect->where("c.nid = $this->tableAlias.nid");
// Use the table definition to correctly add this user ID condition.
if ($this->table != 'comment') {
$subselect = $this->database->select('comment', 'c');
$subselect->addField('c', 'cid');
$subselect->condition('c.uid', $this->argument);
$condition = db_or()
->condition("$this->tableAlias.uid", $this->argument, '=')
->exists($subselect);
$entity_id = $this->definition['entity_id'];
$entity_type = $this->definition['entity_type'];
$subselect->where("c.entity_id = $this->tableAlias.$entity_id");
$subselect->condition('c.entity_type', $entity_type);
$this->query->addWhere(0, $condition);
$condition = db_or()
->condition("$this->tableAlias.uid", $this->argument, '=')
->exists($subselect);
$this->query->addWhere(0, $condition);
}
}
/**

View File

@ -32,14 +32,15 @@ class Comment extends FieldPluginBase {
if (!empty($this->options['link_to_comment'])) {
$this->additional_fields['cid'] = 'cid';
$this->additional_fields['nid'] = 'nid';
$this->additional_fields['entity_id'] = 'entity_id';
$this->additional_fields['entity_type'] = 'entity_type';
}
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['link_to_comment'] = array('default' => TRUE, 'bool' => TRUE);
$options['link_to_node'] = array('default' => FALSE, 'bool' => TRUE);
$options['link_to_entity'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
@ -54,10 +55,10 @@ class Comment extends FieldPluginBase {
'#type' => 'checkbox',
'#default_value' => $this->options['link_to_comment'],
);
$form['link_to_node'] = array(
'#title' => t('Link field to the node if there is no comment.'),
$form['link_to_entity'] = array(
'#title' => t('Link field to the entity if there is no comment.'),
'#type' => 'checkbox',
'#default_value' => $this->options['link_to_node'],
'#default_value' => $this->options['link_to_entity'],
);
parent::buildOptionsForm($form, $form_state);
}
@ -65,15 +66,18 @@ class Comment extends FieldPluginBase {
protected function renderLink($data, ResultRow $values) {
if (!empty($this->options['link_to_comment'])) {
$this->options['alter']['make_link'] = TRUE;
$nid = $this->getValue($values, 'nid');
$cid = $this->getValue($values, 'cid');
if (!empty($cid)) {
$this->options['alter']['path'] = "comment/" . $cid;
$this->options['alter']['fragment'] = "comment-" . $cid;
}
// If there is no comment link to the node.
elseif ($this->options['link_to_node']) {
$this->options['alter']['path'] = "node/" . $nid;
elseif ($this->options['link_to_entity']) {
$entity_id = $this->getValue($values, 'entity_id');
$entity_type = $this->getValue($values, 'entity_type');
$entity = entity_load($entity_type, $entity_id);
$uri = $entity->uri();
$this->options['alter']['path'] = $uri['path'];
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\comment\Plugin\views\field\EntityLink.
*/
namespace Drupal\comment\Plugin\views\field;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\Component\Annotation\PluginID;
use Drupal\views\ResultRow;
/**
* Handler for showing comment module's entity links.
*
* @ingroup views_field_handlers
*
* @PluginID("comment_entity_link")
*/
class EntityLink extends FieldPluginBase {
/**
* Stores the result of node_view_multiple for all rows to reuse it later.
*
* @var array
*/
protected $build;
protected function defineOptions() {
$options = parent::defineOptions();
$options['teaser'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
public function buildOptionsForm(&$form, &$form_state) {
$form['teaser'] = array(
'#type' => 'checkbox',
'#title' => t('Show teaser-style link'),
'#default_value' => $this->options['teaser'],
'#description' => t('Show the comment link in the form used on standard entity teasers, rather than the full entity form.'),
);
parent::buildOptionsForm($form, $form_state);
}
public function query() {}
/**
* Implements \Drupal\views\Plugin\views\field\FieldPluginBase::pre_render().
*/
public function preRender(&$values) {
// Render all nodes, so you can grep the comment links.
$entities = array();
foreach ($values as $row) {
$entity = $row->_entity;
$entities[$entity->id()] = $entity;
}
if ($entities) {
$this->build = entity_view_multiple($entities, $this->options['teaser'] ? 'teaser' : 'full');
}
}
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
$entity = $this->getEntity($values);
// Only render the links, if they are defined.
return !empty($this->build[$entity->id()]['links']['comment__comment']) ? drupal_render($this->build[$entity->id()]['links']['comment__comment']) : '';
}
}

View File

@ -7,9 +7,12 @@
namespace Drupal\comment\Plugin\views\field;
use Drupal\Core\Entity\EntityManager;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\Component\Annotation\PluginID;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\views\ResultRow;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base field handler to present a link.
@ -20,10 +23,46 @@ use Drupal\views\ResultRow;
*/
class Link extends FieldPluginBase {
/**
* Entity Manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')
);
}
/**
* Constructs a Link field plugin.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager service.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['text'] = array('default' => '', 'translatable' => TRUE);
$options['link_to_node'] = array('default' => FALSE, 'bool' => TRUE);
$options['link_to_entity'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
@ -33,10 +72,10 @@ class Link extends FieldPluginBase {
'#title' => t('Text to display'),
'#default_value' => $this->options['text'],
);
$form['link_to_node'] = array(
'#title' => t('Link field to the node if there is no comment.'),
$form['link_to_entity'] = array(
'#title' => t('Link field to the entity if there is no comment.'),
'#type' => 'checkbox',
'#default_value' => $this->options['link_to_node'],
'#default_value' => $this->options['link_to_entity'],
);
parent::buildOptionsForm($form, $form_state);
}
@ -54,7 +93,6 @@ class Link extends FieldPluginBase {
protected function renderLink($data, ResultRow $values) {
$text = !empty($this->options['text']) ? $this->options['text'] : t('view');
$comment = $data;
$nid = $comment->nid;
$cid = $comment->id();
$this->options['alter']['make_link'] = TRUE;
@ -66,7 +104,11 @@ class Link extends FieldPluginBase {
}
// If there is no comment link to the node.
elseif ($this->options['link_to_node']) {
$this->options['alter']['path'] = "node/" . $nid;
$entity_id = $comment->entity_id;
$entity_type = $comment->entity_type;
$entity = $this->entityManager->getStorageController($entity_type)->load($entity_id);
$uri = $entity->uri();
$this->options['alter']['path'] = $uri['path'];
}
return $text;

View File

@ -33,11 +33,11 @@ class LinkApprove extends Link {
}
$text = !empty($this->options['text']) ? $this->options['text'] : t('approve');
$cid = $this->getValue($values, 'cid');
$comment = $this->get_entity($values);
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = "comment/" . $cid . "/approve";
$this->options['alter']['query'] = drupal_get_destination() + array('token' => drupal_get_token("comment/$cid/approve"));
$this->options['alter']['path'] = "comment/" . $comment->id() . "/approve";
$this->options['alter']['query'] = drupal_get_destination() + array('token' => drupal_get_token($this->options['alter']['path']));
return $text;
}

View File

@ -26,10 +26,10 @@ class LinkDelete extends Link {
protected function renderLink($data, ResultRow $values) {
$text = !empty($this->options['text']) ? $this->options['text'] : t('delete');
$cid = $this->getValue($values, 'cid');
$comment = $this->getEntity($values);
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = "comment/" . $cid . "/delete";
$this->options['alter']['path'] = "comment/" . $comment->id(). "/delete";
$this->options['alter']['query'] = drupal_get_destination();
return $text;

View File

@ -26,11 +26,10 @@ class LinkReply extends Link {
protected function renderLink($data, ResultRow $values) {
$text = !empty($this->options['text']) ? $this->options['text'] : t('reply');
$nid = $this->getValue($values, 'nid');
$cid = $this->getValue($values, 'cid');
$comment = $this->getEntity($values);
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = "comment/reply/" . $nid . '/' . $cid;
$this->options['alter']['path'] = "comment/reply/{$comment->entity_type->value}/{$comment->entity_id->value}/{$comment->field_name->value}/{$comment->id()}";
return $text;
}

View File

@ -26,12 +26,12 @@ class NodeComment extends FieldPluginBase {
public function render(ResultRow $values) {
$value = $this->getValue($values);
switch ($value) {
case COMMENT_NODE_HIDDEN:
case COMMENT_HIDDEN:
default:
return t('Hidden');
case COMMENT_NODE_CLOSED:
case COMMENT_CLOSED:
return t('Closed');
case COMMENT_NODE_OPEN:
case COMMENT_OPEN:
return t('Open');
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\comment\Plugin\views\field\NodeLink.
*/
namespace Drupal\comment\Plugin\views\field;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\Component\Annotation\PluginID;
use Drupal\views\ResultRow;
/**
* Handler for showing comment module's node link.
*
* @ingroup views_field_handlers
*
* @PluginID("comment_node_link")
*/
class NodeLink extends FieldPluginBase {
protected function defineOptions() {
$options = parent::defineOptions();
$options['teaser'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
public function buildOptionsForm(&$form, &$form_state) {
$form['teaser'] = array(
'#type' => 'checkbox',
'#title' => t('Show teaser-style link'),
'#default_value' => $this->options['teaser'],
'#description' => t('Show the comment link in the form used on standard node teasers, rather than the full node form.'),
);
parent::buildOptionsForm($form, $form_state);
}
public function query() {}
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
$node = $this->getEntity($values);
comment_node_view($node, $this->options['teaser'] ? 'teaser' : 'full');
// Only render the links if they are defined.
return !empty($node->content['links']['comment']) ? drupal_render($node->content['links']['comment']) : '';
}
}

View File

@ -62,9 +62,9 @@ class NodeNewComments extends Numeric {
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->additional_fields['nid'] = 'nid';
$this->additional_fields['entity_id'] = 'nid';
$this->additional_fields['type'] = 'type';
$this->additional_fields['comment_count'] = array('table' => 'node_comment_statistics', 'field' => 'comment_count');
$this->additional_fields['comment_count'] = array('table' => 'comment_entity_statistics', 'field' => 'comment_count');
}
protected function defineOptions() {
@ -111,15 +111,14 @@ class NodeNewComments extends Numeric {
}
if ($nids) {
$result = $this->database->query('SELECT n.nid, COUNT(c.cid) as num_comments FROM {node} n INNER JOIN {comment} c ON n.nid = c.nid
$result = $this->database->query("SELECT n.nid, COUNT(c.cid) as num_comments FROM {node} n INNER JOIN {comment} c ON n.nid = c.entity_id AND c.entity_type = 'node'
LEFT JOIN {history} h ON h.nid = n.nid AND h.uid = :h_uid WHERE n.nid IN (:nids)
AND c.changed > GREATEST(COALESCE(h.timestamp, :timestamp), :timestamp) AND c.status = :status GROUP BY n.nid', array(
':status' => COMMENT_PUBLISHED,
':h_uid' => $user->id(),
':nids' => $nids,
':timestamp' => HISTORY_READ_LIMIT,
));
AND c.changed > GREATEST(COALESCE(h.timestamp, :timestamp), :timestamp) AND c.status = :status GROUP BY n.nid", array(
':status' => COMMENT_PUBLISHED,
':h_uid' => $user->id(),
':nids' => $nids,
':timestamp' => HISTORY_READ_LIMIT,
));
foreach ($result as $node) {
foreach ($ids[$node->id()] as $id) {
$values[$id]->{$this->field_alias} = $node->num_comments;

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Plugin\views\field\NcsLastCommentName.
* Contains \Drupal\comment\Plugin\views\field\StatisticsLastCommentName.
*/
namespace Drupal\comment\Plugin\views\field;
@ -16,9 +16,9 @@ use Drupal\views\ResultRow;
*
* @ingroup views_field_handlers
*
* @PluginID("comment_ncs_last_comment_name")
* @PluginID("comment_ces_last_comment_name")
*/
class NcsLastCommentName extends FieldPluginBase {
class StatisticsLastCommentName extends FieldPluginBase {
public function query() {
// last_comment_name only contains data if the user is anonymous. So we
@ -28,7 +28,7 @@ class NcsLastCommentName extends FieldPluginBase {
$definition = array(
'table' => 'users',
'field' => 'uid',
'left_table' => $this->tableAlias,
'left_table' => 'comment_entity_statistics',
'left_field' => 'last_comment_uid',
'extra' => array(
array(
@ -40,8 +40,8 @@ class NcsLastCommentName extends FieldPluginBase {
);
$join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition);
// ncs_user alias so this can work with the sort handler, below.
$this->user_table = $this->query->ensureTable('ncs_users', $this->relationship, $join);
// nes_user alias so this can work with the sort handler, below.
$this->user_table = $this->query->ensureTable('ces_users', $this->relationship, $join);
$this->field_alias = $this->query->addField(NULL, "COALESCE($this->user_table.name, $this->tableAlias.$this->field)", $this->tableAlias . '_' . $this->field);

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Plugin\views\field\NcsLastUpdated.
* Contains \Drupal\comment\Plugin\views\field\StatisticsLastUpdated.
*/
namespace Drupal\comment\Plugin\views\field;
@ -15,9 +15,9 @@ use Drupal\Component\Annotation\PluginID;
*
* @ingroup views_field_handlers
*
* @PluginID("comment_ncs_last_updated")
* @PluginID("comment_ces_last_updated")
*/
class NcsLastUpdated extends Date {
class StatisticsLastUpdated extends Date {
public function query() {
$this->ensureMyTable();

View File

@ -21,9 +21,9 @@ class NodeComment extends InOperator {
public function getValueOptions() {
$this->value_options = array(
COMMENT_NODE_HIDDEN => t('Hidden'),
COMMENT_NODE_CLOSED => t('Closed'),
COMMENT_NODE_OPEN => t('Open'),
COMMENT_HIDDEN => t('Hidden'),
COMMENT_CLOSED => t('Closed'),
COMMENT_OPEN => t('Open'),
);
}

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Plugin\views\filter\NcsLastUpdated.
* Contains \Drupal\comment\Plugin\views\filter\StatisticsLastUpdated.
*/
namespace Drupal\comment\Plugin\views\filter;
@ -15,9 +15,9 @@ use Drupal\Component\Annotation\PluginID;
*
* @ingroup views_filter_handlers
*
* @PluginID("ncs_last_updated")
* @PluginID("comment_ces_last_updated")
*/
class NcsLastUpdated extends Date {
class StatisticsLastUpdated extends Date {
public function query() {
$this->ensureMyTable();

View File

@ -26,7 +26,11 @@ class UserUid extends FilterPluginBase {
$subselect = db_select('comment', 'c');
$subselect->addField('c', 'cid');
$subselect->condition('c.uid', $this->value, $this->operator);
$subselect->where("c.nid = $this->tableAlias.nid");
$entity_id = $this->definition['entity_id'];
$entity_type = $this->definition['entity_type'];
$subselect->where("c.entity_id = $this->tableAlias.$entity_id");
$subselect->condition('c.entity_type', $entity_type);
$condition = db_or()
->condition("$this->tableAlias.uid", $this->value, $this->operator)

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Plugin\views\sort\NcsLastCommentName.
* Contains \Drupal\comment\Plugin\views\sort\StatisticsLastCommentName.
*/
namespace Drupal\comment\Plugin\views\sort;
@ -16,16 +16,16 @@ use Drupal\Component\Annotation\PluginID;
*
* @ingroup views_sort_handlers
*
* @PluginID("comment_ncs_last_comment_name")
* @PluginID("comment_ces_last_comment_name")
*/
class NcsLastCommentName extends SortPluginBase {
class StatisticsLastCommentName extends SortPluginBase {
public function query() {
$this->ensureMyTable();
$definition = array(
'table' => 'users',
'field' => 'uid',
'left_table' => $this->tableAlias,
'left_table' => 'comment_entity_statistics',
'left_field' => 'last_comment_uid',
);
$join = drupal_container()->get('plugin.manager.views.join')->createInstance('standard', $definition);
@ -33,7 +33,7 @@ class NcsLastCommentName extends SortPluginBase {
// @todo this might be safer if we had an ensure_relationship rather than guessing
// the table alias. Though if we did that we'd be guessing the relationship name
// so that doesn't matter that much.
$this->user_table = $this->query->ensureTable('ncs_users', $this->relationship, $join);
$this->user_table = $this->query->ensureTable('ces_users', $this->relationship, $join);
$this->user_field = $this->query->addField($this->user_table, 'name');
// Add the field.

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Plugin\views\sort\NcsLastUpdated.
* Contains \Drupal\comment\Plugin\views\sort\StatisticsLastUpdated.
*/
namespace Drupal\comment\Plugin\views\sort;
@ -11,13 +11,13 @@ use Drupal\views\Plugin\views\sort\Date;
use Drupal\Component\Annotation\PluginID;
/**
* Sort handler for the newer of last comment / node updated.
* Sort handler for the newer of last comment / entity updated.
*
* @ingroup views_sort_handlers
*
* @PluginID("ncs_last_updated")
* @PluginID("comment_ces_last_updated")
*/
class NcsLastUpdated extends Date {
class StatisticsLastUpdated extends Date {
public function query() {
$this->ensureMyTable();

View File

@ -61,7 +61,7 @@ class Comment extends WizardPluginBase {
'table' => 'node_field_data',
'field' => 'status',
'provider' => 'node',
'relationship' => 'nid'
'relationship' => 'node',
)
);
@ -144,10 +144,10 @@ class Comment extends WizardPluginBase {
$display_options['access']['type'] = 'perm';
// Add a relationship to nodes.
$display_options['relationships']['nid']['id'] = 'nid';
$display_options['relationships']['nid']['table'] = 'comment';
$display_options['relationships']['nid']['field'] = 'nid';
$display_options['relationships']['nid']['required'] = 1;
$display_options['relationships']['node']['id'] = 'node';
$display_options['relationships']['node']['table'] = 'comment';
$display_options['relationships']['node']['field'] = 'node';
$display_options['relationships']['node']['required'] = 1;
// Remove the default fields, since we are customizing them here.
unset($display_options['fields']);

View File

@ -0,0 +1,64 @@
<?php
/**
* @file
* Contains \Drupal\comment\Routing\RouteSubscriber.
*/
namespace Drupal\comment\Routing;
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\Route;
use Drupal\Core\Extension\ModuleHandlerInterface;
/**
* Defines a route subscriber for the comment module.
*/
class RouteSubscriber implements EventSubscriberInterface {
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a RouteSubscriber object.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
*/
public function __construct(ModuleHandlerInterface $module_handler) {
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::DYNAMIC] = 'routes';
return $events;
}
/**
* If node module is present, adds the legacy /comment/{node}/reply route.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route build event.
*/
public function routes(RouteBuildEvent $event) {
$collection = $event->getRouteCollection();
if ($this->moduleHandler->moduleExists('node')) {
$route = new Route(
"/comment/{node}/reply",
array('_controller' => 'Drupal\comment\Controller\CommentController::redirectNode'),
array('_entity_access' => 'node.view')
);
$collection->add('comment_node_redirect', $route);
}
}
}

View File

@ -2,7 +2,7 @@
/**
* @file
* Definition of Drupal\comment\Tests\CommentApprovalTest.
* Contains \Drupal\comment\Tests\CommentAdminTest.
*/
namespace Drupal\comment\Tests;
@ -10,11 +10,11 @@ namespace Drupal\comment\Tests;
/**
* Tests comment approval functionality.
*/
class CommentApprovalTest extends CommentTestBase {
class CommentAdminTest extends CommentTestBase {
public static function getInfo() {
return array(
'name' => 'Comment approval',
'description' => 'Test comment approval functionality.',
'name' => 'Comment admin',
'description' => 'Test comment admin functionality.',
'group' => 'Comment',
);
}
@ -47,7 +47,14 @@ class CommentApprovalTest extends CommentTestBase {
// Get unapproved comment id.
$this->drupalLogin($this->admin_user);
$anonymous_comment4 = $this->getUnapprovedComment($subject);
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'node_type' => '', 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->id()));
$anonymous_comment4 = entity_create('comment', array(
'cid' => $anonymous_comment4,
'subject' => $subject,
'comment_body' => $body,
'entity_id' => $this->node->id(),
'entity_type' => 'node',
'field_name' => 'comment'
));
$this->drupalLogout();
$this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
@ -111,7 +118,14 @@ class CommentApprovalTest extends CommentTestBase {
// Get unapproved comment id.
$this->drupalLogin($this->admin_user);
$anonymous_comment4 = $this->getUnapprovedComment($subject);
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'node_type' => '', 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->id()));
$anonymous_comment4 = entity_create('comment', array(
'cid' => $anonymous_comment4,
'subject' => $subject,
'comment_body' => $body,
'entity_id' => $this->node->id(),
'entity_type' => 'node',
'field_name' => 'comment'
));
$this->drupalLogout();
$this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
@ -129,4 +143,25 @@ class CommentApprovalTest extends CommentTestBase {
$this->drupalGet('node/' . $this->node->id());
$this->assertTrue($this->commentExists($anonymous_comment4), 'Anonymous comment visible.');
}
/**
* Tests comment bundle admin.
*/
public function testCommentAdmin() {
// Login.
$this->drupalLogin($this->admin_user);
// Browse to comment bundle overview.
$this->drupalGet('admin/structure/comments');
$this->assertResponse(200);
// Make sure titles visible.
$this->assertText('Field name');
$this->assertText('Used in');
// Manage fields.
$this->clickLink('Manage fields');
$this->assertResponse(200);
// Make sure comment_body field is shown.
$this->assertText('comment_body');
// Rest from here on in is field_ui.
}
}

View File

@ -59,7 +59,7 @@ class CommentAnonymousTest extends CommentTestBase {
$this->drupalLogout();
// Post anonymous comment with contact info (optional).
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertTrue($this->commentContactInfoAvailable(), 'Contact information available.');
$anonymous_comment2 = $this->postComment($this->node, $this->randomName(), $this->randomName());
@ -72,7 +72,7 @@ class CommentAnonymousTest extends CommentTestBase {
'subject' => $this->randomName(),
'comment_body[0][value]' => $this->randomName(),
);
$this->drupalPostForm('comment/reply/' . $this->node->id(), $edit, t('Save'));
$this->drupalPostForm('comment/reply/node/' . $this->node->id() . '/comment', $edit, t('Save'));
$this->assertText(t('The name you used belongs to a registered user.'));
// Require contact info.
@ -81,7 +81,7 @@ class CommentAnonymousTest extends CommentTestBase {
$this->drupalLogout();
// Try to post comment with contact info (required).
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertTrue($this->commentContactInfoAvailable(), 'Contact information available.');
$anonymous_comment3 = $this->postComment($this->node, $this->randomName(), $this->randomName(), TRUE);
@ -135,7 +135,7 @@ class CommentAnonymousTest extends CommentTestBase {
$this->assertNoLink('Add new comment', 'Link to add comment was found.');
// Attempt to view node-comment form while disallowed.
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertText('You are not authorized to post comments', 'Error attempting to post comment.');
$this->assertNoFieldByName('subject', '', 'Subject field not found.');
$this->assertNoFieldByName('comment_body[0][value]', '', 'Comment field not found.');
@ -160,7 +160,7 @@ class CommentAnonymousTest extends CommentTestBase {
$this->assertFieldByName('subject', '', 'Subject field found.');
$this->assertFieldByName('comment_body[0][value]', '', 'Comment field found.');
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $anonymous_comment3->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $anonymous_comment3->id());
$this->assertText('You are not authorized to view comments', 'Error attempting to post reply.');
$this->assertNoText($author_name, 'Comment not displayed.');
}

View File

@ -51,8 +51,9 @@ class CommentCSSTest extends CommentTestBase {
// Add a comment.
$comment = entity_create('comment', array(
'nid' => $node->id(),
'node_type' => 'node_type_' . $node->bundle(),
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => $case['comment_uid'],
'status' => $case['comment_status'],
'subject' => $this->randomName(),

View File

@ -34,30 +34,29 @@ class CommentFieldsTest extends CommentTestBase {
// Do not make assumptions on default node types created by the test
// installation profile, and create our own.
$this->drupalCreateContentType(array('type' => 'test_node_type'));
$this->container->get('comment.manager')->addDefaultField('node', 'test_node_type');
// Check that the 'comment_body' field is present on all comment bundles.
$instances = field_info_instances('comment');
foreach (node_type_get_types() as $type_name => $info) {
$this->assertTrue(isset($instances['comment_node_' . $type_name]['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name)));
// Check that the 'comment_body' field is present on the comment bundle.
$instance = $this->container->get('field.info')->getInstance('comment', 'node__comment', 'comment_body');
$this->assertTrue(!empty($instance), 'The comment_body field is added when a comment bundle is created');
// Delete the instance along the way.
$instances['comment_node_' . $type_name]['comment_body']->delete();
}
$instance->delete();
// Check that the 'comment_body' field is deleted.
$field = field_info_field('comment', 'comment_body');
$field = $this->container->get('field.info')->getField('comment', 'comment_body');
$this->assertTrue(empty($field), 'The comment_body field was deleted');
// Create a new content type.
$type_name = 'test_node_type_2';
$this->drupalCreateContentType(array('type' => $type_name));
$this->container->get('comment.manager')->addDefaultField('node', $type_name);
// Check that the 'comment_body' field exists and has an instance on the
// new comment bundle.
$field = field_info_field('comment', 'comment_body');
$field = $this->container->get('field.info')->getField('comment', 'comment_body');
$this->assertTrue($field, 'The comment_body field exists');
$instances = field_info_instances('comment');
$this->assertTrue(isset($instances['comment_node_' . $type_name]['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name)));
$instances = $this->container->get('field.info')->getInstances('comment');
$this->assertTrue(isset($instances['node__comment']['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name)));
}
/**
@ -68,6 +67,19 @@ class CommentFieldsTest extends CommentTestBase {
$this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
$this->drupalLogin($this->admin_user);
// Drop default comment field added in CommentTestBase::setup().
entity_load('field_entity', 'node.comment')->delete();
if ($field = $this->container->get('field.info')->getField('node', 'comment_node_forum')) {
$field->delete();
}
// Purge field data now to allow comment module to be uninstalled once the
// field has been deleted.
field_purge_batch(10);
// Call again as field_purge_batch() won't remove both the instances and
// field in a single pass.
field_purge_batch(10);
// Disable the comment module.
$edit = array();
$edit['uninstall[comment]'] = TRUE;
@ -89,6 +101,7 @@ class CommentFieldsTest extends CommentTestBase {
$this->assertTrue($this->container->get('module_handler')->moduleExists('comment'), 'Comment module enabled.');
// Create nodes of each type.
$this->container->get('comment.manager')->addDefaultField('node', 'book');
$book_node = $this->drupalCreateNode(array('type' => 'book'));
$this->drupalLogout();
@ -108,7 +121,7 @@ class CommentFieldsTest extends CommentTestBase {
// Disable text processing for comments.
$this->drupalLogin($this->admin_user);
$edit = array('instance[settings][text_processing]' => 0);
$this->drupalPostForm('admin/structure/types/manage/article/comment/fields/comment.comment_node_article.comment_body', $edit, t('Save settings'));
$this->drupalPostForm('admin/structure/comments/manage/node__comment/fields/comment.node__comment.comment_body', $edit, t('Save settings'));
// Post a comment without an explicit subject.
$this->drupalLogin($this->web_user);

View File

@ -29,7 +29,7 @@ class CommentInterfaceTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(FALSE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Post comment #1 without subject or preview.
@ -89,23 +89,40 @@ class CommentInterfaceTest extends CommentTestBase {
// Reply to comment #2 creating comment #3 with optional preview and no
// subject though field enabled.
$this->drupalLogin($this->web_user);
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment->id());
// Deliberately use the wrong url to test
// \Drupal\comment\Controller\CommentController::redirectNode().
$this->drupalGet('comment/' . $this->node->id() . '/reply');
// Verify we were correctly redirected.
$this->assertUrl(url('comment/reply/node/' . $this->node->id() . '/comment', array('absolute' => TRUE)));
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment->id());
$this->assertText($subject_text, 'Individual comment-reply subject found.');
$this->assertText($comment_text, 'Individual comment-reply body found.');
$reply = $this->postComment(NULL, $this->randomName(), '', TRUE);
$reply_loaded = comment_load($reply->id());
$this->assertTrue($this->commentExists($reply, TRUE), 'Reply found.');
$this->assertEqual($comment->id(), $reply_loaded->pid->target_id, 'Pid of a reply to a comment is set correctly.');
$this->assertEqual(rtrim($comment->thread->value, '/') . '.00/', $reply_loaded->thread->value, 'Thread of reply grows correctly.');
// Check the thread of reply grows correctly.
$this->assertEqual(rtrim($comment->thread->value, '/') . '.00/', $reply_loaded->thread->value);
// Second reply to comment #3 creating comment #4.
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment->id());
$this->assertText($subject_text, 'Individual comment-reply subject found.');
$this->assertText($comment_text, 'Individual comment-reply body found.');
// Second reply to comment #2 creating comment #4.
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment->id());
$this->assertText($comment->subject->value, 'Individual comment-reply subject found.');
$this->assertText($comment->comment_body->value, 'Individual comment-reply body found.');
$reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
$reply_loaded = comment_load($reply->id());
$this->assertTrue($this->commentExists($reply, TRUE), 'Second reply found.');
$this->assertEqual(rtrim($comment->thread->value, '/') . '.01/', $reply_loaded->thread->value, 'Thread of second reply grows correctly.');
// Check the thread of second reply grows correctly.
$this->assertEqual(rtrim($comment->thread->value, '/') . '.01/', $reply_loaded->thread->value);
// Reply to comment #4 creating comment #5.
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $reply_loaded->id());
$this->assertText($reply_loaded->subject->value, 'Individual comment-reply subject found.');
$this->assertText($reply_loaded->comment_body->value, 'Individual comment-reply body found.');
$reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
$reply_loaded = comment_load($reply->id());
$this->assertTrue($this->commentExists($reply, TRUE), 'Second reply found.');
// Check the thread of reply to second reply grows correctly.
$this->assertEqual(rtrim($comment->thread->value, '/') . '.01.00/', $reply_loaded->thread->value);
// Edit reply.
$this->drupalGet('comment/' . $reply->id() . '/edit');
@ -116,34 +133,34 @@ class CommentInterfaceTest extends CommentTestBase {
$this->setCommentsPerPage(2);
$comment_new_page = $this->postComment($this->node, $this->randomName(), $this->randomName(), TRUE);
$this->assertTrue($this->commentExists($comment_new_page), 'Page one exists. %s');
$this->drupalGet('node/' . $this->node->id(), array('query' => array('page' => 1)));
$this->drupalGet('node/' . $this->node->id(), array('query' => array('page' => 2)));
$this->assertTrue($this->commentExists($reply, TRUE), 'Page two exists. %s');
$this->setCommentsPerPage(50);
// Attempt to reply to an unpublished comment.
$reply_loaded->status->value = COMMENT_NOT_PUBLISHED;
$reply_loaded->save();
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $reply_loaded->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $reply_loaded->id());
$this->assertText(t('The comment you are replying to does not exist.'), 'Replying to an unpublished comment');
// Attempt to post to node with comments disabled.
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_HIDDEN));
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => array(array('status' => COMMENT_HIDDEN))));
$this->assertTrue($this->node, 'Article node created.');
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertText('This discussion is closed', 'Posting to node with comments disabled');
$this->assertNoField('edit-comment', 'Comment body field found.');
// Attempt to post to node with read-only comments.
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_CLOSED));
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => array(array('status' => COMMENT_CLOSED))));
$this->assertTrue($this->node, 'Article node created.');
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertText('This discussion is closed', 'Posting to node with comments read-only');
$this->assertNoField('edit-comment', 'Comment body field found.');
// Attempt to post to node with comments enabled (check field names etc).
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_OPEN));
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => array(array('status' => COMMENT_OPEN))));
$this->assertTrue($this->node, 'Article node created.');
$this->drupalGet('comment/reply/' . $this->node->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$this->assertNoText('This discussion is closed', 'Posting to node with comments enabled');
$this->assertField('edit-comment-body-0-value', 'Comment body field found.');

View File

@ -69,6 +69,9 @@ class CommentLanguageTest extends WebTestBase {
$edit = array('preferred_langcode' => 'fr');
$this->drupalPostForm("user/" . $admin_user->id() . "/edit", $edit, t('Save'));
// Create comment field on article.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
// Make comment body translatable.
$field = field_info_field('comment', 'comment_body');
$field['translatable'] = TRUE;
@ -95,6 +98,7 @@ class CommentLanguageTest extends WebTestBase {
'title' => $title,
'body[0][value]' => $this->randomName(),
'langcode' => $node_langcode,
'comment[0][status]' => COMMENT_OPEN,
);
$this->drupalPostForm("node/add/article", $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($title);
@ -114,7 +118,9 @@ class CommentLanguageTest extends WebTestBase {
// Check that comment language matches the current content language.
$cid = db_select('comment', 'c')
->fields('c', array('cid'))
->condition('nid', $node->id())
->condition('entity_id', $node->id())
->condition('entity_type', 'node')
->condition('field_id', 'node__comment')
->orderBy('cid', 'DESC')
->range(0, 1)
->execute()

View File

@ -67,7 +67,7 @@ class CommentLinksTest extends CommentTestBase {
// 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(COMMENT_NODE_OPEN, COMMENT_NODE_CLOSED, COMMENT_NODE_HIDDEN),
//'comments' => array(COMMENT_OPEN, COMMENT_CLOSED, COMMENT_HIDDEN),
//// COMMENT_ANONYMOUS_MUST_CONTACT is irrelevant for this test.
//'contact ' => array(COMMENT_ANONYMOUS_MAY_CONTACT, COMMENT_ANONYMOUS_MAYNOT_CONTACT),
);
@ -94,8 +94,7 @@ class CommentLinksTest extends CommentTestBase {
* USER_REGISTER_VISITORS.
* - contact: COMMENT_ANONYMOUS_MAY_CONTACT or
* COMMENT_ANONYMOUS_MAYNOT_CONTACT.
* - comments: COMMENT_NODE_OPEN, COMMENT_NODE_CLOSED, or
* COMMENT_NODE_HIDDEN.
* - comments: COMMENT_OPEN, COMMENT_CLOSED, or COMMENT_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
@ -116,7 +115,7 @@ class CommentLinksTest extends CommentTestBase {
'form' => COMMENT_FORM_BELOW,
'user_register' => USER_REGISTER_VISITORS,
'contact' => COMMENT_ANONYMOUS_MAY_CONTACT,
'comments' => COMMENT_NODE_OPEN,
'comments' => COMMENT_OPEN,
'access comments' => 0,
'post comments' => 0,
// Enabled by default, because it's irrelevant for this test.
@ -142,8 +141,9 @@ class CommentLinksTest extends CommentTestBase {
// $this->postComment() relies on actual user permissions.
$comment = entity_create('comment', array(
'cid' => NULL,
'nid' => $this->node->id(),
'node_type' => $this->node->getType(),
'entity_id' => $this->node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'pid' => 0,
'uid' => 0,
'status' => COMMENT_PUBLISHED,
@ -163,9 +163,9 @@ class CommentLinksTest extends CommentTestBase {
}
// Change comment settings.
variable_set('comment_form_location_' . $this->node->getType(), $info['form']);
variable_set('comment_anonymous_' . $this->node->getType(), $info['contact']);
if ($this->node->comment->value != $info['comments']) {
$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();
}
@ -190,9 +190,9 @@ class CommentLinksTest extends CommentTestBase {
COMMENT_ANONYMOUS_MUST_CONTACT => 'required',
);
$t_comments = array(
COMMENT_NODE_OPEN => 'open',
COMMENT_NODE_CLOSED => 'closed',
COMMENT_NODE_HIDDEN => 'hidden',
COMMENT_OPEN => 'open',
COMMENT_CLOSED => 'closed',
COMMENT_HIDDEN => 'hidden',
);
$verbose = $info;
$verbose['form'] = $t_form[$info['form']];
@ -291,12 +291,12 @@ class CommentLinksTest extends CommentTestBase {
// 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/$nid#comment-form", 0, 'Comment form link destination is on a 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/$nid#comment-form");
$this->assertNoLinkByHref("comment/reply/node/$nid/comment#comment-form");
}
}

View File

@ -46,6 +46,7 @@ class CommentNewIndicatorTest extends CommentTestBase {
for ($i = 0; $i < count($node_ids); $i++) {
$post['node_ids[' . $i . ']'] = $node_ids[$i];
}
$post['field_name'] = 'comment';
// Serialize POST values.
foreach ($post as $key => $value) {
@ -75,7 +76,6 @@ class CommentNewIndicatorTest extends CommentTestBase {
// Test if the right links are displayed when no comment is present for the
// node.
$this->drupalLogin($this->admin_user);
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'comment' => COMMENT_NODE_OPEN));
$this->drupalGet('node');
$this->assertNoLink(t('@count comments', array('@count' => 0)));
$this->assertLink(t('Read more'));
@ -89,8 +89,9 @@ class CommentNewIndicatorTest extends CommentTestBase {
// comment settings so use $comment->save() to avoid complex setup.
$comment = entity_create('comment', array(
'cid' => NULL,
'nid' => $this->node->id(),
'node_type' => $this->node->getType(),
'entity_id' => $this->node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'pid' => 0,
'uid' => $this->loggedInUser->id(),
'status' => COMMENT_PUBLISHED,
@ -110,6 +111,7 @@ class CommentNewIndicatorTest extends CommentTestBase {
// node received a comment after the user last viewed it, and hence it would
// perform an HTTP request to render the "new comments" node link.
$this->assertIdentical(1, count($this->xpath('//*[@data-history-node-last-comment-timestamp="' . $comment->changed->value . '"]')), '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.');
$response = $this->renderNewCommentsNodeLinks(array($this->node->id()));
$this->assertResponse(200);
$json = drupal_json_decode($response);

View File

@ -57,7 +57,7 @@ class CommentNodeAccessTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Post comment.
@ -73,7 +73,7 @@ class CommentNodeAccessTest extends CommentTestBase {
$this->assertText($comment_text, 'Individual comment body found.');
// Reply to comment, creating second comment.
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment->id());
$reply_text = $this->randomName();
$reply_subject = $this->randomName();
$reply = $this->postComment(NULL, $reply_text, $reply_subject, TRUE);

View File

@ -29,5 +29,13 @@ class CommentNodeChangesTest extends CommentTestBase {
$this->assertTrue($comment->id(), 'The comment could be loaded.');
$this->node->delete();
$this->assertFalse(comment_load($comment->id()), 'The comment could not be loaded after the node was deleted.');
// Make sure the comment field and all its instances are deleted when node
// type is deleted.
$this->assertNotNull(entity_load('field_entity', 'node.comment'), 'Comment field exists');
$this->assertNotNull(entity_load('field_instance', 'node.article.comment'), 'Comment instance exists');
// Delete the node type.
entity_delete_multiple('node_type', array($this->node->bundle()));
$this->assertNull(entity_load('field_entity', 'node.comment'), 'Comment field deleted');
$this->assertNull(entity_load('field_instance', 'node.article.comment'), 'Comment instance deleted');
}
}

View File

@ -0,0 +1,385 @@
<?php
/**
* @file
* Contains \Drupal\comment\Tests\CommentNonNodeTest.
*/
namespace Drupal\comment\Tests;
use Drupal\comment\CommentInterface;
use Drupal\simpletest\WebTestBase;
use Drupal\Core\Entity\EntityInterface;
/**
* Tests basic comment functionality against the entity_test_render entity type.
*/
class CommentNonNodeTest extends WebTestBase {
public static $modules = array('comment', 'user', 'field_ui', 'entity_test');
/**
* An administrative user with permission to configure comment settings.
*
* @var \Drupal\user\UserInterface
*/
protected $admin_user;
public static function getInfo() {
return array(
'name' => 'Comment non-node tests',
'description' => 'Test commenting on a test entity.',
'group' => 'Comment',
);
}
function setUp() {
parent::setUp();
// Create a bundle for entity_test_render.
entity_test_create_bundle('entity_test_render', 'Entity Test Render', 'entity_test_render');
// Create comment field on entity_test_render bundle.
$this->container->get('comment.manager')->addDefaultField('entity_test_render', 'entity_test_render');
// Create test user.
$this->admin_user = $this->drupalCreateUser(array(
'administer comments',
'skip comment approval',
'post comments',
'access comments',
'view test entity',
'administer entity_test content',
));
// Enable anonymous and authenticated user comments.
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
'access comments',
'post comments',
'skip comment approval',
));
user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array(
'access comments',
'post comments',
'skip comment approval',
));
// Create a test entity.
$random_label = $this->randomName();
$data = array('type' => 'entity_test_render', 'name' => $random_label);
$this->entity = entity_create('entity_test_render', $data);
$this->entity->save();
}
/**
* Posts a comment.
*
* @param \Drupal\Core\Entity\EntityInterface|null $entity
* Entity to post comment on or NULL to post to the previously loaded page.
* @param $comment
* Comment body.
* @param $subject
* Comment subject.
* @param $contact
* Set to NULL for no contact info, TRUE to ignore success checking, and
* array of values to set contact info.
*/
function postComment(EntityInterface $entity, $comment, $subject = '', $contact = NULL) {
$edit = array();
$edit['comment_body[0][value]'] = $comment;
$instance = $this->container->get('field.info')->getInstance('entity_test_render', 'entity_test_render', 'comment');
$preview_mode = $instance['settings']['preview'];
$subject_mode = $instance['settings']['subject'];
// Must get the page before we test for fields.
if ($entity !== NULL) {
$this->drupalGet('comment/reply/entity_test_render/' . $entity->id() . '/comment');
}
if ($subject_mode == TRUE) {
$edit['subject'] = $subject;
}
else {
$this->assertNoFieldByName('subject', '', 'Subject field not found.');
}
if ($contact !== NULL && is_array($contact)) {
$edit += $contact;
}
switch ($preview_mode) {
case DRUPAL_REQUIRED:
// Preview required so no save button should be found.
$this->assertNoFieldByName('op', t('Save'), 'Save button not found.');
$this->drupalPostForm(NULL, $edit, t('Preview'));
// Don't break here so that we can test post-preview field presence and
// function below.
case DRUPAL_OPTIONAL:
$this->assertFieldByName('op', t('Preview'), 'Preview button found.');
$this->assertFieldByName('op', t('Save'), 'Save button found.');
$this->drupalPostForm(NULL, $edit, t('Save'));
break;
case DRUPAL_DISABLED:
$this->assertNoFieldByName('op', t('Preview'), 'Preview button not found.');
$this->assertFieldByName('op', t('Save'), 'Save button found.');
$this->drupalPostForm(NULL, $edit, t('Save'));
break;
}
$match = array();
// Get comment ID
preg_match('/#comment-([0-9]+)/', $this->getURL(), $match);
// Get comment.
if ($contact !== TRUE) { // If true then attempting to find error message.
if ($subject) {
$this->assertText($subject, 'Comment subject posted.');
}
$this->assertText($comment, 'Comment body posted.');
$this->assertTrue((!empty($match) && !empty($match[1])), 'Comment ID found.');
}
if (isset($match[1])) {
return entity_load('comment', $match[1]);
}
}
/**
* Checks current page for specified comment.
*
* @param \Drupal\comment\CommentInterface $comment
* The comment object.
* @param boolean $reply
* Boolean indicating whether the comment is a reply to another comment.
*
* @return boolean
* Boolean indicating whether the comment was found.
*/
function commentExists(CommentInterface $comment = NULL, $reply = FALSE) {
if ($comment) {
$regex = '/' . ($reply ? '<div class="indented">(.*?)' : '');
$regex .= '<a id="comment-' . $comment->id() . '"(.*?)'; // Comment anchor.
$regex .= $comment->subject->value . '(.*?)'; // Match subject.
$regex .= $comment->comment_body->value . '(.*?)'; // Match comment.
$regex .= '/s';
return (boolean)preg_match($regex, $this->drupalGetContent());
}
else {
return FALSE;
}
}
/**
* Checks whether the commenter's contact information is displayed.
*
* @return boolean
* Contact info is available.
*/
function commentContactInfoAvailable() {
return preg_match('/(input).*?(name="name").*?(input).*?(name="mail").*?(input).*?(name="homepage")/s', $this->drupalGetContent());
}
/**
* Performs the specified operation on the specified comment.
*
* @param object $comment
* Comment to perform operation on.
* @param string $operation
* Operation to perform.
* @param boolean $aproval
* Operation is found on approval page.
*/
function performCommentOperation($comment, $operation, $approval = FALSE) {
$edit = array();
$edit['operation'] = $operation;
$edit['comments[' . $comment->id() . ']'] = TRUE;
$this->drupalPostForm('admin/content/comment' . ($approval ? '/approval' : ''), $edit, t('Update'));
if ($operation == 'delete') {
$this->drupalPostForm(NULL, array(), t('Delete comments'));
$this->assertRaw(format_plural(1, 'Deleted 1 comment.', 'Deleted @count comments.'), format_string('Operation "@operation" was performed on comment.', array('@operation' => $operation)));
}
else {
$this->assertText(t('The update has been performed.'), format_string('Operation "@operation" was performed on comment.', array('@operation' => $operation)));
}
}
/**
* Gets the comment ID for an unapproved comment.
*
* @param string $subject
* Comment subject to find.
*
* @return integer
* Comment ID.
*/
function getUnapprovedComment($subject) {
$this->drupalGet('admin/content/comment/approval');
preg_match('/href="(.*?)#comment-([^"]+)"(.*?)>(' . $subject . ')/', $this->drupalGetContent(), $match);
return $match[2];
}
/**
* Tests anonymous comment functionality.
*/
function testCommentFunctionality() {
$limited_user = $this->drupalCreateUser(array(
'administer entity_test_render fields'
));
$this->drupalLogin($limited_user);
// Test that default field exists.
$this->drupalGet('admin/structure/entity-test-render/manage/entity_test_render/fields');
$this->assertText(t('Comment settings'));
$this->assertLinkByHref('admin/structure/entity-test-render/manage/entity_test_render/fields/entity_test_render.entity_test_render.comment');
// Test widget hidden option is not visible when there's no comments.
$this->drupalGet('admin/structure/entity-test-render/manage/entity_test_render/entity-test-render/fields/entity_test_render.entity_test_render.comment');
$this->assertNoField('edit-default-value-input-comment-und-0-status-0');
$this->drupalLogin($this->admin_user);
// Post a comment.
$comment1 = $this->postComment($this->entity, $this->randomName(), $this->randomName());
$this->assertTrue($this->commentExists($comment1), 'Comment on test entity exists.');
// Assert the breadcrumb is valid.
$this->drupalGet('comment/reply/entity_test_render/' . $this->entity->id() . '/comment');
$this->assertLink($this->entity->label());
// Unpublish the comment.
$this->performCommentOperation($comment1, 'unpublish');
$this->drupalGet('admin/content/comment/approval');
$this->assertRaw('comments[' . $comment1->id() . ']', 'Comment was unpublished.');
// Publish the comment.
$this->performCommentOperation($comment1, 'publish', TRUE);
$this->drupalGet('admin/content/comment');
$this->assertRaw('comments[' . $comment1->id() . ']', 'Comment was published.');
// Delete the comment.
$this->performCommentOperation($comment1, 'delete');
$this->drupalGet('admin/content/comment');
$this->assertNoRaw('comments[' . $comment1->id() . ']', 'Comment was deleted.');
// Post another comment.
$comment1 = $this->postComment($this->entity, $this->randomName(), $this->randomName());
$this->assertTrue($this->commentExists($comment1), 'Comment on test entity exists.');
// Check that the comment was found.
$this->drupalGet('admin/content/comment');
$this->assertRaw('comments[' . $comment1->id() . ']', 'Comment was published.');
// Check that entity access applies to administrative page.
$this->assertText($this->entity->label(), 'Name of commented account found.');
$limited_user = $this->drupalCreateUser(array(
'administer comments',
));
$this->drupalLogin($limited_user);
$this->drupalGet('admin/content/comment');
$this->assertNoText($this->entity->label(), 'No commented account name found.');
$this->drupalLogout();
// Deny anonymous users access to comments.
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
'access comments' => FALSE,
'post comments' => FALSE,
'skip comment approval' => FALSE,
'view test entity' => TRUE,
));
// Attempt to view comments while disallowed.
$this->drupalGet('entity-test-render/' . $this->entity->id());
$this->assertNoPattern('@<h2[^>]*>Comments</h2>@', 'Comments were not displayed.');
$this->assertNoLink('Add new comment', 'Link to add comment was found.');
// Attempt to view test entity comment form while disallowed.
$this->drupalGet('comment/reply/entity_test_render/' . $this->entity->id() . '/comment');
$this->assertText('You are not authorized to post comments', 'Error attempting to post comment.');
$this->assertNoFieldByName('subject', '', 'Subject field not found.');
$this->assertNoFieldByName('comment_body[0][value]', '', 'Comment field not found.');
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
'access comments' => TRUE,
'post comments' => FALSE,
'view test entity' => TRUE,
'skip comment approval' => FALSE,
));
// We've changed role permissions, so need to reset render cache.
// @todo Revisit after https://drupal.org/node/2099105
\Drupal::entityManager()->getRenderController('entity_test_render')->resetCache(array($this->entity->id()));
$this->drupalGet('entity-test-render/' . $this->entity->id());
$this->assertPattern('@<h2[^>]*>Comments</h2>@', 'Comments were displayed.');
$this->assertLink('Log in', 0, 'Link to log in was found.');
$this->assertLink('register', 0, 'Link to register was found.');
$this->assertNoFieldByName('subject', '', 'Subject field not found.');
$this->assertNoFieldByName('comment_body[0][value]', '', 'Comment field not found.');
// Test the combination of anonymous users being able to post, but not view
// comments, to ensure that access to post comments doesn't grant access to
// view them.
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
'access comments' => FALSE,
'post comments' => TRUE,
'skip comment approval' => TRUE,
'view test entity' => TRUE,
));
// We've changed role permissions, so need to reset render cache.
// @todo Revisit after https://drupal.org/node/2099105
\Drupal::entityManager()->getRenderController('entity_test_render')->resetCache(array($this->entity->id()));
$this->drupalGet('entity-test-render/' . $this->entity->id());
$this->assertNoPattern('@<h2[^>]*>Comments</h2>@', 'Comments were not displayed.');
$this->assertFieldByName('subject', '', 'Subject field found.');
$this->assertFieldByName('comment_body[0][value]', '', 'Comment field found.');
$this->drupalGet('comment/reply/entity_test_render/' . $this->entity->id() . '/comment/' . $comment1->id());
$this->assertText('You are not authorized to view comments');
$this->assertNoText($comment1->subject->value, 'Comment not displayed.');
// Test comment field widget changes.
$limited_user = $this->drupalCreateUser(array(
'administer entity_test_render fields',
'view test entity',
'administer entity_test content',
));
$this->drupalLogin($limited_user);
$this->drupalGet('admin/structure/entity-test-render/manage/entity_test_render/fields/entity_test_render.entity_test_render.comment');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-1');
$this->assertFieldChecked('edit-default-value-input-comment-0-status-2');
// Test comment option change in field settings.
$edit = array('default_value_input[comment][0][status]' => COMMENT_CLOSED);
$this->drupalPostForm(NULL, $edit, t('Save settings'));
$this->drupalGet('admin/structure/entity-test-render/manage/entity_test_render/fields/entity_test_render.entity_test_render.comment');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-0');
$this->assertFieldChecked('edit-default-value-input-comment-0-status-1');
$this->assertNoFieldChecked('edit-default-value-input-comment-0-status-2');
// Add a new comment field.
$this->drupalGet('admin/structure/entity-test-render/manage/entity_test_render/fields');
$edit = array(
'fields[_add_new_field][label]' => 'Foobar',
'fields[_add_new_field][field_name]' => 'foobar',
'fields[_add_new_field][type]' => 'comment',
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->drupalPostForm(NULL, array(), t('Save field settings'));
$this->drupalPostForm(NULL, array(), t('Save settings'));
$this->assertRaw(t('Saved %name configuration', array('%name' => 'Foobar')));
// Test the new entity commenting inherits default.
$random_label = $this->randomName();
$data = array('bundle' => 'entity_test_render', 'name' => $random_label);
$new_entity = entity_create('entity_test_render', $data);
$new_entity->save();
$this->drupalGet('entity_test_render/manage/' . $new_entity->id() . '/edit');
$this->assertNoFieldChecked('edit-field-foobar-0-status-1');
$this->assertFieldChecked('edit-field-foobar-0-status-2');
$this->assertNoField('edit-field-foobar-0-status-0');
$this->drupalGet('comment/reply/entity_test_render/comment/' . $new_entity->id());
$this->assertNoFieldByName('subject', '', 'Subject field found.');
$this->assertNoFieldByName('comment_body[0][value]', '', 'Comment field found.');
}
}

View File

@ -37,7 +37,7 @@ class CommentPagerTest extends CommentTestBase {
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
// Set comments to one per page so that we are able to test paging without
// needing to insert large numbers of comments.
@ -65,7 +65,7 @@ class CommentPagerTest extends CommentTestBase {
// Post a reply to the oldest comment and test again.
$oldest_comment = reset($comments);
$this->drupalGet('comment/reply/' . $node->id() . '/' . $oldest_comment->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $oldest_comment->id());
$reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
$this->setCommentsPerPage(2);
@ -77,7 +77,7 @@ class CommentPagerTest extends CommentTestBase {
// 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('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$this->setCommentSettings('default_mode', COMMENT_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.');
@ -113,19 +113,19 @@ class CommentPagerTest extends CommentTestBase {
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the second comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[1]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[1]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the first comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[0]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[0]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the last comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[2]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[2]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the second comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[3]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[3]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// At this point, the comment tree is:
@ -137,7 +137,7 @@ class CommentPagerTest extends CommentTestBase {
// - 2
// - 5
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$expected_order = array(
0,
@ -151,7 +151,7 @@ class CommentPagerTest extends CommentTestBase {
$this->drupalGet('node/' . $node->id());
$this->assertCommentOrder($comments, $expected_order);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$expected_order = array(
0,
@ -213,15 +213,15 @@ class CommentPagerTest extends CommentTestBase {
$comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the second comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[1]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[1]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the first comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[0]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[0]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the last comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $comments[2]->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $comments[2]->id());
$comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// At this point, the comment tree is:
@ -232,7 +232,7 @@ class CommentPagerTest extends CommentTestBase {
// - 2
// - 5
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_FLAT, 'Comment paging changed.');
$expected_pages = array(
1 => 5, // Page of comment 5
@ -245,12 +245,12 @@ class CommentPagerTest extends CommentTestBase {
$node = node_load($node->id());
foreach ($expected_pages as $new_replies => $expected_page) {
$returned = comment_new_page_count($node->comment_count, $new_replies, $node);
$returned = comment_new_page_count($node->get('comment')->comment_count, $new_replies, $node);
$returned_page = is_array($returned) ? $returned['page'] : 0;
$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('comment_default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Switched to threaded mode.');
$expected_pages = array(
1 => 5, // Page of comment 5
@ -263,7 +263,7 @@ class CommentPagerTest extends CommentTestBase {
$node = node_load($node->id());
foreach ($expected_pages as $new_replies => $expected_page) {
$returned = comment_new_page_count($node->comment_count, $new_replies, $node);
$returned = comment_new_page_count($node->get('comment')->comment_count, $new_replies, $node);
$returned_page = is_array($returned) ? $returned['page'] : 0;
$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)));
}

View File

@ -64,7 +64,7 @@ class CommentPreviewTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_OPTIONAL);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Login as web user and add a signature and a user picture.
@ -107,7 +107,7 @@ class CommentPreviewTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_OPTIONAL);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$edit = array();
$date = new DrupalDateTime('2008-03-02 17:23');

View File

@ -39,7 +39,7 @@ class CommentRssTest extends CommentTestBase {
$this->assertRaw($raw, 'Comments as part of RSS feed.');
// Hide comments from RSS feed and check presence.
$this->node->comment = COMMENT_NODE_HIDDEN;
$this->node->set('comment', COMMENT_HIDDEN);
$this->node->save();
$this->drupalGet('rss.xml');
$this->assertNoRaw($raw, 'Hidden comments is not a part of RSS feed.');

View File

@ -44,15 +44,15 @@ class CommentStatisticsTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(FALSE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Checks the initial values of node comment statistics with no comment.
$node = node_load($this->node->id());
$this->assertEqual($node->last_comment_timestamp, $this->node->getCreatedTime(), 'The initial value of node last_comment_timestamp is the node created date.');
$this->assertEqual($node->last_comment_name, NULL, 'The initial value of node last_comment_name is NULL.');
$this->assertEqual($node->last_comment_uid, $this->web_user->id(), 'The initial value of node last_comment_uid is the node uid.');
$this->assertEqual($node->comment_count, 0, 'The initial value of node comment_count is zero.');
$this->assertEqual($node->get('comment')->last_comment_timestamp, $this->node->getCreatedTime(), 'The initial value of node last_comment_timestamp is the node created date.');
$this->assertEqual($node->get('comment')->last_comment_name, NULL, 'The initial value of node last_comment_name is NULL.');
$this->assertEqual($node->get('comment')->last_comment_uid, $this->web_user->id(), 'The initial value of node last_comment_uid is the node uid.');
$this->assertEqual($node->get('comment')->comment_count, 0, 'The initial value of node comment_count is zero.');
// Post comment #1 as web_user2.
$this->drupalLogin($this->web_user2);
@ -62,9 +62,9 @@ class CommentStatisticsTest extends CommentTestBase {
// Checks the new values of node comment statistics with comment #1.
// The node needs to be reloaded with a node_load_multiple cache reset.
$node = node_load($this->node->id(), TRUE);
$this->assertEqual($node->last_comment_name, NULL, 'The value of node last_comment_name is NULL.');
$this->assertEqual($node->last_comment_uid, $this->web_user2->id(), 'The value of node last_comment_uid is the comment #1 uid.');
$this->assertEqual($node->comment_count, 1, 'The value of node comment_count is 1.');
$this->assertEqual($node->get('comment')->last_comment_name, NULL, 'The value of node last_comment_name is NULL.');
$this->assertEqual($node->get('comment')->last_comment_uid, $this->web_user2->id(), 'The value of node last_comment_uid is the comment #1 uid.');
$this->assertEqual($node->get('comment')->comment_count, 1, 'The value of node comment_count is 1.');
// Prepare for anonymous comment submission (comment approval enabled).
$this->drupalLogin($this->admin_user);
@ -78,16 +78,16 @@ class CommentStatisticsTest extends CommentTestBase {
$this->drupalLogout();
// Post comment #2 as anonymous (comment approval enabled).
$this->drupalGet('comment/reply/' . $this->node->id());
$this->postComment($this->node, $this->randomName(), '', TRUE);
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$anonymous_comment = $this->postComment($this->node, $this->randomName(), '', TRUE);
// Checks the new values of node comment statistics with comment #2 and
// ensure they haven't changed since the comment has not been moderated.
// The node needs to be reloaded with a node_load_multiple cache reset.
$node = node_load($this->node->id(), TRUE);
$this->assertEqual($node->last_comment_name, NULL, 'The value of node last_comment_name is still NULL.');
$this->assertEqual($node->last_comment_uid, $this->web_user2->id(), 'The value of node last_comment_uid is still the comment #1 uid.');
$this->assertEqual($node->comment_count, 1, 'The value of node comment_count is still 1.');
$this->assertEqual($node->get('comment')->last_comment_name, NULL, 'The value of node last_comment_name is still NULL.');
$this->assertEqual($node->get('comment')->last_comment_uid, $this->web_user2->id(), 'The value of node last_comment_uid is still the comment #1 uid.');
$this->assertEqual($node->get('comment')->comment_count, 1, 'The value of node comment_count is still 1.');
// Prepare for anonymous comment submission (no approval required).
$this->drupalLogin($this->admin_user);
@ -99,15 +99,16 @@ class CommentStatisticsTest extends CommentTestBase {
$this->drupalLogout();
// Post comment #3 as anonymous.
$this->drupalGet('comment/reply/' . $this->node->id());
$comment_loaded = $this->postComment($this->node, $this->randomName(), '', array('name' => $this->randomName()));
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment');
$anonymous_comment = $this->postComment($this->node, $this->randomName(), '', array('name' => $this->randomName()));
$comment_loaded = comment_load($anonymous_comment->id());
// Checks the new values of node comment statistics with comment #3.
// The node needs to be reloaded with a node_load_multiple cache reset.
$node = node_load($this->node->id(), TRUE);
$this->assertEqual($node->last_comment_name, $comment_loaded->name->value, 'The value of node last_comment_name is the name of the anonymous user.');
$this->assertEqual($node->last_comment_uid, 0, 'The value of node last_comment_uid is zero.');
$this->assertEqual($node->comment_count, 2, 'The value of node comment_count is 2.');
$this->assertEqual($node->get('comment')->last_comment_name, $comment_loaded->name->value, 'The value of node last_comment_name is the name of the anonymous user.');
$this->assertEqual($node->get('comment')->last_comment_uid, 0, 'The value of node last_comment_uid is zero.');
$this->assertEqual($node->get('comment')->comment_count, 2, 'The value of node comment_count is 2.');
}
}

View File

@ -73,6 +73,9 @@ abstract class CommentTestBase extends WebTestBase {
'access content',
));
// Create comment field on article.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
// Create a test node authored by the web user.
$this->node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1, 'uid' => $this->web_user->id()));
}
@ -97,12 +100,18 @@ abstract class CommentTestBase extends WebTestBase {
$edit = array();
$edit['comment_body[0][value]'] = $comment;
$preview_mode = variable_get('comment_preview_article', DRUPAL_OPTIONAL);
$subject_mode = variable_get('comment_subject_field_article', 1);
if ($entity !== NULL) {
$instance = $this->container->get('field.info')->getInstance('node', $entity->bundle(), 'comment');
}
else {
$instance = $this->container->get('field.info')->getInstance('node', 'article', 'comment');
}
$preview_mode = $instance->settings['preview'];
$subject_mode = $instance->settings['subject'];
// Must get the page before we test for fields.
if ($entity !== NULL) {
$this->drupalGet('comment/reply/' . $entity->id());
$this->drupalGet('comment/reply/node/' . $entity->id() . '/comment');
}
if ($subject_mode == TRUE) {
@ -196,7 +205,7 @@ abstract class CommentTestBase extends WebTestBase {
* Boolean specifying whether the subject field should be enabled.
*/
function setCommentSubject($enabled) {
$this->setCommentSettings('comment_subject_field', ($enabled ? '1' : '0'), 'Comment subject ' . ($enabled ? 'enabled' : 'disabled') . '.');
$this->setCommentSettings('subject', ($enabled ? '1' : '0'), 'Comment subject ' . ($enabled ? 'enabled' : 'disabled') . '.');
}
/**
@ -219,7 +228,7 @@ abstract class CommentTestBase extends WebTestBase {
$mode_text = 'required';
break;
}
$this->setCommentSettings('comment_preview', $mode, format_string('Comment preview @mode_text.', array('@mode_text' => $mode_text)));
$this->setCommentSettings('preview', $mode, format_string('Comment preview @mode_text.', array('@mode_text' => $mode_text)));
}
/**
@ -230,7 +239,7 @@ abstract class CommentTestBase extends WebTestBase {
* comments; FALSE if it should be displayed on its own page.
*/
function setCommentForm($enabled) {
$this->setCommentSettings('comment_form_location', ($enabled ? COMMENT_FORM_BELOW : COMMENT_FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.');
$this->setCommentSettings('form_location', ($enabled ? COMMENT_FORM_BELOW : COMMENT_FORM_SEPARATE_PAGE), 'Comment controls ' . ($enabled ? 'enabled' : 'disabled') . '.');
}
/**
@ -243,7 +252,7 @@ abstract class CommentTestBase extends WebTestBase {
* - 2: Contact information required.
*/
function setCommentAnonymous($level) {
$this->setCommentSettings('comment_anonymous', $level, format_string('Anonymous commenting set to level @level.', array('@level' => $level)));
$this->setCommentSettings('anonymous', $level, format_string('Anonymous commenting set to level @level.', array('@level' => $level)));
}
/**
@ -253,7 +262,7 @@ abstract class CommentTestBase extends WebTestBase {
* Comments per page value.
*/
function setCommentsPerPage($number) {
$this->setCommentSettings('comment_default_per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)));
$this->setCommentSettings('per_page', $number, format_string('Number of comments per page set to @number.', array('@number' => $number)));
}
/**
@ -267,7 +276,9 @@ abstract class CommentTestBase extends WebTestBase {
* Status message to display.
*/
function setCommentSettings($name, $value, $message) {
variable_set($name . '_article', $value);
$instance = $this->container->get('field.info')->getInstance('node', 'article', 'comment');
$instance->settings[$name] = $value;
$instance->save();
// Display status message.
$this->pass($message);
}

View File

@ -31,7 +31,7 @@ class CommentThreadingTest extends CommentTestBase {
$this->setCommentPreview(DRUPAL_DISABLED);
$this->setCommentForm(TRUE);
$this->setCommentSubject(TRUE);
$this->setCommentSettings('comment_default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->setCommentSettings('default_mode', COMMENT_MODE_THREADED, 'Comment paging changed.');
$this->drupalLogout();
// Create a node.
@ -51,7 +51,7 @@ class CommentThreadingTest extends CommentTestBase {
// Reply to comment #1 creating comment #2.
$this->drupalLogin($this->web_user);
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment1->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment1->id());
$comment2 = $this->postComment(NULL, $this->randomName(), '', TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment2, TRUE), 'Comment #2. Reply found.');
@ -60,7 +60,7 @@ class CommentThreadingTest extends CommentTestBase {
$this->assertParentLink($comment2->id(), $comment1->id());
// Reply to comment #2 creating comment #3.
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment2->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment2->id());
$comment3 = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment3, TRUE), 'Comment #3. Second reply found.');
@ -70,7 +70,7 @@ class CommentThreadingTest extends CommentTestBase {
// Reply to comment #1 creating comment #4.
$this->drupalLogin($this->web_user);
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment1->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment1->id());
$comment4 = $this->postComment(NULL, $this->randomName(), '', TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment4), 'Comment #4. Third reply found.');
@ -91,7 +91,7 @@ class CommentThreadingTest extends CommentTestBase {
// Reply to comment #5 creating comment #6.
$this->drupalLogin($this->web_user);
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment5->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment5->id());
$comment6 = $this->postComment(NULL, $this->randomName(), '', TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment6, TRUE), 'Comment #6. Reply found.');
@ -100,7 +100,7 @@ class CommentThreadingTest extends CommentTestBase {
$this->assertParentLink($comment6->id(), $comment5->id());
// Reply to comment #6 creating comment #7.
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment6->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment6->id());
$comment7 = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment7, TRUE), 'Comment #7. Second reply found.');
@ -110,7 +110,7 @@ class CommentThreadingTest extends CommentTestBase {
// Reply to comment #5 creating comment #8.
$this->drupalLogin($this->web_user);
$this->drupalGet('comment/reply/' . $this->node->id() . '/' . $comment5->id());
$this->drupalGet('comment/reply/node/' . $this->node->id() . '/comment/' . $comment5->id());
$comment8 = $this->postComment(NULL, $this->randomName(), '', TRUE);
// Confirm that the comment was created and has the correct threading.
$this->assertTrue($this->commentExists($comment8), 'Comment #8. Third reply found.');

View File

@ -42,7 +42,7 @@ class CommentTokenReplaceTest extends CommentTestBase {
$parent_comment = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
// Post a reply to the comment.
$this->drupalGet('comment/reply/' . $node->id() . '/' . $parent_comment->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment/' . $parent_comment->id());
$child_comment = $this->postComment(NULL, $this->randomName(), $this->randomName());
$comment = comment_load($child_comment->id());
$comment->homepage->value = 'http://example.org/';
@ -66,7 +66,7 @@ class CommentTokenReplaceTest extends CommentTestBase {
$tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed->value, 2, $language_interface->id);
$tests['[comment:parent:cid]'] = $comment->pid->target_id;
$tests['[comment:parent:title]'] = check_plain($parent_comment->subject->value);
$tests['[comment:node:nid]'] = $comment->nid->target_id;
$tests['[comment:node:nid]'] = $comment->entity_id->value;
$tests['[comment:node:title]'] = check_plain($node->getTitle());
$tests['[comment:author:uid]'] = $comment->uid->target_id;
$tests['[comment:author:name]'] = check_plain($this->admin_user->getUsername());
@ -100,11 +100,13 @@ class CommentTokenReplaceTest extends CommentTestBase {
// Generate comment tokens for the node (it has 2 comments, both new).
$tests = array();
$tests['[entity:comment-count]'] = 2;
$tests['[entity:comment-count-new]'] = 2;
// Also test the deprecated legacy token.
$tests['[node:comment-count]'] = 2;
$tests['[node:comment-count-new]'] = 2;
foreach ($tests as $input => $expected) {
$output = $token_service->replace($input, array('node' => $node), array('langcode' => $language_interface->id));
$output = $token_service->replace($input, array('entity' => $node, 'node' => $node), array('langcode' => $language_interface->id));
$this->assertEqual($output, $expected, format_string('Node comment token %token replaced.', array('%token' => $input)));
}
}

View File

@ -37,7 +37,7 @@ class CommentTranslationUITest extends ContentTranslationUITest {
function setUp() {
$this->entityType = 'comment';
$this->nodeBundle = 'article';
$this->bundle = 'comment_node_' . $this->nodeBundle;
$this->bundle = 'node__comment_article';
$this->testLanguageSelector = FALSE;
$this->subject = $this->randomName();
parent::setUp();
@ -49,6 +49,19 @@ class CommentTranslationUITest extends ContentTranslationUITest {
function setupBundle() {
parent::setupBundle();
$this->drupalCreateContentType(array('type' => $this->nodeBundle, 'name' => $this->nodeBundle));
// Add a comment field to the article content type.
$this->container->get('comment.manager')->addDefaultField('node', 'article', 'comment_article');
// Create a page content type.
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'page'));
// Add a comment field to the page content type - this one won't be
// translatable.
$this->container->get('comment.manager')->addDefaultField('node', 'page', 'comment');
// Mark this bundle as translatable.
content_translation_set_config('comment', 'node__comment_article', 'enabled', TRUE);
// Refresh entity info.
entity_info_cache_clear();
// Flush the permissions after adding the translatable comment bundle.
$this->checkPermissions(array(), TRUE);
}
/**
@ -71,14 +84,32 @@ class CommentTranslationUITest extends ContentTranslationUITest {
/**
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::createEntity().
*/
protected function createEntity($values, $langcode, $node_bundle = NULL) {
if (!isset($node_bundle)) {
$node_bundle = $this->nodeBundle;
protected function createEntity($values, $langcode, $node_bundle = 'node__comment_article') {
// The argument is called 'node_bundle' but its actually just the entity
// bundle. Comment entity's bundle is of the form
// {entity_type}__{field_name}. Based on the passed bundle we need to
// determine the type of node and the name of the comment field.
if ($node_bundle == 'node__comment_article') {
// This is the article node type, with the 'comment_article' field.
$node_type = 'article';
$field_name = 'comment_article';
}
$node = $this->drupalCreateNode(array('type' => $node_bundle));
$values['nid'] = $node->id();
else {
// This is the page node type with the non-translatable 'comment' field.
$node_type = 'page';
$field_name = 'comment';
}
$node = $this->drupalCreateNode(array(
'type' => $node_type,
$field_name => array(
array('status' => COMMENT_OPEN)
),
));
$values['entity_id'] = $node->id();
$values['entity_type'] = 'node';
$values['field_id'] = $node_bundle;
$values['uid'] = $node->getAuthorId();
return parent::createEntity($values, $langcode);
return parent::createEntity($values, $langcode, $node_bundle);
}
/**
@ -124,12 +155,11 @@ class CommentTranslationUITest extends ContentTranslationUITest {
* Tests translate link on comment content admin page.
*/
function testTranslateLinkCommentAdminPage() {
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'page'));
$this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer comments')));
$this->drupalLogin($this->admin_user);
$cid_translatable = $this->createEntity(array(), $this->langcodes[0], $this->nodeBundle);
$cid_untranslatable = $this->createEntity(array(), $this->langcodes[0], 'page');
$cid_translatable = $this->createEntity(array(), $this->langcodes[0]);
$cid_untranslatable = $this->createEntity(array(), $this->langcodes[0], 'node__comment');
// Verify translation links.
$this->drupalGet('admin/content/comment');

View File

@ -32,9 +32,10 @@ class CommentUninstallTest extends WebTestBase {
protected function setUp() {
parent::setup();
// Create a content type so that the comment module creates the
// 'comment_body' field upon installation.
$this->drupalCreateContentType();
// Create an article content type.
$this->drupalCreateContentType(array('type' => 'article', 'name' => t('Article')));
// Create comment field on article so that adds 'comment_body' field.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
}
/**

View File

@ -40,12 +40,16 @@ abstract class CommentTestBase extends ViewTestBase {
$this->account2 = $this->drupalCreateUser();
$this->drupalLogin($this->account);
$this->container->get('comment.manager')->addDefaultField('node', 'page');
$this->node_user_posted = $this->drupalCreateNode();
$this->node_user_commented = $this->drupalCreateNode(array('uid' => $this->account2->id()));
$comment = array(
'uid' => $this->loggedInUser->id(),
'nid' => $this->node_user_commented->id(),
'entity_id' => $this->node_user_commented->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'cid' => '',
'pid' => '',
'node_type' => '',

View File

@ -73,13 +73,22 @@ class DefaultViewRecentComments extends ViewTestBase {
'type' => $content_type->type,
);
$this->container->get('comment.manager')->addDefaultField('node', $content_type->type);
$this->node = $this->drupalCreateNode($node_data);
// Force a flush of the in-memory storage.
$this->container->get('views.views_data')->clear();
// Create some comments and attach them to the created node.
for ($i = 0; $i < $this->masterDisplayResults; $i++) {
$comment = entity_create('comment', array('node_type' => 'comment_node_' . $this->node->getType()));
$comment = entity_create('comment', array(
'field_name' => 'comment',
'entity_type' => 'node',
'entity_id' => $this->node->id(),
));
$comment->uid->target_id = 0;
$comment->nid->target_id = $this->node->id();
// Stagger the comments so the timestamp sorting works.
$comment->created->value = REQUEST_TIME - $i;
$comment->subject->value = 'Test comment ' . $i;
$comment->comment_body->value = 'Test body ' . $i;
$comment->comment_body->format = 'full_html';
@ -108,14 +117,14 @@ class DefaultViewRecentComments extends ViewTestBase {
$this->executeView($view);
$map = array(
'comment_nid' => 'nid',
'comment_entity_id' => 'entity_id',
'comment_subject' => 'subject',
'cid' => 'cid',
'comment_changed' => 'changed'
);
$expected_result = array();
foreach (array_values($this->commentsCreated) as $key => $comment) {
$expected_result[$key]['nid'] = $comment->nid->target_id;
$expected_result[$key]['entity_id'] = $comment->entity_id->value;
$expected_result[$key]['subject'] = $comment->subject->value;
$expected_result[$key]['cid'] = $comment->id();
$expected_result[$key]['changed'] = $comment->changed->value;
@ -139,7 +148,7 @@ class DefaultViewRecentComments extends ViewTestBase {
$this->executeView($view);
$map = array(
'comment_nid' => 'nid',
'comment_entity_id' => 'entity_id',
'comment_subject' => 'subject',
'comment_changed' => 'changed',
'comment_changed' => 'created',
@ -147,7 +156,7 @@ class DefaultViewRecentComments extends ViewTestBase {
);
$expected_result = array();
foreach (array_values($this->commentsCreated) as $key => $comment) {
$expected_result[$key]['nid'] = $comment->nid->target_id;
$expected_result[$key]['entity_id'] = $comment->entity_id->value;
$expected_result[$key]['subject'] = $comment->subject->value;
$expected_result[$key]['changed'] = $comment->changed->value;
$expected_result[$key]['created'] = $comment->created->value;

View File

@ -36,6 +36,8 @@ class WizardTest extends WizardTestBase {
* Tests adding a view of comments.
*/
public function testCommentWizard() {
// Add comment field to page node type.
$this->container->get('comment.manager')->addDefaultField('node', 'page');
$view = array();
$view['label'] = $this->randomName(16);
$view['id'] = strtolower($this->randomName(16));

View File

@ -18,7 +18,7 @@
* be displayed after the main title tag that appears in the template.
*
* The following variables are provided for contextual information.
* - node: The node entity to which the comments belong.
* - entity: The entity to which the comments belong.
* - display_mode: The display mode for the comment listing, flat or threaded.
* The constants below show the possible values and should be used for
* comparison, as in the following example:
@ -36,7 +36,7 @@
*/
#}
<section{{ attributes }}>
{% if comments and node.type != 'forum' %}
{% if comments and (entity.entityType != 'node' or entity.bundle != 'forum') %}
{{ title_prefix }}
<h2 class="title">{{ 'Comments'|t }}</h2>
{{ title_suffix }}

View File

@ -27,7 +27,8 @@
* through CSS. The default values can be one or more of the following:
* - comment: The current template type; e.g., 'theming hook'.
* - by-anonymous: Comment by an unregistered user.
* - by-node-author: Comment by the author of the parent node.
* - by-{entity-type}-author: Comment by the author of the parent entity,
* eg. by-node-author.
* - preview: When previewing a new or edited comment.
* The following applies only to viewers who are registered users:
* - unpublished: An unpublished comment visible only to administrators.
@ -53,7 +54,7 @@
*
* These two variables are provided for context:
* - comment: Full comment object.
* - node: Node entity the comments are attached to.
* - entity: Entity the comments are attached to.
*
* @see template_preprocess_comment()
*

View File

@ -64,11 +64,11 @@ display:
links: '1'
view_mode: full
relationships:
nid:
id: nid
table: comment
field: nid
node:
field: node
id: node
required: '1'
table: comment
relationship: none
group_type: group
admin_label: Content
@ -118,7 +118,7 @@ display:
value: '1'
table: node_field_data
field: status
relationship: nid
relationship: node
id: status_node
expose:
operator: '0'

View File

@ -25,11 +25,11 @@ display:
row:
type: fields
relationships:
nid:
id: nid
table: comment
field: nid
node:
field: node
id: node
required: '1'
table: comment
provider: views
fields:
subject:

View File

@ -20,7 +20,7 @@ class ContentTranslationSettingsTest extends WebTestBase {
*
* @var array
*/
public static $modules = array('language', 'content_translation', 'comment');
public static $modules = array('language', 'content_translation', 'comment', 'field_ui');
public static function getInfo() {
return array(
@ -37,8 +37,9 @@ class ContentTranslationSettingsTest extends WebTestBase {
// bundles.
$this->drupalCreateContentType(array('type' => 'article'));
$this->drupalCreateContentType(array('type' => 'page'));
$this->container->get('comment.manager')->addDefaultField('node', 'article', 'comment_article');
$admin_user = $this->drupalCreateUser(array('administer languages', 'administer content translation', 'administer content types'));
$admin_user = $this->drupalCreateUser(array('administer languages', 'administer content translation', 'administer content types', 'administer comment fields'));
$this->drupalLogin($admin_user);
}
@ -48,21 +49,21 @@ class ContentTranslationSettingsTest extends WebTestBase {
function testSettingsUI() {
// Test that the translation settings are ignored if the bundle is marked
// translatable but the entity type is not.
$edit = array('settings[comment][comment_node_article][translatable]' => TRUE);
$edit = array('settings[comment][node__comment_article][translatable]' => TRUE);
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are ignored if only a field is marked
// as translatable and not the related entity type and bundle.
$edit = array('settings[comment][comment_node_article][fields][comment_body]' => TRUE);
$edit = array('settings[comment][node__comment_article][fields][comment_body]' => TRUE);
$this->assertSettings('comment', NULL, FALSE, $edit);
// Test that the translation settings are not stored if an entity type and
// bundle are marked as translatable but no field is.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][translatable]' => TRUE,
'settings[comment][node__comment_article][translatable]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', FALSE, $edit);
$this->assertSettings('comment', 'node__comment_article', FALSE, $edit);
$xpath_err = '//div[contains(@class, "error")]';
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation only for entity bundles generates a form error.');
@ -70,37 +71,37 @@ class ContentTranslationSettingsTest extends WebTestBase {
// language is set as default and the language selector is hidden.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][settings][language][langcode]' => Language::LANGCODE_NOT_SPECIFIED,
'settings[comment][comment_node_article][settings][language][language_show]' => FALSE,
'settings[comment][comment_node_article][translatable]' => TRUE,
'settings[comment][comment_node_article][fields][comment_body]' => TRUE,
'settings[comment][node__comment_article][settings][language][langcode]' => Language::LANGCODE_NOT_SPECIFIED,
'settings[comment][node__comment_article][settings][language][language_show]' => FALSE,
'settings[comment][node__comment_article][translatable]' => TRUE,
'settings[comment][node__comment_article][fields][comment_body]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', FALSE, $edit);
$this->assertSettings('comment', 'node__comment_article', FALSE, $edit);
$this->assertTrue($this->xpath($xpath_err), 'Enabling translation with a fixed non-configurable language generates a form error.');
// Test that a field shared among different bundles can be enabled without
// needing to make all the related bundles translatable.
$edit = array(
'entity_types[comment]' => TRUE,
'settings[comment][comment_node_article][settings][language][langcode]' => 'current_interface',
'settings[comment][comment_node_article][settings][language][language_show]' => TRUE,
'settings[comment][comment_node_article][translatable]' => TRUE,
'settings[comment][comment_node_article][fields][comment_body]' => TRUE,
'settings[comment][node__comment_article][settings][language][langcode]' => 'current_interface',
'settings[comment][node__comment_article][settings][language][language_show]' => TRUE,
'settings[comment][node__comment_article][translatable]' => TRUE,
'settings[comment][node__comment_article][fields][comment_body]' => TRUE,
);
$this->assertSettings('comment', 'comment_node_article', TRUE, $edit);
$this->assertSettings('comment', 'node__comment_article', TRUE, $edit);
field_info_cache_clear();
$field = field_info_field('comment', 'comment_body');
$this->assertTrue($field['translatable'], 'Comment body is translatable.');
// Test that language settings are correctly stored.
$language_configuration = language_get_default_configuration('comment', 'comment_node_article');
$language_configuration = language_get_default_configuration('comment', 'node__comment_article');
$this->assertEqual($language_configuration['langcode'], 'current_interface', 'The default language for article comments is set to the current interface language.');
$this->assertTrue($language_configuration['language_show'], 'The language selector for article comments is shown.');
// Verify language widget appears on node type form.
$this->drupalGet('admin/structure/types/manage/article');
$this->assertField('content_translation');
$this->assertFieldChecked('edit-content-translation');
$this->drupalGet('admin/structure/comments/manage/node__comment_article/fields/comment.node__comment_article.comment_body/field');
$this->assertField('field[translatable]');
$this->assertFieldChecked('edit-field-translatable');
// Verify that translation may be enabled for the article content type.
$edit = array(

View File

@ -76,11 +76,11 @@ class EntityDisplayModeTest extends WebTestBase {
$this->drupalGet('admin/structure/display-modes/form');
$this->assertResponse(200);
$this->drupalGet('admin/structure/display-modes/form/add/entity_test_render');
$this->drupalGet('admin/structure/display-modes/form/add/entity_test_no_label');
$this->assertResponse(404);
$this->drupalGet('admin/structure/display-modes/form/add');
$this->assertNoLink(t('Test render entity'), 'An entity type with no form controller cannot have form modes.');
$this->assertNoLink(t('Entity Test without label'), 'An entity type with no form controller cannot have form modes.');
// Test adding a form mode.
$this->clickLink(t('Test entity'));

View File

@ -398,9 +398,14 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
$nodes[$key] = $node;
}
// Create comment field on article.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
$comment_values = array(
'published_published' => array(
'nid' => $nodes['published']->id(),
'entity_id' => $nodes['published']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
@ -409,7 +414,9 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
'language' => Language::LANGCODE_NOT_SPECIFIED,
),
'published_unpublished' => array(
'nid' => $nodes['published']->id(),
'entity_id' => $nodes['published']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
@ -418,7 +425,9 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
'language' => Language::LANGCODE_NOT_SPECIFIED,
),
'unpublished_published' => array(
'nid' => $nodes['unpublished']->id(),
'entity_id' => $nodes['unpublished']->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'uid' => 1,
'cid' => NULL,
'pid' => 0,
@ -447,7 +456,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment_node_article' => array(
'node__comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
),
),
@ -457,7 +466,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
array('Published', 'CONTAINS'),
),
'result' => array(
'comment_node_article' => array(
'node__comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
),
),
@ -486,7 +495,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment_node_article' => array(
'node__comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
$comments['published_unpublished']->cid->value => $comment_labels['published_unpublished'],
),
@ -504,7 +513,7 @@ class EntityReferenceSelectionAccessTest extends WebTestBase {
array(NULL, 'CONTAINS'),
),
'result' => array(
'comment_node_article' => array(
'node__comment' => array(
$comments['published_published']->cid->value => $comment_labels['published_published'],
$comments['published_unpublished']->cid->value => $comment_labels['published_unpublished'],
$comments['unpublished_published']->cid->value => $comment_labels['unpublished_published'],

View File

@ -202,6 +202,9 @@ class FileFieldWidgetTest extends FileFieldTestBase {
* Tests a file field with a "Private files" upload destination setting.
*/
function testPrivateFileSetting() {
// Grant the admin user required permissions.
user_role_grant_permissions($this->admin_user->roles[0]->value, array('administer node fields'));
$type_name = 'article';
$field_name = strtolower($this->randomName());
$this->createFileField($field_name, 'node', $type_name);
@ -240,7 +243,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
// Grant the admin user required comment permissions.
$roles = $this->admin_user->getRoles();
user_role_grant_permissions($roles[1], array('administer comment fields'));
user_role_grant_permissions($roles[1], array('administer comment fields', 'administer comments'));
// Revoke access comments permission from anon user, grant post to
// authenticated.
@ -248,12 +251,13 @@ class FileFieldWidgetTest extends FileFieldTestBase {
user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('post comments', 'skip comment approval'));
// Create a new field.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
$edit = array(
'fields[_add_new_field][label]' => $label = $this->randomName(),
'fields[_add_new_field][field_name]' => $name = strtolower($this->randomName()),
'fields[_add_new_field][type]' => 'file',
);
$this->drupalPostForm('admin/structure/types/manage/article/comment/fields', $edit, t('Save'));
$this->drupalPostForm('admin/structure/comments/manage/node__comment/fields', $edit, t('Save'));
$edit = array('field[settings][uri_scheme]' => 'private');
$this->drupalPostForm(NULL, $edit, t('Save field settings'));
$this->drupalPostForm(NULL, array(), t('Save settings'));
@ -274,7 +278,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
'files[field_' . $name . '_' . 0 . ']' => drupal_realpath($text_file->getFileUri()),
'comment_body[0][value]' => $comment_body = $this->randomName(),
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->drupalPostForm('node/' . $node->id(), $edit, t('Save'));
// Get the comment ID.
preg_match('/comment-([0-9]+)/', $this->getUrl(), $matches);

View File

@ -67,6 +67,8 @@ class FilterHtmlImageSecureTest extends WebTestBase {
// Setup a node to comment and test on.
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
// Add a comment field.
$this->container->get('comment.manager')->addDefaultField('node', 'page');
$this->node = $this->drupalCreateNode();
}

View File

@ -104,6 +104,16 @@ function forum_install() {
))
->save();
}
// Add the comment field to the forum node type.
$fields = entity_load_multiple_by_properties('field_entity', array(
'type' => 'comment',
'name' => 'comment_forum',
'include_inactive' => TRUE,
'include_deleted' => FALSE,
));
if (empty($fields)) {
Drupal::service('comment.manager')->addDefaultField('node', 'forum', 'comment_forum', COMMENT_OPEN);
}
}
/**
@ -129,6 +139,13 @@ function forum_uninstall() {
$field->delete();
}
// Load the dependent Comment module, in case it has been disabled.
drupal_load('module', 'comment');
if ($field = field_info_field('node', 'comment_forum')) {
$field->delete();
}
if ($field = field_info_field('taxonomy_term', 'forum_container')) {
$field->delete();
}
@ -226,7 +243,7 @@ function forum_schema() {
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default'=> 0,
'default' => 0,
),
'last_comment_timestamp' => array(
'type' => 'int',
@ -261,7 +278,6 @@ function forum_schema() {
),
);
return $schema;
}

View File

@ -6,6 +6,8 @@
*/
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\taxonomy\Entity\Term;
use Drupal\field\Field;
use Drupal\node\NodeInterface;
@ -457,7 +459,9 @@ function forum_permission() {
* $comment->save() calls hook_comment_publish() for all published comments.
*/
function forum_comment_publish($comment) {
\Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
\Drupal::service('forum_manager')->updateIndex($comment->entity_id->value);
}
}
/**
@ -469,8 +473,8 @@ function forum_comment_publish($comment) {
function forum_comment_update($comment) {
// $comment->save() calls hook_comment_publish() for all published comments,
// so we need to handle all other values here.
if (!$comment->status->value) {
\Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
if (!$comment->status->value && $comment->entity_type->value == 'node') {
\Drupal::service('forum_manager')->updateIndex($comment->entity_id->value);
}
}
@ -478,14 +482,18 @@ function forum_comment_update($comment) {
* Implements hook_comment_unpublish().
*/
function forum_comment_unpublish($comment) {
\Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
\Drupal::service('forum_manager')->updateIndex($comment->entity_id->value);
}
}
/**
* Implements hook_comment_delete().
*/
function forum_comment_delete($comment) {
\Drupal::service('forum_manager')->updateIndex($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
\Drupal::service('forum_manager')->updateIndex($comment->entity_id->value);
}
}
/**
@ -773,7 +781,7 @@ function template_preprocess_forum_topic_list(&$variables) {
$variables['topics'][$id]->new_url = '';
if ($topic->new_replies) {
$variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new post<span class="visually-hidden"> in topic %title</span>', '@count new posts<span class="visually-hidden"> in topic %title</span>', array('%title' => $variables['topics'][$id]->label()));
$variables['topics'][$id]->new_url = url('node/' . $topic->id(), array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
$variables['topics'][$id]->new_url = url('node/' . $topic->id(), array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic, 'comment_node_forum'), 'fragment' => 'new'));
}
}
@ -817,7 +825,7 @@ function template_preprocess_forum_icon(&$variables) {
$variables['icon_title'] = $variables['new_posts'] ? t('New comments') : t('Normal topic');
}
if ($variables['comment_mode'] == COMMENT_NODE_CLOSED || $variables['comment_mode'] == COMMENT_NODE_HIDDEN) {
if ($variables['comment_mode'] == COMMENT_CLOSED || $variables['comment_mode'] == COMMENT_HIDDEN) {
$icon_status_class = 'closed';
$variables['icon_title'] = t('Closed topic');
}

View File

@ -185,23 +185,23 @@ class ForumManager implements ForumManagerInterface {
->extend('Drupal\Core\Database\Query\TableSortExtender');
$query->fields('n', array('nid'));
$query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
$query->fields('ncs', array(
$query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_id = 'node__comment_forum' AND ces.entity_type = 'node'");
$query->fields('ces', array(
'cid',
'last_comment_uid',
'last_comment_timestamp',
'comment_count'
));
$query->join('forum_index', 'f', 'f.nid = ncs.nid');
$query->join('forum_index', 'f', 'f.nid = n.nid');
$query->addField('f', 'tid', 'forum_tid');
$query->join('users', 'u', 'n.uid = u.uid');
$query->addField('u', 'name');
$query->join('users', 'u2', 'ncs.last_comment_uid = u2.uid');
$query->join('users', 'u2', 'ces.last_comment_uid = u2.uid');
$query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u2.name END', 'last_comment_name');
$query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u2.name END', 'last_comment_name');
$query
->orderBy('f.sticky', 'DESC')
@ -214,7 +214,7 @@ class ForumManager implements ForumManagerInterface {
$result = array();
foreach ($query->execute() as $row) {
$topic = $nodes[$row->nid];
$topic->comment_mode = $topic->comment;
$topic->comment_mode = $topic->comment_forum->status;
foreach ($row as $key => $value) {
$topic->{$key} = $value;
@ -356,12 +356,12 @@ class ForumManager implements ForumManagerInterface {
// Query "Last Post" information for this forum.
$query = $this->connection->select('node_field_data', 'n');
$query->join('forum', 'f', 'n.vid = f.vid AND f.tid = :tid', array(':tid' => $tid));
$query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
$query->join('users', 'u', 'ncs.last_comment_uid = u.uid');
$query->addExpression('CASE ncs.last_comment_uid WHEN 0 THEN ncs.last_comment_name ELSE u.name END', 'last_comment_name');
$query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_id = 'node__comment_forum' AND ces.entity_type = 'node'");
$query->join('users', 'u', 'ces.last_comment_uid = u.uid');
$query->addExpression('CASE ces.last_comment_uid WHEN 0 THEN ces.last_comment_name ELSE u.name END', 'last_comment_name');
$topic = $query
->fields('ncs', array('last_comment_timestamp', 'last_comment_uid'))
->fields('ces', array('last_comment_timestamp', 'last_comment_uid'))
->condition('n.status', 1)
->orderBy('last_comment_timestamp', 'DESC')
->range(0, 1)
@ -394,10 +394,10 @@ class ForumManager implements ForumManagerInterface {
if (empty($this->forumStatistics)) {
// Prime the statistics.
$query = $this->connection->select('node_field_data', 'n');
$query->join('node_comment_statistics', 'ncs', 'n.nid = ncs.nid');
$query->join('comment_entity_statistics', 'ces', "n.nid = ces.entity_id AND ces.field_id = 'node__comment_forum' AND ces.entity_type = 'node'");
$query->join('forum', 'f', 'n.vid = f.vid');
$query->addExpression('COUNT(n.nid)', 'topic_count');
$query->addExpression('SUM(ncs.comment_count)', 'comment_count');
$query->addExpression('SUM(ces.comment_count)', 'comment_count');
$this->forumStatistics = $query
->fields('f', array('tid'))
->condition('n.status', 1)
@ -515,14 +515,14 @@ class ForumManager implements ForumManagerInterface {
* {@inheritdoc}
*/
public function updateIndex($nid) {
$count = $this->connection->query('SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.nid = i.nid WHERE c.nid = :nid AND c.status = :status', array(
$count = $this->connection->query("SELECT COUNT(cid) FROM {comment} c INNER JOIN {forum_index} i ON c.entity_id = i.nid WHERE c.entity_id = :nid AND c.field_id = 'node__comment_forum' AND c.entity_type = 'node' AND c.status = :status", array(
':nid' => $nid,
':status' => COMMENT_PUBLISHED,
))->fetchField();
if ($count > 0) {
// Comments exist.
$last_reply = $this->connection->queryRange('SELECT cid, name, created, uid FROM {comment} WHERE nid = :nid AND status = :status ORDER BY cid DESC', 0, 1, array(
$last_reply = $this->connection->queryRange("SELECT cid, name, created, uid FROM {comment} WHERE entity_id = :nid AND field_id = 'node__comment_forum' AND entity_type = 'node' AND status = :status ORDER BY cid DESC", 0, 1, array(
':nid' => $nid,
':status' => COMMENT_PUBLISHED,
))->fetchObject();

View File

@ -103,7 +103,9 @@ class ForumBlockTest extends WebTestBase {
$node = $this->drupalGetNodeByTitle($topics[$index]);
$date->modify('+1 minute');
$comment = entity_create('comment', array(
'nid' => $node->id(),
'entity_id' => $node->id(),
'field_name' => 'comment_forum',
'entity_type' => 'node',
'node_type' => 'node_type_' . $node->bundle(),
'subject' => $this->randomString(20),
'comment_body' => $this->randomString(256),

View File

@ -65,9 +65,9 @@ class ForumIntegrationTest extends ViewTestBase {
$comments = array();
foreach ($nodes as $index => $node) {
for ($i = 0; $i <= $index; $i++) {
$comment = $comment_storage_controller->create(array('node_type' => 'node_type_forum', 'nid' => $node->id()));
$comment = $comment_storage_controller->create(array('entity_type' => 'node', 'entity_id' => $node->id(), 'field_name' => 'comment_forum'));
$comment->save();
$comments[$comment->get('nid')->target_id][$comment->id()] = $comment;
$comments[$comment->get('entity_id')->target_id][$comment->id()] = $comment;
}
}

View File

@ -36,7 +36,7 @@ class HistoryUserTimestamp extends Node {
$this->additional_fields['created'] = array('table' => 'node_field_data', 'field' => 'created');
$this->additional_fields['changed'] = array('table' => 'node_field_data', 'field' => 'changed');
if (module_exists('comment') && !empty($this->options['comments'])) {
$this->additional_fields['last_comment'] = array('table' => 'node_comment_statistics', 'field' => 'last_comment_timestamp');
$this->additional_fields['last_comment'] = array('table' => 'comment_entity_statistics', 'field' => 'last_comment_timestamp');
}
}
}

View File

@ -76,9 +76,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
$clause = '';
$clause2 = '';
if (module_exists('comment')) {
$ncs = $this->query->ensureTable('node_comment_statistics', $this->relationship);
$clause = ("OR $ncs.last_comment_timestamp > (***CURRENT_TIME*** - $limit)");
$clause2 = "OR $field < $ncs.last_comment_timestamp";
$ces = $this->query->ensureTable('comment_entity_statistics', $this->relationship);
$clause = ("OR $ces.last_comment_timestamp > (***CURRENT_TIME*** - $limit)");
$clause2 = "OR $field < $ces.last_comment_timestamp";
}
// NULL means a history record doesn't exist. That's clearly new content.

View File

@ -386,11 +386,6 @@ class Node extends EntityNG implements NodeInterface {
'value' => array('EntityChanged' => array()),
),
);
$properties['comment'] = array(
'label' => t('Comment'),
'description' => t('Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).'),
'type' => 'integer_field',
);
$properties['promote'] = array(
'label' => t('Promote'),
'description' => t('A boolean indicating whether the node should be displayed on the front page.'),

View File

@ -70,16 +70,6 @@ class NodeStorageController extends DatabaseStorageControllerNG {
}
}
/**
* {@inheritdoc}
*/
protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
// @todo Remove this once comment is a regular entity field.
$record = parent::mapToDataStorageRecord($entity, $langcode);
$record->comment = isset($record->comment) ? intval($record->comment) : 0;
return $record;
}
/**
* Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
*/

View File

@ -27,7 +27,6 @@ class NodeRow extends EntityRow {
$options['view_mode']['default'] = 'teaser';
$options['links'] = array('default' => TRUE, 'bool' => TRUE);
$options['comments'] = array('default' => FALSE, 'bool' => TRUE);
return $options;
}
@ -43,11 +42,6 @@ class NodeRow extends EntityRow {
'#title' => t('Display links'),
'#default_value' => $this->options['links'],
);
$form['comments'] = array(
'#type' => 'checkbox',
'#title' => t('Display comments'),
'#default_value' => $this->options['comments'],
);
}
}

View File

@ -115,16 +115,6 @@ class Node extends WizardPluginBase {
),
'#default_value' => 1,
);
$style_form['row_options']['comments'] = array(
'#type' => 'select',
'#title' => t('Should comments be displayed below each node'),
'#title_display' => 'invisible',
'#options' => array(
1 => t('with comments'),
0 => t('without comments'),
),
'#default_value' => 0,
);
break;
}
}
@ -222,13 +212,11 @@ class Node extends WizardPluginBase {
$display_options['row']['type'] = 'entity:node';
$display_options['row']['options']['build_mode'] = 'full';
$display_options['row']['options']['links'] = !empty($row_options['links']);
$display_options['row']['options']['comments'] = !empty($row_options['comments']);
break;
case 'teasers':
$display_options['row']['type'] = 'entity:node';
$display_options['row']['options']['build_mode'] = 'teaser';
$display_options['row']['options']['links'] = !empty($row_options['links']);
$display_options['row']['options']['comments'] = !empty($row_options['comments']);
break;
case 'titles_linked':
$display_options['row']['type'] = 'fields';

View File

@ -34,6 +34,7 @@ class NodeAccessPagerTest extends WebTestBase {
parent::setUp();
node_access_rebuild();
$this->container->get('comment.manager')->addDefaultField('node', 'page');
$this->web_user = $this->drupalCreateUser(array('access content', 'access comments', 'node test view'));
}
@ -47,8 +48,9 @@ class NodeAccessPagerTest extends WebTestBase {
// Create 60 comments.
for ($i = 0; $i < 60; $i++) {
$comment = entity_create('comment', array(
'nid' => $node->id(),
'node_type' => 'node_type_' . $node->bundle(),
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'subject' => $this->randomName(),
'comment_body' => array(
array('value' => $this->randomName()),

View File

@ -34,6 +34,7 @@ class NodeTitleTest extends NodeTestBase {
$this->admin_user = $this->drupalCreateUser(array('administer nodes', 'create article content', 'create page content', 'post comments'));
$this->drupalLogin($this->admin_user);
$this->container->get('comment.manager')->addDefaultField('node', 'page');
}
/**
@ -54,7 +55,7 @@ class NodeTitleTest extends NodeTestBase {
$this->assertEqual(current($this->xpath($xpath)), $node->label() .' | Drupal', 'Page title is equal to node title.', 'Node');
// Test breadcrumb in comment preview.
$this->drupalGet('comment/reply/' . $node->id());
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment');
$xpath = '//nav[@class="breadcrumb"]/ol/li[last()]/a';
$this->assertEqual(current($this->xpath($xpath)), $node->label(), 'Node breadcrumb is equal to node title.', 'Node');

View File

@ -55,6 +55,8 @@ class RowPluginTest extends NodeTestBase {
parent::setUp();
$this->drupalCreateContentType(array('type' => 'article'));
// Create comment field on article.
$this->container->get('comment.manager')->addDefaultField('node', 'article');
// Create two nodes, with 5 comments on all of them.
for ($i = 0; $i < 2; $i++) {
@ -74,7 +76,7 @@ class RowPluginTest extends NodeTestBase {
foreach ($this->nodes as $node) {
for ($i = 0; $i < 5; $i++) {
$this->comments[$node->id()][] = $this->drupalCreateComment(array('nid' => $node->id()));
$this->comments[$node->id()][] = $this->drupalCreateComment(array('entity_id' => $node->id()));
}
}
}
@ -90,10 +92,11 @@ class RowPluginTest extends NodeTestBase {
* Returns the created and saved comment.
*/
public function drupalCreateComment(array $settings = array()) {
$node = node_load($settings['nid']);
$settings += array(
'subject' => $this->randomName(),
'node_type' => 'comment_node_' . $node->bundle(),
'entity_id' => $settings['entity_id'],
'field_name' => 'comment',
'entity_type' => 'node',
'comment_body' => $this->randomName(40),
);
@ -147,25 +150,6 @@ class RowPluginTest extends NodeTestBase {
$this->assertTrue($this->xpath('//li[contains(@class, :class)]/a[contains(@href, :href)]', array(':class' => 'node-readmore', ':href' => "node/{$node->id()}")), 'Make sure no readmore link appears.');
}
// Test with comments enabled.
$view->rowPlugin->options['comments'] = TRUE;
$output = $view->preview();
$output = drupal_render($output);
foreach ($this->nodes as $node) {
foreach ($this->comments[$node->id()] as $comment) {
$this->assertTrue(strpos($output, $comment->comment_body->value) !== FALSE, 'Make sure the comment appears in the output.');
}
}
// Test with comments disabled.
$view->rowPlugin->options['comments'] = FALSE;
$output = $view->preview();
$output = drupal_render($output);
foreach ($this->nodes as $node) {
foreach ($this->comments[$node->id()] as $comment) {
$this->assertFalse(strpos($output, $comment->comment_body->value) !== FALSE, 'Make sure the comment does not appears in the output when the comments option disabled.');
}
}
}
}

View File

@ -1,6 +1,8 @@
<?php
use Drupal\Core\Entity\EntityInterface;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Xss;
/**
* @file
@ -509,6 +511,7 @@ function hook_node_create(\Drupal\Core\Entity\EntityInterface $node) {
*/
function hook_node_load($nodes, $types) {
// Decide whether any of $types are relevant to our purposes.
$types_we_want_to_process = \Drupal::config('my_types')->get('types');
if (count(array_intersect($types_we_want_to_process, $types))) {
// Gather our extra data for each of these nodes.
$result = db_query('SELECT nid, foo FROM {mytable} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)));
@ -604,8 +607,8 @@ function hook_node_access(\Drupal\node\NodeInterface $node, $op, $account, $lang
* @ingroup node_api_hooks
*/
function hook_node_prepare_form(\Drupal\node\NodeInterface $node, $form_display, $operation, array &$form_state) {
if (!isset($node->comment->value)) {
$node->comment = variable_get('comment_' . $node->getType(), COMMENT_NODE_OPEN);
if (!isset($node->my_rating)) {
$node->my_rating = \Drupal::config("my_rating_{$node->bundle()}")->get('enabled');
}
}
@ -632,8 +635,8 @@ function hook_node_prepare_form(\Drupal\node\NodeInterface $node, $form_display,
* @ingroup node_api_hooks
*/
function hook_node_search_result(\Drupal\Core\Entity\EntityInterface $node, $langcode) {
$comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->id()))->fetchField();
return array('comment' => format_plural($comments, '1 comment', '@count comments'));
$rating = db_query('SELECT SUM(points) FROM {my_rating} WHERE nid = :nid', array('nid' => $node->id()))->fetchField();
return array('rating' => format_plural($rating, '1 point', '@count points'));
}
/**
@ -701,9 +704,9 @@ function hook_node_update(\Drupal\Core\Entity\EntityInterface $node) {
*/
function hook_node_update_index(\Drupal\Core\Entity\EntityInterface $node, $langcode) {
$text = '';
$comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->id(), ':status' => COMMENT_PUBLISHED));
foreach ($comments as $comment) {
$text .= '<h2>' . check_plain($comment->subject->value) . '</h2>' . $comment->comment_body->processed;
$ratings = db_query('SELECT title, description FROM {my_ratings} WHERE nid = :nid', array(':nid' => $node->id()));
foreach ($ratings as $rating) {
$text .= '<h2>' . String::checkPlain($rating->title) . '</h2>' . Xss::filter($rating->description);
}
return $text;
}
@ -790,7 +793,6 @@ function hook_node_submit(\Drupal\Core\Entity\EntityInterface $node, $form, &$fo
* The language code used for rendering.
*
* @see forum_node_view()
* @see comment_node_view()
* @see hook_entity_view()
*
* @ingroup node_api_hooks

View File

@ -152,12 +152,6 @@ function node_schema() {
'not null' => TRUE,
'default' => 0,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node translation: 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node translation should be displayed on the front page.',
'type' => 'int',
@ -276,12 +270,6 @@ function node_schema() {
'not null' => TRUE,
'default' => 0,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node (at the time of this revision): 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.',
'type' => 'int',
@ -817,12 +805,6 @@ function _node_update_8016_schema() {
'not null' => TRUE,
'default' => 0,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node translation: 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node translation should be displayed on the front page.',
'type' => 'int',
@ -939,12 +921,6 @@ function _node_update_8016_schema() {
'not null' => TRUE,
'default' => 0,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node (at the time of this revision): 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.',
'type' => 'int',
@ -1041,7 +1017,7 @@ function node_update_8018() {
foreach ($indexes as $index) {
db_drop_index('node', $index);
}
$fields = array('title', 'uid', 'status', 'created', 'changed', 'comment', 'promote', 'sticky');
$fields = array('title', 'uid', 'status', 'created', 'changed', 'promote', 'sticky');
foreach ($fields as $field) {
db_drop_field('node', $field);
}

View File

@ -242,7 +242,7 @@ function node_admin_paths() {
*
* @param $result
* A database result object from a query to fetch node entities. If your
* query joins the {node_comment_statistics} table so that the comment_count
* query joins the {comment_entity_statistics} table so that the comment_count
* field is available, a title attribute will be added to show the number of
* comments.
* @param $title
@ -2223,7 +2223,9 @@ function node_node_update(EntityInterface $node) {
*/
function node_comment_insert($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);
}
}
/**
@ -2231,7 +2233,9 @@ function node_comment_insert($comment) {
*/
function node_comment_update($comment) {
// Reindex the node when comments are changed.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}
/**
@ -2239,7 +2243,9 @@ function node_comment_update($comment) {
*/
function node_comment_delete($comment) {
// Reindex the node when comments are deleted.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}
/**
@ -2247,7 +2253,9 @@ function node_comment_delete($comment) {
*/
function node_comment_publish($comment) {
// Reindex the node when comments are published.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}
/**
@ -2255,5 +2263,7 @@ function node_comment_publish($comment) {
*/
function node_comment_unpublish($comment) {
// Reindex the node when comments are unpublished.
node_reindex_node_search($comment->nid->target_id);
if ($comment->entity_type->value == 'node') {
node_reindex_node_search($comment->entity_id->value);
}
}

View File

@ -623,21 +623,11 @@ function node_views_data() {
* Implements hook_preprocess_node().
*/
function node_row_node_view_preprocess_node(&$variables) {
$node = $variables['node'];
$options = $variables['view']->rowPlugin->options;
// Prevent the comment form from showing up if this is not a page display.
if ($variables['view_mode'] == 'full' && !$variables['view']->display_handler->hasPath()) {
$node->comment = FALSE;
}
if (!$options['links']) {
unset($variables['content']['links']);
}
if (!empty($options['comments']) && user_access('access comments') && $node->comment->value) {
$variables['content']['comments'] = comment_node_page_additions($node);
}
}
/**

View File

@ -15,13 +15,6 @@
* - sticky: Whether the node is 'sticky'. Sticky nodes are ordered above
* other non-sticky nodes in teaser listings
* - published: Whether the node is published.
* - comment: A value representing the comment status of the current node. May
* be one of the following:
* - 0: The comment form and any existing comments are hidden.
* - 1: Comments are closed. No new comments may be posted, but existing
* comments are displayed.
* - 2: Comments are open on this node.
* - comment_count: Number of comments attached to the node.
* - label: The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
@ -100,13 +93,11 @@
{% endif %}
<div{{ content_attributes }}>
{# We hide the comments and links now so that we can render them later. #}
{% hide(content.comments) %}
{# We hide links now so that we can render them later. #}
{% hide(content.links) %}
{{ content }}
</div>
{{ content.links }}
{{ content.comments }}
</article>

Some files were not shown because too many files have changed in this diff Show More