foldershare-8.x-1.2/src/Form/AdminUsageReport.php
src/Form/AdminUsageReport.php
<?php
namespace Drupal\foldershare\Form;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\foldershare\Constants;
use Drupal\foldershare\Utilities\FormatUtilities;
use Drupal\foldershare\Utilities\UserUtilities;
use Drupal\foldershare\ManageUsageStatistics;
/**
* Creates a usage report showing per-user usage of files, folders, & storage.
*
* The usage report is intended for site administrators to help them find and
* track heavy users. The report shows a table with one row per user and the
* number of folders, files, and bytes used by that user.
*
* <B>Internal class</B>
* This class is internal to the FolderShare module. The class's existance,
* name, and content may change from release to release without any promise
* of backwards compatability.
*
* <B>Access control</B>
* The route to this form should restrict access to those with administration
* permission.
*
* @ingroup foldershare
*
* @see \Drupal\foldershare\ManageUsageStatistics
*/
class AdminUsageReport extends FormBase {
/*--------------------------------------------------------------------
*
* Fields - dependency injection.
*
*--------------------------------------------------------------------*/
/**
* The entity type manager, set at construction time.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/*--------------------------------------------------------------------
*
* Construction.
*
*--------------------------------------------------------------------*/
/**
* Constructs a new page.
*
* @param \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
* The entity type manager.
*/
public function __construct(EntityTypeManager $entityTypeManager) {
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('entity_type.manager'));
}
/*--------------------------------------------------------------------
*
* Form setup.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function getFormId() {
return str_replace('\\', '_', get_class($this));
}
/*--------------------------------------------------------------------
*
* Form build.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function buildForm(
array $form,
FormStateInterface $formState = NULL) {
//
// Create table column titles.
// ---------------------------
// These titles are used in the table description, but they also show
// up in URLs as the value of the 'order' argument to select the column
// to sort on.
$userTitle = $this->t('User');
$foldersTitle = $this->t('Folders');
$filesTitle = $this->t('Files');
$bytesTitle = $this->t('Bytes');
//
// Get request arguments.
// ----------------------
// "sort" selects the sort direction, and "order" selects the column.
$request = \Drupal::request();
$sortDirection = mb_strtoupper($request->get('sort', 'asc'));
$sortOrder = $request->get('order', (string) $userTitle);
//
// Assemble and sort usage information.
// ------------------------------------
// Get totals and per-user information (array keys are UIDs).
$nFolders = ManageUsageStatistics::getNumberOfFolders();
$nFiles = ManageUsageStatistics::getNumberOfFiles();
$nBytes = ManageUsageStatistics::getNumberOfBytes();
$userUsage = ManageUsageStatistics::getAllUsage();
// The above usage array contains entries ONLY for users that
// are using the module. Any user that has never created a file or
// folder will have no usage entry.
//
// For the table on this page, we'd like to include ALL users. So,
// for any user not in the above usage array, add an empty entry.
$displayNames = UserUtilities::getAllDisplayNames();
$data = [];
foreach ($displayNames as $uid => $displayName) {
if (isset($userUsage[$uid]) === TRUE) {
$data[$uid] = [
'displayName' => $displayName,
'sortDisplayName' => mb_strtolower($displayName),
'nFolders' => $userUsage[$uid]['nFolders'],
'nFiles' => $userUsage[$uid]['nFiles'],
'nBytes' => $userUsage[$uid]['nBytes'],
];
}
else {
$data[$uid] = [
'displayName' => $displayName,
'sortDisplayName' => mb_strtolower($displayName),
'nFolders' => 0,
'nFiles' => 0,
'nBytes' => 0,
];
}
}
unset($userUsage);
// Sort.
switch ($sortOrder) {
default:
case (string) $userTitle:
if ($sortDirection === 'ASC') {
usort(
$data,
function ($a, $b) {
return ($a['sortDisplayName'] <=> $b['sortDisplayName']);
});
}
else {
usort(
$data,
function ($a, $b) {
return ($b['sortDisplayName'] <=> $a['sortDisplayName']);
});
}
break;
case (string) $foldersTitle:
if ($sortDirection === 'ASC') {
usort(
$data,
function ($a, $b) {
return ($a['nFolders'] <=> $b['nFolders']);
});
}
else {
usort(
$data,
function ($a, $b) {
return ($b['nFolders'] <=> $a['nFolders']);
});
}
break;
case (string) $filesTitle:
if ($sortDirection === 'ASC') {
usort(
$data,
function ($a, $b) {
return ($a['nFiles'] <=> $b['nFiles']);
});
}
else {
usort(
$data,
function ($a, $b) {
return ($b['nFiles'] <=> $a['nFiles']);
});
}
break;
case (string) $bytesTitle:
if ($sortDirection === 'ASC') {
usort(
$data,
function ($a, $b) {
return ($a['nBytes'] <=> $b['nBytes']);
});
}
else {
usort(
$data,
function ($a, $b) {
return ($b['nBytes'] <=> $a['nBytes']);
});
}
break;
}
//
// Setup form.
// -----------
// Create class names, attach libraries, and create the container body
// that will hold the table.
$baseClass = Constants::MODULE . '-admin-usage-table';
$tableClass = $baseClass;
$userColumnClass = $baseClass . '-user';
$foldersColumnClass = $baseClass . '-folders';
$filesColumnClass = $baseClass . '-files';
$bytesColumnClass = $baseClass . '-bytes';
$totalsClass = $baseClass . '-total';
$updateBarClass = $baseClass . '-update-bar';
$updateTimeClass = $baseClass . '-update-time';
$updateButtonClass = $baseClass . '-update-button';
$tableName = Constants::MODULE . '-admin-usage-table';
$form['#attached']['library'][] = Constants::LIBRARY_MODULE;
$form['#attached']['library'][] = Constants::LIBRARY_ADMIN;
$form['#tree'] = TRUE;
$updateTime = ManageUsageStatistics::getLastUpdateTime();
switch ($updateTime) {
case '':
case 'never':
$updateTime = $this->t('Never updated');
break;
case 'pending':
$updateTime = $this->t('Update in progress');
break;
default:
try {
// The stored update time is a timestamp string.
// Compute how long ago the time was.
$now = new \DateTime();
$diff = (array) $now->diff(new \DateTime($updateTime));
// Introduce a weeks count.
$diff['w'] = (int) floor(($diff['d'] / 7));
$diff['d'] -= ($diff['w'] * 7);
// Get rid of the microsecond value, increasing seconds.
if ((float) $diff['f'] !== 0) {
$diff['s']++;
unset($diff['f']);
}
// Get rid of the seconds value, increasing minutes.
if ((int) $diff['s'] > 0) {
$diff['i']++;
unset($diff['s']);
}
// Watch for negatives. While it should not be possible for the
// difference between NOW and a PAST event to ever be negative,
// in practice it can happen. This may be differences in the
// resolution returned by \DateTime() for NOW and \DateTime($time)
// for PAST where $time was earlier set to time().
if ($diff['y'] < 0 ||
$diff['m'] < 0 ||
$diff['w'] < 0 ||
$diff['d'] < 0 ||
$diff['h'] < 0 ||
$diff['i'] < 0) {
// Force everything to zero, except minutes.
$diff['y'] = 0;
$diff['m'] = 0;
$diff['w'] = 0;
$diff['d'] = 0;
$diff['h'] = 0;
$diff['i'] = 1;
}
// Use the rest of the time denominations and build up a string.
$string = [
'y' => [
'@count year',
'@count years',
],
'm' => [
'@count month',
'@count months',
],
'w' => [
'@count week',
'@count weeks',
],
'd' => [
'@count day',
'@count days',
],
'h' => [
'@count hour',
'@count hours',
],
'i' => [
'@count minute',
'@count minutes',
],
];
foreach ($string as $k => &$v) {
if ($diff[$k] === 0) {
unset($string[$k]);
continue;
}
$v = (string) $this->formatPlural(
$diff[$k],
$v[0],
$v[1]);
}
$updateTime = $this->t(
'Updated @time ago',
[
'@time' => ((count($string) === 1) ?
reset($string) : implode(', ', $string)),
]);
}
catch (\Exception $e) {
// The stored time is invalid. Revert to 'never'.
$updateTime = $this->t('Never updated');
}
break;
}
$form['update'] = [
'#type' => 'container',
'#weight' => 10,
'#attributes' => [
'class' => [$updateBarClass],
],
'time' => [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $updateTime,
'#weight' => 0,
'#attributes' => [
'class' => [$updateTimeClass],
],
],
'actions' => [
'#type' => 'actions',
'#weight' => 1,
'submit' => [
'#type' => 'submit',
'#button_type' => 'not-primary',
'#value' => '',
'#attributes' => [
'title' => $this->t('Update the usage table'),
'class' => [$updateButtonClass],
],
],
],
];
//
// Create table and headers.
// -------------------------
// The table's headers are:
// - User name.
// - Number of folders.
// - Number of files.
// - Number of bytes.
$form['usage_table'] = [
'#type' => 'table',
'#name' => $tableName,
'#responsive' => FALSE,
'#sticky' => TRUE,
'#weight' => 10,
'#attributes' => [
'class' => [$tableClass],
],
'#header' => [
'user' => [
'data' => $userTitle,
'class' => [$userColumnClass],
'field' => 'user',
],
'nfolders' => [
'data' => $foldersTitle,
'class' => [$foldersColumnClass],
'field' => 'nfolders',
],
'nfiles' => [
'data' => $filesTitle,
'class' => [$filesColumnClass],
'field' => 'nfiles',
],
'nbytes' => [
'data' => $bytesTitle,
'class' => [$bytesColumnClass],
'field' => 'nbytes',
],
],
];
// Note which column we sorted on.
switch ($sortOrder) {
default:
case (string) $userTitle:
$form['usage_table']['#header']['user']['sort'] = $sortDirection;
break;
case (string) $foldersTitle:
$form['usage_table']['#header']['nfolders']['sort'] = $sortDirection;
break;
case (string) $filesTitle:
$form['usage_table']['#header']['nfiles']['sort'] = $sortDirection;
break;
case (string) $bytesTitle:
$form['usage_table']['#header']['nbytes']['sort'] = $sortDirection;
break;
}
//
// Create table rows.
// ------------------
// One row for each user, followed by a totals row.
$rows = [];
foreach ($data as $d) {
// The user column has the user's display name and link to the user.
$userColumn = [
'data' => [
'#type' => 'item',
'#markup' => $d['displayName'],
],
'class' => [$userColumnClass],
];
// The folders column has the number of folders used by the user.
$foldersColumn = [
'data' => [
'#type' => 'item',
'#markup' => $d['nFolders'],
],
'class' => [$foldersColumnClass],
];
// The files column has the number of files used by the user.
$filesColumn = [
'data' => [
'#type' => 'item',
'#markup' => $d['nFiles'],
],
'class' => [$filesColumnClass],
];
// The bytes column has the number of bytes used by the user.
$bytesColumn = [
'data' => [
'#type' => 'item',
'#markup' => FormatUtilities::formatBytes($d['nBytes']),
],
'class' => [$bytesColumnClass],
];
$rows[] = [
'data' => [
'user' => $userColumn,
'nfolders' => $foldersColumn,
'nfiles' => $filesColumn,
'nbytes' => $bytesColumn,
],
];
}
// Add row for totals.
//
// The user column says "total" for the totals row.
$userColumn = [
'data' => [
'#type' => 'item',
'#markup' => $this->t('Total'),
],
'class' => [$userColumnClass],
];
// The folders column has the total number of folders.
$foldersColumn = [
'data' => [
'#type' => 'item',
'#markup' => $nFolders,
],
'class' => [$foldersColumnClass],
];
// The files column has the total number of files.
$filesColumn = [
'data' => [
'#type' => 'item',
'#markup' => $nFiles,
],
'class' => [$filesColumnClass],
];
// The bytes column has the total number of bytes.
$bytesColumn = [
'data' => [
'#type' => 'item',
'#markup' => FormatUtilities::formatBytes($nBytes),
],
'class' => [$bytesColumnClass],
];
$rows[] = [
'data' => [
'user' => $userColumn,
'nfolders' => $foldersColumn,
'nfiles' => $filesColumn,
'nbytes' => $bytesColumn,
],
'class' => [$totalsClass],
];
// Add the rows to the table.
$form['usage_table']['#rows'] = $rows;
return $form;
}
/*--------------------------------------------------------------------
*
* Form validate.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $formState) {
// Nothing to do.
}
/*--------------------------------------------------------------------
*
* Form submit.
*
*--------------------------------------------------------------------*/
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $formState) {
ManageUsageStatistics::updateUsage();
}
}
