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