837b4de4ad472044029de3ded0c7900c3ebf7a3f
[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\Messaging\FlashMessage;
31 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
32 use TYPO3\CMS\Core\Messaging\FlashMessageService;
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 \TYPO3\Components\TestingFramework\Core\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 'deprecated flexHack PAGE_TSCONFIG_ID is substituted' => [
1637 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1638 [
1639 ['fTable.uid=123'],
1640 [' 1=1'],
1641 ['`pages.uid` = `fTable.pid`']
1642 ],
1643 [
1644 'pageTsConfig' => [
1645 'flexHack.' => [
1646 'PAGE_TSCONFIG_ID' => '123',
1647 ],
1648 ],
1649 ],
1650 ],
1651 'deprecated flexHack PAGE_TSCONFIG_IDLIST is substituted' => [
1652 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1653 [
1654 ['fTable.uid IN (123,124)'],
1655 [' 1=1'],
1656 ['`pages.uid` = `fTable.pid`']
1657 ],
1658 [
1659 'pageTsConfig' => [
1660 'flexHack.' => [
1661 'PAGE_TSCONFIG_IDLIST' => '123,124',
1662 ],
1663 ],
1664 ],
1665 ],
1666 'deprecated flexHack PAGE_TSCONFIG_STR is substituted' => [
1667 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1668 [
1669 ['fTable.uid=\'aString\''],
1670 [' 1=1'],
1671 ['`pages.uid` = `fTable.pid`']
1672 ],
1673 [
1674 'pageTsConfig' => [
1675 'flexHack.' => [
1676 'PAGE_TSCONFIG_STR' => 'aString',
1677 ],
1678 ],
1679 ],
1680 ],
1681 ];
1682 }
1683
1684 /**
1685 * @test
1686 * @dataProvider addDataReplacesMarkersInForeignTableClauseDataProvider
1687 */
1688 public function addDataReplacesMarkersInForeignTableClause($foreignTableWhere, $expectedWhere, array $inputOverride)
1689 {
1690 $input = [
1691 'tableName' => 'aTable',
1692 'effectivePid' => 43,
1693 'databaseRow' => [
1694 'uid' => 42,
1695 'rowField' => 'rowFieldValue',
1696 'rowFieldTwo' => 'rowFieldTwoValue',
1697 ],
1698 'processedTca' => [
1699 'columns' => [
1700 'aField' => [
1701 'config' => [
1702 'type' => 'select',
1703 'renderType' => 'selectSingle',
1704 'foreign_table' => 'fTable',
1705 'foreign_table_where' => $foreignTableWhere,
1706 ],
1707 ],
1708 ]
1709 ],
1710 'rootline' => [
1711 2 => [
1712 'uid' => 999,
1713 'is_siteroot' => 0,
1714 ],
1715 1 => [
1716 'uid' => 44,
1717 'is_siteroot' => 1,
1718 ],
1719 0 => [
1720 'uid' => 0,
1721 'is_siteroot' => null,
1722 ],
1723 ],
1724 'pageTsConfig' => [],
1725 ];
1726 ArrayUtility::mergeRecursiveWithOverrule($input, $inputOverride);
1727
1728 $GLOBALS['TCA']['fTable'] = [];
1729
1730 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1731
1732 /** @var Statement|ObjectProphecy $statementProphet */
1733 $statementProphet = $this->prophesize(Statement::class);
1734
1735 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1736 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1737 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1738 $queryBuilderProphet->where(...array_shift($expectedWhere))->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1739 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1740
1741 while ($constraint = array_shift($expectedWhere)) {
1742 $queryBuilderProphet->andWhere(...$constraint)
1743 ->shouldBeCalled()
1744 ->willReturn($queryBuilderProphet->reveal());
1745 }
1746
1747 // Two instances are needed due to the push/pop behavior of addInstance()
1748 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1749 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1750
1751 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1752 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1753 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1754 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1755
1756 $this->subject->addData($input);
1757 }
1758
1759 /**
1760 * @test
1761 */
1762 public function addDataThrowsExceptionIfForeignTableIsNotDefinedInTca()
1763 {
1764 $input = [
1765 'tableName' => 'aTable',
1766 'processedTca' => [
1767 'columns' => [
1768 'aField' => [
1769 'config' => [
1770 'type' => 'select',
1771 'renderType' => 'selectSingle',
1772 'foreign_table' => 'fTable',
1773 ],
1774 ],
1775 ]
1776 ],
1777 ];
1778
1779 $this->expectException(\UnexpectedValueException::class);
1780 $this->expectExceptionCode(1439569743);
1781
1782 $this->subject->addData($input);
1783 }
1784
1785 /**
1786 * @test
1787 */
1788 public function addDataForeignTableSplitsGroupOrderAndLimit()
1789 {
1790 $input = [
1791 'tableName' => 'aTable',
1792 'databaseRow' => [],
1793 'processedTca' => [
1794 'columns' => [
1795 'aField' => [
1796 'config' => [
1797 'type' => 'select',
1798 'renderType' => 'selectSingle',
1799 'foreign_table' => 'fTable',
1800 'foreign_table_where' => 'AND ftable.uid=1 GROUP BY groupField ORDER BY orderField LIMIT 1,2',
1801 ],
1802 ],
1803 ]
1804 ],
1805 'rootline' => [],
1806 ];
1807
1808 $GLOBALS['TCA']['fTable'] = [];
1809
1810 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1811 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1812 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1813 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1814
1815 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1816
1817 /** @var Statement|ObjectProphecy $statementProphet */
1818 $statementProphet = $this->prophesize(Statement::class);
1819
1820 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1821 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1822 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1823 $queryBuilderProphet->groupBy(['groupField'])->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1824 $queryBuilderProphet->addOrderBy('orderField', null)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1825 $queryBuilderProphet->setFirstResult(1)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1826 $queryBuilderProphet->setMaxResults(2)->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1827 $queryBuilderProphet->where('ftable.uid=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1828 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1829 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1830 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1831
1832 // Two instances are needed due to the push/pop behavior of addInstance()
1833 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1834 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1835
1836 $this->subject->addData($input);
1837 }
1838
1839 /**
1840 * @test
1841 */
1842 public function addDataForeignTableQueuesFlashMessageOnDatabaseError()
1843 {
1844 $input = [
1845 'databaseRow' => [
1846 'aField' => '',
1847 ],
1848 'tableName' => 'aTable',
1849 'processedTca' => [
1850 'columns' => [
1851 'aField' => [
1852 'config' => [
1853 'type' => 'select',
1854 'renderType' => 'selectSingle',
1855 'foreign_table' => 'fTable',
1856 'items' => [
1857 0 => [
1858 0 => 'itemLabel',
1859 1 => 'itemValue',
1860 2 => null,
1861 3 => null,
1862 ],
1863 ],
1864 'maxitems' => 99999,
1865 ],
1866 ],
1867 ]
1868 ],
1869 'rootline' => [],
1870 ];
1871
1872 $GLOBALS['TCA']['fTable'] = [];
1873
1874 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1875 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1876 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1877 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1878
1879 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1880 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1881 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1882 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1883
1884 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1885
1886 /** @var Statement|ObjectProphecy $statementProphet */
1887 $statementProphet = $this->prophesize(Statement::class);
1888
1889 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1890 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1891 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1892 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1893 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1894 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1895
1896 $prevException = new DBALException('Invalid table name', 1476045274);
1897 $exception = new DBALException('Driver error', 1476045971, $prevException);
1898
1899 $queryBuilderProphet->execute()->shouldBeCalled()->willThrow($exception);
1900
1901 // Two instances are needed due to the push/pop behavior of addInstance()
1902 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1903 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1904
1905 /** @var FlashMessage|ObjectProphecy $flashMessage */
1906 $flashMessage = $this->prophesize(FlashMessage::class);
1907 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
1908 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
1909 $flashMessageService = $this->prophesize(FlashMessageService::class);
1910 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
1911 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
1912 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
1913 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
1914
1915 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
1916
1917 $expected = $input;
1918 $expected['databaseRow']['aField'] = [];
1919
1920 $this->assertEquals($expected, $this->subject->addData($input));
1921 }
1922
1923 /**
1924 * @test
1925 */
1926 public function addDataForeignTableHandlesForeignTableRows()
1927 {
1928 $input = [
1929 'databaseRow' => [
1930 'aField' => '',
1931 ],
1932 'tableName' => 'aTable',
1933 'processedTca' => [
1934 'columns' => [
1935 'aField' => [
1936 'config' => [
1937 'type' => 'select',
1938 'renderType' => 'selectSingle',
1939 'foreign_table' => 'fTable',
1940 'foreign_table_prefix' => 'aPrefix',
1941 'items' => [],
1942 'maxitems' => 99999,
1943 ],
1944 ],
1945 ]
1946 ],
1947 'rootline' => [],
1948 ];
1949
1950 $GLOBALS['TCA']['fTable'] = [];
1951
1952 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1953 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1954 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1955 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1956
1957 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1958 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1959 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1960 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1961
1962 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
1963
1964 /** @var Statement|ObjectProphecy $statementProphet */
1965 $statementProphet = $this->prophesize(Statement::class);
1966
1967 $queryBuilderProphet->select('fTable.uid')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1968 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1969 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1970 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1971 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1972 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
1973 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
1974
1975 // Two instances are needed due to the push/pop behavior of addInstance()
1976 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1977 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
1978
1979 $counter = 0;
1980 $statementProphet->fetch()->shouldBeCalled()->will(function ($args) use (&$counter) {
1981 $counter++;
1982 if ($counter >= 3) {
1983 return false;
1984 }
1985 return [
1986 'uid' => $counter,
1987 'aValue' => 'bar,',
1988 ];
1989 });
1990
1991 $expected = $input;
1992 $expected['processedTca']['columns']['aField']['config']['items'] = [
1993 0 => [
1994 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
1995 1 => 1,
1996 2 => 'default-not-found',
1997 3 => null,
1998 ],
1999 1 => [
2000 0 => 'aPrefix[LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.no_title]',
2001 1 => 2,
2002 2 => 'default-not-found',
2003 3 => null,
2004 ],
2005 ];
2006
2007 $expected['databaseRow']['aField'] = [];
2008
2009 $this->assertEquals($expected, $this->subject->addData($input));
2010 }
2011
2012 /**
2013 * @test
2014 */
2015 public function addDataForeignTableResolvesIconFromSelicon()
2016 {
2017 $input = [
2018 'databaseRow' => [
2019 'aField' => '',
2020 ],
2021 'tableName' => 'aTable',
2022 'processedTca' => [
2023 'columns' => [
2024 'aField' => [
2025 'config' => [
2026 'type' => 'select',
2027 'renderType' => 'selectSingle',
2028 'foreign_table' => 'fTable',
2029 'maxitems' => 99999,
2030 ],
2031 ],
2032 ]
2033 ],
2034 'rootline' => [],
2035 ];
2036
2037 // Fake the foreign_table
2038 $GLOBALS['TCA']['fTable'] = [
2039 'ctrl' => [
2040 'label' => 'icon',
2041 'selicon_field' => 'icon',
2042 'selicon_field_path' => 'uploads/media',
2043 ],
2044 'columns' =>[
2045 'icon' => [],
2046 ],
2047 ];
2048
2049 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2050 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2051 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2052 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2053
2054 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
2055 $languageServiceProphecy = $this->prophesize(LanguageService::class);
2056 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
2057 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
2058
2059 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection();
2060
2061 /** @var Statement|ObjectProphecy $statementProphet */
2062 $statementProphet = $this->prophesize(Statement::class);
2063
2064 $queryBuilderProphet->select('fTable.uid', 'fTable.icon')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2065 $queryBuilderProphet->from('fTable')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2066 $queryBuilderProphet->from('pages')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2067 $queryBuilderProphet->where('')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2068 $queryBuilderProphet->andWhere(' 1=1')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2069 $queryBuilderProphet->andWhere('`pages.uid` = `fTable.pid`')->shouldBeCalled()->willReturn($queryBuilderProphet->reveal());
2070 $queryBuilderProphet->execute()->shouldBeCalled()->willReturn($statementProphet->reveal());
2071
2072 // Two instances are needed due to the push/pop behavior of addInstance()
2073 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
2074 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
2075
2076 // Query returns one row, then false on second call
2077 $foreignTableRowResultOne = [
2078 'uid' => 1,
2079 'icon' => 'foo.jpg',
2080 ];
2081 $statementProphet->fetch()->shouldBeCalled()->willReturn($foreignTableRowResultOne, false);
2082
2083 $expected = $input;
2084 $expected['processedTca']['columns']['aField']['config']['items'] = [
2085 0 => [
2086 0 => 'foo.jpg',
2087 1 => 1,
2088 2 => 'uploads/media/foo.jpg', // combination of selicon_field_path and the row value of field 'icon'
2089 3 => null,
2090 ],
2091 ];
2092 $expected['databaseRow']['aField'] = [];
2093
2094 $this->assertEquals($expected, $this->subject->addData($input));
2095 }
2096
2097 /**
2098 * @test
2099 */
2100 public function addDataRemovesItemsByKeepItemsPageTsConfig()
2101 {
2102 $input = [
2103 'databaseRow' => [
2104 'aField' => '',
2105 ],
2106 'tableName' => 'aTable',
2107 'processedTca' => [
2108 'columns' => [
2109 'aField' => [
2110 'config' => [
2111 'type' => 'select',
2112 'renderType' => 'selectSingle',
2113 'items' => [
2114 0 => [
2115 0 => 'keepMe',
2116 1 => 'keep',
2117 null,
2118 null,
2119 ],
2120 1 => [
2121 0 => 'removeMe',
2122 1 => 'remove',
2123 ],
2124 ],
2125 'maxitems' => 99999,
2126 ],
2127 ],
2128 ]
2129 ],
2130 'pageTsConfig' => [
2131 'TCEFORM.' => [
2132 'aTable.' => [
2133 'aField.' => [
2134 'keepItems' => 'keep',
2135 ],
2136 ],
2137 ],
2138 ],
2139 ];
2140
2141 /** @var LanguageService|ObjectProphecy $languageService */
2142 $languageService = $this->prophesize(LanguageService::class);
2143 $GLOBALS['LANG'] = $languageService->reveal();
2144 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2145
2146 $expected = $input;
2147 $expected['databaseRow']['aField'] = [];
2148 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2149
2150 $this->assertEquals($expected, $this->subject->addData($input));
2151 }
2152
2153 /**
2154 * @test
2155 */
2156 public function addDataRemovesAllItemsByEmptyKeepItemsPageTsConfig()
2157 {
2158 $input = [
2159 'databaseRow' => [
2160 'aField' => '',
2161 ],
2162 'tableName' => 'aTable',
2163 'processedTca' => [
2164 'columns' => [
2165 'aField' => [
2166 'config' => [
2167 'type' => 'select',
2168 'renderType' => 'selectSingle',
2169 'items' => [
2170 0 => [
2171 0 => 'keepMe',
2172 1 => 'keep',
2173 null,
2174 null,
2175 ],
2176 1 => [
2177 0 => 'removeMe',
2178 1 => 'remove',
2179 ],
2180 ],
2181 'maxitems' => 99999,
2182 ],
2183 ],
2184 ]
2185 ],
2186 'pageTsConfig' => [
2187 'TCEFORM.' => [
2188 'aTable.' => [
2189 'aField.' => [
2190 'keepItems' => '',
2191 ],
2192 ],
2193 ],
2194 ],
2195 ];
2196
2197 /** @var LanguageService|ObjectProphecy $languageService */
2198 $languageService = $this->prophesize(LanguageService::class);
2199 $GLOBALS['LANG'] = $languageService->reveal();
2200 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2201
2202 $expected = $input;
2203 $expected['databaseRow']['aField'] = [];
2204 $expected['processedTca']['columns']['aField']['config']['items'] = [];
2205
2206 $this->assertEquals($expected, $this->subject->addData($input));
2207 }
2208
2209 /**
2210 * @test
2211 */
2212 public function addDataEvaluatesKeepItemsBeforeAddItemsFromPageTsConfig()
2213 {
2214 $input = [
2215 'databaseRow' => [
2216 'aField' => '',
2217 ],
2218 'tableName' => 'aTable',
2219 'processedTca' => [
2220 'columns' => [
2221 'aField' => [
2222 'config' => [
2223 'type' => 'select',
2224 'renderType' => 'selectSingle',
2225 'items' => [
2226 0 => [
2227 0 => 'keepMe',
2228 1 => '1',
2229 null,
2230 null,
2231 ],
2232 1 => [
2233 0 => 'removeMe',
2234 1 => 'remove',
2235 ],
2236 ],
2237 'maxitems' => 99999,
2238 ],
2239 ],
2240 ]
2241 ],
2242 'pageTsConfig' => [
2243 'TCEFORM.' => [
2244 'aTable.' => [
2245 'aField.' => [
2246 'keepItems' => '1',
2247 'addItems.' => [
2248 '1' => 'addItem #1',
2249 '12' => 'addItem #12',
2250 ],
2251 ],
2252 ],
2253 ],
2254 ],
2255 ];
2256
2257 /** @var LanguageService|ObjectProphecy $languageService */
2258 $languageService = $this->prophesize(LanguageService::class);
2259 $GLOBALS['LANG'] = $languageService->reveal();
2260 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2261
2262 $expected = $input;
2263 $expected['databaseRow']['aField'] = [];
2264 $expected['processedTca']['columns']['aField']['config']['items'] = [
2265 0 => [
2266 0 => 'keepMe',
2267 1 => '1',
2268 null,
2269 null,
2270 ],
2271 1 => [
2272 0 => 'addItem #1',
2273 1 => '1',
2274 null,
2275 null,
2276 ],
2277 2 => [
2278 0 => 'addItem #12',
2279 1 => '12',
2280 null,
2281 null,
2282 ],
2283 ];
2284
2285 $this->assertEquals($expected, $this->subject->addData($input));
2286 }
2287
2288 /**
2289 * @test
2290 */
2291 public function addDataRemovesItemsByRemoveItemsPageTsConfig()
2292 {
2293 $input = [
2294 'databaseRow' => [
2295 'aField' => ''
2296 ],
2297 'tableName' => 'aTable',
2298 'processedTca' => [
2299 'columns' => [
2300 'aField' => [
2301 'config' => [
2302 'type' => 'select',
2303 'renderType' => 'selectSingle',
2304 'items' => [
2305 0 => [
2306 0 => 'keepMe',
2307 1 => 'keep',
2308 null,
2309 null,
2310 ],
2311 1 => [
2312 0 => 'removeMe',
2313 1 => 'remove',
2314 ],
2315 ],
2316 'maxitems' => 99999,
2317 ],
2318 ],
2319 ]
2320 ],
2321 'pageTsConfig' => [
2322 'TCEFORM.' => [
2323 'aTable.' => [
2324 'aField.' => [
2325 'removeItems' => 'remove',
2326 ],
2327 ],
2328 ],
2329 ],
2330 ];
2331
2332 /** @var LanguageService|ObjectProphecy $languageService */
2333 $languageService = $this->prophesize(LanguageService::class);
2334 $GLOBALS['LANG'] = $languageService->reveal();
2335 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2336
2337 $expected = $input;
2338 $expected['databaseRow']['aField'] = [];
2339 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2340
2341 $this->assertEquals($expected, $this->subject->addData($input));
2342 }
2343
2344 /**
2345 * @test
2346 */
2347 public function addDataRemovesItemsAddedByAddItemsFromPageTsConfigByRemoveItemsPageTsConfig()
2348 {
2349 $input = [
2350 'databaseRow' => [
2351 'aField' => ''
2352 ],
2353 'tableName' => 'aTable',
2354 'processedTca' => [
2355 'columns' => [
2356 'aField' => [
2357 'config' => [
2358 'type' => 'select',
2359 'renderType' => 'selectSingle',
2360 'items' => [
2361 0 => [
2362 0 => 'keepMe',
2363 1 => 'keep',
2364 null,
2365 null,
2366 ],
2367 1 => [
2368 0 => 'removeMe',
2369 1 => 'remove',
2370 ],
2371 ],
2372 'maxitems' => 99999,
2373 ],
2374 ],
2375 ]
2376 ],
2377 'pageTsConfig' => [
2378 'TCEFORM.' => [
2379 'aTable.' => [
2380 'aField.' => [
2381 'removeItems' => 'remove,add',
2382 'addItems.' => [
2383 'add' => 'addMe'
2384 ]
2385 ],
2386 ],
2387 ],
2388 ],
2389 ];
2390
2391 /** @var LanguageService|ObjectProphecy $languageService */
2392 $languageService = $this->prophesize(LanguageService::class);
2393 $GLOBALS['LANG'] = $languageService->reveal();
2394 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2395
2396 $expected = $input;
2397 $expected['databaseRow']['aField'] = [];
2398 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2399
2400 $this->assertEquals($expected, $this->subject->addData($input));
2401 }
2402
2403 /**
2404 * @test
2405 */
2406 public function addDataRemovesItemsByLanguageFieldUserRestriction()
2407 {
2408 $input = [
2409 'databaseRow' => [
2410 'aField' => 'aValue,remove'
2411 ],
2412 'tableName' => 'aTable',
2413 'processedTca' => [
2414 'ctrl' => [
2415 'languageField' => 'aField',
2416 ],
2417 'columns' => [
2418 'aField' => [
2419 'config' => [
2420 'type' => 'select',
2421 'renderType' => 'selectSingle',
2422 'items' => [
2423 0 => [
2424 0 => 'keepMe',
2425 1 => 'keep',
2426 null,
2427 null,
2428 ],
2429 1 => [
2430 0 => 'removeMe',
2431 1 => 'remove',
2432 ],
2433 ],
2434 'maxitems' => 99999,
2435 ],
2436 ],
2437 ]
2438 ],
2439 ];
2440
2441 /** @var LanguageService|ObjectProphecy $languageService */
2442 $languageService = $this->prophesize(LanguageService::class);
2443 $GLOBALS['LANG'] = $languageService->reveal();
2444 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2445 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2446
2447 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2448 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2449 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2450 $backendUserProphecy->checkLanguageAccess('keep')->shouldBeCalled()->willReturn(true);
2451 $backendUserProphecy->checkLanguageAccess('remove')->shouldBeCalled()->willReturn(false);
2452
2453 $expected = $input;
2454 $expected['databaseRow']['aField'] = [];
2455 $expected['processedTca']['columns']['aField']['config']['items'] = [
2456 [ '[ INVALID VALUE "aValue" ]', 'aValue', null, null ],
2457 [ 'keepMe', 'keep', null, null ],
2458 ];
2459
2460 $this->assertEquals($expected, $this->subject->addData($input));
2461 }
2462
2463 /**
2464 * @test
2465 */
2466 public function addDataRemovesItemsByUserAuthModeRestriction()
2467 {
2468 $input = [
2469 'databaseRow' => [
2470 'aField' => 'keep,remove'
2471 ],
2472 'tableName' => 'aTable',
2473 'processedTca' => [
2474 'columns' => [
2475 'aField' => [
2476 'config' => [
2477 'type' => 'select',
2478 'renderType' => 'selectSingle',
2479 'authMode' => 'explicitAllow',
2480 'items' => [
2481 0 => [
2482 0 => 'keepMe',
2483 1 => 'keep',
2484 null,
2485 null,
2486 ],
2487 1 => [
2488 0 => 'removeMe',
2489 1 => 'remove',
2490 ],
2491 ],
2492 'maxitems' => 99999,
2493 ],
2494 ],
2495 ]
2496 ],
2497 ];
2498
2499 /** @var LanguageService|ObjectProphecy $languageService */
2500 $languageService = $this->prophesize(LanguageService::class);
2501 $GLOBALS['LANG'] = $languageService->reveal();
2502 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2503
2504 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2505 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2506 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2507 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'keep', 'explicitAllow')->shouldBeCalled()->willReturn(true);
2508 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'remove', 'explicitAllow')->shouldBeCalled()->willReturn(false);
2509
2510 $expected = $input;
2511 $expected['databaseRow']['aField'] = ['keep'];
2512 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2513
2514 $this->assertEquals($expected, $this->subject->addData($input));
2515 }
2516
2517 /**
2518 * @test
2519 */
2520 public function addDataKeepsAllPagesDoktypesForAdminUser()
2521 {
2522 $input = [
2523 'databaseRow' => [
2524 'doktype' => 'keep'
2525 ],
2526 'tableName' => 'pages',
2527 'processedTca' => [
2528 'columns' => [
2529 'doktype' => [
2530 'config' => [
2531 'type' => 'select',
2532 'renderType' => 'selectSingle',
2533 'items' => [
2534 0 => [
2535 0 => 'keepMe',
2536 1 => 'keep',
2537 null,
2538 null,
2539 ],
2540 ],
2541 'maxitems' => 99999,
2542 ],
2543 ],
2544 ],
2545 ],
2546 ];
2547
2548 /** @var LanguageService|ObjectProphecy $languageService */
2549 $languageService = $this->prophesize(LanguageService::class);
2550 $GLOBALS['LANG'] = $languageService->reveal();
2551 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2552
2553 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2554 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2555 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2556 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
2557
2558 $expected = $input;
2559 $expected['databaseRow']['doktype'] = ['keep'];
2560
2561 $this->assertEquals($expected, $this->subject->addData($input));
2562 }
2563
2564 /**
2565 * @test
2566 */
2567 public function addDataKeepsAllowedPageTypesForNonAdminUser()
2568 {
2569 $input = [
2570 'databaseRow' => [
2571 'doktype' => 'keep',
2572 ],
2573 'tableName' => 'pages',
2574 'processedTca' => [
2575 'columns' => [
2576 'doktype' => [
2577 'config' => [
2578 'type' => 'select',
2579 'renderType' => 'selectSingle',
2580 'items' => [
2581 0 => [
2582 0 => 'keepMe',
2583 1 => 'keep',
2584 null,
2585 null,
2586 ],
2587 1 => [
2588 0 => 'removeMe',
2589 1 => 'remove',
2590 ],
2591 ],
2592 'maxitems' => 99999,
2593 ],
2594 ],
2595 ],
2596 ],
2597 ];
2598
2599 /** @var LanguageService|ObjectProphecy $languageService */
2600 $languageService = $this->prophesize(LanguageService::class);
2601 $GLOBALS['LANG'] = $languageService->reveal();
2602 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2603
2604 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2605 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2606 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2607 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
2608 $backendUserProphecy->groupData = [
2609 'pagetypes_select' => 'foo,keep,anotherAllowedDoktype',
2610 ];
2611
2612 $expected = $input;
2613 $expected['databaseRow']['doktype'] = ['keep'];
2614 unset($expected['processedTca']['columns']['doktype']['config']['items'][1]);
2615
2616 $this->assertEquals($expected, $this->subject->addData($input));
2617 }
2618
2619 /**
2620 * @test
2621 */
2622 public function addDataCallsItemsProcFunc()
2623 {
2624 $input = [
2625 'tableName' => 'aTable',
2626 'databaseRow' => [
2627 'aField' => 'aValue'
2628 ],
2629 'processedTca' => [
2630 'columns' => [
2631 'aField' => [
2632 'config' => [
2633 'type' => 'select',
2634 'renderType' => 'selectSingle',
2635 'items' => [],
2636 'itemsProcFunc' => function (array $parameters, $pObj) {
2637 $parameters['items'] = [
2638 0 => [
2639 0 => 'aLabel',
2640 1 => 'aValue',
2641 2 => null,
2642 3 => null,
2643 ],
2644 ];
2645 },
2646 ],
2647 ],
2648 ],
2649 ],
2650 ];
2651
2652 /** @var LanguageService|ObjectProphecy $languageService */
2653 $languageService = $this->prophesize(LanguageService::class);
2654 $GLOBALS['LANG'] = $languageService->reveal();
2655 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2656
2657 $expected = $input;
2658 $expected['databaseRow']['aField'] = ['aValue'];
2659 $expected['processedTca']['columns']['aField']['config'] = [
2660 'type' => 'select',
2661 'renderType' => 'selectSingle',
2662 'items' => [
2663 0 => [
2664 0 => 'aLabel',
2665 1 => 'aValue',
2666 2 => null,
2667 3 => null,
2668 ],
2669 ],
2670 'maxitems' => 99999,
2671 ];
2672
2673 $this->assertSame($expected, $this->subject->addData($input));
2674 }
2675
2676 /**
2677 * @test
2678 */
2679 public function addDataItemsProcFuncReceivesParameters()
2680 {
2681 $input = [
2682 'tableName' => 'aTable',
2683 'databaseRow' => [
2684 'aField' => 'aValue',
2685 ],
2686 'pageTsConfig' => [
2687 'TCEFORM.' => [
2688 'aTable.' => [
2689 'aField.' => [
2690 'itemsProcFunc.' => [
2691 'itemParamKey' => 'itemParamValue',
2692 ],
2693 ]
2694 ],
2695 ],
2696 ],
2697 'processedTca' => [
2698 'columns' => [
2699 'aField' => [
2700 'config' => [
2701 'type' => 'select',
2702 'renderType' => 'selectSingle',
2703 'aKey' => 'aValue',
2704 'items' => [
2705 0 => [
2706 0 => 'aLabel',
2707 1 => 'aValue',
2708 ],
2709 ],
2710 'itemsProcFunc' => function (array $parameters, $pObj) {
2711 if ($parameters['items'] !== [ 0 => [ 'aLabel', 'aValue'] ]
2712 || $parameters['config']['aKey'] !== 'aValue'
2713 || $parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
2714 || $parameters['table'] !== 'aTable'
2715 || $parameters['row'] !== [ 'aField' => 'aValue' ]
2716 || $parameters['field'] !== 'aField'
2717 ) {
2718 throw new \UnexpectedValueException('broken', 1476109436);
2719 }
2720 },
2721 ],
2722 ],
2723 ],
2724 ],
2725 ];
2726
2727 $languageService = $this->prophesize(LanguageService::class);
2728 $GLOBALS['LANG'] = $languageService->reveal();
2729 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2730 /** @var FlashMessage|ObjectProphecy $flashMessage */
2731 $flashMessage = $this->prophesize(FlashMessage::class);
2732 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2733 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2734 $flashMessageService = $this->prophesize(FlashMessageService::class);
2735 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2736 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2737 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2738 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2739
2740 // itemsProcFunc must NOT have raised an exception
2741 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
2742
2743 $this->subject->addData($input);
2744 }
2745
2746 /**
2747 * @test
2748 */
2749 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
2750 {
2751 $input = [
2752 'tableName' => 'aTable',
2753 'databaseRow' => [
2754 'aField' => 'aValue',
2755 ],
2756 'pageTsConfig' => [
2757 'TCEFORM.' => [
2758 'aTable.' => [
2759 'aField.' => [
2760 'itemsProcFunc.' => [
2761 'itemParamKey' => 'itemParamValue',
2762 ],
2763 ]
2764 ],
2765 ],
2766 ],
2767 'processedTca' => [
2768 'columns' => [
2769 'aField' => [
2770 'config' => [
2771 'type' => 'select',
2772 'renderType' => 'selectSingle',
2773 'aKey' => 'aValue',
2774 'items' => [
2775 0 => [
2776 0 => 'aLabel',
2777 1 => 'aValue',
2778 ],
2779 ],
2780 'itemsProcFunc' => function (array $parameters, $pObj) {
2781 throw new \UnexpectedValueException('anException', 1476109437);
2782 },
2783 ],
2784 ],
2785 ],
2786 ],
2787 ];
2788
2789 $languageService = $this->prophesize(LanguageService::class);
2790 $GLOBALS['LANG'] = $languageService->reveal();
2791 /** @var FlashMessage|ObjectProphecy $flashMessage */
2792 $flashMessage = $this->prophesize(FlashMessage::class);
2793 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2794 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2795 $flashMessageService = $this->prophesize(FlashMessageService::class);
2796 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2797 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2798 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2799 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2800
2801 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
2802
2803 $this->subject->addData($input);
2804 }
2805
2806 /**
2807 * @test
2808 */
2809 public function addDataTranslatesItemLabelsFromPageTsConfig()
2810 {
2811 $input = [
2812 'databaseRow' => [
2813 'aField' => 'aValue',
2814 ],
2815 'tableName' => 'aTable',
2816 'processedTca' => [
2817 'columns' => [
2818 'aField' => [
2819 'config' => [
2820 'type' => 'select',
2821 'renderType' => 'selectSingle',
2822 'items' => [
2823 0 => [
2824 0 => 'aLabel',
2825 1 => 'aValue',
2826 null,
2827 null,
2828 ],
2829 ],
2830 'maxitems' => 99999,
2831 ],
2832 ],
2833 ],
2834 ],
2835 'pageTsConfig' => [
2836 'TCEFORM.' => [
2837 'aTable.' => [
2838 'aField.' => [
2839 'altLabels.' => [
2840 'aValue' => 'labelOverride',
2841 ],
2842 ]
2843 ],
2844 ],
2845 ],
2846 ];
2847
2848 /** @var LanguageService|ObjectProphecy $languageService */
2849 $languageService = $this->prophesize(LanguageService::class);
2850 $GLOBALS['LANG'] = $languageService->reveal();
2851 $languageService->sL('aLabel')->willReturnArgument(0);
2852 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
2853 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2854
2855 $expected = $input;
2856 $expected['databaseRow']['aField'] = ['aValue'];
2857 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
2858
2859 $this->assertSame($expected, $this->subject->addData($input));
2860 $this->subject->addData($input);
2861 }
2862
2863 /**
2864 * @test
2865 */
2866 public function processSelectFieldValueSetsMmForeignRelationValues()
2867 {
2868 $GLOBALS['TCA']['foreignTable'] = [];
2869
2870 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2871 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2872 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2873 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2874
2875 $this->mockDatabaseConnectionForProcessSelectField();
2876
2877 $input = [
2878 'tableName' => 'aTable',
2879 'databaseRow' => [
2880 'uid' => 42,
2881 // Two connected rows
2882 'aField' => 2,
2883 ],
2884 'processedTca' => [
2885 'columns' => [
2886 'aField' => [
2887 'config' => [
2888 'type' => 'select',
2889 'renderType' => 'selectSingle',
2890 'maxitems' => 999,
2891 'foreign_table' => 'foreignTable',
2892 'MM' => 'aTable_foreignTable_mm',
2893 'items' => [],
2894 ],
2895 ],
2896 ],
2897 ],
2898 ];
2899 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2900 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2901 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2902 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2903
2904 $relationHandlerUids = [
2905 23,
2906 24
2907 ];
2908
2909 $relationHandlerProphecy->start(2, 'foreignTable', 'aTable_foreignTable_mm', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2910 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2911
2912 $expected = $input;
2913 $expected['databaseRow']['aField'] = $relationHandlerUids;
2914
2915 $this->assertEquals($expected, $this->subject->addData($input));
2916 }
2917
2918 /**
2919 * @test
2920 */
2921 public function processSelectFieldValueSetsForeignRelationValues()
2922 {
2923 $GLOBALS['TCA']['foreignTable'] = [];
2924
2925 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2926 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2927 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2928 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2929
2930 $this->mockDatabaseConnectionForProcessSelectField();
2931
2932 $input = [
2933 'tableName' => 'aTable',
2934 'databaseRow' => [
2935 'uid' => 42,
2936 // Two connected rows
2937 'aField' => '22,23,24,25',
2938 ],
2939 'processedTca' => [
2940 'columns' => [
2941 'aField' => [
2942 'config' => [
2943 'type' => 'select',
2944 'renderType' => 'selectSingle',
2945 'maxitems' => 999,
2946 'foreign_table' => 'foreignTable',
2947 'items' => [],
2948 ],
2949 ],
2950 ],
2951 ],
2952 ];
2953 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2954 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2955 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2956 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2957
2958 $relationHandlerUids = [
2959 23,
2960 24
2961 ];
2962
2963 $relationHandlerProphecy->start('22,23,24,25', 'foreignTable', '', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2964 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2965
2966 $expected = $input;
2967 $expected['databaseRow']['aField'] = $relationHandlerUids;
2968
2969 $this->assertEquals($expected, $this->subject->addData($input));
2970 }
2971
2972 /**
2973 * @test
2974 */
2975 public function processSelectFieldValueRemovesInvalidDynamicValues()
2976 {
2977 $languageService = $this->prophesize(LanguageService::class);
2978 $GLOBALS['LANG'] = $languageService->reveal();
2979 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2980
2981 $GLOBALS['TCA']['foreignTable'] = [];
2982
2983 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2984 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2985 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2986 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
2987
2988 $this->mockDatabaseConnectionForProcessSelectField();
2989
2990 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2991 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2992 $relationHandlerProphecy->start(Argument::cetera())->shouldBeCalled();
2993 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldBeCalled()->willReturn([1]);
2994
2995 $input = [
2996 'tableName' => 'aTable',
2997 'databaseRow' => [
2998 'aField' => '1,2,bar,foo',
2999 ],
3000 'processedTca' => [
3001 'columns' => [
3002 'aField' => [
3003 'config' => [
3004 'type' => 'select',
3005 'renderType' => 'selectSingleBox',
3006 'foreign_table' => 'foreignTable',
3007 'maxitems' => 999,
3008 'items' => [
3009 ['foo', 'foo', null, null],