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