Commit e9069128 authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[BUGFIX] Avoid dangling MM relations on workspace publish

When publishing workspace records with connected
"true" MM relations (uid_local / uid_foreign columns
and no TCA for that table), DataHandler triggers bugs:

* It updates uid_local or uid_foreign rows to negative
  uids during the process as intermediate DB state.
* It leaves "dangling" MM rows of the workspace record
  that has been pushed live.

The involved code is relatively well encapsulated,
it only kicks in for this "publish MM relations"
scenario, the impact of this patch is limited to
this area.

The patch rewrites MM workspace publish handling:

* Avoid parking state in a DataHandler class property
  only used in this scenario.
* Avoid moving a list of stateful RelationHandler
  instances around and dynamically calling methods
  on those instances.
* Avoid recursive flex form MM publish handling with
  indirect callback methods that change class state.
* Obsolete a RelationHandler method used only in
  this scenario.
* Reduce number of queries.

The result is a simplified, better encapsulated and well
commented solution. On a testing side, the patch brings
a scenario to verify flex form MM relation handling.
An according styleguide example is pending, too.

This patch allows us to change auto created MM table
definitions to unsigned uid_local and uid_foreign columns,
which is of course the right way to go.

Change-Id: I0d24f31cbd356f19937c2f8c18d432424e127b97
Resolves: #95275
Resolves: #81718
Releases: master
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/71097

Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent ae4f2b7a
......@@ -679,7 +679,7 @@ class FlexFormTools
}
}
// Hooks may have parse the data structure already to an array. If that is not the case, parse it now.
// Hooks may have parsed the data structure already to an array. If that is not the case, parse it now.
if (is_string($dataStructure)) {
// Resolve FILE: prefix pointing to a DS in a file
if (strpos(trim($dataStructure), 'FILE:') === 0) {
......@@ -710,7 +710,7 @@ class FlexFormTools
// Create default sheet if there is none, yet.
if (isset($dataStructure['ROOT']) && isset($dataStructure['sheets'])) {
throw new \RuntimeException(
'Parsed data structure has both ROOT and sheets on top level. Thats invalid.',
'Parsed data structure has both ROOT and sheets on top level. That is invalid.',
1440676540
);
}
......
......@@ -559,11 +559,6 @@ class DataHandler implements LoggerAwareInterface
*/
protected $checkModifyAccessListHookObjects;
/**
* @var array
*/
protected $version_remapMMForVersionSwap_reg;
/**
* The outer most instance of \TYPO3\CMS\Core\DataHandling\DataHandler:
* This object instantiates itself on versioning and localization ...
......@@ -5845,124 +5840,148 @@ class DataHandler implements LoggerAwareInterface
}
/**
* Swaps MM-relations for current/swap record, see version_swap()
* Handle MM relations attached to a record when publishing a workspace record.
*
* Strategy:
* * Find all MM tables the record can be attached to by scanning TCA. Handle
* flex form "first level" fields too, but skip scanning for MM relations in
* container sections, since core does not support that since v7 - FormEngine
* throws an exception in this case.
* * For each found MM table: Delete current MM rows of the live record, and
* update MM rows of the workspace record to now point to the live record.
*
* @param string $table Table for the two input records
* @param int $id Current record (about to go offline)
* @param int $swapWith Swap record (about to go online)
* @see version_swap()
* @internal should only be used from within DataHandler
*/
public function version_remapMMForVersionSwap($table, $id, $swapWith)
{
// Actually, selecting the records fully is only need if flexforms are found inside... This could be optimized ...
$currentRec = BackendUtility::getRecord($table, $id);
$swapRec = BackendUtility::getRecord($table, $swapWith);
$this->version_remapMMForVersionSwap_reg = [];
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $fConf) {
$conf = $fConf['config'];
if ($this->isReferenceField($conf)) {
$allowedTables = $conf['type'] === 'group' ? ($conf['allowed'] ?? '') : $conf['foreign_table'];
$prependName = $conf['type'] === 'group' ? ($conf['prepend_tname'] ?? '') : '';
if ($conf['MM'] ?? false) {
$dbAnalysis = $this->createRelationHandlerInstance();
$dbAnalysis->start('', $allowedTables, $conf['MM'], $id, $table, $conf);
if (!empty($dbAnalysis->getValueArray($prependName))) {
$this->version_remapMMForVersionSwap_reg[$id][$field] = [$dbAnalysis, $conf['MM'], $prependName];
}
$dbAnalysis = $this->createRelationHandlerInstance();
$dbAnalysis->start('', $allowedTables, $conf['MM'], $swapWith, $table, $conf);
if (!empty($dbAnalysis->getValueArray($prependName))) {
$this->version_remapMMForVersionSwap_reg[$swapWith][$field] = [$dbAnalysis, $conf['MM'], $prependName];
}
}
} elseif ($conf['type'] === 'flex') {
// Current record
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
$fConf,
$table,
$field,
$currentRec
);
public function versionPublishManyToManyRelations(string $table, array $liveRecord, array $workspaceRecord): void
{
if (!is_array($GLOBALS['TCA'][$table]['columns'])) {
return;
}
$toDeleteRegistry = [];
$toUpdateRegistry = [];
foreach ($GLOBALS['TCA'][$table]['columns'] as $dbFieldName => $dbFieldConfig) {
if (empty($dbFieldConfig['config']['type'])) {
continue;
}
if (!empty($dbFieldConfig['config']['MM']) && $this->isReferenceField($dbFieldConfig['config'])) {
$toDeleteRegistry[] = $dbFieldConfig['config'];
$toUpdateRegistry[] = $dbFieldConfig['config'];
}
if ($dbFieldConfig['config']['type'] === 'flex') {
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
// Find possible mm tables attached to live record flex from data structures, mark as to delete
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier($dbFieldConfig, $table, $dbFieldName, $liveRecord);
$dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
$currentValueArray = GeneralUtility::xml2array($currentRec[$field]);
if (is_array($currentValueArray)) {
$this->checkValue_flex_procInData($currentValueArray['data'], [], $dataStructureArray, [$table, $id, $field], 'version_remapMMForVersionSwap_flexFormCallBack');
foreach (($dataStructureArray['sheets'] ?? []) as $flexSheetDefinition) {
foreach (($flexSheetDefinition['ROOT']['el'] ?? []) as $flexFieldDefinition) {
if (is_array($flexFieldDefinition) && $this->flexFieldDefinitionIsMmRelation($flexFieldDefinition)) {
$toDeleteRegistry[] = $flexFieldDefinition['TCEforms']['config'];
}
}
}
// Swap record
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier(
$fConf,
$table,
$field,
$swapRec
);
// Find possible mm tables attached to workspace record flex from data structures, mark as to update uid
$dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier($dbFieldConfig, $table, $dbFieldName, $workspaceRecord);
$dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
$currentValueArray = GeneralUtility::xml2array($swapRec[$field]);
if (is_array($currentValueArray)) {
$this->checkValue_flex_procInData($currentValueArray['data'], [], $dataStructureArray, [$table, $swapWith, $field], 'version_remapMMForVersionSwap_flexFormCallBack');
foreach (($dataStructureArray['sheets'] ?? []) as $flexSheetDefinition) {
foreach (($flexSheetDefinition['ROOT']['el'] ?? []) as $flexFieldDefinition) {
if (is_array($flexFieldDefinition) && $this->flexFieldDefinitionIsMmRelation($flexFieldDefinition)) {
$toUpdateRegistry[] = $flexFieldDefinition['TCEforms']['config'];
}
}
}
}
}
// Execute:
$this->version_remapMMForVersionSwap_execSwap($table, $id, $swapWith);
// Delete mm table relations of live record
foreach ($toDeleteRegistry as $config) {
$uidFieldName = $this->mmRelationIsLocalSide($config) ? 'uid_local' : 'uid_foreign';
$mmTableName = $config['MM'];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($mmTableName);
$queryBuilder->delete($mmTableName);
$queryBuilder->where($queryBuilder->expr()->eq(
$uidFieldName,
$queryBuilder->createNamedParameter((int)$liveRecord['uid'], \PDO::PARAM_INT)
));
if ($this->mmQueryShouldUseTablenamesColumn($config)) {
$queryBuilder->andWhere($queryBuilder->expr()->eq(
'tablenames',
$queryBuilder->createNamedParameter($table)
));
}
$queryBuilder->execute();
}
// Update mm table relations of workspace record to uid of live record
foreach ($toUpdateRegistry as $config) {
$uidFieldName = $this->mmRelationIsLocalSide($config) ? 'uid_local' : 'uid_foreign';
$mmTableName = $config['MM'];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($mmTableName);
$queryBuilder->update($mmTableName);
$queryBuilder->set($uidFieldName, (int)$liveRecord['uid'], true, \PDO::PARAM_INT);
$queryBuilder->where($queryBuilder->expr()->eq(
$uidFieldName,
$queryBuilder->createNamedParameter((int)$workspaceRecord['uid'], \PDO::PARAM_INT)
));
if ($this->mmQueryShouldUseTablenamesColumn($config)) {
$queryBuilder->andWhere($queryBuilder->expr()->eq(
'tablenames',
$queryBuilder->createNamedParameter($table)
));
}
$queryBuilder->execute();
}
}
/**
* Callback function for traversing the FlexForm structure in relation to ...
*
* @param array $pParams Array of parameters in num-indexes: table, uid, field
* @param array $dsConf TCA field configuration (from Data Structure XML)
* @param string $dataValue The value of the flexForm field
* @param string $dataValue_ext1 Not used.
* @param string $path Path in flexforms
* @see version_remapMMForVersionSwap()
* @see checkValue_flex_procInData_travDS()
* @internal should only be used from within DataHandler
* Find out if a given flex field definition is a relation with an MM relation.
* Helper of versionPublishManyToManyRelations().
*/
public function version_remapMMForVersionSwap_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $path)
private function flexFieldDefinitionIsMmRelation(array $flexFieldDefinition): bool
{
// Extract parameters:
[$table, $uid, $field] = $pParams;
if ($this->isReferenceField($dsConf)) {
$allowedTables = $dsConf['type'] === 'group' ? $dsConf['allowed'] : $dsConf['foreign_table'];
$prependName = $dsConf['type'] === 'group' ? $dsConf['prepend_tname'] : '';
if ($dsConf['MM']) {
/** @var RelationHandler $dbAnalysis */
$dbAnalysis = $this->createRelationHandlerInstance();
$dbAnalysis->start('', $allowedTables, $dsConf['MM'], $uid, $table, $dsConf);
$this->version_remapMMForVersionSwap_reg[$uid][$field . '/' . $path] = [$dbAnalysis, $dsConf['MM'], $prependName];
}
}
return ($flexFieldDefinition['type'] ?? '') !== 'array' // is a field, not a section
&& is_array($flexFieldDefinition['TCEforms']['config'] ?? false) // config array exists
&& $this->isReferenceField($flexFieldDefinition['TCEforms']['config']) // select, group, category
&& !empty($flexFieldDefinition['TCEforms']['config']['MM']); // MM exists
}
/**
* Performing the remapping operations found necessary in version_remapMMForVersionSwap()
* It must be done in three steps with an intermediate "fake" uid. The UID can be something else than -$id (fx. 9999999+$id if you dare... :-)- as long as it is unique.
*
* @param string $table Table for the two input records
* @param int $id Current record (about to go offline)
* @param int $swapWith Swap record (about to go online)
* @see version_remapMMForVersionSwap()
* @internal should only be used from within DataHandler
* Find out if a query to an MM table should have a "tablenames=myTable" where. This
* is the case if we're looking at it from the foreign side and if the table must have
* "tablenames" column due to various TCA combinations.
* Helper of versionPublishManyToManyRelations().
*/
public function version_remapMMForVersionSwap_execSwap($table, $id, $swapWith)
private function mmQueryShouldUseTablenamesColumn(array $config): bool
{
if (is_array($this->version_remapMMForVersionSwap_reg[$id] ?? false)) {
foreach ($this->version_remapMMForVersionSwap_reg[$id] as $field => $str) {
$str[0]->remapMM($str[1], $id, -$id, $str[2]);
}
if ($this->mmRelationIsLocalSide($config)) {
return false;
}
if (is_array($this->version_remapMMForVersionSwap_reg[$swapWith] ?? false)) {
foreach ($this->version_remapMMForVersionSwap_reg[$swapWith] as $field => $str) {
$str[0]->remapMM($str[1], $swapWith, $id, $str[2]);
}
if ($config['type'] === 'group' && !empty($config['prepend_tname'])) {
// prepend_tname in MM on foreign side forces 'tablenames' column
// @todo: See if we can get rid of prepend_tname in MM altogether?
return true;
}
if (is_array($this->version_remapMMForVersionSwap_reg[$id] ?? false)) {
foreach ($this->version_remapMMForVersionSwap_reg[$id] as $field => $str) {
$str[0]->remapMM($str[1], -$id, $swapWith, $str[2]);
}
if ($config['type'] === 'group' && is_string($config['allowed'] ?? false)
&& (str_contains($config['allowed'], ',') || $config['allowed'] === '*')
) {
// 'allowed' with *, or more than one table
// @todo: Neither '*' nor 'multiple tables' make sense for MM on foreign side.
// There is a hint in the docs about this, too. Sanitize in TCA bootstrap?!
return true;
}
$localSideTableName = $config['type'] === 'group' ? $config['allowed'] ?? '' : $config['foreign_table'] ?? '';
$localSideFieldName = $config['MM_opposite_field'] ?? '';
$localSideAllowed = $GLOBALS['TCA'][$localSideTableName]['columns'][$localSideFieldName]['config']['allowed'] ?? '';
// Local side with 'allowed' = '*' or multiple tables forces 'tablenames' column
return $localSideAllowed === '*' || str_contains($localSideAllowed, ',');
}
/**
* Find out if we're looking at an MM relation from local or foreign side.
* Helper of versionPublishManyToManyRelations().
*/
private function mmRelationIsLocalSide(array $config): bool
{
return empty($config['MM_opposite_field']);
}
/*********************************************
......
......@@ -688,7 +688,7 @@ class RelationHandler
} else {
$oldMMs[] = $row[$uidForeign_field];
}
$oldMMs_inclUid[] = [$row['tablenames'], $row[$uidForeign_field], $row['uid'] ?? 0];
$oldMMs_inclUid[] = (int)($row['uid'] ?? 0);
}
// For each item, insert it:
foreach ($this->itemArray as $val) {
......@@ -734,7 +734,7 @@ class RelationHandler
$queryBuilder->andWhere(
$expressionBuilder->eq(
'uid',
$queryBuilder->createNamedParameter($oldMMs_inclUid[$oldMMs_index][2], \PDO::PARAM_INT)
$queryBuilder->createNamedParameter($oldMMs_inclUid[$oldMMs_index], \PDO::PARAM_INT)
)
);
}
......@@ -779,7 +779,7 @@ class RelationHandler
if ($this->MM_hasUidField) {
$removeClauses->add($queryBuilder->expr()->eq(
'uid',
$queryBuilder->createNamedParameter($oldMMs_inclUid[$oldMM_key][2], \PDO::PARAM_INT)
$queryBuilder->createNamedParameter($oldMMs_inclUid[$oldMM_key], \PDO::PARAM_INT)
));
} else {
if (is_array($mmItem)) {
......@@ -851,9 +851,15 @@ class RelationHandler
* @param int $uid Local, current UID
* @param int $newUid Local, new UID
* @param bool $prependTableName If set, then table names will always be written.
* @deprecated since v11, will be removed with v12.
*/
public function remapMM($MM_tableName, $uid, $newUid, $prependTableName = false)
{
trigger_error(
'Method ' . __METHOD__ . ' of class ' . __CLASS__ . ' is deprecated since v11 and will be removed in v12.',
E_USER_DEPRECATED
);
// In case of a reverse relation
if ($this->MM_is_foreign) {
$uidLocal_field = 'uid_foreign';
......
......@@ -542,9 +542,7 @@ class DefaultTcaSchema
[
'default' => 0,
'notnull' => true,
// @todo: Should be true, but workspaces inserts negative uid's? wah?
// workspaces/Tests/Functional/DataHandling/ManyToMany/Publish/ActionTest.php
'unsigned' => false,
'unsigned' => true,
]
);
}
......@@ -559,9 +557,7 @@ class DefaultTcaSchema
[
'default' => 0,
'notnull' => true,
// @todo: Should be true, but workspaces inserts negative uid's? wah?
// workspaces/Tests/Functional/DataHandling/ManyToMany/Publish/ActionTest.php
'unsigned' => false,
'unsigned' => true,
]
);
}
......
.. include:: ../../Includes.txt
================================================
Deprecation: #95275 - RelationHandler->remapMM()
================================================
See :issue:`95275`
Description
===========
Method :php:`TYPO3\CMS\Core\Database\RelationHandler->remapMM()` has been
marked as deprecated and will be removed with TYPO3 v12.
Impact
======
Calling above method will trigger a deprecation level log error.
Affected Installations
======================
It is highly unlikely instances are affected: The method handles a detail
related to workspaces publishing and is of little use in third party extensions.
The extension scanner will find usages as weak match.
Migration
=========
No direct substitution available, the method has been integrated into
:php:`TYPO3\CMS\Core\DataHandling\DataHandler`.
.. index:: PHP-API, FullyScanned, ext:core
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\SelectFlex;
use TYPO3\CMS\Core\Tests\Functional\DataHandling\AbstractDataHandlerActionTestCase;
abstract class AbstractActionTestCase extends AbstractDataHandlerActionTestCase
{
protected const VALUE_PageId = 89;
protected const VALUE_LanguageId = 1;
protected const VALUE_ElementIdFirst = 1;
protected const VALUE_ElementIdSecond = 2;
protected const VALUE_ElementIdThird = 3;
protected const VALUE_ForeignIdFirst = 1;
protected const VALUE_ForeignIdSecond = 2;
protected const VALUE_ForeignIdThird = 3;
protected const TABLE_Element = 'tx_testselectflexmm_local';
protected const FIELD_Flex = 'flex_1';
/**
* @var array
*/
protected $testExtensionsToLoad = [
'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_select_flex_mm',
];
/**
* @var string
*/
protected $scenarioDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/SelectFlex/DataSet/';
protected function setUp(): void
{
parent::setUp();
$this->importScenarioDataSet('ImportDefault');
}
public function addElementRelation()
{
$this->actionService->modifyRecord(
self::TABLE_Element,
self::VALUE_ElementIdSecond,
[
'flex_1' => [
'data' => [
'sMultiplesidebyside' => [
'lDEF' => [
'select_multiplesidebyside_1' => [
'vDEF' => implode(',', [self::VALUE_ForeignIdSecond, self::VALUE_ElementIdFirst]),
],
],
],
],
],
],
);
}
}
"pages",,,,,,,,,,,,
,"uid","pid","sorting","deleted","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","title",,
,1,0,256,0,0,0,0,0,0,"FunctionalTest",,
,88,1,256,0,0,0,0,0,0,"DataHandlerTest",,
,89,88,256,0,0,0,0,0,0,"Relations",,
,90,88,512,0,0,0,0,0,0,"Target",,
"sys_language",,,,,,,,,,,,
,"uid","pid","hidden","title","flag",,,,,,,
,1,0,0,"Dansk","dk",,,,,,,
,2,0,0,"Deutsch","de",,,,,,,
"tx_testselectflexmm_local",,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l10n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","flex_1"
,1,89,256,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>2</value></field></language></sheet></data></T3FlexForms>"
,2,89,512,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>1</value></field></language></sheet></data></T3FlexForms>"
,3,89,768,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>0</value></field></language></sheet></data></T3FlexForms>"
"tx_testselectflexmm_flex_1_multiplesidebyside_1_mm",,,,,,,,,,,,
,"uid_local","uid_foreign","sorting","sorting_foreign",,,,,,,,
,1,1,1,0,,,,,,,,
,1,2,2,0,,,,,,,,
,2,2,1,0,,,,,,,,
"tx_testselectflexmm_foreign",,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l10n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","title"
,1,89,256,0,0,0,0,0,0,0,0,"Element #1"
,2,89,512,0,0,0,0,0,0,0,0,"Element #2"
,3,89,1024,0,0,0,0,0,0,0,0,"Element #3"
"sys_refindex",,,,,,,,,,,,
,"hash","tablename","recuid","field","flexpointer","softref_key","softref_id","sorting","workspace","ref_table","ref_uid","ref_string"
,"26265001b2e3d788c5a4082f9cb3037a","tx_testselectflexmm_local",1,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,0,0,"tx_testselectflexmm_foreign",1,
,"1edd61614f5579f222faed0d454077c5","tx_testselectflexmm_local",1,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,1,0,"tx_testselectflexmm_foreign",2,
,"b3fde738939406609f6db48ae1850c38","tx_testselectflexmm_local",2,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,0,0,"tx_testselectflexmm_foreign",2,
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\SelectFlex\Modify;
use TYPO3\CMS\Core\Tests\Functional\DataHandling\SelectFlex\AbstractActionTestCase;
class ActionTest extends AbstractActionTestCase
{
/**
* @var string
*/
protected $assertionDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/SelectFlex/Modify/DataSet/';
/**
* @test
*/
public function verifyCleanReferenceIndex()
{
// The test verifies the imported data set has a clean reference index by the check in tearDown()
self::assertTrue(true);
}
/**
* @test
*/
public function addElementRelation()
{
parent::addElementRelation();
$this->assertAssertionDataSet('addElementRelation');
}
}
"pages",,,,,,,,,,,,
,"uid","pid","sorting","deleted","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","title",,
,1,0,256,0,0,0,0,0,0,"FunctionalTest",,
,88,1,256,0,0,0,0,0,0,"DataHandlerTest",,
,89,88,256,0,0,0,0,0,0,"Relations",,
,90,88,512,0,0,0,0,0,0,"Target",,
"sys_language",,,,,,,,,,,,
,"uid","pid","hidden","title","flag",,,,,,,
,1,0,0,"Dansk","dk",,,,,,,
,2,0,0,"Deutsch","de",,,,,,,
"tx_testselectflexmm_local",,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l10n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","flex_1"
,1,89,256,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>2</value></field></language></sheet></data></T3FlexForms>"
,2,89,512,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>2</value></field></language></sheet></data></T3FlexForms>"
,3,89,768,0,0,0,0,0,0,0,0,"<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sMultiplesidebyside'><language index='lDEF'><field index='select_multiplesidebyside_1'><value index='vDEF'>0</value></field></language></sheet></data></T3FlexForms>"
"tx_testselectflexmm_flex_1_multiplesidebyside_1_mm",,,,,,,,,,,,
,"uid_local","uid_foreign","sorting","sorting_foreign",,,,,,,,
,1,1,1,0,,,,,,,,
,1,2,2,0,,,,,,,,
,2,2,1,0,,,,,,,,
,2,1,2,0,,,,,,,,
"tx_testselectflexmm_foreign",,,,,,,,,,,,
,"uid","pid","sorting","deleted","sys_language_uid","l10n_parent","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","title"
,1,89,256,0,0,0,0,0,0,0,0,"Element #1"
,2,89,512,0,0,0,0,0,0,0,0,"Element #2"
,3,89,1024,0,0,0,0,0,0,0,0,"Element #3"
"sys_refindex",,,,,,,,,,,,
,"hash","tablename","recuid","field","flexpointer","softref_key","softref_id","sorting","workspace","ref_table","ref_uid","ref_string"
,"26265001b2e3d788c5a4082f9cb3037a","tx_testselectflexmm_local",1,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,0,0,"tx_testselectflexmm_foreign",1,
,"1edd61614f5579f222faed0d454077c5","tx_testselectflexmm_local",1,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,1,0,"tx_testselectflexmm_foreign",2,
,"b3fde738939406609f6db48ae1850c38","tx_testselectflexmm_local",2,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,0,0,"tx_testselectflexmm_foreign",2,
,"79e115f5915e3d99172f359f4f044051","tx_testselectflexmm_local",2,"flex_1","sMultiplesidebyside/lDEF/select_multiplesidebyside_1/vDEF/",,,1,0,"tx_testselectflexmm_foreign",1,