comment.module

Same filename in other branches
  1. 7.x modules/comment/comment.module
  2. 9 core/modules/comment/comment.module
  3. 8.9.x core/modules/comment/comment.module
  4. 10 core/modules/comment/comment.module

File

core/modules/comment/comment.module

View source
<?php


/**
 * @file
 */
use Drupal\comment\CommentInterface;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Link;
use Drupal\Core\Url;

/**
 * The time cutoff for comments marked as read for entity types other node.
 *
 * Comments changed before this time are always marked as read.
 * Comments changed after this time may be marked new, updated, or read,
 * depending on their state for the current user. Defaults to 30 days ago.
 *
 * @todo Remove when https://www.drupal.org/node/2006632 lands.
 */
define('COMMENT_NEW_LIMIT', (int) $_SERVER['REQUEST_TIME'] - 30 * 24 * 60 * 60);

/**
 * Entity URI callback.
 */
function comment_uri(CommentInterface $comment) {
    return new Url('entity.comment.canonical', [
        'comment' => $comment->id(),
    ], [
        'fragment' => 'comment-' . $comment->id(),
    ]);
}

/**
 * Determines if an entity type is using an integer-based ID definition.
 *
 * @param string $entity_type_id
 *   The ID the represents the entity type.
 *
 * @return bool
 *   Returns TRUE if the entity type has an integer-based ID definition and
 *   FALSE otherwise.
 */
function _comment_entity_uses_integer_id($entity_type_id) {
    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
    $entity_type_id_key = $entity_type->getKey('id');
    if ($entity_type_id_key === FALSE) {
        return FALSE;
    }
    $field_definitions = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($entity_type->id());
    $entity_type_id_definition = $field_definitions[$entity_type_id_key];
    return $entity_type_id_definition->getType() === 'integer';
}

/**
 * Generates a comment preview.
 *
 * @param \Drupal\comment\CommentInterface $comment
 *   The comment entity to preview.
 * @param Drupal\Core\Form\FormStateInterface $form_state
 *   The current state of the form.
 *
 * @return array
 *   An array as expected by \Drupal\Core\Render\RendererInterface::render().
 */
function comment_preview(CommentInterface $comment, FormStateInterface $form_state) : array {
    $preview_build = [];
    $entity = $comment->getCommentedEntity();
    if (!$form_state->getErrors()) {
        $comment->in_preview = TRUE;
        $comment_build = \Drupal::entityTypeManager()->getViewBuilder('comment')
            ->view($comment);
        $comment_build['#weight'] = -100;
        $preview_build['comment_preview'] = $comment_build;
    }
    if ($comment->hasParentComment()) {
        $build = [];
        $parent = $comment->getParentComment();
        if ($parent && $parent->isPublished()) {
            $build = \Drupal::entityTypeManager()->getViewBuilder('comment')
                ->view($parent);
        }
    }
    else {
        // The comment field output includes rendering the parent entity of the
        // thread to which the comment is a reply. The rendered entity output
        // includes the comment reply form, which contains the comment preview and
        // therefore the rendered parent entity. This results in an infinite loop of
        // parent entity output rendering the comment form and the comment form
        // rendering the parent entity. To prevent this infinite loop we temporarily
        // set the value of the comment field on a clone of the entity to hidden
        // before calling the entity view builder. That way when the output of
        // the commented entity is rendered, it excludes the comment field output.
        $field_name = $comment->getFieldName();
        $entity = clone $entity;
        $entity->{$field_name}->status = CommentItemInterface::HIDDEN;
        $build = \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId())
            ->view($entity, 'full');
    }
    $preview_build['comment_output_below'] = $build;
    $preview_build['comment_output_below']['#weight'] = 200;
    return $preview_build;
}

/**
 * Implements hook_preprocess_HOOK() for block templates.
 */
function comment_preprocess_block(&$variables) : void {
    if ($variables['configuration']['provider'] == 'comment') {
        $variables['attributes']['role'] = 'navigation';
    }
}

/**
 * Prepares variables for comment templates.
 *
 * By default this function performs special preprocessing of some base fields
 * so they are available as variables in the template. For example 'subject'
 * appears as 'title'. This preprocessing is skipped if:
 * - a module makes the field's display configurable via the field UI by means
 *   of BaseFieldDefinition::setDisplayConfigurable()
 * - AND the additional entity type property
 *   'enable_base_field_custom_preprocess_skipping' has been set using
 *   hook_entity_type_build().
 *
 * Default template: comment.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing the comment and entity objects.
 *     Array keys: #comment, #commented_entity.
 */
function template_preprocess_comment(&$variables) : void {
    
    /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
    $date_formatter = \Drupal::service('date.formatter');
    
    /** @var \Drupal\comment\CommentInterface $comment */
    $comment = $variables['elements']['#comment'];
    $commented_entity = $comment->getCommentedEntity();
    $variables['comment'] = $comment;
    $variables['commented_entity'] = $commented_entity;
    $variables['threaded'] = $variables['elements']['#comment_threaded'];
    $skip_custom_preprocessing = $comment->getEntityType()
        ->get('enable_base_field_custom_preprocess_skipping');
    // Make created, uid, pid and subject fields available separately. Skip this
    // custom preprocessing if the field display is configurable and skipping has
    // been enabled.
    // @todo https://www.drupal.org/project/drupal/issues/3015623
    //   Eventually delete this code and matching template lines. Using
    //   $variables['content'] is more flexible and consistent.
    $submitted_configurable = $comment->getFieldDefinition('created')
        ->isDisplayConfigurable('view') || $comment->getFieldDefinition('uid')
        ->isDisplayConfigurable('view');
    if (!$skip_custom_preprocessing || !$submitted_configurable) {
        $account = $comment->getOwner();
        $username = [
            '#theme' => 'username',
            '#account' => $account,
        ];
        $variables['author'] = \Drupal::service('renderer')->render($username);
        $variables['author_id'] = $comment->getOwnerId();
        $variables['new_indicator_timestamp'] = $comment->getChangedTime();
        $variables['created'] = $date_formatter->format($comment->getCreatedTime());
        // Avoid calling DateFormatterInterface::format() twice on the same
        // timestamp.
        if ($comment->getChangedTime() == $comment->getCreatedTime()) {
            $variables['changed'] = $variables['created'];
        }
        else {
            $variables['changed'] = $date_formatter->format($comment->getChangedTime());
        }
        if (theme_get_setting('features.comment_user_picture')) {
            // To change user picture settings (for instance, image style), edit the
            // 'compact' view mode on the User entity.
            $variables['user_picture'] = \Drupal::entityTypeManager()->getViewBuilder('user')
                ->view($account, 'compact');
        }
        else {
            $variables['user_picture'] = [];
        }
        $variables['submitted'] = t('Submitted by @username on @datetime', [
            '@username' => $variables['author'],
            '@datetime' => $variables['created'],
        ]);
    }
    if (isset($comment->in_preview)) {
        $variables['permalink'] = Link::fromTextAndUrl(t('Permalink'), Url::fromRoute('<front>'))->toString();
    }
    else {
        $variables['permalink'] = Link::fromTextAndUrl(t('Permalink'), $comment->permalink())
            ->toString();
    }
    if (($comment_parent = $comment->getParentComment()) && (!$skip_custom_preprocessing || !$comment->getFieldDefinition('pid')
        ->isDisplayConfigurable('view'))) {
        // Fetch and store the parent comment information for use in templates.
        $account_parent = $comment_parent->getOwner();
        $variables['parent_comment'] = $comment_parent;
        $username = [
            '#theme' => 'username',
            '#account' => $account_parent,
        ];
        $variables['parent_author'] = \Drupal::service('renderer')->render($username);
        $variables['parent_created'] = $date_formatter->format($comment_parent->getCreatedTime());
        // Avoid calling DateFormatterInterface::format() twice on same timestamp.
        if ($comment_parent->getChangedTime() == $comment_parent->getCreatedTime()) {
            $variables['parent_changed'] = $variables['parent_created'];
        }
        else {
            $variables['parent_changed'] = $date_formatter->format($comment_parent->getChangedTime());
        }
        $permalink_uri_parent = $comment_parent->permalink();
        $attributes = $permalink_uri_parent->getOption('attributes') ?: [];
        $attributes += [
            'class' => [
                'permalink',
            ],
            'rel' => 'bookmark',
        ];
        $permalink_uri_parent->setOption('attributes', $attributes);
        $variables['parent_title'] = Link::fromTextAndUrl($comment_parent->getSubject(), $permalink_uri_parent)
            ->toString();
        $variables['parent_permalink'] = Link::fromTextAndUrl(t('Parent permalink'), $permalink_uri_parent)->toString();
        $variables['parent'] = t('In reply to @parent_title by @parent_username', [
            '@parent_username' => $variables['parent_author'],
            '@parent_title' => $variables['parent_title'],
        ]);
    }
    else {
        $variables['parent_comment'] = '';
        $variables['parent_author'] = '';
        $variables['parent_created'] = '';
        $variables['parent_changed'] = '';
        $variables['parent_title'] = '';
        $variables['parent_permalink'] = '';
        $variables['parent'] = '';
    }
    if (!$skip_custom_preprocessing || !$comment->getFieldDefinition('subject')
        ->isDisplayConfigurable('view')) {
        if (isset($comment->in_preview)) {
            $variables['title'] = Link::fromTextAndUrl($comment->getSubject(), Url::fromRoute('<front>'))
                ->toString();
        }
        else {
            $uri = $comment->permalink();
            $attributes = $uri->getOption('attributes') ?: [];
            $attributes += [
                'class' => [
                    'permalink',
                ],
                'rel' => 'bookmark',
            ];
            $uri->setOption('attributes', $attributes);
            $variables['title'] = Link::fromTextAndUrl($comment->getSubject(), $uri)
                ->toString();
        }
    }
    // Helpful $content variable for templates.
    foreach (Element::children($variables['elements']) as $key) {
        $variables['content'][$key] = $variables['elements'][$key];
    }
    // Set status to a string representation of comment->status.
    if (isset($comment->in_preview)) {
        $variables['status'] = 'preview';
    }
    else {
        $variables['status'] = $comment->isPublished() ? 'published' : 'unpublished';
    }
    // Add comment author user ID. Necessary for the comment-by-viewer library.
    $variables['attributes']['data-comment-user-id'] = $comment->getOwnerId();
    // Add anchor for each comment.
    $variables['attributes']['id'] = 'comment-' . $comment->id();
}

/**
 * Prepares variables for comment field templates.
 *
 * Default template: field--comment.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - element: An associative array containing render arrays for the list of
 *     comments, and the comment form. Array keys: comments, comment_form.
 *
 * @todo Rename to template_preprocess_field__comment() once
 *   https://www.drupal.org/node/939462 is resolved.
 */
function comment_preprocess_field(&$variables) : void {
    $element = $variables['element'];
    if ($element['#field_type'] == 'comment') {
        // Provide contextual information.
        $variables['comment_display_mode'] = $element[0]['#comment_display_mode'];
        $variables['comment_type'] = $element[0]['#comment_type'];
        // Append additional attributes from the first field item.
        $variables['attributes'] += $variables['items'][0]['attributes']->storage();
        // Create separate variables for the comments and comment form.
        $variables['comments'] = $element[0]['comments'];
        $variables['comment_form'] = $element[0]['comment_form'];
    }
}

Functions

Title Deprecated Summary
comment_preprocess_block Implements hook_preprocess_HOOK() for block templates.
comment_preprocess_field Prepares variables for comment field templates.
comment_preview Generates a comment preview.
comment_uri Entity URI callback.
template_preprocess_comment Prepares variables for comment templates.
_comment_entity_uses_integer_id Determines if an entity type is using an integer-based ID definition.

Constants

Title Deprecated Summary
COMMENT_NEW_LIMIT The time cutoff for comments marked as read for entity types other node.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.