Commit f3c23a15 authored by Benni Mack's avatar Benni Mack Committed by Andreas Fernandez
Browse files

[TASK] Move workspace-related methods to DataHandler

Some methods which do not have any connection
to the current backend user and are only
used in DataHandler and DataHandlerHook of
workspaces are moved to DataHandler in order
to clean up the usages regarding permission
handling of backend users within workspaces.

Resolves: #94744
Releases: master
Change-Id: I0473fe195d267fdb09440ab981ea2550f428d0b0
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70199

Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent ec53937f
......@@ -48,7 +48,6 @@ use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Core\Versioning\VersionState;
use TYPO3\CMS\Install\Service\SessionService;
/**
......@@ -858,88 +857,6 @@ class BackendUserAuthentication extends AbstractUserAuthentication
&& !($this->getTSConfig()['options.']['mayNotCreateEditBookmarks'] ?? false);
}
/**
* Checking if editing of an existing record is allowed in current workspace if that is offline.
* Rules for editing in offline mode:
* - record supports versioning and is an offline version from workspace and has the current stage
* - or record (any) is in a branch where there is a page which is a version from the workspace
* and where the stage is not preventing records
*
* @param string $table Table of record
* @param array|int $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_oid, t3ver_stage (if versioningWS is set)
* @return string String error code, telling the failure state. FALSE=All ok
* @internal should only be used from within TYPO3 Core
*/
public function workspaceCannotEditRecord($table, $recData)
{
// Only test if the user is in a workspace
if ($this->workspace === 0) {
return false;
}
$tableSupportsVersioning = BackendUtility::isTableWorkspaceEnabled($table);
if (!is_array($recData)) {
$recData = BackendUtility::getRecord(
$table,
$recData,
'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.
$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';
}
// So is the user allowed to "use" the edit stage within the workspace?
return $this->workspaceCheckStageForCurrent(0)
? false
: 'User\'s access level did not allow for editing';
}
// Check if we are testing a "live" record
if ($this->workspaceAllowsLiveEditingInTable($table)) {
// Live records are OK in the current workspace
return false;
}
// If not offline, output error
return 'Online record was not in a workspace!';
}
return 'No record';
}
/**
* Evaluates if a user is allowed to edit the offline version
*
* @param string $table Table of record
* @param array|int $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
* @return string String error code, telling the failure state. FALSE=All ok
* @see workspaceCannotEditRecord()
* @internal this method will be moved to EXT:workspaces
*/
public function workspaceCannotEditOfflineVersion($table, $recData)
{
if (!BackendUtility::isTableWorkspaceEnabled($table)) {
return 'Table does not support versioning.';
}
if (!is_array($recData)) {
$recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_oid,t3ver_wsid,t3ver_state,t3ver_stage');
}
if (is_array($recData)) {
$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';
}
return 'No record';
}
/**
* Checks if a record is allowed to be edited in the current workspace.
* This is not bound to an actual record, but to the mere fact if the user is in a workspace
......@@ -988,38 +905,6 @@ class BackendUserAuthentication extends AbstractUserAuthentication
return true;
}
/**
* Evaluates if auto creation of a version of a record is allowed.
* Auto-creation of version: In offline workspace, test if versioning is
* enabled and look for workspace version of input record.
* If there is no versionized record found we will create one and save to that.
*
* @param string $table Table of the record
* @param int $id UID of record
* @param int $recpid PID of record
* @return bool TRUE if ok.
* @internal should only be used from within TYPO3 Core
*/
public function workspaceAllowAutoCreation($table, $id, $recpid)
{
// No version can be created in live workspace
if ($this->workspace === 0) {
return false;
}
// No versioning support for this table, so no version can be created
if (!BackendUtility::isTableWorkspaceEnabled($table)) {
return false;
}
if ($recpid < 0) {
return false;
}
// There must be no existing version of this record in workspace
if (BackendUtility::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid')) {
return false;
}
return true;
}
/**
* Checks if an element stage allows access for the user in the current workspace
* In live workspace (= 0) access is always granted for any stage.
......
......@@ -999,7 +999,7 @@ class DataHandler implements LoggerAwareInterface
// Use the new id of the copied/versionized record:
$id = $this->autoVersionIdMap[$table][$id];
$recordAccess = true;
} elseif (!$this->bypassWorkspaceRestrictions && ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $tempdata))) {
} elseif (!$this->bypassWorkspaceRestrictions && $tempdata && ($errorCode = $this->workspaceCannotEditRecord($table, $tempdata))) {
$recordAccess = false;
// Versioning is required and it must be offline version!
// Check if there already is a workspace version
......@@ -1007,7 +1007,7 @@ class DataHandler implements LoggerAwareInterface
if ($workspaceVersion) {
$id = $workspaceVersion['uid'];
$recordAccess = true;
} elseif ($this->BE_USER->workspaceAllowAutoCreation($table, $id, $theRealPid)) {
} elseif ($this->workspaceAllowAutoCreation($table, $id, $theRealPid)) {
// new version of a record created in a workspace - so always refresh pagetree to indicate there is a change in the workspace
$this->pagetreeNeedsRefresh = true;
......@@ -5577,7 +5577,7 @@ class DataHandler implements LoggerAwareInterface
$this->log($table, $versionRecord['uid'], SystemLogDatabaseAction::DISCARD, 0, SystemLogErrorClassification::USER_ERROR, 'Attempt to discard workspace record ' . $table . ':' . $versionRecord['uid'] . ' failed: Different workspace');
return;
}
if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $versionRecord['uid'])) {
if ($errorCode = $this->workspaceCannotEditOfflineVersion($table, $versionRecord)) {
$this->log($table, $versionRecord['uid'], SystemLogDatabaseAction::DISCARD, 0, SystemLogErrorClassification::USER_ERROR, 'Attempt to discard workspace record ' . $table . ':' . $versionRecord['uid'] . ' failed: ' . $errorCode);
return;
}
......@@ -9032,6 +9032,114 @@ class DataHandler implements LoggerAwareInterface
return $records;
}
/**
* Evaluates if auto creation of a version of a record is allowed.
* Auto-creation of version: In offline workspace, test if versioning is
* enabled and look for workspace version of input record.
* If there is no versionized record found we will create one and save to that.
*
* @param string $table Table of the record
* @param int $id UID of record
* @param int|null $recpid PID of record
* @return bool TRUE if ok.
* @internal should only be used from within TYPO3 Core
*/
protected function workspaceAllowAutoCreation(string $table, $id, $recpid): bool
{
// No version can be created in live workspace
if ($this->BE_USER->workspace === 0) {
return false;
}
// No versioning support for this table, so no version can be created
if (!BackendUtility::isTableWorkspaceEnabled($table)) {
return false;
}
if ($recpid < 0) {
return false;
}
// There must be no existing version of this record in workspace
if (BackendUtility::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id, 'uid')) {
return false;
}
return true;
}
/**
* Evaluates if a user is allowed to edit the offline version
*
* @param string $table Table of record
* @param array $record array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
* @return string String error code, telling the failure state. FALSE=All ok
* @see workspaceCannotEditRecord()
* @internal this method will be moved to EXT:workspaces
*/
public function workspaceCannotEditOfflineVersion(string $table, array $record)
{
if (!BackendUtility::isTableWorkspaceEnabled($table)) {
return 'Table does not support versioning.';
}
$versionState = new VersionState($record['t3ver_state']);
if ($versionState->equals(VersionState::NEW_PLACEHOLDER) || (int)$record['t3ver_oid'] > 0) {
return $this->workspaceCannotEditRecord($table, $record);
}
return 'Not an offline version';
}
/**
* Checking if editing of an existing record is allowed in current workspace if that is offline.
* Rules for editing in offline mode:
* - record supports versioning and is an offline version from workspace and has the current stage
* - or record (any) is in a branch where there is a page which is a version from the workspace
* and where the stage is not preventing records
*
* @param string $table Table of record
* @param array|int $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_oid, t3ver_stage (if versioningWS is set)
* @return string String error code, telling the failure state. FALSE=All ok
* @internal should only be used from within TYPO3 Core
*/
public function workspaceCannotEditRecord($table, $recData)
{
// Only test if the user is in a workspace
if ($this->BE_USER->workspace === 0) {
return false;
}
$tableSupportsVersioning = BackendUtility::isTableWorkspaceEnabled($table);
if (!is_array($recData)) {
$recData = BackendUtility::getRecord(
$table,
$recData,
'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.
$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->BE_USER->workspace) {
// So does workspace match?
return 'Workspace ID of record didn\'t match current workspace';
}
// So is the user allowed to "use" the edit stage within the workspace?
return $this->BE_USER->workspaceCheckStageForCurrent(0)
? false
: 'User\'s access level did not allow for editing';
}
// Check if we are testing a "live" record
if ($this->BE_USER->workspaceAllowsLiveEditingInTable($table)) {
// Live records are OK in the current workspace
return false;
}
// If not offline, output error
return 'Online record was not in a workspace!';
}
return 'No record';
}
/**
* Gets the outer most instance of \TYPO3\CMS\Core\DataHandling\DataHandler
* Since \TYPO3\CMS\Core\DataHandling\DataHandler can create nested objects of itself,
......
......@@ -361,7 +361,7 @@ class DataHandlerHook
$canMoveRecord = $recIsNewVersion || $tableSupportsVersioning;
// Workspace source check:
if (!$recIsNewVersion) {
$errorCode = $dataHandler->BE_USER->workspaceCannotEditRecord($table, $versionedRecord['uid'] ?: $uid);
$errorCode = $dataHandler->workspaceCannotEditRecord($table, $versionedRecord['uid'] ?: $uid);
if ($errorCode) {
$workspaceAccessBlocked['src1'] = 'Record could not be edited in workspace: ' . $errorCode . ' ';
} elseif (!$canMoveRecord && !$recordMustNotBeVersionized) {
......@@ -479,10 +479,12 @@ class DataHandlerHook
*/
protected function version_setStage($table, $id, $stageId, string $comment, DataHandler $dataHandler, array $notificationAlternativeRecipients = [])
{
if ($errorCode = $dataHandler->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) {
$record = BackendUtility::getRecord($table, $id);
if (!is_array($record)) {
$dataHandler->log($table, $id, DatabaseAction::VERSIONIZE, 0, SystemLogErrorClassification::USER_ERROR, 'Attempt to set stage for record failed: No Record');
} elseif ($errorCode = $dataHandler->workspaceCannotEditOfflineVersion($table, $record)) {
$dataHandler->log($table, $id, DatabaseAction::VERSIONIZE, 0, SystemLogErrorClassification::USER_ERROR, 'Attempt to set stage for record failed: ' . $errorCode);
} elseif ($dataHandler->checkRecordUpdateAccess($table, $id)) {
$record = BackendUtility::getRecord($table, $id);
$workspaceInfo = $dataHandler->BE_USER->checkWorkspace($record['t3ver_wsid']);
// check if the user is allowed to the current stage, so it's also allowed to send to next stage
if ($dataHandler->BE_USER->workspaceCheckStageForCurrent($record['t3ver_stage'])) {
......
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