Commit f5ad3545 authored by Christian Kuhn's avatar Christian Kuhn Committed by Anja Leichsenring
Browse files

[BUGFIX] FormEngine does not edit workspace delete placeholders

Workspace delete placeholders are just a marker that a
live record should be deleted in live when the delete
placeholder is published. Actual field data does not matter.
Those records should thus never be editable via FormEngine.

With patch, FormEngine throws an exception when a delete
placeholder is edited. Additionally, the module menu language
selector drop down when editing a record is adapted to not
link to delete placeholder workspace translations.

Change-Id: Iaf73777ffb1d01e6fde5bef1eaa4e281a1b46fe1
Resolves: #88153
Releases: master, 10.4
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67325

Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent 40a352f9
......@@ -24,6 +24,7 @@ use TYPO3\CMS\Backend\Controller\Event\AfterFormEnginePageInitializedEvent;
use TYPO3\CMS\Backend\Controller\Event\BeforeFormEnginePageInitializedEvent;
use TYPO3\CMS\Backend\Form\Exception\AccessDeniedException;
use TYPO3\CMS\Backend\Form\Exception\DatabaseRecordException;
use TYPO3\CMS\Backend\Form\Exception\DatabaseRecordWorkspaceDeletePlaceholderException;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
use TYPO3\CMS\Backend\Form\FormResultCompiler;
......@@ -1187,7 +1188,7 @@ class EditDocumentController
$title = $this->getLanguageService()
->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noEditPermission');
$editForm .= $this->getInfobox($message, $title);
} catch (DatabaseRecordException $e) {
} catch (DatabaseRecordException | DatabaseRecordWorkspaceDeletePlaceholderException $e) {
$editForm .= $this->getInfobox($e->getMessage());
}
} // End of for each uid
......@@ -1983,9 +1984,8 @@ class EditDocumentController
*
***************************/
/**
* Make selector box for creating new translation for a record or switching to edit the record in an existing
* language.
* Displays only languages which are available for the current page.
* Make selector box for creating new translation for a record or switching to edit the record
* in an existing language. Displays only languages which are available for the current page.
*
* @param string $table Table name
* @param int $uid Uid for which to create a new language
......@@ -1993,10 +1993,11 @@ class EditDocumentController
*/
protected function languageSwitch(string $table, int $uid, $pid = null)
{
$backendUser = $this->getBackendUser();
$languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
$transOrigPointerField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'];
// Table editable and activated for languages?
if ($this->getBackendUser()->check('tables_modify', $table)
if ($backendUser->check('tables_modify', $table)
&& $languageField
&& $transOrigPointerField
) {
......@@ -2045,6 +2046,8 @@ class EditDocumentController
} else {
$rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
}
// List of language id's that should not be added to the selector
$noAddOption = [];
if ($rowCurrent[$transOrigPointerField] || $currentLanguage === 0) {
// Get record in other languages to see what's already available
......@@ -2054,7 +2057,7 @@ class EditDocumentController
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class))
->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $backendUser->workspace));
$result = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fetchFields, true))
->from($table)
......@@ -2075,6 +2078,20 @@ class EditDocumentController
->execute();
while ($row = $result->fetch()) {
if ($backendUser->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
$workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($backendUser->workspace, $table, $row['uid'], 'uid,t3ver_state');
if (!empty($workspaceVersion)) {
$versionState = VersionState::cast($workspaceVersion['t3ver_state']);
if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
// If a workspace delete placeholder exists for this translation: Mark
// this language as "don't add to selector" and continue with next row,
// otherwise an edit link to a delete placeholder would be created, which
// does not make sense.
$noAddOption[] = (int)$row[$languageField];
continue;
}
}
}
$rowsByLang[$row[$languageField]] = $row;
}
}
......@@ -2117,7 +2134,7 @@ class EditDocumentController
}
$href = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $params);
}
if ($addOption) {
if ($addOption && !in_array($languageId, $noAddOption, true)) {
$menuItem = $languageMenu->makeMenuItem()
->setTitle($selectorOptionLabel)
->setHref($href);
......
<?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\Backend\Form\Exception;
use TYPO3\CMS\Backend\Form\Exception;
/**
* Thrown if a workspace delete placeholder is being edited, which is not allowed.
*/
class DatabaseRecordWorkspaceDeletePlaceholderException extends Exception
{
/**
* @var string Table name
*/
protected string $tableName;
/**
* @var int Table row uid
*/
protected int $uid;
/**
* Constructor overwrites default constructor.
*
* @param string $message Human readable error message
* @param int $code Exception code timestamp
* @param string $tableName Table name query was working on
* @param int $uid Table row uid
*/
public function __construct(string $message, int $code, string $tableName, int $uid)
{
parent::__construct($message, $code);
$this->tableName = $tableName;
$this->uid = $uid;
}
/**
* Return table name
*
* @return string
*/
public function getTableName()
{
return $this->tableName;
}
/**
* Return row uid
*
* @return int
*/
public function getUid()
{
return $this->uid;
}
}
......@@ -15,7 +15,10 @@
namespace TYPO3\CMS\Backend\Form\FormDataProvider;
use TYPO3\CMS\Backend\Form\Exception\DatabaseRecordWorkspaceDeletePlaceholderException;
use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
/**
* Fetch existing database row on edit
......@@ -28,6 +31,7 @@ class DatabaseEditRow extends AbstractDatabaseRecordProvider implements FormData
* @param array $result
* @return array
* @throws \UnexpectedValueException
* @throws DatabaseRecordWorkspaceDeletePlaceholderException
*/
public function addData(array $result)
{
......@@ -42,6 +46,21 @@ class DatabaseEditRow extends AbstractDatabaseRecordProvider implements FormData
1437663061
);
}
if (BackendUtility::isTableWorkspaceEnabled($result['tableName'])
&& isset($databaseRow['t3ver_state'])
&& VersionState::cast($databaseRow['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
) {
// Workspace delete placeholder records (t3ver_state = 2) should never be edited. This is a fallback
// to suppress editing in case something still links to FormEngine edit of such a record.
throw new DatabaseRecordWorkspaceDeletePlaceholderException(
'Record with uid "' . $databaseRow['uid'] . '" from table "' . $result['tableName'] . '" is'
. ' a workspace delete placeholder record which can not be edited.',
1608658396,
$result['tableName'],
(int)$databaseRow['uid']
);
}
$result['databaseRow'] = $databaseRow;
return $result;
......
......@@ -16,6 +16,7 @@
namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
use TYPO3\CMS\Backend\Form\Exception\DatabaseRecordException;
use TYPO3\CMS\Backend\Form\Exception\DatabaseRecordWorkspaceDeletePlaceholderException;
use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseEditRow;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
......@@ -133,6 +134,28 @@ class DatabaseEditRowTest extends UnitTestCase
}
}
/**
* @test
*/
public function addDataThrowsWorkspaceDeletePlaceholderExeptionWithDeletePlaceholderRecord()
{
$this->expectException(DatabaseRecordWorkspaceDeletePlaceholderException::class);
$this->expectExceptionCode(1608658396);
$GLOBALS['TCA']['tt_content']['ctrl']['versioningWS'] = 1;
$input = [
'tableName' => 'tt_content',
'command' => 'edit',
'vanillaUid' => 10,
];
$resultRow = [
'uid' => 10,
'pid' => 123,
't3ver_state' => 2,
];
$this->subject->expects(self::once())->method('getDatabaseRow')->willReturn($resultRow);
$this->subject->addData($input);
}
/**
* @test
*/
......
Markdown is supported
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