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