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