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