236 lines
9.3 KiB
PHP
236 lines
9.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Backend\Controller\FileStorage;
|
|
|
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use TYPO3\CMS\Backend\Attribute\AsController;
|
|
use TYPO3\CMS\Backend\Controller\Event\AfterFileStorageTreeItemsPreparedEvent;
|
|
use TYPO3\CMS\Backend\Dto\Tree\FileTreeItem;
|
|
use TYPO3\CMS\Backend\Dto\Tree\Label\Label;
|
|
use TYPO3\CMS\Backend\Dto\Tree\TreeItem;
|
|
use TYPO3\CMS\Backend\Tree\FileStorageTreeProvider;
|
|
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
|
use TYPO3\CMS\Core\Http\JsonResponse;
|
|
use TYPO3\CMS\Core\Imaging\IconFactory;
|
|
use TYPO3\CMS\Core\Imaging\IconSize;
|
|
use TYPO3\CMS\Core\Localization\LanguageService;
|
|
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
|
|
use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
|
|
use TYPO3\CMS\Core\Resource\Folder;
|
|
use TYPO3\CMS\Core\Resource\FolderInterface;
|
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
|
|
|
/**
|
|
* Controller providing data to the file storage tree.
|
|
*
|
|
* @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
|
|
*/
|
|
#[AsController]
|
|
readonly class TreeController
|
|
{
|
|
public function __construct(
|
|
protected IconFactory $iconFactory,
|
|
protected FileStorageTreeProvider $treeProvider,
|
|
protected ResourceFactory $resourceFactory,
|
|
protected EventDispatcherInterface $eventDispatcher,
|
|
) {}
|
|
|
|
/**
|
|
* Loads data for the first time, or when expanding a folder.
|
|
*/
|
|
public function fetchDataAction(ServerRequestInterface $request): ResponseInterface
|
|
{
|
|
$parentIdentifier = $request->getQueryParams()['parent'] ?? null;
|
|
if ($parentIdentifier) {
|
|
$currentDepth = (int)($request->getQueryParams()['depth'] ?? 1);
|
|
$parentIdentifier = rawurldecode($parentIdentifier);
|
|
$folder = $this->resourceFactory->getFolderObjectFromCombinedIdentifier($parentIdentifier);
|
|
$items = $this->treeProvider->getSubfolders($folder, $currentDepth + 1);
|
|
} else {
|
|
$items = $this->treeProvider->getRootNodes($this->getBackendUser());
|
|
}
|
|
return new JsonResponse($this->getPreparedItemsForOutput($request, $items));
|
|
}
|
|
|
|
/**
|
|
* Returns JSON representing page rootline
|
|
*/
|
|
public function fetchRootlineAction(ServerRequestInterface $request): ResponseInterface
|
|
{
|
|
$identifier = (string)($request->getQueryParams()['identifier'] ?? '');
|
|
if ($identifier === '') {
|
|
return new JsonResponse(null, 400);
|
|
}
|
|
|
|
try {
|
|
$folder = $this->resourceFactory->getFolderObjectFromCombinedIdentifier($identifier);
|
|
} catch (InsufficientFolderAccessPermissionsException) {
|
|
return new JsonResponse(null, 403);
|
|
} catch (FolderDoesNotExistException) {
|
|
return new JsonResponse(null, 404);
|
|
}
|
|
|
|
$rootline = [];
|
|
while (true) {
|
|
$identifier = $folder->getCombinedIdentifier();
|
|
$rootline[] = $identifier;
|
|
try {
|
|
$parent = $folder->getParentFolder();
|
|
} catch (InsufficientFolderAccessPermissionsException) {
|
|
break;
|
|
}
|
|
if ($parent->getCombinedIdentifier() === $identifier) {
|
|
// parent folder of root folder is the root folder => break
|
|
break;
|
|
}
|
|
$folder = $parent;
|
|
}
|
|
|
|
return new JsonResponse([
|
|
'rootline' => array_reverse($rootline),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Used when the search / filter is used.
|
|
*
|
|
* @throws \Exception
|
|
*/
|
|
public function filterDataAction(ServerRequestInterface $request): ResponseInterface
|
|
{
|
|
$search = $request->getQueryParams()['q'] ?? '';
|
|
$foundFolders = $this->treeProvider->getFilteredTree($this->getBackendUser(), $search);
|
|
|
|
$items = [];
|
|
foreach ($foundFolders as $folder) {
|
|
if (!$folder instanceof Folder) {
|
|
continue;
|
|
}
|
|
$storage = $folder->getStorage();
|
|
$itemsInRootLine = [];
|
|
|
|
// Go back the root folder structure until the root folder
|
|
$nextFolder = $folder;
|
|
$isParent = false;
|
|
do {
|
|
$itemsInRootLine[$nextFolder->getCombinedIdentifier()] = array_merge(
|
|
$this->treeProvider->prepareFolderInformation($nextFolder),
|
|
[
|
|
'expanded' => $isParent,
|
|
'loaded' => true,
|
|
]
|
|
);
|
|
$isParent = true;
|
|
try {
|
|
$nextFolder = $nextFolder->getParentFolder();
|
|
} catch (InsufficientFolderAccessPermissionsException) {
|
|
$nextFolder = null;
|
|
}
|
|
} while ($nextFolder instanceof FolderInterface && $nextFolder->getIdentifier() !== '/');
|
|
// Add the storage / sys_filemount itself
|
|
$storageData = $this->treeProvider->prepareFolderInformation(
|
|
$storage->getRootLevelFolder(true),
|
|
$storage->getName()
|
|
);
|
|
$storageData = array_merge($storageData, [
|
|
'depth' => 0,
|
|
'expanded' => true,
|
|
]);
|
|
$itemsInRootLine[$storage->getUid() . ':/'] = $storageData;
|
|
|
|
$itemsInRootLine = array_reverse($itemsInRootLine);
|
|
$depth = 0;
|
|
foreach ($itemsInRootLine as $k => $itm) {
|
|
$itm['depth'] = $depth++;
|
|
$items[$k] = $itm;
|
|
}
|
|
}
|
|
|
|
ksort($items);
|
|
$items = array_values($items);
|
|
return new JsonResponse($this->getPreparedItemsForOutput($request, $items));
|
|
}
|
|
|
|
/**
|
|
* Adds information for the JSON result to be rendered. Additionally, dispatches event for modification.
|
|
*/
|
|
protected function getPreparedItemsForOutput(ServerRequestInterface $request, array $items): array
|
|
{
|
|
foreach ($items as &$item) {
|
|
$folder = $item['resource'];
|
|
$isStorage = $item['recordType'] !== 'sys_file';
|
|
$item['resourceType'] = $isStorage ? 'storage' : 'folder';
|
|
if ($isStorage && !$folder->getStorage()->isOnline()) {
|
|
$item['name'] .= ' (' . $this->getLanguageService()->translate('is_offline', 'core.db.sys_file_storage') . ')';
|
|
}
|
|
$icon = $this->iconFactory->getIconForResource($folder, IconSize::SMALL, null, $isStorage ? ['mount-root' => true] : []);
|
|
$item['icon'] = $icon->getIdentifier();
|
|
$item['overlayIcon'] = $icon->getOverlayIcon() ? $icon->getOverlayIcon()->getIdentifier() : '';
|
|
|
|
$tsConfigLabels = $this->getBackendUser()->getTSConfig()['options.']['folderTree.']['label.'] ?? [];
|
|
if (trim($tsConfigLabels[$folder->getCombinedIdentifier() . '.']['label'] ?? '') !== '') {
|
|
$item['labels'][] = new Label(
|
|
label: $this->getLanguageService()->sL($tsConfigLabels[$folder->getCombinedIdentifier() . '.']['label']),
|
|
color: (string)($tsConfigLabels[$folder->getCombinedIdentifier() . '.']['color'] ?? '#ff8722'),
|
|
);
|
|
}
|
|
}
|
|
|
|
return array_map(
|
|
static function (array $item): FileTreeItem {
|
|
return new FileTreeItem(
|
|
item: new TreeItem(
|
|
identifier: $item['identifier'],
|
|
parentIdentifier: (string)($item['parentIdentifier'] ?? ''),
|
|
recordType: (string)($item['recordType'] ?? ''),
|
|
name: (string)($item['name'] ?? ''),
|
|
prefix: (string)($item['prefix'] ?? ''),
|
|
suffix: (string)($item['suffix'] ?? ''),
|
|
tooltip: (string)($item['tooltip'] ?? ''),
|
|
depth: (int)($item['depth'] ?? 0),
|
|
hasChildren: (bool)($item['hasChildren'] ?? false),
|
|
loaded: (bool)($item['loaded'] ?? false),
|
|
icon: $item['icon'],
|
|
overlayIcon: $item['overlayIcon'],
|
|
statusInformation: (array)($item['statusInformation'] ?? []),
|
|
labels: (array)($item['labels'] ?? []),
|
|
),
|
|
pathIdentifier: (string)($item['pathIdentifier'] ?? ''),
|
|
storage: (int)($item['storage'] ?? 0),
|
|
resourceType: $item['resourceType'],
|
|
);
|
|
},
|
|
$this->eventDispatcher->dispatch(
|
|
new AfterFileStorageTreeItemsPreparedEvent($request, $items)
|
|
)->getItems()
|
|
);
|
|
}
|
|
|
|
protected function getBackendUser(): BackendUserAuthentication
|
|
{
|
|
return $GLOBALS['BE_USER'];
|
|
}
|
|
|
|
protected function getLanguageService(): LanguageService
|
|
{
|
|
return $GLOBALS['LANG'];
|
|
}
|
|
}
|