2 declare(strict_types = 1);
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
46 $GLOBALS[
'TCA'] = array_merge(
$GLOBALS[
'TCA'], GeneralUtility::makeInstance(SiteTcaConfiguration::class)->getTca());
58 $ajaxArguments = $request->getParsedBody()[
'ajax'] ?? $request->getQueryParams()[
'ajax'];
60 $domObjectId = $ajaxArguments[0];
62 $childChildUid =
null;
64 $childChildUid = (int)$ajaxArguments[1];
67 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
68 $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
69 $inlineStackProcessor->injectAjaxConfiguration($parentConfig);
70 $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
72 $parent = $inlineStackProcessor->getStructureLevel(-1);
74 $child = $inlineStackProcessor->getUnstableStructure();
77 $childVanillaUid = -1 * abs((
int)$child[
'uid']);
80 $childVanillaUid = (int)$inlineFirstPid;
82 $childTableName = $parentConfig[
'foreign_table'];
84 $defaultDatabaseRow = [];
85 if ($childTableName ===
'site_language') {
87 if ($childChildUid > 0) {
88 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'sys_language');
89 $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
90 $row = $queryBuilder->select(
'*')->from(
'sys_language')
91 ->where($queryBuilder->expr()->eq(
'uid', $queryBuilder->createNamedParameter($childChildUid, \PDO::PARAM_INT)))
94 throw new \RuntimeException(
'Referenced sys_language row not found', 1521783937);
96 if (!empty($row[
'language_isocode'])) {
97 $defaultDatabaseRow[
'iso-639-1'] = $row[
'language_isocode'];
98 $defaultDatabaseRow[
'base'] =
'/' . $row[
'language_isocode'] .
'/';
100 if (!empty($row[
'flag']) && $row[
'flag'] ===
'multiple') {
101 $defaultDatabaseRow[
'flag'] =
'global';
102 } elseif (!empty($row)) {
103 $defaultDatabaseRow[
'flag'] = $row[
'flag'];
105 if (!empty($row[
'title'])) {
106 $defaultDatabaseRow[
'title'] = $row[
'title'];
111 $formDataGroup = GeneralUtility::makeInstance(SiteConfigurationDataGroup::class);
112 $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
113 $formDataCompilerInput = [
115 'tableName' => $childTableName,
116 'vanillaUid' => $childVanillaUid,
117 'databaseRow' => $defaultDatabaseRow,
118 'isInlineChild' =>
true,
119 'inlineStructure' => $inlineStackProcessor->getStructure(),
120 'inlineFirstPid' => $inlineFirstPid,
121 'inlineParentUid' => $parent[
'uid'],
122 'inlineParentTableName' => $parent[
'table'],
123 'inlineParentFieldName' => $parent[
'field'],
124 'inlineParentConfig' => $parentConfig,
125 'inlineTopMostParentUid' => $inlineTopMostParent[
'uid'],
126 'inlineTopMostParentTableName' => $inlineTopMostParent[
'table'],
127 'inlineTopMostParentFieldName' => $inlineTopMostParent[
'field'],
129 if ($childChildUid) {
130 $formDataCompilerInput[
'inlineChildChildUid'] = $childChildUid;
132 $childData = $formDataCompiler->compile($formDataCompilerInput);
134 if ($parentConfig[
'foreign_selector'] && $parentConfig[
'appearance'][
'useCombination']) {
135 throw new \RuntimeException(
'useCombination not implemented in sites module', 1522493094);
138 $childData[
'inlineParentUid'] = (int)$parent[
'uid'];
139 $childData[
'renderType'] =
'inlineRecordContainer';
140 $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
141 $childResult = $nodeFactory->create($childData)->render();
145 'stylesheetFiles' => [],
150 $objectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
151 $objectPrefix = $objectName .
'-' . $child[
'table'];
152 $objectId = $objectPrefix .
'-' . $childData[
'databaseRow'][
'uid'];
153 $expandSingle = $parentConfig[
'appearance'][
'expandSingle'];
154 if (!$child[
'uid']) {
155 $jsonArray[
'scriptCall'][] =
'inline.domAddNewRecord(\'bottom\',' . GeneralUtility::quoteJSvalue($objectName .
'_records') .
',' . GeneralUtility::quoteJSvalue($objectPrefix) .
',json.data);';
156 $jsonArray[
'scriptCall'][] =
'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($objectPrefix) .
',' . GeneralUtility::quoteJSvalue($childData[
'databaseRow'][
'uid']) .
',null,' . GeneralUtility::quoteJSvalue($childChildUid) .
');';
158 $jsonArray[
'scriptCall'][] =
'inline.domAddNewRecord(\'after\',' . GeneralUtility::quoteJSvalue($domObjectId .
'_div') .
',' . GeneralUtility::quoteJSvalue($objectPrefix) .
',json.data);';
159 $jsonArray[
'scriptCall'][] =
'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($objectPrefix) .
',' . GeneralUtility::quoteJSvalue($childData[
'databaseRow'][
'uid']) .
',' . GeneralUtility::quoteJSvalue($child[
'uid']) .
',' . GeneralUtility::quoteJSvalue($childChildUid) .
');';
162 if ($parentConfig[
'appearance'][
'useSortable']) {
163 $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
164 $jsonArray[
'scriptCall'][] =
'inline.createDragAndDropSorting(' . GeneralUtility::quoteJSvalue($inlineObjectName .
'_records') .
');';
166 if (!$parentConfig[
'appearance'][
'collapseAll'] && $expandSingle) {
167 $jsonArray[
'scriptCall'][] =
'inline.collapseAllRecords(' . GeneralUtility::quoteJSvalue($objectId) .
',' . GeneralUtility::quoteJSvalue($objectPrefix) .
',' . GeneralUtility::quoteJSvalue($childData[
'databaseRow'][
'uid']) .
');';
170 $jsonArray[
'scriptCall'][] =
'inline.fadeOutFadeIn(' . GeneralUtility::quoteJSvalue($objectId .
'_div') .
');';
184 $ajaxArguments = $request->getParsedBody()[
'ajax'] ?? $request->getQueryParams()[
'ajax'];
186 $domObjectId = $ajaxArguments[0];
191 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
192 $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
193 $inlineStackProcessor->injectAjaxConfiguration($parentConfig);
196 $parent = $inlineStackProcessor->getStructureLevel(-1);
197 $parentFieldName = $parent[
'field'];
201 $parentConfig[
'renderFieldsOnly'] =
true;
206 $parentFieldName => [
207 'config' => $parentConfig,
211 'tableName' => $parent[
'table'],
212 'inlineFirstPid' => $inlineFirstPid,
217 'returnUrl' => $parentConfig[
'originalReturnUrl'],
221 $child = $inlineStackProcessor->getUnstableStructure();
223 $childData = $this->
compileChild($parentData, $parentFieldName, (
int)$child[
'uid'], $inlineStackProcessor->getStructure());
225 $childData[
'inlineParentUid'] = (int)$parent[
'uid'];
226 $childData[
'renderType'] =
'inlineRecordContainer';
227 $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
228 $childResult = $nodeFactory->create($childData)->render();
232 'stylesheetFiles' => [],
237 $objectPrefix = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid) .
'-' . $child[
'table'];
238 $objectId = $objectPrefix .
'-' . (int)$child[
'uid'];
239 $expandSingle = $parentConfig[
'appearance'][
'expandSingle'];
240 $jsonArray[
'scriptCall'][] =
'inline.domAddRecordDetails(' . GeneralUtility::quoteJSvalue($domObjectId) .
',' . GeneralUtility::quoteJSvalue($objectPrefix) .
',' . ($expandSingle ?
'1' :
'0') .
',json.data);';
241 if ($parentConfig[
'foreign_unique']) {
242 $jsonArray[
'scriptCall'][] =
'inline.removeUsed(' . GeneralUtility::quoteJSvalue($objectPrefix) .
',\'' . (int)$child[
'uid'] .
'\');
';
244 $jsonArray = $this->mergeChildResultIntoJsonResult($jsonArray, $childResult);
245 if ($parentConfig['appearance
']['useSortable
']) {
246 $inlineObjectName = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid);
247 $jsonArray['scriptCall
'][] = 'inline.createDragAndDropSorting(
' . GeneralUtility::quoteJSvalue($inlineObjectName . '_records
') . ');
';
249 if (!$parentConfig['appearance
']['collapseAll
'] && $expandSingle) {
250 $jsonArray['scriptCall
'][] = 'inline.collapseAllRecords(
' . GeneralUtility::quoteJSvalue($objectId) . ',
' . GeneralUtility::quoteJSvalue($objectPrefix) . ',\
'' . (
int)$child[
'uid'] .
'\');
';
253 return new JsonResponse($jsonArray);
269 protected function compileChild(array $parentData, string $parentFieldName, int $childUid, array $inlineStructure): array
271 $parentConfig = $parentData['processedTca
']['columns
'][$parentFieldName]['config
'];
273 $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
274 $inlineStackProcessor->initializeByGivenStructure($inlineStructure);
275 $inlineTopMostParent = $inlineStackProcessor->getStructureLevel(0);
277 // @todo: do not use stack processor here ...
278 $child = $inlineStackProcessor->getUnstableStructure();
279 $childTableName = $child['table
'];
281 $formDataGroup = GeneralUtility::makeInstance(SiteConfigurationDataGroup::class);
282 $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
283 $formDataCompilerInput = [
285 'tableName
' => $childTableName,
286 'vanillaUid
' => (int)$childUid,
287 'returnUrl
' => $parentData['returnUrl
'],
288 'isInlineChild
' => true,
289 'inlineStructure
' => $inlineStructure,
290 'inlineFirstPid
' => $parentData['inlineFirstPid
'],
291 'inlineParentConfig
' => $parentConfig,
292 'isInlineAjaxOpeningContext
' => true,
294 // values of the current parent element
295 // it is always a string either an id or new...
296 'inlineParentUid
' => $parentData['databaseRow
']['uid
'],
297 'inlineParentTableName
' => $parentData['tableName
'],
298 'inlineParentFieldName
' => $parentFieldName,
300 // values of the top most parent element set on first level and not overridden on following levels
301 'inlineTopMostParentUid
' => $inlineTopMostParent['uid
'],
302 'inlineTopMostParentTableName
' => $inlineTopMostParent['table
'],
303 'inlineTopMostParentFieldName
' => $inlineTopMostParent['field
'],
305 if ($parentConfig['foreign_selector
'] && $parentConfig['appearance
']['useCombination
']) {
306 throw new \RuntimeException('useCombination not implemented in sites module
', 1522493095);
308 return $formDataCompiler->compile($formDataCompilerInput);
319 protected function mergeChildResultIntoJsonResult(array $jsonResult, array $childResult): array
321 $jsonResult['data
'] .= $childResult['html
'];
322 $jsonResult['stylesheetFiles
'] = [];
323 foreach ($childResult['stylesheetFiles
'] as $stylesheetFile) {
324 $jsonResult['stylesheetFiles
'][] = $this->getRelativePathToStylesheetFile($stylesheetFile);
326 if (!empty($childResult['inlineData
'])) {
327 $jsonResult['scriptCall
'][] = 'inline.addToDataArray(
' . json_encode($childResult['inlineData
']) . ');
';
329 if (!empty($childResult['additionalJavaScriptSubmit
'])) {
330 $additionalJavaScriptSubmit = implode('', $childResult['additionalJavaScriptSubmit
']);
331 $additionalJavaScriptSubmit = str_replace([CR, LF], '', $additionalJavaScriptSubmit);
332 $jsonResult['scriptCall
'][] = 'TBE_EDITOR.addActionChecks(
"submit",
"' . addslashes($additionalJavaScriptSubmit) . '");
';
334 foreach ($childResult['additionalJavaScriptPost
'] as $singleAdditionalJavaScriptPost) {
335 $jsonResult['scriptCall
'][] = $singleAdditionalJavaScriptPost;
337 if (!empty($childResult['additionalInlineLanguageLabelFiles
'])) {
339 foreach ($childResult['additionalInlineLanguageLabelFiles
'] as $additionalInlineLanguageLabelFile) {
340 ArrayUtility::mergeRecursiveWithOverrule(
342 $this->getLabelsFromLocalizationFile($additionalInlineLanguageLabelFile)
345 $javaScriptCode = [];
346 $javaScriptCode[] = 'if (typeof
TYPO3 === \
'undefined\' || typeof TYPO3.lang === \'undefined\') {';
347 $javaScriptCode[] =
' TYPO3.lang = {}';
348 $javaScriptCode[] =
'}';
349 $javaScriptCode[] =
'var additionalInlineLanguageLabels = ' . json_encode($labels) .
';';
350 $javaScriptCode[] =
'for (var attributeName in additionalInlineLanguageLabels) {';
351 $javaScriptCode[] =
' if (typeof TYPO3.lang[attributeName] === \'undefined\') {';
352 $javaScriptCode[] =
' TYPO3.lang[attributeName] = additionalInlineLanguageLabels[attributeName]';
353 $javaScriptCode[] =
' }';
354 $javaScriptCode[] =
'}';
356 $jsonResult[
'scriptCall'][] = implode(LF, $javaScriptCode);
359 $jsonResult[
'scriptCall'] = array_merge($requireJsModule, $jsonResult[
'scriptCall']);
376 if ($contextString ===
'') {
377 throw new \RuntimeException(
'Empty context string given', 1522771624);
379 $context = json_decode($contextString,
true);
380 if (empty($context[
'config'])) {
381 throw new \RuntimeException(
'Empty context config section given', 1522771632);
383 if (!hash_equals(GeneralUtility::hmac((
string)$context[
'config'],
'InlineContext'), (
string)$context[
'hmac'])) {
384 throw new \RuntimeException(
'Hash does not validate', 1522771640);
386 return json_decode($context[
'config'],
true);
398 $domObjectId = str_replace(
'---',
':', $domObjectId);
400 $pattern =
'/^data' .
'-' .
'(.+?)' .
'-' .
'(.+)$/';
401 if (preg_match($pattern, $domObjectId, $match)) {
402 return (
int)$match[1];