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