[BUGFIX] Fix foreign_table_where GROUP BY handling
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / TcaSelectItemsTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
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.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
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
37 /**
38 * Test case
39 */
40 class TcaSelectItemsTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
41 {
42 /**
43 * @var TcaSelectItems|\PHPUnit_Framework_MockObject_MockObject
44 */
45 protected $subject;
46
47 /**
48 * @var array A backup of registered singleton instances
49 */
50 protected $singletonInstances = [];
51
52 protected function setUp()
53 {
54 $this->singletonInstances = GeneralUtility::getSingletonInstances();
55 $this->subject = $this->getMockBuilder(TcaSelectItems::class)
56 ->setMethods(['getDatabaseRow'])
57 ->getMock();
58
59 $this->subject = new TcaSelectItems();
60 }
61
62 protected function tearDown()
63 {
64 GeneralUtility::purgeInstances();
65 GeneralUtility::resetSingletonInstances($this->singletonInstances);
66 parent::tearDown();
67 }
68
69 /**
70 * Prepare a mock database setup for a Doctrine connection
71 * and return an array of all prophets to set expectations upon.
72 *
73 * @param string $tableName
74 * @return array
75 */
76 protected function mockDatabaseConnection($tableName = 'fTable')
77 {
78 $connectionProphet = $this->prophesize(Connection::class);
79 $connectionProphet->quote(Argument::cetera())->will(function ($arguments) {
80 return "'" . $arguments[0] . "'";
81 });
82 $connectionProphet->quoteIdentifier(Argument::cetera())->will(function ($arguments) {
83 return '`' . $arguments[0] . '`';
84 });
85
86 $restrictionProphet = $this->prophesize(DefaultRestrictionContainer::class);
87 $restrictionProphet->removeAll()->willReturn($restrictionProphet->reveal());
88 $restrictionProphet->add(Argument::cetera())->willReturn($restrictionProphet->reveal());
89
90 $queryBuilderProphet = $this->prophesize(QueryBuilder::class);
91 $queryBuilderProphet->expr()->willReturn(
92 GeneralUtility::makeInstance(ExpressionBuilder::class, $connectionProphet->reveal())
93 );
94 $queryBuilderProphet->getRestrictions()->willReturn($restrictionProphet->reveal());
95 $queryBuilderProphet->quoteIdentifier(Argument::cetera())->will(function ($arguments) {
96 return '`' . $arguments[0] . '`';
97 });
98
99 $connectionPoolProphet = $this->prophesize(ConnectionPool::class);
100 $connectionPoolProphet->getConnectionForTable($tableName)
101 ->willReturn($connectionProphet->reveal());
102 $connectionPoolProphet->getQueryBuilderForTable($tableName)
103 ->shouldBeCalled()
104 ->willReturn($queryBuilderProphet->reveal());
105
106 return [$queryBuilderProphet, $connectionPoolProphet, $connectionProphet, $restrictionProphet];
107 }
108
109 /**
110 * Mock a doctrine database connection with all expectations
111 * required for the processSelectField* tests.
112 */
113 protected function mockDatabaseConnectionForProcessSelectField()
114 {
115 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection('foreignTable');
116
117 /** @var Statement|ObjectProphecy $statementProphet */
118 $statementProphet = $this->prophesize(Statement::class);
119 $statementProphet->fetch()->shouldBeCalled();
120
121 $queryBuilderProphet->select('foreignTable.uid')
122 ->shouldBeCalled()
123 ->willReturn($queryBuilderProphet->reveal());
124 $queryBuilderProphet->from('foreignTable')
125 ->shouldBeCalled()
126 ->willReturn($queryBuilderProphet->reveal());
127 $queryBuilderProphet->from('pages')
128 ->shouldBeCalled()
129 ->willReturn($queryBuilderProphet->reveal());
130 $queryBuilderProphet->where('')
131 ->shouldBeCalled()
132 ->willReturn($queryBuilderProphet->reveal());
133 $queryBuilderProphet->andWhere(' 1=1')
134 ->shouldBeCalled()
135 ->willReturn($queryBuilderProphet->reveal());
136 $queryBuilderProphet->andWhere('`pages.uid` = `foreignTable.pid`')
137 ->shouldBeCalled()
138 ->willReturn($queryBuilderProphet->reveal());
139 $queryBuilderProphet->execute()
140 ->shouldBeCalled()
141 ->willReturn($statementProphet->reveal());
142
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());
146 }
147
148 /**
149 * @test
150 */
151 public function addDataKeepExistingItems()
152 {
153 $input = [
154 'processedTca' => [
155 'columns' => [
156 'aField' => [
157 'config' => [
158 'type' => 'radio',
159 'items' => [
160 0 => [
161 'foo',
162 'bar',
163 ],
164 ],
165 ],
166 ],
167 'anotherField' => [
168 'config' => [
169 'type' => 'group',
170 'items' => [
171 0 => [
172 'foo',
173 'bar',
174 ],
175 ],
176 ],
177 ],
178 ],
179 ],
180 ];
181 $languageService = $this->prophesize(LanguageService::class);
182 $GLOBALS['LANG'] = $languageService->reveal();
183 $languageService->sL(Argument::cetera())->willReturnArgument(0);
184
185 $expected = $input;
186 $this->assertSame($expected, $this->subject->addData($input));
187 }
188
189 /**
190 * @test
191 */
192 public function addDataThrowsExceptionIfAnItemIsNotAnArray()
193 {
194 $input = [
195 'processedTca' => [
196 'columns' => [
197 'aField' => [
198 'config' => [
199 'type' => 'select',
200 'renderType' => 'selectSingle',
201 'items' => [
202 0 => 'foo',
203 ],
204 ],
205 ],
206 ],
207 ],
208 ];
209
210 $this->expectException(\UnexpectedValueException::class);
211 $this->expectExceptionCode(1439288036);
212
213 $this->subject->addData($input);
214 }
215
216 /**
217 * @test
218 */
219 public function addDataTranslatesItemLabels()
220 {
221 $input = [
222 'databaseRow' => [
223 'aField' => 'aValue',
224 ],
225 'processedTca' => [
226 'columns' => [
227 'aField' => [
228 'config' => [
229 'type' => 'select',
230 'renderType' => 'selectSingle',
231 'items' => [
232 0 => [
233 0 => 'aLabel',
234 1 => 'aValue',
235 ],
236 ],
237 'maxitems' => 99999,
238 ],
239 ],
240 ],
241 ],
242 ];
243
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"');
248
249 $languageService->sL('aLabel')->shouldBeCalled()->willReturn('translated');
250
251 $expected = $input;
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;
255
256 $expected['databaseRow']['aField'] = ['aValue'];
257
258 $this->assertSame($expected, $this->subject->addData($input));
259 }
260
261 /**
262 * @test
263 */
264 public function addDataKeepsIconFromItem()
265 {
266 $input = [
267 'databaseRow' => [
268 'aField' => 'aValue',
269 ],
270 'processedTca' => [
271 'columns' => [
272 'aField' => [
273 'config' => [
274 'type' => 'select',
275 'renderType' => 'selectSingle',
276 'items' => [
277 0 => [
278 0 => 'aLabel',
279 1 => 'aValue',
280 2 => 'an-icon-reference',
281 3 => null,
282 ],
283 ],
284 'maxitems' => 99999,
285 ],
286 ],
287 ],
288 ],
289 ];
290
291 /** @var LanguageService|ObjectProphecy $languageService */
292 $languageService = $this->prophesize(LanguageService::class);
293 $GLOBALS['LANG'] = $languageService->reveal();
294 $languageService->sL(Argument::cetera())->willReturnArgument(0);
295
296 $expected = $input;
297 $expected['databaseRow']['aField'] = ['aValue'];
298
299 $this->assertSame($expected, $this->subject->addData($input));
300 }
301
302 /**
303 * @test
304 */
305 public function addDataThrowsExceptionWithUnknownSpecialValue()
306 {
307 $input = [
308 'tableName' => 'aTable',
309 'processedTca' => [
310 'columns' => [
311 'aField' => [
312 'config' => [
313 'type' => 'select',
314 'renderType' => 'selectSingle',
315 'special' => 'anUnknownValue',
316 ],
317 ],
318 ],
319 ],
320 ];
321
322 $this->expectException(\UnexpectedValueException::class);
323 $this->expectExceptionCode(1439298496);
324
325 $this->subject->addData($input);
326 }
327
328 /**
329 * @test
330 */
331 public function addDataAddsTablesWithSpecialTables()
332 {
333 $input = [
334 'databaseRow' => [
335 'aField' => '',
336 ],
337 'tableName' => 'aTable',
338 'processedTca' => [
339 'columns' => [
340 'aField' => [
341 'config' => [
342 'type' => 'select',
343 'renderType' => 'selectSingle',
344 'special' => 'tables',
345 'maxitems' => 99999,
346 ],
347 ],
348 ],
349 ],
350 ];
351 $GLOBALS['TCA'] = [
352 'notInResult' => [
353 'ctrl' => [
354 'adminOnly' => true,
355 ],
356 ],
357 'aTable' => [
358 'ctrl' => [
359 'title' => 'aTitle',
360 ],
361 ],
362 ];
363 $GLOBALS['TCA_DESCR']['aTable']['columns']['']['description'] = 'aDescription';
364
365 /** @var LanguageService|ObjectProphecy $languageService */
366 $languageService = $this->prophesize(LanguageService::class);
367 $GLOBALS['LANG'] = $languageService->reveal();
368 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
369 $languageService->sL(Argument::containingString('INVALID VALUE'))->willReturnArgument(0);
370
371 $languageService->sL('aTitle')->shouldBeCalled()->willReturnArgument(0);
372 $languageService->loadSingleTableDescription('aTable')->shouldBeCalled();
373
374 $expected = $input;
375 $expected['databaseRow']['aField'] = [];
376 $expected['processedTca']['columns']['aField']['config']['items'] = [
377 0 => [
378 0 => 'aTitle',
379 1 => 'aTable',
380 2 => 'default-not-found',
381 3 => [
382 'description' => 'aDescription',
383 ],
384 ]
385 ];
386
387 $this->assertSame($expected, $this->subject->addData($input));
388 }
389
390 /**
391 * @test
392 */
393 public function addDataAddsTablesWithSpecialPageTypes()
394 {
395 $input = [
396 'databaseRow' => [
397 'aField' => 'aValue',
398 ],
399 'tableName' => 'aTable',
400 'processedTca' => [
401 'columns' => [
402 'aField' => [
403 'config' => [
404 'type' => 'select',
405 'renderType' => 'selectSingle',
406 'special' => 'pagetypes',
407 'items' => [],
408 'maxitems' => 99999,
409 ],
410 ],
411 ],
412 ],
413 ];
414 $GLOBALS['TCA'] = [
415 'pages' => [
416 'columns' => [
417 'doktype' => [
418 'config' => [
419 'items' => [
420 0 => [
421 0 => 'aLabel',
422 1 => 'aValue',
423 ],
424 ],
425 ],
426 ],
427 ],
428 ],
429 ];
430
431 /** @var LanguageService|ObjectProphecy $languageService */
432 $languageService = $this->prophesize(LanguageService::class);
433 $GLOBALS['LANG'] = $languageService->reveal();
434 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
435
436 $languageService->sL('aLabel')->shouldBeCalled()->willReturnArgument(0);
437
438 $expected = $input;
439 $expected['databaseRow']['aField'] = ['aValue'];
440 $expected['processedTca']['columns']['aField']['config']['items'] = [
441 0 => [
442 0 => 'aLabel',
443 1 => 'aValue',
444 2 => 'default-not-found',
445 3 => null,
446 ]
447 ];
448
449 $this->assertSame($expected, $this->subject->addData($input));
450 }
451
452 /**
453 * Data provider
454 */
455 public function addDataAddsExcludeFieldsWithSpecialExcludeDataProvider()
456 {
457 return [
458 'Table with exclude and non exclude field returns exclude item' => [
459 [
460 // input tca
461 'fooTable' => [
462 'ctrl' => [
463 'title' => 'fooTableTitle',
464 ],
465 'columns' => [
466 'bar' => [
467 'label' => 'barColumnTitle',
468 'exclude' => 1
469 ],
470 'baz' => [
471 'label' => 'bazColumnTitle',
472 ],
473 ],
474 ],
475 ],
476 [
477 // expected items
478 0 => [
479 0 => 'fooTableTitle',
480 1 => '--div--',
481 2 => 'default-not-found',
482 3 => null,
483 ],
484 1 => [
485 0 => 'barColumnTitle (bar)',
486 1 => 'fooTable:bar',
487 2 => 'empty-empty',
488 3 => null,
489 ],
490 ],
491 ],
492 'Root level table with ignored root level restriction returns exclude item' => [
493 [
494 // input tca
495 'fooTable' => [
496 'ctrl' => [
497 'title' => 'fooTableTitle',
498 'rootLevel' => true,
499 'security' => [
500 'ignoreRootLevelRestriction' => true,
501 ],
502 ],
503 'columns' => [
504 'bar' => [
505 'label' => 'barColumnTitle',
506 'exclude' => true,
507 ],
508 ],
509 ],
510 ],
511 [
512 // expected items
513 0 => [
514 0 => 'fooTableTitle',
515 1 => '--div--',
516 2 => 'default-not-found',
517 3 => null,
518 ],
519 1 => [
520 0 => 'barColumnTitle (bar)',
521 1 => 'fooTable:bar',
522 2 => 'empty-empty',
523 3 => null,
524 ],
525 ],
526 ],
527 'Root level table without ignored root level restriction returns no item' => [
528 [
529 // input tca
530 'fooTable' => [
531 'ctrl' => [
532 'title' => 'fooTableTitle',
533 'rootLevel' => true,
534 ],
535 'columns' => [
536 'bar' => [
537 'label' => 'barColumnTitle',
538 'exclude' => true,
539 ],
540 ],
541 ],
542 ],
543 [
544 // no items
545 ],
546 ],
547 'Admin table returns no item' => [
548 [
549 // input tca
550 'fooTable' => [
551 'ctrl' => [
552 'title' => 'fooTableTitle',
553 'adminOnly' => true,
554 ],
555 'columns' => [
556 'bar' => [
557 'label' => 'barColumnTitle',
558 'exclude' => true,
559 ],
560 ],
561 ],
562 ],
563 [
564 // no items
565 ],
566 ],
567 ];
568 }
569
570 /**
571 * @test
572 * @dataProvider addDataAddsExcludeFieldsWithSpecialExcludeDataProvider
573 */
574 public function addDataAddsExcludeFieldsWithSpecialExclude($tca, $expectedItems)
575 {
576 $input = [
577 'tableName' => 'aTable',
578 'databaseRow' => [],
579 'processedTca' => [
580 'columns' => [
581 'aField' => [
582 'config' => [
583 'type' => 'select',
584 'renderType' => 'selectSingle',
585 'special' => 'exclude',
586 ],
587 ],
588 ],
589 ],
590 ];
591 $GLOBALS['TCA'] = $tca;
592
593 /** @var LanguageService|ObjectProphecy $languageService */
594 $languageService = $this->prophesize(LanguageService::class);
595 $GLOBALS['LANG'] = $languageService->reveal();
596 $languageService->loadSingleTableDescription(Argument::cetera())->willReturn(null);
597 $languageService->sL(Argument::cetera())->willReturnArgument(0);
598
599 $result = $this->subject->addData($input);
600
601 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
602 }
603
604 /**
605 * @test
606 */
607 public function addDataAddsExcludeFieldsFromFlexWithSpecialExclude()
608 {
609 $input = [
610 'tableName' => 'aTable',
611 'databaseRow' => [],
612 'processedTca' => [
613 'columns' => [
614 'aField' => [
615 'config' => [
616 'type' => 'select',
617 'renderType' => 'selectSingle',
618 'special' => 'exclude',
619 ],
620 ],
621 ],
622 ],
623 ];
624
625 $GLOBALS['TCA'] = [
626 'fooTable' => [
627 'ctrl' => [
628 'title' => 'fooTableTitle',
629 ],
630 'columns' => [
631 'aFlexField' => [
632 'label' => 'aFlexFieldTitle',
633 'config' => [
634 'type' => 'flex',
635 'title' => 'title',
636 'ds' => [
637 'dummy' => '
638 <T3DataStructure>
639 <ROOT>
640 <type>array</type>
641 <el>
642 <input1>
643 <TCEforms>
644 <label>flexInputLabel</label>
645 <exclude>1</exclude>
646 <config>
647 <type>input</type>
648 <size>23</size>
649 </config>
650 </TCEforms>
651 </input1>
652 </el>
653 </ROOT>
654 </T3DataStructure>
655 ',
656 ],
657 ],
658 ],
659 ],
660 ],
661 ];
662
663 /** @var LanguageService|ObjectProphecy $languageService */
664 $languageService = $this->prophesize(LanguageService::class);
665 $GLOBALS['LANG'] = $languageService->reveal();
666 $languageService->loadSingleTableDescription(Argument::cetera())->willReturn(null);
667 $languageService->sL(Argument::cetera())->willReturnArgument(0);
668
669 $expectedItems = [
670 0 => [
671 0 => 'fooTableTitle aFlexFieldTitle dummy',
672 1 => '--div--',
673 2 => 'default-not-found',
674 3 => null,
675 ],
676 1 => [
677 0 => 'flexInputLabel (input1)',
678 1 => 'fooTable:aFlexField;dummy;sDEF;input1',
679 2 => 'empty-empty',
680 3 => null,
681 ],
682 ];
683
684 $result = $this->subject->addData($input);
685
686 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
687 }
688
689 /**
690 * @test
691 */
692 public function addDataAddsExplicitAllowFieldsWithSpecialExplicitValues()
693 {
694 $input = [
695 'tableName' => 'aTable',
696 'databaseRow' => [],
697 'processedTca' => [
698 'columns' => [
699 'aField' => [
700 'config' => [
701 'type' => 'select',
702 'renderType' => 'selectSingle',
703 'special' => 'explicitValues',
704 ],
705 ],
706 ],
707 ],
708 ];
709
710 $GLOBALS['TCA'] = [
711 'fooTable' => [
712 'ctrl' => [
713 'title' => 'fooTableTitle',
714 ],
715 'columns' => [
716 'aField' => [
717 'label' => 'aFieldTitle',
718 'config' => [
719 'type' => 'select',
720 'renderType' => 'selectSingle',
721 'authMode' => 'explicitAllow',
722 'items' => [
723 0 => [
724 'anItemTitle',
725 'anItemValue',
726 ],
727 ]
728 ],
729 ],
730 ],
731 ],
732 ];
733
734 /** @var LanguageService|ObjectProphecy $languageService */
735 $languageService = $this->prophesize(LanguageService::class);
736 $GLOBALS['LANG'] = $languageService->reveal();
737 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.allow')->shouldBeCalled()->willReturn('allowMe');
738 $languageService->sL(Argument::cetera())->willReturnArgument(0);
739
740 $expectedItems = [
741 0 => [
742 0 => 'fooTableTitle: aFieldTitle',
743 1 => '--div--',
744 2 => null,
745 3 => null,
746 ],
747 1 => [
748 0 => '[allowMe] anItemTitle',
749 1 => 'fooTable:aField:anItemValue:ALLOW',
750 2 => 'status-status-permission-granted',
751 3 => null,
752 ],
753 ];
754
755 $result = $this->subject->addData($input);
756
757 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
758 }
759
760 /**
761 * @test
762 */
763 public function addDataAddsExplicitDenyFieldsWithSpecialExplicitValues()
764 {
765 $input = [
766 'tableName' => 'aTable',
767 'databaseRow' => [],
768 'processedTca' => [
769 'columns' => [
770 'aField' => [
771 'config' => [
772 'type' => 'select',
773 'renderType' => 'selectSingle',
774 'special' => 'explicitValues',
775 ],
776 ],
777 ],
778 ],
779 ];
780
781 $GLOBALS['TCA'] = [
782 'fooTable' => [
783 'ctrl' => [
784 'title' => 'fooTableTitle',
785 ],
786 'columns' => [
787 'aField' => [
788 'label' => 'aFieldTitle',
789 'config' => [
790 'type' => 'select',
791 'renderType' => 'selectSingle',
792 'authMode' => 'explicitDeny',
793 'items' => [
794 0 => [
795 'anItemTitle',
796 'anItemValue',
797 ],
798 ]
799 ],
800 ],
801 ],
802 ],
803 ];
804
805 /** @var LanguageService|ObjectProphecy $languageService */
806 $languageService = $this->prophesize(LanguageService::class);
807 $GLOBALS['LANG'] = $languageService->reveal();
808 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.deny')->shouldBeCalled()->willReturn('denyMe');
809 $languageService->sL(Argument::cetera())->willReturnArgument(0);
810
811 $expectedItems = [
812 0 => [
813 0 => 'fooTableTitle: aFieldTitle',
814 1 => '--div--',
815 2 => null,
816 3 => null,
817 ],
818 1 => [
819 0 => '[denyMe] anItemTitle',
820 1 => 'fooTable:aField:anItemValue:DENY',
821 2 => 'status-status-permission-denied',
822 3 => null,
823 ],
824 ];
825
826 $result = $this->subject->addData($input);
827
828 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
829 }
830
831 /**
832 * @test
833 */
834 public function addDataAddsExplicitIndividualAllowFieldsWithSpecialExplicitValues()
835 {
836 $input = [
837 'tableName' => 'aTable',
838 'databaseRow' => [],
839 'processedTca' => [
840 'columns' => [
841 'aField' => [
842 'config' => [
843 'type' => 'select',
844 'renderType' => 'selectSingle',
845 'special' => 'explicitValues',
846 ],
847 ],
848 ],
849 ],
850 ];
851
852 $GLOBALS['TCA'] = [
853 'fooTable' => [
854 'ctrl' => [
855 'title' => 'fooTableTitle',
856 ],
857 'columns' => [
858 'aField' => [
859 'label' => 'aFieldTitle',
860 'config' => [
861 'type' => 'select',
862 'renderType' => 'selectSingle',
863 'authMode' => 'individual',
864 'items' => [
865 0 => [
866 'aItemTitle',
867 'aItemValue',
868 null,
869 null,
870 'EXPL_ALLOW',
871 ],
872 // 1 is not selectable as allow and is always allowed
873 1 => [
874 'bItemTitle',
875 'bItemValue',
876 ],
877 2 => [
878 'cItemTitle',
879 'cItemValue',
880 null,
881 null,
882 'EXPL_ALLOW',
883 ],
884 ]
885 ],
886 ],
887 ],
888 ],
889 ];
890
891 /** @var LanguageService|ObjectProphecy $languageService */
892 $languageService = $this->prophesize(LanguageService::class);
893 $GLOBALS['LANG'] = $languageService->reveal();
894 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.allow')->shouldBeCalled()->willReturn('allowMe');
895 $languageService->sL(Argument::cetera())->willReturnArgument(0);
896
897 $expectedItems = [
898 0 => [
899 0 => 'fooTableTitle: aFieldTitle',
900 1 => '--div--',
901 2 => null,
902 3 => null,
903 ],
904 1 => [
905 0 => '[allowMe] aItemTitle',
906 1 => 'fooTable:aField:aItemValue:ALLOW',
907 2 => 'status-status-permission-granted',
908 3 => null,
909 ],
910 2 => [
911 0 => '[allowMe] cItemTitle',
912 1 => 'fooTable:aField:cItemValue:ALLOW',
913 2 => 'status-status-permission-granted',
914 3 => null,
915 ],
916 ];
917
918 $result = $this->subject->addData($input);
919
920 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
921 }
922
923 /**
924 * @test
925 */
926 public function addDataAddsExplicitIndividualDenyFieldsWithSpecialExplicitValues()
927 {
928 $input = [
929 'tableName' => 'aTable',
930 'databaseRow' => [],
931 'processedTca' => [
932 'columns' => [
933 'aField' => [
934 'config' => [
935 'type' => 'select',
936 'renderType' => 'selectSingle',
937 'special' => 'explicitValues',
938 ],
939 ],
940 ],
941 ],
942 ];
943
944 $GLOBALS['TCA'] = [
945 'fooTable' => [
946 'ctrl' => [
947 'title' => 'fooTableTitle',
948 ],
949 'columns' => [
950 'aField' => [
951 'label' => 'aFieldTitle',
952 'config' => [
953 'type' => 'select',
954 'renderType' => 'selectSingle',
955 'authMode' => 'individual',
956 'items' => [
957 0 => [
958 'aItemTitle',
959 'aItemValue',
960 null,
961 null,
962 'EXPL_DENY',
963 ],
964 // 1 is not selectable as allow and is always allowed
965 1 => [
966 'bItemTitle',
967 'bItemValue',
968 ],
969 2 => [
970 'cItemTitle',
971 'cItemValue',
972 null,
973 null,
974 'EXPL_DENY',
975 ],
976 ]
977 ],
978 ],
979 ],
980 ],
981 ];
982
983 /** @var LanguageService|ObjectProphecy $languageService */
984 $languageService = $this->prophesize(LanguageService::class);
985 $GLOBALS['LANG'] = $languageService->reveal();
986 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.deny')->shouldBeCalled()->willReturn('denyMe');
987 $languageService->sL(Argument::cetera())->willReturnArgument(0);
988
989 $expectedItems = [
990 0 => [
991 0 => 'fooTableTitle: aFieldTitle',
992 1 => '--div--',
993 2 => null,
994 3 => null,
995 ],
996 1 => [
997 0 => '[denyMe] aItemTitle',
998 1 => 'fooTable:aField:aItemValue:DENY',
999 2 => 'status-status-permission-denied',
1000 3 => null,
1001 ],
1002 2 => [
1003 0 => '[denyMe] cItemTitle',
1004 1 => 'fooTable:aField:cItemValue:DENY',
1005 2 => 'status-status-permission-denied',
1006 3 => null,
1007 ],
1008 ];
1009
1010 $result = $this->subject->addData($input);
1011
1012 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1013 }
1014
1015 /**
1016 * @test
1017 */
1018 public function addDataAddsLanguagesWithSpecialLanguages()
1019 {
1020 $input = [
1021 'tableName' => 'aTable',
1022 'databaseRow' => [],
1023 'processedTca' => [
1024 'columns' => [
1025 'aField' => [
1026 'config' => [
1027 'type' => 'select',
1028 'renderType' => 'selectSingle',
1029 'special' => 'languages',
1030 ],
1031 ],
1032 ],
1033 ],
1034 'systemLanguageRows' => [
1035 0 => [
1036 'title' => 'aLangTitle',
1037 'uid' => 42,
1038 'flagIconIdentifier' => 'aFlag.gif',
1039 ],
1040 ],
1041 ];
1042
1043 /** @var LanguageService|ObjectProphecy $languageService */
1044 $languageService = $this->prophesize(LanguageService::class);
1045 $GLOBALS['LANG'] = $languageService->reveal();
1046 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1047
1048 $expectedItems = [
1049 0 => [
1050 0 => 'aLangTitle [42]',
1051 1 => 42,
1052 2 => 'aFlag.gif',
1053 3 => null,
1054 ],
1055 ];
1056
1057 $result = $this->subject->addData($input);
1058
1059 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1060 }
1061
1062 /**
1063 * @test
1064 */
1065 public function addDataAddsCustomOptionsWithSpecialCustom()
1066 {
1067 $input = [
1068 'tableName' => 'aTable',
1069 'databaseRow' => [],
1070 'processedTca' => [
1071 'columns' => [
1072 'aField' => [
1073 'config' => [
1074 'type' => 'select',
1075 'renderType' => 'selectSingle',
1076 'special' => 'custom',
1077 ],
1078 ],
1079 ],
1080 ],
1081 ];
1082
1083 /** @var LanguageService|ObjectProphecy $languageService */
1084 $languageService = $this->prophesize(LanguageService::class);
1085 $GLOBALS['LANG'] = $languageService->reveal();
1086 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1087
1088 $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'] = [
1089 'aKey' => [
1090 'header' => 'aHeader',
1091 'items' => [
1092 'anItemKey' => [
1093 0 => 'anItemTitle',
1094 ],
1095 'anotherKey' => [
1096 0 => 'anotherTitle',
1097 1 => 'status-status-permission-denied',
1098 2 => 'aDescription',
1099 ],
1100 ],
1101 ]
1102 ];
1103
1104 $expectedItems = [
1105 0 => [
1106 0 => 'aHeader',
1107 1 => '--div--',
1108 null,
1109 null,
1110 ],
1111 1 => [
1112 0 => 'anItemTitle',
1113 1 => 'aKey:anItemKey',
1114 2 => 'empty-empty',
1115 3 => null,
1116 ],
1117 2 => [
1118 0 => 'anotherTitle',
1119 1 => 'aKey:anotherKey',
1120 2 => 'status-status-permission-denied',
1121 3 => [ 'description' => 'aDescription' ],
1122 ],
1123 ];
1124
1125 $result = $this->subject->addData($input);
1126
1127 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1128 }
1129
1130 /**
1131 * @test
1132 */
1133 public function addDataAddsGroupItemsWithSpecialModListGroup()
1134 {
1135 $input = [
1136 'tableName' => 'aTable',
1137 'databaseRow' => [],
1138 'processedTca' => [
1139 'columns' => [
1140 'aField' => [
1141 'config' => [
1142 'type' => 'select',
1143 'renderType' => 'selectSingle',
1144 'special' => 'modListGroup',
1145 ],
1146 ],
1147 ],
1148 ],
1149 ];
1150
1151 $GLOBALS['TBE_MODULES'] = [];
1152
1153 /** @var LanguageService|ObjectProphecy $languageService */
1154 $languageService = $this->prophesize(LanguageService::class);
1155 $GLOBALS['LANG'] = $languageService->reveal();
1156 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1157
1158 /** @var ModuleLoader|ObjectProphecy $moduleLoaderProphecy */
1159 $moduleLoaderProphecy = $this->prophesize(ModuleLoader::class);
1160 GeneralUtility::addInstance(ModuleLoader::class, $moduleLoaderProphecy->reveal());
1161 $moduleLoaderProphecy->load([])->shouldBeCalled();
1162 $moduleLoaderProphecy->modListGroup = [
1163 'aModule',
1164 ];
1165 $moduleLoaderProphecy->modules = [
1166 'aModule' => [
1167 'iconIdentifier' => 'empty-empty'
1168 ]
1169 ];
1170 $moduleLoaderProphecy->getLabelsForModule('aModule')->shouldBeCalled()->willReturn([
1171 'shortdescription' => 'aModuleTabLabel',
1172 'description' => 'aModuleTabDescription',
1173 'title' => 'aModuleLabel'
1174 ]);
1175
1176 $expectedItems = [
1177 0 => [
1178 0 => 'aModuleLabel',
1179 1 => 'aModule',
1180 2 => 'empty-empty',
1181 3 => [
1182 'title' => 'aModuleTabLabel',
1183 'description' => 'aModuleTabDescription',
1184 ],
1185 ],
1186 ];
1187
1188 $result = $this->subject->addData($input);
1189
1190 $result['processedTca']['columns']['aField']['config']['items'][0][2] = str_replace([CR, LF, TAB], ['', '', ''], $result['processedTca']['columns']['aField']['config']['items'][0][2]);
1191 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1192 }
1193
1194 /**
1195 * @test
1196 */
1197 public function addDataAddsFileItemsWithConfiguredFileFolder()
1198 {
1199 $directory = $this->getUniqueId('typo3temp/var/tests/test-') . '/';
1200 $input = [
1201 'tableName' => 'aTable',
1202 'databaseRow' => [],
1203 'processedTca' => [
1204 'columns' => [
1205 'aField' => [
1206 'config' => [
1207 'type' => 'select',
1208 'renderType' => 'selectSingle',
1209 'fileFolder' => $directory,
1210 'fileFolder_extList' => 'gif',
1211 'fileFolder_recursions' => 1,
1212 ],
1213 ],
1214 ],
1215 ],
1216 ];
1217
1218 /** @var LanguageService|ObjectProphecy $languageService */
1219 $languageService = $this->prophesize(LanguageService::class);
1220 $GLOBALS['LANG'] = $languageService->reveal();
1221 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1222
1223 mkdir(PATH_site . $directory);
1224 $this->testFilesToDelete[] = PATH_site . $directory;
1225 touch(PATH_site . $directory . 'anImage.gif');
1226 touch(PATH_site . $directory . 'aFile.txt');
1227 mkdir(PATH_site . $directory . '/subdir');
1228 touch(PATH_site . $directory . '/subdir/anotherImage.gif');
1229
1230 $expectedItems = [
1231 0 => [
1232 0 => 'anImage.gif',
1233 1 => 'anImage.gif',
1234 2 => PATH_site . $directory . 'anImage.gif',
1235 3 => null,
1236 ],
1237 1 => [
1238 0 => 'subdir/anotherImage.gif',
1239 1 => 'subdir/anotherImage.gif',
1240 2 => PATH_site . $directory . 'subdir/anotherImage.gif',
1241 3 => null,
1242 ],
1243 ];
1244
1245 $result = $this->subject->addData($input);
1246
1247 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1248 }
1249
1250 /**
1251 * @test
1252 */
1253 public function addDataThrowsExceptionForInvalidFileFolder()
1254 {
1255 $input = [
1256 'tableName' => 'aTable',
1257 'databaseRow' => [],
1258 'processedTca' => [
1259 'columns' => [
1260 'aField' => [
1261 'config' => [
1262 'type' => 'select',
1263 'renderType' => 'selectSingle',
1264 'fileFolder' => 'EXT:non_existing/Resources/Public/',
1265 ],
1266 ],
1267 ],
1268 ],
1269 ];
1270
1271 $this->expectException(\RuntimeException::class);
1272 $this->expectExceptionCode(1479399227);
1273 $this->subject->addData($input);
1274 }
1275
1276 /**
1277 * @test
1278 */
1279 public function addDataAddsItemsByAddItemsFromPageTsConfig()
1280 {
1281 $input = [
1282 'databaseRow' => [
1283 'aField' => '',
1284 ],
1285 'tableName' => 'aTable',
1286 'processedTca' => [
1287 'columns' => [
1288 'aField' => [
1289 'config' => [
1290 'type' => 'select',
1291 'renderType' => 'selectSingle',
1292 'items' => [
1293 0 => [
1294 0 => 'keepMe',
1295 1 => 'keep',
1296 null,
1297 null,
1298 ],
1299 ],
1300 'maxitems' => 99999,
1301 ],
1302 ],
1303 ]
1304 ],
1305 'pageTsConfig' => [
1306 'TCEFORM.' => [
1307 'aTable.' => [
1308 'aField.' => [
1309 'addItems.' => [
1310 '1' => 'addMe'
1311 ],
1312 ],
1313 ],
1314 ],
1315 ],
1316 ];
1317
1318 /** @var LanguageService|ObjectProphecy $languageService */
1319 $languageService = $this->prophesize(LanguageService::class);
1320 $GLOBALS['LANG'] = $languageService->reveal();
1321 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1322
1323 $expected = $input;
1324 $expected['databaseRow']['aField'] = [];
1325 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1326 0 => 'addMe',
1327 1 => '1',
1328 null,
1329 null,
1330 ];
1331
1332 $this->assertEquals($expected, $this->subject->addData($input));
1333 }
1334
1335 /**
1336 * @test
1337 */
1338 public function addDataAddsItemsByAddItemsWithDuplicateValuesFromPageTsConfig()
1339 {
1340 $input = [
1341 'databaseRow' => [
1342 'aField' => '',
1343 ],
1344 'tableName' => 'aTable',
1345 'processedTca' => [
1346 'columns' => [
1347 'aField' => [
1348 'config' => [
1349 'type' => 'select',
1350 'renderType' => 'selectSingle',
1351 'items' => [
1352 0 => [
1353 0 => 'keepMe',
1354 1 => 'keep',
1355 null,
1356 null,
1357 ],
1358 ],
1359 'maxitems' => 99999,
1360 ],
1361 ],
1362 ]
1363 ],
1364 'pageTsConfig' => [
1365 'TCEFORM.' => [
1366 'aTable.' => [
1367 'aField.' => [
1368 'addItems.' => [
1369 'keep' => 'addMe'
1370 ],
1371 ],
1372 ],
1373 ],
1374 ],
1375 ];
1376
1377 /** @var LanguageService|ObjectProphecy $languageService */
1378 $languageService = $this->prophesize(LanguageService::class);
1379 $GLOBALS['LANG'] = $languageService->reveal();
1380 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1381
1382 $expected = $input;
1383 $expected['databaseRow']['aField'] = [];
1384 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1385 0 => 'addMe',
1386 1 => 'keep',
1387 null,
1388 null,
1389 ];
1390
1391 $this->assertEquals($expected, $this->subject->addData($input));
1392 }
1393
1394 /**
1395 * Data provider
1396 */
1397 public function addDataReplacesMarkersInForeignTableClauseDataProvider()
1398 {
1399 return [
1400 'replace REC_FIELD' => [
1401 'AND fTable.title=\'###REC_FIELD_rowField###\'',
1402 [
1403 ['fTable.title=\'rowFieldValue\''],
1404 [' 1=1'],
1405 ['`pages.uid` = `fTable.pid`']
1406 ],
1407 [],
1408 ],
1409 'replace REC_FIELD within FlexForm' => [
1410 'AND fTable.title=###REC_FIELD_rowFieldFlexForm###',
1411 [
1412 ['fTable.title=\'rowFieldFlexFormValue\''],
1413 [' 1=1'],
1414 ['`pages.uid` = `fTable.pid`']
1415 ],
1416 [
1417 'databaseRow' => [
1418 'rowFieldThree' => [
1419 0 => 'rowFieldThreeValue'
1420 ]
1421 ],
1422 'flexParentDatabaseRow' => [
1423 'rowFieldFlexForm' => [
1424 0 => 'rowFieldFlexFormValue'
1425 ]
1426 ],
1427 ],
1428 ],
1429 'replace REC_FIELD fullQuote' => [
1430 'AND fTable.title=###REC_FIELD_rowField###',
1431 [
1432 ['fTable.title=\'rowFieldValue\''],
1433 [' 1=1'],
1434 ['`pages.uid` = `fTable.pid`']
1435 ],
1436 [],
1437 ],
1438 'replace REC_FIELD fullQuoteWithArray' => [
1439 'AND fTable.title=###REC_FIELD_rowFieldThree###',
1440 [
1441 ['fTable.title=\'rowFieldThreeValue\''],
1442 [' 1=1'],
1443 ['`pages.uid` = `fTable.pid`']
1444 ],
1445 [
1446 'databaseRow' => [
1447 'rowFieldThree' => [
1448 0 => 'rowFieldThreeValue'
1449 ]
1450 ],
1451 ],
1452 ],
1453 'replace REC_FIELD multiple markers' => [
1454 'AND fTable.title=\'###REC_FIELD_rowField###\' AND fTable.pid=###REC_FIELD_rowFieldTwo###',
1455 [
1456 ['fTable.title=\'rowFieldValue\' AND fTable.pid=\'rowFieldTwoValue\''],
1457 [' 1=1'],
1458 ['`pages.uid` = `fTable.pid`']
1459 ],
1460 [],
1461 ],
1462 'replace CURRENT_PID' => [
1463 'AND fTable.uid=###CURRENT_PID###',
1464 [
1465 ['fTable.uid=43'],
1466 [' 1=1'],
1467 ['`pages.uid` = `fTable.pid`']
1468 ],
1469 [],
1470 ],
1471 'replace CURRENT_PID within FlexForm' => [
1472 'AND fTable.uid=###CURRENT_PID###',
1473 [
1474 ['fTable.uid=77'],
1475 [' 1=1'],
1476 ['`pages.uid` = `fTable.pid`']
1477 ],
1478 [
1479 'flexParentDatabaseRow' => [
1480 'pid' => '77',
1481 ],
1482 ],
1483 ],
1484 'replace CURRENT_PID integer cast' => [
1485 'AND fTable.uid=###CURRENT_PID###',
1486 [
1487 ['fTable.uid=431'],
1488 [' 1=1'],
1489 ['`pages.uid` = `fTable.pid`']
1490 ],
1491 [
1492 'effectivePid' => '431string',
1493 ],
1494 ],
1495 'replace THIS_UID' => [
1496 'AND fTable.uid=###THIS_UID###',
1497 [
1498 ['fTable.uid=42'],
1499 [' 1=1'],
1500 ['`pages.uid` = `fTable.pid`']
1501 ],
1502 [],
1503 ],
1504 'replace THIS_UID integer cast' => [
1505 'AND fTable.uid=###THIS_UID###',
1506 [
1507 ['fTable.uid=421'],
1508 [' 1=1'],
1509 ['`pages.uid` = `fTable.pid`']
1510 ],
1511 [
1512 'databaseRow' => [
1513 'uid' => '421string',
1514 ],
1515 ],
1516 ],
1517 'replace SITEROOT' => [
1518 'AND fTable.uid=###SITEROOT###',
1519 [
1520 ['fTable.uid=44'],
1521 [' 1=1'],
1522 ['`pages.uid` = `fTable.pid`']
1523 ],
1524 [],
1525 ],
1526 'replace SITEROOT integer cast' => [
1527 'AND fTable.uid=###SITEROOT###',
1528 [
1529 ['fTable.uid=441'],
1530 [' 1=1'],
1531 ['`pages.uid` = `fTable.pid`']
1532 ],
1533 [
1534 'rootline' => [
1535 1 => [
1536 'uid' => '441string',
1537 ],
1538 ],
1539 ],
1540 ],
1541 'replace PAGE_TSCONFIG_ID' => [
1542 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1543 [
1544 ['fTable.uid=45'],
1545 [' 1=1'],
1546 ['`pages.uid` = `fTable.pid`']
1547 ],
1548 [
1549 'pageTsConfig' => [
1550 'TCEFORM.' => [
1551 'aTable.' => [
1552 'aField.' => [
1553 'PAGE_TSCONFIG_ID' => '45',
1554 ],
1555 ],
1556 ],
1557 ],
1558 ],
1559 ],
1560 'replace PAGE_TSCONFIG_ID integer cast' => [
1561 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1562 [
1563 ['fTable.uid=451'],
1564 [' 1=1'],
1565 ['`pages.uid` = `fTable.pid`']
1566 ],
1567 [
1568 'pageTsConfig' => [
1569 'TCEFORM.' => [
1570 'aTable.' => [
1571 'aField.' => [
1572 'PAGE_TSCONFIG_ID' => '451string'
1573 ],
1574 ],
1575 ],
1576 ],
1577 ],
1578 ],
1579 'replace PAGE_TSCONFIG_STR' => [
1580 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1581 [
1582 ['fTable.uid=\'46\''],
1583 [' 1=1'],
1584 ['`pages.uid` = `fTable.pid`']
1585 ],
1586 [
1587 'pageTsConfig' => [
1588 'TCEFORM.' => [
1589 'aTable.' => [
1590 'aField.' => [
1591 'PAGE_TSCONFIG_STR' => '46',
1592 ],
1593 ],
1594 ],
1595 ],
1596 ],
1597 ],
1598 'replace PAGE_TSCONFIG_IDLIST' => [
1599 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1600 [
1601 ['fTable.uid IN (47,48)'],
1602 [' 1=1'],
1603 ['`pages.uid` = `fTable.pid`']
1604 ],
1605 [
1606 'pageTsConfig' => [
1607 'TCEFORM.' => [
1608 'aTable.' => [
1609 'aField.' => [
1610 'PAGE_TSCONFIG_IDLIST' => '47,48',
1611 ],
1612 ],
1613 ],
1614 ],
1615 ],
1616 ],
1617 'replace PAGE_TSCONFIG_IDLIST cleans list' => [
1618 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1619 [
1620 ['fTable.uid IN (471,481)'],
1621 [' 1=1'],
1622 ['`pages.uid` = `fTable.pid`']
1623 ],
1624 [
1625 'pageTsConfig' => [
1626 'TCEFORM.' => [
1627 'aTable.' => [
1628 'aField.' => [
1629 'PAGE_TSCONFIG_IDLIST' => 'a, 471, b, 481, c',
1630 ],
1631 ],
1632 ],
1633 ],
1634 ],
1635 ],
1636 ];
1637 }
1638
1639 /**
1640 * @test
1641 * @dataProvider addDataReplacesMarkersInForeignTableClauseDataProvider
1642 */
1643 public function addDataReplacesMarkersInForeignTableClause($foreignTableWhere, $expectedWhere, array $inputOverride)
1644 {
1645 $input = [
1646 'tableName' => 'aTable',
1647 'effectivePid' => 43,
1648 'databaseRow' => [
1649 'uid' => 42,
1650 'rowField' => 'rowFieldValue',
1651 'rowFieldTwo' => 'rowFieldTwoValue',
1652 ],
1653 'processedTca' => [
1654 'columns' => [
1655 'aField' => [
1656 'config' => [
1657 'type' => 'select',
1658 'renderType' => 'selectSingle',
1659 'foreign_table' => 'fTable',
1660 'foreign_table_where' => $foreignTableWhere,
1661 ],
1662 ],
1663 ]
1664 ],
1665 'rootline' => [
1666 2 => [
1667 'uid' => 999,
1668 'is_siteroot' => 0,
1669 ],
1670 1 => [
1671 'uid' => 44,
1672 'is_siteroot' => 1,
1673 ],
1674 0 => [
1675 'uid' => 0,
1676 'is_siteroot' => null,
1677 ],
1678 ],
1679 'pageTsConfig' => [],
1680 ];
1681 ArrayUtility::mergeRecursiveWithOverrule($input, $inputOverride);
1682
1683 $GLOBALS['TCA']['fTable'] = [];
1684
1685 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1686
1687 /** @var Statement|ObjectProphecy $statementProphet */
1688 $statementProphet = $this->prophesize(Statement::class);
1689
1690 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1691 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1692 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1693 $queryBuilderProphet->where(...array_shift($expectedWhere))->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1694 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1695
1696 while ($constraint = array_shift($expectedWhere)) {
1697 $queryBuilderProphet->andWhere(...$constraint)
1698 ->shouldBeCalled()
1699 ->willReturn($queryBuilderProphet->reveal());
1700 }
1701
1702 // Two instances are needed due to the push/pop behavior of addInstance()
1703 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1704 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1705
1706 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1707 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1708 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1709 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1710
1711 $this->subject->addData($input);
1712 }
1713
1714 /**
1715 * @test
1716 */
1717 public function addDataThrowsExceptionIfForeignTableIsNotDefinedInTca()
1718 {
1719 $input = [
1720 'tableName' => 'aTable',
1721 'processedTca' => [
1722 'columns' => [
1723 'aField' => [
1724 'config' => [
1725 'type' => 'select',
1726 'renderType' => 'selectSingle',
1727 'foreign_table' => 'fTable',
1728 ],
1729 ],
1730 ]
1731 ],
1732 ];
1733
1734 $this->expectException(\UnexpectedValueException::class);
1735 $this->expectExceptionCode(1439569743);
1736
1737 $this->subject->addData($input);
1738 }
1739
1740 /**
1741 * @test
1742 */
1743 public function addDataForeignTableSplitsGroupOrderAndLimit()
1744 {
1745 $input = [
1746 'tableName' => 'aTable',
1747 'databaseRow' => [],
1748 'processedTca' => [
1749 'columns' => [
1750 'aField' => [
1751 'config' => [
1752 'type' => 'select',
1753 'renderType' => 'selectSingle',
1754 'foreign_table' => 'fTable',
1755 'foreign_table_where' => '
1756 AND ftable.uid=1
1757 GROUP BY groupField1, groupField2
1758 ORDER BY orderField
1759 LIMIT 1,2',
1760 ],
1761 ],
1762 ]
1763 ],
1764 'rootline' => [],
1765 ];
1766
1767 $GLOBALS['TCA']['fTable'] = [];
1768
1769 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1770 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1771 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1772 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1773
1774 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1775
1776 /** @var Statement|ObjectProphecy $statementProphet */
1777 $statementProphet = $this->prophesize(Statement::class);
1778
1779 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1780 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1781 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1782 $queryBuilderProphet->groupBy('groupField1', 'groupField2')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1783 $queryBuilderProphet->addOrderBy('orderField', null)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1784 $queryBuilderProphet->setFirstResult(1)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1785 $queryBuilderProphet->setMaxResults(2)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1786 $queryBuilderProphet->where('ftable.uid=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1787 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1788 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1789 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1790
1791 // Two instances are needed due to the push/pop behavior of addInstance()
1792 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1793 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1794
1795 $this->subject->addData($input);
1796 }
1797
1798 /**
1799 * @test
1800 */
1801 public function addDataForeignTableQueuesFlashMessageOnDatabaseError()
1802 {
1803 $input = [
1804 'databaseRow' => [
1805 'aField' => '',
1806 ],
1807 'tableName' => 'aTable',
1808 'processedTca' => [
1809 'columns' => [
1810 'aField' => [
1811 'config' => [
1812 'type' => 'select',
1813 'renderType' => 'selectSingle',
1814 'foreign_table' => 'fTable',
1815 'items' => [
1816 0 => [
1817 0 => 'itemLabel',
1818 1 => 'itemValue',
1819 2 => null,
1820 3 => null,
1821 ],
1822 ],
1823 'maxitems' => 99999,
1824 ],
1825 ],
1826 ]
1827 ],
1828 'rootline' => [],
1829 ];
1830
1831 $GLOBALS['TCA']['fTable'] = [];
1832
1833 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1834 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1835 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1836 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1837
1838 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1839 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1840 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1841 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1842
1843 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1844
1845 /** @var Statement|ObjectProphecy $statementProphet */
1846 $statementProphet = $this->prophesize(Statement::class);
1847
1848 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1849 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1850 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1851 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1852 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1853 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1854
1855 $prevException = new DBALException('Invalid table name', 1476045274);
1856 $exception = new DBALException('Driver error', 1476045971, $prevException);
1857
1858 $queryBuilderProphet->execute()->shouldBeCalled()->willThrow($exception);
1859
1860 // Two instances are needed due to the push/pop behavior of addInstance()
1861 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1862 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1863
1864 /** @var FlashMessage|ObjectProphecy $flashMessage */
1865 $flashMessage = $this->prophesize(FlashMessage::class);
1866 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
1867 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
1868 $flashMessageService = $this->prophesize(FlashMessageService::class);
1869 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
1870 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
1871 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
1872 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
1873
1874 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
1875
1876 $expected = $input;
1877 $expected['databaseRow']['aField'] = [];
1878
1879 $this->assertEquals($expected, $this->subject->addData($input));
1880 }
1881
1882 /**
1883 * @test
1884 */
1885 public function addDataForeignTableHandlesForeignTableRows()
1886 {
1887 $input = [
1888 'databaseRow' => [
1889 'aField' => '',
1890 ],
1891 'tableName' => 'aTable',
1892 'processedTca' => [
1893 'columns' => [
1894 'aField' => [
1895 'config' => [
1896 'type' => 'select',
1897 'renderType' => 'selectSingle',
1898 'foreign_table' => 'fTable',
1899 'foreign_table_prefix' => 'aPrefix',
1900 'items' => [],
1901 'maxitems' => 99999,
1902 ],
1903 ],
1904 ]
1905 ],
1906 'rootline' => [],
1907 ];
1908
1909 $GLOBALS['TCA']['fTable'] = [];
1910
1911 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1912 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1913 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1914 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1915
1916 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1917 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1918 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1919 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1920
1921 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1922
1923 /** @var Statement|ObjectProphecy $statementProphet */
1924 $statementProphet = $this->prophesize(Statement::class);
1925
1926 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1927 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1928 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1929 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1930 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1931 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1932 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1933
1934 // Two instances are needed due to the push/pop behavior of addInstance()
1935 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1936 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1937
1938 $counter = 0;
1939 $statementProphet->fetch()->shouldBeCalled()->will(function ($args) use (&$counter) {
1940 $counter++;
1941 if ($counter >= 3) {
1942 return false;
1943 }
1944 return [
1945 'uid' => $counter,
1946 'aValue' => 'bar,',
1947 ];
1948 });
1949
1950 $expected = $input;
1951 $expected['processedTca']['columns']['aField']['config']['items'] = [
1952 0 => [
1953 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
1954 1 => 1,
1955 2 => 'default-not-found',
1956 3 => null,
1957 ],
1958 1 => [
1959 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
1960 1 => 2,
1961 2 => 'default-not-found',
1962 3 => null,
1963 ],
1964 ];
1965
1966 $expected['databaseRow']['aField'] = [];
1967
1968 $this->assertEquals($expected, $this->subject->addData($input));
1969 }
1970
1971 /**
1972 * @test
1973 */
1974 public function addDataForeignTableResolvesIconFromSelicon()
1975 {
1976 $input = [
1977 'databaseRow' => [
1978 'aField' => '',
1979 ],
1980 'tableName' => 'aTable',
1981 'processedTca' => [
1982 'columns' => [
1983 'aField' => [
1984 'config' => [
1985 'type' => 'select',
1986 'renderType' => 'selectSingle',
1987 'foreign_table' => 'fTable',
1988 'maxitems' => 99999,
1989 ],
1990 ],
1991 ]
1992 ],
1993 'rootline' => [],
1994 ];
1995
1996 // Fake the foreign_table
1997 $GLOBALS['TCA']['fTable'] = [
1998 'ctrl' => [
1999 'label' => 'icon',
2000 'selicon_field' => 'icon',
2001 'selicon_field_path' => 'uploads/media',
2002 ],
2003 'columns' =>[
2004 'icon' => [],
2005 ],
2006 ];
2007
2008 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2009 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2010 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2011 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2012
2013 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
2014 $languageServiceProphecy = $this->prophesize(LanguageService::class);
2015 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
2016 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
2017
2018 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
2019
2020 /** @var Statement|ObjectProphecy $statementProphet */
2021 $statementProphet = $this->prophesize(Statement::class);
2022
2023 $queryBuilderProphet->select('fTable.uid', 'fTable.icon')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2024 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2025 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2026 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2027 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2028 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2029 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
2030
2031 // Two instances are needed due to the push/pop behavior of addInstance()
2032 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
2033 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
2034
2035 // Query returns one row, then false on second call
2036 $foreignTableRowResultOne = [
2037 'uid' => 1,
2038 'icon' => 'foo.jpg',
2039 ];
2040 $statementProphet->fetch()->shouldBeCalled()->willReturn($foreignTableRowResultOne, false);
2041
2042 $expected = $input;
2043 $expected['processedTca']['columns']['aField']['config']['items'] = [
2044 0 => [
2045 0 => 'foo.jpg',
2046 1 => 1,
2047 2 => 'uploads/media/foo.jpg', // combination of selicon_field_path and the row value of field 'icon'
2048 3 => null,
2049 ],
2050 ];
2051 $expected['databaseRow']['aField'] = [];
2052
2053 $this->assertEquals($expected, $this->subject->addData($input));
2054 }
2055
2056 /**
2057 * @test
2058 */
2059 public function addDataRemovesItemsByKeepItemsPageTsConfig()
2060 {
2061 $input = [
2062 'databaseRow' => [
2063 'aField' => '',
2064 ],
2065 'tableName' => 'aTable',
2066 'processedTca' => [
2067 'columns' => [
2068 'aField' => [
2069 'config' => [
2070 'type' => 'select',
2071 'renderType' => 'selectSingle',
2072 'items' => [
2073 0 => [
2074 0 => 'keepMe',
2075 1 => 'keep',
2076 null,
2077 null,
2078 ],
2079 1 => [
2080 0 => 'removeMe',
2081 1 => 'remove',
2082 ],
2083 ],
2084 'maxitems' => 99999,
2085 ],
2086 ],
2087 ]
2088 ],
2089 'pageTsConfig' => [
2090 'TCEFORM.' => [
2091 'aTable.' => [
2092 'aField.' => [
2093 'keepItems' => 'keep',
2094 ],
2095 ],
2096 ],
2097 ],
2098 ];
2099
2100 /** @var LanguageService|ObjectProphecy $languageService */
2101 $languageService = $this->prophesize(LanguageService::class);
2102 $GLOBALS['LANG'] = $languageService->reveal();
2103 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2104
2105 $expected = $input;
2106 $expected['databaseRow']['aField'] = [];
2107 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2108
2109 $this->assertEquals($expected, $this->subject->addData($input));
2110 }
2111
2112 /**
2113 * @test
2114 */
2115 public function addDataRemovesAllItemsByEmptyKeepItemsPageTsConfig()
2116 {
2117 $input = [
2118 'databaseRow' => [
2119 'aField' => '',
2120 ],
2121 'tableName' => 'aTable',
2122 'processedTca' => [
2123 'columns' => [
2124 'aField' => [
2125 'config' => [
2126 'type' => 'select',
2127 'renderType' => 'selectSingle',
2128 'items' => [
2129 0 => [
2130 0 => 'keepMe',
2131 1 => 'keep',
2132 null,
2133 null,
2134 ],
2135 1 => [
2136 0 => 'removeMe',
2137 1 => 'remove',
2138 ],
2139 ],
2140 'maxitems' => 99999,
2141 ],
2142 ],
2143 ]
2144 ],
2145 'pageTsConfig' => [
2146 'TCEFORM.' => [
2147 'aTable.' => [
2148 'aField.' => [
2149 'keepItems' => '',
2150 ],
2151 ],
2152 ],
2153 ],
2154 ];
2155
2156 /** @var LanguageService|ObjectProphecy $languageService */
2157 $languageService = $this->prophesize(LanguageService::class);
2158 $GLOBALS['LANG'] = $languageService->reveal();
2159 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2160
2161 $expected = $input;
2162 $expected['databaseRow']['aField'] = [];
2163 $expected['processedTca']['columns']['aField']['config']['items'] = [];
2164
2165 $this->assertEquals($expected, $this->subject->addData($input));
2166 }
2167
2168 /**
2169 * @test
2170 */
2171 public function addDataEvaluatesKeepItemsBeforeAddItemsFromPageTsConfig()
2172 {
2173 $input = [
2174 'databaseRow' => [
2175 'aField' => '',
2176 ],
2177 'tableName' => 'aTable',
2178 'processedTca' => [
2179 'columns' => [
2180 'aField' => [
2181 'config' => [
2182 'type' => 'select',
2183 'renderType' => 'selectSingle',
2184 'items' => [
2185 0 => [
2186 0 => 'keepMe',
2187 1 => '1',
2188 null,
2189 null,
2190 ],
2191 1 => [
2192 0 => 'removeMe',
2193 1 => 'remove',
2194 ],
2195 ],
2196 'maxitems' => 99999,
2197 ],
2198 ],
2199 ]
2200 ],
2201 'pageTsConfig' => [
2202 'TCEFORM.' => [
2203 'aTable.' => [
2204 'aField.' => [
2205 'keepItems' => '1',
2206 'addItems.' => [
2207 '1' => 'addItem #1',
2208 '12' => 'addItem #12',
2209 ],
2210 ],
2211 ],
2212 ],
2213 ],
2214 ];
2215
2216 /** @var LanguageService|ObjectProphecy $languageService */
2217 $languageService = $this->prophesize(LanguageService::class);
2218 $GLOBALS['LANG'] = $languageService->reveal();
2219 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2220
2221 $expected = $input;
2222 $expected['databaseRow']['aField'] = [];
2223 $expected['processedTca']['columns']['aField']['config']['items'] = [
2224 0 => [
2225 0 => 'keepMe',
2226 1 => '1',
2227 null,
2228 null,
2229 ],
2230 1 => [
2231 0 => 'addItem #1',
2232 1 => '1',
2233 null,
2234 null,
2235 ],
2236 2 => [
2237 0 => 'addItem #12',
2238 1 => '12',
2239 null,
2240 null,
2241 ],
2242 ];
2243
2244 $this->assertEquals($expected, $this->subject->addData($input));
2245 }
2246
2247 /**
2248 * @test
2249 */
2250 public function addDataRemovesItemsByRemoveItemsPageTsConfig()
2251 {
2252 $input = [
2253 'databaseRow' => [
2254 'aField' => ''
2255 ],
2256 'tableName' => 'aTable',
2257 'processedTca' => [
2258 'columns' => [
2259 'aField' => [
2260 'config' => [
2261 'type' => 'select',
2262 'renderType' => 'selectSingle',
2263 'items' => [
2264 0 => [
2265 0 => 'keepMe',
2266 1 => 'keep',
2267 null,
2268 null,
2269 ],
2270 1 => [
2271 0 => 'removeMe',
2272 1 => 'remove',
2273 ],
2274 ],
2275 'maxitems' => 99999,
2276 ],
2277 ],
2278 ]
2279 ],
2280 'pageTsConfig' => [
2281 'TCEFORM.' => [
2282 'aTable.' => [
2283 'aField.' => [
2284 'removeItems' => 'remove',
2285 ],
2286 ],
2287 ],
2288 ],
2289 ];
2290
2291 /** @var LanguageService|ObjectProphecy $languageService */
2292 $languageService = $this->prophesize(LanguageService::class);
2293 $GLOBALS['LANG'] = $languageService->reveal();
2294 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2295
2296 $expected = $input;
2297 $expected['databaseRow']['aField'] = [];
2298 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2299
2300 $this->assertEquals($expected, $this->subject->addData($input));
2301 }
2302
2303 /**
2304 * @test
2305 */
2306 public function addDataRemovesItemsAddedByAddItemsFromPageTsConfigByRemoveItemsPageTsConfig()
2307 {
2308 $input = [
2309 'databaseRow' => [
2310 'aField' => ''
2311 ],
2312 'tableName' => 'aTable',
2313 'processedTca' => [
2314 'columns' => [
2315 'aField' => [
2316 'config' => [
2317 'type' => 'select',
2318 'renderType' => 'selectSingle',
2319 'items' => [
2320 0 => [
2321 0 => 'keepMe',
2322 1 => 'keep',
2323 null,
2324 null,
2325 ],
2326 1 => [
2327 0 => 'removeMe',
2328 1 => 'remove',
2329 ],
2330 ],
2331 'maxitems' => 99999,
2332 ],
2333 ],
2334 ]
2335 ],
2336 'pageTsConfig' => [
2337 'TCEFORM.' => [
2338 'aTable.' => [
2339 'aField.' => [
2340 'removeItems' => 'remove,add',
2341 'addItems.' => [
2342 'add' => 'addMe'
2343 ]
2344 ],
2345 ],
2346 ],
2347 ],
2348 ];
2349
2350 /** @var LanguageService|ObjectProphecy $languageService */
2351 $languageService = $this->prophesize(LanguageService::class);
2352 $GLOBALS['LANG'] = $languageService->reveal();
2353 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2354
2355 $expected = $input;
2356 $expected['databaseRow']['aField'] = [];
2357 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2358
2359 $this->assertEquals($expected, $this->subject->addData($input));
2360 }
2361
2362 /**
2363 * @test
2364 */
2365 public function addDataRemovesItemsByLanguageFieldUserRestriction()
2366 {
2367 $input = [
2368 'databaseRow' => [
2369 'aField' => 'aValue,remove'
2370 ],
2371 'tableName' => 'aTable',
2372 'processedTca' => [
2373 'ctrl' => [
2374 'languageField' => 'aField',
2375 ],
2376 'columns' => [
2377 'aField' => [
2378 'config' => [
2379 'type' => 'select',
2380 'renderType' => 'selectSingle',
2381 'items' => [
2382 0 => [
2383 0 => 'keepMe',
2384 1 => 'keep',
2385 null,
2386 null,
2387 ],
2388 1 => [
2389 0 => 'removeMe',
2390 1 => 'remove',
2391 ],
2392 ],
2393 'maxitems' => 99999,
2394 ],
2395 ],
2396 ]
2397 ],
2398 ];
2399
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);
2405
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);
2411
2412 $expected = $input;
2413 $expected['databaseRow']['aField'] = [];
2414 $expected['processedTca']['columns']['aField']['config']['items'] = [
2415 [ '[ INVALID VALUE "aValue" ]', 'aValue', null, null ],
2416 [ 'keepMe', 'keep', null, null ],
2417 ];
2418
2419 $this->assertEquals($expected, $this->subject->addData($input));
2420 }
2421
2422 /**
2423 * @test
2424 */
2425 public function addDataRemovesItemsByUserAuthModeRestriction()
2426 {
2427 $input = [
2428 'databaseRow' => [
2429 'aField' => 'keep,remove'
2430 ],
2431 'tableName' => 'aTable',
2432 'processedTca' => [
2433 'columns' => [
2434 'aField' => [
2435 'config' => [
2436 'type' => 'select',
2437 'renderType' => 'selectSingle',
2438 'authMode' => 'explicitAllow',
2439 'items' => [
2440 0 => [
2441 0 => 'keepMe',
2442 1 => 'keep',
2443 null,
2444 null,
2445 ],
2446 1 => [
2447 0 => 'removeMe',
2448 1 => 'remove',
2449 ],
2450 ],
2451 'maxitems' => 99999,
2452 ],
2453 ],
2454 ]
2455 ],
2456 ];
2457
2458 /** @var LanguageService|ObjectProphecy $languageService */
2459 $languageService = $this->prophesize(LanguageService::class);
2460 $GLOBALS['LANG'] = $languageService->reveal();
2461 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2462
2463 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2464 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2465 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2466 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'keep', 'explicitAllow')->shouldBeCalled()->willReturn(true);
2467 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'remove', 'explicitAllow')->shouldBeCalled()->willReturn(false);
2468
2469 $expected = $input;
2470 $expected['databaseRow']['aField'] = ['keep'];
2471 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2472
2473 $this->assertEquals($expected, $this->subject->addData($input));
2474 }
2475
2476 /**
2477 * @test
2478 */
2479 public function addDataKeepsAllPagesDoktypesForAdminUser()
2480 {
2481 $input = [
2482 'databaseRow' => [
2483 'doktype' => 'keep'
2484 ],
2485 'tableName' => 'pages',
2486 'processedTca' => [
2487 'columns' => [
2488 'doktype' => [
2489 'config' => [
2490 'type' => 'select',
2491 'renderType' => 'selectSingle',
2492 'items' => [
2493 0 => [
2494 0 => 'keepMe',
2495 1 => 'keep',
2496 null,
2497 null,
2498 ],
2499 ],
2500 'maxitems' => 99999,
2501 ],
2502 ],
2503 ],
2504 ],
2505 ];
2506
2507 /** @var LanguageService|ObjectProphecy $languageService */
2508 $languageService = $this->prophesize(LanguageService::class);
2509 $GLOBALS['LANG'] = $languageService->reveal();
2510 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2511
2512 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2513 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2514 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2515 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
2516
2517 $expected = $input;
2518 $expected['databaseRow']['doktype'] = ['keep'];
2519
2520 $this->assertEquals($expected, $this->subject->addData($input));
2521 }
2522
2523 /**
2524 * @test
2525 */
2526 public function addDataKeepsAllowedPageTypesForNonAdminUser()
2527 {
2528 $input = [
2529 'databaseRow' => [
2530 'doktype' => 'keep',
2531 ],
2532 'tableName' => 'pages',
2533 'processedTca' => [
2534 'columns' => [
2535 'doktype' => [
2536 'config' => [
2537 'type' => 'select',
2538 'renderType' => 'selectSingle',
2539 'items' => [
2540 0 => [
2541 0 => 'keepMe',
2542 1 => 'keep',
2543 null,
2544 null,
2545 ],
2546 1 => [
2547 0 => 'removeMe',
2548 1 => 'remove',
2549 ],
2550 ],
2551 'maxitems' => 99999,
2552 ],
2553 ],
2554 ],
2555 ],
2556 ];
2557
2558 /** @var LanguageService|ObjectProphecy $languageService */
2559 $languageService = $this->prophesize(LanguageService::class);
2560 $GLOBALS['LANG'] = $languageService->reveal();
2561 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2562
2563 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2564 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2565 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2566 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
2567 $backendUserProphecy->groupData = [
2568 'pagetypes_select' => 'foo,keep,anotherAllowedDoktype',
2569 ];
2570
2571 $expected = $input;
2572 $expected['databaseRow']['doktype'] = ['keep'];
2573 unset($expected['processedTca']['columns']['doktype']['config']['items'][1]);
2574
2575 $this->assertEquals($expected, $this->subject->addData($input));
2576 }
2577
2578 /**
2579 * @test
2580 */
2581 public function addDataCallsItemsProcFunc()
2582 {
2583 $input = [
2584 'tableName' => 'aTable',
2585 'databaseRow' => [
2586 'aField' => 'aValue'
2587 ],
2588 'processedTca' => [
2589 'columns' => [
2590 'aField' => [
2591 'config' => [
2592 'type' => 'select',
2593 'renderType' => 'selectSingle',
2594 'items' => [],
2595 'itemsProcFunc' => function (array $parameters, $pObj) {
2596 $parameters['items'] = [
2597 0 => [
2598 0 => 'aLabel',
2599 1 => 'aValue',
2600 2 => null,
2601 3 => null,
2602 ],
2603 ];
2604 },
2605 ],
2606 ],
2607 ],
2608 ],
2609 ];
2610
2611 /** @var LanguageService|ObjectProphecy $languageService */
2612 $languageService = $this->prophesize(LanguageService::class);
2613 $GLOBALS['LANG'] = $languageService->reveal();
2614 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2615
2616 $expected = $input;
2617 $expected['databaseRow']['aField'] = ['aValue'];
2618 $expected['processedTca']['columns']['aField']['config'] = [
2619 'type' => 'select',
2620 'renderType' => 'selectSingle',
2621 'items' => [
2622 0 => [
2623 0 => 'aLabel',
2624 1 => 'aValue',
2625 2 => null,
2626 3 => null,
2627 ],
2628 ],
2629 'maxitems' => 99999,
2630 ];
2631
2632 $this->assertSame($expected, $this->subject->addData($input));
2633 }
2634
2635 /**
2636 * @test
2637 */
2638 public function addDataItemsProcFuncReceivesParameters()
2639 {
2640 $input = [
2641 'tableName' => 'aTable',
2642 'databaseRow' => [
2643 'aField' => 'aValue',
2644 ],
2645 'pageTsConfig' => [
2646 'TCEFORM.' => [
2647 'aTable.' => [
2648 'aField.' => [
2649 'itemsProcFunc.' => [
2650 'itemParamKey' => 'itemParamValue',
2651 ],
2652 ]
2653 ],
2654 ],
2655 ],
2656 'processedTca' => [
2657 'columns' => [
2658 'aField' => [
2659 'config' => [
2660 'type' => 'select',
2661 'renderType' => 'selectSingle',
2662 'aKey' => 'aValue',
2663 'items' => [
2664 0 => [
2665 0 => 'aLabel',
2666 1 => 'aValue',
2667 ],
2668 ],
2669 'itemsProcFunc' => function (array $parameters, $pObj) {
2670 if ($parameters['items'] !== [ 0 => [ 'aLabel', 'aValue'] ]
2671 || $parameters['config']['aKey'] !== 'aValue'
2672 || $parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
2673 || $parameters['table'] !== 'aTable'
2674 || $parameters['row'] !== [ 'aField' => 'aValue' ]
2675 || $parameters['field'] !== 'aField'
2676 ) {
2677 throw new \UnexpectedValueException('broken', 1476109436);
2678 }
2679 },
2680 ],
2681 ],
2682 ],
2683 ],
2684 ];
2685
2686 $languageService = $this->prophesize(LanguageService::class);
2687 $GLOBALS['LANG'] = $languageService->reveal();
2688 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2689 /** @var FlashMessage|ObjectProphecy $flashMessage */
2690 $flashMessage = $this->prophesize(FlashMessage::class);
2691 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2692 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2693 $flashMessageService = $this->prophesize(FlashMessageService::class);
2694 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2695 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2696 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2697 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2698
2699 // itemsProcFunc must NOT have raised an exception
2700 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
2701
2702 $this->subject->addData($input);
2703 }
2704
2705 /**
2706 * @test
2707 */
2708 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
2709 {
2710 $input = [
2711 'tableName' => 'aTable',
2712 'databaseRow' => [
2713 'aField' => 'aValue',
2714 ],
2715 'pageTsConfig' => [
2716 'TCEFORM.' => [
2717 'aTable.' => [
2718 'aField.' => [
2719 'itemsProcFunc.' => [
2720 'itemParamKey' => 'itemParamValue',
2721 ],
2722 ]
2723 ],
2724 ],
2725 ],
2726 'processedTca' => [
2727 'columns' => [
2728 'aField' => [
2729 'config' => [
2730 'type' => 'select',
2731 'renderType' => 'selectSingle',
2732 'aKey' => 'aValue',
2733 'items' => [
2734 0 => [
2735 0 => 'aLabel',
2736 1 => 'aValue',
2737 ],
2738 ],
2739 'itemsProcFunc' => function (array $parameters, $pObj) {
2740 throw new \UnexpectedValueException('anException', 1476109437);
2741 },
2742 ],
2743 ],
2744 ],
2745 ],
2746 ];
2747
2748 $languageService = $this->prophesize(LanguageService::class);
2749 $GLOBALS['LANG'] = $languageService->reveal();
2750 /** @var FlashMessage|ObjectProphecy $flashMessage */
2751 $flashMessage = $this->prophesize(FlashMessage::class);
2752 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2753 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2754 $flashMessageService = $this->prophesize(FlashMessageService::class);
2755 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2756 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2757 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2758 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2759
2760 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
2761
2762 $this->subject->addData($input);
2763 }
2764
2765 /**
2766 * @test
2767 */
2768 public function addDataTranslatesItemLabelsFromPageTsConfig()
2769 {
2770 $input = [
2771 'databaseRow' => [
2772 'aField' => 'aValue',
2773 ],
2774 'tableName' => 'aTable',
2775 'processedTca' => [
2776 'columns' => [
2777 'aField' => [
2778 'config' => [
2779 'type' => 'select',
2780 'renderType' => 'selectSingle',
2781 'items' => [
2782 0 => [
2783 0 => 'aLabel',
2784 1 => 'aValue',
2785 null,
2786 null,
2787 ],
2788 ],
2789 'maxitems' => 99999,
2790 ],
2791 ],
2792 ],
2793 ],
2794 'pageTsConfig' => [
2795 'TCEFORM.' => [
2796 'aTable.' => [
2797 'aField.' => [
2798 'altLabels.' => [
2799 'aValue' => 'labelOverride',
2800 ],
2801 ]
2802 ],
2803 ],
2804 ],
2805 ];
2806
2807 /** @var LanguageService|ObjectProphecy $languageService */
2808 $languageService = $this->prophesize(LanguageService::class);
2809 $GLOBALS['LANG'] = $languageService->reveal();
2810 $languageService->sL('aLabel')->willReturnArgument(0);
2811 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
2812 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2813
2814 $expected = $input;
2815 $expected['databaseRow']['aField'] = ['aValue'];
2816 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
2817
2818 $this->assertSame($expected, $this->subject->addData($input));
2819 $this->subject->addData($input);
2820 }
2821
2822 /**
2823 * @test
2824 */
2825 public function processSelectFieldValueSetsMmForeignRelationValues()
2826 {
2827 $GLOBALS['TCA']['foreignTable'] = [];
2828
2829 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2830 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2831 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2832 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2833
2834 $this->mockDatabaseConnectionForProcessSelectField();
2835
2836 $input = [
2837 'tableName' => 'aTable',
2838 'databaseRow' => [
2839 'uid' => 42,
2840 // Two connected rows
2841 'aField' => 2,
2842 ],
2843 'processedTca' => [
2844 'columns' => [
2845 'aField' => [
2846 'config' => [
2847 'type' => 'select',
2848 'renderType' => 'selectSingle',
2849 'maxitems' => 999,
2850 'foreign_table' => 'foreignTable',
2851 'MM' => 'aTable_foreignTable_mm',
2852 'items' => [],
2853 ],
2854 ],
2855 ],
2856 ],
2857 ];
2858 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2859 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2860 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2861 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2862
2863 $relationHandlerUids = [
2864 23,
2865 24
2866 ];
2867
2868 $relationHandlerProphecy->start(2, 'foreignTable', 'aTable_foreignTable_mm', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2869 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2870
2871 $expected = $input;
2872 $expected['databaseRow']['aField'] = $relationHandlerUids;
2873
2874 $this->assertEquals($expected, $this->subject->addData($input));
2875 }
2876
2877 /**
2878 * @test
2879 */
2880 public function processSelectFieldValueSetsForeignRelationValues()
2881 {
2882 $GLOBALS['TCA']['foreignTable'] = [];
2883
2884 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2885 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2886 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2887 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2888
2889 $this->mockDatabaseConnectionForProcessSelectField();
2890
2891 $input = [
2892 'tableName' => 'aTable',
2893 'databaseRow' => [
2894 'uid' => 42,
2895 // Two connected rows
2896 'aField' => '22,23,24,25',
2897 ],
2898 'processedTca' => [
2899 'columns' => [
2900 'aField' => [
2901 'config' => [
2902 'type' => 'select',
2903 'renderType' => 'selectSingle',
2904 'maxitems' => 999,
2905 'foreign_table' => 'foreignTable',
2906 'items' => [],
2907 ],
2908 ],
2909 ],
2910 ],
2911 ];
2912 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2913 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2914 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2915 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2916
2917 $relationHandlerUids = [
2918 23,
2919 24
2920 ];
2921
2922 $relationHandlerProphecy->start('22,23,24,25', 'foreignTable', '', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2923 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2924
2925 $expected = $input;
2926 $expected['databaseRow']['aField'] = $relationHandlerUids;
2927
2928 $this->assertEquals($expected, $this->subject->addData($input));
2929 }
2930
2931 /**
2932 * @test
2933 */
2934 public function processSelectFieldValueRemovesInvalidDynamicValues()
2935 {
2936 $languageService = $this->prophesize(LanguageService::class);
2937 $GLOBALS['LANG'] = $languageService->reveal();
2938 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2939
2940 $GLOBALS['TCA']['foreignTable'] = [];
2941
2942 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2943 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2944 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2945 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2946
2947 $this->mockDatabaseConnectionForProcessSelectField();
2948
2949 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2950 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2951 $relationHandlerProphecy->start(Argument::cetera())->shouldBeCalled();
2952 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldBeCalled()->willReturn([1]);
2953
2954 $input = [
2955 'tableName' => 'aTable',
2956 'databaseRow' => [
2957 'aField' => '1,2,bar,foo',
2958 ],
2959 'processedTca' => [
2960 'columns' => [
2961 'aField' => [
2962 'config' => [
2963 'type' => 'select',
2964 'renderType' => 'selectSingleBox',
2965 'foreign_table' => 'foreignTable',
2966 'maxitems' => 999,
2967 'items' => [
2968 ['foo', 'foo', null, null],
2969 ],
2970 ],
2971 ],
2972 ],
2973 ],
2974 ];
2975
2976 $expected = $input;
2977 $expected['databaseRow']['aField'] = ['foo', 1];
2978
2979 $this->assertEquals($expected, $this->subject->addData($input));
2980 }
2981
2982 /**
2983 * @test
2984 */
2985 public function processSelectFieldValueKeepsValuesFromStaticItems()
2986 {
2987 $languageService = $this->prophesize(LanguageService::class);
2988 $GLOBALS['LANG'] = $languageService->reveal();
2989 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2990
2991 $input = [
2992 'tableName' => 'aTable',
2993 'databaseRow' => [
2994 'aField' => 'foo,bar',
2995 ],
2996 'processedTca' => [
2997 'columns' => [
2998 'aField' => [
2999 'config' => [
3000 'type' => 'select',
3001 'renderType' => 'selectSingle',
3002 'maxitems' => 999,
3003 'items' => [
3004 ['foo', 'foo', null, null],
3005 ['bar', 'bar', null, null],
3006 ],
3007 ],
3008 ],
3009 ],
3010 ],
3011 ];
3012