2 namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use Doctrine\DBAL\DBALException
;
18 use Doctrine\DBAL\Driver\Statement
;
19 use Prophecy\Argument
;
20 use Prophecy\Prophecy\ObjectProphecy
;
21 use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSelectItems
;
22 use TYPO3\CMS\Backend\Module\ModuleLoader
;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
24 use TYPO3\CMS\Core\Database\Connection
;
25 use TYPO3\CMS\Core\Database\ConnectionPool
;
26 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
;
27 use TYPO3\CMS\Core\Database\Query\QueryBuilder
;
28 use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer
;
29 use TYPO3\CMS\Core\Database\RelationHandler
;
30 use TYPO3\CMS\Core\Localization\LanguageService
;
31 use TYPO3\CMS\Core\Messaging\FlashMessage
;
32 use TYPO3\CMS\Core\Messaging\FlashMessageQueue
;
33 use TYPO3\CMS\Core\Messaging\FlashMessageService
;
34 use TYPO3\CMS\Core\Utility\ArrayUtility
;
35 use TYPO3\CMS\Core\Utility\GeneralUtility
;
36 use TYPO3\TestingFramework\Core\Unit\UnitTestCase
;
41 class TcaSelectItemsTest
extends UnitTestCase
44 * @var array A backup of registered singleton instances
46 protected $singletonInstances = [];
48 protected function setUp()
50 $this->singletonInstances
= GeneralUtility
::getSingletonInstances();
52 // Default LANG prophecy just returns incoming value as label if calling ->sL()
53 $languageServiceProphecy = $this->prophesize(LanguageService
::class);
54 $languageServiceProphecy->loadSingleTableDescription(Argument
::cetera())->willReturn(null);
55 $languageServiceProphecy->sL(Argument
::cetera())->willReturnArgument(0);
56 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
58 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
59 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
62 protected function tearDown()
64 GeneralUtility
::purgeInstances();
65 GeneralUtility
::resetSingletonInstances($this->singletonInstances
);
70 * Prepare a mock database setup for a Doctrine connection
71 * and return an array of all prophets to set expectations upon.
73 * @param string $tableName
76 protected function mockDatabaseConnection($tableName = 'fTable')
78 $connectionProphet = $this->prophesize(Connection
::class);
79 $connectionProphet->quote(Argument
::cetera())->will(function ($arguments) {
80 return "'" . $arguments[0] . "'";
82 $connectionProphet->quoteIdentifier(Argument
::cetera())->will(function ($arguments) {
83 return '`' . $arguments[0] . '`';
86 $restrictionProphet = $this->prophesize(DefaultRestrictionContainer
::class);
87 $restrictionProphet->removeAll()->willReturn($restrictionProphet->reveal());
88 $restrictionProphet->add(Argument
::cetera())->willReturn($restrictionProphet->reveal());
90 $queryBuilderProphet = $this->prophesize(QueryBuilder
::class);
91 $queryBuilderProphet->expr()->willReturn(
92 GeneralUtility
::makeInstance(ExpressionBuilder
::class, $connectionProphet->reveal())
94 $queryBuilderProphet->getRestrictions()->willReturn($restrictionProphet->reveal());
95 $queryBuilderProphet->quoteIdentifier(Argument
::cetera())->will(function ($arguments) {
96 return '`' . $arguments[0] . '`';
99 $connectionPoolProphet = $this->prophesize(ConnectionPool
::class);
100 $connectionPoolProphet->getConnectionForTable($tableName)
101 ->willReturn($connectionProphet->reveal());
102 $connectionPoolProphet->getQueryBuilderForTable($tableName)
104 ->willReturn($queryBuilderProphet->reveal());
106 return [$queryBuilderProphet, $connectionPoolProphet, $connectionProphet, $restrictionProphet];
110 * Mock a doctrine database connection with all expectations
111 * required for the processSelectField* tests.
113 protected function mockDatabaseConnectionForProcessSelectField()
115 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection('foreignTable');
117 /** @var Statement|ObjectProphecy $statementProphet */
118 $statementProphet = $this->prophesize(Statement
::class);
119 $statementProphet->fetch()->shouldBeCalled();
121 $queryBuilderProphet->select('foreignTable.uid')
123 ->willReturn($queryBuilderProphet->reveal());
124 $queryBuilderProphet->from('foreignTable')
126 ->willReturn($queryBuilderProphet->reveal());
127 $queryBuilderProphet->from('pages')
129 ->willReturn($queryBuilderProphet->reveal());
130 $queryBuilderProphet->where('')
132 ->willReturn($queryBuilderProphet->reveal());
133 $queryBuilderProphet->andWhere(' 1=1')
135 ->willReturn($queryBuilderProphet->reveal());
136 $queryBuilderProphet->andWhere('`pages.uid` = `foreignTable.pid`')
138 ->willReturn($queryBuilderProphet->reveal());
139 $queryBuilderProphet->execute()
141 ->willReturn($statementProphet->reveal());
143 // Two instances are needed due to the push/pop behavior of addInstance()
144 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
145 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
151 public function addDataKeepExistingItems()
154 'tableName' => 'aTable',
184 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
190 public function addDataThrowsExceptionIfAnItemIsNotAnArray()
193 'tableName' => 'aTable',
199 'renderType' => 'selectSingle',
209 $this->expectException(\UnexpectedValueException
::class);
210 $this->expectExceptionCode(1439288036);
212 (new TcaSelectItems
)->addData($input);
218 public function addDataTranslatesItemLabels()
221 'tableName' => 'aTable',
223 'aField' => 'aValue',
230 'renderType' => 'selectSingle',
244 /** @var LanguageService|ObjectProphecy $languageService */
245 $languageService = $this->prophesize(LanguageService
::class);
246 $GLOBALS['LANG'] = $languageService->reveal();
247 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
249 $languageService->sL('aLabel')->shouldBeCalled()->willReturn('translated');
252 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'translated';
253 $expected['processedTca']['columns']['aField']['config']['items'][0][2] = null;
254 $expected['processedTca']['columns']['aField']['config']['items'][0][3] = null;
256 $expected['databaseRow']['aField'] = ['aValue'];
258 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
264 public function addDataKeepsIconFromItem()
267 'tableName' => 'aTable',
269 'aField' => 'aValue',
276 'renderType' => 'selectSingle',
281 2 => 'an-icon-reference',
293 $expected['databaseRow']['aField'] = ['aValue'];
295 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
301 public function addDataThrowsExceptionWithUnknownSpecialValue()
304 'tableName' => 'aTable',
310 'renderType' => 'selectSingle',
311 'special' => 'anUnknownValue',
318 $this->expectException(\UnexpectedValueException
::class);
319 $this->expectExceptionCode(1439298496);
321 (new TcaSelectItems
)->addData($input);
327 public function addDataAddsTablesWithSpecialTables()
333 'tableName' => 'aTable',
339 'renderType' => 'selectSingle',
340 'special' => 'tables',
359 $GLOBALS['TCA_DESCR']['aTable']['columns']['']['description'] = 'aDescription';
361 /** @var LanguageService|ObjectProphecy $languageService */
362 $languageService = $this->prophesize(LanguageService
::class);
363 $GLOBALS['LANG'] = $languageService->reveal();
364 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
365 $languageService->sL(Argument
::containingString('INVALID VALUE'))->willReturnArgument(0);
367 $languageService->sL('aTitle')->shouldBeCalled()->willReturnArgument(0);
368 $languageService->loadSingleTableDescription('aTable')->shouldBeCalled();
371 $expected['databaseRow']['aField'] = [];
372 $expected['processedTca']['columns']['aField']['config']['items'] = [
376 2 => 'default-not-found',
378 'description' => 'aDescription',
383 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
389 public function addDataAddsTablesWithSpecialPageTypes()
393 'aField' => 'aValue',
395 'tableName' => 'aTable',
401 'renderType' => 'selectSingle',
402 'special' => 'pagetypes',
427 /** @var LanguageService|ObjectProphecy $languageService */
428 $languageService = $this->prophesize(LanguageService
::class);
429 $GLOBALS['LANG'] = $languageService->reveal();
430 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
432 $languageService->sL('aLabel')->shouldBeCalled()->willReturnArgument(0);
435 $expected['databaseRow']['aField'] = ['aValue'];
436 $expected['processedTca']['columns']['aField']['config']['items'] = [
440 2 => 'default-not-found',
445 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
451 public function addDataAddsExcludeFieldsWithSpecialExcludeDataProvider()
454 'Table with exclude and non exclude field returns exclude item' => [
459 'title' => 'fooTableTitle',
463 'label' => 'barColumnTitle',
467 'label' => 'bazColumnTitle',
475 0 => 'fooTableTitle',
477 2 => 'default-not-found',
481 0 => 'barColumnTitle (bar)',
488 'Root level table with ignored root level restriction returns exclude item' => [
493 'title' => 'fooTableTitle',
496 'ignoreRootLevelRestriction' => true,
501 'label' => 'barColumnTitle',
510 0 => 'fooTableTitle',
512 2 => 'default-not-found',
516 0 => 'barColumnTitle (bar)',
523 'Root level table without ignored root level restriction returns no item' => [
528 'title' => 'fooTableTitle',
533 'label' => 'barColumnTitle',
543 'Admin table returns no item' => [
548 'title' => 'fooTableTitle',
553 'label' => 'barColumnTitle',
568 * @dataProvider addDataAddsExcludeFieldsWithSpecialExcludeDataProvider
570 public function addDataAddsExcludeFieldsWithSpecialExclude($tca, $expectedItems)
573 'tableName' => 'aTable',
580 'renderType' => 'selectSingle',
581 'special' => 'exclude',
587 $GLOBALS['TCA'] = $tca;
589 $result = (new TcaSelectItems
)->addData($input);
591 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
597 public function addDataAddsExcludeFieldsFromFlexWithSpecialExclude()
600 'tableName' => 'aTable',
607 'renderType' => 'selectSingle',
608 'special' => 'exclude',
618 'title' => 'fooTableTitle',
622 'label' => 'aFlexFieldTitle',
634 <label>flexInputLabel</label>
655 0 => 'fooTableTitle aFlexFieldTitle dummy',
657 2 => 'default-not-found',
661 0 => 'flexInputLabel (input1)',
662 1 => 'fooTable:aFlexField;dummy;sDEF;input1',
668 $result = (new TcaSelectItems
)->addData($input);
670 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
676 public function addDataAddsExplicitAllowFieldsWithSpecialExplicitValues()
679 'tableName' => 'aTable',
686 'renderType' => 'selectSingle',
687 'special' => 'explicitValues',
697 'title' => 'fooTableTitle',
701 'label' => 'aFieldTitle',
704 'renderType' => 'selectSingle',
705 'authMode' => 'explicitAllow',
718 /** @var LanguageService|ObjectProphecy $languageService */
719 $languageService = $this->prophesize(LanguageService
::class);
720 $GLOBALS['LANG'] = $languageService->reveal();
721 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.allow')->shouldBeCalled()->willReturn('allowMe');
722 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
726 0 => 'fooTableTitle: aFieldTitle',
732 0 => '[allowMe] anItemTitle',
733 1 => 'fooTable:aField:anItemValue:ALLOW',
734 2 => 'status-status-permission-granted',
739 $result = (new TcaSelectItems
)->addData($input);
741 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
747 public function addDataAddsExplicitDenyFieldsWithSpecialExplicitValues()
750 'tableName' => 'aTable',
757 'renderType' => 'selectSingle',
758 'special' => 'explicitValues',
768 'title' => 'fooTableTitle',
772 'label' => 'aFieldTitle',
775 'renderType' => 'selectSingle',
776 'authMode' => 'explicitDeny',
789 /** @var LanguageService|ObjectProphecy $languageService */
790 $languageService = $this->prophesize(LanguageService
::class);
791 $GLOBALS['LANG'] = $languageService->reveal();
792 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.deny')->shouldBeCalled()->willReturn('denyMe');
793 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
797 0 => 'fooTableTitle: aFieldTitle',
803 0 => '[denyMe] anItemTitle',
804 1 => 'fooTable:aField:anItemValue:DENY',
805 2 => 'status-status-permission-denied',
810 $result = (new TcaSelectItems
)->addData($input);
812 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
818 public function addDataAddsExplicitIndividualAllowFieldsWithSpecialExplicitValues()
821 'tableName' => 'aTable',
828 'renderType' => 'selectSingle',
829 'special' => 'explicitValues',
839 'title' => 'fooTableTitle',
843 'label' => 'aFieldTitle',
846 'renderType' => 'selectSingle',
847 'authMode' => 'individual',
856 // 1 is not selectable as allow and is always allowed
875 /** @var LanguageService|ObjectProphecy $languageService */
876 $languageService = $this->prophesize(LanguageService
::class);
877 $GLOBALS['LANG'] = $languageService->reveal();
878 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.allow')->shouldBeCalled()->willReturn('allowMe');
879 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
883 0 => 'fooTableTitle: aFieldTitle',
889 0 => '[allowMe] aItemTitle',
890 1 => 'fooTable:aField:aItemValue:ALLOW',
891 2 => 'status-status-permission-granted',
895 0 => '[allowMe] cItemTitle',
896 1 => 'fooTable:aField:cItemValue:ALLOW',
897 2 => 'status-status-permission-granted',
902 $result = (new TcaSelectItems
)->addData($input);
904 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
910 public function addDataAddsExplicitIndividualDenyFieldsWithSpecialExplicitValues()
913 'tableName' => 'aTable',
920 'renderType' => 'selectSingle',
921 'special' => 'explicitValues',
931 'title' => 'fooTableTitle',
935 'label' => 'aFieldTitle',
938 'renderType' => 'selectSingle',
939 'authMode' => 'individual',
948 // 1 is not selectable as allow and is always allowed
967 /** @var LanguageService|ObjectProphecy $languageService */
968 $languageService = $this->prophesize(LanguageService
::class);
969 $GLOBALS['LANG'] = $languageService->reveal();
970 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.deny')->shouldBeCalled()->willReturn('denyMe');
971 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
975 0 => 'fooTableTitle: aFieldTitle',
981 0 => '[denyMe] aItemTitle',
982 1 => 'fooTable:aField:aItemValue:DENY',
983 2 => 'status-status-permission-denied',
987 0 => '[denyMe] cItemTitle',
988 1 => 'fooTable:aField:cItemValue:DENY',
989 2 => 'status-status-permission-denied',
994 $result = (new TcaSelectItems
)->addData($input);
996 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1002 public function addDataAddsLanguagesWithSpecialLanguages()
1005 'tableName' => 'aTable',
1006 'databaseRow' => [],
1012 'renderType' => 'selectSingle',
1013 'special' => 'languages',
1018 'systemLanguageRows' => [
1020 'title' => 'aLangTitle',
1022 'flagIconIdentifier' => 'aFlag.gif',
1029 0 => 'aLangTitle [42]',
1036 $result = (new TcaSelectItems
)->addData($input);
1038 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1044 public function addDataAddsCustomOptionsWithSpecialCustom()
1047 'tableName' => 'aTable',
1048 'databaseRow' => [],
1054 'renderType' => 'selectSingle',
1055 'special' => 'custom',
1062 $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'] = [
1064 'header' => 'aHeader',
1070 0 => 'anotherTitle',
1071 1 => 'status-status-permission-denied',
1072 2 => 'aDescription',
1087 1 => 'aKey:anItemKey',
1092 0 => 'anotherTitle',
1093 1 => 'aKey:anotherKey',
1094 2 => 'status-status-permission-denied',
1095 3 => [ 'description' => 'aDescription' ],
1099 $result = (new TcaSelectItems
)->addData($input);
1101 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1107 public function addDataAddsGroupItemsWithSpecialModListGroup()
1110 'tableName' => 'aTable',
1111 'databaseRow' => [],
1117 'renderType' => 'selectSingle',
1118 'special' => 'modListGroup',
1125 $GLOBALS['TBE_MODULES'] = [];
1127 /** @var ModuleLoader|ObjectProphecy $moduleLoaderProphecy */
1128 $moduleLoaderProphecy = $this->prophesize(ModuleLoader
::class);
1129 GeneralUtility
::addInstance(ModuleLoader
::class, $moduleLoaderProphecy->reveal());
1130 $moduleLoaderProphecy->load([])->shouldBeCalled();
1131 $moduleLoaderProphecy->modListGroup
= [
1134 $moduleLoaderProphecy->modules
= [
1136 'iconIdentifier' => 'empty-empty'
1139 $moduleLoaderProphecy->getLabelsForModule('aModule')->shouldBeCalled()->willReturn([
1140 'shortdescription' => 'aModuleTabLabel',
1141 'description' => 'aModuleTabDescription',
1142 'title' => 'aModuleLabel'
1147 0 => 'aModuleLabel',
1151 'title' => 'aModuleTabLabel',
1152 'description' => 'aModuleTabDescription',
1157 $result = (new TcaSelectItems
)->addData($input);
1159 $result['processedTca']['columns']['aField']['config']['items'][0][2] = str_replace([CR
, LF
, TAB
], ['', '', ''], $result['processedTca']['columns']['aField']['config']['items'][0][2]);
1160 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1166 public function addDataAddsFileItemsWithConfiguredFileFolder()
1168 $directory = $this->getUniqueId('typo3temp/var/tests/test-') . '/';
1170 'tableName' => 'aTable',
1171 'databaseRow' => [],
1177 'renderType' => 'selectSingle',
1178 'fileFolder' => $directory,
1179 'fileFolder_extList' => 'gif',
1180 'fileFolder_recursions' => 1,
1187 mkdir(PATH_site
. $directory);
1188 $this->testFilesToDelete
[] = PATH_site
. $directory;
1189 touch(PATH_site
. $directory . 'anImage.gif');
1190 touch(PATH_site
. $directory . 'aFile.txt');
1191 mkdir(PATH_site
. $directory . '/subdir');
1192 touch(PATH_site
. $directory . '/subdir/anotherImage.gif');
1198 2 => PATH_site
. $directory . 'anImage.gif',
1202 0 => 'subdir/anotherImage.gif',
1203 1 => 'subdir/anotherImage.gif',
1204 2 => PATH_site
. $directory . 'subdir/anotherImage.gif',
1209 $result = (new TcaSelectItems
)->addData($input);
1211 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1217 public function addDataThrowsExceptionForInvalidFileFolder()
1220 'tableName' => 'aTable',
1221 'databaseRow' => [],
1227 'renderType' => 'selectSingle',
1228 'fileFolder' => 'EXT:non_existing/Resources/Public/',
1235 $this->expectException(\RuntimeException
::class);
1236 $this->expectExceptionCode(1479399227);
1237 (new TcaSelectItems
)->addData($input);
1243 public function addDataAddsItemsByAddItemsFromPageTsConfig()
1249 'tableName' => 'aTable',
1255 'renderType' => 'selectSingle',
1264 'maxitems' => 99999,
1283 $expected['databaseRow']['aField'] = [];
1284 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1291 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
1297 public function addDataAddsItemsByAddItemsWithDuplicateValuesFromPageTsConfig()
1303 'tableName' => 'aTable',
1309 'renderType' => 'selectSingle',
1318 'maxitems' => 99999,
1337 $expected['databaseRow']['aField'] = [];
1338 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1345 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
1351 public function addDataReplacesMarkersInForeignTableClauseDataProvider()
1354 'replace REC_FIELD' => [
1355 'AND fTable.title=\'###REC_FIELD_rowField###\'',
1357 ['fTable.title=\'rowFieldValue\''],
1359 ['`pages.uid` = `fTable.pid`']
1363 'replace REC_FIELD within FlexForm' => [
1364 'AND fTable.title=###REC_FIELD_rowFieldFlexForm###',
1366 ['fTable.title=\'rowFieldFlexFormValue\''],
1368 ['`pages.uid` = `fTable.pid`']
1372 'rowFieldThree' => [
1373 0 => 'rowFieldThreeValue'
1376 'flexParentDatabaseRow' => [
1377 'rowFieldFlexForm' => [
1378 0 => 'rowFieldFlexFormValue'
1383 'replace REC_FIELD fullQuote' => [
1384 'AND fTable.title=###REC_FIELD_rowField###',
1386 ['fTable.title=\'rowFieldValue\''],
1388 ['`pages.uid` = `fTable.pid`']
1392 'replace REC_FIELD fullQuoteWithArray' => [
1393 'AND fTable.title=###REC_FIELD_rowFieldThree###',
1395 ['fTable.title=\'rowFieldThreeValue\''],
1397 ['`pages.uid` = `fTable.pid`']
1401 'rowFieldThree' => [
1402 0 => 'rowFieldThreeValue'
1407 'replace REC_FIELD multiple markers' => [
1408 'AND fTable.title=\'###REC_FIELD_rowField###\' AND fTable.pid=###REC_FIELD_rowFieldTwo###',
1410 ['fTable.title=\'rowFieldValue\' AND fTable.pid=\'rowFieldTwoValue\''],
1412 ['`pages.uid` = `fTable.pid`']
1416 'replace CURRENT_PID' => [
1417 'AND fTable.uid=###CURRENT_PID###',
1421 ['`pages.uid` = `fTable.pid`']
1425 'replace CURRENT_PID within FlexForm' => [
1426 'AND fTable.uid=###CURRENT_PID###',
1430 ['`pages.uid` = `fTable.pid`']
1433 'flexParentDatabaseRow' => [
1438 'replace CURRENT_PID integer cast' => [
1439 'AND fTable.uid=###CURRENT_PID###',
1443 ['`pages.uid` = `fTable.pid`']
1446 'effectivePid' => '431string',
1449 'replace THIS_UID' => [
1450 'AND fTable.uid=###THIS_UID###',
1454 ['`pages.uid` = `fTable.pid`']
1458 'replace THIS_UID integer cast' => [
1459 'AND fTable.uid=###THIS_UID###',
1463 ['`pages.uid` = `fTable.pid`']
1467 'uid' => '421string',
1471 'replace SITEROOT' => [
1472 'AND fTable.uid=###SITEROOT###',
1476 ['`pages.uid` = `fTable.pid`']
1480 'replace SITEROOT integer cast' => [
1481 'AND fTable.uid=###SITEROOT###',
1485 ['`pages.uid` = `fTable.pid`']
1490 'uid' => '441string',
1495 'replace PAGE_TSCONFIG_ID' => [
1496 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1500 ['`pages.uid` = `fTable.pid`']
1507 'PAGE_TSCONFIG_ID' => '45',
1514 'replace PAGE_TSCONFIG_ID integer cast' => [
1515 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1519 ['`pages.uid` = `fTable.pid`']
1526 'PAGE_TSCONFIG_ID' => '451string'
1533 'replace PAGE_TSCONFIG_STR' => [
1534 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1536 ['fTable.uid=\'46\''],
1538 ['`pages.uid` = `fTable.pid`']
1545 'PAGE_TSCONFIG_STR' => '46',
1552 'replace PAGE_TSCONFIG_IDLIST' => [
1553 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1555 ['fTable.uid IN (47,48)'],
1557 ['`pages.uid` = `fTable.pid`']
1564 'PAGE_TSCONFIG_IDLIST' => '47,48',
1571 'replace PAGE_TSCONFIG_IDLIST cleans list' => [
1572 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1574 ['fTable.uid IN (471,481)'],
1576 ['`pages.uid` = `fTable.pid`']
1583 'PAGE_TSCONFIG_IDLIST' => 'a, 471, b, 481, c',
1595 * @dataProvider addDataReplacesMarkersInForeignTableClauseDataProvider
1597 public function addDataReplacesMarkersInForeignTableClause($foreignTableWhere, $expectedWhere, array $inputOverride)
1600 'tableName' => 'aTable',
1601 'effectivePid' => 43,
1604 'rowField' => 'rowFieldValue',
1605 'rowFieldTwo' => 'rowFieldTwoValue',
1612 'renderType' => 'selectSingle',
1613 'foreign_table' => 'fTable',
1614 'foreign_table_where' => $foreignTableWhere,
1630 'is_siteroot' => null,
1633 'pageTsConfig' => [],
1635 ArrayUtility
::mergeRecursiveWithOverrule($input, $inputOverride);
1637 $GLOBALS['TCA']['fTable'] = [];
1639 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1641 /** @var Statement|ObjectProphecy $statementProphet */
1642 $statementProphet = $this->prophesize(Statement
::class);
1644 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1645 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1646 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1647 $queryBuilderProphet->where(...array_shift($expectedWhere))->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1648 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1650 while ($constraint = array_shift($expectedWhere)) {
1651 $queryBuilderProphet->andWhere(...$constraint)
1653 ->willReturn($queryBuilderProphet->reveal());
1656 // Two instances are needed due to the push/pop behavior of addInstance()
1657 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1658 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1660 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1661 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
1662 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1663 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1665 (new TcaSelectItems
)->addData($input);
1671 public function addDataThrowsExceptionIfForeignTableIsNotDefinedInTca()
1674 'tableName' => 'aTable',
1680 'renderType' => 'selectSingle',
1681 'foreign_table' => 'fTable',
1688 $this->expectException(\UnexpectedValueException
::class);
1689 $this->expectExceptionCode(1439569743);
1691 (new TcaSelectItems
)->addData($input);
1697 public function addDataForeignTableSplitsGroupOrderAndLimit()
1700 'tableName' => 'aTable',
1701 'effectivePid' => 42,
1710 'renderType' => 'selectSingle',
1711 'foreign_table' => 'fTable',
1712 'foreign_table_where' => '
1714 GROUP BY groupField1, groupField2
1724 $GLOBALS['TCA']['fTable'] = [];
1726 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1727 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
1728 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1729 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1731 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1733 /** @var Statement|ObjectProphecy $statementProphet */
1734 $statementProphet = $this->prophesize(Statement
::class);
1736 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1737 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1738 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1739 $queryBuilderProphet->groupBy('groupField1', 'groupField2')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1740 $queryBuilderProphet->addOrderBy('orderField', null)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1741 $queryBuilderProphet->setFirstResult(1)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1742 $queryBuilderProphet->setMaxResults(2)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1743 $queryBuilderProphet->where('ftable.uid=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1744 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1745 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1746 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1748 // Two instances are needed due to the push/pop behavior of addInstance()
1749 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1750 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1752 (new TcaSelectItems
)->addData($input);
1758 public function addDataForeignTableQueuesFlashMessageOnDatabaseError()
1765 'tableName' => 'aTable',
1766 'effectivePid' => 42,
1772 'renderType' => 'selectSingle',
1773 'foreign_table' => 'fTable',
1782 'maxitems' => 99999,
1790 $GLOBALS['TCA']['fTable'] = [];
1792 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1793 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
1794 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1795 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1797 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1799 /** @var Statement|ObjectProphecy $statementProphet */
1800 $statementProphet = $this->prophesize(Statement
::class);
1802 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1803 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1804 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1805 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1806 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1807 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1809 $prevException = new DBALException('Invalid table name', 1476045274);
1810 $exception = new DBALException('Driver error', 1476045971, $prevException);
1812 $queryBuilderProphet->execute()->shouldBeCalled()->willThrow($exception);
1814 // Two instances are needed due to the push/pop behavior of addInstance()
1815 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1816 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1818 /** @var FlashMessage|ObjectProphecy $flashMessage */
1819 $flashMessage = $this->prophesize(FlashMessage
::class);
1820 GeneralUtility
::addInstance(FlashMessage
::class, $flashMessage->reveal());
1821 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
1822 $flashMessageService = $this->prophesize(FlashMessageService
::class);
1823 GeneralUtility
::setSingletonInstance(FlashMessageService
::class, $flashMessageService->reveal());
1824 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
1825 $flashMessageQueue = $this->prophesize(FlashMessageQueue
::class);
1826 $flashMessageService->getMessageQueueByIdentifier(Argument
::cetera())->willReturn($flashMessageQueue->reveal());
1828 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
1831 $expected['databaseRow']['aField'] = [];
1833 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
1839 public function addDataForeignTableHandlesForeignTableRows()
1846 'tableName' => 'aTable',
1847 'effectivePid' => 42,
1853 'renderType' => 'selectSingle',
1854 'foreign_table' => 'fTable',
1855 'foreign_table_prefix' => 'aPrefix',
1857 'maxitems' => 99999,
1865 $GLOBALS['TCA']['fTable'] = [
1867 'label' => 'labelField',
1872 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1873 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
1874 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1875 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1877 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1879 /** @var Statement|ObjectProphecy $statementProphet */
1880 $statementProphet = $this->prophesize(Statement
::class);
1882 $queryBuilderProphet->select('fTable.uid', 'fTable.labelField')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1883 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1884 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1885 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1886 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1887 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1888 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1890 // Two instances are needed due to the push/pop behavior of addInstance()
1891 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1892 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1895 $statementProphet->fetch()->shouldBeCalled()->will(function ($args) use (&$counter) {
1897 if ($counter >= 3) {
1903 'labelField' => 'aLabel',
1909 $expected['processedTca']['columns']['aField']['config']['items'] = [
1911 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
1913 2 => 'default-not-found',
1917 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
1919 2 => 'default-not-found',
1924 $expected['databaseRow']['aField'] = [];
1926 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
1932 public function addDataForeignTableResolvesIconFromSelicon()
1939 'tableName' => 'aTable',
1940 'effectivePid' => 42,
1946 'renderType' => 'selectSingle',
1947 'foreign_table' => 'fTable',
1948 'maxitems' => 99999,
1956 // Fake the foreign_table
1957 $GLOBALS['TCA']['fTable'] = [
1960 'selicon_field' => 'icon',
1961 'selicon_field_path' => 'uploads/media',
1968 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1969 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
1970 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1971 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1973 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1975 /** @var Statement|ObjectProphecy $statementProphet */
1976 $statementProphet = $this->prophesize(Statement
::class);
1978 $queryBuilderProphet->select('fTable.uid', 'fTable.icon')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1979 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1980 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1981 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1982 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1983 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1984 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1986 // Two instances are needed due to the push/pop behavior of addInstance()
1987 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1988 GeneralUtility
::addInstance(ConnectionPool
::class, $connectionPoolProphet->reveal());
1990 // Query returns one row, then false on second call
1991 $foreignTableRowResultOne = [
1994 'icon' => 'foo.jpg',
1996 $statementProphet->fetch()->shouldBeCalled()->willReturn($foreignTableRowResultOne, false);
1999 $expected['processedTca']['columns']['aField']['config']['items'] = [
2003 2 => 'uploads/media/foo.jpg', // combination of selicon_field_path and the row value of field 'icon'
2007 $expected['databaseRow']['aField'] = [];
2009 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2015 public function addDataRemovesItemsByKeepItemsPageTsConfig()
2021 'tableName' => 'aTable',
2027 'renderType' => 'selectSingle',
2044 'maxitems' => 99999,
2053 'keepItems' => 'keep',
2061 $expected['databaseRow']['aField'] = [];
2063 $expected['processedTca']['columns']['aField']['config']['items'][1],
2064 $expected['processedTca']['columns']['aField']['config']['items'][2]
2067 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2073 public function addDataRemovesAllItemsByEmptyKeepItemsPageTsConfig()
2079 'tableName' => 'aTable',
2085 'renderType' => 'selectSingle',
2098 'maxitems' => 99999,
2115 $expected['databaseRow']['aField'] = [];
2116 $expected['processedTca']['columns']['aField']['config']['items'] = [];
2118 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2124 public function addDataEvaluatesKeepItemsBeforeAddItemsFromPageTsConfig()
2130 'tableName' => 'aTable',
2136 'renderType' => 'selectSingle',
2149 'maxitems' => 99999,
2160 '1' => 'addItem #1',
2161 '12' => 'addItem #12',
2170 $expected['databaseRow']['aField'] = [];
2171 $expected['processedTca']['columns']['aField']['config']['items'] = [
2192 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2198 public function addDataRemovesItemsByRemoveItemsPageTsConfig()
2204 'tableName' => 'aTable',
2210 'renderType' => 'selectSingle',
2229 'maxitems' => 99999,
2238 'removeItems' => 'remove',
2246 $expected['databaseRow']['aField'] = [];
2247 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2248 $expected['processedTca']['columns']['aField']['config']['items'] = array_values($expected['processedTca']['columns']['aField']['config']['items']);
2249 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2255 public function addDataRemovesItemsByZeroValueRemoveItemsPageTsConfig()
2261 'tableName' => 'aTable',
2267 'renderType' => 'selectSingle',
2286 'maxitems' => 99999,
2295 'removeItems' => '0',
2303 $expected['databaseRow']['aField'] = [];
2304 unset($expected['processedTca']['columns']['aField']['config']['items'][2]);
2305 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2311 public function addDataRemovesItemsAddedByAddItemsFromPageTsConfigByRemoveItemsPageTsConfig()
2317 'tableName' => 'aTable',
2323 'renderType' => 'selectSingle',
2336 'maxitems' => 99999,
2345 'removeItems' => 'remove,add',
2356 $expected['databaseRow']['aField'] = [];
2357 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2359 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2365 public function addDataRemovesItemsByLanguageFieldUserRestriction()
2369 'aField' => 'aValue,remove'
2371 'tableName' => 'aTable',
2374 'languageField' => 'aField',
2380 'renderType' => 'selectSingle',
2393 'maxitems' => 99999,
2400 /** @var LanguageService|ObjectProphecy $languageService */
2401 $languageService = $this->prophesize(LanguageService
::class);
2402 $GLOBALS['LANG'] = $languageService->reveal();
2403 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2404 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
2406 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2407 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2408 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2409 $backendUserProphecy->checkLanguageAccess('keep')->shouldBeCalled()->willReturn(true);
2410 $backendUserProphecy->checkLanguageAccess('remove')->shouldBeCalled()->willReturn(false);
2413 $expected['databaseRow']['aField'] = [];
2414 $expected['processedTca']['columns']['aField']['config']['items'] = [
2415 [ '[ INVALID VALUE "aValue" ]', 'aValue', null, null ],
2416 [ 'keepMe', 'keep', null, null ],
2419 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2425 public function addDataRemovesItemsByUserAuthModeRestriction()
2429 'aField' => 'keep,remove'
2431 'tableName' => 'aTable',
2437 'renderType' => 'selectSingle',
2438 'authMode' => 'explicitAllow',
2451 'maxitems' => 99999,
2458 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2459 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2460 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2461 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'keep', 'explicitAllow')->shouldBeCalled()->willReturn(true);
2462 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'remove', 'explicitAllow')->shouldBeCalled()->willReturn(false);
2465 $expected['databaseRow']['aField'] = ['keep'];
2466 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2468 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2474 public function addDataKeepsAllPagesDoktypesForAdminUser()
2480 'tableName' => 'pages',
2486 'renderType' => 'selectSingle',
2495 'maxitems' => 99999,
2502 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2503 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2504 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2505 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
2508 $expected['databaseRow']['doktype'] = ['keep'];
2510 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2516 public function addDataKeepsAllowedPageTypesForNonAdminUser()
2520 'doktype' => 'keep',
2522 'tableName' => 'pages',
2528 'renderType' => 'selectSingle',
2541 'maxitems' => 99999,
2548 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2549 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2550 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2551 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
2552 $backendUserProphecy->groupData
= [
2553 'pagetypes_select' => 'foo,keep,anotherAllowedDoktype',
2557 $expected['databaseRow']['doktype'] = ['keep'];
2558 unset($expected['processedTca']['columns']['doktype']['config']['items'][1]);
2560 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2566 public function addDataCallsItemsProcFunc()
2569 'tableName' => 'aTable',
2571 'aField' => 'aValue'
2578 'renderType' => 'selectSingle',
2580 'itemsProcFunc' => function (array $parameters, $pObj) {
2581 $parameters['items'] = [
2597 $expected['databaseRow']['aField'] = ['aValue'];
2598 $expected['processedTca']['columns']['aField']['config'] = [
2600 'renderType' => 'selectSingle',
2609 'maxitems' => 99999,
2612 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
2618 public function addDataItemsProcFuncReceivesParameters()
2621 'tableName' => 'aTable',
2623 'aField' => 'aValue',
2629 'itemsProcFunc.' => [
2630 'itemParamKey' => 'itemParamValue',
2641 'renderType' => 'selectSingle',
2649 'itemsProcFunc' => function (array $parameters, $pObj) {
2650 if ($parameters['items'] !== [ 0 => [ 'aLabel', 'aValue'] ]
2651 ||
$parameters['config']['aKey'] !== 'aValue'
2652 ||
$parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
2653 ||
$parameters['table'] !== 'aTable'
2654 ||
$parameters['row'] !== [ 'aField' => 'aValue' ]
2655 ||
$parameters['field'] !== 'aField'
2657 throw new \
UnexpectedValueException('broken', 1476109436);
2666 $languageService = $this->prophesize(LanguageService
::class);
2667 $GLOBALS['LANG'] = $languageService->reveal();
2668 $languageService->sL(Argument
::cetera())->willReturnArgument(0);
2669 /** @var FlashMessage|ObjectProphecy $flashMessage */
2670 $flashMessage = $this->prophesize(FlashMessage
::class);
2671 GeneralUtility
::addInstance(FlashMessage
::class, $flashMessage->reveal());
2672 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2673 $flashMessageService = $this->prophesize(FlashMessageService
::class);
2674 GeneralUtility
::setSingletonInstance(FlashMessageService
::class, $flashMessageService->reveal());
2675 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2676 $flashMessageQueue = $this->prophesize(FlashMessageQueue
::class);
2677 $flashMessageService->getMessageQueueByIdentifier(Argument
::cetera())->willReturn($flashMessageQueue->reveal());
2679 // itemsProcFunc must NOT have raised an exception
2680 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
2682 (new TcaSelectItems
)->addData($input);
2688 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
2691 'tableName' => 'aTable',
2693 'aField' => 'aValue',
2699 'itemsProcFunc.' => [
2700 'itemParamKey' => 'itemParamValue',
2711 'renderType' => 'selectSingle',
2719 'itemsProcFunc' => function (array $parameters, $pObj) {
2720 throw new \
UnexpectedValueException('anException', 1476109437);
2728 $languageService = $this->prophesize(LanguageService
::class);
2729 $GLOBALS['LANG'] = $languageService->reveal();
2730 /** @var FlashMessage|ObjectProphecy $flashMessage */
2731 $flashMessage = $this->prophesize(FlashMessage
::class);
2732 GeneralUtility
::addInstance(FlashMessage
::class, $flashMessage->reveal());
2733 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2734 $flashMessageService = $this->prophesize(FlashMessageService
::class);
2735 GeneralUtility
::setSingletonInstance(FlashMessageService
::class, $flashMessageService->reveal());
2736 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2737 $flashMessageQueue = $this->prophesize(FlashMessageQueue
::class);
2738 $flashMessageService->getMessageQueueByIdentifier(Argument
::cetera())->willReturn($flashMessageQueue->reveal());
2740 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
2742 (new TcaSelectItems
)->addData($input);
2748 public function addDataTranslatesItemLabelsFromPageTsConfig()
2752 'aField' => 'aValue',
2754 'tableName' => 'aTable',
2760 'renderType' => 'selectSingle',
2769 'maxitems' => 99999,
2779 'aValue' => 'labelOverride',
2787 /** @var LanguageService|ObjectProphecy $languageService */
2788 $languageService = $this->prophesize(LanguageService
::class);
2789 $GLOBALS['LANG'] = $languageService->reveal();
2790 $languageService->sL('aLabel')->willReturnArgument(0);
2791 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
2792 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2795 $expected['databaseRow']['aField'] = ['aValue'];
2796 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
2798 $this->assertSame($expected, (new TcaSelectItems
)->addData($input));
2804 public function processSelectFieldValueSetsMmForeignRelationValues()
2806 $GLOBALS['TCA']['foreignTable'] = [];
2808 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2809 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2810 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2811 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2813 $this->mockDatabaseConnectionForProcessSelectField();
2816 'command' => 'edit',
2817 'tableName' => 'aTable',
2818 'effectivePid' => 23,
2821 // Two connected rows
2829 'renderType' => 'selectSingle',
2831 'foreign_table' => 'foreignTable',
2832 'MM' => 'aTable_foreignTable_mm',
2839 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2840 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2841 $relationHandlerProphecy = $this->prophesize(RelationHandler
::class);
2842 GeneralUtility
::addInstance(RelationHandler
::class, $relationHandlerProphecy->reveal());
2844 $relationHandlerUids = [
2849 $relationHandlerProphecy->start(2, 'foreignTable', 'aTable_foreignTable_mm', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2850 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2853 $expected['databaseRow']['aField'] = $relationHandlerUids;
2855 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2861 public function processSelectFieldValueSetsForeignRelationValues()
2863 $GLOBALS['TCA']['foreignTable'] = [];
2865 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2866 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2867 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2868 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2870 $this->mockDatabaseConnectionForProcessSelectField();
2873 'tableName' => 'aTable',
2874 'effectivePid' => 23,
2877 // Two connected rows
2878 'aField' => '22,23,24,25',
2885 'renderType' => 'selectSingle',
2887 'foreign_table' => 'foreignTable',
2894 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2895 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2896 $relationHandlerProphecy = $this->prophesize(RelationHandler
::class);
2897 GeneralUtility
::addInstance(RelationHandler
::class, $relationHandlerProphecy->reveal());
2899 $relationHandlerUids = [
2904 $relationHandlerProphecy->start('22,23,24,25', 'foreignTable', '', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2905 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2908 $expected['databaseRow']['aField'] = $relationHandlerUids;
2910 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2916 public function processSelectFieldValueRemovesInvalidDynamicValues()
2918 $GLOBALS['TCA']['foreignTable'] = [];
2920 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2921 $backendUserProphecy = $this->prophesize(BackendUserAuthentication
::class);
2922 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2923 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2925 $this->mockDatabaseConnectionForProcessSelectField();
2927 $relationHandlerProphecy = $this->prophesize(RelationHandler
::class);
2928 GeneralUtility
::addInstance(RelationHandler
::class, $relationHandlerProphecy->reveal());
2929 $relationHandlerProphecy->start(Argument
::cetera())->shouldBeCalled();
2930 $relationHandlerProphecy->getValueArray(Argument
::cetera())->shouldBeCalled()->willReturn([1]);
2933 'tableName' => 'aTable',
2934 'effectivePid' => 23,
2937 'aField' => '1,2,bar,foo',
2944 'renderType' => 'selectSingleBox',
2945 'foreign_table' => 'foreignTable',
2948 ['foo', 'foo', null, null],
2957 $expected['databaseRow']['aField'] = ['foo', 1];
2959 $this->assertEquals($expected, (new TcaSelectItems
)->addData($input));
2965 public function processSelectFieldValueKeepsValuesFromStaticItems()
2968 'tableName' => 'aTable',
2970 'aField' => 'foo,bar',
2977 'renderType' => 'selectSingle',
2980 ['foo', 'foo', null, null],
2981 ['bar', 'bar', null, null],