[TASK] Create own response instance in controller actions
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / Wizard / AddController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller\Wizard;
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\FormDataCompiler;
20 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
23 use TYPO3\CMS\Core\DataHandling\DataHandler;
24 use TYPO3\CMS\Core\Http\HtmlResponse;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Core\Utility\HttpUtility;
27 use TYPO3\CMS\Core\Utility\MathUtility;
28
29 /**
30 * Script Class for adding new items to a group/select field. Performs proper redirection as needed.
31 * Script is typically called after new child record was added and then adds the new child to select value of parent.
32 */
33 class AddController extends AbstractWizardController
34 {
35 /**
36 * Content accumulation for the module.
37 *
38 * @var string
39 */
40 public $content;
41
42 /**
43 * If set, the DataHandler class is loaded and used to add the returning ID to the parent record.
44 *
45 * @var int
46 */
47 public $processDataFlag = 0;
48
49 /**
50 * Create new record -pid (pos/neg). If blank, return immediately
51 *
52 * @var int
53 */
54 public $pid;
55
56 /**
57 * The parent table we are working on.
58 *
59 * @var string
60 */
61 public $table;
62
63 /**
64 * Loaded with the created id of a record FormEngine returns ...
65 *
66 * @var int
67 */
68 public $id;
69
70 /**
71 * Wizard parameters, coming from TCEforms linking to the wizard.
72 *
73 * @var array
74 */
75 public $P;
76
77 /**
78 * Information coming back from the FormEngine script, telling what the table/id was of the newly created record.
79 *
80 * @var array
81 */
82 public $returnEditConf;
83
84 /**
85 * Constructor
86 */
87 public function __construct()
88 {
89 $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_wizards.xlf');
90 $GLOBALS['SOBE'] = $this;
91
92 $this->init();
93 }
94
95 /**
96 * Initialization of the class.
97 */
98 protected function init()
99 {
100 // Init GPvars:
101 $this->P = GeneralUtility::_GP('P');
102 $this->returnEditConf = GeneralUtility::_GP('returnEditConf');
103 // Get this record
104 $record = BackendUtility::getRecord($this->P['table'], $this->P['uid']);
105 // Set table:
106 $this->table = $this->P['params']['table'];
107 // Get TSconfig for it.
108 $TSconfig = BackendUtility::getTCEFORM_TSconfig(
109 $this->P['table'],
110 is_array($record) ? $record : ['pid' => $this->P['pid']]
111 );
112 // Set [params][pid]
113 if (substr($this->P['params']['pid'], 0, 3) === '###' && substr($this->P['params']['pid'], -3) === '###') {
114 $keyword = substr($this->P['params']['pid'], 3, -3);
115 if (strpos($keyword, 'PAGE_TSCONFIG_') === 0) {
116 $this->pid = (int)$TSconfig[$this->P['field']][$keyword];
117 } else {
118 $this->pid = (int)$TSconfig['_' . $keyword];
119 }
120 } else {
121 $this->pid = (int)$this->P['params']['pid'];
122 }
123 // Return if new record as parent (not possibly/allowed)
124 if ($this->pid === '') {
125 HttpUtility::redirect(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
126 }
127 // Else proceed:
128 // If a new id has returned from a newly created record...
129 if ($this->returnEditConf) {
130 $editConfiguration = json_decode($this->returnEditConf, true);
131 if (is_array($editConfiguration[$this->table]) && MathUtility::canBeInterpretedAsInteger($this->P['uid'])) {
132 // Getting id and cmd from returning editConf array.
133 reset($editConfiguration[$this->table]);
134 $this->id = (int)key($editConfiguration[$this->table]);
135 $cmd = current($editConfiguration[$this->table]);
136 // ... and if everything seems OK we will register some classes for inclusion and instruct the object
137 // to perform processing later.
138 if ($this->P['params']['setValue']
139 && $cmd === 'edit'
140 && $this->id
141 && $this->P['table']
142 && $this->P['field'] && $this->P['uid']
143 ) {
144 $liveRecord = BackendUtility::getLiveVersionOfRecord($this->table, $this->id, 'uid');
145 if ($liveRecord) {
146 $this->id = $liveRecord['uid'];
147 }
148 $this->processDataFlag = 1;
149 }
150 }
151 }
152 }
153
154 /**
155 * Injects the request object for the current request or subrequest
156 * As this controller goes only through the main() method, it is rather simple for now
157 *
158 * @param ServerRequestInterface $request
159 * @return ResponseInterface
160 */
161 public function mainAction(ServerRequestInterface $request): ResponseInterface
162 {
163 $this->main();
164 return new HtmlResponse('');
165 }
166
167 /**
168 * Main function
169 * Will issue a location-header, redirecting either BACK or to a new FormEngine instance...
170 */
171 public function main()
172 {
173 if ($this->returnEditConf) {
174 if ($this->processDataFlag) {
175 // Because OnTheFly can't handle MM relations with intermediate tables we use TcaDatabaseRecord here
176 // Otherwise already stored relations are overwritten with the new entry
177 /** @var TcaDatabaseRecord $formDataGroup */
178 $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
179 /** @var FormDataCompiler $formDataCompiler */
180 $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
181 $input = [
182 'tableName' => $this->P['table'],
183 'vanillaUid' => (int)$this->P['uid'],
184 'command' => 'edit',
185 ];
186 $result = $formDataCompiler->compile($input);
187 $currentParentRow = $result['databaseRow'];
188
189 // If that record was found (should absolutely be...), then init DataHandler and set, prepend or append
190 // the record
191 if (is_array($currentParentRow)) {
192 /** @var DataHandler $dataHandler */
193 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
194 $data = [];
195 $recordId = $this->table . '_' . $this->id;
196 // Setting the new field data:
197 // If the field is a flexForm field, work with the XML structure instead:
198 if ($this->P['flexFormPath']) {
199 // Current value of flexForm path:
200 $currentFlexFormData = $currentParentRow[$this->P['field']];
201 /** @var FlexFormTools $flexFormTools */
202 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
203 $currentFlexFormValueByPath = $flexFormTools->getArrayValueByPath(
204 $this->P['flexFormPath'],
205 $currentFlexFormData
206 );
207
208 // Compile currentFlexFormData to functional string
209 $currentFlexFormValues = [];
210 foreach ($currentFlexFormValueByPath as $value) {
211 $currentFlexFormValues[] = $value['table'] . '_' . $value['uid'];
212 }
213 $currentFlexFormValue = implode(',', $currentFlexFormValues);
214
215 $insertValue = '';
216 switch ((string)$this->P['params']['setValue']) {
217 case 'set':
218 $insertValue = $recordId;
219 break;
220 case 'append':
221 $insertValue = $currentFlexFormValue . ',' . $recordId;
222 break;
223 case 'prepend':
224 $insertValue = $recordId . ',' . $currentFlexFormValue;
225 break;
226 }
227 $insertValue = implode(',', GeneralUtility::trimExplode(',', $insertValue, true));
228 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = [];
229 $flexFormTools->setArrayValueByPath(
230 $this->P['flexFormPath'],
231 $data[$this->P['table']][$this->P['uid']][$this->P['field']],
232 $insertValue
233 );
234 } else {
235 // Check the row for its datatype. If it is an array it stores the relation
236 // to other rows. Implode it into a comma separated list to be able to restore the stored
237 // values after the wizard falls back to the parent record
238 $currentValue = $currentParentRow[$this->P['field']];
239 if (is_array($currentValue)) {
240 $currentValue = implode(',', array_column($currentValue, 'uid'));
241 }
242 switch ((string)$this->P['params']['setValue']) {
243 case 'set':
244 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $recordId;
245 break;
246 case 'append':
247 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $currentValue . ',' . $recordId;
248 break;
249 case 'prepend':
250 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = $recordId . ',' . $currentValue;
251 break;
252 }
253 $data[$this->P['table']][$this->P['uid']][$this->P['field']] = implode(
254 ',',
255 GeneralUtility::trimExplode(
256 ',',
257 $data[$this->P['table']][$this->P['uid']][$this->P['field']],
258 true
259 )
260 );
261 }
262 // Submit the data:
263 $dataHandler->start($data, []);
264 $dataHandler->process_datamap();
265 }
266 }
267 // Return to the parent FormEngine record editing session:
268 HttpUtility::redirect(GeneralUtility::sanitizeLocalUrl($this->P['returnUrl']));
269 } else {
270 // Redirecting to FormEngine with instructions to create a new record
271 // AND when closing to return back with information about that records ID etc.
272 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
273 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
274 $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', [
275 'returnEditConf' => 1,
276 'edit[' . $this->P['params']['table'] . '][' . $this->pid . ']' => 'new',
277 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
278 ]);
279 HttpUtility::redirect($redirectUrl);
280 }
281 }
282 }