Issue #2958241 by fjgarlin, thetwentyseven, Wim Leers, joachim, immaculatexavier, fulgent, larowlan, lastlink, andypost, nnevill: Impossible to reply to comments: commented entity considered unreferencable because CommentSelection::entityQueryAlter() joins on {node_field_data} table
parent
c78d1a8987
commit
3f9d9b449f
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Drupal\comment\Plugin\EntityReferenceSelection;
|
namespace Drupal\comment\Plugin\EntityReferenceSelection;
|
||||||
|
|
||||||
|
use Drupal\Component\Utility\Html;
|
||||||
use Drupal\Core\Database\Query\SelectInterface;
|
use Drupal\Core\Database\Query\SelectInterface;
|
||||||
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
|
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
|
||||||
use Drupal\comment\CommentInterface;
|
use Drupal\comment\CommentInterface;
|
||||||
|
|
@ -76,19 +77,75 @@ class CommentSelection extends DefaultSelection {
|
||||||
$query->innerJoin($data_table, NULL, "[base_table].[cid] = [$data_table].[cid] AND [$data_table].[default_langcode] = 1");
|
$query->innerJoin($data_table, NULL, "[base_table].[cid] = [$data_table].[cid] AND [$data_table].[default_langcode] = 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Comment module doesn't implement any proper comment access,
|
// Find the host entity type the comment field is on.
|
||||||
// and as a consequence doesn't make sure that comments cannot be viewed
|
$comment = $this->getConfiguration()['entity'];
|
||||||
// when the user doesn't have access to the node.
|
if ($comment) {
|
||||||
$node_alias = $query->innerJoin('node_field_data', 'n', "[%alias].[nid] = [$data_table].[entity_id] AND [$data_table].[entity_type] = 'node'");
|
$host_entity_type_id = $comment->getCommentedEntityTypeId();
|
||||||
// Pass the query to the node access control.
|
|
||||||
$this->reAlterQuery($query, 'node_access', $node_alias);
|
|
||||||
|
|
||||||
|
/** @var \Drupal\Core\Entity\EntityTypeInterface $host_entity_type */
|
||||||
|
$host_entity_type = $this->entityTypeManager->getDefinition($host_entity_type_id);
|
||||||
|
$host_entity_field_data_table = $host_entity_type->getDataTable();
|
||||||
|
|
||||||
|
// Not all entities have a data table, so check first.
|
||||||
|
if ($host_entity_field_data_table) {
|
||||||
|
$id_key = $host_entity_type->getKey('id');
|
||||||
|
|
||||||
|
// The Comment module doesn't implement per-comment access, so it
|
||||||
|
// checks instead that the user has access to the host entity.
|
||||||
|
$entity_alias = $query->innerJoin($host_entity_field_data_table, 'n', "[%alias].[$id_key] = [$data_table].[entity_id] AND [$data_table].[entity_type] = '$host_entity_type_id'");
|
||||||
|
// Pass the query to the entity access control.
|
||||||
|
$this->reAlterQuery($query, $host_entity_type_id . '_access', $entity_alias);
|
||||||
|
|
||||||
|
// Additional checks for "node" entities.
|
||||||
|
if ($host_entity_type_id === 'node') {
|
||||||
// Passing the query to node_query_node_access_alter() is sadly
|
// Passing the query to node_query_node_access_alter() is sadly
|
||||||
// insufficient for nodes.
|
// insufficient for nodes.
|
||||||
// @see \Drupal\node\Plugin\EntityReferenceSelection\NodeSelection::buildEntityQuery()
|
// @see \Drupal\node\Plugin\EntityReferenceSelection\NodeSelection::buildEntityQuery()
|
||||||
if (!$this->currentUser->hasPermission('bypass node access') && !$this->moduleHandler->hasImplementations('node_grants')) {
|
if (!$this->currentUser->hasPermission('bypass node access') && !$this->moduleHandler->hasImplementations('node_grants')) {
|
||||||
$query->condition($node_alias . '.status', 1);
|
$query->condition($entity_alias . '.status', 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
|
||||||
|
$target_type = $this->getConfiguration()['target_type'];
|
||||||
|
|
||||||
|
$query = $this->buildEntityQuery($match, $match_operator);
|
||||||
|
if ($limit > 0) {
|
||||||
|
$query->range(0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $query->execute();
|
||||||
|
|
||||||
|
if (empty($result)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = [];
|
||||||
|
$entities = $this->entityTypeManager->getStorage($target_type)->loadMultiple($result);
|
||||||
|
foreach ($entities as $entity_id => $entity) {
|
||||||
|
// Additional access check as comments might be attached to entities
|
||||||
|
// which the current user does not have access to.
|
||||||
|
if ($entity->access('view', $this->currentUser)) {
|
||||||
|
$bundle = $entity->bundle();
|
||||||
|
$options[$bundle][$entity_id] = Html::escape($this->entityRepository->getTranslationFromContext($entity)->label() ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS') {
|
||||||
|
$options = $this->getReferenceableEntities($match, $match_operator);
|
||||||
|
return count($options, COUNT_RECURSIVE) - count($options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ class CommentNonNodeTest extends BrowserTestBase {
|
||||||
* @return \Drupal\comment\CommentInterface
|
* @return \Drupal\comment\CommentInterface
|
||||||
* The new comment entity.
|
* The new comment entity.
|
||||||
*/
|
*/
|
||||||
public function postComment(EntityInterface $entity, $comment, $subject = '', $contact = NULL) {
|
public function postComment(?EntityInterface $entity, $comment, $subject = '', $contact = NULL) {
|
||||||
$edit = [];
|
$edit = [];
|
||||||
$edit['comment_body[0][value]'] = $comment;
|
$edit['comment_body[0][value]'] = $comment;
|
||||||
|
|
||||||
|
|
@ -309,6 +309,18 @@ class CommentNonNodeTest extends BrowserTestBase {
|
||||||
$xpath = '//nav[@aria-labelledby="system-breadcrumb"]/ol/li[last()]/a';
|
$xpath = '//nav[@aria-labelledby="system-breadcrumb"]/ol/li[last()]/a';
|
||||||
$this->assertEquals($comment1->getSubject(), current($this->xpath($xpath))->getText(), 'Last breadcrumb item is equal to comment subject on delete confirm page.');
|
$this->assertEquals($comment1->getSubject(), current($this->xpath($xpath))->getText(), 'Last breadcrumb item is equal to comment subject on delete confirm page.');
|
||||||
|
|
||||||
|
// Test threading replying to comment #1 creating comment #1_2.
|
||||||
|
$this->drupalGet('comment/reply/entity_test/' . $this->entity->id() . '/comment/' . $comment1->id());
|
||||||
|
$comment1_2 = $this->postComment(NULL, $this->randomMachineName(), $this->randomMachineName());
|
||||||
|
$this->assertTrue($this->commentExists($comment1_2, TRUE), 'Comment #1_2. Reply found.');
|
||||||
|
$this->assertEquals('01.00/', $comment1_2->getThread());
|
||||||
|
|
||||||
|
// Test nested threading replying to comment #1_2 creating comment #1_2_3.
|
||||||
|
$this->drupalGet('comment/reply/entity_test/' . $this->entity->id() . '/comment/' . $comment1_2->id());
|
||||||
|
$comment1_2_3 = $this->postComment(NULL, $this->randomMachineName(), $this->randomMachineName());
|
||||||
|
$this->assertTrue($this->commentExists($comment1_2_3, TRUE), 'Comment #1_2_3. Reply found.');
|
||||||
|
$this->assertEquals('01.00.00/', $comment1_2_3->getThread());
|
||||||
|
|
||||||
// Unpublish the comment.
|
// Unpublish the comment.
|
||||||
$this->performCommentOperation($comment1, 'unpublish');
|
$this->performCommentOperation($comment1, 'unpublish');
|
||||||
$this->drupalGet('admin/content/comment/approval');
|
$this->drupalGet('admin/content/comment/approval');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue