Newer
Older
Angie Byron
committed
<?php
/**
* @file
Alex Pott
committed
* Definition of Drupal\comment\CommentViewBuilder.
Angie Byron
committed
*/
namespace Drupal\comment;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
Angie Byron
committed
use Drupal\Core\Entity\EntityInterface;
Angie Byron
committed
use Drupal\Core\Entity\EntityManagerInterface;
Alex Pott
committed
use Drupal\Core\Entity\EntityTypeInterface;
Alex Pott
committed
use Drupal\Core\Entity\EntityViewBuilder;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\field\FieldInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
Angie Byron
committed
/**
* Render controller for comments.
*/
class CommentViewBuilder extends EntityViewBuilder {
/**
* The field info service.
*
* @var \Drupal\field\FieldInfo
*/
protected $fieldInfo;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The CSRF token manager service.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;
/**
* {@inheritdoc}
*/
Alex Pott
committed
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
return new static(
$entity_info,
$container->get('entity.manager'),
$container->get('language_manager'),
$container->get('field.info'),
$container->get('csrf_token')
);
}
/**
Alex Pott
committed
* Constructs a new CommentViewBuilder.
Alex Pott
committed
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
* The entity information array.
Angie Byron
committed
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\field\FieldInfo $field_info
* The field info service.
* @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
* The CSRF token manager service.
public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, FieldInfo $field_info, CsrfTokenGenerator $csrf_token) {
parent::__construct($entity_info, $entity_manager, $language_manager);
$this->fieldInfo = $field_info;
$this->csrfToken = $csrf_token;
Angie Byron
committed
/**
Alex Pott
committed
* Overrides Drupal\Core\Entity\EntityViewBuilder::buildContent().
Angie Byron
committed
*
* In addition to modifying the content key on entities, this implementation
* 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.
Angie Byron
committed
*/
Angie Byron
committed
public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) {
Angie Byron
committed
$return = array();
if (empty($entities)) {
return $return;
}
// Pre-load associated users into cache to leverage multiple loading.
$uids = array();
foreach ($entities as $entity) {
Dries Buytaert
committed
$uids[] = $entity->uid->target_id;
}
$this->entityManager->getStorageController('user')->loadMultiple(array_unique($uids));
Angie Byron
committed
parent::buildContent($entities, $displays, $view_mode, $langcode);
Angie Byron
committed
// Load all the entities that have comments attached.
$commented_entity_ids = array();
$commented_entities = array();
Angie Byron
committed
foreach ($entities as $entity) {
$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);
foreach ($entities as $entity) {
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 entity for comment.'));
Angie Byron
committed
}
$entity->content['#entity'] = $entity;
$entity->content['#theme'] = 'comment__' . $entity->field_id->value . '__' . $commented_entity->bundle();
Angie Byron
committed
$entity->content['links'] = array(
'#type' => 'render_cache_placeholder',
'#callback' => '\Drupal\comment\CommentViewBuilder::renderLinks',
'#context' => array(
'comment_entity_id' => $entity->id(),
'view_mode' => $view_mode,
'langcode' => $langcode,
'commented_entity_type' => $commented_entity->entityType(),
'commented_entity_id' => $commented_entity->id(),
'in_preview' => !empty($entity->in_preview),
),
Angie Byron
committed
);
if (!isset($entity->content['#attached'])) {
$entity->content['#attached'] = array();
}
$entity->content['#attached']['library'][] = array('comment', 'drupal.comment-by-viewer');
if ($this->moduleHandler->moduleExists('history') && \Drupal::currentUser()->isAuthenticated()) {
$entity->content['#attached']['library'][] = array('comment', 'drupal.comment-new-indicator');
// Embed the metadata for the comment "new" indicators on this node.
$entity->content['#post_render_cache']['history_attach_timestamp'] = array(
array('node_id' => $commented_entity->id()),
);
Angie Byron
committed
}
}
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
* #post_render_cache callback; replaces the placeholder with comment links.
*
* Renders the links on a comment.
*
* @param array $context
* An array with the following keys:
* - comment_entity_id: a comment entity ID
* - view_mode: the view mode in which the comment entity is being viewed
* - langcode: in which language the comment entity is being viewed
* - commented_entity_type: the entity type to which the comment is attached
* - commented_entity_id: the entity ID to which the comment is attached
* - in_preview: whether the comment is currently being previewed
*
* @return array
* A renderable array representing the comment links.
*/
public static function renderLinks(array $context) {
$links = array(
'#theme' => 'links__comment',
'#pre_render' => array('drupal_pre_render_links'),
'#attributes' => array('class' => array('links', 'inline')),
);
if (!$context['in_preview']) {
$entity = entity_load('comment', $context['comment_entity_id']);
$commented_entity = entity_load($context['commented_entity_type'], $context['commented_entity_id']);
$links['comment'] = self::buildLinks($entity, $commented_entity);
// Allow other modules to alter the comment links.
$hook_context = array(
'view_mode' => $context['view_mode'],
'langcode' => $context['langcode'],
'commented_entity' => $commented_entity
);
\Drupal::moduleHandler()->alter('comment_links', $links, $entity, $hook_context);
}
return $links;
}
/**
* Build the default links (reply, edit, delete …) for a comment.
*
* @param \Drupal\comment\CommentInterface $entity
* The comment object.
* @param \Drupal\Core\Entity\EntityInterface $commented_entity
* The entity to which the comment is attached.
*
* @return array
* An array that can be processed by drupal_pre_render_links().
*/
protected static function buildLinks(CommentInterface $entity, EntityInterface $commented_entity) {
$links = array();
$status = $commented_entity->get($entity->field_name->value)->status;
$container = \Drupal::getContainer();
if ($status == COMMENT_OPEN) {
if ($entity->access('delete')) {
$links['comment-delete'] = array(
'title' => t('Delete'),
'href' => "comment/{$entity->id()}/delete",
'html' => TRUE,
);
}
if ($entity->access('update')) {
$links['comment-edit'] = array(
'title' => t('Edit'),
'href' => "comment/{$entity->id()}/edit",
'html' => TRUE,
);
}
if ($entity->access('create')) {
$links['comment-reply'] = array(
'title' => t('Reply'),
'href' => "comment/reply/{$entity->entity_type->value}/{$entity->entity_id->value}/{$entity->field_name->value}/{$entity->id()}",
'html' => TRUE,
);
}
if ($entity->status->value == CommentInterface::NOT_PUBLISHED && $entity->access('approve')) {
$links['comment-approve'] = array(
'title' => t('Approve'),
Angie Byron
committed
'route_name' => 'comment.approve',
'route_parameters' => array('comment' => $entity->id()),
'html' => TRUE,
);
}
if (empty($links)) {
$comment_post_forbidden = array(
'#theme' => 'comment_post_forbidden',
'#commented_entity' => $commented_entity,
'#field_name' => $entity->field_name->value,
);
$links['comment-forbidden']['title'] = drupal_render($comment_post_forbidden);
$links['comment-forbidden']['html'] = TRUE;
}
}
// Add translations link for translation-enabled comment bundles.
if ($container->get('module_handler')->moduleExists('content_translation') && content_translation_translate_access($entity)) {
$links['comment-translations'] = array(
'title' => t('Translate'),
'href' => 'comment/' . $entity->id() . '/translations',
'html' => TRUE,
);
}
return array(
'#theme' => 'links__comment__comment',
// The "entity" property is specified to be present, so no need to
// check.
'#links' => $links,
'#attributes' => array('class' => array('links', 'inline')),
);
}
Angie Byron
committed
/**
Alex Pott
committed
* {@inheritdoc}
Angie Byron
committed
*/
protected function alterBuild(array &$build, EntityInterface $comment, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) {
Angie Byron
committed
parent::alterBuild($build, $comment, $display, $view_mode, $langcode);
Angie Byron
committed
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);
Angie Byron
committed
$is_threaded = isset($comment->divs)
Alex Pott
committed
&& $instance->getSetting('default_mode') == COMMENT_MODE_THREADED;
Angie Byron
committed
// Add indentation div or close open divs as needed.
if ($is_threaded) {
Alex Pott
committed
$build['#attached']['css'][] = drupal_get_path('module', 'comment') . '/css/comment.theme.css';
Angie Byron
committed
$prefix .= $comment->divs <= 0 ? str_repeat('</div>', abs($comment->divs)) : "\n" . '<div class="indented">';
}
// Add anchor for each comment.
$prefix .= "<a id=\"comment-{$comment->id()}\"></a>\n";
Angie Byron
committed
$build['#prefix'] = $prefix;
// Close all open divs.
if ($is_threaded && !empty($comment->divs_final)) {
$build['#suffix'] = str_repeat('</div>', $comment->divs_final);
}
}
}
Angie Byron
committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/**
* #post_render_cache callback; attaches "X new comments" link metadata.
*
* @param array $element
* A render array with the following keys:
* - #markup
* - #attached
* @param array $context
* An array with the following keys:
* - entity_type: an entity type
* - entity_id: an entity ID
* - field_name: a comment field name
*
* @return array $element
* The updated $element.
*/
public static function attachNewCommentsLinkMetadata(array $element, array $context) {
// Build "X new comments" link metadata.
$new = (int)comment_num_new($context['entity_id'], $context['entity_type']);
// Early-return if there are zero new comments for the current user.
if ($new === 0) {
return $element;
}
$entity = \Drupal::entityManager()
->getStorageController($context['entity_type'])
->load($context['entity_id']);
$field_name = $context['field_name'];
$query = comment_new_page_count($entity->{$field_name}->comment_count, $new, $entity);
// Attach metadata.
$element['#attached']['js'][] = array(
'type' => 'setting',
'data' => array(
'comment' => array(
'newCommentsLinks' => array(
$context['entity_type'] => array(
$context['field_name'] => array(
$context['entity_id'] => array(
'new_comment_count' => (int)$new,
'first_new_comment_link' => \Drupal::urlGenerator()->generateFromPath('node/' . $entity->id(), array('query' => $query, 'fragment' => 'new')),
)
)
),
)
),
),
);
return $element;
}