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