[TASK] Use trait for public method access deprecation
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / File / FileController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Backend\Controller\File;
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
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Clipboard\Clipboard;
21 use TYPO3\CMS\Backend\Routing\UriBuilder;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
25 use TYPO3\CMS\Core\Http\HtmlResponse;
26 use TYPO3\CMS\Core\Http\JsonResponse;
27 use TYPO3\CMS\Core\Http\RedirectResponse;
28 use TYPO3\CMS\Core\Imaging\Icon;
29 use TYPO3\CMS\Core\Imaging\IconFactory;
30 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
31 use TYPO3\CMS\Core\Resource\File;
32 use TYPO3\CMS\Core\Resource\Folder;
33 use TYPO3\CMS\Core\Resource\ProcessedFile;
34 use TYPO3\CMS\Core\Resource\ResourceFactory;
35 use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
36 use TYPO3\CMS\Core\Utility\GeneralUtility;
37 use TYPO3\CMS\Core\Utility\HttpUtility;
38
39 /**
40 * Gateway for TCE (TYPO3 Core Engine) file-handling through POST forms.
41 * This script serves as the file administration part of the TYPO3 Core Engine.
42 * Basically it includes two libraries which are used to manipulate files on the server.
43 */
44 class FileController
45 {
46 use PublicMethodDeprecationTrait;
47
48 /**
49 * @var array
50 */
51 private $deprecatedPublicMethods = [
52 'initClipboard' => 'Using FileController::initClipboard() is deprecated and will not be possible anymore in TYPO3 v10.',
53 'main' => 'Using FileController::main() is deprecated and will not be possible anymore in TYPO3 v10.',
54 ];
55
56 /**
57 * Array of file-operations.
58 *
59 * @var array
60 */
61 protected $file;
62
63 /**
64 * Clipboard operations array
65 *
66 * @var array
67 */
68 protected $CB;
69
70 /**
71 * Defines behaviour when uploading files with names that already exist; possible values are
72 * the values of the \TYPO3\CMS\Core\Resource\DuplicationBehavior enumeration
73 *
74 * @var \TYPO3\CMS\Core\Resource\DuplicationBehavior
75 */
76 protected $overwriteExistingFiles;
77
78 /**
79 * The page where the user should be redirected after everything is done
80 *
81 * @var string
82 */
83 protected $redirect;
84
85 /**
86 * Internal, dynamic:
87 * File processor object
88 *
89 * @var ExtendedFileUtility
90 */
91 protected $fileProcessor;
92
93 /**
94 * The result array from the file processor
95 *
96 * @var array
97 */
98 protected $fileData;
99
100 /**
101 * Constructor
102 */
103 public function __construct()
104 {
105 $GLOBALS['SOBE'] = $this;
106 // @deprecated since v9, will be moved out of __construct() in v10
107 $this->init($GLOBALS['TYPO3_REQUEST']);
108 }
109
110 /**
111 * Injects the request object for the current request or subrequest
112 * As this controller goes only through the main() method, it just redirects to the given URL afterwards.
113 *
114 * @param ServerRequestInterface $request the current request
115 * @return ResponseInterface the response with the content
116 */
117 public function mainAction(ServerRequestInterface $request): ResponseInterface
118 {
119 $this->main();
120
121 BackendUtility::setUpdateSignal('updateFolderTree');
122
123 // go and edit the new created file
124 if ($request->getParsedBody()['edit'] ?? '') {
125 /** @var File $file */
126 $file = $this->fileData['newfile'][0];
127 $properties = $file->getProperties();
128 $urlParameters = [
129 'target' => $properties['storage'] . ':' . $properties['identifier']
130 ];
131 if ($this->redirect) {
132 $urlParameters['returnUrl'] = $this->redirect;
133 }
134 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
135 $this->redirect = (string)$uriBuilder->buildUriFromRoute('file_edit', $urlParameters);
136 }
137 if ($this->redirect) {
138 return new RedirectResponse(
139 GeneralUtility::locationHeaderUrl($this->redirect),
140 303
141 );
142 }
143 // empty response
144 return new HtmlResponse('');
145 }
146
147 /**
148 * Handles the actual process from within the ajaxExec function
149 * therefore, it does exactly the same as the real typo3/tce_file.php
150 * but without calling the "finish" method, thus makes it simpler to deal with the
151 * actual return value
152 *
153 * @param ServerRequestInterface $request
154 * @return ResponseInterface
155 */
156 public function processAjaxRequest(ServerRequestInterface $request): ResponseInterface
157 {
158 $this->main();
159 $errors = $this->fileProcessor->getErrorMessages();
160 if (!empty($errors)) {
161 return (new HtmlResponse('<t3err>' . implode(',', $errors) . '</t3err>'))->withStatus(500, '(AJAX)');
162 }
163 $flatResult = [];
164 foreach ($this->fileData as $action => $results) {
165 foreach ($results as $result) {
166 if (is_array($result)) {
167 foreach ($result as $subResult) {
168 $flatResult[$action][] = $this->flattenResultDataValue($subResult);
169 }
170 } else {
171 $flatResult[$action][] = $this->flattenResultDataValue($result);
172 }
173 }
174 }
175 return (new JsonResponse())->setPayload($flatResult);
176 }
177
178 /**
179 * Ajax entry point to check if a file exists in a folder
180 *
181 * @param ServerRequestInterface $request
182 * @return ResponseInterface
183 */
184 public function fileExistsInFolderAction(ServerRequestInterface $request): ResponseInterface
185 {
186 $fileName = $request->getParsedBody()['fileName'] ?? $request->getQueryParams()['fileName'] ?? null;
187 $fileTarget = $request->getParsedBody()['fileTarget'] ?? $request->getQueryParams()['fileTarget'] ?? null;
188
189 $fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
190 $fileTargetObject = $fileFactory->retrieveFileOrFolderObject($fileTarget);
191 $processedFileName = $fileTargetObject->getStorage()->sanitizeFileName($fileName, $fileTargetObject);
192
193 $result = [];
194 if ($fileTargetObject->hasFile($processedFileName)) {
195 $result = $this->flattenResultDataValue($fileTargetObject->getStorage()->getFileInFolder($processedFileName, $fileTargetObject));
196 }
197 return (new JsonResponse())->setPayload($result);
198 }
199
200 /**
201 * Registering incoming data
202 *
203 * @param ServerRequestInterface $request
204 */
205 protected function init(ServerRequestInterface $request): void
206 {
207 // Set the GPvars from outside
208 $parsedBody = $request->getParsedBody();
209 $queryParams = $request->getQueryParams();
210 $this->file = $parsedBody['data'] ?? $queryParams['data'] ?? null;
211 if ($this->file === null) {
212 // This happens in clipboard mode only
213 $this->redirect = GeneralUtility::sanitizeLocalUrl($parsedBody['redirect'] ?? $queryParams['redirect'] ?? '');
214 } else {
215 $mode = key($this->file);
216 $elementKey = key($this->file[$mode]);
217 $this->redirect = GeneralUtility::sanitizeLocalUrl($this->file[$mode][$elementKey]['redirect']);
218 }
219 $this->CB = $parsedBody['CB'] ?? $queryParams['CB'] ?? null;
220
221 if (isset($this->file['rename'][0]['conflictMode'])) {
222 $conflictMode = $this->file['rename'][0]['conflictMode'];
223 unset($this->file['rename'][0]['conflictMode']);
224 $this->overwriteExistingFiles = DuplicationBehavior::cast($conflictMode);
225 } else {
226 $this->overwriteExistingFiles = DuplicationBehavior::cast($parsedBody['overwriteExistingFiles'] ?? $queryParams['overwriteExistingFiles'] ?? null);
227 }
228 $this->initClipboard();
229 $this->fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class);
230 }
231
232 /**
233 * Initialize the Clipboard. This will fetch the data about files to paste/delete if such an action has been sent.
234 */
235 protected function initClipboard(): void
236 {
237 if (is_array($this->CB)) {
238 $clipObj = GeneralUtility::makeInstance(Clipboard::class);
239 $clipObj->initializeClipboard();
240 if ($this->CB['paste']) {
241 $clipObj->setCurrentPad($this->CB['pad']);
242 $this->file = $clipObj->makePasteCmdArray_file($this->CB['paste'], $this->file);
243 }
244 if ($this->CB['delete']) {
245 $clipObj->setCurrentPad($this->CB['pad']);
246 $this->file = $clipObj->makeDeleteCmdArray_file($this->file);
247 }
248 }
249 }
250
251 /**
252 * Performing the file admin action:
253 * Initializes the objects, setting permissions, sending data to object.
254 */
255 protected function main(): void
256 {
257 // Initializing:
258 $this->fileProcessor->setActionPermissions();
259 $this->fileProcessor->setExistingFilesConflictMode($this->overwriteExistingFiles);
260 $this->fileProcessor->start($this->file);
261 $this->fileData = $this->fileProcessor->processData();
262 }
263
264 /**
265 * Redirecting the user after the processing has been done.
266 * Might also display error messages directly, if any.
267 *
268 * @deprecated since TYPO3 v9. Will be removed in v10.
269 */
270 public function finish()
271 {
272 trigger_error('Method finish() will be removed in v10. Do not call from other extension', E_USER_DEPRECATED);
273 BackendUtility::setUpdateSignal('updateFolderTree');
274 if ($this->redirect) {
275 HttpUtility::redirect($this->redirect);
276 }
277 }
278
279 /**
280 * Flatten result value from FileProcessor
281 *
282 * The value can be a File, Folder or boolean
283 *
284 * @param bool|File|Folder $result
285 *
286 * @return bool|string|array
287 */
288 protected function flattenResultDataValue($result)
289 {
290 if ($result instanceof File) {
291 $thumbUrl = '';
292 if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $result->getExtension())) {
293 $processedFile = $result->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, []);
294 if ($processedFile) {
295 $thumbUrl = $processedFile->getPublicUrl(true);
296 }
297 }
298 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
299 $result = array_merge(
300 $result->toArray(),
301 [
302 'date' => BackendUtility::date($result->getModificationTime()),
303 'icon' => $iconFactory->getIconForFileExtension($result->getExtension(), Icon::SIZE_SMALL)->render(),
304 'thumbUrl' => $thumbUrl
305 ]
306 );
307 } elseif ($result instanceof Folder) {
308 $result = $result->getIdentifier();
309 }
310
311 return $result;
312 }
313
314 /**
315 * Returns the current BE user.
316 *
317 * @return BackendUserAuthentication
318 */
319 protected function getBackendUser(): BackendUserAuthentication
320 {
321 return $GLOBALS['BE_USER'];
322 }
323 }