0415f673ca37b271f0291b89101c34c053ff8986
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / LinkHandler / FileLinkHandler.php
1 <?php
2 namespace TYPO3\CMS\Recordlist\LinkHandler;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ServerRequestInterface;
18 use TYPO3\CMS\Backend\Tree\View\ElementBrowserFolderTreeView;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\LinkHandling\LinkService;
21 use TYPO3\CMS\Core\Page\PageRenderer;
22 use TYPO3\CMS\Core\Resource\File;
23 use TYPO3\CMS\Core\Resource\FileInterface;
24 use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
25 use TYPO3\CMS\Core\Resource\Folder;
26 use TYPO3\CMS\Core\Resource\ResourceFactory;
27 use TYPO3\CMS\Core\Resource\ResourceInterface;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface;
30 use TYPO3\CMS\Recordlist\View\FolderUtilityRenderer;
31
32 /**
33 * Link handler for files
34 * @internal This class is a specific LinkHandler implementation and is not part of the TYPO3's Core API.
35 */
36 class FileLinkHandler extends AbstractLinkHandler implements LinkHandlerInterface, LinkParameterProviderInterface
37 {
38 /**
39 * Parts of the current link
40 *
41 * @var array
42 */
43 protected $linkParts = [];
44
45 /**
46 * @var string
47 */
48 protected $expectedClass = File::class;
49
50 /**
51 * @var string
52 */
53 protected $mode = 'file';
54
55 /**
56 * @var string
57 */
58 protected $expandFolder;
59
60 /**
61 * Checks if this is the handler for the given link
62 *
63 * The handler may store this information locally for later usage.
64 *
65 * @param array $linkParts Link parts as returned from TypoLinkCodecService
66 *
67 * @return bool
68 */
69 public function canHandleLink(array $linkParts)
70 {
71 if (!$linkParts['url']) {
72 return false;
73 }
74 if (isset($linkParts['url'][$this->mode]) && $linkParts['url'][$this->mode] instanceof $this->expectedClass) {
75 $this->linkParts = $linkParts;
76 return true;
77 }
78 return false;
79 }
80
81 /**
82 * Format the current link for HTML output
83 *
84 * @return string
85 */
86 public function formatCurrentUrl()
87 {
88 return $this->linkParts['url'][$this->mode]->getName();
89 }
90
91 /**
92 * Render the link handler
93 *
94 * @param ServerRequestInterface $request
95 *
96 * @return string
97 */
98 public function render(ServerRequestInterface $request)
99 {
100 GeneralUtility::makeInstance(PageRenderer::class)->loadRequireJsModule('TYPO3/CMS/Recordlist/FileLinkHandler');
101
102 /** @var ElementBrowserFolderTreeView $folderTree */
103 $folderTree = GeneralUtility::makeInstance(ElementBrowserFolderTreeView::class);
104 $folderTree->setLinkParameterProvider($this);
105 $this->view->assign('tree', $folderTree->getBrowsableTree());
106
107 $this->expandFolder = $request->getQueryParams()['expandFolder'] ?? null;
108 if (!empty($this->linkParts) && !isset($this->expandFolder)) {
109 $this->expandFolder = $this->linkParts['url'][$this->mode];
110 if ($this->expandFolder instanceof File) {
111 $this->expandFolder = $this->expandFolder->getParentFolder();
112 }
113 if ($this->expandFolder instanceof Folder) {
114 $this->expandFolder = $this->expandFolder->getCombinedIdentifier();
115 }
116 }
117
118 // Create upload/create folder forms, if a path is given
119 $selectedFolder = $this->getSelectedFolder($this->expandFolder);
120
121 // Build the file upload and folder creation form
122 if ($selectedFolder) {
123 $folderUtilityRenderer = GeneralUtility::makeInstance(FolderUtilityRenderer::class, $this);
124 $uploadForm = $this->mode === 'file' ? $folderUtilityRenderer->uploadForm($selectedFolder, []) : '';
125 $createFolder = $folderUtilityRenderer->createFolder($selectedFolder);
126
127 $this->view->assign('uploadFileForm', $uploadForm);
128 $this->view->assign('createFolderForm', $createFolder);
129
130 // Render the file or folderlist
131 if ($selectedFolder->checkActionPermission('read')) {
132 $this->view->assign('selectedFolder', $selectedFolder);
133 $parameters = $this->linkBrowser->getParameters();
134 $allowedExtensions = $parameters['params']['allowedExtensions'] ?? '';
135 $this->expandFolder($selectedFolder, $allowedExtensions);
136 }
137 }
138
139 return $this->view->render(ucfirst($this->mode));
140 }
141
142 /**
143 * For RTE: This displays all files from folder. No thumbnails shown
144 *
145 * @param Folder $folder The folder path to expand
146 * @param string $extensionList List of file extensions to show
147 */
148 public function expandFolder(Folder $folder, $extensionList = '')
149 {
150 // Create header element; The folder from which files are listed.
151 $folderIcon = $this->iconFactory->getIconForResource($folder, Icon::SIZE_SMALL)->render();
152 $this->view->assign('selectedFolderIcon', $folderIcon);
153 $this->view->assign('selectedFolderTitle', GeneralUtility::fixed_lgd_cs($folder->getIdentifier(), (int)$this->getBackendUser()->uc['titleLen']));
154 if ($this->mode === 'file') {
155 $this->view->assign('currentIdentifier', !empty($this->linkParts) ? $this->linkParts['url']['file']->getUid() : '');
156 } else {
157 $this->view->assign('currentIdentifier', !empty($this->linkParts) ? $this->linkParts['url']['folder']->getCombinedIdentifier() : '');
158 }
159
160 // Get files from the folder:
161 $fileObjects = $this->getFolderContent($folder, $extensionList);
162 $itemsInSelectedFolder = [];
163 if (!empty($fileObjects)) {
164 foreach ($fileObjects as $fileOrFolderObject) {
165 $itemsInSelectedFolder[] = $this->renderItem($fileOrFolderObject);
166 }
167 }
168 $this->view->assign('itemsInSelectedFolder', $itemsInSelectedFolder);
169 }
170
171 /**
172 * @param Folder $folder
173 * @param string $extensionList
174 *
175 * @return FileInterface[]|Folder[]
176 */
177 protected function getFolderContent(Folder $folder, $extensionList)
178 {
179 if ($extensionList !== '') {
180 /** @var FileExtensionFilter $filter */
181 $filter = GeneralUtility::makeInstance(FileExtensionFilter::class);
182 $filter->setAllowedFileExtensions($extensionList);
183 $folder->setFileAndFolderNameFilters([[$filter, 'filterFileList']]);
184 }
185 return $folder->getFiles();
186 }
187
188 /**
189 * Renders a single item displayed in the current folder
190 *
191 * @param ResourceInterface $fileOrFolderObject
192 *
193 * @return array
194 * @throws \InvalidArgumentException
195 */
196 protected function renderItem(ResourceInterface $fileOrFolderObject)
197 {
198 if (!$fileOrFolderObject instanceof File) {
199 throw new \InvalidArgumentException('Expected File object, got "' . get_class($fileOrFolderObject) . '" object.', 1443651368);
200 }
201 // Get size and icon:
202 $size = GeneralUtility::formatSize(
203 $fileOrFolderObject->getSize(),
204 $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:byteSizeUnits')
205 );
206
207 return [
208 'icon' => $this->iconFactory->getIconForResource($fileOrFolderObject, Icon::SIZE_SMALL)->render(),
209 'uid' => $fileOrFolderObject->getUid(),
210 'size' => $size,
211 'name' => $fileOrFolderObject->getName(),
212 'url' => GeneralUtility::makeInstance(LinkService::class)->asString(['type' => LinkService::TYPE_FILE, 'file' => $fileOrFolderObject]),
213 'title' => GeneralUtility::fixed_lgd_cs($fileOrFolderObject->getName(), (int)$this->getBackendUser()->uc['titleLen'])
214 ];
215 }
216
217 /**
218 * @return string[] Array of body-tag attributes
219 */
220 public function getBodyTagAttributes()
221 {
222 return [
223 'data-current-link' => GeneralUtility::makeInstance(LinkService::class)->asString(['type' => LinkService::TYPE_FILE, 'file' => $this->linkParts['url']['file']])
224 ];
225 }
226
227 /**
228 * @param array $values Array of values to include into the parameters or which might influence the parameters
229 *
230 * @return string[] Array of parameters which have to be added to URLs
231 */
232 public function getUrlParameters(array $values)
233 {
234 $parameters = [
235 'expandFolder' => $values['identifier'] ?? $this->expandFolder
236 ];
237 return array_merge($this->linkBrowser->getUrlParameters($values), $parameters);
238 }
239
240 /**
241 * @param array $values Values to be checked
242 *
243 * @return bool Returns TRUE if the given values match the currently selected item
244 */
245 public function isCurrentlySelectedItem(array $values)
246 {
247 return false;
248 }
249
250 /**
251 * Returns the URL of the current script
252 *
253 * @return string
254 */
255 public function getScriptUrl()
256 {
257 return $this->linkBrowser->getScriptUrl();
258 }
259
260 /**
261 * Returns the currently selected folder, or th default upload folder
262 *
263 * @param string $folderIdentifier
264 * @return mixed the folder object or false if nothing was found
265 */
266 protected function getSelectedFolder($folderIdentifier = '')
267 {
268 $selectedFolder = false;
269 if ($folderIdentifier) {
270 try {
271 $fileOrFolderObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($folderIdentifier);
272 if ($fileOrFolderObject instanceof Folder) {
273 // It's a folder
274 $selectedFolder = $fileOrFolderObject;
275 } elseif ($fileOrFolderObject instanceof FileInterface) {
276 // It's a file
277 try {
278 $selectedFolder = $fileOrFolderObject->getParentFolder();
279 } catch (\Exception $e) {
280 // Accessing the parent folder failed for some reason. e.g. permissions
281 }
282 }
283 } catch (\Exception $e) {
284 // No path is selected
285 }
286 }
287
288 // If no folder is selected, get the user's default upload folder
289 if (!$selectedFolder) {
290 try {
291 $selectedFolder = $this->getBackendUser()->getDefaultUploadFolder();
292 } catch (\Exception $e) {
293 // The configured default user folder does not exist
294 }
295 }
296 return $selectedFolder;
297 }
298 }