[BUGFIX] selicon_field does not create thumbnails 87/47887/3
authorStefan Froemken <froemken@gmail.com>
Wed, 13 Apr 2016 15:19:33 +0000 (17:19 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 24 Apr 2016 13:45:00 +0000 (15:45 +0200)
A select field with renderType selectSingle can point to a
foreign_table. If on the foreign_table TCA the ctrl keys
selicon_field and selicon_field_path are set, a single foreign row
can have an "icon" field that is shown to represent this row.

The patch fixes a bug where selicon_field of the own table
instead of the foreign table was used.

Resolves: #75577
Releases: master, 7.6
Change-Id: I50bac28018b17a61a334aac7d241bcdd96663656
Reviewed-on: https://review.typo3.org/47887
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectItemsTest.php

index c685f05..655c16b 100644 (file)
@@ -402,6 +402,7 @@ abstract class AbstractItemProvider
      * @param string $fieldName Current handle field name
      * @param array $items Incoming items
      * @return array Modified item array
+     * @throws \UnexpectedValueException
      */
     protected function addItemsFromForeignTable(array $result, $fieldName, array $items)
     {
@@ -416,6 +417,15 @@ abstract class AbstractItemProvider
         $database = $this->getDatabaseConnection();
 
         $foreignTable = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
+
+        if (!is_array($GLOBALS['TCA'][$foreignTable])) {
+            throw new \UnexpectedValueException(
+                'Field ' . $fieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
+                . $foreignTable . ', but this table is not defined in TCA',
+                1439569743
+            );
+        }
+
         $foreignTableQueryArray = $this->buildForeignTableQuery($result, $fieldName);
         $queryResource = $database->exec_SELECT_queryArray($foreignTableQueryArray);
 
@@ -441,25 +451,28 @@ abstract class AbstractItemProvider
             $labelPrefix = $result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'];
             $labelPrefix = $languageService->sL($labelPrefix);
         }
-        $iconFieldName = '';
-        if (!empty($result['processedTca']['ctrl']['selicon_field'])) {
-            $iconFieldName = $result['processedTca']['ctrl']['selicon_field'];
-        }
-        $iconPath = '';
-        if (!empty($result['processedTca']['ctrl']['selicon_field_path'])) {
-            $iconPath = $result['processedTca']['ctrl']['selicon_field_path'];
-        }
 
         $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
 
         while ($foreignRow = $database->sql_fetch_assoc($queryResource)) {
             BackendUtility::workspaceOL($foreignTable, $foreignRow);
             if (is_array($foreignRow)) {
-                // Prepare the icon if available:
+                // If the foreign table sets selicon_field, this field can contain an image
+                // that represents this specific row.
+                $iconFieldName = '';
+                if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'])) {
+                    $iconFieldName = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'];
+                }
+                $iconPath = '';
+                if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'])) {
+                    $iconPath = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'];
+                }
                 if ($iconFieldName && $iconPath && $foreignRow[$iconFieldName]) {
+                    // Prepare the row icon if available
                     $iParts = GeneralUtility::trimExplode(',', $foreignRow[$iconFieldName], true);
                     $icon = '../' . $iconPath . '/' . trim($iParts[0]);
                 } else {
+                    // Else, determine icon based on record type, or a generic fallback
                     $icon = $iconFactory->mapRecordTypeToIconIdentifier($foreignTable, $foreignRow);
                 }
                 // Add the item
@@ -878,27 +891,18 @@ abstract class AbstractItemProvider
     }
 
     /**
-     * Build query to fetch foreign records
+     * Build query to fetch foreign records. Helper method of
+     * addItemsFromForeignTable(), do not call otherwise.
      *
      * @param array $result Result array
      * @param string $localFieldName Current handle field name
      * @return array Query array ready to be executed via Database->exec_SELECT_queryArray()
-     * @throws \UnexpectedValueException
      */
     protected function buildForeignTableQuery(array $result, $localFieldName)
     {
         $backendUser = $this->getBackendUser();
 
         $foreignTableName = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table'];
-
-        if (!is_array($GLOBALS['TCA'][$foreignTableName])) {
-            throw new \UnexpectedValueException(
-                'Field ' . $localFieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
-                . $foreignTableName . ', but this table is not defined in TCA',
-                1439569743
-            );
-        }
-
         $foreignTableClauseArray = $this->processForeignTableClause($result, $foreignTableName, $localFieldName);
 
         $queryArray = [];
index e17996d..7e9c948 100644 (file)
@@ -1727,6 +1727,85 @@ class TcaSelectItemsTest extends UnitTestCase
     /**
      * @test
      */
+    public function addDataForeignTableResolvesIconFromSelicon()
+    {
+        $input = [
+            'databaseRow' => [
+                'aField' => '',
+            ],
+            'tableName' => 'aTable',
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'select',
+                            'renderType' => 'selectSingle',
+                            'foreign_table' => 'fTable',
+                            'maxitems' => 1,
+                        ],
+                    ],
+                ]
+            ],
+            'rootline' => [],
+        ];
+
+        // Fake the foreign_table
+        $GLOBALS['TCA']['fTable'] = [
+            'ctrl' => [
+                'label' => 'icon',
+                'selicon_field' => 'icon',
+                'selicon_field_path' => 'uploads/media',
+            ],
+            'columns' =>[
+                'icon' => [
+                    'config' => [
+                        'type' => 'group',
+                    ],
+                ],
+            ],
+        ];
+
+        /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
+        $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
+        $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
+        $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
+
+        /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
+        $languageServiceProphecy = $this->prophesize(LanguageService::class);
+        $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
+        $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
+
+        /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
+        $databaseProphecy = $this->prophesize(DatabaseConnection::class);
+        $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
+        $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
+        $databaseProphecy->sql_free_result(Argument::cetera())->willReturn(null);
+        // Query on foreign table is successful
+        $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(true);
+        // Query returns one row, then false on second call
+        $foreignTableRowResultOne = [
+            'uid' => 1,
+            'icon' => 'foo.jpg',
+        ];
+        $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn($foreignTableRowResultOne, false);
+
+        $expected = $input;
+        $expected['processedTca']['columns']['aField']['config']['items'] = [
+            0 => [
+                0 => 'foo.jpg',
+                1 => 1,
+                2 => '../uploads/media/foo.jpg', // combination of selicon_field_path and the row value of field 'icon'
+                3 => null,
+            ],
+        ];
+        $expected['databaseRow']['aField'] = [];
+
+        $this->assertEquals($expected, $this->subject->addData($input));
+    }
+
+    /**
+     * @test
+     */
     public function addDataRemovesItemsByKeepItemsPageTsConfig()
     {
         $input = [