diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php index 2616b3e6d12bb5017125ac394714aa0aa7d251d6..6d481cdd0585225cc27bfe647e3cdebf07c40777 100644 --- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php @@ -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. diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php index 02c9dc74238b9228d4e78656008ebdf4d3699cf9..2c7b7175555adf710f2df84af48f645ade56af94 100644 --- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php +++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php @@ -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, diff --git a/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php b/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php index 901487510e017753f4057022f5b8292d5a9f071d..7086f25e836ce9bca1beec675da6e67588c6ec1e 100644 --- a/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php +++ b/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php @@ -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'])) {