You are here

comments_order.module in Comments Order 8

File

comments_order.module
View source
<?php

/**
 * @file
 * Contains comments_order.module.
 */
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_help().
 */
function comments_order_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {

    // Main module help for the comments_order module.
    case 'help.page.comments_order':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('This module allows to set order for node comments') . '</p>';
      return $output;
  }
}

/**
 * Implements hook_query_TAG_alter() for comment_filter tag.
 */
function comments_order_query_comment_filter_alter(AlterableInterface $query) {

  // Change comment order to DESC for 'comment' field.
  if ($query
    ->getMetaData('base_table') == 'comment') {

    /** @var \Drupal\Core\Database\Query\SelectInterface $query */
    $order_by =& $query
      ->getOrderBy();
    $expression =& $query
      ->getExpressions();

    /** @var \Drupal\Core\Entity\Entity $entity */
    $entity = $query
      ->getMetaData('entity');

    /** @var \Drupal\field\Entity\FieldConfig $field */
    $field = FieldConfig::loadByName($entity
      ->getEntityTypeId(), $entity
      ->bundle(), $query
      ->getMetaData('field_name'));

    // Set reverse order if checked.
    $field_order = $field
      ->getThirdPartySetting('comments_order', 'order', 'ASC');
    if (substr($field_order, 0, 4) == 'DESC') {

      // 'c.cid' is for flat comment lists.
      if (isset($order_by['c.cid']) && substr($order_by['c.cid'], 0, 3) == 'ASC') {

        // Reverse order.
        $order_by['c.cid'] = 'DESC';
      }

      // 'torder' is for threaded comment lists.
      if (isset($order_by['torder']) && substr($order_by['torder'], 0, 3) == 'ASC') {
        if (!$field
          ->getThirdPartySetting('comments_order', 'children_natural_order', 1)) {

          // Reverse order for parent comments.
          // And reverse order for children comments.
          // EXPLANATION of '.z':
          // The 'thread' field is string like '10.01.12f'.
          // Each part of it is generated by Number::intToAlphadecimal.
          // See core/lib/Drupal/Component/Utility/Number.php.
          // This function returns string followed by character.
          // This character is finite:
          // Maximum integer value on a 64-bit system is 9223372036854775807.
          // In higher code variable $num will be '1y2p0ij32e8e7'.
          // And variable $length will be 13. Expression ord('0') is 48.
          // Therefore first character can be 60 at maximum.
          // For reverse sorting we need to add first heavy character,
          // For parent comment thread string.
          // If use previous logic the character 'z' will be always heaviest,
          // Than other available children thread string.
          // For each comment thread we add '.z' and sort by it.
          $expression['torder']['expression'] = 'SUBSTRING_INDEX(SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1)), \'.\', 1)';
          $order_by['torder'] = 'DESC';
          $query
            ->addExpression('CONCAT(SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1)), \'.z\')', 'torderchild');
          $query
            ->orderBy('torderchild', 'DESC');
        }
        else {

          // Reverse order for parent comments.
          // And natural order for children comments.
          // Children comments are sorted like default.
          $expression['torder']['expression'] = 'SUBSTRING_INDEX(SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1)), \'.\', 1)';
          $order_by['torder'] = 'DESC';
          $query
            ->addExpression('SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))', 'torderchild');
          $query
            ->orderBy('torderchild', 'ASC');
        }
      }
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function comments_order_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Alter 'field_config_edit_form' form.
  if ($form_id == 'field_config_edit_form') {

    /** @var \Drupal\field\Entity\FieldConfig $field */
    $field = $form_state
      ->getFormObject()
      ->getEntity();

    // Alter only for comments field.
    if ($field
      ->getType() == 'comment') {

      // Order field.
      $form['order'] = [
        '#type' => 'select',
        '#title' => t('Comments order'),
        '#options' => [
          'ASC' => t('Oldest first (ascending order)'),
          'DESC' => t('Newest first (descending order)'),
        ],
        '#default_value' => $field
          ->getThirdPartySetting('comments_order', 'order', 'ASC'),
        '#weight' => 10,
      ];

      // Children order field.
      $form['children_natural_order'] = [
        '#type' => 'checkbox',
        '#title' => t('Natural order for children'),
        '#default_value' => $field
          ->getThirdPartySetting('comments_order', 'children_natural_order', 1),
        '#states' => [
          'visible' => [
            'select[name="order"]' => [
              'value' => 'DESC',
            ],
          ],
        ],
        '#weight' => 11,
      ];

      // Add entity builder.
      $form['#entity_builders'][] = '_comments_order_field_config_edit_form_builder';
    }
  }
  elseif (strpos($form['#id'], 'comment-form') === 0) {

    // Add entity builder.
    $form['actions']['submit']['#submit'][] = '_comments_order_comment_form_submit';
  }
}

/**
 * Comments order form builder to map values to third party settings.
 */
function _comments_order_field_config_edit_form_builder($entity_type, FieldConfig $field, &$form, FormStateInterface $form_state) {

  // Save settings.
  $field
    ->setThirdPartySetting('comments_order', 'order', $form_state
    ->getValue('order'));
  $field
    ->setThirdPartySetting('comments_order', 'children_natural_order', $form_state
    ->getValue('children_natural_order'));
}

/**
 * Comment form builder to alter redirect.
 */
function _comments_order_comment_form_submit($form, FormStateInterface $form_state) {

  /** @var \Drupal\comment\Entity\Comment $comment */
  $comment = $form_state
    ->getFormObject()
    ->getEntity();

  /** @var \Drupal\Core\Entity\Entity $entity */
  $entity = $comment
    ->getCommentedEntity();

  /** @var \Drupal\field\Entity\FieldConfig $field */
  $field = FieldConfig::loadByName($entity
    ->getEntityTypeId(), $entity
    ->bundle(), $comment
    ->getFieldName());

  // Redirect to the first page after comment submition if set reverse order.
  if ($field
    ->getThirdPartySetting('comments_order', 'order', 'ASC') == 'DESC' && \Drupal::request()
    ->isXmlHttpRequest() == FALSE) {

    // Redirect to the first page after comment submition.
    $uri = $form_state
      ->getRedirect();
    $uri
      ->setOption('query', []);
    $form_state
      ->setRedirectUrl($uri);
  }
}

Functions

Namesort descending Description
comments_order_form_alter Implements hook_form_alter().
comments_order_help Implements hook_help().
comments_order_query_comment_filter_alter Implements hook_query_TAG_alter() for comment_filter tag.
_comments_order_comment_form_submit Comment form builder to alter redirect.
_comments_order_field_config_edit_form_builder Comments order form builder to map values to third party settings.