/** * Adds localizations or synchronizes the locations of all child records. * Handle AJAX calls to localize all records of a parent, localize a single record or to synchronize with the original language parent. * * @param ServerRequestInterface $request the incoming request * @param ResponseInterface $response the empty response * @return ResponseInterface the filled response */ public function synchronizeLocalizeAction(ServerRequestInterface $request, ResponseInterface $response) { $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax']; $domObjectId = $ajaxArguments[0]; $type = $ajaxArguments[1]; /** @var InlineStackProcessor $inlineStackProcessor */ $inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class); // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config: $inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId); $inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']); $inlineFirstPid = $this->getInlineFirstPidFromDomObjectId($domObjectId); $jsonArray = false; if ($type === 'localize' || $type === 'synchronize' || MathUtility::canBeInterpretedAsInteger($type)) { // Parent, this table embeds the child table $parent = $inlineStackProcessor->getStructureLevel(-1); $parentFieldName = $parent['field']; // Child, a record from this table should be rendered $child = $inlineStackProcessor->getUnstableStructure(); $formDataCompilerInputForParent = ['vanillaUid' => (int) $parent['uid'], 'command' => 'edit', 'tableName' => $parent['table'], 'databaseRow' => ['uid' => (int) $parent['uid']], 'inlineFirstPid' => $inlineFirstPid, 'columnsToProcess' => [$parentFieldName], 'inlineStructure' => $inlineStackProcessor->getStructure(), 'inlineCompileExistingChildren' => false]; /** @var TcaDatabaseRecord $formDataGroup */ $formDataGroup = GeneralUtility::makeInstance(InlineParentRecord::class); /** @var FormDataCompiler $formDataCompiler */ $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup); $parentData = $formDataCompiler->compile($formDataCompilerInputForParent); $parentConfig = $parentData['processedTca']['columns'][$parentFieldName]['config']; $oldItemList = $parentData['databaseRow'][$parentFieldName]; $cmd = array(); $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = $parent['field'] . ',' . $type; /** @var $tce DataHandler */ $tce = GeneralUtility::makeInstance(DataHandler::class); $tce->stripslashes_values = false; $tce->start(array(), $cmd); $tce->process_cmdmap(); $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parentFieldName]; $jsonArray = array('data' => '', 'stylesheetFiles' => [], 'scriptCall' => []); $nameObject = $inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid); $nameObjectForeignTable = $nameObject . '-' . $child['table']; $oldItems = FormEngineUtility::getInlineRelatedRecordsUidArray($oldItemList); $newItems = FormEngineUtility::getInlineRelatedRecordsUidArray($newItemList); // Set the items that should be removed in the forms view: $removedItems = array_diff($oldItems, $newItems); foreach ($removedItems as $childUid) { $jsonArray['scriptCall'][] = 'inline.deleteRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $childUid) . ', {forceDirectRemoval: true});'; } $localizedItems = array_diff($newItems, $oldItems); foreach ($localizedItems as $childUid) { $childData = $this->compileChild($parentData, $parentFieldName, (int) $childUid); $childData['inlineParentUid'] = (int) $parent['uid']; // @todo: needed? $childData['inlineStructure'] = $inlineStackProcessor->getStructure(); // @todo: needed? $childData['inlineExpandCollapseStateArray'] = $parentData['inlineExpandCollapseStateArray']; $childData['renderType'] = 'inlineRecordContainer'; $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class); $childResult = $nodeFactory->create($childData)->render(); $jsonArray = $this->mergeChildResultIntoJsonResult($jsonArray, $childResult); // Get the name of the field used as foreign selector (if any): $foreignSelector = isset($parentConfig['foreign_selector']) && $parentConfig['foreign_selector'] ? $parentConfig['foreign_selector'] : false; $selectedValue = $foreignSelector ? GeneralUtility::quoteJSvalue($childData['databaseRow'][$foreignSelector]) : 'null'; if (is_array($selectedValue)) { $selectedValue = $selectedValue[0]; } $jsonArray['scriptCall'][] = 'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', ' . GeneralUtility::quoteJSvalue($childUid) . ', null, ' . $selectedValue . ');'; // Remove possible virtual records in the form which showed that a child records could be localized: $transOrigPointerFieldName = $GLOBALS['TCA'][$childData['table']]['ctrl']['transOrigPointerField']; if (isset($childData['databaseRow'][$transOrigPointerFieldName]) && $childData['databaseRow'][$transOrigPointerFieldName]) { $transOrigPointerField = $childData['databaseRow'][$transOrigPointerFieldName]; if (is_array($transOrigPointerField)) { $transOrigPointerField = $transOrigPointerField[0]; } $jsonArray['scriptCall'][] = 'inline.fadeAndRemove(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $transOrigPointerField . '_div') . ');'; } if (!empty($childResult['html'])) { array_unshift($jsonArray['scriptCall'], 'inline.domAddNewRecord(\'bottom\', ' . GeneralUtility::quoteJSvalue($nameObject . '_records') . ', ' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', json.data);'); } } } $response->getBody()->write(json_encode($jsonArray)); return $response; }
/** * Gets the related records of the embedding item, this could be 1:n, m:n. * * @param string $table The table name of the record * @param string $itemList The list of related child records * @return array The records related to the parent item */ protected function getRelatedRecordsArray($table, $itemList) { $records = array(); $itemArray = FormEngineUtility::getInlineRelatedRecordsUidArray($itemList); // Perform modification of the selected items array: foreach ($itemArray as $uid) { // Get the records for this uid using \TYPO3\CMS\Backend\Form\DataPreprocessor if ($record = $this->getRecord($table, $uid)) { $records[$uid] = $record; } } return $records; }
/** * Handle AJAX calls to localize all records of a parent, localize a single record or to synchronize with the original language parent. * * @param string $type Defines the type 'localize' or 'synchronize' (string) or a single uid to be localized (int) * @param int $inlineFirstPid Inline first pid * @return array An array to be used for JSON */ protected function renderInlineSynchronizeLocalizeRecords($type, $inlineFirstPid) { $jsonArray = FALSE; if (GeneralUtility::inList('localize,synchronize', $type) || MathUtility::canBeInterpretedAsInteger($type)) { $inlineRelatedRecordResolver = GeneralUtility::makeInstance(InlineRelatedRecordResolver::class); // The parent level: $parent = $this->inlineStackProcessor->getStructureLevel(-1); $current = $this->inlineStackProcessor->getUnstableStructure(); $parentRecord = $inlineRelatedRecordResolver->getRecord($parent['table'], $parent['uid']); $cmd = array(); $cmd[$parent['table']][$parent['uid']]['inlineLocalizeSynchronize'] = $parent['field'] . ',' . $type; /** @var $tce \TYPO3\CMS\Core\DataHandling\DataHandler */ $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class); $tce->stripslashes_values = FALSE; $tce->start(array(), $cmd); $tce->process_cmdmap(); $oldItemList = $parentRecord[$parent['field']]; $newItemList = $tce->registerDBList[$parent['table']][$parent['uid']][$parent['field']]; $jsonArray = array('scriptCall' => array()); $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($inlineFirstPid); $nameObjectForeignTable = $nameObject . '-' . $current['table']; // Get the name of the field pointing to the original record: $transOrigPointerField = $GLOBALS['TCA'][$current['table']]['ctrl']['transOrigPointerField']; // Get the name of the field used as foreign selector (if any): $foreignSelector = isset($parent['config']['foreign_selector']) && $parent['config']['foreign_selector'] ? $parent['config']['foreign_selector'] : FALSE; // Convert lists to array with uids of child records: $oldItems = FormEngineUtility::getInlineRelatedRecordsUidArray($oldItemList); $newItems = FormEngineUtility::getInlineRelatedRecordsUidArray($newItemList); // Determine the items that were localized or localized: $removedItems = array_diff($oldItems, $newItems); $localizedItems = array_diff($newItems, $oldItems); // Set the items that should be removed in the forms view: foreach ($removedItems as $item) { $jsonArray['scriptCall'][] = 'inline.deleteRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $item) . ', {forceDirectRemoval: true});'; } // Set the items that should be added in the forms view: $html = ''; $resultArray = NULL; // @todo: This should be another container ... foreach ($localizedItems as $item) { $row = $inlineRelatedRecordResolver->getRecord($current['table'], $item); $selectedValue = $foreignSelector ? GeneralUtility::quoteJSvalue($row[$foreignSelector]) : 'null'; $options = $this->getConfigurationOptionsForChildElements(); $options['databaseRow'] = array('uid' => $parent['uid']); $options['inlineFirstPid'] = $inlineFirstPid; $options['inlineRelatedRecordToRender'] = $row; $options['inlineRelatedRecordConfig'] = $parent['config']; $options['inlineStructure'] = $this->inlineStackProcessor->getStructure(); $options['isAjaxContext'] = TRUE; $options['renderType'] = 'inlineRecordContainer'; $childArray = $this->nodeFactory->create($options)->render(); $html .= $childArray['html']; $childArray['html'] = ''; // @todo: Obsolete if a container and copied from AbstractContainer for now if ($resultArray === NULL) { $resultArray = $childArray; } else { if (!empty($childArray['extJSCODE'])) { $resultArray['extJSCODE'] .= LF . $childArray['extJSCODE']; } foreach ($childArray['additionalJavaScriptPost'] as $value) { $resultArray['additionalJavaScriptPost'][] = $value; } foreach ($childArray['additionalJavaScriptSubmit'] as $value) { $resultArray['additionalJavaScriptSubmit'][] = $value; } if (!empty($childArray['inlineData'])) { $resultArrayInlineData = $resultArray['inlineData']; $childInlineData = $childArray['inlineData']; ArrayUtility::mergeRecursiveWithOverrule($resultArrayInlineData, $childInlineData); $resultArray['inlineData'] = $resultArrayInlineData; } } $jsonArray['scriptCall'][] = 'inline.memorizeAddRecord(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', ' . GeneralUtility::quoteJSvalue($item) . ', null, ' . $selectedValue . ');'; // Remove possible virtual records in the form which showed that a child records could be localized: if (isset($row[$transOrigPointerField]) && $row[$transOrigPointerField]) { $jsonArray['scriptCall'][] = 'inline.fadeAndRemove(' . GeneralUtility::quoteJSvalue($nameObjectForeignTable . '-' . $row[$transOrigPointerField] . '_div') . ');'; } } if (!empty($html)) { $jsonArray['data'] = $html; array_unshift($jsonArray['scriptCall'], 'inline.domAddNewRecord(\'bottom\', ' . GeneralUtility::quoteJSvalue($nameObject . '_records') . ', ' . GeneralUtility::quoteJSvalue($nameObjectForeignTable) . ', json.data);'); } $this->mergeResult($resultArray); $jsonArray = $this->getInlineAjaxCommonScriptCalls($jsonArray, $parent['config'], $inlineFirstPid); } return $jsonArray; }