‪TYPO3CMS  ‪main
FileDownloadController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseFactoryInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Psr\Http\Message\StreamFactoryInterface;
33 
39 #[AsController]
41 {
43  protected ResponseFactoryInterface ‪$responseFactory;
44  protected StreamFactoryInterface ‪$streamFactory;
46 
47  public function ‪__construct(
49  ResponseFactoryInterface ‪$responseFactory,
50  StreamFactoryInterface ‪$streamFactory,
52  ) {
53  $this->resourceFactory = ‪$resourceFactory;
54  $this->responseFactory = ‪$responseFactory;
55  $this->streamFactory = ‪$streamFactory;
56  $this->context = ‪$context;
57  }
58 
59  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
60  {
61  $items = (array)($request->getParsedBody()['items'] ?? []);
62  if ($items === []) {
63  // Return in case no items are given
64  return $this->responseFactory->createResponse(400);
65  }
66 
67  $fileExtensionFilter = null;
68  $fileDownloadConfiguration = (array)($this->‪getBackendUser()->getTSConfig()['options.']['file_list.']['fileDownload.'] ?? []);
69  if ($fileDownloadConfiguration !== []) {
70  if (!($fileDownloadConfiguration['enabled'] ?? true)) {
71  // Return if file download is disabled
72  return $this->responseFactory->createResponse(403);
73  }
74  // Initialize file extension filter, if configured
75  $fileExtensionFilter = GeneralUtility::makeInstance(FileExtensionFilter::class);
76  $fileExtensionFilter->setAllowedFileExtensions(
77  ‪GeneralUtility::trimExplode(',', (string)($fileDownloadConfiguration['allowedFileExtensions'] ?? ''), true)
78  );
79  $fileExtensionFilter->setDisallowedFileExtensions(
80  ‪GeneralUtility::trimExplode(',', (string)($fileDownloadConfiguration['disallowedFileExtensions'] ?? ''), true)
81  );
82  }
83 
84  $zipStream = tmpfile();
85  if (!is_resource($zipStream)) {
86  throw new \RuntimeException('Could not open temporary resource for creating archive', 1630346631);
87  }
88  $zipFileName = stream_get_meta_data($zipStream)['uri'];
89  $zipFile = new \ZipArchive();
90  $zipFile->open($zipFileName, \ZipArchive::OVERWRITE);
91  $filesAdded = 0;
92  foreach ($this->‪collectFiles($items) as $fileName => $fileObject) {
93  // Add files with read permission and allowed file extension
94  if (!$fileObject->getStorage()->checkFileActionPermission('read', $fileObject)
95  || ($fileExtensionFilter !== null && !$fileExtensionFilter->isAllowed($fileObject->getExtension()))
96  ) {
97  continue;
98  }
99  $filesAdded++;
100  $zipFile->addFile($fileObject->getForLocalProcessing(false), $fileName);
101  }
102  $zipFile->close();
103  $response = $this->‪createResponse($zipFileName, $filesAdded);
104  unlink($zipFileName);
105  return $response;
106  }
107 
108  protected function ‪createResponse(string $temporaryFileName, int $filesAdded): ResponseInterface
109  {
110  if ($filesAdded === 0) {
111  return $this->responseFactory->createResponse()
112  ->withHeader('Content-Type', 'application/json; charset=utf-8')
113  ->withBody($this->streamFactory->createStream(json_encode(['success' => false, 'status' => 'noFiles'])));
114  }
115 
116  $downloadFileName = 'typo3_download_' . $this->context->getAspect('date')->getDateTime()->format('Y-m-d-His') . '.zip';
117  return $this->responseFactory->createResponse()
118  ->withHeader('Content-Type', 'application/zip')
119  ->withHeader('Content-Disposition', 'attachment; filename=' . $downloadFileName)
120  ->withHeader('Content-Transfer-Encoding', 'binary')
121  ->withHeader('Pragma', 'no-cache')
122  ->withHeader('Cache-Control', 'no-cache, no-store')
123  ->withBody($this->streamFactory->createStreamFromFile($temporaryFileName));
124  }
125 
129  protected function ‪collectFiles(array $items): array
130  {
131  $files = [];
132  foreach ($items as $itemIdentifier) {
133  $fileOrFolderObject = $this->resourceFactory->retrieveFileOrFolderObject($itemIdentifier);
134  if ($fileOrFolderObject === null) {
135  continue;
136  }
137  $baseIdentifier = dirname($fileOrFolderObject->getIdentifier());
138  if ($fileOrFolderObject instanceof ‪Folder) {
139  // handle file / folder structure
140  foreach ($this->‪getFilesAndFoldersRecursive($fileOrFolderObject) as $fileObject) {
141  $commonPrefix = (string)‪PathUtility::getCommonPrefix([$baseIdentifier, $fileObject->getIdentifier()]);
142  $files[substr($fileObject->getIdentifier(), strlen($commonPrefix))] = $fileObject;
143  }
144  } else {
145  $files[$fileOrFolderObject->getName()] = $fileOrFolderObject;
146  }
147  }
148  return $files;
149  }
150 
151  protected function ‪getFilesAndFoldersRecursive(‪Folder $folder): iterable
152  {
153  foreach ($folder->‪getSubfolders() as $subFolder) {
154  yield from $this->‪getFilesAndFoldersRecursive($subFolder);
155  }
156  foreach ($folder->‪getFiles() as $file) {
157  yield $file;
158  }
159  }
160 
162  {
163  return ‪$GLOBALS['BE_USER'];
164  }
165 }
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\getBackendUser
‪getBackendUser()
Definition: FileDownloadController.php:161
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:26
‪TYPO3\CMS\Core\Resource\Folder\getSubfolders
‪getSubfolders($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false)
Definition: Folder.php:267
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\$resourceFactory
‪ResourceFactory $resourceFactory
Definition: FileDownloadController.php:42
‪TYPO3\CMS\Core\Utility\PathUtility\getCommonPrefix
‪static getCommonPrefix(array $paths)
Definition: PathUtility.php:165
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\collectFiles
‪FileInterface[] collectFiles(array $items)
Definition: FileDownloadController.php:129
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\getFilesAndFoldersRecursive
‪getFilesAndFoldersRecursive(Folder $folder)
Definition: FileDownloadController.php:151
‪TYPO3\CMS\Filelist\Controller
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\$context
‪Context $context
Definition: FileDownloadController.php:45
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:38
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\createResponse
‪createResponse(string $temporaryFileName, int $filesAdded)
Definition: FileDownloadController.php:108
‪TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter
Definition: FileExtensionFilter.php:31
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Resource\Folder\getFiles
‪TYPO3 CMS Core Resource File[] getFiles($start=0, $numberOfItems=0, $filterMode=self::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, $recursive=false, $sort='', $sortRev=false)
Definition: Folder.php:202
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\__construct
‪__construct(ResourceFactory $resourceFactory, ResponseFactoryInterface $responseFactory, StreamFactoryInterface $streamFactory, Context $context)
Definition: FileDownloadController.php:47
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: FileDownloadController.php:59
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\$responseFactory
‪ResponseFactoryInterface $responseFactory
Definition: FileDownloadController.php:43
‪TYPO3\CMS\Filelist\Controller\FileDownloadController\$streamFactory
‪StreamFactoryInterface $streamFactory
Definition: FileDownloadController.php:44
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Filelist\Controller\FileDownloadController
Definition: FileDownloadController.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822