Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX] Workspaces full compatibility #1927 #1914 #1905 #1942 #1933

150 changes: 108 additions & 42 deletions Classes/Integration/HookSubscribers/DataHandlerSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
use FluidTYPO3\Flux\Provider\Interfaces\GridProviderInterface;
use FluidTYPO3\Flux\Provider\ProviderResolver;
use FluidTYPO3\Flux\Utility\ColumnNumberUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

/**
* TCEMain
Expand Down Expand Up @@ -85,7 +89,8 @@ public function processDatamap_afterDatabaseOperations($command, $table, $id, $f
// right parent for the language and update the column position accordingly.
$originalParentUid = ColumnNumberUtility::calculateParentUid($fieldArray['colPos']);
$originalParent = $this->getSingleRecordWithoutRestrictions($table, $originalParentUid, 'sys_language_uid');
if ($originalParent['sys_language_uid'] !== $fieldArray['sys_language_uid']) {

if ($originalParent['sys_language_uid'] != $fieldArray['sys_language_uid']) {
// copyToLanguage case. Resolve the most recent translated version of the parent record in language of
// child record, and calculate the new column position number based on it.
$newParentRecord = $this->getTranslatedVersionOfParentInLanguageOnPage((int) $fieldArray['sys_language_uid'], (int) $fieldArray['pid'], (int) $originalParentUid);
Expand Down Expand Up @@ -133,7 +138,7 @@ public function processDatamap_preProcessFieldArray(array &$fieldArray, $table,
if ($primaryConfigurationProvider && is_array($fieldArray[$fieldName]) && array_key_exists('data', $fieldArray[$fieldName])) {
foreach ($fieldArray[$fieldName]['data'] as $sheet) {
foreach ($sheet['lDEF'] as $key => $value) {
list ($possibleTableName, $columnName) = explode('.', $key, 2);
[$possibleTableName, $columnName] = explode('.', $key, 2);
if ($possibleTableName === $table && isset($GLOBALS['TCA'][$table]['columns'][$columnName])) {
$fieldArray[$columnName] = $value['vDEF'];
}
Expand All @@ -160,7 +165,7 @@ public function processDatamap_preProcessFieldArray(array &$fieldArray, $table,

protected function cascadeCommandToChildRecords(string $table, int $id, string $command, $value, DataHandler $dataHandler)
{
list (, $childRecords) = $this->getParentAndRecordsNestedInGrid(
[, $childRecords] = $this->getParentAndRecordsNestedInGrid(
$table,
(int)$id,
'uid, pid',
Expand Down Expand Up @@ -251,16 +256,16 @@ public function processCmdmap_beforeStart(DataHandler $dataHandler)
// @phpcs:ignore PSR1.Methods.CamelCapsMethodName
public function processCmdmap_postProcess(&$command, $table, $id, &$relativeTo, &$reference, &$pasteUpdate, &$pasteDataMap)
{

/*
if ($table === 'pages' && $command === 'copy') {
foreach ($reference->copyMappingArray['tt_content'] ?? [] as $originalRecordUid => $copiedRecordUid) {
$copiedRecord = $this->getSingleRecordWithoutRestrictions('tt_content', $copiedRecordUid, 'colPos');
if ($copiedRecord['colPos'] < ColumnNumberUtility::MULTIPLIER) {
continue;
}

$oldParentUid = ColumnNumberUtility::calculateParentUid($copiedRecord['colPos']);
$originalRecord = $this->getSingleRecordWithoutRestrictions('tt_content', $originalRecordUid, 'colPos');

$oldParentUid = ColumnNumberUtility::calculateParentUid($originalRecord['colPos']);
$newParentUid = $reference->copyMappingArray['tt_content'][$oldParentUid];

$overrideArray['colPos'] = ColumnNumberUtility::calculateColumnNumberForParentAndColumn(
Expand All @@ -278,49 +283,84 @@ public function processCmdmap_postProcess(&$command, $table, $id, &$relativeTo,
}
}
}
*/


if ($GLOBALS['BE_USER']->workspace) {
if ($command === 'copy' || $command === 'move' || $command === 'copyToLanguage') {
if ($reference->copyMappingArray['sys_file_reference'] && $reference->copyMappingArray['tt_content']) {
foreach ($reference->copyMappingArray['sys_file_reference'] as $ttUidOld => $ttUidNew) {
if (isset($reference->autoVersionIdMap['sys_file_reference'][$ttUidNew])) {
//get uid_foreign of initial placeholder record
$placeholderRecord = BackendUtility::getRecord(
'sys_file_reference',
$ttUidNew,
'uid_foreign'
);
if ($placeholderRecord) {
$placeholderUidForeign = $placeholderRecord['uid_foreign'];

$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
$queryBuilder->update('sys_file_reference')
->
where($queryBuilder->expr()
->eq('uid', $reference->autoVersionIdMap['sys_file_reference'][$ttUidNew]))
->set('uid_foreign', $placeholderUidForeign)
->execute();
}
}
}

}
}
}

if ($table !== 'tt_content' || $command !== 'move') {
return;
}

list ($originalRecord, $recordsToProcess) = $this->getParentAndRecordsNestedInGrid(
[$originalRecord, $recordsToProcess] = $this->getParentAndRecordsNestedInGrid(
$table,
$id,
'uid, pid, colPos',
false,
$command
);

if (empty($recordsToProcess)) {
return;
}

$languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];
$languageUid = (int)($reference->cmdmap[$table][$id][$command]['update'][$languageField] ?? $originalRecord[$languageField]);
//complete a case of an FCE which is moved back to original position
if ($GLOBALS['BE_USER']->workspace) {
$workspaceRecord = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, 'tt_content', $originalRecord['uid']);
if ($workspaceRecord) {
if (class_exists(Typo3Version::class)) {
$version = GeneralUtility::makeInstance(Typo3Version::class)->getVersion();
} else {
$version = ExtensionManagementUtility::getExtensionVersion('core');
}

if ($relativeTo > 0) {
$destinationPid = $relativeTo;
} else {
$relativeRecord = $this->getSingleRecordWithoutRestrictions($table, abs($relativeTo), 'pid');
$destinationPid = (int)($relativeRecord['pid'] ?? $relativeTo);
}
if (version_compare($version, 10, '<')) {
$placeholder = BackendUtility::getMovePlaceholder('tt_content', $originalRecord['uid']);
} else {
$workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord(
$GLOBALS['BE_USER']->workspace,
'tt_content',
$originalRecord['uid']
);
if ($workspaceVersion) {
$placeholder = $workspaceVersion['pid'] ?? $originalRecord['uid'];
}
}

if ($command === 'move') {
$subCommandMap = $this->recursivelyMoveChildRecords($table, $id, $destinationPid, $languageUid, $reference);
}

if (!empty($subCommandMap)) {
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->copyMappingArray = $reference->copyMappingArray;
$dataHandler->start([], $subCommandMap);
$dataHandler->process_cmdmap();
if ($placeholder && $placeholder['colPos'] != $workspaceRecord['colPos']) {
$recordUid = $placeholder['uid'];
$reference->updateDB('tt_content', $recordUid, ['colPos' => intval($workspaceRecord['colPos'])]);
}
}
}
}

protected function fetchAllColumnNumbersBeneathParent(int $parentUid): array
{
list (, $recordsToProcess, $bannedColumnNumbers) = $this->getParentAndRecordsNestedInGrid(
[, $recordsToProcess, $bannedColumnNumbers] = $this->getParentAndRecordsNestedInGrid(
'tt_content',
$parentUid,
'uid, colPos'
Expand All @@ -336,7 +376,7 @@ protected function recursivelyMoveChildRecords(string $table, int $parentUid, in
{
$dataMap = [];

list (, $recordsToProcess) = $this->getParentAndRecordsNestedInGrid(
[, $recordsToProcess] = $this->getParentAndRecordsNestedInGrid(
$table,
$parentUid,
'uid, colPos'
Expand Down Expand Up @@ -376,6 +416,7 @@ protected function getSingleRecordWithoutRestrictions(string $table, int $uid, s

protected function getMostRecentCopyOfRecord(int $uid, string $fieldsToSelect = 'uid'): ?array
{
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()->removeAll();
$queryBuilder->select(...GeneralUtility::trimExplode(',', $fieldsToSelect))
Expand All @@ -384,7 +425,9 @@ protected function getMostRecentCopyOfRecord(int $uid, string $fieldsToSelect =
->setMaxResults(1)
->where(
$queryBuilder->expr()->eq('t3_origuid', $uid),
$queryBuilder->expr()->neq('t3ver_state', -1)
$queryBuilder->expr()->neq('t3ver_state', -1),
//remove deleted but not published
$queryBuilder->expr()->neq('t3ver_state', 2)
);
return $queryBuilder->execute()->fetch() ?: null;
}
Expand All @@ -405,15 +448,11 @@ protected function getTranslatedVersionOfParentInLanguageOnPage(int $languageUid
return $queryBuilder->execute()->fetch() ?: null;
}

protected function getParentAndRecordsNestedInGrid(string $table, int $parentUid, string $fieldsToSelect, bool $respectPid = false, ?string $command = null)
protected function getParentAndRecordsNestedInGrid(string $table, int $parentUid, string $fieldsToSelect, bool $respectPid = false)
{
// A Provider must be resolved which implements the GridProviderInterface
$resolver = GeneralUtility::makeInstance(ObjectManager::class)->get(ProviderResolver::class);
if ($command === 'undelete') {
$originalRecord = $this->getSingleRecordWithoutRestrictions($table, $parentUid, '*');
} else {
$originalRecord = $this->getSingleRecordWithRestrictions($table, $parentUid, '*');
}
$originalRecord = $this->getSingleRecordWithoutRestrictions($table, $parentUid, '*');
$primaryProvider = $resolver->resolvePrimaryConfigurationProvider(
$table,
null,
Expand Down Expand Up @@ -441,19 +480,46 @@ protected function getParentAndRecordsNestedInGrid(string $table, int $parentUid

$languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'];

/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
if ($command === 'undelete') {
$queryBuilder->getRestrictions()->removeAll();
}
$queryBuilder->getRestrictions()->removeAll();

$currentWsId = $GLOBALS['BE_USER']->workspace;

$query = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fieldsToSelect))
->from($table)
->from($table, 'main_table')
->andWhere(
$queryBuilder->expr()->in('colPos', $queryBuilder->createNamedParameter($childColPosValues, Connection::PARAM_INT_ARRAY)),
$queryBuilder->expr()->eq($languageField, (int)$originalRecord[$languageField]),
$queryBuilder->expr()->in('t3ver_wsid', $queryBuilder->createNamedParameter([0, $GLOBALS['BE_USER']->workspace], Connection::PARAM_INT_ARRAY))
$queryBuilder->expr()->in('t3ver_wsid', $queryBuilder->createNamedParameter([0, $currentWsId], Connection::PARAM_INT_ARRAY)),
//remove deleted records
$queryBuilder->expr()->eq('deleted', 0),
//remove workspace pending records
$queryBuilder->expr()->neq('t3ver_state', -1),
//remove deleted records but not published
$queryBuilder->expr()->neq('t3ver_state', 2),
//remove move-to pointer
$queryBuilder->expr()->neq('t3ver_state', 4)
)->orderBy('sorting', 'DESC');


//remove from the copy the LIVE records that are:
//- deleted into the current WORKSPACE [t3ver_state=2]
//- moved into the current WORKSPACE [t3ver_state=4]
if ($currentWsId > 0) {
$queryBuilder->andWhere(
$queryBuilder->expr()->notIn(
'uid',
sprintf('(select a.t3_origuid
from %s a
where a.t3_origuid = main_table.uid and
a.t3ver_oid = main_table.uid and
t3ver_wsid = %s and
t3ver_state in (2,4))', $table, $currentWsId)
)
);
}

if ($respectPid) {
$query->andWhere($queryBuilder->expr()->eq('pid', $originalRecord['pid']));
} else {
Expand Down
27 changes: 20 additions & 7 deletions Classes/Integration/PreviewView.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3Fluid\Fluid\View\TemplateView;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;

/**
* PreviewView
Expand Down Expand Up @@ -253,13 +255,24 @@ protected function renderGrid(ProviderInterface $provider, array $row, Form $for

$pageUid = $row['pid'];
if ($GLOBALS['BE_USER']->workspace > 0) {
$workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord(
$GLOBALS['BE_USER']->workspace,
'tt_content',
$row['uid']
);
if ($workspaceVersion) {
$pageUid = $workspaceVersion['pid'] ?? $pageUid;
if (class_exists(Typo3Version::class)) {
$version = GeneralUtility::makeInstance(Typo3Version::class)->getVersion();
} else {
$version = ExtensionManagementUtility::getExtensionVersion('core');
}

if (version_compare($version, 10, '<')) {
$placeholder = BackendUtility::getMovePlaceholder('tt_content', $row['uid'], 'pid', $GLOBALS['BE_USER']->workspace);
$pageUid = $placeholder['pid'] ?? $pageUid;
} else {
$workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord(
$GLOBALS['BE_USER']->workspace,
'tt_content',
$row['uid']
);
if ($workspaceVersion) {
$pageUid = $workspaceVersion['pid'] ?? $pageUid;
}
}
}
$pageLayoutView = $this->getInitializedPageLayoutView($provider, $row);
Expand Down
28 changes: 26 additions & 2 deletions Classes/Provider/PageProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use TYPO3\CMS\Core\Utility\RootlineUtility;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Fluid\View\TemplatePaths;
use TYPO3\CMS\Backend\Utility\BackendUtility;

/**
* Page Configuration Provider
Expand Down Expand Up @@ -409,10 +410,33 @@ protected function getParentFieldValue(array $row)
*/
protected function loadRecordTreeFromDatabase($record)
{
if (empty($record)) {

if (empty($record['uid'])) {
return [];
}

if ($record['deleted'] == 1) {
return [];
}
$rootLineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $record['uid']);

if (!is_int($record['uid'])) {
return [];
}

if ($record['t3ver_state'] == 2) {
return [];
}

$contextUid = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, 'pages', $record['uid']);

if (!empty($contextUid["uid"]) && $contextUid["t3ver_state"] != 4 && $contextUid["t3ver_state"] != 2) {
$pageUid = $contextUid["uid"];
} else {
$pageUid = $record['uid'];
}

$rootLineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $pageUid);

return array_slice($rootLineUtility->get(), 1);
}
}
15 changes: 15 additions & 0 deletions Classes/Service/PageService.php
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Fluid\View\TemplatePaths;
use TYPO3\CMS\Fluid\View\TemplateView;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3Fluid\Fluid\Component\Error\ChildNotFoundException;
use TYPO3Fluid\Fluid\View\Exception\InvalidSectionException;

Expand Down Expand Up @@ -105,6 +106,20 @@ public function getPageTemplateConfiguration($pageUid)
if (1 > $pageUid) {
return null;
}
$allDataRecord = BackendUtility::getRecord('pages', $pageUid, $fields = '*', $where = '', $useDeleteClause = false);

if ($allDataRecord["deleted"] == 1) {
return null;
}

if ($allDataRecord["t3ver_state"] == 2) {
return null;
}

if ($allDataRecord["t3ver_state"] == 4) {
return null;
}

$cacheId = 'flux-template-configuration-' . $pageUid;
$runtimeCache = $this->getRuntimeCache();
$fromCache = $runtimeCache->get($cacheId);
Expand Down