[!!!][TASK] Remove deprecated code in EditFileController
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / File / EditFileController.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\Form\FormResultCompiler;
21 use TYPO3\CMS\Backend\Form\NodeFactory;
22 use TYPO3\CMS\Backend\Routing\UriBuilder;
23 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
24 use TYPO3\CMS\Backend\Template\ModuleTemplate;
25 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
26 use TYPO3\CMS\Core\Http\HtmlResponse;
27 use TYPO3\CMS\Core\Http\RedirectResponse;
28 use TYPO3\CMS\Core\Imaging\Icon;
29 use TYPO3\CMS\Core\Localization\LanguageService;
30 use TYPO3\CMS\Core\Messaging\FlashMessage;
31 use TYPO3\CMS\Core\Messaging\FlashMessageService;
32 use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
33 use TYPO3\CMS\Core\Resource\ResourceFactory;
34 use TYPO3\CMS\Core\Utility\GeneralUtility;
35 use TYPO3\CMS\Fluid\View\StandaloneView;
36
37 /**
38 * Script Class for rendering the file editing screen
39 * @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
40 */
41 class EditFileController
42 {
43 /**
44 * Module content accumulated.
45 *
46 * @var string
47 */
48 protected $content;
49
50 /**
51 * Original input target
52 *
53 * @var string
54 */
55 protected $origTarget;
56
57 /**
58 * The original target, but validated.
59 *
60 * @var string
61 */
62 protected $target;
63
64 /**
65 * Return URL of list module.
66 *
67 * @var string
68 */
69 protected $returnUrl;
70
71 /**
72 * the file that is being edited on
73 *
74 * @var \TYPO3\CMS\Core\Resource\AbstractFile
75 */
76 protected $fileObject;
77
78 /**
79 * ModuleTemplate object
80 *
81 * @var ModuleTemplate
82 */
83 protected $moduleTemplate;
84
85 /**
86 * Constructor
87 */
88 public function __construct()
89 {
90 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
91 }
92
93 /**
94 * Processes the request, currently everything is handled and put together via "process()"
95 *
96 * @param ServerRequestInterface $request the current request
97 * @return ResponseInterface the response with the content
98 */
99 public function mainAction(ServerRequestInterface $request): ResponseInterface
100 {
101 $this->init($request);
102 if ($response = $this->process()) {
103 return $response;
104 }
105
106 return new HtmlResponse($this->moduleTemplate->renderContent());
107 }
108
109 /**
110 * Initialize script class
111 *
112 * @param ServerRequestInterface $request
113 *
114 * @throws InsufficientFileAccessPermissionsException
115 */
116 protected function init(ServerRequestInterface $request): void
117 {
118 $parsedBody = $request->getParsedBody();
119 $queryParams = $request->getQueryParams();
120
121 // Setting target, which must be a file reference to a file within the mounts.
122 $this->target = $this->origTarget = $parsedBody['target'] ?? $queryParams['target'] ?? '';
123 $this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
124 // create the file object
125 if ($this->target) {
126 $this->fileObject = ResourceFactory::getInstance()
127 ->retrieveFileOrFolderObject($this->target);
128 }
129 // Cleaning and checking target directory
130 if (!$this->fileObject) {
131 $title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
132 $message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
133 throw new \RuntimeException($title . ': ' . $message, 1294586841);
134 }
135 if ($this->fileObject->getStorage()->getUid() === 0) {
136 throw new InsufficientFileAccessPermissionsException(
137 'You are not allowed to access files outside your storages',
138 1375889832
139 );
140 }
141
142 // Setting template object
143 $this->moduleTemplate->addJavaScriptCode(
144 'FileEditBackToList',
145 'function backToList() {
146 top.goToModule("file_FilelistList");
147 }'
148 );
149 }
150
151 /**
152 * Main function, rendering the actual content of the editing page
153 *
154 * @return ResponseInterface|null Possible redirect response
155 */
156 protected function process(): ?ResponseInterface
157 {
158 $dataColumnDefinition = [
159 'label' => htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:file'))
160 . ' ' . htmlspecialchars($this->target),
161 'config' => [
162 'type' => 'text',
163 'cols' => 48,
164 'wrap' => 'OFF',
165 ],
166 'defaultExtras' => 'fixed-font: enable-tab'
167 ];
168
169 $this->getButtonsInternal();
170 // Hook: before compiling the output
171 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook'] ?? [] as $hookFunction) {
172 $hookParameters = [
173 'content' => &$this->content,
174 'target' => &$this->target,
175 'dataColumnDefinition' => &$dataColumnDefinition,
176 ];
177 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
178 }
179
180 $assigns = [];
181 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
182 $assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
183 $assigns['fileName'] = $this->fileObject->getName();
184
185 $extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
186 try {
187 if (!$extList || !GeneralUtility::inList($extList, $this->fileObject->getExtension())) {
188 // @todo throw a minor exception here, not the global one
189 throw new \Exception('Files with that extension are not editable. Allowed extensions are: ' . $extList, 1476050135);
190 }
191
192 // Making the formfields
193 $hValue = (string)$uriBuilder->buildUriFromRoute('file_edit', [
194 'target' => $this->origTarget,
195 'returnUrl' => $this->returnUrl
196 ]);
197
198 $formData = [
199 'databaseRow' => [
200 'uid' => 0,
201 'data' => $this->fileObject->getContents(),
202 'target' => $this->fileObject->getUid(),
203 'redirect' => $hValue,
204 ],
205 'tableName' => 'editfile',
206 'processedTca' => [
207 'columns' => [
208 'data' => $dataColumnDefinition,
209 'target' => [
210 'config' => [
211 'type' => 'input',
212 'renderType' => 'hidden',
213 ],
214 ],
215 'redirect' => [
216 'config' => [
217 'type' => 'input',
218 'renderType' => 'hidden',
219 ],
220 ],
221 ],
222 'types' => [
223 1 => [
224 'showitem' => 'data,target,redirect',
225 ],
226 ],
227 ],
228 'recordTypeValue' => 1,
229 'inlineStructure' => [],
230 'renderType' => 'fullRecordContainer',
231 ];
232
233 $resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
234 $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
235 $formResultCompiler->mergeResult($resultArray);
236
237 $form = $formResultCompiler->addCssFiles()
238 . $resultArray['html']
239 . $formResultCompiler->printNeededJSFunctions();
240
241 $assigns['form'] = $form;
242 } catch (\Exception $e) {
243 // @todo catch dedicated exceptions, not the global one, if possible
244 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $e->getMessage(), '', FlashMessage::ERROR, true);
245
246 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
247 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
248 $defaultFlashMessageQueue->enqueue($flashMessage);
249
250 return new RedirectResponse($this->returnUrl, 500);
251 }
252
253 // Rendering of the output via fluid
254 $view = GeneralUtility::makeInstance(StandaloneView::class);
255 $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
256 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
257 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
258 'EXT:backend/Resources/Private/Templates/File/EditFile.html'
259 ));
260 $view->assignMultiple($assigns);
261 $pageContent = $view->render();
262
263 // Hook: after compiling the output
264 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook'] ?? [] as $hookFunction) {
265 $hookParameters = [
266 'pageContent' => &$pageContent,
267 'target' => &$this->target
268 ];
269 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
270 }
271
272 $this->content .= $pageContent;
273 $this->moduleTemplate->setContent($this->content);
274 return null;
275 }
276
277 /**
278 * Builds the buttons for the docheader and returns them as an
279 *
280 * @deprecated since TYPO3 v9, will be set protected in TYPO3 v10.0
281 */
282 public function getButtons()
283 {
284 trigger_error('EditFileController->getButtons() will be replaced by protected method getButtonsInternal() in TYPO3 v10.0. Do not call from other extension.', E_USER_DEPRECATED);
285 $this->getButtonsInternal();
286 }
287
288 /**
289 * Builds the buttons for the docheader
290 */
291 protected function getButtonsInternal(): void
292 {
293 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
294
295 $lang = $this->getLanguageService();
296 // CSH button
297 $helpButton = $buttonBar->makeHelpButton()
298 ->setFieldName('file_edit')
299 ->setModuleName('xMOD_csh_corebe');
300 $buttonBar->addButton($helpButton);
301
302 // Save button
303 $saveButton = $buttonBar->makeInputButton()
304 ->setName('_save')
305 ->setValue('1')
306 ->setForm('EditFileController')
307 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_edit.php.submit'))
308 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL));
309
310 // Save and Close button
311 $saveAndCloseButton = $buttonBar->makeInputButton()
312 ->setName('_saveandclosedok')
313 ->setValue('1')
314 ->setForm('EditFileController')
315 ->setOnClick(
316 'document.editform.elements.namedItem("data[editfile][0][redirect]").value='
317 . GeneralUtility::quoteJSvalue($this->returnUrl)
318 . ';'
319 )
320 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:file_edit.php.saveAndClose'))
321 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
322 'actions-document-save-close',
323 Icon::SIZE_SMALL
324 ));
325
326 $splitButton = $buttonBar->makeSplitButton()
327 ->addItem($saveButton)
328 ->addItem($saveAndCloseButton);
329 $buttonBar->addButton($splitButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
330
331 // Cancel button
332 $closeButton = $buttonBar->makeLinkButton()
333 ->setHref('#')
334 ->setOnClick('backToList(); return false;')
335 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel'))
336 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL));
337 $buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
338
339 // Make shortcut:
340 $shortButton = $buttonBar->makeShortcutButton()
341 ->setModuleName('file_edit')
342 ->setGetVariables(['target']);
343 $buttonBar->addButton($shortButton);
344 }
345
346 /**
347 * Returns LanguageService
348 *
349 * @return LanguageService
350 */
351 protected function getLanguageService(): LanguageService
352 {
353 return $GLOBALS['LANG'];
354 }
355
356 /**
357 * Returns the current BE user.
358 *
359 * @return BackendUserAuthentication
360 */
361 protected function getBackendUser(): BackendUserAuthentication
362 {
363 return $GLOBALS['BE_USER'];
364 }
365 }