378a1fa0cd175b85fac4ce779009ad18dec12ead
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / EditDocumentController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
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\ServerRequestInterface;
18 use TYPO3\CMS\Backend\Form\DataPreprocessor;
19 use TYPO3\CMS\Backend\Form\FormEngine;
20 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Backend\Utility\IconUtility;
23 use TYPO3\CMS\Core\DataHandling\DataHandler;
24 use TYPO3\CMS\Core\Http\Response;
25 use TYPO3\CMS\Core\Imaging\Icon;
26 use TYPO3\CMS\Core\Imaging\IconFactory;
27 use TYPO3\CMS\Core\Messaging\FlashMessage;
28 use TYPO3\CMS\Core\Messaging\FlashMessageService;
29 use TYPO3\CMS\Core\Page\PageRenderer;
30 use TYPO3\CMS\Core\Type\Bitmask\Permission;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\CMS\Core\Utility\HttpUtility;
33 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
34 use TYPO3\CMS\Core\Utility\MathUtility;
35 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
36 use TYPO3\CMS\Frontend\Page\PageRepository;
37
38 /**
39 * Script Class: Drawing the editing form for editing records in TYPO3.
40 * Notice: It does NOT use tce_db.php to submit data to, rather it handles submissions itself
41 */
42 class EditDocumentController implements \TYPO3\CMS\Core\Http\ControllerInterface {
43
44 /**
45 * GPvar "edit": Is an array looking approx like [tablename][list-of-ids]=command, eg.
46 * "&edit[pages][123]=edit". See \TYPO3\CMS\Backend\Utility\BackendUtility::editOnClick(). Value can be seen modified
47 * internally (converting NEW keyword to id, workspace/versioning etc).
48 *
49 * @var array
50 */
51 public $editconf;
52
53 /**
54 * Commalist of fieldnames to edit. The point is IF you specify this list, only those
55 * fields will be rendered in the form. Otherwise all (available) fields in the record
56 * is shown according to the types configuration in $GLOBALS['TCA']
57 *
58 * @var bool
59 */
60 public $columnsOnly;
61
62 /**
63 * Default values for fields (array with tablenames, fields etc. as keys).
64 * Can be seen modified internally.
65 *
66 * @var array
67 */
68 public $defVals;
69
70 /**
71 * Array of values to force being set (as hidden fields). Will be set as $this->defVals
72 * IF defVals does not exist.
73 *
74 * @var array
75 */
76 public $overrideVals;
77
78 /**
79 * If set, this value will be set in $this->retUrl (which is used quite many places
80 * as the return URL). If not set, "dummy.php" will be set in $this->retUrl
81 *
82 * @var string
83 */
84 public $returnUrl;
85
86 /**
87 * Close-document command. Not really sure of all options...
88 *
89 * @var int
90 */
91 public $closeDoc;
92
93 /**
94 * Quite simply, if this variable is set, then the processing of incoming data will be performed
95 * as if a save-button is pressed. Used in the forms as a hidden field which can be set through
96 * JavaScript if the form is somehow submitted by JavaScript).
97 *
98 * @var bool
99 */
100 public $doSave;
101
102 /**
103 * The data array from which the data comes...
104 *
105 * @var array
106 */
107 public $data;
108
109 /**
110 * @var string
111 */
112 public $cmd;
113
114 /**
115 * @var array
116 */
117 public $mirror;
118
119 /**
120 * Clear-cache cmd.
121 *
122 * @var string
123 */
124 public $cacheCmd;
125
126 /**
127 * Redirect (not used???)
128 *
129 * @var string
130 */
131 public $redirect;
132
133 /**
134 * Boolean: If set, then the GET var "&id=" will be added to the
135 * retUrl string so that the NEW id of something is returned to the script calling the form.
136 *
137 * @var bool
138 */
139 public $returnNewPageId;
140
141 /**
142 * @var string
143 */
144 public $vC;
145
146 /**
147 * update BE_USER->uc
148 *
149 * @var array
150 */
151 public $uc;
152
153 /**
154 * ID for displaying the page in the frontend (used for SAVE/VIEW operations)
155 *
156 * @var int
157 */
158 public $popViewId;
159
160 /**
161 * Additional GET vars for the link, eg. "&L=xxx"
162 *
163 * @var string
164 */
165 public $popViewId_addParams;
166
167 /**
168 * Alternative URL for viewing the frontend pages.
169 *
170 * @var string
171 */
172 public $viewUrl;
173
174 /**
175 * If this is pointing to a page id it will automatically load all content elements
176 * (NORMAL column/default language) from that page into the form!
177 *
178 * @var int
179 * @deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8
180 */
181 public $editRegularContentFromId;
182
183 /**
184 * Alternative title for the document handler.
185 *
186 * @var string
187 */
188 public $recTitle;
189
190 /**
191 * If set, then no SAVE/VIEW button is printed
192 *
193 * @var bool
194 */
195 public $noView;
196
197 /**
198 * @var string
199 */
200 public $perms_clause;
201
202 /**
203 * If set, the $this->editconf array is returned to the calling script
204 * (used by wizard_add.php for instance)
205 *
206 * @var bool
207 */
208 public $returnEditConf;
209
210 /**
211 * localization mode for TCEforms (eg. "text")
212 *
213 * @var string
214 */
215 public $localizationMode;
216
217 /**
218 * Workspace used for the editing action.
219 *
220 * @var NULL|integer
221 */
222 protected $workspace;
223
224 /**
225 * document template object
226 *
227 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
228 */
229 public $doc;
230
231 /**
232 * a static HTML template, usually in templates/alt_doc.html
233 *
234 * @var string
235 */
236 public $template;
237
238 /**
239 * Content accumulation
240 *
241 * @var string
242 */
243 public $content;
244
245 /**
246 * Return URL script, processed. This contains the script (if any) that we should
247 * RETURN TO from the FormEngine script IF we press the close button. Thus this
248 * variable is normally passed along from the calling script so we can properly return if needed.
249 *
250 * @var string
251 */
252 public $retUrl;
253
254 /**
255 * Contains the parts of the REQUEST_URI (current url). By parts we mean the result of resolving
256 * REQUEST_URI (current url) by the parse_url() function. The result is an array where eg. "path"
257 * is the script path and "query" is the parameters...
258 *
259 * @var array
260 */
261 public $R_URL_parts;
262
263 /**
264 * Contains the current GET vars array; More specifically this array is the foundation for creating
265 * the R_URI internal var (which becomes the "url of this script" to which we submit the forms etc.)
266 *
267 * @var array
268 */
269 public $R_URL_getvars;
270
271 /**
272 * Set to the URL of this script including variables which is needed to re-display the form. See main()
273 *
274 * @var string
275 */
276 public $R_URI;
277
278 /**
279 * @var array
280 */
281 public $MCONF;
282
283 /**
284 * @var array
285 */
286 public $pageinfo;
287
288 /**
289 * Is loaded with the "title" of the currently "open document" - this is used in the
290 * Document Selector box. (see makeDocSel())
291 *
292 * @var string
293 */
294 public $storeTitle;
295
296 /**
297 * Contains an array with key/value pairs of GET parameters needed to reach the
298 * current document displayed - used in the Document Selector box. (see compileStoreDat())
299 *
300 * @var array
301 */
302 public $storeArray;
303
304 /**
305 * Contains storeArray, but imploded into a GET parameter string (see compileStoreDat())
306 *
307 * @var string
308 */
309 public $storeUrl;
310
311 /**
312 * Hashed value of storeURL (see compileStoreDat())
313 *
314 * @var string
315 */
316 public $storeUrlMd5;
317
318 /**
319 * Module session data
320 *
321 * @var array
322 */
323 public $docDat;
324
325 /**
326 * An array of the "open documents" - keys are md5 hashes (see $storeUrlMd5) identifying
327 * the various documents on the GET parameter list needed to open it. The values are
328 * arrays with 0,1,2 keys with information about the document (see compileStoreDat()).
329 * The docHandler variable is stored in the $docDat session data, key "0".
330 *
331 * @var array
332 */
333 public $docHandler;
334
335 /**
336 * Array of the elements to create edit forms for.
337 *
338 * @var array
339 */
340 public $elementsData;
341
342 /**
343 * Pointer to the first element in $elementsData
344 *
345 * @var array
346 */
347 public $firstEl;
348
349 /**
350 * Counter, used to count the number of errors (when users do not have edit permissions)
351 *
352 * @var int
353 */
354 public $errorC;
355
356 /**
357 * Counter, used to count the number of new record forms displayed
358 *
359 * @var int
360 */
361 public $newC;
362
363 /**
364 * Is set to the pid value of the last shown record - thus indicating which page to
365 * show when clicking the SAVE/VIEW button
366 *
367 * @var int
368 */
369 public $viewId;
370
371 /**
372 * Is set to additional parameters (like "&L=xxx") if the record supports it.
373 *
374 * @var string
375 */
376 public $viewId_addParams;
377
378 /**
379 * Module TSconfig, loaded from main() based on the page id value of viewId
380 *
381 * @var array
382 */
383 public $modTSconfig;
384
385 /**
386 * instance of TCEforms class
387 *
388 * @var \TYPO3\CMS\Backend\Form\FormEngine
389 */
390 public $tceforms;
391
392 /**
393 * Contains the root-line path of the currently edited record(s) - for display.
394 *
395 * @var string
396 */
397 public $generalPathOfForm;
398
399 /**
400 * Used internally to disable the storage of the document reference (eg. new records)
401 *
402 * @var bool
403 */
404 public $dontStoreDocumentRef;
405
406 /**
407 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
408 */
409 protected $signalSlotDispatcher;
410
411 /**
412 * Stores information needed to preview the currently saved record
413 *
414 * @var array
415 */
416 protected $previewData = [];
417
418 /**
419 * @var IconFactory
420 */
421 protected $iconFactory;
422
423 /**
424 * Constructor
425 */
426 public function __construct() {
427 $GLOBALS['SOBE'] = $this;
428 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_alt_doc.xlf');
429 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
430 }
431
432 /**
433 * Get the SignalSlot dispatcher
434 *
435 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
436 */
437 protected function getSignalSlotDispatcher() {
438 if (!isset($this->signalSlotDispatcher)) {
439 $this->signalSlotDispatcher = GeneralUtility::makeInstance(Dispatcher::class);
440 }
441 return $this->signalSlotDispatcher;
442 }
443
444 /**
445 * Emits a signal after a function was executed
446 *
447 * @param string $signalName
448 */
449 protected function emitFunctionAfterSignal($signalName) {
450 $this->getSignalSlotDispatcher()->dispatch(__CLASS__, $signalName . 'After', array($this));
451 }
452
453 /**
454 * First initialization.
455 *
456 * @return void
457 */
458 public function preInit() {
459 if (GeneralUtility::_GP('justLocalized')) {
460 $this->localizationRedirect(GeneralUtility::_GP('justLocalized'));
461 }
462 // Setting GPvars:
463 $this->editconf = GeneralUtility::_GP('edit');
464 $this->defVals = GeneralUtility::_GP('defVals');
465 $this->overrideVals = GeneralUtility::_GP('overrideVals');
466 $this->columnsOnly = GeneralUtility::_GP('columnsOnly');
467 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
468 $this->closeDoc = GeneralUtility::_GP('closeDoc');
469 $this->doSave = GeneralUtility::_GP('doSave');
470 $this->returnEditConf = GeneralUtility::_GP('returnEditConf');
471 $this->localizationMode = GeneralUtility::_GP('localizationMode');
472 $this->workspace = GeneralUtility::_GP('workspace');
473 $this->uc = GeneralUtility::_GP('uc');
474 // Setting override values as default if defVals does not exist.
475 if (!is_array($this->defVals) && is_array($this->overrideVals)) {
476 $this->defVals = $this->overrideVals;
477 }
478 // Setting return URL
479 $this->retUrl = $this->returnUrl ?: BackendUtility::getModuleUrl('dummy');
480 // Fix $this->editconf if versioning applies to any of the records
481 $this->fixWSversioningInEditConf();
482 // Make R_URL (request url) based on input GETvars:
483 $this->R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
484 $this->R_URL_getvars = GeneralUtility::_GET();
485 $this->R_URL_getvars['edit'] = $this->editconf;
486 // MAKE url for storing
487 $this->compileStoreDat();
488 // Initialize more variables.
489 $this->dontStoreDocumentRef = 0;
490 $this->storeTitle = '';
491 // Get session data for the module:
492 $this->docDat = $this->getBackendUser()->getModuleData('FormEngine', 'ses');
493 $this->docHandler = $this->docDat[0];
494 // If a request for closing the document has been sent, act accordingly:
495 if ($this->closeDoc > 0) {
496 $this->closeDocument($this->closeDoc);
497 }
498 // If NO vars are sent to the script, try to read first document:
499 // Added !is_array($this->editconf) because editConf must not be set either.
500 // Anyways I can't figure out when this situation here will apply...
501 if (is_array($this->R_URL_getvars) && count($this->R_URL_getvars) < 2 && !is_array($this->editconf)) {
502 $this->setDocument($this->docDat[1]);
503 }
504
505 // Sets a temporary workspace, this request is based on
506 if ($this->workspace !== NULL) {
507 $this->getBackendUser()->setTemporaryWorkspace($this->workspace);
508 }
509
510 $this->emitFunctionAfterSignal(__FUNCTION__);
511 }
512
513 /**
514 * Detects, if a save command has been triggered.
515 *
516 * @return bool TRUE, then save the document (data submitted)
517 */
518 public function doProcessData() {
519 $out = $this->doSave || isset($_POST['_savedok']) || isset($_POST['_saveandclosedok']) || isset($_POST['_savedokview']) || isset($_POST['_savedoknew']) || isset($_POST['_translation_savedok_x']) || isset($_POST['_translation_savedokclear_x']);
520 return $out;
521 }
522
523 /**
524 * Do processing of data, submitting it to TCEmain.
525 *
526 * @return void
527 */
528 public function processData() {
529 $beUser = $this->getBackendUser();
530 // GPvars specifically for processing:
531 $control = GeneralUtility::_GP('control');
532 $this->data = GeneralUtility::_GP('data');
533 $this->cmd = GeneralUtility::_GP('cmd');
534 $this->mirror = GeneralUtility::_GP('mirror');
535 $this->cacheCmd = GeneralUtility::_GP('cacheCmd');
536 $this->redirect = GeneralUtility::_GP('redirect');
537 $this->returnNewPageId = GeneralUtility::_GP('returnNewPageId');
538 $this->vC = GeneralUtility::_GP('vC');
539 // See tce_db.php for relevate options here:
540 // Only options related to $this->data submission are included here.
541 /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */
542 $tce = GeneralUtility::makeInstance(DataHandler::class);
543 $tce->stripslashes_values = FALSE;
544
545 if (!empty($control)) {
546 $tce->setControl($control);
547 }
548 if (isset($_POST['_translation_savedok_x'])) {
549 $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
550 }
551 if (isset($_POST['_translation_savedokclear_x'])) {
552 $tce->updateModeL10NdiffData = 'FORCE_FFUPD';
553 $tce->updateModeL10NdiffDataClear = TRUE;
554 }
555 // Setting default values specific for the user:
556 $TCAdefaultOverride = $beUser->getTSConfigProp('TCAdefaults');
557 if (is_array($TCAdefaultOverride)) {
558 $tce->setDefaultsFromUserTS($TCAdefaultOverride);
559 }
560 // Setting internal vars:
561 if ($beUser->uc['neverHideAtCopy']) {
562 $tce->neverHideAtCopy = 1;
563 }
564 // Loading TCEmain with data:
565 $tce->start($this->data, $this->cmd);
566 if (is_array($this->mirror)) {
567 $tce->setMirror($this->mirror);
568 }
569 // Checking referer / executing
570 $refInfo = parse_url(GeneralUtility::getIndpEnv('HTTP_REFERER'));
571 $httpHost = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY');
572 if ($httpHost != $refInfo['host'] && $this->vC != $beUser->veriCode() && !$GLOBALS['TYPO3_CONF_VARS']['SYS']['doNotCheckReferer']) {
573 $tce->log('', 0, 0, 0, 1, 'Referer host \'%s\' and server host \'%s\' did not match and veriCode was not valid either!', 1, array($refInfo['host'], $httpHost));
574 debug('Error: Referer host did not match with server host.');
575 } else {
576 // Perform the saving operation with TCEmain:
577 $tce->process_uploads($_FILES);
578 $tce->process_datamap();
579 $tce->process_cmdmap();
580 // If pages are being edited, we set an instruction about updating the page tree after this operation.
581 if ($tce->pagetreeNeedsRefresh && (isset($this->data['pages']) || $beUser->workspace != 0 && !empty($this->data))) {
582 BackendUtility::setUpdateSignal('updatePageTree');
583 }
584 // If there was saved any new items, load them:
585 if (!empty($tce->substNEWwithIDs_table)) {
586 // save the expanded/collapsed states for new inline records, if any
587 FormEngineUtility::updateInlineView($this->uc, $tce);
588 $newEditConf = array();
589 foreach ($this->editconf as $tableName => $tableCmds) {
590 $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
591 if (!empty($keys)) {
592 foreach ($keys as $key) {
593 $editId = $tce->substNEWwithIDs[$key];
594 // Check if the $editId isn't a child record of an IRRE action
595 if (!(is_array($tce->newRelatedIDs[$tableName]) && in_array($editId, $tce->newRelatedIDs[$tableName]))) {
596 // Translate new id to the workspace version:
597 if ($versionRec = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $tableName, $editId, 'uid')) {
598 $editId = $versionRec['uid'];
599 }
600 $newEditConf[$tableName][$editId] = 'edit';
601 }
602 // Traverse all new records and forge the content of ->editconf so we can continue to EDIT these records!
603 if ($tableName == 'pages' && $this->retUrl != BackendUtility::getModuleUrl('dummy') && $this->returnNewPageId) {
604 $this->retUrl .= '&id=' . $tce->substNEWwithIDs[$key];
605 }
606 }
607 } else {
608 $newEditConf[$tableName] = $tableCmds;
609 }
610 }
611 // Resetting editconf if newEditConf has values:
612 if (!empty($newEditConf)) {
613 $this->editconf = $newEditConf;
614 }
615 // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
616 $this->R_URL_getvars['edit'] = $this->editconf;
617 // Unsetting default values since we don't need them anymore.
618 unset($this->R_URL_getvars['defVals']);
619 // Re-compile the store* values since editconf changed...
620 $this->compileStoreDat();
621 }
622 // See if any records was auto-created as new versions?
623 if (!empty($tce->autoVersionIdMap)) {
624 $this->fixWSversioningInEditConf($tce->autoVersionIdMap);
625 }
626 // If a document is saved and a new one is created right after.
627 if (isset($_POST['_savedoknew']) && is_array($this->editconf)) {
628 // Finding the current table:
629 reset($this->editconf);
630 $nTable = key($this->editconf);
631 // Finding the first id, getting the records pid+uid
632 reset($this->editconf[$nTable]);
633 $nUid = key($this->editconf[$nTable]);
634 $nRec = BackendUtility::getRecord($nTable, $nUid, 'pid,uid');
635 // Setting a blank editconf array for a new record:
636 $this->editconf = array();
637 if ($this->getNewIconMode($nTable) == 'top') {
638 $this->editconf[$nTable][$nRec['pid']] = 'new';
639 } else {
640 $this->editconf[$nTable][-$nRec['uid']] = 'new';
641 }
642 // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
643 $this->R_URL_getvars['edit'] = $this->editconf;
644 // Re-compile the store* values since editconf changed...
645 $this->compileStoreDat();
646 }
647 // If a preview is requested
648 if (isset($_POST['_savedokview'])) {
649 // Get the first table and id of the data array from DataHandler
650 $table = reset(array_keys($this->data));
651 $id = reset(array_keys($this->data[$table]));
652 if (!MathUtility::canBeInterpretedAsInteger($id)) {
653 $id = $tce->substNEWwithIDs[$id];
654 }
655 // Store this information for later use
656 $this->previewData['table'] = $table;
657 $this->previewData['id'] = $id;
658 }
659 $tce->printLogErrorMessages(isset($_POST['_saveandclosedok']) || isset($_POST['_translation_savedok_x']) ? $this->retUrl : $this->R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $this->R_URL_getvars));
660 }
661 // || count($tce->substNEWwithIDs)... If any new items has been save, the document is CLOSED
662 // because if not, we just get that element re-listed as new. And we don't want that!
663 if (isset($_POST['_saveandclosedok']) || isset($_POST['_translation_savedok_x']) || $this->closeDoc < 0) {
664 $this->closeDocument(abs($this->closeDoc));
665 }
666 }
667
668 /**
669 * Initialize the normal module operation
670 *
671 * @return void
672 */
673 public function init() {
674 $beUser = $this->getBackendUser();
675 // Setting more GPvars:
676 $this->popViewId = GeneralUtility::_GP('popViewId');
677 $this->popViewId_addParams = GeneralUtility::_GP('popViewId_addParams');
678 $this->viewUrl = GeneralUtility::_GP('viewUrl');
679 $this->editRegularContentFromId = GeneralUtility::_GP('editRegularContentFromId');
680 $this->recTitle = GeneralUtility::_GP('recTitle');
681 $this->noView = GeneralUtility::_GP('noView');
682 $this->perms_clause = $beUser->getPagePermsClause(1);
683 // Set other internal variables:
684 $this->R_URL_getvars['returnUrl'] = $this->retUrl;
685 $this->R_URI = $this->R_URL_parts['path'] . '?' . ltrim(GeneralUtility::implodeArrayForUrl('', $this->R_URL_getvars), '&');
686 // Setting virtual document name
687 $this->MCONF['name'] = 'xMOD_alt_doc.php';
688
689 // Create an instance of the document template object
690 $this->doc = $GLOBALS['TBE_TEMPLATE'];
691 $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
692 $pageRenderer->addInlineLanguageLabelFile('EXT:lang/locallang_alt_doc.xlf');
693 $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/alt_doc.html');
694 $this->doc->form = '<form action="' . htmlspecialchars($this->R_URI) . '" method="post" enctype="multipart/form-data" name="editform" onsubmit="document.editform._scrollPosition.value=(document.documentElement.scrollTop || document.body.scrollTop); TBE_EDITOR.checkAndDoSubmit(1); return false;">';
695 // override the default jumpToUrl
696 $this->doc->JScodeArray['jumpToUrl'] = '
697 function jumpToUrl(URL,formEl) {
698 if (!TBE_EDITOR.isFormChanged()) {
699 window.location.href = URL;
700 } else if (formEl && formEl.type=="checkbox") {
701 formEl.checked = formEl.checked ? 0 : 1;
702 }
703 }
704 ';
705 // define the window size of the element browser
706 $popupWindowWidth = 700;
707 $popupWindowHeight = 750;
708 $popupWindowSize = trim($beUser->getTSConfigVal('options.popupWindowSize'));
709 if (!empty($popupWindowSize)) {
710 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize);
711 }
712 $t3Configuration = array(
713 'PopupWindow' => array(
714 'width' => $popupWindowWidth,
715 'height' => $popupWindowHeight
716 ),
717 );
718 $javascript = '
719 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
720 // Object: TS:
721 // passwordDummy and decimalSign are used by tbe_editor.js and have to be declared here as
722 // TS object overwrites the object declared in tbe_editor.js
723 function typoSetup() { //
724 this.uniqueID = "";
725 this.passwordDummy = "********";
726 this.PATH_typo3 = "";
727 this.decimalSign = ".";
728 }
729 var TS = new typoSetup();
730
731 // Info view:
732 function launchView(table,uid,bP) { //
733 var backPath= bP ? bP : "";
734 var thePreviewWindow = window.open(
735 backPath+' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('show_item') . '&table=') . ' + encodeURIComponent(table) + "&uid=" + encodeURIComponent(uid),
736 "ShowItem" + TS.uniqueID,
737 "height=300,width=410,status=0,menubar=0,resizable=0,location=0,directories=0,scrollbars=1,toolbar=0"
738 );
739 if (thePreviewWindow && thePreviewWindow.focus) {
740 thePreviewWindow.focus();
741 }
742 }
743 function deleteRecord(table,id,url) { //
744 window.location.href = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&cmd[') . '+table+"]["+id+"][delete]=1' . BackendUtility::getUrlToken('tceAction') . '&redirect="+escape(url)+"&vC=' . $beUser->veriCode() . '&prErr=1&uPT=1";
745 }
746 ';
747
748 $previewCode = isset($_POST['_savedokview']) && $this->popViewId ? $this->generatePreviewCode() : '';
749
750 $this->doc->JScode = $this->doc->wrapScriptTags($javascript . $previewCode);
751 // Setting up the context sensitive menu:
752 $this->doc->getContextMenuCode();
753 $this->doc->bodyTagAdditions = 'onload="window.scrollTo(0,' . MathUtility::forceIntegerInRange(GeneralUtility::_GP('_scrollPosition'), 0, 10000) . ');"';
754
755 $this->emitFunctionAfterSignal(__FUNCTION__);
756 }
757
758 /**
759 * @return string
760 */
761 protected function generatePreviewCode() {
762 $currentPageId = MathUtility::convertToPositiveInteger($this->popViewId);
763 $table = $this->previewData['table'];
764 $recordId = $this->previewData['id'];
765
766 $pageTsConfig = BackendUtility::getPagesTSconfig($currentPageId);
767 $previewConfiguration = isset($pageTsConfig['TCEMAIN.']['preview.'][$table . '.'])
768 ? $pageTsConfig['TCEMAIN.']['preview.'][$table . '.']
769 : array();
770
771 $recordArray = BackendUtility::getRecord($table, $recordId);
772
773 // find the right preview page id
774 $previewPageId = 0;
775 if (isset($previewConfiguration['previewPageId'])) {
776 $previewPageId = $previewConfiguration['previewPageId'];
777 }
778 // if no preview page was configured
779 if (!$previewPageId) {
780 $rootPageData = NULL;
781 $rootLine = BackendUtility::BEgetRootLine($currentPageId);
782 $currentPage = reset($rootLine);
783 if ((int)$currentPage['doktype'] === PageRepository::DOKTYPE_DEFAULT) {
784 // try the current page
785 $previewPageId = $currentPageId;
786 } else {
787 // or search for the root page
788 foreach ($rootLine as $page) {
789 if ($page['is_siteroot']) {
790 $rootPageData = $page;
791 break;
792 }
793 }
794 $previewPageId = isset($rootPageData)
795 ? (int)$rootPageData['uid']
796 : $currentPageId;
797 }
798 }
799
800 $linkParameters = [
801 'no_cache' => 1,
802 ];
803
804 // language handling
805 $languageField = isset($GLOBALS['TCA'][$table]['ctrl']['languageField'])
806 ? $GLOBALS['TCA'][$table]['ctrl']['languageField']
807 : '';
808 if ($languageField && !empty($recordArray[$languageField])) {
809 $l18nPointer = isset($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])
810 ? $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
811 : '';
812 if (
813 $l18nPointer && !empty($recordArray[$l18nPointer])
814 && isset($previewConfiguration['useDefaultLanguageRecord'])
815 && !$previewConfiguration['useDefaultLanguageRecord']
816 ) {
817 // use parent record
818 $recordId = $recordArray[$l18nPointer];
819 }
820 $linkParameters['L'] = $recordArray[$languageField];
821 }
822
823 // map record data to GET parameters
824 if (isset($previewConfiguration['fieldToParameterMap.'])) {
825 foreach ($previewConfiguration['fieldToParameterMap.'] as $field => $parameterName) {
826 $value = $recordArray[$field];
827 if ($field === 'uid') {
828 $value = $recordId;
829 }
830 $linkParameters[$parameterName] = $value;
831 }
832 }
833
834 // add/override parameters by configuration
835 if (isset($previewConfiguration['additionalGetParameters.'])) {
836 $linkParameters = array_replace($linkParameters, $previewConfiguration['additionalGetParameters.']);
837 }
838
839 $this->popViewId = $previewPageId;
840 $this->popViewId_addParams = GeneralUtility::implodeArrayForUrl('', $linkParameters, '', FALSE, TRUE);
841
842 $previewPageRootline = BackendUtility::BEgetRootLine($this->popViewId);
843 return '
844 if (window.opener) {
845 '
846 . BackendUtility::viewOnClick($this->popViewId, '', $previewPageRootline, '', $this->viewUrl, $this->popViewId_addParams, FALSE)
847 . '
848 } else {
849 '
850 . BackendUtility::viewOnClick($this->popViewId, '', $previewPageRootline, '', $this->viewUrl, $this->popViewId_addParams)
851 . '
852 }';
853 }
854
855 /**
856 * Main module operation
857 *
858 * @return void
859 */
860 public function main() {
861 $body = '';
862 // Begin edit:
863 if (is_array($this->editconf)) {
864 // Initialize TCEforms (rendering the forms)
865 $this->tceforms = GeneralUtility::makeInstance(FormEngine::class);
866 $this->tceforms->doSaveFieldName = 'doSave';
867 $this->tceforms->localizationMode = GeneralUtility::inList('text,media', $this->localizationMode) ? $this->localizationMode : '';
868 // text,media is keywords defined in TYPO3 Core API..., see "l10n_cat"
869 $this->tceforms->returnUrl = $this->R_URI;
870 if ($this->editRegularContentFromId) {
871 $this->editRegularContentFromId();
872 }
873 // Creating the editing form, wrap it with buttons, document selector etc.
874 $editForm = $this->makeEditForm();
875 if ($editForm) {
876 $this->firstEl = reset($this->elementsData);
877 // Checking if the currently open document is stored in the list of "open documents" - if not, then add it:
878 if (($this->docDat[1] !== $this->storeUrlMd5 || !isset($this->docHandler[$this->storeUrlMd5])) && !$this->dontStoreDocumentRef) {
879 $this->docHandler[$this->storeUrlMd5] = array($this->storeTitle, $this->storeArray, $this->storeUrl, $this->firstEl);
880 $this->getBackendUser()->pushModuleData('FormEngine', array($this->docHandler, $this->storeUrlMd5));
881 BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
882 }
883 // Module configuration
884 $this->modTSconfig = $this->viewId ? BackendUtility::getModTSconfig($this->viewId, 'mod.xMOD_alt_doc') : array();
885 $body = $this->tceforms->printNeededJSFunctions_top();
886 $body .= $this->compileForm($editForm);
887 $body .= $this->tceforms->printNeededJSFunctions();
888 }
889 }
890 // Access check...
891 // The page will show only if there is a valid page and if this page may be viewed by the user
892 $this->pageinfo = BackendUtility::readPageAccess($this->viewId, $this->perms_clause);
893 // Setting up the buttons and markers for docheader
894 $docHeaderButtons = $this->getButtons();
895 $markers = array(
896 'LANGSELECTOR' => $this->langSelector(),
897 'EXTRAHEADER' => $this->extraFormHeaders(),
898 'CSH' => $docHeaderButtons['csh'],
899 'CONTENT' => $body
900 );
901 // Build the <body> for the module
902 $this->content = $this->doc->startPage('TYPO3 Edit Document');
903 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
904 $this->content .= $this->doc->endPage();
905 $this->content = $this->doc->insertStylesAndJS($this->content);
906 }
907
908 /**
909 * Outputting the accumulated content to screen
910 *
911 * @return void
912 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
913 */
914 public function printContent() {
915 GeneralUtility::logDeprecatedFunction();
916 echo $this->content;
917 }
918
919 /***************************
920 *
921 * Sub-content functions, rendering specific parts of the module content.
922 *
923 ***************************/
924 /**
925 * Creates the editing form with TCEforms, based on the input from GPvars.
926 *
927 * @return string HTML form elements wrapped in tables
928 */
929 public function makeEditForm() {
930 // Initialize variables:
931 $this->elementsData = array();
932 $this->errorC = 0;
933 $this->newC = 0;
934 $thePrevUid = '';
935 $editForm = '';
936 $trData = NULL;
937 $beUser = $this->getBackendUser();
938 // Traverse the GPvar edit array
939 // Tables:
940 foreach ($this->editconf as $table => $conf) {
941 if (is_array($conf) && $GLOBALS['TCA'][$table] && $beUser->check('tables_modify', $table)) {
942 // Traverse the keys/comments of each table (keys can be a commalist of uids)
943 foreach ($conf as $cKey => $cmd) {
944 if ($cmd == 'edit' || $cmd == 'new') {
945 // Get the ids:
946 $ids = GeneralUtility::trimExplode(',', $cKey, TRUE);
947 // Traverse the ids:
948 foreach ($ids as $theUid) {
949 // Checking if the user has permissions? (Only working as a precaution,
950 // because the final permission check is always down in TCE. But it's
951 // good to notify the user on beforehand...)
952 // First, resetting flags.
953 $hasAccess = 1;
954 $deniedAccessReason = '';
955 $deleteAccess = 0;
956 $this->viewId = 0;
957 // If the command is to create a NEW record...:
958 if ($cmd == 'new') {
959 // NOTICE: the id values in this case points to the page uid onto which the
960 // record should be create OR (if the id is negativ) to a record from the
961 // same table AFTER which to create the record.
962 if ((int)$theUid) {
963 // Find parent page on which the new record reside
964 // Less than zero - find parent page
965 if ($theUid < 0) {
966 $calcPRec = BackendUtility::getRecord($table, abs($theUid));
967 $calcPRec = BackendUtility::getRecord('pages', $calcPRec['pid']);
968 } else {
969 // always a page
970 $calcPRec = BackendUtility::getRecord('pages', abs($theUid));
971 }
972 // Now, calculate whether the user has access to creating new records on this position:
973 if (is_array($calcPRec)) {
974 // Permissions for the parent page
975 $CALC_PERMS = $beUser->calcPerms($calcPRec);
976 if ($table == 'pages') {
977 // If pages:
978 $hasAccess = $CALC_PERMS & Permission::PAGE_NEW ? 1 : 0;
979 $this->viewId = 0;
980 } else {
981 $hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
982 $this->viewId = $calcPRec['uid'];
983 }
984 }
985 }
986 // Don't save this document title in the document selector if the document is new.
987 $this->dontStoreDocumentRef = 1;
988 } else {
989 // Edit:
990 $calcPRec = BackendUtility::getRecord($table, $theUid);
991 BackendUtility::fixVersioningPid($table, $calcPRec);
992 if (is_array($calcPRec)) {
993 if ($table == 'pages') { // If pages:
994 $CALC_PERMS = $beUser->calcPerms($calcPRec);
995 $hasAccess = $CALC_PERMS & Permission::PAGE_EDIT ? 1 : 0;
996 $deleteAccess = $CALC_PERMS & Permission::PAGE_DELETE ? 1 : 0;
997 $this->viewId = $calcPRec['uid'];
998 } else {
999 // Fetching pid-record first
1000 $CALC_PERMS = $beUser->calcPerms(BackendUtility::getRecord('pages', $calcPRec['pid']));
1001 $hasAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
1002 $deleteAccess = $CALC_PERMS & Permission::CONTENT_EDIT ? 1 : 0;
1003 $this->viewId = $calcPRec['pid'];
1004 // Adding "&L=xx" if the record being edited has a languageField with a value larger than zero!
1005 if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
1006 $this->viewId_addParams = '&L=' . $calcPRec[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
1007 }
1008 }
1009 // Check internals regarding access:
1010 $isRootLevelRestrictionIgnored = BackendUtility::isRootLevelRestrictionIgnored($table);
1011 if ($hasAccess || (int)$calcPRec['pid'] === 0 && $isRootLevelRestrictionIgnored) {
1012 $hasAccess = $beUser->recordEditAccessInternals($table, $calcPRec);
1013 $deniedAccessReason = $beUser->errorMsg;
1014 }
1015 } else {
1016 $hasAccess = 0;
1017 }
1018 }
1019 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'])) {
1020 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck'] as $_funcRef) {
1021 $_params = array(
1022 'table' => $table,
1023 'uid' => $theUid,
1024 'cmd' => $cmd,
1025 'hasAccess' => $hasAccess
1026 );
1027 $hasAccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1028 }
1029 }
1030 // AT THIS POINT we have checked the access status of the editing/creation of
1031 // records and we can now proceed with creating the form elements:
1032 if ($hasAccess) {
1033 /** @var DataPreprocessor $trData */
1034 $prevPageID = is_object($trData) ? $trData->prevPageID : '';
1035 $trData = GeneralUtility::makeInstance(DataPreprocessor::class);
1036 $trData->addRawData = TRUE;
1037 $trData->defVals = $this->defVals;
1038 $trData->lockRecords = 1;
1039 $trData->prevPageID = $prevPageID;
1040 // 'new'
1041 $trData->fetchRecord($table, $theUid, $cmd == 'new' ? 'new' : '');
1042 $rec = reset($trData->regTableItems_data);
1043 $rec['uid'] = $cmd == 'new' ? uniqid('NEW', TRUE) : $theUid;
1044 if ($cmd == 'new') {
1045 $rec['pid'] = $theUid == 'prev' ? $thePrevUid : $theUid;
1046 }
1047 $this->elementsData[] = array(
1048 'table' => $table,
1049 'uid' => $rec['uid'],
1050 'pid' => $rec['pid'],
1051 'cmd' => $cmd,
1052 'deleteAccess' => $deleteAccess
1053 );
1054 // Now, render the form:
1055 if (is_array($rec)) {
1056 // Setting visual path / title of form:
1057 $this->generalPathOfForm = $this->tceforms->getRecordPath($table, $rec);
1058 if (!$this->storeTitle) {
1059 $this->storeTitle = $this->recTitle ? htmlspecialchars($this->recTitle) : BackendUtility::getRecordTitle($table, $rec, TRUE);
1060 }
1061 // Setting variables in TCEforms object:
1062 if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
1063 $this->tceforms->hiddenFieldListArr = array_keys($this->overrideVals[$table]);
1064 }
1065 // Create form for the record (either specific list of fields or the whole record):
1066 $panel = '';
1067 if ($this->columnsOnly) {
1068 if (is_array($this->columnsOnly)) {
1069 $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly[$table]);
1070 } else {
1071 $panel .= $this->tceforms->getListedFields($table, $rec, $this->columnsOnly);
1072 }
1073 } else {
1074 $panel .= $this->tceforms->getMainFields($table, $rec);
1075 }
1076 $panel = $this->tceforms->wrapTotal($panel, $rec, $table);
1077 // Setting the pid value for new records:
1078 if ($cmd == 'new') {
1079 $panel .= '<input type="hidden" name="data[' . $table . '][' . $rec['uid'] . '][pid]" value="' . $rec['pid'] . '" />';
1080 $this->newC++;
1081 }
1082 // Display "is-locked" message:
1083 if ($lockInfo = BackendUtility::isRecordLocked($table, $rec['uid'])) {
1084 /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
1085 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING);
1086 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
1087 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1088 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
1089 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1090 $defaultFlashMessageQueue->enqueue($flashMessage);
1091 }
1092 // Combine it all:
1093 $editForm .= $panel;
1094 }
1095 $thePrevUid = $rec['uid'];
1096 } else {
1097 $this->errorC++;
1098 $editForm .= $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.noEditPermission', TRUE) . '<br /><br />' . ($deniedAccessReason ? 'Reason: ' . htmlspecialchars($deniedAccessReason) . '<br /><br />' : '');
1099 }
1100 }
1101 }
1102 }
1103 }
1104 }
1105 return $editForm;
1106 }
1107
1108 /**
1109 * Create the panel of buttons for submitting the form or otherwise perform operations.
1110 *
1111 * @return array All available buttons as an assoc. array
1112 */
1113 protected function getButtons() {
1114 $lang = $this->getLanguageService();
1115 $buttons = array(
1116 'save' => '',
1117 'save_view' => '',
1118 'save_new' => '',
1119 'save_close' => '',
1120 'close' => '',
1121 'delete' => '',
1122 'undo' => '',
1123 'history' => '',
1124 'columns_only' => '',
1125 'csh' => '',
1126 'translation_save' => '',
1127 'translation_saveclear' => ''
1128 );
1129 /** @var IconFactory $iconFactory */
1130 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
1131 // Render SAVE type buttons:
1132 // The action of each button is decided by its name attribute. (See doProcessData())
1133 if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly']) {
1134 // SAVE button:
1135 $iconSave = $iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL);
1136 $buttons['save'] = '<button name="_savedok" class="c-inputButton" value="1" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE) . '">' . $iconSave . '</button>';
1137 // SAVE / VIEW button:
1138 if ($this->viewId && !$this->noView && $this->getNewIconMode($this->firstEl['table'], 'saveDocView')) {
1139 $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
1140 if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
1141 $excludeDokTypes = GeneralUtility::intExplode(',', $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'], TRUE);
1142 } else {
1143 // exclude sysfolders, spacers and recycler by default
1144 $excludeDokTypes = array(PageRepository::DOKTYPE_RECYCLER, PageRepository::DOKTYPE_SYSFOLDER, PageRepository::DOKTYPE_SPACER);
1145 }
1146 if (!in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, TRUE) || isset($pagesTSconfig['TCEMAIN.']['preview.'][$this->firstEl['table'].'.']['previewPageId'])) {
1147 $buttons['save_view'] = IconUtility::getSpriteIcon('actions-document-save-view', array('html' => '<input onclick="window.open(\'\', \'newTYPO3frontendWindow\');" type="submit" class="c-inputButton t3js-editform-submitButton" name="_savedokview" value="1" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow', TRUE) . '" />'));
1148 }
1149 }
1150 // SAVE / NEW button:
1151 if (count($this->elementsData) === 1 && $this->getNewIconMode($this->firstEl['table'])) {
1152 $iconSaveNew = $iconFactory->getIcon('actions-document-save-new', Icon::SIZE_SMALL);
1153 $buttons['save_new'] = '<button name="_savedoknew" class="c-inputButton t3js-editform-submitButton" value="1" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveNewDoc', TRUE) . '">' . $iconSaveNew . '</button>';
1154 }
1155 // SAVE / CLOSE
1156 $iconSaveClose = $iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL);
1157 $buttons['save_close'] = '<button name="_saveandclosedok" class="c-inputButton t3js-editform-submitButton" value="1" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE) . '">' . $iconSaveClose . '</button>';
1158 // FINISH TRANSLATION / SAVE / CLOSE
1159 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation']) {
1160 $buttons['translation_save'] = '<input type="image" class="c-inputButton" name="_translation_savedok" src="sysext/t3skin/images/icons/actions/document-save-translation.png" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.translationSaveDoc', TRUE) . '" /> ';
1161 $buttons['translation_saveclear'] = '<input type="image" class="c-inputButton" name="_translation_savedokclear" src="sysext/t3skin/images/icons/actions/document-save-cleartranslationcache.png" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.translationSaveDocClear', TRUE) . '" />';
1162 }
1163 }
1164 // CLOSE button:
1165 $buttons['close'] = '<a href="#" class="t3js-editform-close" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '">' . $this->iconFactory->getIcon('actions-document-close', Icon::SIZE_SMALL) . '</a>';
1166 // DELETE + UNDO buttons:
1167 if (!$this->errorC && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && count($this->elementsData) === 1) {
1168 if ($this->firstEl['cmd'] != 'new' && MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])) {
1169 // Delete:
1170 if ($this->firstEl['deleteAccess'] && !$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] && !$this->getNewIconMode($this->firstEl['table'], 'disableDelete')) {
1171 $buttons['delete'] = '<a href="#" class="t3js-editform-delete-record" data-return-url="' . htmlspecialchars($this->retUrl) . '" data-uid="' . htmlspecialchars($this->firstEl['uid']) . '" data-table="' . htmlspecialchars($this->firstEl['table']) . '" title="' . $lang->getLL('deleteItem', TRUE) . '">' . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL) . '</a>';
1172 }
1173 // Undo:
1174 $undoRes = $this->getDatabaseConnection()->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $this->getDatabaseConnection()->fullQuoteStr($this->firstEl['table'], 'sys_history') . ' AND recuid=' . (int)$this->firstEl['uid'], '', 'tstamp DESC', '1');
1175 if ($undoButtonR = $this->getDatabaseConnection()->sql_fetch_assoc($undoRes)) {
1176 $aOnClick = 'window.location.href=' .
1177 GeneralUtility::quoteJSvalue(
1178 BackendUtility::getModuleUrl(
1179 'record_history',
1180 array(
1181 'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1182 'revert' => 'ALL_FIELDS',
1183 'sumUp' => -1,
1184 'returnUrl' => $this->R_URI,
1185 )
1186 )
1187 ) . '; return false;';
1188 $buttons['undo'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '"' . ' title="' . htmlspecialchars(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge(($GLOBALS['EXEC_TIME'] - $undoButtonR['tstamp']), $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')))) . '">' . $this->iconFactory->getIcon('actions-edit-undo', Icon::SIZE_SMALL) . '</a>';
1189 }
1190 if ($this->getNewIconMode($this->firstEl['table'], 'showHistory')) {
1191 $aOnClick = 'window.location.href=' .
1192 GeneralUtility::quoteJSvalue(
1193 BackendUtility::getModuleUrl(
1194 'record_history',
1195 array(
1196 'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1197 'returnUrl' => $this->R_URI,
1198 )
1199 )
1200 ) . '; return false;';
1201 $buttons['history'] = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $this->iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL) . '</a>';
1202 }
1203 // If only SOME fields are shown in the form, this will link the user to the FULL form:
1204 if ($this->columnsOnly) {
1205 $buttons['columns_only'] = '<a href="' . htmlspecialchars(($this->R_URI . '&columnsOnly=')) . '" title="' . $lang->getLL('editWholeRecord', TRUE) . '">' . $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL) . '</a>';
1206 }
1207 }
1208 }
1209 // add the CSH icon
1210 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'TCEforms');
1211 $buttons['shortcut'] = $this->shortCutLink();
1212 $buttons['open_in_new_window'] = $this->openInNewWindowLink();
1213
1214 return $buttons;
1215 }
1216
1217 /**
1218 * Returns the language switch/selector for editing,
1219 * show only when a single record is edited
1220 * - multiple records are too confusing
1221 *
1222 * @return string The HTML
1223 */
1224 public function langSelector() {
1225 $langSelector = '';
1226 if (count($this->elementsData) === 1) {
1227 $langSelector = $this->languageSwitch($this->firstEl['table'], $this->firstEl['uid'], $this->firstEl['pid']);
1228 }
1229 return $langSelector;
1230 }
1231
1232 /**
1233 * Compiles the extra form headers if the tceforms
1234 *
1235 * @return string The HTML
1236 */
1237 public function extraFormHeaders() {
1238 /** @var MarkerBasedTemplateService $templateService */
1239 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1240 $extraTemplate = '';
1241 if (is_array($this->tceforms->extraFormHeaders)) {
1242 $extraTemplate = $templateService->getSubpart($this->doc->moduleTemplate, '###DOCHEADER_EXTRAHEADER###');
1243 $extraTemplate = $templateService->substituteMarker($extraTemplate, '###EXTRAHEADER###', implode(LF, $this->tceforms->extraFormHeaders));
1244 }
1245 return $extraTemplate;
1246 }
1247
1248 /**
1249 * Put together the various elements (buttons, selectors, form) into a table
1250 *
1251 * @param string $editForm HTML form.
1252 * @return string Composite HTML
1253 */
1254 public function compileForm($editForm) {
1255 $formContent = '
1256 <!-- EDITING FORM -->
1257 ' . $editForm . '
1258
1259 <input type="hidden" name="returnUrl" value="' . htmlspecialchars($this->retUrl) . '" />
1260 <input type="hidden" name="viewUrl" value="' . htmlspecialchars($this->viewUrl) . '" />';
1261 if ($this->returnNewPageId) {
1262 $formContent .= '<input type="hidden" name="returnNewPageId" value="1" />';
1263 }
1264 $formContent .= '<input type="hidden" name="popViewId" value="' . htmlspecialchars($this->viewId) . '" />';
1265 if ($this->viewId_addParams) {
1266 $formContent .= '<input type="hidden" name="popViewId_addParams" value="' . htmlspecialchars($this->viewId_addParams) . '" />';
1267 }
1268 $formContent .= '
1269 <input type="hidden" name="closeDoc" value="0" />
1270 <input type="hidden" name="doSave" value="0" />
1271 <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
1272 <input type="hidden" name="_scrollPosition" value="" />' . FormEngine::getHiddenTokenField('editRecord');
1273 return $formContent;
1274 }
1275
1276 /**
1277 * Create shortcut icon
1278 *
1279 * @return string
1280 */
1281 public function shortCutLink() {
1282 if ($this->returnUrl === 'sysext/backend/Resources/Private/Templates/Close.html' || !$this->getBackendUser()->mayMakeShortcut()) {
1283 return '';
1284 }
1285 return $this->doc->makeShortcutIcon('returnUrl,edit,defVals,overrideVals,columnsOnly,returnNewPageId,editRegularContentFromId,noView', '', $this->MCONF['name'], 1);
1286 }
1287
1288 /**
1289 * Creates open-in-window link
1290 *
1291 * @return string
1292 */
1293 public function openInNewWindowLink() {
1294 if ($this->returnUrl === 'sysext/backend/Resources/Private/Templates/Close.html') {
1295 return '';
1296 }
1297 $aOnClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue(GeneralUtility::linkThisScript(array('returnUrl' => 'sysext/backend/Resources/Private/Templates/Close.html'))) . ',' . GeneralUtility::quoteJSvalue(md5($this->R_URI)) . ',\'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
1298 return '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.openInNewWindow', TRUE) . '">' . IconUtility::getSpriteIcon('actions-window-open') . '</a>';
1299 }
1300
1301 /***************************
1302 *
1303 * Localization stuff
1304 *
1305 ***************************/
1306 /**
1307 * Make selector box for creating new translation for a record or switching to edit the record in an existing language.
1308 * Displays only languages which are available for the current page.
1309 *
1310 * @param string $table Table name
1311 * @param int $uid Uid for which to create a new language
1312 * @param int $pid Pid of the record
1313 * @return string <select> HTML element (if there were items for the box anyways...)
1314 */
1315 public function languageSwitch($table, $uid, $pid = NULL) {
1316 $content = '';
1317 $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
1318 $transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
1319 // Table editable and activated for languages?
1320 if ($this->getBackendUser()->check('tables_modify', $table) && $languageField && $transOrigPointerField && !$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
1321 if (is_null($pid)) {
1322 $row = BackendUtility::getRecord($table, $uid, 'pid');
1323 $pid = $row['pid'];
1324 }
1325 // Get all avalibale languages for the page
1326 $langRows = $this->getLanguages($pid);
1327 // Page available in other languages than default language?
1328 if (is_array($langRows) && count($langRows) > 1) {
1329 $rowsByLang = array();
1330 $fetchFields = 'uid,' . $languageField . ',' . $transOrigPointerField;
1331 // Get record in current language
1332 $rowCurrent = BackendUtility::getLiveVersionOfRecord($table, $uid, $fetchFields);
1333 if (!is_array($rowCurrent)) {
1334 $rowCurrent = BackendUtility::getRecord($table, $uid, $fetchFields);
1335 }
1336 $currentLanguage = $rowCurrent[$languageField];
1337 // Disabled for records with [all] language!
1338 if ($currentLanguage > -1) {
1339 // Get record in default language if needed
1340 if ($currentLanguage && $rowCurrent[$transOrigPointerField]) {
1341 $rowsByLang[0] = BackendUtility::getLiveVersionOfRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1342 if (!is_array($rowsByLang[0])) {
1343 $rowsByLang[0] = BackendUtility::getRecord($table, $rowCurrent[$transOrigPointerField], $fetchFields);
1344 }
1345 } else {
1346 $rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
1347 }
1348 if ($rowCurrent[$transOrigPointerField] || $currentLanguage === '0') {
1349 // Get record in other languages to see what's already available
1350 $translations = $this->getDatabaseConnection()->exec_SELECTgetRows($fetchFields, $table, 'pid=' . (int)$pid . ' AND ' . $languageField . '>0' . ' AND ' . $transOrigPointerField . '=' . (int)$rowsByLang[0]['uid'] . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table));
1351 foreach ($translations as $row) {
1352 $rowsByLang[$row[$languageField]] = $row;
1353 }
1354 }
1355 $langSelItems = array();
1356 foreach ($langRows as $lang) {
1357 if ($this->getBackendUser()->checkLanguageAccess($lang['uid'])) {
1358 $newTranslation = isset($rowsByLang[$lang['uid']]) ? '' : ' [' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.new', TRUE) . ']';
1359 // Create url for creating a localized record
1360 if ($newTranslation) {
1361 $redirectUrl = BackendUtility::getModuleUrl('record_edit', array(
1362 'justLocalized' => $table . ':' . $rowsByLang[0]['uid'] . ':' . $lang['uid'],
1363 'returnUrl' => $this->retUrl
1364 )) . BackendUtility::getUrlToken('editRecord');
1365 $href = $this->doc->issueCommand('&cmd[' . $table . '][' . $rowsByLang[0]['uid'] . '][localize]=' . $lang['uid'], $redirectUrl);
1366 } else {
1367 $href = BackendUtility::getModuleUrl('record_edit', array(
1368 'edit[' . $table . '][' . $rowsByLang[$lang['uid']]['uid'] . ']' => 'edit',
1369 'returnUrl' => $this->retUrl
1370 )) . BackendUtility::getUrlToken('editRecord');
1371 }
1372 $langSelItems[$lang['uid']] = '
1373 <option value="' . htmlspecialchars($href) . '"' . ($currentLanguage == $lang['uid'] ? ' selected="selected"' : '') . '>' . htmlspecialchars(($lang['title'] . $newTranslation)) . '</option>';
1374 }
1375 }
1376 // If any languages are left, make selector:
1377 if (count($langSelItems) > 1) {
1378 $onChange = 'if(this.options[this.selectedIndex].value){window.location.href=(this.options[this.selectedIndex].value);}';
1379 $content = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_general.xlf:LGL.language', TRUE) . ' <select name="_langSelector" onchange="' . htmlspecialchars($onChange) . '">
1380 ' . implode('', $langSelItems) . '
1381 </select>';
1382 }
1383 }
1384 }
1385 }
1386 return $content;
1387 }
1388
1389 /**
1390 * Redirects to FormEngine with new parameters to edit a just created localized record
1391 *
1392 * @param string $justLocalized String passed by GET &justLocalized=
1393 * @return void
1394 */
1395 public function localizationRedirect($justLocalized) {
1396 list($table, $orig_uid, $language) = explode(':', $justLocalized);
1397 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
1398 $localizedRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', $table, $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$language . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$orig_uid . BackendUtility::deleteClause($table) . BackendUtility::versioningPlaceholderClause($table));
1399 if (is_array($localizedRecord)) {
1400 // Create parameters and finally run the classic page module for creating a new page translation
1401 $location = BackendUtility::getModuleUrl('record_edit', array(
1402 'edit[' . $table . '][' . $localizedRecord['uid'] . ']' => 'edit',
1403 'returnUrl' => GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'))
1404 ));
1405 HttpUtility::redirect($location . BackendUtility::getUrlToken('editRecord'));
1406 }
1407 }
1408 }
1409
1410 /**
1411 * Returns sys_language records available for record translations on given page.
1412 *
1413 * @param int $id Page id: If zero, the query will select all sys_language records from root level which are NOT hidden. If set to another value, the query will select all sys_language records that has a pages_language_overlay record on that page (and is not hidden, unless you are admin user)
1414 * @return array Language records including faked record for default language
1415 */
1416 public function getLanguages($id) {
1417 $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
1418 // Fallback non sprite-configuration
1419 if (preg_match('/\\.gif$/', $modSharedTSconfig['properties']['defaultLanguageFlag'])) {
1420 $modSharedTSconfig['properties']['defaultLanguageFlag'] = str_replace('.gif', '', $modSharedTSconfig['properties']['defaultLanguageFlag']);
1421 }
1422 $languages = array(
1423 0 => array(
1424 'uid' => 0,
1425 'pid' => 0,
1426 'hidden' => 0,
1427 'title' => $modSharedTSconfig['properties']['defaultLanguageLabel'] !== ''
1428 ? $modSharedTSconfig['properties']['defaultLanguageLabel'] . ' (' . $this->getLanguageService()->sl('LLL:EXT:lang/locallang_mod_web_list.xlf:defaultLanguage') . ')'
1429 : $this->getLanguageService()->sl('LLL:EXT:lang/locallang_mod_web_list.xlf:defaultLanguage'),
1430 'flag' => $modSharedTSconfig['properties']['defaultLanguageFlag']
1431 )
1432 );
1433 $exQ = $this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0';
1434 if ($id) {
1435 $rows = $this->getDatabaseConnection()->exec_SELECTgetRows('sys_language.*', 'pages_language_overlay,sys_language', 'pages_language_overlay.sys_language_uid=sys_language.uid AND pages_language_overlay.pid=' . (int)$id . BackendUtility::deleteClause('pages_language_overlay') . $exQ, 'pages_language_overlay.sys_language_uid,sys_language.uid,sys_language.pid,sys_language.tstamp,sys_language.hidden,sys_language.title,sys_language.language_isocode,sys_language.static_lang_isocode,sys_language.flag', 'sys_language.title');
1436 } else {
1437 $rows = $this->getDatabaseConnection()->exec_SELECTgetRows('sys_language.*', 'sys_language', 'sys_language.hidden=0', '', 'sys_language.title');
1438 }
1439 if ($rows) {
1440 foreach ($rows as $row) {
1441 $languages[$row['uid']] = $row;
1442 }
1443 }
1444 return $languages;
1445 }
1446
1447 /***************************
1448 *
1449 * Other functions
1450 *
1451 ***************************/
1452 /**
1453 * Fix $this->editconf if versioning applies to any of the records
1454 *
1455 * @param array|bool $mapArray Mapping between old and new ids if auto-versioning has been performed.
1456 * @return void
1457 */
1458 public function fixWSversioningInEditConf($mapArray = FALSE) {
1459 // Traverse the editConf array
1460 if (is_array($this->editconf)) {
1461 // Tables:
1462 foreach ($this->editconf as $table => $conf) {
1463 if (is_array($conf) && $GLOBALS['TCA'][$table]) {
1464 // Traverse the keys/comments of each table (keys can be a commalist of uids)
1465 $newConf = array();
1466 foreach ($conf as $cKey => $cmd) {
1467 if ($cmd == 'edit') {
1468 // Traverse the ids:
1469 $ids = GeneralUtility::trimExplode(',', $cKey, TRUE);
1470 foreach ($ids as $idKey => $theUid) {
1471 if (is_array($mapArray)) {
1472 if ($mapArray[$table][$theUid]) {
1473 $ids[$idKey] = $mapArray[$table][$theUid];
1474 }
1475 } else {
1476 // Default, look for versions in workspace for record:
1477 $calcPRec = $this->getRecordForEdit($table, $theUid);
1478 if (is_array($calcPRec)) {
1479 // Setting UID again if it had changed, eg. due to workspace versioning.
1480 $ids[$idKey] = $calcPRec['uid'];
1481 }
1482 }
1483 }
1484 // Add the possibly manipulated IDs to the new-build newConf array:
1485 $newConf[implode(',', $ids)] = $cmd;
1486 } else {
1487 $newConf[$cKey] = $cmd;
1488 }
1489 }
1490 // Store the new conf array:
1491 $this->editconf[$table] = $newConf;
1492 }
1493 }
1494 }
1495 }
1496
1497 /**
1498 * Get record for editing.
1499 *
1500 * @param string $table Table name
1501 * @param int $theUid Record UID
1502 * @return array Returns record to edit, FALSE if none
1503 */
1504 public function getRecordForEdit($table, $theUid) {
1505 // Fetch requested record:
1506 $reqRecord = BackendUtility::getRecord($table, $theUid, 'uid,pid');
1507 if (is_array($reqRecord)) {
1508 // If workspace is OFFLINE:
1509 if ($this->getBackendUser()->workspace != 0) {
1510 // Check for versioning support of the table:
1511 if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1512 // If the record is already a version of "something" pass it by.
1513 if ($reqRecord['pid'] == -1) {
1514 // (If it turns out not to be a version of the current workspace there will be trouble, but that is handled inside TCEmain then and in the interface it would clearly be an error of links if the user accesses such a scenario)
1515 return $reqRecord;
1516 } else {
1517 // The input record was online and an offline version must be found or made:
1518 // Look for version of this workspace:
1519 $versionRec = BackendUtility::getWorkspaceVersionOfRecord($this->getBackendUser()->workspace, $table, $reqRecord['uid'], 'uid,pid,t3ver_oid');
1520 return is_array($versionRec) ? $versionRec : $reqRecord;
1521 }
1522 } else {
1523 // This means that editing cannot occur on this record because it was not supporting versioning which is required inside an offline workspace.
1524 return FALSE;
1525 }
1526 } else {
1527 // In ONLINE workspace, just return the originally requested record:
1528 return $reqRecord;
1529 }
1530 } else {
1531 // Return FALSE because the table/uid was not found anyway.
1532 return FALSE;
1533 }
1534 }
1535
1536 /**
1537 * Function, which populates the internal editconf array with editing commands for all tt_content elements from the normal column in normal language from the page pointed to by $this->editRegularContentFromId
1538 *
1539 * @return void
1540 * @deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8
1541 */
1542 public function editRegularContentFromId() {
1543 GeneralUtility::logDeprecatedFunction();
1544 $dbConnection = $this->getDatabaseConnection();
1545 $res = $dbConnection->exec_SELECTquery('uid', 'tt_content', 'pid=' . (int)$this->editRegularContentFromId . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content') . ' AND colPos=0 AND sys_language_uid=0', '', 'sorting');
1546 if ($dbConnection->sql_num_rows($res)) {
1547 $ecUids = array();
1548 while ($ecRec = $dbConnection->sql_fetch_assoc($res)) {
1549 $ecUids[] = $ecRec['uid'];
1550 }
1551 $this->editconf['tt_content'][implode(',', $ecUids)] = 'edit';
1552 }
1553 $dbConnection->sql_free_result($res);
1554 }
1555
1556 /**
1557 * Populates the variables $this->storeArray, $this->storeUrl, $this->storeUrlMd5
1558 *
1559 * @return void
1560 * @see makeDocSel()
1561 */
1562 public function compileStoreDat() {
1563 $this->storeArray = GeneralUtility::compileSelectedGetVarsFromArray('edit,defVals,overrideVals,columnsOnly,noView,editRegularContentFromId,workspace', $this->R_URL_getvars);
1564 $this->storeUrl = GeneralUtility::implodeArrayForUrl('', $this->storeArray);
1565 $this->storeUrlMd5 = md5($this->storeUrl);
1566 }
1567
1568 /**
1569 * Function used to look for configuration of buttons in the form: Fx. disabling buttons or showing them at various positions.
1570 *
1571 * @param string $table The table for which the configuration may be specific
1572 * @param string $key The option for look for. Default is checking if the saveDocNew button should be displayed.
1573 * @return string Return value fetched from USER TSconfig
1574 */
1575 public function getNewIconMode($table, $key = 'saveDocNew') {
1576 $TSconfig = $this->getBackendUser()->getTSConfig('options.' . $key);
1577 $output = trim(isset($TSconfig['properties'][$table]) ? $TSconfig['properties'][$table] : $TSconfig['value']);
1578 return $output;
1579 }
1580
1581 /**
1582 * Handling the closing of a document
1583 *
1584 * @param int $code Close code: 0/1 will redirect to $this->retUrl, 3 will clear the docHandler (thus closing all documents) and other values will call setDocument with ->retUrl
1585 * @return void
1586 */
1587 public function closeDocument($code = 0) {
1588 // If current document is found in docHandler,
1589 // then unset it, possibly unset it ALL and finally, write it to the session data
1590 if (isset($this->docHandler[$this->storeUrlMd5])) {
1591 // add the closing document to the recent documents
1592 $recentDocs = $this->getBackendUser()->getModuleData('opendocs::recent');
1593 if (!is_array($recentDocs)) {
1594 $recentDocs = array();
1595 }
1596 $closedDoc = $this->docHandler[$this->storeUrlMd5];
1597 $recentDocs = array_merge(array($this->storeUrlMd5 => $closedDoc), $recentDocs);
1598 if (count($recentDocs) > 8) {
1599 $recentDocs = array_slice($recentDocs, 0, 8);
1600 }
1601 // remove it from the list of the open documents
1602 unset($this->docHandler[$this->storeUrlMd5]);
1603 if ($code == '3') {
1604 $recentDocs = array_merge($this->docHandler, $recentDocs);
1605 $this->docHandler = array();
1606 }
1607 $this->getBackendUser()->pushModuleData('opendocs::recent', $recentDocs);
1608 $this->getBackendUser()->pushModuleData('FormEngine', array($this->docHandler, $this->docDat[1]));
1609 BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
1610 }
1611 // If ->returnEditConf is set, then add the current content of editconf to the ->retUrl variable: (used by other scripts, like wizard_add, to know which records was created or so...)
1612 if ($this->returnEditConf && $this->retUrl != BackendUtility::getModuleUrl('dummy')) {
1613 $this->retUrl .= '&returnEditConf=' . rawurlencode(json_encode($this->editconf));
1614 }
1615 // If code is NOT set OR set to 1, then make a header location redirect to $this->retUrl
1616 if (!$code || $code == 1) {
1617 HttpUtility::redirect($this->retUrl);
1618 } else {
1619 $this->setDocument('', $this->retUrl);
1620 }
1621 }
1622
1623 /**
1624 * Redirects to the document pointed to by $currentDocFromHandlerMD5 OR $retUrl (depending on some internal calculations).
1625 * Most likely you will get a header-location redirect from this function.
1626 *
1627 * @param string $currentDocFromHandlerMD5 Pointer to the document in the docHandler array
1628 * @param string $retUrl Alternative/Default retUrl
1629 * @return void
1630 */
1631 public function setDocument($currentDocFromHandlerMD5 = '', $retUrl = '') {
1632 if ($retUrl === '') {
1633 return;
1634 }
1635 if (!$this->modTSconfig['properties']['disableDocSelector'] && is_array($this->docHandler) && !empty($this->docHandler)) {
1636 if (isset($this->docHandler[$currentDocFromHandlerMD5])) {
1637 $setupArr = $this->docHandler[$currentDocFromHandlerMD5];
1638 } else {
1639 $setupArr = reset($this->docHandler);
1640 }
1641 if ($setupArr[2]) {
1642 $sParts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
1643 $retUrl = $sParts['path'] . '?' . $setupArr[2] . '&returnUrl=' . rawurlencode($retUrl);
1644 }
1645 }
1646 HttpUtility::redirect($retUrl);
1647 }
1648
1649 /**
1650 * Injects the request object for the current request or subrequest
1651 *
1652 * @param ServerRequestInterface $request
1653 * @return \Psr\Http\Message\ResponseInterface $response
1654 */
1655 public function processRequest(ServerRequestInterface $request) {
1656
1657 BackendUtility::lockRecords();
1658
1659 // Preprocessing, storing data if submitted to
1660 $this->preInit();
1661
1662 // Checks, if a save button has been clicked (or the doSave variable is sent)
1663 if ($this->doProcessData()) {
1664 $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
1665 if ($formProtection->validateToken(\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('formToken'), 'editRecord')) {
1666 $this->processData();
1667 }
1668 }
1669
1670 $this->init();
1671 $this->main();
1672
1673 /** @var Response $response */
1674 $response = GeneralUtility::makeInstance(Response::class);
1675 $response->getBody()->write($this->content);
1676 return $response;
1677 }
1678
1679 /**
1680 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1681 */
1682 protected function getBackendUser() {
1683 return $GLOBALS['BE_USER'];
1684 }
1685
1686 /**
1687 * Returns LanguageService
1688 *
1689 * @return \TYPO3\CMS\Lang\LanguageService
1690 */
1691 protected function getLanguageService() {
1692 return $GLOBALS['LANG'];
1693 }
1694
1695 /**
1696 * Returns the database connection
1697 *
1698 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
1699 */
1700 protected function getDatabaseConnection() {
1701 return $GLOBALS['TYPO3_DB'];
1702 }
1703
1704 }