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