diff --git a/appinfo/routes.php b/appinfo/routes.php index b923ba06..72f630b7 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -23,5 +23,15 @@ 'url' => '/mime/img/{iconname}.svg', 'verb' => 'GET', ], + [ + 'name' => 'ajax#getStorageStats', + 'url' => '/ajax/getstoragestats', + 'verb' => 'GET', + ], + [ + 'name' => 'Api#getStorageStats', + 'url' => '/api/v1/stats', + 'verb' => 'GET' + ], ] ]; diff --git a/lib/Controller/AjaxController.php b/lib/Controller/AjaxController.php new file mode 100644 index 00000000..b00c3730 --- /dev/null +++ b/lib/Controller/AjaxController.php @@ -0,0 +1,59 @@ + + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\NMCTheme\Controller; + +use OCA\NMCTheme\Service\NMCFilesService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\Files\NotFoundException; +use OCP\IRequest; + +class AjaxController extends Controller { + + /** @var NMCFilesService */ + private $filesService; + + public function __construct(string $appName, IRequest $request, NMCFilesService $filesService) { + parent::__construct($appName, $request); + $this->filesService = $filesService; + } + + /** + * @NoAdminRequired + */ + public function getStorageStats(string $dir = '/'): JSONResponse { + $storageInfo = $this->filesService->buildFileStorageStatistics($dir ?: '/'); + $l = \OC::$server->getL10N('files'); + + $maxUploadFileSize = \OCP\Util::maxUploadFilesize($dir, $storageInfo['free']); + $maxHumanFileSize = \OCP\Util::humanFileSize($maxUploadFileSize); + $maxHumanFileSize = $l->t('Upload (max. %s)', [$maxHumanFileSize]); + + $storageInfo['uploadMaxFilesize'] = $maxUploadFileSize; + $storageInfo['maxHumanFilesize'] = $maxHumanFileSize; + $storageInfo['usedSpacePercent'] = $storageInfo['relative']; + + try { + return new JSONResponse([ + 'status' => 'success', + 'data' => $storageInfo, + ]); + } catch (NotFoundException $e) { + return new JSONResponse([ + 'status' => 'error', + 'data' => [ + 'message' => 'Folder not found' + ], + ]); + } + } +} diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php new file mode 100644 index 00000000..f74fa688 --- /dev/null +++ b/lib/Controller/ApiController.php @@ -0,0 +1,36 @@ + + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\NMCTheme\Controller; + +use OCA\NMCTheme\Service\NMCFilesService; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; + +class ApiController extends Controller { + + /** @var NMCFilesService */ + private $filesService; + + public function __construct(string $appName, IRequest $request, NMCFilesService $filesService) { + parent::__construct($appName, $request); + $this->filesService = $filesService; + } + + /** + * @NoAdminRequired + */ + public function getStorageStats(string $dir = '/'): JSONResponse { + $storageInfo = $this->filesService->buildFileStorageStatistics($dir ?: '/'); + return new JSONResponse(['message' => 'ok', 'data' => $storageInfo]); + } +} diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php index 4b07687e..3a9f0009 100644 --- a/lib/Listener/BeforeTemplateRenderedListener.php +++ b/lib/Listener/BeforeTemplateRenderedListener.php @@ -12,8 +12,10 @@ namespace OCA\NMCTheme\Listener; use OC\Security\CSP\ContentSecurityPolicyNonceManager; +use OCA\NMCTheme\Service\NMCFilesService; use OCA\Theming\Util; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\IL10N; @@ -25,6 +27,8 @@ class BeforeTemplateRenderedListener implements IEventListener { private ContentSecurityPolicyNonceManager $nonceManager; private Util $themingUtil; private INavigationManager $navManager; + private IInitialState $initialState; + private NMCFilesService $filesService; private IL10N $l; public function __construct( @@ -32,11 +36,15 @@ public function __construct( ContentSecurityPolicyNonceManager $nonceManager, Util $themingUtil, INavigationManager $navManager, + IInitialState $initialState, + NMCFilesService $filesService, IL10N $l) { $this->urlGenerator = $urlGenerator; $this->nonceManager = $nonceManager; $this->themingUtil = $themingUtil; $this->navManager = $navManager; + $this->initialState = $initialState; + $this->filesService = $filesService; $this->l = $l; } @@ -88,7 +96,9 @@ public function handle(Event $event): void { [ 'nonce' => $this->nonceManager->getNonce(), 'src' => $mimetypelist . '?nmcv=' . $this->themingUtil->getCacheBuster() ], ''); // the empty text is needed to generate HTML5 valid tags - + + // provide corrected storageStats for v27 + $this->initialState->provideInitialState('storageStats', $this->filesService->buildFileStorageStatistics()); // you can add additional styles, links and scripts before rendering // keep src for future use: \OCP\Util::addScript("nmctheme", "../dist/l10nappender"); diff --git a/lib/Service/NMCFilesService.php b/lib/Service/NMCFilesService.php index a9f7bda9..1bd2270d 100644 --- a/lib/Service/NMCFilesService.php +++ b/lib/Service/NMCFilesService.php @@ -9,6 +9,7 @@ namespace OCA\NMCTheme\Service; +use OC\Files\View; use OCP\IL10N; class NMCFilesService { @@ -68,4 +69,46 @@ public function addFilesAppNavigationEntries() { ]; }); } + + public static function buildFileStorageStatistics(string $dir = '/') { + // information about storage capacities + $storageInfo = \OC_Helper::getStorageInfo($dir); + $trashbin = self::getTrashbinSize(\OC_User::getUser()); + $free = $storageInfo['free'] - $trashbin; + $used = $storageInfo['used'] + $trashbin; + $relative = self::getUsedRelative($storageInfo['quota'], $storageInfo['total'], $used); + + return [ + 'freeSpace' => $free, + 'quota' => $storageInfo['quota'], + 'total' => $storageInfo['total'], + 'used' => $used, + 'relative' => $relative, + 'trashbin' => $trashbin, + 'owner' => $storageInfo['owner'], + 'ownerDisplayName' => $storageInfo['ownerDisplayName'], + 'mountType' => $storageInfo['mountType'], + 'mountPoint' => $storageInfo['mountPoint'], + ]; + } + + private static function getTrashbinSize($user) { + $view = new View('/' . $user); + $fileInfo = $view->getFileInfo('/files_trashbin'); + return isset($fileInfo['size']) ? $fileInfo['size'] : 0; + } + + private static function getUsedRelative($quota, $total, $used) { + if ($total > 0) { + if ($quota > 0 && $total > $quota) { + $total = $quota; + } + // prevent division by zero or error codes (negative values) + $relative = round(($used / $total) * 10000) / 100; + } else { + $relative = 0; + } + + return $relative; + } } diff --git a/src/components/StorageQuota.vue b/src/components/StorageQuota.vue index e3e34a3f..ee254a7b 100644 --- a/src/components/StorageQuota.vue +++ b/src/components/StorageQuota.vue @@ -89,8 +89,8 @@ export default { try { const response = await axios.get( IS_LEGACY_VERSION - ? generateUrl('/apps/files/ajax/getstoragestats') - : generateUrl('/apps/files/api/v1/stats'), + ? generateUrl('/apps/nmctheme/ajax/getstoragestats') + : generateUrl('/apps/nmctheme/api/v1/stats'), ) if (!response?.data?.data) { throw new Error('Invalid storage stats') diff --git a/src/components/filesSettings.utils.ts b/src/components/filesSettings.utils.ts index de318002..3f0f34c9 100644 --- a/src/components/filesSettings.utils.ts +++ b/src/components/filesSettings.utils.ts @@ -16,11 +16,16 @@ const CONFIG_SETTINGS_MAPPING = { } export const loadStats = async () => { - let browserState = loadState('files', 'storageStats', null) + let browserState = loadState('nmctheme', 'storageStats', null) // v25 if (!browserState) { try { - const response = await axios.get(generateUrl('/apps/files/ajax/getstoragestats')) + let statsUrl = generateUrl('/apps/nmctheme/api/v1/stats') + // v25 + if (IS_LEGACY_VERSION) { + statsUrl = generateUrl('/apps/nmctheme/ajax/getstoragestats') + } + const response = await axios.get(statsUrl) if (!response?.data?.data) { throw new Error('Invalid storage stats') }