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