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