foldershare-8.x-1.2/src/ManageUsageStatistics.php

src/ManageUsageStatistics.php
<?php

namespace Drupal\foldershare;

use Drupal\Core\Database\Database;

use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Entity\FolderShareScheduledTask;

/**
 * Manages usage statistics for FolderShare entities.
 *
 * This class provides static methods to manage collecting and retrieving
 * usage statistics about FolderShare entities. Supported operations include:
 * - Clearing usage statistics.
 * - Deleting usage statistics for specific users.
 * - Getting usage statistics.
 * - Updating usage statistics.
 *
 * This method also provides methods to query configuration settings for
 * usage statistics updates, and the total number of FolderShare entities
 * of various types.
 *
 * <B>Access control</B>
 * This class's methods do not do access control. The caller should check
 * access as needed by their situation.
 *
 * @ingroup foldershare
 *
 * @see \Drupal\foldershare\Entity\FolderShare
 */
final class ManageUsageStatistics {

  /*--------------------------------------------------------------------
   *
   * Constants.
   *
   *-------------------------------------------------------------------*/

  /**
   * The name of the per-user usage tracking database table.
   */
  const USAGE_TABLE = 'foldershare_usage';

  /*--------------------------------------------------------------------
   *
   * Table definition.
   *
   *-------------------------------------------------------------------*/

  /**
   * Returns the database schema that defines the user usage table.
   *
   * The table contains one record for each user. Record fields include:
   *
   * - The user ID.
   * - The number of subfolders they own.
   * - The number of files they own.
   * - The total storage used by the user.
   *
   * @return array
   *   Returns an array describing the user usage table.
   */
  public static function getSchema() {
    $schema[self::USAGE_TABLE] = [
      'description' => 'Stores file and folder usage information for users.',

      'fields' => [
        // The user ID for the user.
        'uid' => [
          'type'        => 'int',
          'unsigned'    => TRUE,
          'not null'    => TRUE,
          'default'     => 0,
          'description' => 'The user ID.',
        ],

        // The number of sub-folders.
        'nFolders' => [
          'type'        => 'int',
          'unsigned'    => TRUE,
          'not null'    => TRUE,
          'default'     => 0,
          'description' => 'The number of folders owned by the user.',
        ],

        // The number of files.
        'nFiles' => [
          'type'        => 'int',
          'unsigned'    => TRUE,
          'not null'    => TRUE,
          'default'     => 0,
          'description' => 'The number of files owned by the user.',
        ],

        // The total storage used by the user.
        'nBytes' => [
          'type'        => 'int',
          'unsigned'    => TRUE,
          'not null'    => TRUE,
          'default'     => 0,
          'description' => 'The amount of storage (in bytes) used by the user.',
        ],
      ],

      // Primary Key.
      'primary key' => [
        'uid',
      ],

      // No additional indexes are needed beyond the primary key.
    ];

    return $schema;
  }

  /*--------------------------------------------------------------------
   *
   * Usage operations.
   *
   *-------------------------------------------------------------------*/

  /**
   * Clears usage statistics for all users.
   *
   * @see ::deleteFromUsage()
   * @see ::getUsage()
   * @see ::getAllUsage()
   */
  public static function clearUsage() {
    $connection = Database::getConnection();
    $connection->delete(self::USAGE_TABLE)->execute();
  }

  /**
   * Deletes the entries for a list of users.
   *
   * @param int[]|int $uids
   *   (optional, default = -1) With a -1, usage statistics are deleted
   *   for all users by forwarding to clearUsage(). Otherwise each user ID
   *   given is deleted from the usage table.
   *
   * @see ::clearUsage()
   * @see ::getUsage()
   * @see ::getAllUsage()
   */
  public static function deleteFromUsage($uids = -1) {
    if ($uids === -1 || $uids === '-1' || $uids === '') {
      self::clearUsage();
      return;
    }

    // User IDs are technically strings, but are often really integers and
    // are sometimes cast as integers. Accept either.
    if (is_int($uids) === TRUE || is_string[$uids] === TRUE) {
      $uids = [$uids];
    }

    if (is_array($uids) === TRUE) {
      for ($i = 0; $i < count($uids); ++$i) {
        $uids[$i] = (string) $uids[$i];
      }

      $connection = Database::getConnection();
      $query = $connection->delete(self::USAGE_TABLE);
      $query->condition('uid', $uids, 'IN');
      $query->execute();
    }
  }

  /**
   * Returns usage statistics of all users.
   *
   * The returned array has one entry for each user in the
   * database. Array keys are user IDs, and array values are associative
   * arrays with keys for specific metrics and values for those
   * metrics. Supported array keys are:
   *
   * - nFolders: the number of folders.
   * - nFiles: the number of files.
   * - nBytes: the total storage of all files.
   *
   * All metrics are for the total number of items or bytes owned by
   * the user.
   *
   * The returned values for bytes used is the current storage space use
   * for each user. This value does not include any database storage space
   * required for file and folder metadata.
   *
   * The returned array only contains records for those users that
   * have current usage. Users who have no recorded metrics
   * will not be listed in the returned array.
   *
   * @return array
   *   An array with user ID array keys. Each array value is an
   *   associative array with keys for each of the above usage.
   *
   * @see ::clearUsage()
   * @see ::deleteFromUsage()
   * @see ::getUsage()
   * @see ::updateUsage()
   */
  public static function getAllUsage() {
    // Query the usage table for all entries.
    $connection = Database::getConnection();
    $select = $connection->select(self::USAGE_TABLE, 'u');
    $select->addField('u', 'uid', 'uid');
    $select->addField('u', 'nFolders', 'nFolders');
    $select->addField('u', 'nFiles', 'nFiles');
    $select->addField('u', 'nBytes', 'nBytes');
    $records = $select->execute()->fetchAll();

    // Build and return an array from the records.  Array keys
    // are user IDs, while values are usage info.
    $usage = [];
    foreach ($records as $record) {
      $usage[$record->uid] = [
        'nFolders' => $record->nFolders,
        'nFiles'   => $record->nFiles,
        'nBytes'   => $record->nBytes,
      ];
    }

    return $usage;
  }

  /**
   * Returns the time of the last usage statistics update.
   *
   * @return string
   *   The last update time.
   */
  public static function getLastUpdateTime() {
    return \Drupal::state()->get('foldershare.usage_last');
  }

  /**
   * Returns usage statistics for a user.
   *
   * The returned associative array has keys for specific metrics,
   * and values for those metrics. Supported array keys are:
   *
   * - nFolders: the number of folders.
   * - nFiles: the number of files.
   * - nBytes: the total storage of all files.
   *
   * All metrics are for the total number of items or bytes owned by
   * the user.
   *
   * The returned value for bytes used is the current storage space use
   * for the user. This value does not include any database storage space
   * required for file and folder metadata.
   *
   * If there is no recorded usage information for the user, an
   * array is returned with all metric values zero.
   *
   * @param int|string $uid
   *   The user ID of the user whose usage is to be returned.
   *
   * @return array
   *   An associative array is returned that includes keys for each
   *   of the above usage.
   *
   * @see ::clearUsage()
   * @see ::deleteFromUsage()
   * @see ::getAllUsage()
   * @see ::updateUsage()
   */
  public static function getUsage($uid) {
    // User IDs are technically strings, but are often really integers and
    // are sometimes cast as integers. Accept either.
    if (is_int($uid) === FALSE && is_string($uid) === FALSE) {
      return [
        'nFolders' => 0,
        'nFiles'   => 0,
        'nBytes'   => 0,
      ];
    }

    $uid = (string) $uid;

    // Query the usage table for an entry for this user.
    // There could be none, or one, but not multiple entries.
    $connection = Database::getConnection();
    $select = $connection->select(self::USAGE_TABLE, 'u');
    $select->addField('u', 'uid', 'uid');
    $select->addField('u', 'nFolders', 'nFolders');
    $select->addField('u', 'nFiles', 'nFiles');
    $select->addField('u', 'nBytes', 'nBytes');
    $select->condition('u.uid', $uid, '=');
    $select->range(0, 1);
    $records = $select->execute()->fetchAll();

    // If none, return an empty usage array.
    if (count($records) === 0) {
      return [
        'nFolders' => 0,
        'nFiles'   => 0,
        'nBytes'   => 0,
      ];
    }

    // Otherwise return the usage.
    $record = array_shift($records);
    return [
      'nFolders' => $record->nFolders,
      'nFiles'   => $record->nFiles,
      'nBytes'   => $record->nBytes,
    ];
  }

  /**
   * Updates the usage table.
   *
   * All current usage information is deleted and a new set assembled
   * and saved for all users at the site. Users that have no files or
   * folders are not included.
   *
   * <B>Process locks</B>
   * This method uses a process lock to insure that the usage table is
   * updated by only one process at a time. If the table is found to be
   * locked, this method returns immediately without updating the table.
   *
   * @return bool
   *   Returns FALSE if the update aborted because another update is already
   *   in progress. Otherwise returns TRUE.
   *
   * @see ::clearUsage()
   * @see ::deleteFromUsage()
   * @see ::getAllUsage()
   * @see ::getUsage()
   */
  public static function updateUsage() {
    // LOCK DURING REBUILD.
    if (\Drupal::lock()->acquire('foldershare_usage_lock', 30) === FALSE) {
      // Another update is in progress.
      return FALSE;
    }

    \Drupal::state()->set('foldershare.usage_last', 'pending');

    // Empty the table.
    $connection = Database::getConnection();
    $connection->delete(self::USAGE_TABLE)->execute();

    // For each user, count the number of files, folders, and bytes, then
    // update the usage table.
    $userIds = \Drupal::entityQuery('user')->execute();

    foreach ($userIds as $uid) {
      $nFolders = FolderShare::countNumberOfFolders($uid);
      $nFiles   = FolderShare::countNumberOfFiles($uid);
      $nBytes   = FolderShare::countNumberOfBytes($uid);

      // Add a new entry.
      $query = $connection->insert(self::USAGE_TABLE);
      $query->fields(
        [
          'uid'      => $uid,
          'nFolders' => $nFolders,
          'nFiles'   => $nFiles,
          'nBytes'   => $nBytes,
        ]);
      $query->execute();
    }

    // UNLOCK.
    \Drupal::lock()->release('foldershare_usage_lock');

    // Update the stored date.
    \Drupal::state()->set('foldershare.usage_last', '@' . (string) time());

    ManageLog::notice('Usage statistics updated');

    return TRUE;
  }

  /*--------------------------------------------------------------------
   *
   * Totals.
   *
   *-------------------------------------------------------------------*/

  /**
   * Returns the total number of bytes.
   *
   * The returned value only includes storage space used for files.
   * Any storage space required in the database for folder or file
   * metadata is not included.
   *
   * @return int
   *   The total number of bytes.
   *
   * @see FolderShare::countNumberOfBytes()
   */
  public static function getNumberOfBytes() {
    return FolderShare::countNumberOfBytes();
  }

  /**
   * Returns the total number of folders.
   *
   * @return int
   *   The total number of folders.
   *
   * @see FolderShare::countNumberOfFolders()
   */
  public static function getNumberOfFolders() {
    return FolderShare::countNumberOfFolders();
  }

  /**
   * Returns the total number of files.
   *
   * @return int
   *   The total number of files.
   *
   * @see FolderShare::countNumberOfFiles()
   */
  public static function getNumberOfFiles() {
    return FolderShare::countNumberOfFiles();
  }

  /*---------------------------------------------------------------------
   *
   * Update scheduling.
   *
   *---------------------------------------------------------------------*/

  /**
   * Returns the usage update task run interval.
   *
   * The run interval indicates the time between execution of the usage
   * updating scheduled task. Known values are:
   *
   * - 'manual' = the table is only updated manually.
   * - 'hourly' = the table is updated hourly.
   * - 'daily' = the table is updated daily.
   * - 'weekly' = the table is updated weekly.
   *
   * @return string
   *   Returns the run interval setting.
   *
   * @see \Drupal\foldershare\Settings::getUsageUpdateInterval()
   */
  public static function getIndexInterval() {
    return Settings::getUsageUpdateInterval();
  }

  /**
   * Returns the usage update task run interval in seconds.
   *
   * The current run interval is interpreted to compute and return the
   * interval time in seconds.
   *
   * @return int
   *   Returns the run interval in seconds. Zero is returned if the
   *   interval is 'manual'.
   *
   * @see ::getIndexInterval()
   * @see \Drupal\foldershare\Settings::getUsageUpdateInterval()
   */
  private static function getIndexIntervalSeconds() {
    switch (self::getIndexInterval()) {
      default:
      case 'manual':
        // No update task.
        return 0;

      case 'hourly':
        // 60 minutes/hour * 60 seconds/minute.
        return (60 * 60);

      case 'daily':
        // 24 hours/day * 60 minutes/hour * 60 seconds/minute.
        return (24 * 60 * 60);

      case 'weekly':
        // 7 days/week * 24 hours/day * 60 minutes/hour * 60 seconds/minute.
        return (7 * 24 * 60 * 60);
    }
  }

  /**
   * Schedules usage statistics updates.
   *
   * <B>This method is internal and strictly for use by the FolderShare
   * module itself.</B>
   *
   * The current usage updating task, if any, is deleted and a new task
   * scheduled, if the current interval is not 'manual'.
   *
   * @see ::getIndexInterval()
   * @see ::taskUpdateUsage()
   * @see \Drupal\foldershare\Settings::getUsageUpdateInterval()
   */
  public static function scheduleUsageUpdate() {
    // If a task is already scheduled, delete it first.
    FolderShareScheduledTask::deleteTasks(
      '\Drupal\foldershare\ManageUsageStatistics::taskUpdateUsage');

    $seconds = self::getIndexIntervalSeconds();
    if ($seconds === 0) {
      // There is no scheduled update task.
      return;
    }

    // Schedule a new task with the current interval.
    FolderShareScheduledTask::createTask(
      time() + $seconds,
      '\Drupal\foldershare\ManageUsageStatistics::taskUpdateUsage',
      (int) \Drupal::currentUser()->id(),
      NULL,
      time(),
      self::getIndexInterval() . ' usage table update',
      0);
  }

  /*---------------------------------------------------------------------
   *
   * Usage update task.
   *
   *---------------------------------------------------------------------*/

  /**
   * Updates the usage table as a scheduled task.
   *
   * <B>This method is internal and strictly for use by the FolderShare
   * module itself.</B>
   *
   * The task updates the usage table.
   *
   * @param int $requester
   *   The user ID of the user that requested the update. This is ignored.
   * @param array $parameters
   *   The queued task's parameters. This is ignored.
   * @param int $started
   *   The timestamp of the start date & time for an operation that causes
   *   a chain of tasks.
   * @param string $comments
   *   A comment on the current task.
   * @param int $executionTime
   *   The accumulated total execution time of the task chain, in seconds.
   */
  public static function taskUpdateUsage(
    int $requester,
    array $parameters,
    int $started,
    string $comments,
    int $executionTime) {

    $seconds = self::getIndexIntervalSeconds();
    if ($seconds === 0) {
      // There is no scheduled update task.
      return;
    }

    // Schedule the next task with the current interval.
    FolderShareScheduledTask::createTask(
      time() + $seconds,
      '\Drupal\foldershare\ManageUsageStatistics::taskUpdateUsage',
      $requester,
      NULL,
      time(),
      self::getIndexInterval() . ' usage table update',
      0);

    // Update the usage table.
    self::updateUsage();
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc