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