‪TYPO3CMS  ‪main
FormInlineAjaxController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
31 use TYPO3\CMS\Core\Page\JavaScriptItems;
36 
40 #[AsController]
42 {
43  public function ‪__construct(
44  private readonly ‪FormDataCompiler $formDataCompiler,
45  ) {}
46 
50  public function ‪createAction(ServerRequestInterface $request): ResponseInterface
51  {
52  $ajaxArguments = $request->getParsedBody()['ajax'] ?? $request->getQueryParams()['ajax'];
53  $parentConfig = $this->‪extractSignedParentConfigFromRequest((string)$ajaxArguments['context']);
54 
55  $domObjectId = $ajaxArguments[0] ?? '';
56  $inlineFirstPid = $this->‪getInlineFirstPidFromDomObjectId($domObjectId);
57  if (!‪MathUtility::canBeInterpretedAsInteger($inlineFirstPid)
58  && !str_starts_with((string)$inlineFirstPid, 'NEW')
59  ) {
60  throw new \RuntimeException(
61  'inlineFirstPid should either be an integer or a "NEW..." string',
62  1521220491
63  );
64  }
65  $childChildUid = null;
66  if (isset($ajaxArguments[1]) && ‪MathUtility::canBeInterpretedAsInteger($ajaxArguments[1])) {
67  $childChildUid = (int)$ajaxArguments[1];
68  }
69 
70  // Parse the DOM identifier, add the levels to the structure stack
71  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
72  $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
73  $inlineStackProcessor->setAjaxConfiguration($parentConfig);
74  $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
75 
76  // Parent, this table embeds the child table
77  $parent = $inlineStackProcessor->getStructureLevel(-1);
78 
79  // Child, a record from this table should be rendered
80  $child = $inlineStackProcessor->getUnstableStructure();
81  if (isset($child['uid']) && ‪MathUtility::canBeInterpretedAsInteger($child['uid'])) {
82  // If uid comes in, it is the id of the record neighbor record "create after"
83  $childVanillaUid = -1 * abs((int)$child['uid']);
84  } else {
85  // Else inline first Pid is the storage pid of new inline records
86  $childVanillaUid = $inlineFirstPid;
87  }
88 
89  $childTableName = $parentConfig['foreign_table'];
90 
91  $formDataCompilerInput = [
92  'request' => $request,
93  'command' => 'new',
94  'tableName' => $childTableName,
95  'vanillaUid' => $childVanillaUid,
96  'isInlineChild' => true,
97  'inlineStructure' => $inlineStackProcessor->getStructure(),
98  'inlineFirstPid' => $inlineFirstPid,
99  'inlineParentUid' => $parent['uid'],
100  'inlineParentTableName' => $parent['table'],
101  'inlineParentFieldName' => $parent['field'],
102  'inlineParentConfig' => $parentConfig,
103  'inlineTopMostParentUid' => $inlineTopMostParent['uid'],
104  'inlineTopMostParentTableName' => $inlineTopMostParent['table'],
105  'inlineTopMostParentFieldName' => $inlineTopMostParent['field'],
106  ];
107  if ($childChildUid) {
108  $formDataCompilerInput['inlineChildChildUid'] = $childChildUid;
109  }
110  $childData = $this->formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(TcaDatabaseRecord::class));
111 
112  if (($parentConfig['foreign_selector'] ?? false) && ($parentConfig['appearance']['useCombination'] ?? false)) {
113  // We have a foreign_selector. So, we just created a new record on an intermediate table in $childData.
114  // Now, if a valid id is given as second ajax parameter, the intermediate row should be connected to an
115  // existing record of the child-child table specified by the given uid. If there is no such id, user
116  // clicked on "created new" and a new child-child should be created, too.
117  if ($childChildUid) {
118  // Fetch existing child child
119  $childData['databaseRow'][$parentConfig['foreign_selector']] = [
120  $childChildUid,
121  ];
122  $childData['combinationChild'] = $this->‪compileChildChild($request, $childData, $parentConfig, $inlineStackProcessor->getStructure());
123  } else {
124  $formDataCompilerInput = [
125  'request' => $request,
126  'command' => 'new',
127  'tableName' => $this->‪getChildChildTableName($parentConfig['foreign_selector'], $childData),
128  'vanillaUid' => $inlineFirstPid,
129  'isInlineChild' => true,
130  'isInlineAjaxOpeningContext' => true,
131  'inlineStructure' => $inlineStackProcessor->getStructure(),
132  'inlineFirstPid' => $inlineFirstPid,
133  ];
134  $childData['combinationChild'] = $this->formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(TcaDatabaseRecord::class));
135  }
136  }
137 
138  $childData['inlineParentUid'] = $parent['uid'];
139  $childData['renderType'] = 'inlineRecordContainer';
140  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
141  $childResult = $nodeFactory->create($childData)->render();
142 
143  $jsonArray = [
144  'data' => '',
145  'stylesheetFiles' => [],
146  'scriptItems' => GeneralUtility::makeInstance(JavaScriptItems::class),
147  'compilerInput' => [
148  'uid' => $childData['databaseRow']['uid'],
149  'childChildUid' => $childChildUid,
150  'parentConfig' => $parentConfig,
151  ],
152  ];
153 
154  $jsonArray = $this->‪mergeChildResultIntoJsonResult($jsonArray, $childResult);
155 
156  return new ‪JsonResponse($jsonArray);
157  }
158 
162  public function ‪detailsAction(ServerRequestInterface $request): ResponseInterface
163  {
164  $ajaxArguments = $request->getParsedBody()['ajax'] ?? $request->getQueryParams()['ajax'];
165 
166  $domObjectId = $ajaxArguments[0] ?? '';
167  $inlineFirstPid = $this->‪getInlineFirstPidFromDomObjectId($domObjectId);
168  $parentConfig = $this->‪extractSignedParentConfigFromRequest((string)$ajaxArguments['context']);
169 
170  // Parse the DOM identifier, add the levels to the structure stack
171  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
172  $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
173  $inlineStackProcessor->setAjaxConfiguration($parentConfig);
174 
175  // Parent, this table embeds the child table
176  $parent = $inlineStackProcessor->getStructureLevel(-1);
177  $parentFieldName = $parent['field'];
178 
179  // Set flag in config so that only the fields are rendered
180  // @todo: Solve differently / rename / whatever
181  $parentConfig['renderFieldsOnly'] = true;
182 
183  $parentData = [
184  'processedTca' => [
185  'columns' => [
186  $parentFieldName => [
187  'config' => $parentConfig,
188  ],
189  ],
190  ],
191  'uid' => $parent['uid'],
192  'tableName' => $parent['table'],
193  'inlineFirstPid' => $inlineFirstPid,
194  // Hand over given original return url to compile stack. Needed if inline children compile links to
195  // another view (eg. edit metadata in a nested inline situation like news with inline content element image),
196  // so the back link is still the link from the original request. See issue #82525. This is additionally
197  // given down in TcaInline data provider to compiled children data.
198  'returnUrl' => $parentConfig['originalReturnUrl'],
199  ];
200 
201  // Child, a record from this table should be rendered
202  $child = $inlineStackProcessor->getUnstableStructure();
203 
204  $childData = $this->‪compileChild($request, $parentData, $parentFieldName, (int)$child['uid'], $inlineStackProcessor->getStructure());
205 
206  $childData['inlineParentUid'] = (int)$parent['uid'];
207  $childData['renderType'] = 'inlineRecordContainer';
208  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
209  $childResult = $nodeFactory->create($childData)->render();
210 
211  $jsonArray = [
212  'data' => '',
213  'stylesheetFiles' => [],
214  'scriptItems' => GeneralUtility::makeInstance(JavaScriptItems::class),
215  ];
216 
217  $jsonArray = $this->‪mergeChildResultIntoJsonResult($jsonArray, $childResult);
218 
219  return new ‪JsonResponse($jsonArray);
220  }
221 
229  public function ‪synchronizeLocalizeAction(ServerRequestInterface $request): ResponseInterface
230  {
231  $ajaxArguments = $request->getParsedBody()['ajax'] ?? $request->getQueryParams()['ajax'];
232  $domObjectId = $ajaxArguments[0] ?? '';
233  $type = $ajaxArguments[1] ?? null;
234  $parentConfig = $this->‪extractSignedParentConfigFromRequest((string)$ajaxArguments['context']);
235 
236  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
237  // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
238  $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
239  $inlineStackProcessor->setAjaxConfiguration($parentConfig);
240  $inlineFirstPid = $this->‪getInlineFirstPidFromDomObjectId($domObjectId);
241 
242  $jsonArray = [
243  'data' => '',
244  'stylesheetFiles' => [],
245  'scriptItems' => GeneralUtility::makeInstance(JavaScriptItems::class),
246  'compilerInput' => [
247  'localize' => [],
248  ],
249  ];
250  if ($type === 'localize' || $type === 'synchronize' || ‪MathUtility::canBeInterpretedAsInteger($type)) {
251  // Parent, this table embeds the child table
252  $parent = $inlineStackProcessor->getStructureLevel(-1);
253  $parentFieldName = $parent['field'];
254 
255  $processedTca = ‪$GLOBALS['TCA'][$parent['table']];
256  $processedTca['columns'][$parentFieldName]['config'] = $parentConfig;
257 
258  $formDataCompilerInputForParent = [
259  'request' => $request,
260  'vanillaUid' => (int)$parent['uid'],
261  'command' => 'edit',
262  'tableName' => $parent['table'],
263  'processedTca' => $processedTca,
264  'inlineFirstPid' => $inlineFirstPid,
265  'columnsToProcess' => [
266  $parentFieldName,
267  ],
268  // @todo: still needed? NO!
269  'inlineStructure' => $inlineStackProcessor->getStructure(),
270  // Do not compile existing children, we don't need them now
271  'inlineCompileExistingChildren' => false,
272  ];
273  // Full TcaDatabaseRecord is required here to have the list of connected uids $oldItemList
274  $parentData = $this->formDataCompiler->compile($formDataCompilerInputForParent, GeneralUtility::makeInstance(TcaDatabaseRecord::class));
275  $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
276  $parentLanguageField = $parentData['processedTca']['ctrl']['languageField'];
277  $parentLanguage = $parentData['databaseRow'][$parentLanguageField];
278  $oldItemList = $parentData['databaseRow'][$parentFieldName];
279 
280  // DataHandler cannot handle arrays as field value
281  if (is_array($parentLanguage)) {
282  $parentLanguage = implode(',', $parentLanguage);
283  }
284 
285  $cmd = [];
286  // Localize a single child element from default language of the parent element
288  $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = [
289  'field' => $parent['field'],
290  'language' => $parentLanguage,
291  'ids' => [$type],
292  ];
293  } else {
294  // Either localize or synchronize all child elements from default language of the parent element
295  $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = [
296  'field' => $parent['field'],
297  'language' => $parentLanguage,
298  'action' => $type,
299  ];
300  }
301 
302  $tce = GeneralUtility::makeInstance(DataHandler::class);
303  $tce->start([], $cmd);
304  $tce->process_cmdmap();
305 
306  $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parentFieldName];
307 
308  $oldItems = $this->‪getInlineRelatedRecordsUidArray($oldItemList);
309  $newItems = $this->‪getInlineRelatedRecordsUidArray($newItemList);
310 
311  // Render error messages from DataHandler
312  $tce->printLogErrorMessages();
313  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
314  $messages = $flashMessageService->getMessageQueueByIdentifier()->getAllMessagesAndFlush();
315  if (!empty($messages)) {
316  foreach ($messages as $message) {
317  $jsonArray['messages'][] = [
318  'title' => $message->getTitle(),
319  'message' => $message->getMessage(),
320  'severity' => $message->getSeverity(),
321  ];
322  if ($message->getSeverity() === ContextualFeedbackSeverity::ERROR) {
323  $jsonArray['hasErrors'] = true;
324  }
325  }
326  }
327 
328  // Set the items that should be removed in the forms view:
329  $removedItems = array_diff($oldItems, $newItems);
330  $jsonArray['compilerInput']['delete'] = $removedItems;
331 
332  $localizedItems = array_diff($newItems, $oldItems);
333  foreach ($localizedItems as $i => $childUid) {
334  $childData = $this->‪compileChild($request, $parentData, $parentFieldName, (int)$childUid, $inlineStackProcessor->getStructure());
335 
336  $childData['inlineParentUid'] = (int)$parent['uid'];
337  $childData['renderType'] = 'inlineRecordContainer';
338  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
339  $childResult = $nodeFactory->create($childData)->render();
340 
341  $jsonArray = $this->‪mergeChildResultIntoJsonResult($jsonArray, $childResult);
342 
343  // Get the name of the field used as foreign selector (if any):
344  $foreignSelector = isset($parentConfig['foreign_selector']) && $parentConfig['foreign_selector'] ? $parentConfig['foreign_selector'] : false;
345  $selectedValue = $foreignSelector ? $childData['databaseRow'][$foreignSelector] : null;
346  if (is_array($selectedValue)) {
347  $selectedValue = $selectedValue[0];
348  }
349 
350  $jsonArray['compilerInput']['localize'][$i] = [
351  'uid' => $childUid,
352  'selectedValue' => $selectedValue,
353  ];
354 
355  // Remove possible virtual records in the form which showed that a child records could be localized:
356  $transOrigPointerFieldName = $childData['processedTca']['ctrl']['transOrigPointerField'];
357  if (isset($childData['databaseRow'][$transOrigPointerFieldName]) && $childData['databaseRow'][$transOrigPointerFieldName]) {
358  $transOrigPointerFieldValue = $childData['databaseRow'][$transOrigPointerFieldName];
359  if (is_array($transOrigPointerFieldValue)) {
360  $transOrigPointerFieldValue = $transOrigPointerFieldValue[0];
361  if (is_array($transOrigPointerFieldValue) && ($transOrigPointerFieldValue['uid'] ?? false)) {
362  // With nested inline containers (eg. fal sys_file_reference), row[l10n_parent][0] is sometimes
363  // a table / row combination again. See tx_styleguide_file file_5. If this happens we
364  // pick the uid field from the array ... Basically, we need the uid of the 'default language' record,
365  // since this is used in JS to locate and remove the 'shadowed' container.
366  // @todo: Find out if this is really necessary that sometimes ['databaseRow']['l10n_parent'][0]
367  // is resolved to a direct uid, and sometimes it's an array with items. Could this be harmonized?
368  $transOrigPointerFieldValue = $transOrigPointerFieldValue['uid'];
369  }
370  }
371  $jsonArray['compilerInput']['localize'][$i]['remove'] = $transOrigPointerFieldValue;
372  }
373  }
374  }
375  return new ‪JsonResponse($jsonArray);
376  }
377 
384  public function ‪expandOrCollapseAction(ServerRequestInterface $request): ResponseInterface
385  {
386  $ajaxArguments = $request->getParsedBody()['ajax'] ?? $request->getQueryParams()['ajax'];
387  [$domObjectId, $expand, $collapse] = $ajaxArguments;
388 
389  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
390  // Parse the DOM identifier (string), add the levels to the structure stack (array), don't load TCA config
391  $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
392 
393  $backendUser = $this->‪getBackendUserAuthentication();
394  // The current table - for this table we should add/import records
395  $currentTable = $inlineStackProcessor->getUnstableStructure();
396  $currentTable = $currentTable['table'];
397  // The top parent table - this table embeds the current table
398  $top = $inlineStackProcessor->getStructureLevel(0);
399  $topTable = $top['table'];
400  $topUid = $top['uid'];
401  $inlineView = $this->‪getInlineExpandCollapseStateArray();
402  // Only do some action if the top record and the current record were saved before
404  $expandUids = ‪GeneralUtility::trimExplode(',', $expand);
405  $collapseUids = ‪GeneralUtility::trimExplode(',', $collapse);
406  // Set records to be expanded
407  foreach ($expandUids as ‪$uid) {
408  $inlineView[$topTable][$topUid][$currentTable][] = ‪$uid;
409  }
410  // Set records to be collapsed
411  foreach ($collapseUids as ‪$uid) {
412  $inlineView[$topTable][$topUid][$currentTable] = $this->‪removeFromArray($uid, $inlineView[$topTable][$topUid][$currentTable]);
413  }
414  // Save states back to database
415  if (is_array($inlineView[$topTable][$topUid][$currentTable])) {
416  $inlineView[$topTable][$topUid][$currentTable] = array_unique($inlineView[$topTable][$topUid][$currentTable]);
417  $backendUser->uc['inlineView'] = json_encode($inlineView);
418  $backendUser->writeUC();
419  }
420  }
421  return new ‪JsonResponse([]);
422  }
423 
436  protected function ‪compileChild(ServerRequestInterface $request, array $parentData, $parentFieldName, $childUid, array $inlineStructure)
437  {
438  $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config'];
439 
440  $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
441  $inlineStackProcessor->initializeByGivenStructure($inlineStructure);
442  $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
443 
444  // @todo: do not use stack processor here ...
445  $child = $inlineStackProcessor->getUnstableStructure();
446  $childTableName = $child['table'];
447 
448  $formDataCompilerInput = [
449  'request' => $request,
450  'command' => 'edit',
451  'tableName' => $childTableName,
452  'vanillaUid' => (int)$childUid,
453  'returnUrl' => $parentData['returnUrl'],
454  'isInlineChild' => true,
455  'inlineStructure' => $inlineStructure,
456  'inlineFirstPid' => $parentData['inlineFirstPid'],
457  'inlineParentConfig' => $parentConfig,
458  'isInlineAjaxOpeningContext' => true,
459 
460  // values of the current parent element
461  // it is always a string either an id or new...
462  'inlineParentUid' => $parentData['databaseRow']['uid'] ?? $parentData['uid'],
463  'inlineParentTableName' => $parentData['tableName'],
464  'inlineParentFieldName' => $parentFieldName,
465 
466  // values of the top most parent element set on first level and not overridden on following levels
467  'inlineTopMostParentUid' => $inlineTopMostParent['uid'],
468  'inlineTopMostParentTableName' => $inlineTopMostParent['table'],
469  'inlineTopMostParentFieldName' => $inlineTopMostParent['field'],
470  ];
471  // For foreign_selector with useCombination $mainChild is the mm record
472  // and $combinationChild is the child-child. For "normal" relations, $mainChild
473  // is just the normal child record and $combinationChild is empty.
474  $mainChild = $this->formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(TcaDatabaseRecord::class));
475  if (($parentConfig['foreign_selector'] ?? false) && ($parentConfig['appearance']['useCombination'] ?? false)) {
476  // This kicks in if opening an existing mainChild that has a child-child set
477  $mainChild['combinationChild'] = $this->‪compileChildChild($request, $mainChild, $parentConfig, $inlineStructure);
478  }
479  return $mainChild;
480  }
481 
491  protected function ‪compileChildChild(ServerRequestInterface $request, array $child, array $parentConfig, array $inlineStructure)
492  {
493  // foreign_selector on intermediate is probably type=select, so data provider of this table resolved that to the uid already
494  $childChildUid = $child['databaseRow'][$parentConfig['foreign_selector']][0];
495  $formDataCompilerInput = [
496  'request' => $request,
497  'command' => 'edit',
498  'tableName' => $this->‪getChildChildTableName($parentConfig['foreign_selector'] ?? '', $child),
499  'vanillaUid' => (int)$childChildUid,
500  'isInlineChild' => true,
501  'isInlineAjaxOpeningContext' => true,
502  // @todo: this is the wrong inline structure, isn't it? Shouldn't contain it the part from child child, too?
503  'inlineStructure' => $inlineStructure,
504  'inlineFirstPid' => $child['inlineFirstPid'],
505  // values of the top most parent element set on first level and not overridden on following levels
506  'inlineTopMostParentUid' => $child['inlineTopMostParentUid'],
507  'inlineTopMostParentTableName' => $child['inlineTopMostParentTableName'],
508  'inlineTopMostParentFieldName' => $child['inlineTopMostParentFieldName'],
509  ];
510  return $this->formDataCompiler->compile($formDataCompilerInput, GeneralUtility::makeInstance(TcaDatabaseRecord::class));
511  }
512 
521  protected function ‪mergeChildResultIntoJsonResult(array $jsonResult, array $childResult)
522  {
524  $scriptItems = $jsonResult['scriptItems'];
525 
526  $jsonResult['data'] .= $childResult['html'];
527  $jsonResult['stylesheetFiles'] = [];
528  foreach ($childResult['stylesheetFiles'] as $stylesheetFile) {
529  $jsonResult['stylesheetFiles'][] = $this->‪getRelativePathToStylesheetFile($stylesheetFile);
530  }
531  if (!empty($childResult['inlineData'])) {
532  $jsonResult['inlineData'] = $childResult['inlineData'];
533  }
534  if (!empty($childResult['additionalInlineLanguageLabelFiles'])) {
535  $labels = [];
536  foreach ($childResult['additionalInlineLanguageLabelFiles'] as $additionalInlineLanguageLabelFile) {
537  ArrayUtility::mergeRecursiveWithOverrule(
538  $labels,
539  $this->‪getLabelsFromLocalizationFile($additionalInlineLanguageLabelFile)
540  );
541  }
542  $scriptItems->addGlobalAssignment(['TYPO3' => ['lang' => $labels]]);
543  }
544  $this->‪addJavaScriptModulesToJavaScriptItems($childResult['javaScriptModules'] ?? [], $scriptItems);
545 
546  return $jsonResult;
547  }
548 
557  protected function ‪getInlineRelatedRecordsUidArray($itemList)
558  {
559  $itemArray = ‪GeneralUtility::trimExplode(',', $itemList, true);
560  // Perform modification of the selected items array:
561  foreach ($itemArray as &$value) {
562  $parts = explode('|', $value, 2);
563  $value = $parts[0];
564  }
565  unset($value);
566  return $itemArray;
567  }
568 
575  {
576  $backendUser = $this->‪getBackendUserAuthentication();
577  if (!$this->‪backendUserHasUcInlineView($backendUser)) {
578  return [];
579  }
580 
581  $inlineView = json_decode($backendUser->uc['inlineView'], true);
582  if (!is_array($inlineView)) {
583  $inlineView = [];
584  }
585 
586  return $inlineView;
587  }
588 
596  {
597  return !empty($backendUser->uc['inlineView']);
598  }
599 
608  protected function ‪removeFromArray($needle, $haystack, $strict = false)
609  {
610  $pos = array_search($needle, $haystack, $strict);
611  if ($pos !== false) {
612  unset($haystack[$pos]);
613  }
614  return $haystack;
615  }
616 
623  protected function ‪getInlineFirstPidFromDomObjectId(string $domObjectId)
624  {
625  // Substitute FlexForm addition and make parsing a bit easier
626  $domObjectId = str_replace('---', ':', $domObjectId);
627  // The starting pattern of an object identifier (e.g. "data-<firstPidValue>-<anything>)
628  $pattern = '/^data-(.+?)-(.+)$/';
629  if (preg_match($pattern, $domObjectId, $match)) {
630  return $match[1];
631  }
632  return null;
633  }
634 
641  protected function ‪extractSignedParentConfigFromRequest(string $contextString): array
642  {
643  if ($contextString === '') {
644  throw new \RuntimeException('Empty context string given', 1489751361);
645  }
646  $context = json_decode($contextString, true);
647  if (empty($context['config'])) {
648  throw new \RuntimeException('Empty context config section given', 1489751362);
649  }
650  if (!hash_equals(‪GeneralUtility::hmac((string)$context['config'], 'InlineContext'), (string)$context['hmac'])) {
651  throw new \RuntimeException('Hash does not validate', 1489751363);
652  }
653  return json_decode($context['config'], true);
654  }
655 
660  protected function ‪getChildChildTableName(string $foreignSelector, array $childConfiguration): string
661  {
662  $config = $childConfiguration['processedTca']['columns'][$foreignSelector]['config'] ?? [];
663  $type = $config['type'] ?? '';
664 
665  return match ($type) {
666  'select' => $config['foreign_table'] ?? '',
667  'group' => ‪GeneralUtility::trimExplode(',', $config['allowed'] ?? '', true)[0] ?? '',
668  default => '',
669  };
670  }
671 
673  {
674  return ‪$GLOBALS['BE_USER'];
675  }
676 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:94
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\expandOrCollapseAction
‪ResponseInterface expandOrCollapseAction(ServerRequestInterface $request)
Definition: FormInlineAjaxController.php:384
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController
Definition: FormInlineAjaxController.php:42
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\backendUserHasUcInlineView
‪bool backendUserHasUcInlineView(BackendUserAuthentication $backendUser)
Definition: FormInlineAjaxController.php:595
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\detailsAction
‪detailsAction(ServerRequestInterface $request)
Definition: FormInlineAjaxController.php:162
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\compileChild
‪array compileChild(ServerRequestInterface $request, array $parentData, $parentFieldName, $childUid, array $inlineStructure)
Definition: FormInlineAjaxController.php:436
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\extractSignedParentConfigFromRequest
‪extractSignedParentConfigFromRequest(string $contextString)
Definition: FormInlineAjaxController.php:641
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Controller\AbstractFormEngineAjaxController\getLabelsFromLocalizationFile
‪array getLabelsFromLocalizationFile(string $file)
Definition: AbstractFormEngineAjaxController.php:78
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\synchronizeLocalizeAction
‪ResponseInterface synchronizeLocalizeAction(ServerRequestInterface $request)
Definition: FormInlineAjaxController.php:229
‪TYPO3\CMS\Backend\Controller\AbstractFormEngineAjaxController\getRelativePathToStylesheetFile
‪string getRelativePathToStylesheetFile(string $stylesheetFile)
Definition: AbstractFormEngineAjaxController.php:59
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\createAction
‪createAction(ServerRequestInterface $request)
Definition: FormInlineAjaxController.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:474
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\__construct
‪__construct(private readonly FormDataCompiler $formDataCompiler,)
Definition: FormInlineAjaxController.php:43
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:61
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\getInlineExpandCollapseStateArray
‪array getInlineExpandCollapseStateArray()
Definition: FormInlineAjaxController.php:574
‪TYPO3\CMS\Backend\Controller\AbstractFormEngineAjaxController\addJavaScriptModulesToJavaScriptItems
‪addJavaScriptModulesToJavaScriptItems(array $modules, JavaScriptItems $items)
Definition: AbstractFormEngineAjaxController.php:36
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:40
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\removeFromArray
‪array removeFromArray($needle, $haystack, $strict=false)
Definition: FormInlineAjaxController.php:608
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\getInlineRelatedRecordsUidArray
‪array getInlineRelatedRecordsUidArray($itemList)
Definition: FormInlineAjaxController.php:557
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\mergeChildResultIntoJsonResult
‪array mergeChildResultIntoJsonResult(array $jsonResult, array $childResult)
Definition: FormInlineAjaxController.php:521
‪TYPO3\CMS\Backend\Controller\AbstractFormEngineAjaxController
Definition: AbstractFormEngineAjaxController.php:35
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\getBackendUserAuthentication
‪getBackendUserAuthentication()
Definition: FormInlineAjaxController.php:672
‪TYPO3\CMS\Backend\Form\InlineStackProcessor
Definition: InlineStackProcessor.php:32
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\getInlineFirstPidFromDomObjectId
‪int string null getInlineFirstPidFromDomObjectId(string $domObjectId)
Definition: FormInlineAjaxController.php:623
‪TYPO3\CMS\Backend\Form\FormDataCompiler
Definition: FormDataCompiler.php:26
‪TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord
Definition: TcaDatabaseRecord.php:25
‪TYPO3\CMS\Backend\Controller
Definition: AboutController.php:18
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\getChildChildTableName
‪getChildChildTableName(string $foreignSelector, array $childConfiguration)
Definition: FormInlineAjaxController.php:660
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Backend\Controller\FormInlineAjaxController\compileChildChild
‪array compileChildChild(ServerRequestInterface $request, array $child, array $parentConfig, array $inlineStructure)
Definition: FormInlineAjaxController.php:491