Skip to content
Snippets Groups Projects
PrivateMessageFloodService.php 4.65 KiB
Newer Older
<?php

namespace Drupal\private_message_flood\Service;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Config\ConfigFactoryInterface;

/**
 * Provides services for the Private Message Flood Protection module.
 */
class PrivateMessageFloodService implements PrivateMessageFloodServiceInterface {

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

  /**
   * The configuration factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $time;

  /**
   * Constructs a PrivateMessageFloodService object.
   *
   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
   *   The current user.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The configuration factory.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time service.
   */
  public function __construct(
    AccountProxyInterface $currentUser,
    ConfigFactoryInterface $configFactory,
    TimeInterface $time
  ) {
    $this->currentUser = $currentUser;
    $this->configFactory = $configFactory;
    $this->time = $time;
  }

  /**
   * {@inheritdoc}
   */
  public function checkUserFlood() {
    $flood_info = $this->getFloodProtectionInfo();

    // Check if we even need to check anything.
    if ($limit = $flood_info['limit']) {
      // Get the amount of time in which they are limited to make said number of
      // posts.
      $duration = new \DateInterval($flood_info['duration']);
      // Get the current time.
      $now = new \DateTime();
      $now->setTimestamp($this->time->getRequestTime());
      // Get the UNIX timestamp for time that is $duration ago from now. For
      // example, if $duration is one year, then $since will be the exact date
      // and time one year ago from now.
      $since = $now->sub($duration)->format('U');
      // Query the number of posts that the user has made since the given
      // timestamp.
      $post_count = db_select('private_messages', 'pm')
        ->fields('pm', ['id'])
        ->condition('owner', $this->currentUser->id())
        ->condition('created', $since, '>=')
        ->countQuery()
        ->execute()
        ->fetchField();

      // Return TRUE if they've posted their limit already, FALSE if they have
      // not.
      return $post_count >= $limit;
    }

    // If there is no limit for the user, then they have no flood protection
    // applied to them, and therefore are unable to flood.
    return FALSE;
  }

  /**
   * Gets flood protection info for the current user, based on their roles.
   *
   * Each role in the system can be assigned flood protection settings. Roles
   * are also assigned a priority. This function fetches the flood protection
   * data for the highest prioirty role that the current user has.
   *
   * @return array
   *   An array of flood protection data for the current user based on their
   *   roles.
   */
  private function getFloodProtectionInfo() {
    $user_roles = $this->currentUser->getRoles();
    $role = array_intersect_key($this->getRoleInfo(), array_flip($user_roles));
    $role_id = key($role);

    $config = $this->configFactory->get('private_message_flood.role.' . $role_id);
    if ($config) {
      return $config->get();
    }

    return [
      'limit' => 0,
    ];
  }

  /**
   * Fetches an array of roles, ordered by priority.
   *
   * Each role in the system may have flood protection settings that determine
   * how many posts a user can make in a given duration. Roles have priority,
   * and the highest priority role is the one that a user is checked against
   * when determining whether or not they have crossed the threshold for their
   * flood settings.
   *
   * @return array
   *   An array of roles that have flood protection assigned to them, keyed by
   *   the role ID, with the value being the weight. Roles in the array are
   *   ordered highest to lowest priority.
   */
  private function getRoleInfo() {
    $roles = array_map(['\Drupal\Component\Utility\Html', 'escape'], user_role_names(TRUE));
    $items = [];
    foreach (array_keys($roles) as $role_id) {
      $role_info = $this->configFactory->get('private_message_flood.role.' . $role_id)->get();
      $items[$role_id] = $role_info['weight'];
    }
    uasort($items, [$this, 'sortByWeight']);

    return $items;
  }

  /**
   * Sorting callback to sort arrays by their 'weight' attribute.
   */
  private function sortByWeight($a, $b) {
    if ($a === $b) {
      return 0;
    }

    return $a > $b ? 1 : -1;
  }

}