-
Notifications
You must be signed in to change notification settings - Fork 3
/
QuotaUtility.php
224 lines (201 loc) · 7.91 KB
/
QuotaUtility.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
<?php
declare(strict_types=1);
namespace Mehrwert\FalQuota\Utility;
/*
* 2019 - EXT:fal_quota
*
* This file is subject to the terms and conditions defined in
* file 'LICENSE.md', which is part of this source code package.
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\ResourceStorage;
use TYPO3\CMS\Core\Resource\StorageRepository;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class QuotaUtility provides utilities to get storage details, quota settings and issue warning mails
*/
final class QuotaUtility
{
private ConnectionPool $connectionPool;
public function __construct(ConnectionPool $connectionPool)
{
$this->connectionPool = $connectionPool;
}
/**
* Aggregate details for a given storage and return as array
*
* @param int $storageId
* @return array
*/
public function getStorageDetails(int $storageId): array
{
$storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($storageId);
if ($storage !== null) {
$isOverQuota = false;
$isOverThreshold = false;
$currentThreshold = 0;
if ((int)$storage->getStorageRecord()['soft_quota'] > 0) {
$currentThreshold = (int)$storage->getStorageRecord()['current_usage'] / (int)$storage->getStorageRecord()['soft_quota'] * 100;
if ((int)$storage->getStorageRecord()['current_usage'] > (int)$storage->getStorageRecord()['soft_quota']) {
$isOverQuota = true;
}
if ($currentThreshold >= (int)$storage->getStorageRecord()['quota_warning_threshold']) {
$isOverThreshold = true;
}
}
return [
'uid' => $storage->getUid(),
'name' => $storage->getName(),
'driver' => $storage->getDriverType(),
'over_quota' => $isOverQuota,
'over_threshold' => $isOverThreshold,
'current_usage' => self::numberFormat((int)$storage->getStorageRecord()['current_usage'], 'MB'),
'current_usage_raw' => (int)$storage->getStorageRecord()['current_usage'],
'soft_quota' => self::numberFormat((int)$storage->getStorageRecord()['soft_quota'], 'MB'),
'soft_quota_raw' => (int)$storage->getStorageRecord()['soft_quota'],
'hard_limit' => self::numberFormat((int)$storage->getStorageRecord()['hard_limit'], 'MB'),
'hard_limit_raw' => (int)$storage->getStorageRecord()['hard_limit'],
'quota_warning_threshold' => number_format((int)$storage->getStorageRecord()['quota_warning_threshold'], 2, ',', '.'),
'current_threshold' => number_format($currentThreshold, 2, ',', '.'),
'quota_warning_recipients' => $storage->getStorageRecord()['quota_warning_recipients'],
];
}
return [];
}
/**
* Get the total disk space used in a storage by SUM()ing upd all file sizes in this storage
*
* @param int $storageId
* @return int
*/
public function getTotalDiskSpaceUsedInStorage(int $storageId): int
{
$storage = $this->getStorage($storageId);
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_file');
$queryBuilder
->addSelectLiteral(
$queryBuilder->expr()->sum('size', 'current_usage')
)
->from('sys_file')
->where(
$queryBuilder->expr()->eq('storage', $storage->getUid())
);
$result = $queryBuilder->execute()->fetchAll();
return (int)$result[0]['current_usage'];
}
/**
* Calculate and update the current usage for the storage
*
* @param int $storageId
* @return int Current usage in MB
*/
public function updateStorageUsage(int $storageId): int
{
$currentUsage = $this->getTotalDiskSpaceUsedInStorage($storageId);
$connection = $this->connectionPool->getConnectionForTable('sys_file_storage');
$connection->update(
'sys_file_storage',
[ 'current_usage' => $currentUsage ],
[ 'uid' => $storageId ]
);
return $currentUsage;
}
/**
* Return the size of a FAL folder by recursively aggregating the files. To speed up, the process can by
* stopped if the total size is exceeding a given limit.
*
* @param Folder $folder
* @param int $breakAt
* @return int
*/
public function getFolderSize(Folder $folder, int $breakAt = 0): int
{
$folderSize = 0;
foreach ($folder->getFiles(0, 0, 1, true) as $file) {
$folderSize += (int)$file->getSize();
unset($file);
if ($breakAt > 0 && $folderSize > $breakAt) {
unset($files);
return $folderSize;
}
}
return $folderSize;
}
/**
* Returns the available size in bytes on the given storage
*
* @param int $storageId
* @return int Returns -1 if no value could be determined of method not available
*/
public function getAvailableSpaceOnStorageOnDevice(int $storageId): int
{
$availableSize = -1;
$storage = GeneralUtility::makeInstance(StorageRepository::class)->findByUid($storageId);
// Check local storage only
if ($storage !== null && function_exists('disk_free_space') && $storage->getDriverType() === 'Local') {
$storageConfiguration = $storage->getConfiguration();
if ($storageConfiguration['pathType'] === 'absolute') {
$absoluteStoragePath = $storageConfiguration['basePath'];
} else {
$absoluteStoragePath = Environment::getPublicPath() . '/' . $storageConfiguration['basePath'];
}
if (is_dir($absoluteStoragePath)) {
$availableSize = disk_free_space($absoluteStoragePath);
}
}
return (int)$availableSize;
}
/**
* Return a formatted number and append the given unit. Uses fallback to number_format() if the PHP extension
* »intl« is not available. Uses TYPO3 [SYS][systemLocale] if set, falls back to locale_get_default() if empty.
*
* @param int $number
* @param string $unit
* @param bool $addUnit
* @return string
*/
public static function numberFormat(int $number, string $unit = '', bool $addUnit = true): string
{
switch ($unit) {
case 'kB':
$number /= 1024;
break;
case 'MB':
$number = (int)($number / 1024 ** 2);
break;
case 'GB':
$number = (int)($number / 1024 ** 3);
break;
case 'TB':
$number = (int)($number / 1024 ** 4);
break;
default:
}
if (extension_loaded('intl') && class_exists('NumberFormatter')) {
$locale = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'];
if ($locale === '') {
$locale = locale_get_default();
}
$fmt = new \NumberFormatter($locale, \NumberFormatter::DECIMAL);
$formattedNumber = $fmt->format($number);
if (intl_is_failure($fmt->getErrorCode())) {
$formattedNumber = number_format($number, 0, '', '.');
}
} else {
$formattedNumber = number_format($number, 0, '', '.');
}
return $formattedNumber . ($addUnit ? ' ' . $unit : '');
}
/**
* Returns a storage object for the given storage id
*
* @param int $storageId
* @return ResourceStorage
*/
private function getStorage(int $storageId): ResourceStorage
{
return GeneralUtility::makeInstance(StorageRepository::class)->findByUid($storageId);
}
}