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