Commit 2cbe7705 authored by Oliver Hader's avatar Oliver Hader Committed by Susanne Moog
Browse files

[BUGFIX] Skip swapping/publishing of deleted records

In case a content element and the accordant page have been deleted in
separate actions and get published together, the workspace process will
trigger an error message since the removed content element cannot be
published anymore (since it has been processed already with the page).

To avoid this behavior deleted records are collected and checked in the
workspace swapping/publishing process.

Change-Id: If04a198abf81efdc88e75da79da0c01cfaa361ff
Resolves: #47384
Releases: master, 7.6
Reviewed-on: https://review.typo3.org/23129


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog's avatarSusanne Moog <susanne.moog@typo3.org>
parent b5ca2c1a
......@@ -297,6 +297,13 @@ class DataHandler
*/
public $copyMappingArray_merged = [];
/**
* Per-table array with UIDs that have been deleted.
*
* @var array
*/
protected $deletedRecords = [];
/**
* A map between input file name and final destination for files being attached to records.
*
......@@ -4978,6 +4985,7 @@ class DataHandler
->update($table, $updateFields, ['uid' => (int)$uid]);
// Delete all l10n records as well, impossible during undelete because it might bring too many records back to life
if (!$undeleteRecord) {
$this->deletedRecords[$table][] = (int)$uid;
$this->deleteL10nOverlayRecords($table, $uid);
}
} catch (DBALException $e) {
......@@ -5020,6 +5028,7 @@ class DataHandler
GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($table)
->delete($table, ['uid' => (int)$uid]);
$this->deletedRecords[$table][] = (int)$uid;
$this->deleteL10nOverlayRecords($table, $uid);
} catch (DBALException $e) {
$databaseErrorMessage = $e->getPrevious()->getMessage();
......@@ -8517,6 +8526,22 @@ class DataHandler
return $result;
}
/**
* Determines whether a particular record has been deleted
* using DataHandler::deleteRecord() in this instance.
*
* @param string $tableName
* @param string $uid
* @return bool
*/
public function hasDeletedRecord($tableName, $uid)
{
return
!empty($this->deletedRecords[$tableName])
&& in_array($uid, $this->deletedRecords[$tableName])
;
}
/**
* Gets the automatically versionized id of a record.
*
......
......@@ -765,6 +765,11 @@ class DataHandlerHook
// Check prerequisites before start swapping
// Skip records that have been deleted during the current execution
if ($dataHandler->hasDeletedRecord($table, $id)) {
return;
}
// First, check if we may actually edit the online record
if (!$dataHandler->checkRecordUpdateAccess($table, $id)) {
$dataHandler->newlog('Error: You cannot swap versions for a record you do not have access to edit!', 1);
......
......@@ -230,6 +230,15 @@ abstract class AbstractActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\D
$this->actionService->deleteRecord(self::TABLE_Page, self::VALUE_PageId);
}
/**
* @see DataSet/Assertion/deleteContentAndPage.csv
*/
public function deleteContentAndPage()
{
$this->actionService->deleteRecord(self::TABLE_Content, self::VALUE_ContentIdSecond);
$this->actionService->deleteRecord(self::TABLE_Page, self::VALUE_PageId);
}
/**
* @see DataSet/Assertion/copyPageRecord.csv
*/
......
......@@ -279,6 +279,19 @@ class ActionTest extends \TYPO3\CMS\Workspaces\Tests\Functional\DataHandling\Reg
$this->assertContains('RuntimeException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/deleteContentAndPage.csv
*/
public function deleteContentAndPage()
{
parent::deleteContentAndPage();
$this->assertAssertionDataSet('deleteContentAndPage');
$response = $this->getFrontendResponse(self::VALUE_PageId, 0, self::VALUE_BackendUserId, self::VALUE_WorkspaceId, false);
$this->assertContains('RuntimeException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/copyPageRecord.csv
......
"pages",,,,,,,,,,,,,
,"uid","pid","sorting","deleted","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title",,
,1,0,256,0,0,0,0,0,0,0,"FunctionalTest",,
,88,1,256,0,0,0,0,0,0,0,"DataHandlerTest",,
,89,88,256,0,0,0,0,0,0,0,"Relations",,
,90,88,512,0,0,0,0,0,0,0,"Target",,
,91,-1,256,0,89,1,2,0,89,0,"Relations",,
"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"
,296,88,256,0,0,0,0,0,0,0,0,0,"Regular Element #0"
,297,89,256,0,0,0,0,0,0,0,0,0,"Regular Element #1"
,298,89,512,0,0,0,0,0,0,0,0,0,"Regular Element #2"
,299,89,768,0,0,0,0,0,0,0,0,0,"Regular Element #3"
,300,89,1024,0,1,299,299,0,0,0,0,0,"[Translate to Dansk:] Regular Element #3"
,301,-1,512,0,0,0,298,2,2,0,298,0,"Regular Element #2"
,302,-1,512,0,0,0,298,1,2,0,298,0,"Regular Element #2"
......@@ -278,6 +278,20 @@ class ActionTest extends \TYPO3\CMS\Workspaces\Tests\Functional\DataHandling\Reg
$this->assertContains('PageNotFoundException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/deleteContentAndPage.csv
*/
public function deleteContentAndPage()
{
parent::deleteContentAndPage();
$this->actionService->publishRecord(self::TABLE_Page, self::VALUE_PageId);
$this->assertAssertionDataSet('deleteContentAndPage');
$response = $this->getFrontendResponse(self::VALUE_PageId, 0, 0, 0, false);
$this->assertContains('PageNotFoundException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/copyPageRecord.csv
......
"pages",,,,,,,,,,,,,
,"uid","pid","sorting","deleted","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title",,
,1,0,256,0,0,0,0,0,0,0,"FunctionalTest",,
,88,1,256,0,0,0,0,0,0,0,"DataHandlerTest",,
,89,88,1000000000,1,89,0,0,0,0,0,"Relations",,
,90,88,512,0,0,0,0,0,0,0,"Target",,
,91,-1,1000000000,1,0,0,0,0,89,0,"Relations",,
"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"
,296,88,256,0,0,0,0,0,0,0,0,0,"Regular Element #0"
,297,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #1"
,298,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #2"
,299,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #3"
,300,89,1000000000,1,1,299,299,0,0,0,0,0,"[Translate to Dansk:] Regular Element #3"
,301,-1,512,0,0,0,298,2,2,0,298,0,"Regular Element #2"
,302,-1,1000000000,1,0,0,298,1,2,0,298,0,"Regular Element #2"
,303,-1,1000000000,1,1,299,300,1,2,0,300,0,"[Translate to Dansk:] Regular Element #3"
......@@ -263,6 +263,20 @@ class ActionTest extends \TYPO3\CMS\Workspaces\Tests\Functional\DataHandling\Reg
$this->assertContains('PageNotFoundException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/deleteContentAndPage.csv
*/
public function deleteContentAndPage()
{
parent::deleteContentAndPage();
$this->actionService->publishWorkspace(self::VALUE_WorkspaceId);
$this->assertAssertionDataSet('deleteContentAndPage');
$response = $this->getFrontendResponse(self::VALUE_PageId, 0, 0, 0, false);
$this->assertContains('PageNotFoundException', $response->getError());
}
/**
* @test
* @see DataSet/Assertion/copyPageRecord.csv
......@@ -372,10 +386,6 @@ class ActionTest extends \TYPO3\CMS\Workspaces\Tests\Functional\DataHandling\Reg
*/
public function createPlaceholdersAndDeleteDraftParentPage()
{
// @todo These two log entries could be avoided in DataHandlerHook, but are expected
// "[newlog()] Error: You cannot swap versions for a record you do not have access to edit!"
$this->expectedErrorLogEntries = 2;
parent::createPlaceholdersAndDeleteDraftParentPage();
$this->actionService->publishWorkspace(self::VALUE_WorkspaceId);
$this->assertAssertionDataSet('createPlaceholdersAndDeleteDraftParentPage');
......
"pages",,,,,,,,,,,,,
,"uid","pid","sorting","deleted","t3_origuid","t3ver_wsid","t3ver_state","t3ver_stage","t3ver_oid","t3ver_move_id","title",,
,1,0,256,0,0,0,0,0,0,0,"FunctionalTest",,
,88,1,256,0,0,0,0,0,0,0,"DataHandlerTest",,
,89,88,1000000000,1,89,0,0,0,0,0,"Relations",,
,90,88,512,0,0,0,0,0,0,0,"Target",,
,91,-1,1000000000,1,0,0,0,0,89,0,"Relations",,
"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"
,296,88,256,0,0,0,0,0,0,0,0,0,"Regular Element #0"
,297,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #1"
,298,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #2"
,299,89,1000000000,1,0,0,0,0,0,0,0,0,"Regular Element #3"
,300,89,1000000000,1,1,299,299,0,0,0,0,0,"[Translate to Dansk:] Regular Element #3"
,301,-1,512,0,0,0,298,2,2,0,298,0,"Regular Element #2"
,302,-1,1000000000,1,0,0,298,1,2,0,298,0,"Regular Element #2"
,303,-1,1000000000,1,1,299,300,1,2,0,300,0,"[Translate to Dansk:] Regular Element #3"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment