TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
<?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'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user