You are here

CommentDeleteManager.php in Comment Delete 8

File

src/CommentDeleteManager.php
View source
<?php

namespace Drupal\comment_delete;

use Drupal\comment\Entity\Comment;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Service container for comment delete operations.
 *
 * @package Drupal\comment_delete
 */
class CommentDeleteManager {
  use StringTranslationTrait;

  /**
   * The delete replies operation.
   *
   * @var int
   */
  const DELETE_REPLIES = 0;

  /**
   * The move replies operation.
   *
   * @var int
   */
  const MOVE_REPLIES = 1;

  /**
   * The keep replies operation.
   *
   * @var int
   */
  const KEEP_REPLIES = 2;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * The config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The comment storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $commentStorage;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The comment thread ripper.
   *
   * @var \Drupal\comment_delete\CommentThread
   */
  protected $thread;

  /**
   * CommentDelete constructor.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\Database\Connection $connection
   *   The database connection.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The current user.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   * @param \Drupal\comment_delete\CommentThread $comment_thread
   *   The comment thread ripper.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function __construct(RequestStack $request_stack, Connection $connection, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $current_user, MessengerInterface $messenger, CommentThread $comment_thread) {
    $this->requestStack = $request_stack;
    $this->connection = $connection;
    $this->config = $config_factory
      ->get('comment_delete.config');
    $this->commentStorage = $entity_type_manager
      ->getStorage('comment');
    $this->currentUser = $current_user;
    $this->messenger = $messenger;
    $this->thread = $comment_thread;
  }

  /**
   * Get the comment delete operations available to current user.
   *
   * @return array
   *   An array of available comment delete operations.
   */
  public function getOperations() {
    $options = [];
    if ($this->currentUser
      ->hasPermission('delete comment replies')) {
      $options[self::DELETE_REPLIES] = $this
        ->t('Delete the comment and its replies');
    }
    if (!$this->config
      ->get('soft') && $this->currentUser
      ->hasPermission('move comment replies')) {
      $options[self::MOVE_REPLIES] = $this
        ->t('Delete the comment and move its replies up');
    }
    $options[self::KEEP_REPLIES] = $this
      ->t('Delete the comment and keep its replies');
    return $options;
  }

  /**
   * Delete a comment and do something with its replies.
   *
   * @param \Drupal\comment\Entity\Comment $comment
   *   The comment entity.
   * @param int $op
   *   The operation type.
   */
  public function delete(Comment $comment, $op = self::KEEP_REPLIES) {
    switch ($op) {
      case self::DELETE_REPLIES:

        // Delete comment and replies. Default Drupal behavior.
        $this
          ->deleteReplies($comment);
        break;
      case self::MOVE_REPLIES:

        // Delete comment and move replies up one thread.
        $this
          ->moveReplies($comment);
        break;
      case self::KEEP_REPLIES:

        // Soft delete comment and keep replies at current thread.
        $this
          ->keepReplies($comment);
        break;
    }

    // Set translatable confirmation message.
    $this->messenger
      ->addMessage($this->config
      ->get('message'));
  }

  /**
   * Delete comment and replies.
   *
   * @param \Drupal\comment\Entity\Comment $comment
   *   The comment entity.
   */
  protected function deleteReplies(Comment $comment) {

    // Permanently delete comment and replies.
    if (!$this->config
      ->get('soft')) {
      try {
        $comment
          ->delete();
      } catch (EntityStorageException $e) {

        // Unable to delete.
        watchdog_exception('comment_delete', $e);
      }
      return;
    }

    // Soft delete comment and replies.
    $thread = $comment
      ->get('thread')
      ->getString();
    $query = $this->commentStorage
      ->getQuery()
      ->condition('entity_type', $comment
      ->get('entity_type')
      ->getString())
      ->condition('entity_id', $comment
      ->get('entity_id')
      ->getString());
    $db_or = $query
      ->orConditionGroup()
      ->condition('thread', $thread)
      ->condition('thread', $this->connection
      ->escapeLike(str_replace('/', '.', $thread)) . '%', 'LIKE');
    $query
      ->condition($db_or);
    $comment_ids = $query
      ->execute();
    if (!empty($comment_ids)) {
      $comment_thread = $this->commentStorage
        ->loadMultiple($comment_ids);
      foreach ($comment_thread as $_comment) {

        /** @var \Drupal\comment\Entity\Comment $_comment */
        $this
          ->softDelete($_comment);
      }
    }
  }

  /**
   * Delete comment and move replies up one level.
   *
   * @param \Drupal\comment\Entity\Comment $comment
   *   The comment entity.
   */
  protected function moveReplies(Comment $comment) {

    // Get all direct replies to the comment. Each will be given to the next
    // thread up if one is available or set as a top level thread.
    $query = $this->commentStorage
      ->getQuery()
      ->condition('pid', $comment
      ->id());
    $comment_ids = $query
      ->execute();
    if (!empty($comment_ids)) {
      $comments = $this->commentStorage
        ->loadMultiple($comment_ids);
      foreach ($comments as $_comment) {

        /** @var \Drupal\comment\Entity\Comment $_comment */
        try {
          $parent_comment = $comment
            ->getParentComment();
          if ($parent_comment && ($pid = $comment
            ->getParentComment()
            ->id())) {
            $_comment
              ->set('pid', $pid);
          }
          else {
            $_comment
              ->set('pid', NULL);
          }
          $_comment
            ->save();
        } catch (EntityStorageException $e) {

          // Unable to save.
          watchdog_exception('comment_delete', $e);
        }
      }
    }
    try {
      $comment
        ->delete();
      $this->thread
        ->thread($comment
        ->getCommentedEntity());
    } catch (EntityStorageException $e) {

      // Unable to delete.
      watchdog_exception('comment_delete', $e);
    }
  }

  /**
   * Soft delete comment and keep replies at current level.
   *
   * @param \Drupal\comment\Entity\Comment $comment
   *   The comment entity.
   */
  protected function keepReplies(Comment $comment) {

    // Get the total number of replies to the comment. If the comment has
    // no replies we can safely hard delete the comment.
    $replies_count = $this->commentStorage
      ->getQuery()
      ->condition('pid', $comment
      ->id())
      ->count()
      ->execute();
    if ($replies_count > 0) {

      // Replies exist so do the soft delete boogie.
      $this
        ->softDelete($comment);
    }
    else {
      if (!$this->config
        ->get('soft')) {
        try {
          $comment
            ->delete();
        } catch (EntityStorageException $e) {

          // Unable to delete.
          watchdog_exception('comment_delete', $e);
        }
      }
      else {
        $this
          ->softDelete($comment);
      }
    }
  }

  /**
   * Soft delete comment.
   *
   * @param \Drupal\comment\Entity\Comment $comment
   *   The comment entity.
   */
  protected function softDelete(Comment $comment) {
    if ($this->config
      ->get('soft_mode') === 'unpublished') {

      // Hiding a comment is the same as setting unpublished. This mode is not
      // recommended because it can cause thread indentation to go out of whack
      // (unpublished comments still count towards the thread). Mitigated by the
      // fact an unpublished comment can be published to restore indentation.
      try {
        $comment
          ->setUnpublished();
        $comment
          ->save();
      } catch (EntityStorageException $e) {

        // Unable to save.
        watchdog_exception('comment_delete', $e);
      }
    }
    else {
      $unset_author = $this->config
        ->get('soft_user');
      $fields = array_filter($comment
        ->getFields(), function ($field_machine_name) {
        return $field_machine_name !== 'field_name' && preg_match('/^field_(.*)/i', $field_machine_name);
      }, ARRAY_FILTER_USE_KEY);
      try {

        // Reset values on source and all translations.
        foreach ($comment
          ->getTranslationLanguages() as $translation) {
          $comment_translation = $comment
            ->getTranslation($translation
            ->getId());
          if ($unset_author) {
            $comment
              ->setOwnerId(0);
          }
          $comment_translation
            ->set('subject', NULL);
          $comment_translation
            ->set('comment_body', NULL);
          foreach ($fields as $field_machine_name => $field) {
            $comment_translation
              ->set($field_machine_name, NULL);
          }
          $comment_translation
            ->save();
        }
      } catch (EntityStorageException $e) {

        // Unable to save.
        watchdog_exception('comment_delete', $e);
      }
    }
  }

  /**
   * Check if current request is Ajax.
   *
   * @return bool
   *   A boolean indicating if current request is Ajax.
   */
  public function isAjaxRequest() {
    $request = $this->requestStack
      ->getCurrentRequest();
    $has_ajax_parameter = $request->request
      ->has(AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER);
    $has_ajax_format = $request->query
      ->get(MainContentViewSubscriber::WRAPPER_FORMAT) == 'drupal_ajax';
    return $has_ajax_parameter || $has_ajax_format;
  }

}

Classes

Namesort descending Description
CommentDeleteManager Service container for comment delete operations.