7df8db6278d94c90bd18ef21db0a40dd7366a71b
[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\Imaging\Icon;
25 use TYPO3\CMS\Core\Messaging\FlashMessage;
26 use TYPO3\CMS\Core\Messaging\FlashMessageService;
27 use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
28 use TYPO3\CMS\Core\Resource\ResourceFactory;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Core\Utility\HttpUtility;
31 use TYPO3\CMS\Fluid\View\StandaloneView;
32
33 /**
34 * Script Class for rendering the file editing screen
35 */
36 class EditFileController
37 {
38 /**
39 * Module content accumulated.
40 *
41 * @var string
42 */
43 public $content;
44
45 /**
46 * @var string
47 */
48 public $title;
49
50 /**
51 * Document template object
52 *
53 * @var DocumentTemplate
54 */
55 public $doc;
56
57 /**
58 * Original input target
59 *
60 * @var string
61 */
62 public $origTarget;
63
64 /**
65 * The original target, but validated.
66 *
67 * @var string
68 */
69 public $target;
70
71 /**
72 * Return URL of list module.
73 *
74 * @var string
75 */
76 public $returnUrl;
77
78 /**
79 * the file that is being edited on
80 *
81 * @var \TYPO3\CMS\Core\Resource\AbstractFile
82 */
83 protected $fileObject;
84
85 /**
86 * ModuleTemplate object
87 *
88 * @var ModuleTemplate
89 */
90 protected $moduleTemplate;
91
92 /**
93 * Constructor
94 */
95 public function __construct()
96 {
97 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
98 $GLOBALS['SOBE'] = $this;
99 $this->init();
100 }
101
102 /**
103 * Initialize script class
104 *
105 * @throws InsufficientFileAccessPermissionsException
106 * @throws \InvalidArgumentException
107 * @throws \RuntimeException
108 */
109 protected function init()
110 {
111 // Setting target, which must be a file reference to a file within the mounts.
112 $this->target = ($this->origTarget = ($fileIdentifier = GeneralUtility::_GP('target')));
113 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
114 // create the file object
115 if ($fileIdentifier) {
116 $this->fileObject = ResourceFactory::getInstance()
117 ->retrieveFileOrFolderObject($fileIdentifier);
118 }
119 // Cleaning and checking target directory
120 if (!$this->fileObject) {
121 $title = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:paramError');
122 $message = $this->getLanguageService()->sL('LLL:EXT:filelist/Resources/Private/Language/locallang_mod_file_list.xlf:targetNoDir');
123 throw new \RuntimeException($title . ': ' . $message, 1294586841);
124 }
125 if ($this->fileObject->getStorage()->getUid() === 0) {
126 throw new InsufficientFileAccessPermissionsException(
127 'You are not allowed to access files outside your storages',
128 1375889832
129 );
130 }
131
132 // Setting the title and the icon
133 $icon = $this->moduleTemplate->getIconFactory()->getIcon('apps-filetree-root', Icon::SIZE_SMALL)->render();
134 $this->title = $icon
135 . htmlspecialchars(
136 $this->fileObject->getStorage()->getName()
137 ) . ': ' . htmlspecialchars(
138 $this->fileObject->getIdentifier()
139 );
140
141 // Setting template object
142 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
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 public function main()
155 {
156 $dataColumnDefinition = [
157 'label' => htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:file'))
158 . ' ' . htmlspecialchars($this->target),
159 'config' => [
160 'type' => 'text',
161 'cols' => 48,
162 'wrap' => 'OFF',
163 ],
164 'defaultExtras' => 'fixed-font: enable-tab'
165 ];
166
167 $this->getButtons();
168 // Hook: before compiling the output
169 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook'] ?? [] as $hookFunction) {
170 $hookParameters = [
171 'content' => &$this->content,
172 'target' => &$this->target,
173 'dataColumnDefinition' => &$dataColumnDefinition,
174 ];
175 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
176 }
177
178 $assigns = [];
179 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
180 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
181 $assigns['moduleUrlTceFile'] = (string)$uriBuilder->buildUriFromRoute('tce_file');
182 $assigns['fileName'] = $this->fileObject->getName();
183
184 $extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
185 try {
186 if (!$extList || !GeneralUtility::inList($extList, $this->fileObject->getExtension())) {
187 throw new \Exception('Files with that extension are not editable. Allowed extensions are: ' . $extList, 1476050135);
188 }
189
190 // Making the formfields
191 $hValue = (string)$uriBuilder->buildUriFromRoute('file_edit', [
192 'target' => $this->origTarget,
193 'returnUrl' => $this->returnUrl
194 ]);
195
196 $formData = [
197 'databaseRow' => [
198 'uid' => 0,
199 'data' => $this->fileObject->getContents(),
200 'target' => $this->fileObject->getUid(),
201 'redirect' => $hValue,
202 ],
203 'tableName' => 'editfile',
204 'processedTca' => [
205 'columns' => [
206 'data' => $dataColumnDefinition,
207 'target' => [
208 'config' => [
209 'type' => 'input',
210 'renderType' => 'hidden',
211 ],
212 ],
213 'redirect' => [
214 'config' => [
215 'type' => 'input',
216 'renderType' => 'hidden',
217 ],
218 ],
219 ],
220 'types' => [
221 1 => [
222 'showitem' => 'data,target,redirect',
223 ],
224 ],
225 ],
226 'recordTypeValue' => 1,
227 'inlineStructure' => [],
228 'renderType' => 'fullRecordContainer',
229 ];
230
231 $resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
232 $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
233 $formResultCompiler->mergeResult($resultArray);
234
235 $form = $formResultCompiler->addCssFiles()
236 . $resultArray['html']
237 . $formResultCompiler->printNeededJSFunctions();
238
239 $assigns['form'] = $form;
240 } catch (\Exception $e) {
241 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $e->getMessage(), '', FlashMessage::ERROR, true);
242
243 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
244 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
245 $defaultFlashMessageQueue->enqueue($flashMessage);
246
247 HttpUtility::redirect($this->returnUrl, HttpUtility::HTTP_STATUS_500);
248 }
249
250 // Rendering of the output via fluid
251 $view = GeneralUtility::makeInstance(StandaloneView::class);
252 $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
253 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
254 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
255 'EXT:backend/Resources/Private/Templates/File/EditFile.html'
256 ));
257 $view->assignMultiple($assigns);
258 $pageContent = $view->render();
259
260 // Hook: after compiling the output
261 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook'] ?? [] as $hookFunction) {
262 $hookParameters = [
263 'pageContent' => &$pageContent,
264 'target' => &$this->target
265 ];
266 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
267 }
268
269 $this->content .= $pageContent;
270 $this->moduleTemplate->setContent($this->content);
271 }
272
273 /**
274 * Processes the request, currently everything is handled and put together via "main()"
275 *
276 * @param ServerRequestInterface $request the current request
277 * @param ResponseInterface $response
278 * @return ResponseInterface the response with the content
279 */
280 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
281 {
282 $this->main();
283
284 $response->getBody()->write($this->moduleTemplate->renderContent());
285 return $response;
286 }
287
288 /**
289 * Builds the buttons for the docheader and returns them as an array
290 */
291 public function getButtons()
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:lang/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:lang/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:lang/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 \TYPO3\CMS\Core\Localization\LanguageService
350 */
351 protected function getLanguageService()
352 {
353 return $GLOBALS['LANG'];
354 }
355
356 /**
357 * Returns the current BE user.
358 *
359 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
360 */
361 protected function getBackendUser()
362 {
363 return $GLOBALS['BE_USER'];
364 }
365 }