[TASK] Migrate tce_db, tce_file and FormEngine entry points to routing
[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\ServerRequestInterface;
18 use TYPO3\CMS\Core\Http\AjaxRequestHandler;
19 use TYPO3\CMS\Core\Http\Response;
20 use TYPO3\CMS\Core\Resource\Folder;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Utility\File\ExtendedFileUtility;
24
25 /**
26 * Gateway for TCE (TYPO3 Core Engine) file-handling through POST forms.
27 * This script serves as the file administration part of the TYPO3 Core Engine.
28 * Basically it includes two libraries which are used to manipulate files on the server.
29 * Before TYPO3 4.3, it was located in typo3/tce_file.php and redirected back to a
30 * $redirectURL. Since 4.3 this class is also used for accessing via AJAX
31 */
32 class FileController implements \TYPO3\CMS\Core\Http\ControllerInterface {
33
34 /**
35 * Array of file-operations.
36 *
37 * @var array
38 */
39 protected $file;
40
41 /**
42 * Clipboard operations array
43 *
44 * @var array
45 */
46 protected $CB;
47
48 /**
49 * Defines behaviour when uploading files with names that already exist; possible value are 'cancel', 'replace', 'changeName'
50 *
51 * @var string
52 */
53 protected $overwriteExistingFiles;
54
55 /**
56 * VeriCode - a hash of server specific value and other things which
57 * identifies if a submission is OK. (see $GLOBALS['BE_USER']->veriCode())
58 *
59 * @var string
60 */
61 protected $vC;
62
63 /**
64 * The page where the user should be redirected after everything is done
65 *
66 * @var string
67 */
68 protected $redirect;
69
70 /**
71 * Internal, dynamic:
72 * File processor object
73 *
74 * @var ExtendedFileUtility
75 */
76 protected $fileProcessor;
77
78 /**
79 * The result array from the file processor
80 *
81 * @var array
82 */
83 protected $fileData;
84
85 /**
86 * Constructor
87 */
88 public function __construct() {
89 $GLOBALS['SOBE'] = $this;
90 $this->init();
91 }
92
93 /**
94 * Registering incoming data
95 *
96 * @return void
97 */
98 protected function init() {
99 // Set the GPvars from outside
100 $this->file = GeneralUtility::_GP('file');
101 $this->CB = GeneralUtility::_GP('CB');
102 $this->overwriteExistingFiles = GeneralUtility::_GP('overwriteExistingFiles');
103
104 if ((string)$this->overwriteExistingFiles === '1') {
105 GeneralUtility::deprecationLog('overwriteExitingFiles = 1 is deprecated. Use overwriteExitingFiles = "replace". Support for old behavior will be removed in TYPO3 CMS 8.');
106 $this->overwriteExistingFiles = 'replace';
107 }
108
109 $this->vC = GeneralUtility::_GP('vC');
110 $this->redirect = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('redirect'));
111 $this->initClipboard();
112 $this->fileProcessor = GeneralUtility::makeInstance(ExtendedFileUtility::class);
113 }
114
115 /**
116 * Initialize the Clipboard. This will fetch the data about files to paste/delete if such an action has been sent.
117 *
118 * @return void
119 */
120 public function initClipboard() {
121 if (is_array($this->CB)) {
122 $clipObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Clipboard\Clipboard::class);
123 $clipObj->initializeClipboard();
124 if ($this->CB['paste']) {
125 $clipObj->setCurrentPad($this->CB['pad']);
126 $this->file = $clipObj->makePasteCmdArray_file($this->CB['paste'], $this->file);
127 }
128 if ($this->CB['delete']) {
129 $clipObj->setCurrentPad($this->CB['pad']);
130 $this->file = $clipObj->makeDeleteCmdArray_file($this->file);
131 }
132 }
133 }
134
135 /**
136 * Performing the file admin action:
137 * Initializes the objects, setting permissions, sending data to object.
138 *
139 * @return void
140 */
141 public function main() {
142 // Initializing:
143 $this->fileProcessor->init(array(), $GLOBALS['TYPO3_CONF_VARS']['BE']['fileExtensions']);
144 $this->fileProcessor->setActionPermissions();
145 switch ($this->overwriteExistingFiles) {
146 case 'replace':
147 case 'changeName':
148 $conflictMode = $this->overwriteExistingFiles;
149 break;
150 default:
151 $conflictMode = 'cancel';
152 break;
153 }
154 $this->fileProcessor->setExistingFilesConflictMode($conflictMode);
155 // Checking referrer / executing:
156 $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
157 $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
158 if ($httpHost !== $refInfo['host'] && $this->vC !== $this->getBackendUser()->veriCode() && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']) {
159 $this->fileProcessor->writeLog(0, 2, 1, 'Referrer host "%s" and server host "%s" did not match!', array($refInfo['host'], $httpHost));
160 } else {
161 $this->fileProcessor->start($this->file);
162 $this->fileData = $this->fileProcessor->processData();
163 }
164 }
165
166 /**
167 * Redirecting the user after the processing has been done.
168 * Might also display error messages directly, if any.
169 *
170 * @return void
171 */
172 public function finish() {
173 // Push errors to flash message queue, if there are any
174 $this->fileProcessor->pushErrorMessagesToFlashMessageQueue();
175 BackendUtility::setUpdateSignal('updateFolderTree');
176 if ($this->redirect) {
177 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($this->redirect);
178 }
179 }
180
181 /**
182 * Injects the request object for the current request or subrequest
183 * As this controller goes only through the main() method, it just redirects to the given URL afterwards.
184 *
185 * @param ServerRequestInterface $request
186 * @return \Psr\Http\Message\ResponseInterface $response
187 */
188 public function processRequest(ServerRequestInterface $request) {
189 $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
190 $formToken = isset($request->getQueryParams()['formToken']) ? $request->getQueryParams()['formToken'] : $request->getParsedBody()['formToken'];
191 if ($formProtection->validateToken($formToken, 'tceAction')) {
192 $this->main();
193 }
194
195 // Push errors to flash message queue, if there are any
196 $this->fileProcessor->pushErrorMessagesToFlashMessageQueue();
197 BackendUtility::setUpdateSignal('updateFolderTree');
198
199 /** @var Response $response */
200 $response = GeneralUtility::makeInstance(Response::class);
201 if ($this->redirect) {
202 $response = $response->withHeader('Location', GeneralUtility::locationHeaderUrl($this->redirect));
203 return $response->withStatus(303);
204 } else {
205 // empty response
206 return $response;
207 }
208 }
209
210 /**
211 * Handles the actual process from within the ajaxExec function
212 * therefore, it does exactly the same as the real typo3/tce_file.php
213 * but without calling the "finish" method, thus makes it simpler to deal with the
214 * actual return value
215 *
216 * @param array $params Always empty.
217 * @param AjaxRequestHandler $ajaxObj The AjaxRequestHandler object used to return content and set content types
218 * @return void
219 */
220 public function processAjaxRequest(array $params, AjaxRequestHandler $ajaxObj) {
221 $this->main();
222 $errors = $this->fileProcessor->getErrorMessages();
223 if (!empty($errors)) {
224 $ajaxObj->setError(implode(',', $errors));
225 } else {
226 $flatResult = array();
227 foreach ($this->fileData as $action => $results) {
228 foreach ($results as $result) {
229 if (is_array($result)) {
230 foreach ($result as $subResult) {
231 $flatResult[$action][] = $this->flattenResultDataValue($subResult);
232 }
233 } else {
234 $flatResult[$action][] = $this->flattenResultDataValue($result);
235 }
236 }
237 }
238 $ajaxObj->addContent('result', $flatResult);
239 if ($this->redirect) {
240 $ajaxObj->addContent('redirect', $this->redirect);
241 }
242 $ajaxObj->setContentFormat('json');
243 }
244 }
245
246 /**
247 * Ajax entry point to check if a file exists in a folder
248 *
249 * @param array $params Always empty.
250 * @param AjaxRequestHandler $ajaxObj The AjaxRequestHandler object used to return content and set content types
251 * @return void
252 */
253 public function fileExistsAjaxRequest(array $params, AjaxRequestHandler $ajaxObj) {
254 $fileName = GeneralUtility::_GP('fileName');
255 $fileTarget = GeneralUtility::_GP('fileTarget');
256
257 /** @var \TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory */
258 $fileFactory = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
259 /** @var Folder $fileTargetObject */
260 $fileTargetObject = $fileFactory->retrieveFileOrFolderObject($fileTarget);
261 $processedFileName = $fileTargetObject->getStorage()->sanitizeFileName($fileName, $fileTargetObject);
262
263 $result = FALSE;
264 if ($fileTargetObject->hasFile($processedFileName)) {
265 $result = $this->flattenResultDataValue($fileTargetObject->getStorage()->getFileInFolder($processedFileName, $fileTargetObject));
266 }
267 $ajaxObj->addContent('result', $result);
268 $ajaxObj->setContentFormat('json');
269 }
270
271 /**
272 * Flatten result value from FileProcessor
273 *
274 * The value can be a File, Folder or boolean
275 *
276 * @param bool|\TYPO3\CMS\Core\Resource\File|\TYPO3\CMS\Core\Resource\Folder $result
277 * @return bool|string|array
278 */
279 protected function flattenResultDataValue($result) {
280 if ($result instanceof \TYPO3\CMS\Core\Resource\File) {
281 $thumbUrl = '';
282 if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $result->getExtension())) {
283 $processedFile = $result->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array());
284 if ($processedFile) {
285 $thumbUrl = $processedFile->getPublicUrl(TRUE);
286 }
287 }
288 $result = array_merge(
289 $result->toArray(),
290 array (
291 'date' => BackendUtility::date($result->getModificationTime()),
292 'iconClasses' => \TYPO3\CMS\Backend\Utility\IconUtility::mapFileExtensionToSpriteIconClass($result->getExtension()),
293 'thumbUrl' => $thumbUrl
294 )
295 );
296 } elseif ($result instanceof \TYPO3\CMS\Core\Resource\Folder) {
297 $result = $result->getIdentifier();
298 }
299
300 return $result;
301 }
302
303 /**
304 * Returns the current BE user.
305 *
306 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
307 */
308 protected function getBackendUser() {
309 return $GLOBALS['BE_USER'];
310 }
311
312 }