$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; } /** * 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(''))->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(''))->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']; } }