[BUGFIX] File Relations don't work with workspaces 12/29712/9
authorBenjamin Mack <benni@typo3.org>
Mon, 28 Apr 2014 13:28:53 +0000 (15:28 +0200)
committerSteffen Ritter <info@rs-websystems.de>
Wed, 11 Jun 2014 09:50:38 +0000 (11:50 +0200)
The file repository used to resolve all sys_file_references
handles enable fields manually currently, which is bad because
it forgets to handle the workspace-related where clause.

The attached patch splits the behavior into frontend and
backend context. In backend the RelationHandler is capable
of automatically resolving IRRE structures. In frontend mode
the enableFiels and version preview settings are relevant to
show correct results.

Resolves: #43916
Releases: 6.2
Change-Id: I6c19e5d7b4cd7a2134921c669b82ab90b54844de
Reviewed-on: https://review.typo3.org/29712
Tested-by: Lorenz Ulrich
Reviewed-by: Fabien Udriot
Tested-by: Fabien Udriot
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
typo3/sysext/backend/Classes/Form/Element/InlineElement.php
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/core/Classes/Database/RelationHandler.php
typo3/sysext/core/Classes/Resource/AbstractRepository.php
typo3/sysext/core/Classes/Resource/FileRepository.php

index e0b96a0..3e505d2 100644 (file)
@@ -33,6 +33,7 @@ use TYPO3\CMS\Core\Database\RelationHandler;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
+use TYPO3\CMS\Core\Versioning\VersionState;
 
 /**
  * The Inline-Relational-Record-Editing (IRRE) functions as part of the TCEforms.
@@ -1747,9 +1748,13 @@ class InlineElement {
         */
        public function getRecord($pid, $table, $uid, $cmd = '') {
                // Fetch workspace version of a record (if any):
-               if ($cmd !== 'new' && $GLOBALS['BE_USER']->workspace !== 0) {
-                       $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid');
+               if ($cmd !== 'new' && $GLOBALS['BE_USER']->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
+                       $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid,t3ver_state');
                        if ($workspaceVersion !== FALSE) {
+                               $versionState = VersionState::cast($workspaceVersion['t3ver_state']);
+                               if ($versionState->equals(VersionState::DELETE_PLACEHOLDER)) {
+                                       return FALSE;
+                               }
                                $uid = $workspaceVersion['uid'];
                        }
                }
index b234c56..8e29a1b 100644 (file)
@@ -1506,21 +1506,15 @@ class BackendUtility {
                }
                $thumbData = '';
                // FAL references
-               if ($tcaConfig['type'] === 'inline') {
-                       $sortingField = isset($tcaConfig['foreign_sortby']) ? $tcaConfig['foreign_sortby'] : '';
-                       $referenceUids = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-                               'uid',
-                               'sys_file_reference',
-                               'tablenames = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_file_reference')
-                                       . ' AND fieldname=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($field, 'sys_file_reference')
-                                       . ' AND uid_foreign=' . (int)$row['uid']
-                                       . self::deleteClause('sys_file_reference')
-                                       . self::versioningPlaceholderClause('sys_file_reference'),
-                               '',
-                               $sortingField
-                       );
+               if ($tcaConfig['type'] === 'inline' && $tcaConfig['foreign_table'] === 'sys_file_reference') {
+                       /** @var $relationHandler \TYPO3\CMS\Core\Database\RelationHandler */
+                       $relationHandler = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
+                       $relationHandler->start($row[$field], $tcaConfig['foreign_table'], $tcaConfig['MM'], $row['uid'], $table, $tcaConfig);
+                       $relationHandler->processDeletePlaceholder();
+                       $referenceUids = $relationHandler->tableArray[$tcaConfig['foreign_table']];
+
                        foreach ($referenceUids as $referenceUid) {
-                               $fileReferenceObject = ResourceFactory::getInstance()->getFileReferenceObject($referenceUid['uid']);
+                               $fileReferenceObject = ResourceFactory::getInstance()->getFileReferenceObject($referenceUid);
                                $fileObject = $fileReferenceObject->getOriginalFile();
 
                                if ($fileObject->isMissing()) {
index de85f5c..1396e4d 100644 (file)
@@ -414,7 +414,7 @@ class RelationHandler {
        }
 
        /**
-        * @param bool $useLiveReferences
+        * @param bool $useLiveReferenceIds
         */
        public function setUseLiveReferenceIds($useLiveReferenceIds) {
                $this->useLiveReferenceIds = (bool)$useLiveReferenceIds;
@@ -1146,11 +1146,9 @@ class RelationHandler {
 
        /**
         * @param NULL|int $workspaceId
-        * @return bool
+        * @return bool Whether items have been purged
         */
        public function purgeItemArray($workspaceId = NULL) {
-               $itemArrayHasBeenPurged = FALSE;
-
                if ($workspaceId === NULL) {
                        $workspaceId = $this->getWorkspaceId();
                } else {
@@ -1165,6 +1163,33 @@ class RelationHandler {
                        $purgeCallback = 'purgeLiveVersionedIds';
                }
 
+               $itemArrayHasBeenPurged = $this->purgeItemArrayHandler($purgeCallback);
+               $this->purged = ($this->purged || $itemArrayHasBeenPurged);
+               return $itemArrayHasBeenPurged;
+       }
+
+       /**
+        * Removes items having a delete placeholder from $this->itemArray
+        *
+        * @return bool Whether items have been purged
+        */
+       public function processDeletePlaceholder() {
+               if (!$this->useLiveReferenceIds || $this->getWorkspaceId() === 0) {
+                       return FALSE;
+               }
+
+               return $this->purgeItemArrayHandler('purgeDeletePlaceholder');
+       }
+
+       /**
+        * Handles a purge callback on $this->itemArray
+        *
+        * @param callable $purgeCallback
+        * @return bool Whether items have been purged
+        */
+       protected function purgeItemArrayHandler($purgeCallback) {
+               $itemArrayHasBeenPurged = FALSE;
+
                foreach ($this->tableArray as $itemTableName => $itemIds) {
                        if (!count($itemIds) || !BackendUtility::isTableWorkspaceEnabled($itemTableName)) {
                                continue;
@@ -1181,7 +1206,6 @@ class RelationHandler {
                        }
                }
 
-               $this->purged = ($this->purged || $itemArrayHasBeenPurged);
                return $itemArrayHasBeenPurged;
        }
 
@@ -1248,6 +1272,36 @@ class RelationHandler {
                return array_values($ids);
        }
 
+       /**
+        * Purges ids that have a delete placeholder
+        *
+        * @param string $tableName
+        * @param array $ids
+        * @return array
+        */
+       protected function purgeDeletePlaceholder($tableName, array $ids) {
+               $ids = $this->getDatabaseConnection()->cleanIntArray($ids);
+               $ids = array_combine($ids, $ids);
+
+               $versions = $this->getDatabaseConnection()->exec_SELECTgetRows(
+                       'uid,t3ver_oid,t3ver_state',
+                       $tableName,
+                       'pid=-1 AND t3ver_oid IN (' . implode(',', $ids) . ') AND t3ver_wsid=' . $this->getWorkspaceId() .
+                               ' AND t3ver_state=' . VersionState::cast(VersionState::DELETE_PLACEHOLDER)
+               );
+
+               if (!empty($versions)) {
+                       foreach ($versions as $version) {
+                               $liveId = $version['t3ver_oid'];
+                               if (isset($ids[$liveId])) {
+                                       unset($ids[$liveId]);
+                               }
+                       }
+               }
+
+               return array_values($ids);
+       }
+
        protected function removeFromItemArray($tableName, $id) {
                foreach ($this->itemArray as $index => $item) {
                        if ($item['table'] === $tableName && (string)$item['id'] === (string)$id) {
index cdabd7b..69ebe61 100644 (file)
@@ -302,4 +302,11 @@ abstract class AbstractRepository implements \TYPO3\CMS\Extbase\Persistence\Repo
                return TYPO3_MODE;
        }
 
+       /**
+        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+        */
+       protected function getDatabaseConnection() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
 }
index 1938bc6..c8cea68 100644 (file)
@@ -181,19 +181,46 @@ class FileRepository extends AbstractRepository {
                if (!\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($uid)) {
                        throw new \InvalidArgumentException('Uid of related record has to be an integer.', 1316789798);
                }
-               $references = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-                       '*',
-                       'sys_file_reference',
-                       'tablenames=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tableName, 'sys_file_reference') .
-                               ' AND deleted = 0' .
-                               ' AND hidden = 0' .
-                               ' AND uid_foreign=' . (int)$uid .
-                               ' AND fieldname=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($fieldName, 'sys_file_reference'),
-                       '',
-                       'sorting_foreign'
-               );
-               foreach ($references as $referenceRecord) {
-                       $itemList[] = $this->createFileReferenceObject($referenceRecord);
+               $referenceUids = NULL;
+               if ($this->getEnvironmentMode() === 'FE' && !empty($GLOBALS['TSFE']->sys_page)) {
+                       /** @var $frontendController \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController */
+                       $frontendController = $GLOBALS['TSFE'];
+                       $references = $this->getDatabaseConnection()->exec_SELECTgetRows(
+                               'uid',
+                               'sys_file_reference',
+                               'tablenames=' . $this->getDatabaseConnection()->fullQuoteStr($tableName, 'sys_file_reference') .
+                                       ' AND uid_foreign=' . (int)$uid .
+                                       ' AND fieldname=' . $this->getDatabaseConnection()->fullQuoteStr($fieldName, 'sys_file_reference')
+                                       . $frontendController->sys_page->enableFields('sys_file_reference', $frontendController->showHiddenRecords),
+                               '',
+                               'sorting_foreign',
+                               '',
+                               'uid'
+                       );
+                       if (!empty($references)) {
+                               $referenceUids = array_keys($references);
+                       }
+               } else {
+                       /** @var $relationHandler \TYPO3\CMS\Core\Database\RelationHandler */
+                       $relationHandler = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
+                       $relationHandler->start(
+                               '', 'sys_file_reference', '', $uid, $tableName,
+                               \TYPO3\CMS\Backend\Utility\BackendUtility::getTcaFieldConfiguration($tableName, $fieldName)
+                       );
+                       if (!empty($relationHandler->tableArray['sys_file_reference'])) {
+                               $referenceUids = $relationHandler->tableArray['sys_file_reference'];
+                       }
+               }
+               if (!empty($referenceUids)) {
+                       foreach ($referenceUids as $referenceUid) {
+                               try {
+                                       // Just passing the reference uid, the factory is doing workspace
+                                       // overlays automatically depending on the current environment
+                                       $itemList[] = $this->factory->getFileReferenceObject($referenceUid);
+                               } catch (\InvalidArgumentException $exception) {
+                                       // No handling, just omit the invalid reference uid
+                               }
+                       }
                }
                return $itemList;
        }
@@ -207,19 +234,13 @@ class FileRepository extends AbstractRepository {
         * @api
         */
        public function findFileReferenceByUid($uid) {
-               $fileReferenceObject = FALSE;
                if (!\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($uid)) {
                        throw new \InvalidArgumentException('uid of record has to be an integer.', 1316889798);
                }
-               $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
-                       '*',
-                       'sys_file_reference',
-                       'uid=' . $uid .
-                               ' AND deleted=0' .
-                               ' AND hidden=0'
-               );
-               if (is_array($row)) {
-                       $fileReferenceObject = $this->createFileReferenceObject($row);
+               try {
+                       $fileReferenceObject = $this->factory->getFileReferenceObject($uid);
+               } catch (\InvalidArgumentException $exception) {
+                       $fileReferenceObject = FALSE;
                }
                return $fileReferenceObject;
        }
@@ -243,6 +264,7 @@ class FileRepository extends AbstractRepository {
         *
         * @param array $databaseRow
         * @return FileReference
+        * @deprecated Use $this->factory->getFileReferenceObject() directly
         */
        protected function createFileReferenceObject(array $databaseRow) {
                return $this->factory->getFileReferenceObject($databaseRow['uid'], $databaseRow);