[TASK] FormEngine: Inline BackendUtility::getRecordTitle as data provider 85/43485/12
authorMorton Jonuschat <m.jonuschat@mojocode.de>
Mon, 21 Sep 2015 18:04:57 +0000 (20:04 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Fri, 2 Oct 2015 11:15:29 +0000 (13:15 +0200)
Provide the foundation to get rid of BackendUtility::getRecordTitle()
in FormEngine scope by:

 * adding a recordTitle field to the FormDataCompiler result array
 * adding a provider that fills in the recordTitle field
 * using the recordTitle in OuterWrapContainer where appropriate
 * ensuring the TcaTypesRemoveUnusedColumns provider does not remove
   columns used to generate the label.

Resolves: #69721
Releases: master
Change-Id: Ibb4edd25f786b1af47657677d6d9baa27dfb3665
Reviewed-on: http://review.typo3.org/43485
Reviewed-by: Nicole Cordes <typo3@cordes.co>
Tested-by: Nicole Cordes <typo3@cordes.co>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/backend/Classes/Form/Container/OuterWrapContainer.php
typo3/sysext/backend/Classes/Form/FormDataCompiler.php
typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Form/FormDataProvider/TcaTypesRemoveUnusedColumns.php
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaRecordTitleTest.php [new file with mode: 0644]
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaTypesRemoveUnusedColumnsTest.php
typo3/sysext/core/Configuration/DefaultConfiguration.php

index b3aed3c..9aeb130 100644 (file)
@@ -92,7 +92,8 @@ class OuterWrapContainer extends AbstractContainer {
 
                        $newOrUid = ' <span class="typo3-TCEforms-recUid">[' . htmlspecialchars($row['uid']) . ']</span>';
 
-                       $recordLabel = BackendUtility::getRecordTitle($table, FormEngineUtility::databaseRowCompatibility($row), TRUE, FALSE);
+                       // @todo: getRecordTitlePrep applies an htmlspecialchars here
+                       $recordLabel = BackendUtility::getRecordTitlePrep($this->data['recordTitle']);
                        if ($table === 'pages') {
                                $label = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.editPage', TRUE);
                                $pageTitle = sprintf($label, $tableTitle, $recordLabel);
@@ -100,13 +101,13 @@ class OuterWrapContainer extends AbstractContainer {
                                $label = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.editRecord', TRUE);
                                $workspacedPageRecord = BackendUtility::getRecordWSOL('pages', $row['pid'], 'uid,title');
                                $pageTitle = BackendUtility::getRecordTitle('pages', $workspacedPageRecord, TRUE, FALSE);
-                               if ($recordLabel === BackendUtility::getNoRecordTitle(TRUE)) {
+                               if (empty($recordLabel)) {
                                        $label = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.editRecordNoTitle', TRUE);
                                }
                                if ($this->data['effectivePid'] === 0) {
                                        $label = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.editRecordRootLevel', TRUE);
                                }
-                               if ($recordLabel !== BackendUtility::getNoRecordTitle(TRUE)) {
+                               if (!empty($recordLabel)) {
                                        // Use record title and prepend an edit label.
                                        $pageTitle = sprintf($label, $tableTitle, $recordLabel, $pageTitle);
                                } else {
index 8f4f70d..2c6c927 100644 (file)
@@ -116,6 +116,8 @@ class FormDataCompiler {
                        'vanillaUid' => 0,
                        // Url to return to
                        'returnUrl' => NULL,
+                       // Title of the handled record.
+                       'recordTitle' => '',
                        // Parent page record is either the full row of the parent page the record is located at or should
                        // be added to, or it is NULL, if a record is added or edited below the root page node.
                        'parentPageRow' => NULL,
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php
new file mode 100644 (file)
index 0000000..6872320
--- /dev/null
@@ -0,0 +1,326 @@
+<?php
+namespace TYPO3\CMS\Backend\Form\FormDataProvider;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Database\DatabaseConnection;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Lang\LanguageService;
+
+/**
+ * Determine the title of a record and write it to $result['recordTitle'].
+ *
+ * TCA ctrl fields like label and label_alt are evaluated and their
+ * current values from databaseRow used to create the title.
+ */
+class TcaRecordTitle implements FormDataProviderInterface {
+
+       /**
+        * Enrich the processed record information with the resolved title
+        *
+        * @param array $result Incoming result array
+        * @return array Modified array
+        */
+       public function addData(array $result) {
+               if (!isset($result['processedTca']['ctrl']['label'])) {
+                       throw new \UnexpectedValueException(
+                               'TCA of table ' . $result['tableName'] . ' misses required [\'ctrl\'][\'label\'] definition.',
+                               1443706103
+                       );
+               }
+
+               if (isset($result['processedTca']['ctrl']['label_userFunc'])) {
+                       // userFunc takes precedence over everything
+                       $parameters = [
+                               'table' => $result['tableName'],
+                               'row' => $result['databaseRow'],
+                               'title' => '',
+                               'options' => isset($result['processedTca']['ctrl']['label_userFunc_options'])
+                                       ? $result['processedTca']['ctrl']['label_userFunc_options']
+                                       : [],
+                       ];
+                       $null = NULL;
+                       GeneralUtility::callUserFunction($result['processedTca']['ctrl']['label_userFunc'], $parameters, $null);
+                       $result['recordTitle'] = $parameters['title'];
+               } else {
+                       $result = $this->getRecordTitleByLabelProperties($result);
+               }
+
+               return $result;
+       }
+
+       /**
+        * Build the record title from label, label_alt and label_alt_force properties
+        *
+        * @param array $result Incoming result array
+        * @return array Modified result array
+        */
+       protected function getRecordTitleByLabelProperties(array $result) {
+               $titles = [];
+               $titleByLabel = $this->getRecordTitleForField($result['processedTca']['ctrl']['label'], $result);
+               if (!empty($titleByLabel)) {
+                       $titles[] = $titleByLabel;
+               }
+
+               $labelAltForce = isset($result['processedTca']['ctrl']['label_alt_force'])
+                       ? (bool)$result['processedTca']['ctrl']['label_alt_force']
+                       : FALSE;
+               if (!empty($result['processedTca']['ctrl']['label_alt']) && ($labelAltForce || empty($titleByLabel))) {
+                       // Dive into label_alt evaluation if label_alt_force is set or if label did not came up with a title yet
+                       $labelAltFields = GeneralUtility::trimExplode(',', $result['processedTca']['ctrl']['label_alt'], TRUE);
+                       foreach ($labelAltFields as $fieldName) {
+                               $titleByLabelAlt = $this->getRecordTitleForField($fieldName, $result);
+                               if (!empty($titleByLabelAlt)) {
+                                       $titles[] = $titleByLabelAlt;
+                               }
+                               if (!$labelAltForce && !empty($titleByLabelAlt)) {
+                                       // label_alt_force creates a comma separated list of multiple fields.
+                                       // If not set, one found field with content is enough
+                                       break;
+                               }
+                       }
+               }
+
+               $result['recordTitle'] = implode(', ', $titles);
+               return $result;
+       }
+
+       /**
+        * Record title of a single field
+        *
+        * @param string $fieldName Field to handle
+        * @param array $result Incoming result array
+        * @return string
+        */
+       protected function getRecordTitleForField($fieldName, $result) {
+               if ($fieldName === 'uid') {
+                       // uid return field content directly since it usually has not TCA definition
+                       return $result['databaseRow']['uid'];
+               }
+
+               if (!isset($result['processedTca']['columns'][$fieldName]['config']['type'])
+                       || !is_string($result['processedTca']['columns'][$fieldName]['config']['type'])
+               ) {
+                       return '';
+               }
+
+               $recordTitle = '';
+               $rawValue = NULL;
+               if (array_key_exists($fieldName, $result['databaseRow'])) {
+                       $rawValue = $result['databaseRow'][$fieldName];
+               }
+               $fieldConfig = $result['processedTca']['columns'][$fieldName]['config'];
+               switch ($fieldConfig['type']) {
+                       case 'radio':
+                               $recordTitle = $this->getRecordTitleForRadioType($rawValue, $fieldConfig);
+                               break;
+                       case 'inline':
+                               // intentional fall-through
+                       case 'select':
+                               $recordTitle = $this->getRecordTitleForSelectType($rawValue, $fieldConfig);
+                               break;
+                       case 'group':
+                               $recordTitle = $this->getRecordTitleForGroupType($rawValue, $fieldConfig);
+                               break;
+                       case 'check':
+                               $recordTitle = $this->getRecordTitleForCheckboxType($rawValue, $fieldConfig);
+                               break;
+                       case 'input':
+                               $recordTitle = $this->getRecordTitleForInputType($rawValue, $fieldConfig);
+                               break;
+                       case 'text':
+                               $recordTitle = $this->getRecordTitleForTextType($rawValue);
+                       case 'flex':
+                               // TODO: Check if and how a label could be generated from flex field data
+                       default:
+
+               }
+
+               return $recordTitle;
+       }
+
+       /**
+        * Return the record title for radio fields
+        *
+        * @param mixed $value Current database value of this field
+        * @param array $fieldConfig TCA field configuration
+        * @return string
+        */
+       protected function getRecordTitleForRadioType($value, $fieldConfig) {
+               if (!isset($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
+                       return '';
+               }
+               foreach ($fieldConfig['items'] as $item) {
+                       list($itemLabel, $itemValue) = $item;
+                       if ((string)$value === (string)$itemValue) {
+                               return $itemLabel;
+                       }
+               }
+               return '';
+       }
+
+       /**
+        * Return the record title for database records
+        *
+        * @param mixed $value Current database value of this field
+        * @param array $fieldConfig TCA field configuration
+        * @return string
+        */
+       protected function getRecordTitleForSelectType($value, $fieldConfig) {
+               if (!is_array($value)) {
+                       return '';
+               }
+               $labelParts = [];
+               foreach ($value as $itemValue) {
+                       $itemKey = array_search($itemValue, array_column($fieldConfig['items'], 1));
+                       if ($itemKey !== FALSE) {
+                               $labelParts[] = $fieldConfig['items'][$itemKey][0];
+                       }
+               }
+               $title = implode(', ', $labelParts);
+               if (empty($title) && !empty($value)) {
+                       $title = implode(', ', $value);
+               }
+               return $title;
+       }
+
+       /**
+        * Return the record title for database records
+        *
+        * @param mixed $value Current database value of this field
+        * @param array $fieldConfig TCA field configuration
+        * @return string
+        */
+       protected function getRecordTitleForGroupType($value, $fieldConfig) {
+               if ($fieldConfig['internal_type'] !== 'db') {
+                       return implode(', ', GeneralUtility::trimExplode(',', $value, TRUE));
+               }
+               $labelParts = array_map(
+                       function($rawLabelItem) {
+                               return array_pop(GeneralUtility::trimExplode('|', $rawLabelItem, TRUE, 2));
+                       },
+                       GeneralUtility::trimExplode(',', $value, TRUE)
+               );
+               if (!empty($labelParts)) {
+                       sort($labelParts);
+                       return implode(', ', $labelParts);
+               }
+               return '';
+       }
+
+       /**
+        * Returns the record title for checkbox fields
+        *
+        * @param mixed $value Current database value of this field
+        * @param array $fieldConfig TCA field configuration
+        * @return string
+        */
+       protected function getRecordTitleForCheckboxType($value, $fieldConfig) {
+               $languageService = $this->getLanguageService();
+               if (empty($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
+                       $title = (bool)$value
+                               ? $languageService->sL('LLL:EXT:lang/locallang_common.xlf:yes')
+                               : $languageService->sL('LLL:EXT:lang/locallang_common.xlf:no');
+               } else {
+                       $labelParts = [];
+                       foreach ($fieldConfig['items'] as $key => $val) {
+                               if ($value & pow(2, $key)) {
+                                       $labelParts[] = $val[0];
+                               }
+                       }
+                       $title = implode(', ', $labelParts);
+               }
+               return $title;
+       }
+
+       /**
+        * Returns the record title for input fields
+        *
+        * @param mixed $value Current database value of this field
+        * @param array $fieldConfig TCA field configuration
+        * @return string
+        */
+       protected function getRecordTitleForInputType($value, $fieldConfig) {
+               if (!isset($value)) {
+                       return '';
+               }
+               $title = $value;
+               if (GeneralUtility::inList($fieldConfig['eval'], 'date')) {
+                       if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'date') {
+                               $value = $value === '0000-00-00' ? 0 : (int)strtotime($value);
+                       } else {
+                               $value = (int)$value;
+                       }
+                       if (!empty($value)) {
+                               $ageSuffix = '';
+                               // Generate age suffix as long as not explicitly suppressed
+                               if (!isset($fieldConfig['disableAgeDisplay']) || (bool)$fieldConfig['disableAgeDisplay'] === FALSE) {
+                                       $ageDelta = $GLOBALS['EXEC_TIME'] - $value;
+                                       $calculatedAge = BackendUtility::calcAge(
+                                               abs($ageDelta),
+                                               $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')
+                                       );
+                                       $ageSuffix = ' (' . ($ageDelta > 0 ? '-' : '') . $calculatedAge . ')';
+                               }
+                               $title = BackendUtility::date($value) . $ageSuffix;
+                       }
+               } elseif (GeneralUtility::inList($fieldConfig['eval'], 'time')) {
+                       if (!empty($value)) {
+                               $title = BackendUtility::time((int)$value, FALSE);
+                       }
+               } elseif (GeneralUtility::inList($fieldConfig['eval'], 'timesec')) {
+                       if (!empty($value)) {
+                               $title = BackendUtility::time((int)$value);
+                       }
+               } elseif (GeneralUtility::inList($fieldConfig['eval'], 'datetime')) {
+                       // Handle native date/time field
+                       if (isset($fieldConfig['dbType']) && $fieldConfig['dbType'] === 'datetime') {
+                               $value = $value === '0000-00-00 00:00:00' ? 0 : (int)strtotime($value);
+                       } else {
+                               $value = (int)$value;
+                       }
+                       if (!empty($value)) {
+                               $title = BackendUtility::datetime($value);
+                       }
+               }
+               return $title;
+       }
+
+       /**
+        * Returns the record title for text fields
+        *
+        * @param mixed $value Current database value of this field
+        * @return string
+        */
+       protected function getRecordTitleForTextType($value) {
+               return trim(strip_tags($value));
+       }
+
+       /**
+        * @return DatabaseConnection
+        */
+       protected function getDatabaseConnection() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
+       /**
+        * @return LanguageService
+        */
+       protected function getLanguageService() {
+               return $GLOBALS['LANG'];
+       }
+
+}
index 51b580b..c0c6a73 100644 (file)
@@ -43,7 +43,16 @@ class TcaTypesRemoveUnusedColumns implements FormDataProviderInterface {
 
                $showItemFieldString = $result['processedTca']['types'][$recordTypeValue]['showitem'];
                $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, TRUE);
-               $shownColumnFields = [];
+
+               // Do not remove fields that are used for record title calculation
+               $shownColumnFields = empty($result['processedTca']['ctrl']['label']) ? [] : [$result['processedTca']['ctrl']['label']];
+               if (!empty($result['processedTca']['ctrl']['label_alt'])) {
+                       $shownColumnFields = array_merge(
+                               $shownColumnFields,
+                               GeneralUtility::trimExplode(',', $result['processedTca']['ctrl']['label_alt'], TRUE)
+                       );
+               }
+
                foreach ($showItemFieldArray as $fieldConfigurationString) {
                        $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
                        $fieldName = $fieldConfigurationArray[0];
diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaRecordTitleTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaRecordTitleTest.php
new file mode 100644 (file)
index 0000000..93b290b
--- /dev/null
@@ -0,0 +1,796 @@
+<?php
+namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Prophecy\Argument;
+use Prophecy\Prophecy\ObjectProphecy;
+use TYPO3\CMS\Backend\Form\FormDataProvider\TcaRecordTitle;
+use TYPO3\CMS\Core\Tests\UnitTestCase;
+use TYPO3\CMS\Lang\LanguageService;
+
+/**
+ * Test case
+ */
+class TcaRecordTitleTest extends UnitTestCase {
+
+       /**
+        * @var TcaRecordTitle
+        */
+       protected $subject;
+
+       /**
+        * @var string
+        */
+       protected $timeZone;
+
+       public function setUp() {
+               $this->subject = new TcaRecordTitle();
+               $this->timeZone = date_default_timezone_get();
+               date_default_timezone_set('UTC');
+       }
+
+       protected function tearDown() {
+               date_default_timezone_set($this->timeZone);
+       }
+
+       /**
+        * @test
+        */
+       public function addDataThrowsExceptionWithMissingLabel() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRew' => [],
+                       'processedTca' => [
+                               'ctrl' => [],
+                       ],
+               ];
+               $this->setExpectedException(\UnexpectedValueException::class, $this->any(), 1443706103);
+               $this->subject->addData($input);
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForLabelUserFunction() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'uid',
+                                       'label_userFunc' => function (&$parameters) {
+                                               $parameters['title'] = 'Test';
+                                       }
+                               ],
+                               'columns' => [],
+                       ],
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'Test';
+
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForUid() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => 'NEW56017ee37d10e587251374',
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'uid'
+                               ],
+                               'columns' => [],
+                       ]
+               ];
+
+               /** @var LanguageService|ObjectProphecy $languageService */
+               $languageService = $this->prophesize(LanguageService::class);
+               $GLOBALS['LANG'] = $languageService->reveal();
+               $languageService->sL(Argument::cetera())->willReturnArgument(0);
+
+               $expected = $input;
+               $expected['recordTitle'] = 'NEW56017ee37d10e587251374';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * Data provider for addDataReturnsRecordTitleForInputType
+        * Each data set is an array with the following elements:
+        *  - TCA field ['config'] section
+        *  - Database value for field
+        *  - expected title to be generated
+        *
+        * @returns array
+        */
+       public function addDataReturnsRecordTitleForInputTypeDataProvider() {
+               return [
+                       'new record' => [
+                               [
+                                       'type' => 'input',
+                               ],
+                               '',
+                               '',
+                       ],
+                       'plain text input' => [
+                               [
+                                       'type' => 'input',
+                               ],
+                               'aValue',
+                               'aValue',
+                       ],
+                       'date input' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'date'
+                               ],
+                               '978307261',
+                               '01-01-01 (-7 days)',
+                       ],
+                       'date input (dbType: date)' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'date',
+                                       'dbType' => 'date'
+                               ],
+                               '2001-01-01',
+                               '01-01-01 (-7 days)',
+                       ],
+                       'date input (disableAgeDisplay: TRUE)' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'date',
+                                       'disableAgeDisplay' => TRUE
+                               ],
+                               '978307261',
+                               '01-01-01',
+                       ],
+                       'time input' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'time',
+                               ],
+                               '44100',
+                               '12:15',
+                       ],
+                       'timesec input' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'timesec',
+                               ],
+                               '44130',
+                               '12:15:30',
+                       ],
+                       'datetime input' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'datetime',
+                                       'dbType' => 'date'
+                               ],
+                               '978307261',
+                               '01-01-01 00:01',
+                       ],
+                       'datetime input (dbType: datetime)' => [
+                               [
+                                       'type' => 'input',
+                                       'eval' => 'datetime',
+                                       'dbType' => 'datetime'
+                               ],
+                               '2014-12-31 23:59:59',
+                               '31-12-14 23:59',
+                       ],
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider addDataReturnsRecordTitleForInputTypeDataProvider
+        *
+        * @param array $fieldConfig
+        * @param string $fieldValue
+        * @param string $expectedTitle
+        */
+       public function addDataReturnsRecordTitleForInputType($fieldConfig, $fieldValue, $expectedTitle) {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => $fieldValue,
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => $fieldConfig,
+                                       ]
+                               ],
+                       ]
+               ];
+
+               /** @var LanguageService|ObjectProphecy $languageService */
+               $languageService = $this->prophesize(LanguageService::class);
+               $GLOBALS['LANG'] = $languageService->reveal();
+               $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')
+                       ->willReturn(' min| hrs| days| yrs| min| hour| day| year');
+               $languageService->sL(Argument::cetera())->willReturnArgument(0);
+               $GLOBALS['EXEC_TIME'] = 978912061;
+
+               $expected = $input;
+               $expected['recordTitle'] = $expectedTitle;
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleWithAlternativeLabel() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => '',
+                               'anotherField' => 'anotherValue',
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                                       'label_alt' => 'anotherField',
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'anotherField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'anotherValue';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleWithMultipleAlternativeLabels() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => '',
+                               'anotherField' => '',
+                               'additionalField' => 'additionalValue'
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                                       'label_alt' => 'anotherField,additionalField',
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'anotherField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'additionalField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'additionalValue';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleWithForcedAlternativeLabel() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => 'aField',
+                               'anotherField' => 'anotherField'
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                                       'label_alt' => 'anotherField',
+                                       'label_alt_force' => TRUE,
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'anotherField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'aField, anotherField';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleWithMultipleForcedAlternativeLabels() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => 'aField',
+                               'anotherField' => 'anotherField',
+                               'additionalField' => 'additionalValue'
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                                       'label_alt' => 'anotherField,additionalField',
+                                       'label_alt_force' => TRUE,
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'anotherField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'additionalField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'aField, anotherField, additionalValue';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleIgnoresEmptyAlternativeLabels() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => 'aField',
+                               'anotherField' => '',
+                               'additionalField' => 'additionalValue'
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                                       'label_alt' => 'anotherField,additionalField',
+                                       'label_alt_force' => TRUE,
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'anotherField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                                       'additionalField' => [
+                                               'config' => [
+                                                       'type' => 'input'
+                                               ]
+                                       ],
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'aField, additionalValue';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForRadioType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => '2',
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'radio',
+                                                       'items' => [
+                                                               ['foo', 1],
+                                                               ['bar', 2],
+                                                               ['baz', 3],
+                                                       ]
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'bar';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * Data provider for addDataReturnsRecordTitleForGroupType
+        * Each data set is an array with the following elements:
+        *  - TCA field configuration (merged with base config)
+        *  - Database value for field
+        *  - expected title to be generated
+        *
+        * @returns array
+        */
+       public function addDataReturnsRecordTitleForGroupTypeDataProvider() {
+               return [
+                       'new record' => [
+                               [
+                                       'internal_type' => 'db',
+                               ],
+                               '',
+                               ''
+                       ],
+                       'internal_type: file' => [
+                               [
+                                       'internal_type' => 'file',
+                               ],
+                               'somePath/aFile.jpg,someOtherPath/anotherFile.png',
+                               'somePath/aFile.jpg, someOtherPath/anotherFile.png',
+                       ],
+                       'internal_type: db, single table, single record' => [
+                               [
+                                       'internal_type' => 'db',
+                                       'allowed' => 'aTable'
+                               ],
+                               '1|aValue',
+                               'aValue',
+                       ],
+                       'internal_type: db, single table, multiple records' => [
+                               [
+                                       'internal_type' => 'db',
+                                       'allowed' => 'aTable'
+                               ],
+                               '1|aValue,3|anotherValue',
+                               'aValue, anotherValue',
+                       ],
+                       'internal_type: db, multiple tables, single record' => [
+                               [
+                                       'internal_type' => 'db',
+                                       'allowed' => 'aTable,anotherTable'
+                               ],
+                               'anotherTable_1|anotherValue',
+                               'anotherValue',
+                       ],
+                       'internal_type: db, multiple tables, multiple records' => [
+                               [
+                                       'internal_type' => 'db',
+                                       'allowed' => 'aTable,anotherTable'
+                               ],
+                               'anotherTable_1|anotherValue,aTable_1|aValue',
+                               'aValue, anotherValue',
+                       ],
+               ];
+       }
+
+       /**
+        * @test
+        * @dataProvider addDataReturnsRecordTitleForGroupTypeDataProvider
+        *
+        * @param array $fieldConfig
+        * @param string $fieldValue
+        * @param string $expectedTitle
+        */
+       public function addDataReturnsRecordTitleForGroupType($fieldConfig, $fieldValue, $expectedTitle) {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => $fieldValue,
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => array_merge(
+                                                       [
+                                                               'type' => 'group',
+                                                       ],
+                                                       $fieldConfig
+                                               ),
+                                       ]
+                               ],
+                       ]
+               ];
+
+               /** @var LanguageService|ObjectProphecy $languageService */
+               $languageService = $this->prophesize(LanguageService::class);
+               $GLOBALS['LANG'] = $languageService->reveal();
+               $languageService->sL(Argument::cetera())->willReturnArgument(0);
+
+               $expected = $input;
+               $expected['recordTitle'] = $expectedTitle;
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForGroupTypeWithInternalTypeDb() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'uid' => '1',
+                               'aField' => 'aTable_1|aValue,anotherTable_2|anotherValue',
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'group',
+                                                       'internal_type' => 'db',
+                                                       'allowed' => 'aTable,anotherTable',
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'aValue, anotherValue';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForSingleCheckboxType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'aField' => 1,
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'check',
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               /** @var LanguageService|ObjectProphecy $languageService */
+               $languageService = $this->prophesize(LanguageService::class);
+               $GLOBALS['LANG'] = $languageService->reveal();
+               $languageService->sL(Argument::cetera())->willReturnArgument(0)->shouldBeCalled();
+
+               $expected = $input;
+               $expected['recordTitle'] = 'LLL:EXT:lang/locallang_common.xlf:yes';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForArrayCheckboxType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'aField' => '5'
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'check',
+                                                       'items' => [
+                                                               ['foo', ''],
+                                                               ['bar', ''],
+                                                               ['baz', ''],
+                                                       ]
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'foo, baz';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsEmptyRecordTitleForFlexType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'aField' => [
+                                       'data' => [
+                                               'sDEF' => [
+                                                       'lDEF' => [
+                                                               'aFlexField' => [
+                                                                       'vDEF' => 'aFlexValue',
+                                                               ]
+                                                       ]
+                                               ]
+                                       ]
+                               ]
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'flex',
+                                                       'ds' => [
+                                                               'sheets' => [
+                                                                       'sDEF' => [
+                                                                               'ROOT' => [
+                                                                                       'type' => 'array',
+                                                                                       'el' => [
+                                                                                               'aFlexField' => [
+                                                                                                       'label' => 'Some input field',
+                                                                                                       'config' => [
+                                                                                                               'type' => 'input',
+                                                                                                       ],
+                                                                                               ],
+                                                                                       ],
+                                                                               ],
+                                                                       ],
+                                                               ],
+                                                       ]
+
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = '';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsRecordTitleForSelectType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'aField' => [
+                                       '1',
+                                       '2'
+                               ]
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField'
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'select',
+                                                       'items' => [
+                                                               ['foo', 1, NULL, NULL],
+                                                               ['bar', 2, NULL, NULL],
+                                                               ['baz', 4, NULL, NULL],
+                                                       ]
+                                               ]
+                                       ]
+                               ],
+                       ]
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'foo, bar';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataReturnsStrippedAndTrimmedValueForTextType() {
+               $input = [
+                       'tableName' => 'aTable',
+                       'databaseRow' => [
+                               'aField' => '<p> text </p>',
+                       ],
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'aField',
+                               ],
+                               'columns' => [
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'text',
+                                               ],
+                                       ],
+                               ],
+                       ],
+               ];
+
+               $expected = $input;
+               $expected['recordTitle'] = 'text';
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+}
index bee73bc..8cbea5d 100644 (file)
@@ -81,7 +81,6 @@ class TcaTypesRemoveUnusedColumnsTest extends UnitTestCase {
                                'palettes' => [
                                        'aPalette' => array(
                                                'showitem' => 'keepMe',
-                                               'canNotCollapse' => TRUE
                                        ),
                                ],
                                'columns' => [
@@ -121,7 +120,6 @@ class TcaTypesRemoveUnusedColumnsTest extends UnitTestCase {
                                'palettes' => [
                                        'aPalette' => array(
                                                'showitem' => 'aField',
-                                               'canNotCollapse' => TRUE
                                        ),
                                ],
                                'columns' => [
@@ -150,4 +148,99 @@ class TcaTypesRemoveUnusedColumnsTest extends UnitTestCase {
                $this->assertSame($expected, $this->subject->addData($input));
        }
 
+       /**
+        * @test
+        */
+       public function addDataKeepsColumnsFieldReferencedInLabel() {
+               $input = [
+                       'databaseRow' => [],
+                       'recordTypeValue' => 'aType',
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'keepMe'
+                               ],
+                               'types' => [
+                                       'aType' => [
+                                               'showitem' => 'keepMeToo'
+                                       ],
+                               ],
+                               'palettes' => [
+                                       'aPalette' => array(
+                                               'showitem' => 'keepMe',
+                                       ),
+                               ],
+                               'columns' => [
+                                       'keepMe' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ],
+                                       'keepMeToo' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ],
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ]
+                               ]
+                       ]
+               ];
+
+               $expected = $input;
+               unset($expected['processedTca']['columns']['aField']);
+
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
+       /**
+        * @test
+        */
+       public function addDataKeepsColumnsFieldReferencedInLabelAlt() {
+               $input = [
+                       'databaseRow' => [],
+                       'recordTypeValue' => 'aType',
+                       'processedTca' => [
+                               'ctrl' => [
+                                       'label' => 'keepMe',
+                                       'label_alt' => 'keepMeToo'
+                               ],
+                               'types' => [
+                                       'aType' => [
+                                               'showitem' => 'keepMe'
+                                       ],
+                               ],
+                               'palettes' => [
+                                       'aPalette' => array(
+                                               'showitem' => 'keepMe',
+                                       ),
+                               ],
+                               'columns' => [
+                                       'keepMe' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ],
+                                       'keepMeToo' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ],
+                                       'aField' => [
+                                               'config' => [
+                                                       'type' => 'input',
+                                               ]
+                                       ]
+                               ]
+                       ]
+               ];
+
+               $expected = $input;
+               unset($expected['processedTca']['columns']['aField']);
+
+               $this->assertSame($expected, $this->subject->addData($input));
+       }
+
 }
index 96a4917..8ed9668 100644 (file)
@@ -467,11 +467,16 @@ return array(
                                                        \TYPO3\CMS\Backend\Form\FormDataProvider\TcaSelectItems::class,
                                                ),
                                        ),
-                                       \TYPO3\CMS\Backend\Form\FormDataProvider\EvaluateDisplayConditions::class => array(
+                                       \TYPO3\CMS\Backend\Form\FormDataProvider\TcaRecordTitle::class => array(
                                                'depends' => array(
                                                        \TYPO3\CMS\Backend\Form\FormDataProvider\TcaInline::class,
                                                ),
                                        ),
+                                       \TYPO3\CMS\Backend\Form\FormDataProvider\EvaluateDisplayConditions::class => array(
+                                               'depends' => array(
+                                                       \TYPO3\CMS\Backend\Form\FormDataProvider\TcaRecordTitle::class,
+                                               ),
+                                       ),
                                ),
                                'flexFormSegment' => array(
                                        \TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseRowDefaultValues::class => array(),