ef5d9c6a5234dd1e23d2f97d7bf33c0cb25f353e
[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\Backend\Utility\BackendUtility;
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 $assigns['moduleUrlTceFile'] = BackendUtility::getModuleUrl('tce_file');
181 $assigns['fileName'] = $this->fileObject->getName();
182
183 $extList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['textfile_ext'];
184 try {
185 if (!$extList || !GeneralUtility::inList($extList, $this->fileObject->getExtension())) {
186 throw new \Exception('Files with that extension are not editable. Allowed extensions are: ' . $extList, 1476050135);
187 }
188
189 // Making the formfields
190 $hValue = BackendUtility::getModuleUrl('file_edit', [
191 'target' => $this->origTarget,
192 'returnUrl' => $this->returnUrl
193 ]);
194
195 $formData = [
196 'databaseRow' => [
197 'uid' => 0,
198 'data' => $this->fileObject->getContents(),
199 'target' => $this->fileObject->getUid(),
200 'redirect' => $hValue,
201 ],
202 'tableName' => 'editfile',
203 'processedTca' => [
204 'columns' => [
205 'data' => $dataColumnDefinition,
206 'target' => [
207 'config' => [
208 'type' => 'input',
209 'renderType' => 'hidden',
210 ],
211 ],
212 'redirect' => [
213 'config' => [
214 'type' => 'input',
215 'renderType' => 'hidden',
216 ],
217 ],
218 ],
219 'types' => [
220 1 => [
221 'showitem' => 'data,target,redirect',
222 ],
223 ],
224 ],
225 'recordTypeValue' => 1,
226 'inlineStructure' => [],
227 'renderType' => 'fullRecordContainer',
228 ];
229
230 $resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
231 $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
232 $formResultCompiler->mergeResult($resultArray);
233
234 $form = $formResultCompiler->addCssFiles()
235 . $resultArray['html']
236 . $formResultCompiler->printNeededJSFunctions();
237
238 $assigns['form'] = $form;
239 } catch (\Exception $e) {
240 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $e->getMessage(), '', FlashMessage::ERROR, true);
241
242 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
243 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
244 $defaultFlashMessageQueue->enqueue($flashMessage);
245
246 HttpUtility::redirect($this->returnUrl, HttpUtility::HTTP_STATUS_500);
247 }
248
249 // Rendering of the output via fluid
250 $view = GeneralUtility::makeInstance(StandaloneView::class);
251 $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates')]);
252 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials')]);
253 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
254 'EXT:backend/Resources/Private/Templates/File/EditFile.html'
255 ));
256 $view->assignMultiple($assigns);
257 $pageContent = $view->render();
258
259 // Hook: after compiling the output
260 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook'] ?? [] as $hookFunction) {
261 $hookParameters = [
262 'pageContent' => &$pageContent,
263 'target' => &$this->target
264 ];
265 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
266 }
267
268 $this->content .= $pageContent;
269 $this->moduleTemplate->setContent($this->content);
270 }
271
272 /**
273 * Processes the request, currently everything is handled and put together via "main()"
274 *
275 * @param ServerRequestInterface $request the current request
276 * @param ResponseInterface $response
277 * @return ResponseInterface the response with the content
278 */
279 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
280 {
281 $this->main();
282
283 $response->getBody()->write($this->moduleTemplate->renderContent());
284 return $response;
285 }
286
287 /**
288 * Builds the buttons for the docheader and returns them as an array
289 */
290 public function getButtons()
291 {
292 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
293
294 $lang = $this->getLanguageService();
295 // CSH button
296 $helpButton = $buttonBar->makeHelpButton()
297 ->setFieldName('file_edit')
298 ->setModuleName('xMOD_csh_corebe');
299 $buttonBar->addButton($helpButton);
300
301 // Save button
302 $saveButton = $buttonBar->makeInputButton()
303 ->setName('_save')
304 ->setValue('1')
305 ->setForm('EditFileController')
306 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:file_edit.php.submit'))
307 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL));
308
309 // Save and Close button
310 $saveAndCloseButton = $buttonBar->makeInputButton()
311 ->setName('_saveandclosedok')
312 ->setValue('1')
313 ->setForm('EditFileController')
314 ->setOnClick(
315 'document.editform.elements.namedItem("data[editfile][0][redirect]").value='
316 . GeneralUtility::quoteJSvalue($this->returnUrl)
317 . ';'
318 )
319 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:file_edit.php.saveAndClose'))
320 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
321 'actions-document-save-close',
322 Icon::SIZE_SMALL
323 ));
324
325 $splitButton = $buttonBar->makeSplitButton()
326 ->addItem($saveButton)
327 ->addItem($saveAndCloseButton);
328 $buttonBar->addButton($splitButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
329
330 // Cancel button
331 $closeButton = $buttonBar->makeLinkButton()
332 ->setHref('#')
333 ->setOnClick('backToList(); return false;')
334 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.cancel'))
335 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-close', Icon::SIZE_SMALL));
336 $buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
337
338 // Make shortcut:
339 $shortButton = $buttonBar->makeShortcutButton()
340 ->setModuleName('file_edit')
341 ->setGetVariables(['target']);
342 $buttonBar->addButton($shortButton);
343 }
344
345 /**
346 * Returns LanguageService
347 *
348 * @return \TYPO3\CMS\Core\Localization\LanguageService
349 */
350 protected function getLanguageService()
351 {
352 return $GLOBALS['LANG'];
353 }
354
355 /**
356 * Returns the current BE user.
357 *
358 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
359 */
360 protected function getBackendUser()
361 {
362 return $GLOBALS['BE_USER'];
363 }
364 }