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