Commit 74899ecf authored by Benni Mack's avatar Benni Mack
Browse files

[!!!][TASK] Do not create new version placeholders in workspaces anymore

Creating a new record in a workspace adds two database rows.

One that is the "placeholder", which - since v10.4 - contains
the same metadata as the other record:

* t3ver_wsid = workspaceID
* t3ver_oid = 0 (simulating behavior of an "online pendant record")
* t3ver_state = 1

And the "versionized" record, identified by:

* t3ver_wsid = workspaceID
* t3ver_oid = uid of the new placeholder record
* t3ver_state = -1

As of TYPO3 v10, the first record is not needed anymore,
the versioned record can be queried directly, however, since
the relations (except MM) point to the placeholder record,
this one is kept.

As result, only one record is created from now on:

* t3ver_wsid = workspaceID
* t3ver_oid = 0 (no online counterpart)
* t3ver_state = 1

On reading, the record is queried directly (no overlay needed anymore!)
with the existing Database Doctrine Restrictions. On publishing, the
record just gets the state/stage/wsid set and is "live".

This brings fundamental benefits:

* No overlays needed when querying
* Fewer database records (placeholders are not helpful)
* Conceptual problems with placeholder and shadowed fields are removed

Resolves: #92791
Releases: master
Change-Id: I0288cc63fe72d8442d586f309bd4054ac44e829b
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65587


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 1e85f091
......@@ -56,6 +56,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
/**
* Main backend controller almost always used if some database record is edited in the backend.
......@@ -2341,7 +2342,7 @@ class EditDocumentController
// Check for versioning support of the table:
if ($tableSupportsVersioning) {
// If the record is already a version of "something" pass it by.
if ($reqRecord['t3ver_oid'] > 0) {
if ($reqRecord['t3ver_oid'] > 0 || (int)$reqRecord['t3ver_state'] === VersionState::NEW_PLACEHOLDER) {
// (If it turns out not to be a version of the current workspace there will be trouble, but
// that is handled inside DataHandler then and in the interface it would clearly be an error of
// links if the user accesses such a scenario)
......
......@@ -36,6 +36,7 @@ use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
use TYPO3\CMS\Workspaces\Service\WorkspaceService;
/**
......@@ -584,7 +585,9 @@ class TreeController
}
$workspaceId = (int)$this->getBackendUser()->workspace;
if ($workspaceId > 0 && ExtensionManagementUtility::isLoaded('workspaces')) {
if ($page['t3ver_oid'] > 0 && (int)$page['t3ver_wsid'] === $workspaceId) {
if ((int)$page['t3ver_wsid'] === $workspaceId
&& ((int)$page['t3ver_oid'] > 0 || (int)$page['t3ver_state'] === VersionState::NEW_PLACEHOLDER)
) {
$classes[] = 'ver-element';
$classes[] = 'ver-versions';
} elseif (
......
......@@ -480,7 +480,7 @@ class PageTreeRepository
$expressionBuilder = $queryBuilder->expr();
if ($this->currentWorkspace === 0) {
// Only include ws_id=0
// Only include records from live workspace
$workspaceIdExpression = $expressionBuilder->eq('t3ver_wsid', 0);
} else {
// Include live records PLUS records from the given workspace
......
......@@ -1189,16 +1189,13 @@ class BackendUtility
return $includeAttrib ? 'title="' . $out . '"' : $out;
}
switch (VersionState::cast($row['t3ver_state'])) {
case new VersionState(VersionState::NEW_PLACEHOLDER):
$parts[] = 'PLH WSID#' . $row['t3ver_wsid'];
break;
case new VersionState(VersionState::DELETE_PLACEHOLDER):
$parts[] = 'Deleted element!';
break;
case new VersionState(VersionState::MOVE_POINTER):
$parts[] = 'NEW LOCATION (Move-to Pointer) WSID#' . $row['t3ver_wsid'];
break;
case new VersionState(VersionState::NEW_PLACEHOLDER_VERSION):
case new VersionState(VersionState::NEW_PLACEHOLDER):
$parts[] = 'New element!';
break;
}
......@@ -1300,16 +1297,13 @@ class BackendUtility
$out .= 'id=' . $row['uid'];
if (static::isTableWorkspaceEnabled($table)) {
switch (VersionState::cast($row['t3ver_state'])) {
case new VersionState(VersionState::NEW_PLACEHOLDER):
$out .= ' - PLH WSID#' . $row['t3ver_wsid'];
break;
case new VersionState(VersionState::DELETE_PLACEHOLDER):
$out .= ' - Deleted element!';
break;
case new VersionState(VersionState::MOVE_POINTER):
$out .= ' - NEW LOCATION (Move-to Pointer) WSID#' . $row['t3ver_wsid'];
break;
case new VersionState(VersionState::NEW_PLACEHOLDER_VERSION):
case new VersionState(VersionState::NEW_PLACEHOLDER):
$out .= ' - New element!';
break;
}
......@@ -3505,8 +3499,10 @@ class BackendUtility
$wsAlt['_ORIG_pid'] = $row['pid'];
}
// Swap UID
$wsAlt['_ORIG_uid'] = $wsAlt['uid'];
$wsAlt['uid'] = $row['uid'];
if (!$versionState->equals(VersionState::NEW_PLACEHOLDER)) {
$wsAlt['_ORIG_uid'] = $wsAlt['uid'];
$wsAlt['uid'] = $row['uid'];
}
// Backend css class:
$wsAlt['_CSSCLASS'] = 'ver-element';
// Changing input record to the workspace version alternative:
......@@ -3540,13 +3536,26 @@ class BackendUtility
$row = $queryBuilder
->from($table)
->where(
$queryBuilder->expr()->eq(
't3ver_oid',
$queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
't3ver_wsid',
$queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
),
$queryBuilder->expr()->orX(
// t3ver_state=1 does not contain a t3ver_oid, and returns itself
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq(
'uid',
$queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
't3ver_state',
$queryBuilder->createNamedParameter(VersionState::NEW_PLACEHOLDER, \PDO::PARAM_INT)
)
),
$queryBuilder->expr()->eq(
't3ver_oid',
$queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
)
)
)
->execute()
......@@ -3590,9 +3599,14 @@ class BackendUtility
}
$liveVersionId = null;
if (self::isTableWorkspaceEnabled($table)) {
$currentRecord = self::getRecord($table, $uid, 'pid,t3ver_oid');
if (is_array($currentRecord) && (int)$currentRecord['t3ver_oid'] > 0) {
$liveVersionId = $currentRecord['t3ver_oid'];
$currentRecord = self::getRecord($table, $uid, 'pid,t3ver_oid,t3ver_state');
if (is_array($currentRecord)) {
if ((int)$currentRecord['t3ver_oid'] > 0) {
$liveVersionId = $currentRecord['t3ver_oid'];
} elseif ((int)($currentRecord['t3ver_state']) === VersionState::NEW_PLACEHOLDER) {
// New versions do not have a live counterpart
$liveVersionId = (int)$uid;
}
}
}
return $liveVersionId;
......
......@@ -60,7 +60,7 @@ class GridColumnItem extends AbstractGridObject
public function isVersioned(): bool
{
return $this->record['_ORIG_uid'] > 0;
return $this->record['_ORIG_uid'] > 0 || (int)$this->record['t3ver_state'] !== 0;
}
public function getPreview(): string
......
......@@ -46,6 +46,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
use TYPO3\CMS\Install\Service\SessionService;
/**
......@@ -928,13 +929,18 @@ class BackendUserAuthentication extends AbstractUserAuthentication
$recData = BackendUtility::getRecord(
$table,
$recData,
'pid' . ($tableSupportsVersioning ? ',t3ver_oid,t3ver_wsid,t3ver_stage' : '')
'pid' . ($tableSupportsVersioning ? ',t3ver_oid,t3ver_wsid,t3ver_state,t3ver_stage' : '')
);
}
if (is_array($recData)) {
// We are testing a "version" (identified by having a t3ver_oid): it can be edited provided
// that workspace matches and versioning is enabled for the table.
if ($tableSupportsVersioning && (int)($recData['t3ver_oid'] ?? 0) > 0) {
$versionState = new VersionState($recData['t3ver_state'] ?? 0);
if ($tableSupportsVersioning
&& (
$versionState->equals(VersionState::NEW_PLACEHOLDER) || (int)(($recData['t3ver_oid'] ?? 0) > 0)
)
) {
if ((int)$recData['t3ver_wsid'] !== $this->workspace) {
// So does workspace match?
return 'Workspace ID of record didn\'t match current workspace';
......@@ -970,10 +976,11 @@ class BackendUserAuthentication extends AbstractUserAuthentication
return 'Table does not support versioning.';
}
if (!is_array($recData)) {
$recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_oid,t3ver_wsid,t3ver_stage');
$recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_oid,t3ver_wsid,t3ver_state,t3ver_stage');
}
if (is_array($recData)) {
if ((int)$recData['t3ver_oid'] > 0) {
$versionState = new VersionState($recData['t3ver_state']);
if ($versionState->equals(VersionState::NEW_PLACEHOLDER) || (int)$recData['t3ver_oid'] > 0) {
return $this->workspaceCannotEditRecord($table, $recData);
}
return 'Not an offline version';
......
......@@ -1065,31 +1065,17 @@ class DataHandler implements LoggerAwareInterface
}
// Processing of all fields in incomingFieldArray and setting them in $fieldArray
$fieldArray = $this->fillInFieldArray($table, $id, $fieldArray, $incomingFieldArray, $theRealPid, $status, $tscPID);
$newVersion_placeholderFieldArray = [];
if ($createNewVersion) {
// create a placeholder array with already processed field content
$newVersion_placeholderFieldArray = $fieldArray;
}
// NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations to field!
// Forcing some values unto field array:
// NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!!
$fieldArray = $this->overrideFieldArray($table, $fieldArray);
if ($createNewVersion) {
$newVersion_placeholderFieldArray = $this->overrideFieldArray($table, $newVersion_placeholderFieldArray);
}
// Setting system fields
if ($status === 'new') {
if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
$fieldArray[$GLOBALS['TCA'][$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
if ($createNewVersion) {
$newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
}
}
if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
$fieldArray[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']] = $this->userid;
if ($createNewVersion) {
$newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['cruser_id']] = $this->userid;
}
}
} elseif ($this->checkSimilar) {
// Removing fields which are equal to the current value:
......@@ -1097,9 +1083,6 @@ class DataHandler implements LoggerAwareInterface
}
if ($GLOBALS['TCA'][$table]['ctrl']['tstamp'] && !empty($fieldArray)) {
$fieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
if ($createNewVersion) {
$newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
}
}
// Set stage to "Editing" to make sure we restart the workflow
if (BackendUtility::isTableWorkspaceEnabled($table)) {
......@@ -1120,36 +1103,18 @@ class DataHandler implements LoggerAwareInterface
$this->pagetreeNeedsRefresh = true;
}
// This creates a new version of the record with online placeholder and offline version
// This creates a version of the record, instead of adding it to the live workspace
if ($createNewVersion) {
// new record created in a workspace - so always refresh pagetree to indicate there is a change in the workspace
$this->pagetreeNeedsRefresh = true;
// Setting placeholder state value for temporary record
$newVersion_placeholderFieldArray['t3ver_state'] = (string)new VersionState(VersionState::NEW_PLACEHOLDER);
// Setting workspace - only so display of placeholders can filter out those from other workspaces.
$newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
// Only set a label if it is an input field
$labelField = $GLOBALS['TCA'][$table]['ctrl']['label'];
if ($GLOBALS['TCA'][$table]['columns'][$labelField]['config']['type'] === 'input') {
$newVersion_placeholderFieldArray[$labelField] = $this->getPlaceholderTitleForTableLabel($table);
}
// Saving placeholder as 'original'
$this->insertDB($table, $id, $newVersion_placeholderFieldArray, false, (int)($incomingFieldArray['uid'] ?? 0));
// For the actual new offline version, set versioning values to point to placeholder
$fieldArray['pid'] = $theRealPid;
$fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id];
// Setting placeholder state value for version (so it can know it is currently a new version...)
$fieldArray['t3ver_state'] = (string)new VersionState(VersionState::NEW_PLACEHOLDER_VERSION);
$fieldArray['t3ver_oid'] = 0;
// Setting state for version (so it can know it is currently a new version...)
$fieldArray['t3ver_state'] = (string)new VersionState(VersionState::NEW_PLACEHOLDER);
$fieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
// When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
$phShadowId = $this->insertDB($table, $id, $fieldArray, true, 0, true);
if ($phShadowId) {
// Processes fields of the placeholder record:
$this->triggerRemapAction($table, $id, [$this, 'placeholderShadowing'], [$table, $phShadowId]);
// Hold auto-versionized ids of placeholders:
$this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $phShadowId;
}
$this->insertDB($table, $id, $fieldArray, true, (int)($incomingFieldArray['uid'] ?? 0));
// Hold auto-versionized ids of placeholders
$this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $this->substNEWwithIDs[$id];
} else {
$this->insertDB($table, $id, $fieldArray, false, (int)($incomingFieldArray['uid'] ?? 0));
}
......@@ -1163,7 +1128,6 @@ class DataHandler implements LoggerAwareInterface
}
}
$this->updateDB($table, $id, $fieldArray);
$this->placeholderShadowing($table, $id);
}
}
// Hook: processDatamap_afterDatabaseOperations
......@@ -1251,79 +1215,6 @@ class DataHandler implements LoggerAwareInterface
return $fieldArray;
}
/**
* Fix shadowing of data in case we are editing an offline version of a live "New" placeholder record.
*
* @param string $table Table name
* @param int $id Record uid
* @internal should only be used from within DataHandler
*/
public function placeholderShadowing($table, $id)
{
$liveRecord = BackendUtility::getLiveVersionOfRecord($table, $id, '*');
if (empty($liveRecord)) {
return;
}
$liveState = VersionState::cast($liveRecord['t3ver_state']);
$versionRecord = BackendUtility::getRecord($table, $id);
$versionState = VersionState::cast($versionRecord['t3ver_state']);
if (!$liveState->indicatesPlaceholder() || $versionState->equals(VersionState::MOVE_POINTER)) {
return;
}
$placeholderRecord = $liveRecord;
$factory = GeneralUtility::makeInstance(
PlaceholderShadowColumnsResolver::class,
$table,
$GLOBALS['TCA'][$table] ?? []
);
$shadowColumns = $factory->forNewPlaceholder();
if (empty($shadowColumns)) {
return;
}
$placeholderValues = [];
foreach ($shadowColumns as $fieldName) {
if ((string)$versionRecord[$fieldName] !== (string)$placeholderRecord[$fieldName]) {
$placeholderValues[$fieldName] = $versionRecord[$fieldName];
}
}
if (empty($placeholderValues)) {
return;
}
if ($this->enableLogging) {
$this->log($table, $placeholderRecord['uid'], SystemLogGenericAction::UNDEFINED, 0, SystemLogErrorClassification::MESSAGE, 'Shadowing done on fields <i>' . implode(',', array_keys($placeholderValues)) . '</i> in placeholder record ' . $table . ':' . $liveRecord['uid'] . ' (offline version UID=' . $id . ')', -1, [], $this->eventPid($table, $liveRecord['uid'], $liveRecord['pid']));
}
$this->updateDB($table, $placeholderRecord['uid'], $placeholderValues);
}
/**
* Create a placeholder title for the label field that does match the field requirements
*
* @param string $table The table name
* @param string $placeholderContent Placeholder content to be used
* @return string placeholder value
* @internal should only be used from within DataHandler
*/
public function getPlaceholderTitleForTableLabel($table, $placeholderContent = null)
{
if ($placeholderContent === null) {
$placeholderContent = 'PLACEHOLDER';
}
$labelPlaceholder = '[' . $placeholderContent . ', WS#' . $this->BE_USER->workspace . ']';
$labelField = $GLOBALS['TCA'][$table]['ctrl']['label'];
if (!isset($GLOBALS['TCA'][$table]['columns'][$labelField]['config']['eval'])) {
return $labelPlaceholder;
}
$evalCodesArray = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['columns'][$labelField]['config']['eval'], true);
$transformedLabel = $this->checkValue_input_Eval($labelPlaceholder, $evalCodesArray, '', $table);
return $transformedLabel['value'] ?? $labelPlaceholder;
}
/**
* Filling in the field array
* $this->excludedTablesAndFields is used to filter fields if needed.
......@@ -4482,10 +4373,6 @@ class DataHandler implements LoggerAwareInterface
$previousUid = $this->getPreviousLocalizedRecordUid($table, $uid, $row['pid'], $language);
// Execute the copy:
$newId = $this->copyRecord($table, $uid, -$previousUid, true, $overrideValues, '', $language);
$autoVersionNewId = $this->getAutoVersionId($table, $newId);
if ($autoVersionNewId !== null) {
$this->triggerRemapAction($table, $newId, [$this, 'placeholderShadowing'], [$table, $autoVersionNewId], true);
}
} else {
// Create new page which needs to contain the same pid as the original page
$overrideValues['pid'] = $row['pid'];
......@@ -5293,32 +5180,15 @@ class DataHandler implements LoggerAwareInterface
return;
}
// Gather versioned and placeholder record if there are any
// Gather versioned record
$versionRecord = null;
$placeholderRecord = null;
if ((int)$record['t3ver_wsid'] === 0) {
$record = BackendUtility::getWorkspaceVersionOfRecord($userWorkspace, $table, $uid);
}
if (!is_array($record)) {
return;
}
$recordState = VersionState::cast($record['t3ver_state']);
if ($recordState->equals(VersionState::NEW_PLACEHOLDER)) {
$placeholderRecord = $record;
$versionRecord = BackendUtility::getWorkspaceVersionOfRecord($userWorkspace, $table, $uid);
if (!is_array($versionRecord)) {
return;
}
} elseif ($recordState->equals(VersionState::NEW_PLACEHOLDER_VERSION)) {
$versionRecord = $record;
$placeholderRecord = BackendUtility::getLiveVersionOfRecord($table, $uid);
if (!is_array($placeholderRecord)) {
return;
}
} else {
$versionRecord = $record;
}
// Do not use $record, $recordState and $uid below anymore, rely on $versionRecord and $placeholderRecord
$versionRecord = $record;
// User access checks
if ($userWorkspace !== (int)$versionRecord['t3ver_wsid']) {
......@@ -5341,8 +5211,8 @@ class DataHandler implements LoggerAwareInterface
// Perform discard operations
$versionState = VersionState::cast($versionRecord['t3ver_state']);
if ($table === 'pages' && $versionState->equals(VersionState::NEW_PLACEHOLDER_VERSION)) {
// When discarding a new page page, there can be new sub pages and new records.
if ($table === 'pages' && $versionState->equals(VersionState::NEW_PLACEHOLDER)) {
// When discarding a new page, there can be new sub pages and new records.
// Those need to be discarded, otherwise they'd end up as records without parent page.
$this->discardSubPagesAndRecordsOnPage($versionRecord);
}
......@@ -5364,12 +5234,6 @@ class DataHandler implements LoggerAwareInterface
[],
(int)$versionRecord['pid']
);
if ($versionState->equals(VersionState::NEW_PLACEHOLDER_VERSION)) {
// Drop placeholder records if any
$this->hardDeleteSingleRecord($table, (int)$placeholderRecord['uid']);
$this->deletedRecords[$table][] = (int)$placeholderRecord['uid'];
}
}
/**
......@@ -5382,6 +5246,7 @@ class DataHandler implements LoggerAwareInterface
{
$isLocalizedPage = false;
$sysLanguageId = (int)$page[$GLOBALS['TCA']['pages']['ctrl']['languageField']];
$versionState = VersionState::cast($page['t3ver_state']);
if ($sysLanguageId > 0) {
// New or moved localized page.
// Discard records on this page localization, but no sub pages.
......@@ -5389,11 +5254,15 @@ class DataHandler implements LoggerAwareInterface
// @todo: Discard other page translations that inherit from this?! (l10n_source field)
$isLocalizedPage = true;
$pid = (int)$page[$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']];
} elseif ($versionState->equals(VersionState::NEW_PLACEHOLDER)) {
// New default language page.
// Discard any sub pages and all other records of this page, including any page localizations.
// The t3ver_state=1 record is incoming here. Records on this page have their pid field set to the uid
// of this record. So, since t3ver_state=1 does not have an online counter-part, the actual UID is used here.
$pid = (int)$page['uid'];
} else {
// New or moved default language page.
// Moved default language page.
// Discard any sub pages and all other records of this page, including any page localizations.
// The t3ver_state=-1 record is incoming here. Records on this page have their pid field set to the uid
// of the t3ver_state=1 record, which is in the t3ver_oid field of the incoming record.
$pid = (int)$page['t3ver_oid'];
}
$tables = $this->compileAdminTables();
......@@ -5489,12 +5358,6 @@ class DataHandler implements LoggerAwareInterface
return;
}
$uid = (int)$record['uid'];
$versionState = VersionState::cast($record['t3ver_state']);
if ($versionState->equals(VersionState::NEW_PLACEHOLDER_VERSION)) {
// The t3ver_state=-1 record is incoming here. Localization overlays of this record have their uid field set
// to the uid of the t3ver_state=1 record, which is in the t3ver_oid field of the incoming record.
$uid = (int)$record['t3ver_oid'];
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
$this->addDeleteRestriction($queryBuilder->getRestrictions()->removeAll());
$statement = $queryBuilder->select('*')
......@@ -6173,30 +6036,6 @@ class DataHandler implements LoggerAwareInterface
}
}
/**
* Triggers a remap action for a specific record.
*
* Some records are post-processed by the processRemapStack() method (e.g. IRRE children).
* This method determines whether an action/modification is executed directly to a record
* or is postponed to happen after remapping data.
*
* @param string $table Name of the table
* @param string $id Id of the record (can also be a "NEW..." string)
* @param array $callback The method to be called
* @param array $arguments The arguments to be submitted to the callback method
* @param bool $forceRemapStackActions Whether to force to use the stack
* @see processRemapStack
*/
protected function triggerRemapAction($table, $id, array $callback, array $arguments, $forceRemapStackActions = false)
{
// Check whether the affected record is marked to be remapped:
if (!$forceRemapStackActions && !isset($this->remapStackRecords[$table][$id]) && !isset($this->remapStackChildIds[$id])) {
call_user_func_array($callback, $arguments);
} else {
$this->addRemapAction($table, $id, $callback, $arguments);
}
}
/**
* Adds an instruction to the remap action stack (used with IRRE).
*
......
<?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\DataHandling;
use TYPO3\CMS\Core\Exception;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Resolver for placeholder shadow columns to be used in workspace aware environments.
*
* Certain fields need to be "shadowed" - NEW and MOVE placeholder need to have values kept in sync
* that are modified, like the "hidden" field (enable fields, slug fields etc).
*
* This class resolves the columns for a TCA table record that should be kept in sync.
*
* @see \TYPO3\CMS\Core\DataHandling\DataHandler::placeholderShadowing()
*/
class PlaceholderShadowColumnsResolver
{
protected const CONTROL_COLUMNS = [
'languageField',
'transOrigPointerField',
'translationSource',
'type',
'label'
];
protected const FLAG_NONE = 0;
protected const FLAG_APPLY_SYSTEM_COLUMNS = 1;
protected const FLAG_APPLY_SLUG_COLUMNS = 2;
/**
* @var string
*/
protected $tableName;
/**
* @var array
*/
protected $tableConfiguration;
/**
* @var int|null
*/
protected $flags;
/**
* @param string $tableName Name of the databa