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