Commit 5099297e authored by Oliver Hader's avatar Oliver Hader Committed by Susanne Moog
Browse files

[TASK] Enhance DataHandler translation and synchronization handling

* add more tests for allowLanguageSynchronization & l10n_mode=exclude
* enhance inline children synchronization for non-translatable tables
  using $GLOBALS['TCA'][$tableName]['ctrl']['origUid'] if defined
* bugfix for copying records instead of localizing them - the target
  page-id was missing
* integrate handling of l10n_mode=prefixLangTitle for cloning data
  in memory - DataHandler hooks are not process in that regard

Resolves: #79856
Releases: master
Change-Id: I7f5ef7edba9e80deba6025fe1394d10ee8e2d78c
Reviewed-on: https://review.typo3.org/51714


Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <typo3@scripting-base.de>
Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
parent b8f7bae5
......@@ -25,6 +25,7 @@ use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Lang\LanguageService;
/**
* This processor analyses the provided data-map before actually being process
......@@ -402,17 +403,22 @@ class DataMapProcessor
* @param string $fieldName
* @param array $fromRecord
* @param array $forRecord
* @throws \RuntimeException
*/
protected function synchronizeInlineRelations(DataMapItem $item, string $fieldName, array $fromRecord, array $forRecord)
{
$fromId = $fromRecord['uid'];
$configuration = $GLOBALS['TCA'][$item->getFromTableName()]['columns'][$fieldName];
$isLocalizationModeExclude = ($configuration['l10n_mode'] ?? null) === 'exclude';
$foreignTableName = $configuration['config']['foreign_table'];
$manyToManyTable = ($configuration['config']['MM'] ?? '');
$languageFieldName = ($GLOBALS['TCA'][$foreignTableName]['ctrl']['languageField'] ?? null);
$parentFieldName = ($GLOBALS['TCA'][$foreignTableName]['ctrl']['transOrigPointerField'] ?? null);
$sourceFieldName = ($GLOBALS['TCA'][$foreignTableName]['ctrl']['translationSource'] ?? null);
$fieldNames = [
'language' => ($GLOBALS['TCA'][$foreignTableName]['ctrl']['languageField'] ?? null),
'parent' => ($GLOBALS['TCA'][$foreignTableName]['ctrl']['transOrigPointerField'] ?? null),
'source' => ($GLOBALS['TCA'][$foreignTableName]['ctrl']['translationSource'] ?? null),
];
$isTranslatable = (!empty($fieldNames['language']) && !empty($fieldNames['parent']));
// determine suggested elements of either translation parent or source record
// from data-map, in case the accordant language parent/source record was modified
......@@ -463,19 +469,19 @@ class DataMapProcessor
$createAncestorIds = $this->filterNumericIds($missingAncestorIds, true);
// non-persisted elements that should be duplicated in data-map directly
$populateAncestorIds = $this->filterNumericIds($missingAncestorIds, false);
// this desired state map defines the final result of child elements of the translation
$desiredLocalizationIdMap = array_combine($suggestedAncestorIds, $suggestedAncestorIds);
// this desired state map defines the final result of child elements in their parent translation
$desiredIdMap = array_combine($suggestedAncestorIds, $suggestedAncestorIds);
// update existing translations in the desired state map
foreach ($dependentIdMap as $ancestorId => $translationId) {
if (isset($desiredLocalizationIdMap[$ancestorId])) {
$desiredLocalizationIdMap[$ancestorId] = $translationId;
if (isset($desiredIdMap[$ancestorId])) {
$desiredIdMap[$ancestorId] = $translationId;
}
}
// nothing to synchronize, but element order could have been changed
if (empty($removeAncestorIds) && empty($missingAncestorIds)) {
$this->dataMap[$item->getTableName()][$item->getId()][$fieldName] = implode(
',',
array_values($desiredLocalizationIdMap)
array_values($desiredIdMap)
);
return;
}
......@@ -490,8 +496,8 @@ class DataMapProcessor
}
foreach ($createAncestorIds as $createAncestorId) {
// if child table is not aware of localization, just copy
if (empty($languageFieldName) || empty($parentFieldName)) {
$localCommandMap[$foreignTableName][$createAncestorId]['copy'] = true;
if ($isLocalizationModeExclude || !$isTranslatable) {
$localCommandMap[$foreignTableName][$createAncestorId]['copy'] = -$createAncestorId;
// otherwise, trigger the localization process
} else {
$localCommandMap[$foreignTableName][$createAncestorId]['localize'] = $item->getLanguage();
......@@ -505,33 +511,37 @@ class DataMapProcessor
// update copied or localized ids
foreach ($createAncestorIds as $createAncestorId) {
if (empty($localDataHandler->copyMappingArray_merged[$foreignTableName][$createAncestorId])) {
throw new \RuntimeException('Child record was not processed', 1486233164);
$additionalInformation = '';
if (!empty($localDataHandler->errorLog)) {
$additionalInformation = ', reason "'
. implode(', ', $localDataHandler->errorLog) . '"';
}
throw new \RuntimeException(
'Child record was not processed' . $additionalInformation,
1486233164);
}
$newLocalizationId = $localDataHandler->copyMappingArray_merged[$foreignTableName][$createAncestorId];
$newLocalizationId = $localDataHandler->getAutoVersionId($foreignTableName, $newLocalizationId) ?? $newLocalizationId;
$desiredLocalizationIdMap[$createAncestorId] = $newLocalizationId;
$desiredIdMap[$createAncestorId] = $newLocalizationId;
}
}
// populate new child records in data-map
if (!empty($populateAncestorIds)) {
foreach ($populateAncestorIds as $populateId) {
foreach ($populateAncestorIds as $populateAncestorId) {
$newLocalizationId = StringUtility::getUniqueId('NEW');
$desiredLocalizationIdMap[$populateId] = $newLocalizationId;
// @todo l10n_mode=prefixLangTitle is not applied to this "in-memory translation"
$this->dataMap[$foreignTableName][$newLocalizationId] = $this->dataMap[$foreignTableName][$populateId];
$this->dataMap[$foreignTableName][$newLocalizationId][$languageFieldName] = $item->getLanguage();
// @todo Only $populatedIs used in TCA type 'select' is resolved in DataHandler's remapStack
$this->dataMap[$foreignTableName][$newLocalizationId][$parentFieldName] = $populateId;
if ($sourceFieldName !== null) {
// @todo Not sure, whether $populateId is resolved in DataHandler's remapStack
$this->dataMap[$foreignTableName][$newLocalizationId][$sourceFieldName] = $populateId;
}
$desiredIdMap[$populateAncestorId] = $newLocalizationId;
$this->dataMap[$foreignTableName][$newLocalizationId] = $this->duplicateFromDataMap(
$foreignTableName,
$populateAncestorId,
$item->getLanguage(),
$fieldNames
);
}
}
// update inline parent field references - required to update pointer fields
$this->dataMap[$item->getTableName()][$item->getId()][$fieldName] = implode(
',',
array_values($desiredLocalizationIdMap)
array_values($desiredIdMap)
);
}
......@@ -622,8 +632,9 @@ class DataMapProcessor
}
/**
* Fetch dependent records that depend on given record id's in their parent or source field and
* create an id map as further lookup array
* Fetches dependent records that depend on given record id's in in either
* their parent or source field for translatable tables or their origin
* field for non-translatable tables and creates an id mapping.
*
* @param string $tableName
* @param array $ids
......@@ -635,48 +646,67 @@ class DataMapProcessor
$tableName = 'pages_language_overlay';
}
if (!BackendUtility::isTableLocalizable($tableName)) {
$isTranslatable = BackendUtility::isTableLocalizable($tableName);
$originFieldName = ($GLOBALS['TCA'][$tableName]['ctrl']['origUid'] ?? null);
if (!$isTranslatable && $originFieldName === null) {
return [];
}
$fieldNames = [
'uid' => 'uid',
'l10n_state' => 'l10n_state',
'language' => $GLOBALS['TCA'][$tableName]['ctrl']['languageField'],
'parent' => $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'],
];
if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['translationSource'])) {
$fieldNames['source'] = $GLOBALS['TCA'][$tableName]['ctrl']['translationSource'];
if ($isTranslatable) {
$fieldNames = [
'uid' => 'uid',
'l10n_state' => 'l10n_state',
'language' => $GLOBALS['TCA'][$tableName]['ctrl']['languageField'],
'parent' => $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'],
];
if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['translationSource'])) {
$fieldNames['source'] = $GLOBALS['TCA'][$tableName]['ctrl']['translationSource'];
}
} else {
$fieldNames = [
'uid' => 'uid',
'origin' => $originFieldName,
];
}
$dependentElements = $this->fetchDependentElements($tableName, $ids, $fieldNames);
$dependentIdMap = [];
foreach ($dependentElements as $dependentElement) {
$dependentId = $dependentElement['uid'];
// implicit: use origin pointer if table cannot be translated
if (!$isTranslatable) {
$dependentIdMap[$dependentElement[$fieldNames['origin']]] = $dependentId;
// implicit: having source value different to parent value, use source pointer
if (
} elseif (
!empty($fieldNames['source'])
&& $dependentElement[$fieldNames['source']] !== $dependentElement[$fieldNames['parent']]
) {
$dependentIdMap[$dependentElement[$fieldNames['source']]] = $dependentElement['uid'];
$dependentIdMap[$dependentElement[$fieldNames['source']]] = $dependentId;
// implicit: otherwise, use parent pointer
} else {
$dependentIdMap[$dependentElement[$fieldNames['parent']]] = $dependentElement['uid'];
$dependentIdMap[$dependentElement[$fieldNames['parent']]] = $dependentId;
}
}
return $dependentIdMap;
}
/**
* Fetch all elements that depend on given record id's in their parent or source field
* Fetch all elements that depend on given record id's in either their
* parent or source field for translatable tables or their origin field
* for non-translatable tables.
*
* @param string $tableName
* @param array $ids
* @param array $fieldNames
* @return array
* @throws \InvalidArgumentException
*/
protected function fetchDependentElements(string $tableName, array $ids, array $fieldNames)
{
$ids = $this->filterNumericIds($ids, true);
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($tableName);
$queryBuilder->getRestrictions()
......@@ -688,24 +718,21 @@ class DataMapProcessor
$ids = array_filter($ids, [MathUtility::class, 'canBeInterpretedAsInteger']);
$idsParameter = $queryBuilder->createNamedParameter($ids, Connection::PARAM_INT_ARRAY);
$predicates = [
$queryBuilder->expr()->in(
$fieldNames['parent'],
$idsParameter
)
];
if (!empty($fieldNames['source'])) {
$predicates[] = $queryBuilder->expr()->in(
$fieldNames['source'],
$idsParameter
);
}
$statement = $queryBuilder
->select(...array_values($fieldNames))
->from($tableName)
->andWhere(
// fetch by language dependency
if (!empty($fieldNames['language']) && !empty($fieldNames['parent'])) {
$ancestorPredicates = [
$queryBuilder->expr()->in(
$fieldNames['parent'],
$idsParameter
)
];
if (!empty($fieldNames['source'])) {
$ancestorPredicates[] = $queryBuilder->expr()->in(
$fieldNames['source'],
$idsParameter
);
}
$predicates = [
// must be any kind of localization
$queryBuilder->expr()->gt(
$fieldNames['language'],
......@@ -717,8 +744,28 @@ class DataMapProcessor
$zeroParameter
),
// any parent or source pointers
$queryBuilder->expr()->orX(...$predicates)
)
$queryBuilder->expr()->orX(...$ancestorPredicates),
];
// fetch by origin dependency ("copied from")
} elseif (!empty($fieldNames['origin'])) {
$predicates = [
$queryBuilder->expr()->eq(
$fieldNames['origin'],
$idsParameter
)
];
// otherwise: stop execution
} else {
throw new \InvalidArgumentException(
'Invalid combination of query field names given',
1487192370
);
}
$statement = $queryBuilder
->select(...array_values($fieldNames))
->from($tableName)
->andWhere(...$predicates)
->execute();
$dependentElements = [];
......@@ -789,6 +836,68 @@ class DataMapProcessor
return $this->items[$tableName . ':' . $id] ?? null;
}
/**
* Duplicates an item from data-map and prefixed language title,
* if applicable for the accordant field name.
*
* @param string $tableName
* @param string|int $id
* @param int $language
* @param array $fieldNames
* @return array
*/
protected function duplicateFromDataMap(string $tableName, $id, int $language, array $fieldNames)
{
$data = $this->dataMap[$tableName][$id];
$isTranslatable = (!empty($fieldNames['language']) && !empty($fieldNames['parent']));
if (empty($language) || !$isTranslatable) {
return $data;
}
$data[$fieldNames['language']] = $language;
// @todo Only $id used in TCA type 'select' is resolved in DataHandler's remapStack
$data[$fieldNames['parent']] = $id;
if (!empty($fieldNames['source'])) {
// @todo Not sure, whether $id is resolved in DataHandler's remapStack
$data[$fieldNames['source']] = $id;
}
$prefixFieldNames = array_intersect(
array_keys($data),
$this->getPrefixLanguageTitleFieldNames($tableName)
);
if (empty($prefixFieldNames)) {
return $data;
}
$languageService = $this->getLanguageService();
$languageRecord = BackendUtility::getRecord('sys_language', $language, 'title');
list($pageId) = BackendUtility::getTSCpid($tableName, $id, $data['pid'] ?? null);
$TSconfig = $this->backendUser->getTSConfig(
'TCEMAIN',
BackendUtility::getPagesTSconfig($pageId)
);
if (!empty($TSconfig['translateToMessage'])) {
$prefix = $TSconfig['translateToMessage'];
if ($languageService !== null) {
$prefix = $languageService->sL($prefix);
}
$prefix = sprintf($prefix, $languageRecord['title']);
}
if (empty($prefix)) {
$prefix = 'Translate to ' . $languageRecord['title'] . ':';
}
foreach ($prefixFieldNames as $prefixFieldName) {
// @todo The hook in DataHandler is not applied here
$data[$prefixFieldName] = '[' . $prefix . '] ' . $data[$prefixFieldName];
}
return $data;
}
/**
* Field names we have to deal with
*
......@@ -841,6 +950,36 @@ class DataMapProcessor
return $localizationExcludeFieldNames;
}
/**
* Field names of TCA table with columns having l10n_mode=prefixLangTitle
*
* @param string $tableName
* @return array
*/
protected function getPrefixLanguageTitleFieldNames(string $tableName)
{
if ($tableName === 'pages') {
$tableName = 'pages_language_overlay';
}
$prefixLanguageTitleFieldNames = [];
if (empty($GLOBALS['TCA'][$tableName]['columns'])) {
return $prefixLanguageTitleFieldNames;
}
foreach ($GLOBALS['TCA'][$tableName]['columns'] as $fieldName => $configuration) {
$type = $configuration['config']['type'] ?? null;
if (
($configuration['l10n_mode'] ?? null) === 'prefixLangTitle'
&& ($type === 'input' || $type === 'text')
) {
$prefixLanguageTitleFieldNames[] = $fieldName;
}
}
return $prefixLanguageTitleFieldNames;
}
/**
* True if we're dealing with a field that has foreign db relations
*
......@@ -917,4 +1056,12 @@ class DataMapProcessor
$relationHandler->setWorkspaceId($this->backendUser->workspace);
return $relationHandler;
}
/**
* @return null|LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'] ?? null;
}
}
......@@ -239,8 +239,7 @@ class ActionTest extends \TYPO3\CMS\Core\Tests\Functional\DataHandling\IRRE\CSV\
$responseSections = $this->getFrontendResponse(self::VALUE_PageId, self::VALUE_LanguageId)->getResponseSections();
$this->assertThat($responseSections, $this->getRequestSectionStructureHasRecordConstraint()
->setRecordIdentifier(self::TABLE_Content . ':' . self::VALUE_ContentIdLast)->setRecordField(self::FIELD_ContentHotel)
// @todo Actually Hotel #2 should be prefixed as well
->setTable(self::TABLE_Hotel)->setField('title')->setValues('[Translate to Dansk:] Hotel #1', 'Hotel #2'));
->setTable(self::TABLE_Hotel)->setField('title')->setValues('[Translate to Dansk:] Hotel #1', '[Translate to Dansk:] Hotel #2'));
}
/**
......
"tt_content",,,,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l18n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","header","tx_irretutorial_1ncsv_hotels"
tt_content
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,tx_irretutorial_1ncsv_hotels
,297,89,256,0,0,0,0,0,0,0,0,0,"Regular Element #1","3,4"
,298,89,512,0,0,0,0,0,0,0,0,0,"Regular Element #2","5,7"
,299,89,768,0,1,298,298,0,0,0,0,0,"[Translate to Dansk:] Regular Element #2","6,8"
"tx_irretutorial_1ncsv_hotel",,,,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l18n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title","offers"
tx_irretutorial_1ncsv_hotel
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title,offers
,3,89,256,0,0,0,0,0,0,0,0,0,"Hotel #1","5,6"
,4,89,128,0,0,0,0,0,0,0,0,0,"Hotel #2",7
,5,89,64,0,0,0,0,0,0,0,0,0,"Hotel #1",8
,6,89,96,0,1,5,5,0,0,0,0,0,"[Translate to Dansk:] Hotel #1",9
,7,89,32,0,0,0,0,0,0,0,0,0,"Hotel #2",""
,8,89,16,0,1,7,0,0,0,0,0,0,"Hotel #2",""
"tx_irretutorial_1ncsv_offer",,,,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l18n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title","prices"
,7,89,32,0,0,0,0,0,0,0,0,0,"Hotel #2",
,8,89,16,0,1,7,0,0,0,0,0,0,"[Translate to Dansk:] Hotel #2",
tx_irretutorial_1ncsv_offer
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title,prices
,5,89,256,0,0,0,0,0,0,0,0,0,"Offer #1.1","7,8,9"
,6,89,128,0,0,0,0,0,0,0,0,0,"Offer #1.2","10,11"
,7,89,64,0,0,0,0,0,0,0,0,0,"Offer #2.1",12
,8,89,32,0,0,0,0,0,0,0,0,0,"Offer #1.1",13
,9,89,48,0,1,8,8,0,0,0,0,0,"[Translate to Dansk:] Offer #1.1",14
"tx_irretutorial_1ncsv_price",,,,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l18n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title",
,7,89,256,0,0,0,0,0,0,0,0,0,"Price #1.1.1",
,8,89,128,0,0,0,0,0,0,0,0,0,"Price #1.1.2",
,9,89,64,0,0,0,0,0,0,0,0,0,"Price #1.1.3",
,10,89,32,0,0,0,0,0,0,0,0,0,"Price #1.2.1",
,11,89,16,0,0,0,0,0,0,0,0,0,"Price #1.2.2",
,12,89,8,0,0,0,0,0,0,0,0,0,"Price #2.1.1",
,13,89,4,0,0,0,0,0,0,0,0,0,"Price #1.1.1",
,14,89,6,0,1,13,13,0,0,0,0,0,"[Translate to Dansk:] Price #1.1.1",
tx_irretutorial_1ncsv_price
,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3_origuid,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title
,7,89,256,0,0,0,0,0,0,0,0,0,"Price #1.1.1"
,8,89,128,0,0,0,0,0,0,0,0,0,"Price #1.1.2"
,9,89,64,0,0,0,0,0,0,0,0,0,"Price #1.1.3"
,10,89,32,0,0,0,0,0,0,0,0,0,"Price #1.2.1"
,11,89,16,0,0,0,0,0,0,0,0,0,"Price #1.2.2"
,12,89,8,0,0,0,0,0,0,0,0,0,"Price #2.1.1"
,13,89,4,0,0,0,0,0,0,0,0,0,"Price #1.1.1"
,14,89,6,0,1,13,13,0,0,0,0,0,"[Translate to Dansk:] Price #1.1.1"
......@@ -30,10 +30,12 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D
const VALUE_LanguageId = 1;
const TABLE_Page = 'pages';
const TABLE_PageOverlay = 'pages_language_overlay';
const TABLE_Content = 'tt_content';
const TABLE_Hotel = 'tx_irretutorial_1nff_hotel';
const TABLE_Offer = 'tx_irretutorial_1nff_offer';
const FIELD_PageHotel = 'tx_irretutorial_hotels';
const FIELD_ContentHotel = 'tx_irretutorial_1nff_hotels';
const FIELD_HotelOffer = 'offers';
......@@ -429,4 +431,80 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D
[self::TABLE_Hotel => [4]]
);
}
public function localizePageWithLocalizationExclude()
{
$GLOBALS['TCA'][self::TABLE_Page]['columns'][self::FIELD_PageHotel]['l10n_mode'] = 'exclude';
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['l10n_mode'] = 'exclude';
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
}
public function localizePageAndAddHotelChildWithLocalizationExclude()
{
$GLOBALS['TCA'][self::TABLE_Page]['columns'][self::FIELD_PageHotel]['l10n_mode'] = 'exclude';
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['l10n_mode'] = 'exclude';
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
$this->actionService->modifyRecords(
self::VALUE_PageId,
[
self::TABLE_Page => ['uid' => self::VALUE_PageId, self::FIELD_PageHotel => '2,__nextUid'],
self::TABLE_Hotel => ['uid' => '__NEW', 'title' => 'Hotel #007'],
]
);
}
public function localizePageWithLanguageSynchronization()
{
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['config']['behaviour']['allowLanguageSynchronization'] = true;
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
}
public function localizePageAndAddHotelChildWithLanguageSynchronization()
{
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['config']['behaviour']['allowLanguageSynchronization'] = true;
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
$this->actionService->modifyRecords(
self::VALUE_PageId,
[
self::TABLE_Page => ['uid' => self::VALUE_PageId, self::FIELD_PageHotel => '2,__nextUid'],
self::TABLE_Hotel => ['uid' => '__NEW', 'title' => 'Hotel #007'],
]
);
}
public function localizePageAndAddMonoglotHotelChildWithLanguageSynchronization()
{
unset($GLOBALS['TCA'][self::TABLE_Hotel]['ctrl']['languageField']);
unset($GLOBALS['TCA'][self::TABLE_Hotel]['ctrl']['transOrigPointerField']);
unset($GLOBALS['TCA'][self::TABLE_Hotel]['ctrl']['transOrigDiffSourceField']);
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['config']['behaviour']['allowLanguageSynchronization'] = true;
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
$this->actionService->modifyRecords(
self::VALUE_PageId,
[
self::TABLE_Page => ['uid' => self::VALUE_PageId, self::FIELD_PageHotel => '2,__nextUid'],
self::TABLE_Hotel => ['uid' => '__NEW', 'title' => 'Hotel #007'],
]
);
}
public function localizeAndCopyPageWithLanguageSynchronization()
{
$GLOBALS['TCA'][self::TABLE_PageOverlay]['columns'][self::FIELD_PageHotel]['config']['behaviour']['allowLanguageSynchronization'] = true;
$localizedTableIds = $this->actionService->localizeRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_LanguageId);
$this->recordIds['localizedPageId'] = $localizedTableIds[self::TABLE_Page][self::VALUE_PageId];
$this->recordIds['localizedPageOverlayId'] = $localizedTableIds[self::TABLE_PageOverlay][self::VALUE_PageId];
$newTableIds = $this->actionService->copyRecord(self::TABLE_Page, self::VALUE_PageId, self::VALUE_PageIdTarget);
$this->recordIds['newPageId'] = $newTableIds[self::TABLE_Page][self::VALUE_PageId];
}
}
......@@ -240,8 +240,7 @@ class ActionTest extends \TYPO3\CMS\Core\Tests\Functional\DataHandling\IRRE\Fore
$responseSections = $this->getFrontendResponse(self::VALUE_PageId, self::VALUE_LanguageId)->getResponseSections('Default', 'Extbase:list()');
$this->assertThat($responseSections, $this->getRequestSectionStructureHasRecordConstraint()
->setRecordIdentifier(self::TABLE_Content . ':' . self::VALUE_ContentIdLast)->setRecordField(self::FIELD_ContentHotel)
// @todo Actually Hotel #2 should be prefixed as well