db6d664893d110b1fed0f033e1f32567ca5718f4
[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 'systemLanguageRows' => [
940 0 => [
941 'title' => 'aLangTitle',
942 'uid' => 42,
943 'flagIconIdentifier' => 'aFlag.gif',
944 ],
945 ],
946 ];
947
948 /** @var LanguageService|ObjectProphecy $languageService */
949 $languageService = $this->prophesize(LanguageService::class);
950 $GLOBALS['LANG'] = $languageService->reveal();
951 $languageService->sL(Argument::cetera())->willReturnArgument(0);
952
953 $expectedItems = [
954 0 => [
955 0 => 'aLangTitle [42]',
956 1 => 42,
957 2 => 'aFlag.gif',
958 3 => null,
959 ],
960 ];
961
962 $result = $this->subject->addData($input);
963
964 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
965 }
966
967 /**
968 * @test
969 */
970 public function addDataAddsCustomOptionsWithSpecialCustom()
971 {
972 $input = [
973 'tableName' => 'aTable',
974 'processedTca' => [
975 'columns' => [
976 'aField' => [
977 'config' => [
978 'type' => 'select',
979 'renderType' => 'selectSingle',
980 'special' => 'custom',
981 ],
982 ],
983 ],
984 ],
985 ];
986
987 /** @var LanguageService|ObjectProphecy $languageService */
988 $languageService = $this->prophesize(LanguageService::class);
989 $GLOBALS['LANG'] = $languageService->reveal();
990 $languageService->sL(Argument::cetera())->willReturnArgument(0);
991
992 $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'] = [
993 'aKey' => [
994 'header' => 'aHeader',
995 'items' => [
996 'anItemKey' => [
997 0 => 'anItemTitle',
998 ],
999 ],
1000 ]
1001 ];
1002
1003 $expectedItems = [
1004 0 => [
1005 0 => 'aHeader',
1006 1 => '--div--',
1007 null,
1008 null,
1009 ],
1010 1 => [
1011 0 => 'anItemTitle',
1012 1 => 'aKey:anItemKey',
1013 2 => 'empty-empty',
1014 3 => null,
1015 ],
1016 ];
1017
1018 $result = $this->subject->addData($input);
1019
1020 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1021 }
1022
1023 /**
1024 * @test
1025 */
1026 public function addDataAddsGroupItemsWithSpecialModListGroup()
1027 {
1028 $input = [
1029 'tableName' => 'aTable',
1030 'processedTca' => [
1031 'columns' => [
1032 'aField' => [
1033 'config' => [
1034 'type' => 'select',
1035 'renderType' => 'selectSingle',
1036 'special' => 'modListGroup',
1037 ],
1038 ],
1039 ],
1040 ],
1041 ];
1042
1043 $GLOBALS['TBE_MODULES'] = [];
1044
1045 /** @var LanguageService|ObjectProphecy $languageService */
1046 $languageService = $this->prophesize(LanguageService::class);
1047 $GLOBALS['LANG'] = $languageService->reveal();
1048 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1049 $languageService->moduleLabels = [
1050 'tabs_images' => [
1051 'aModule_tab' => PATH_site . 'aModuleTabIcon.gif',
1052 ],
1053 'labels' => [
1054 'aModule_tablabel' => 'aModuleTabLabel',
1055 'aModule_tabdescr' => 'aModuleTabDescription',
1056 ],
1057 'tabs' => [
1058 'aModule_tab' => 'aModuleLabel',
1059 ]
1060 ];
1061
1062 /** @var ModuleLoader|ObjectProphecy $moduleLoaderProphecy */
1063 $moduleLoaderProphecy = $this->prophesize(ModuleLoader::class);
1064 GeneralUtility::addInstance(ModuleLoader::class, $moduleLoaderProphecy->reveal());
1065 $moduleLoaderProphecy->load([])->shouldBeCalled();
1066 $moduleLoaderProphecy->modListGroup = [
1067 'aModule',
1068 ];
1069
1070 $expectedItems = [
1071 0 => [
1072 0 => 'aModuleLabel',
1073 1 => 'aModule',
1074 2 => '../aModuleTabIcon.gif',
1075 3 => [
1076 'title' => 'aModuleTabLabel',
1077 'description' => 'aModuleTabDescription',
1078 ],
1079 ],
1080 ];
1081
1082 $result = $this->subject->addData($input);
1083
1084 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1085 }
1086
1087 /**
1088 * @test
1089 */
1090 public function addDataAddsFileItemsWithConfiguredFileFolder()
1091 {
1092 $directory = $this->getUniqueId('typo3temp/test-') . '/';
1093 $input = [
1094 'tableName' => 'aTable',
1095 'processedTca' => [
1096 'columns' => [
1097 'aField' => [
1098 'config' => [
1099 'type' => 'select',
1100 'renderType' => 'selectSingle',
1101 'fileFolder' => $directory,
1102 'fileFolder_extList' => 'gif',
1103 'fileFolder_recursions' => 1,
1104 ],
1105 ],
1106 ],
1107 ],
1108 ];
1109
1110 /** @var LanguageService|ObjectProphecy $languageService */
1111 $languageService = $this->prophesize(LanguageService::class);
1112 $GLOBALS['LANG'] = $languageService->reveal();
1113 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1114
1115 mkdir(PATH_site . $directory);
1116 $this->testFilesToDelete[] = PATH_site . $directory;
1117 touch(PATH_site . $directory . 'anImage.gif');
1118 touch(PATH_site . $directory . 'aFile.txt');
1119 mkdir(PATH_site . $directory . '/subdir');
1120 touch(PATH_site . $directory . '/subdir/anotherImage.gif');
1121
1122 $expectedItems = [
1123 0 => [
1124 0 => 'anImage.gif',
1125 1 => 'anImage.gif',
1126 2 => '../' . $directory . 'anImage.gif',
1127 3 => null,
1128 ],
1129 1 => [
1130 0 => 'subdir/anotherImage.gif',
1131 1 => 'subdir/anotherImage.gif',
1132 2 => '../' . $directory . 'subdir/anotherImage.gif',
1133 3 => null,
1134 ],
1135 ];
1136
1137 $result = $this->subject->addData($input);
1138
1139 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1140 }
1141
1142 /**
1143 * Data provider
1144 */
1145 public function addDataReplacesMarkersInForeignTableClauseDataProvider()
1146 {
1147 return [
1148 'replace REC_FIELD' => [
1149 'AND fTable.title=\'###REC_FIELD_rowField###\'',
1150 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldValue\'',
1151 [],
1152 ],
1153 'replace REC_FIELD fullQuote' => [
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 fullQuoteWithArray' => [
1159 'AND fTable.title=###REC_FIELD_rowFieldThree###',
1160 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldThreeValue\'',
1161 [
1162 'databaseRow' => [
1163 'rowFieldThree' => [
1164 0 => 'rowFieldThreeValue'
1165 ]
1166 ],
1167 ],
1168 ],
1169 'replace REC_FIELD multiple markers' => [
1170 'AND fTable.title=\'###REC_FIELD_rowField###\' AND fTable.pid=###REC_FIELD_rowFieldTwo###',
1171 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldValue\' AND fTable.pid=\'rowFieldTwoValue\'',
1172 [],
1173 ],
1174 'replace CURRENT_PID' => [
1175 'AND fTable.uid=###CURRENT_PID###',
1176 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=43',
1177 [],
1178 ],
1179 'replace CURRENT_PID integer cast' => [
1180 'AND fTable.uid=###CURRENT_PID###',
1181 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=431',
1182 [
1183 'effectivePid' => '431string',
1184 ],
1185 ],
1186 'replace THIS_UID' => [
1187 'AND fTable.uid=###THIS_UID###',
1188 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=42',
1189 [],
1190 ],
1191 'replace THIS_UID integer cast' => [
1192 'AND fTable.uid=###THIS_UID###',
1193 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=421',
1194 [
1195 'databaseRow' => [
1196 'uid' => '421string',
1197 ],
1198 ],
1199 ],
1200 'replace SITEROOT' => [
1201 'AND fTable.uid=###SITEROOT###',
1202 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=44',
1203 [],
1204 ],
1205 'replace SITEROOT integer cast' => [
1206 'AND fTable.uid=###SITEROOT###',
1207 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=441',
1208 [
1209 'rootline' => [
1210 1 => [
1211 'uid' => '441string',
1212 ],
1213 ],
1214 ],
1215 ],
1216 'replace PAGE_TSCONFIG_ID' => [
1217 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1218 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=45',
1219 [],
1220 ],
1221 'replace PAGE_TSCONFIG_ID integer cast' => [
1222 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1223 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=451',
1224 [
1225 'pageTsConfig' => [
1226 'TCEFORM.' => [
1227 'aTable.' => [
1228 'aField.' => [
1229 'PAGE_TSCONFIG_ID' => '451string'
1230 ],
1231 ],
1232 ],
1233 ],
1234 ],
1235 ],
1236 'replace PAGE_TSCONFIG_STR' => [
1237 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1238 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=\'46\'',
1239 [],
1240 ],
1241 'replace PAGE_TSCONFIG_IDLIST' => [
1242 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1243 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid IN (47,48)',
1244 [],
1245 ],
1246 'replace PAGE_TSCONFIG_IDLIST cleans list' => [
1247 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1248 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid IN (471,481)',
1249 [
1250 'pageTsConfig' => [
1251 'TCEFORM.' => [
1252 'aTable.' => [
1253 'aField.' => [
1254 'PAGE_TSCONFIG_IDLIST' => 'a, 471, b, 481, c',
1255 ],
1256 ],
1257 ],
1258 ],
1259 ],
1260 ],
1261 ];
1262 }
1263
1264 /**
1265 * @test
1266 * @dataProvider addDataReplacesMarkersInForeignTableClauseDataProvider
1267 */
1268 public function addDataReplacesMarkersInForeignTableClause($foreignTableWhere, $expectedWhere, array $inputOverride)
1269 {
1270 $input = [
1271 'tableName' => 'aTable',
1272 'effectivePid' => 43,
1273 'databaseRow' => [
1274 'uid' => 42,
1275 'rowField' => 'rowFieldValue',
1276 'rowFieldTwo' => 'rowFieldTwoValue',
1277 ],
1278 'processedTca' => [
1279 'columns' => [
1280 'aField' => [
1281 'config' => [
1282 'type' => 'select',
1283 'renderType' => 'selectSingle',
1284 'foreign_table' => 'fTable',
1285 'foreign_table_where' => $foreignTableWhere,
1286 ],
1287 ],
1288 ]
1289 ],
1290 'rootline' => [
1291 2 => [
1292 'uid' => 999,
1293 'is_siteroot' => 0,
1294 ],
1295 1 => [
1296 'uid' => 44,
1297 'is_siteroot' => 1,
1298 ],
1299 0 => [
1300 'uid' => 0,
1301 'is_siteroot' => null,
1302 ],
1303 ],
1304 'pageTsConfig' => [
1305 'TCEFORM.' => [
1306 'aTable.' => [
1307 'aField.' => [
1308 'PAGE_TSCONFIG_ID' => 45,
1309 'PAGE_TSCONFIG_STR' => '46',
1310 'PAGE_TSCONFIG_IDLIST' => '47, 48',
1311 ],
1312 ],
1313 ],
1314 ],
1315 ];
1316 ArrayUtility::mergeRecursiveWithOverrule($input, $inputOverride);
1317
1318 $GLOBALS['TCA']['fTable'] = [];
1319
1320 $expectedQueryArray = [
1321 'SELECT' => 'fTable.uid',
1322 'FROM' => 'fTable, pages',
1323 'WHERE' => $expectedWhere,
1324 'GROUPBY' => '',
1325 'ORDERBY' => '',
1326 'LIMIT' => '',
1327 ];
1328
1329 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1330 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1331 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1332 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1333
1334 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1335 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1336 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1337 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1338 $databaseProphecy->quoteStr(Argument::cetera())->willReturnArgument(0);
1339 $databaseProphecy->fullQuoteStr(Argument::cetera())->will(function ($args) {
1340 return '\'' . $args[0] . '\'';
1341 });
1342 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn(false);
1343 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1344
1345 $databaseProphecy->exec_SELECT_queryArray($expectedQueryArray)->shouldBeCalled()->willReturn(false);
1346
1347 $this->subject->addData($input);
1348 }
1349
1350 /**
1351 * @test
1352 */
1353 public function addDataThrowsExceptionIfForeignTableIsNotDefinedInTca()
1354 {
1355 $input = [
1356 'tableName' => 'aTable',
1357 'processedTca' => [
1358 'columns' => [
1359 'aField' => [
1360 'config' => [
1361 'type' => 'select',
1362 'renderType' => 'selectSingle',
1363 'foreign_table' => 'fTable',
1364 ],
1365 ],
1366 ]
1367 ],
1368 ];
1369
1370 $this->setExpectedException(\UnexpectedValueException::class, $this->anything(), 1439569743);
1371
1372 $this->subject->addData($input);
1373 }
1374
1375 /**
1376 * @test
1377 */
1378 public function addDataForeignTableSplitsGroupOrderAndLimit()
1379 {
1380 $input = [
1381 'tableName' => 'aTable',
1382 'processedTca' => [
1383 'columns' => [
1384 'aField' => [
1385 'config' => [
1386 'type' => 'select',
1387 'renderType' => 'selectSingle',
1388 'foreign_table' => 'fTable',
1389 'foreign_table_where' => 'AND ftable.uid=1 GROUP BY groupField ORDER BY orderField LIMIT 1,2',
1390 ],
1391 ],
1392 ]
1393 ],
1394 'rootline' => [],
1395 ];
1396
1397 $GLOBALS['TCA']['fTable'] = [];
1398
1399 $expectedQueryArray = [
1400 'SELECT' => 'fTable.uid',
1401 'FROM' => 'fTable, pages',
1402 'WHERE' => 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND ftable.uid=1',
1403 'GROUPBY' => 'groupField',
1404 'ORDERBY' => 'orderField',
1405 'LIMIT' => '1,2',
1406 ];
1407
1408 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1409 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1410 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1411 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1412
1413 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1414 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1415 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1416 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1417 $databaseProphecy->quoteStr(Argument::cetera())->willReturnArgument(0);
1418 $databaseProphecy->fullQuoteStr(Argument::cetera())->will(function ($args) {
1419 return '\'' . $args[0] . '\'';
1420 });
1421 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn(false);
1422 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1423
1424 $databaseProphecy->exec_SELECT_queryArray($expectedQueryArray)->shouldBeCalled()->willReturn(false);
1425
1426 $this->subject->addData($input);
1427 }
1428
1429 /**
1430 * @test
1431 */
1432 public function addDataForeignTableQueuesFlashMessageOnDatabaseError()
1433 {
1434 $input = [
1435 'databaseRow' => [
1436 'aField' => 'aValue',
1437 ],
1438 'tableName' => 'aTable',
1439 'processedTca' => [
1440 'columns' => [
1441 'aField' => [
1442 'config' => [
1443 'type' => 'select',
1444 'renderType' => 'selectSingle',
1445 'foreign_table' => 'fTable',
1446 'items' => [
1447 0 => [
1448 0 => 'itemLabel',
1449 1 => 'itemValue',
1450 2 => null,
1451 3 => null,
1452 ],
1453 ],
1454 'maxitems' => 1,
1455 ],
1456 ],
1457 ]
1458 ],
1459 'rootline' => [],
1460 ];
1461
1462 $GLOBALS['TCA']['fTable'] = [];
1463
1464 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1465 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1466 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1467 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1468
1469 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1470 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1471 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1472 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1473
1474 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1475 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1476 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1477 $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(false);
1478
1479 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn('anError');
1480 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1481
1482 /** @var FlashMessage|ObjectProphecy $flashMessage */
1483 $flashMessage = $this->prophesize(FlashMessage::class);
1484 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
1485 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
1486 $flashMessageService = $this->prophesize(FlashMessageService::class);
1487 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
1488 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
1489 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
1490 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
1491
1492 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
1493
1494 $expected = $input;
1495 $expected['databaseRow']['aField'] = ['aValue'];
1496
1497 $this->assertEquals($expected, $this->subject->addData($input));
1498 }
1499
1500 /**
1501 * @test
1502 */
1503 public function addDataForeignTableHandlesForegnTableRows()
1504 {
1505 $input = [
1506 'databaseRow' => [
1507 'aField' => 'aValue',
1508 ],
1509 'tableName' => 'aTable',
1510 'processedTca' => [
1511 'columns' => [
1512 'aField' => [
1513 'config' => [
1514 'type' => 'select',
1515 'renderType' => 'selectSingle',
1516 'foreign_table' => 'fTable',
1517 'foreign_table_prefix' => 'aPrefix',
1518 'items' => [],
1519 'maxitems' => 1,
1520 ],
1521 ],
1522 ]
1523 ],
1524 'rootline' => [],
1525 ];
1526
1527 $GLOBALS['TCA']['fTable'] = [];
1528
1529 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1530 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1531 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1532 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1533
1534 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1535 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1536 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1537 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1538
1539 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1540 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1541 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1542 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1543 $databaseProphecy->sql_free_result(Argument::cetera())->willReturn(null);
1544 $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(true);
1545
1546 $counter = 0;
1547 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->will(function ($args) use (&$counter) {
1548 $counter++;
1549 if ($counter >= 3) {
1550 return false;
1551 }
1552 return [
1553 'uid' => $counter,
1554 'aValue' => 'bar,',
1555 ];
1556 });
1557
1558 $expected = $input;
1559 $expected['processedTca']['columns']['aField']['config']['items'] = [
1560 0 => [
1561 0 => 'aPrefix[LLL:EXT:lang/locallang_core.xlf:labels.no_title]',
1562 1 => 1,
1563 2 => 'default-not-found',
1564 3 => null,
1565 ],
1566 1 => [
1567 0 => 'aPrefix[LLL:EXT:lang/locallang_core.xlf:labels.no_title]',
1568 1 => 2,
1569 2 => 'default-not-found',
1570 3 => null,
1571 ],
1572 ];
1573
1574 $expected['databaseRow']['aField'] = ['aValue'];
1575
1576 $this->assertEquals($expected, $this->subject->addData($input));
1577 }
1578
1579 /**
1580 * @test
1581 */
1582 public function addDataRemovesItemsByKeepItemsPageTsConfig()
1583 {
1584 $input = [
1585 'databaseRow' => [
1586 'aField' => 'aValue',
1587 ],
1588 'tableName' => 'aTable',
1589 'processedTca' => [
1590 'columns' => [
1591 'aField' => [
1592 'config' => [
1593 'type' => 'select',
1594 'renderType' => 'selectSingle',
1595 'items' => [
1596 0 => [
1597 0 => 'keepMe',
1598 1 => 'keep',
1599 null,
1600 null,
1601 ],
1602 1 => [
1603 0 => 'removeMe',
1604 1 => 'remove',
1605 ],
1606 ],
1607 'maxitems' => 1,
1608 ],
1609 ],
1610 ]
1611 ],
1612 'pageTsConfig' => [
1613 'TCEFORM.' => [
1614 'aTable.' => [
1615 'aField.' => [
1616 'keepItems' => 'keep',
1617 ],
1618 ],
1619 ],
1620 ],
1621 ];
1622
1623 /** @var LanguageService|ObjectProphecy $languageService */
1624 $languageService = $this->prophesize(LanguageService::class);
1625 $GLOBALS['LANG'] = $languageService->reveal();
1626 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1627
1628 $expected = $input;
1629 $expected['databaseRow']['aField'] = ['aValue'];
1630 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
1631
1632 $this->assertEquals($expected, $this->subject->addData($input));
1633 }
1634
1635 /**
1636 * @test
1637 */
1638 public function addDataRemovesItemsByRemoveItemsPageTsConfig()
1639 {
1640 $input = [
1641 'databaseRow' => [
1642 'aField' => 'aValue'
1643 ],
1644 'tableName' => 'aTable',
1645 'processedTca' => [
1646 'columns' => [
1647 'aField' => [
1648 'config' => [
1649 'type' => 'select',
1650 'renderType' => 'selectSingle',
1651 'items' => [
1652 0 => [
1653 0 => 'keepMe',
1654 1 => 'keep',
1655 null,
1656 null,
1657 ],
1658 1 => [
1659 0 => 'removeMe',
1660 1 => 'remove',
1661 ],
1662 ],
1663 'maxitems' => 1,
1664 ],
1665 ],
1666 ]
1667 ],
1668 'pageTsConfig' => [
1669 'TCEFORM.' => [
1670 'aTable.' => [
1671 'aField.' => [
1672 'removeItems' => 'remove',
1673 ],
1674 ],
1675 ],
1676 ],
1677 ];
1678
1679 /** @var LanguageService|ObjectProphecy $languageService */
1680 $languageService = $this->prophesize(LanguageService::class);
1681 $GLOBALS['LANG'] = $languageService->reveal();
1682 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1683
1684 $expected = $input;
1685 $expected['databaseRow']['aField'] = ['aValue'];
1686 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
1687
1688 $this->assertEquals($expected, $this->subject->addData($input));
1689 }
1690
1691 /**
1692 * @test
1693 */
1694 public function addDataRemovesItemsByLanguageFieldUserRestriction()
1695 {
1696 $input = [
1697 'databaseRow' => [
1698 'aField' => 'aValue'
1699 ],
1700 'tableName' => 'aTable',
1701 'processedTca' => [
1702 'ctrl' => [
1703 'languageField' => 'aField',
1704 ],
1705 'columns' => [
1706 'aField' => [
1707 'config' => [
1708 'type' => 'select',
1709 'renderType' => 'selectSingle',
1710 'items' => [
1711 0 => [
1712 0 => 'keepMe',
1713 1 => 'keep',
1714 null,
1715 null,
1716 ],
1717 1 => [
1718 0 => 'removeMe',
1719 1 => 'remove',
1720 ],
1721 ],
1722 'maxitems' => 1,
1723 ],
1724 ],
1725 ]
1726 ],
1727 ];
1728
1729 /** @var LanguageService|ObjectProphecy $languageService */
1730 $languageService = $this->prophesize(LanguageService::class);
1731 $GLOBALS['LANG'] = $languageService->reveal();
1732 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1733
1734 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1735 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1736 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1737 $backendUserProphecy->checkLanguageAccess('keep')->shouldBeCalled()->willReturn(true);
1738 $backendUserProphecy->checkLanguageAccess('remove')->shouldBeCalled()->willReturn(false);
1739
1740 $expected = $input;
1741 $expected['databaseRow']['aField'] = ['aValue'];
1742 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
1743
1744 $this->assertEquals($expected, $this->subject->addData($input));
1745 }
1746
1747 /**
1748 * @test
1749 */
1750 public function addDataRemovesItemsByUserAuthModeRestriction()
1751 {
1752 $input = [
1753 'databaseRow' => [
1754 'aField' => 'aValue'
1755 ],
1756 'tableName' => 'aTable',
1757 'processedTca' => [
1758 'columns' => [
1759 'aField' => [
1760 'config' => [
1761 'type' => 'select',
1762 'renderType' => 'selectSingle',
1763 'authMode' => 'explicitAllow',
1764 'items' => [
1765 0 => [
1766 0 => 'keepMe',
1767 1 => 'keep',
1768 null,
1769 null,
1770 ],
1771 1 => [
1772 0 => 'removeMe',
1773 1 => 'remove',
1774 ],
1775 ],
1776 'maxitems' => 1,
1777 ],
1778 ],
1779 ]
1780 ],
1781 ];
1782
1783 /** @var LanguageService|ObjectProphecy $languageService */
1784 $languageService = $this->prophesize(LanguageService::class);
1785 $GLOBALS['LANG'] = $languageService->reveal();
1786 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1787
1788 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1789 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1790 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1791 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'keep', 'explicitAllow')->shouldBeCalled()->willReturn(true);
1792 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'remove', 'explicitAllow')->shouldBeCalled()->willReturn(false);
1793
1794 $expected = $input;
1795 $expected['databaseRow']['aField'] = ['aValue'];
1796 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
1797
1798 $this->assertEquals($expected, $this->subject->addData($input));
1799 }
1800
1801 /**
1802 * @test
1803 */
1804 public function addDataKeepsAllPagesDoktypesForAdminUser()
1805 {
1806 $input = [
1807 'databaseRow' => [
1808 'doktype' => 'keep'
1809 ],
1810 'tableName' => 'pages',
1811 'processedTca' => [
1812 'columns' => [
1813 'doktype' => [
1814 'config' => [
1815 'type' => 'select',
1816 'renderType' => 'selectSingle',
1817 'items' => [
1818 0 => [
1819 0 => 'keepMe',
1820 1 => 'keep',
1821 null,
1822 null,
1823 ],
1824 ],
1825 'maxitems' => 1,
1826 ],
1827 ],
1828 ],
1829 ],
1830 ];
1831
1832 /** @var LanguageService|ObjectProphecy $languageService */
1833 $languageService = $this->prophesize(LanguageService::class);
1834 $GLOBALS['LANG'] = $languageService->reveal();
1835 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1836
1837 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1838 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1839 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1840 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
1841
1842 $expected = $input;
1843 $expected['databaseRow']['doktype'] = ['keep'];
1844
1845 $this->assertEquals($expected, $this->subject->addData($input));
1846 }
1847
1848 /**
1849 * @test
1850 */
1851 public function addDataKeepsAllowedPageTypesForNonAdminUser()
1852 {
1853 $input = [
1854 'databaseRow' => [
1855 'doktype' => 'keep',
1856 ],
1857 'tableName' => 'pages',
1858 'processedTca' => [
1859 'columns' => [
1860 'doktype' => [
1861 'config' => [
1862 'type' => 'select',
1863 'renderType' => 'selectSingle',
1864 'items' => [
1865 0 => [
1866 0 => 'keepMe',
1867 1 => 'keep',
1868 null,
1869 null,
1870 ],
1871 1 => [
1872 0 => 'removeMe',
1873 1 => 'remove',
1874 ],
1875 ],
1876 'maxitems' => 1,
1877 ],
1878 ],
1879 ],
1880 ],
1881 ];
1882
1883 /** @var LanguageService|ObjectProphecy $languageService */
1884 $languageService = $this->prophesize(LanguageService::class);
1885 $GLOBALS['LANG'] = $languageService->reveal();
1886 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1887
1888 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1889 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1890 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1891 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
1892 $backendUserProphecy->groupData = [
1893 'pagetypes_select' => 'foo,keep,anotherAllowedDoktype',
1894 ];
1895
1896 $expected = $input;
1897 $expected['databaseRow']['doktype'] = ['keep'];
1898 unset($expected['processedTca']['columns']['doktype']['config']['items'][1]);
1899
1900 $this->assertEquals($expected, $this->subject->addData($input));
1901 }
1902
1903 /**
1904 * @test
1905 */
1906 public function addDataCallsItemsProcFunc()
1907 {
1908 $input = [
1909 'tableName' => 'aTable',
1910 'databaseRow' => [
1911 'aField' => 'aValue'
1912 ],
1913 'processedTca' => [
1914 'columns' => [
1915 'aField' => [
1916 'config' => [
1917 'type' => 'select',
1918 'renderType' => 'selectSingle',
1919 'items' => [],
1920 'itemsProcFunc' => function (array $parameters, $pObj) {
1921 $parameters['items'] = [
1922 0 => [
1923 0 => 'aLabel',
1924 1 => 'aValue',
1925 2 => null,
1926 3 => null,
1927 ],
1928 ];
1929 },
1930 ],
1931 ],
1932 ],
1933 ],
1934 ];
1935
1936 /** @var LanguageService|ObjectProphecy $languageService */
1937 $languageService = $this->prophesize(LanguageService::class);
1938 $GLOBALS['LANG'] = $languageService->reveal();
1939 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1940
1941 $expected = $input;
1942 $expected['databaseRow']['aField'] = ['aValue'];
1943 $expected['processedTca']['columns']['aField']['config'] = [
1944 'type' => 'select',
1945 'renderType' => 'selectSingle',
1946 'items' => [
1947 0 => [
1948 0 => 'aLabel',
1949 1 => 'aValue',
1950 2 => null,
1951 3 => null,
1952 ],
1953 ],
1954 'maxitems' => 1,
1955 ];
1956
1957 $this->assertSame($expected, $this->subject->addData($input));
1958 }
1959
1960 /**
1961 * @test
1962 */
1963 public function addDataItemsProcFuncReceivesParameters()
1964 {
1965 $input = [
1966 'tableName' => 'aTable',
1967 'databaseRow' => [
1968 'aField' => 'aValue',
1969 ],
1970 'pageTsConfig' => [
1971 'TCEFORM.' => [
1972 'aTable.' => [
1973 'aField.' => [
1974 'itemsProcFunc.' => [
1975 'itemParamKey' => 'itemParamValue',
1976 ],
1977 ]
1978 ],
1979 ],
1980 ],
1981 'processedTca' => [
1982 'columns' => [
1983 'aField' => [
1984 'config' => [
1985 'type' => 'select',
1986 'renderType' => 'selectSingle',
1987 'aKey' => 'aValue',
1988 'items' => [
1989 0 => [
1990 0 => 'aLabel',
1991 1 => 'aValue',
1992 ],
1993 ],
1994 'itemsProcFunc' => function (array $parameters, $pObj) {
1995 if ($parameters['items'] !== [ 0 => [ 'aLabel', 'aValue'] ]
1996 || $parameters['config']['aKey'] !== 'aValue'
1997 || $parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
1998 || $parameters['table'] !== 'aTable'
1999 || $parameters['row'] !== [ 'aField' => 'aValue' ]
2000 || $parameters['field'] !== 'aField'
2001 ) {
2002 throw new \UnexpectedValueException('broken', 1438604329);
2003 }
2004 },
2005 ],
2006 ],
2007 ],
2008 ],
2009 ];
2010
2011 $languageService = $this->prophesize(LanguageService::class);
2012 $GLOBALS['LANG'] = $languageService->reveal();
2013 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2014 /** @var FlashMessage|ObjectProphecy $flashMessage */
2015 $flashMessage = $this->prophesize(FlashMessage::class);
2016 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2017 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2018 $flashMessageService = $this->prophesize(FlashMessageService::class);
2019 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2020 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2021 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2022 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2023
2024 // itemsProcFunc must NOT have raised an exception
2025 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
2026
2027 $this->subject->addData($input);
2028 }
2029
2030 /**
2031 * @test
2032 */
2033 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
2034 {
2035 $input = [
2036 'tableName' => 'aTable',
2037 'databaseRow' => [
2038 'aField' => 'aValue',
2039 ],
2040 'pageTsConfig' => [
2041 'TCEFORM.' => [
2042 'aTable.' => [
2043 'aField.' => [
2044 'itemsProcFunc.' => [
2045 'itemParamKey' => 'itemParamValue',
2046 ],
2047 ]
2048 ],
2049 ],
2050 ],
2051 'processedTca' => [
2052 'columns' => [
2053 'aField' => [
2054 'config' => [
2055 'type' => 'select',
2056 'renderType' => 'selectSingle',
2057 'aKey' => 'aValue',
2058 'items' => [
2059 0 => [
2060 0 => 'aLabel',
2061 1 => 'aValue',
2062 ],
2063 ],
2064 'itemsProcFunc' => function (array $parameters, $pObj) {
2065 throw new \UnexpectedValueException('anException', 1438604329);
2066 },
2067 ],
2068 ],
2069 ],
2070 ],
2071 ];
2072
2073 $languageService = $this->prophesize(LanguageService::class);
2074 $GLOBALS['LANG'] = $languageService->reveal();
2075 /** @var FlashMessage|ObjectProphecy $flashMessage */
2076 $flashMessage = $this->prophesize(FlashMessage::class);
2077 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2078 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2079 $flashMessageService = $this->prophesize(FlashMessageService::class);
2080 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2081 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2082 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2083 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2084
2085 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
2086
2087 $this->subject->addData($input);
2088 }
2089
2090 /**
2091 * @test
2092 */
2093 public function addDataTranslatesItemLabelsFromPageTsConfig()
2094 {
2095 $input = [
2096 'databaseRow' => [
2097 'aField' => 'aValue',
2098 ],
2099 'tableName' => 'aTable',
2100 'processedTca' => [
2101 'columns' => [
2102 'aField' => [
2103 'config' => [
2104 'type' => 'select',
2105 'renderType' => 'selectSingle',
2106 'items' => [
2107 0 => [
2108 0 => 'aLabel',
2109 1 => 'aValue',
2110 null,
2111 null,
2112 ],
2113 ],
2114 'maxitems' => 1,
2115 ],
2116 ],
2117 ],
2118 ],
2119 'pageTsConfig' => [
2120 'TCEFORM.' => [
2121 'aTable.' => [
2122 'aField.' => [
2123 'altLabels.' => [
2124 'aValue' => 'labelOverride',
2125 ],
2126 ]
2127 ],
2128 ],
2129 ],
2130 ];
2131
2132 /** @var LanguageService|ObjectProphecy $languageService */
2133 $languageService = $this->prophesize(LanguageService::class);
2134 $GLOBALS['LANG'] = $languageService->reveal();
2135 $languageService->sL('aLabel')->willReturnArgument(0);
2136
2137 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
2138
2139 $expected = $input;
2140 $expected['databaseRow']['aField'] = ['aValue'];
2141 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
2142
2143 $this->assertSame($expected, $this->subject->addData($input));
2144 $this->subject->addData($input);
2145 }
2146
2147 /**
2148 * @test
2149 */
2150 public function processSelectFieldValueSetsMmForeignRelationValues()
2151 {
2152 $GLOBALS['TCA']['foreignTable'] = [];
2153
2154 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2155 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2156 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2157
2158 /** @var DatabaseConnection|ObjectProphecy $database */
2159 $database = $this->prophesize(DatabaseConnection::class);
2160 $GLOBALS['TYPO3_DB'] = $database->reveal();
2161
2162 $input = [
2163 'tableName' => 'aTable',
2164 'databaseRow' => [
2165 'uid' => 42,
2166 // Two connected rows
2167 'aField' => 2,
2168 ],
2169 'processedTca' => [
2170 'columns' => [
2171 'aField' => [
2172 'config' => [
2173 'type' => 'select',
2174 'renderType' => 'selectSingle',
2175 'maxitems' => 999,
2176 'foreign_table' => 'foreignTable',
2177 'MM' => 'aTable_foreignTable_mm',
2178 'items' => [],
2179 ],
2180 ],
2181 ],
2182 ],
2183 ];
2184 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2185 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2186 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2187 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2188
2189 $relationHandlerUids = [
2190 23,
2191 24
2192 ];
2193
2194 $relationHandlerProphecy->start(2, 'foreignTable', 'aTable_foreignTable_mm', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2195 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2196
2197 $expected = $input;
2198 $expected['databaseRow']['aField'] = $relationHandlerUids;
2199
2200 $this->assertEquals($expected, $this->subject->addData($input));
2201 }
2202
2203 /**
2204 * @test
2205 */
2206 public function processSelectFieldValueSetsForeignRelationValues()
2207 {
2208 $GLOBALS['TCA']['foreignTable'] = [];
2209
2210 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2211 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2212 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2213
2214 /** @var DatabaseConnection|ObjectProphecy $database */
2215 $database = $this->prophesize(DatabaseConnection::class);
2216 $GLOBALS['TYPO3_DB'] = $database->reveal();
2217
2218 $input = [
2219 'tableName' => 'aTable',
2220 'databaseRow' => [
2221 'uid' => 42,
2222 // Two connected rows
2223 'aField' => '22,23,24,25',
2224 ],
2225 'processedTca' => [
2226 'columns' => [
2227 'aField' => [
2228 'config' => [
2229 'type' => 'select',
2230 'renderType' => 'selectSingle',
2231 'maxitems' => 999,
2232 'foreign_table' => 'foreignTable',
2233 'items' => [],
2234 ],
2235 ],
2236 ],
2237 ],
2238 ];
2239 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2240 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2241 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2242 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2243
2244 $relationHandlerUids = [
2245 23,
2246 24
2247 ];
2248
2249 $relationHandlerProphecy->start('22,23,24,25', 'foreignTable', '', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2250 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2251
2252 $expected = $input;
2253 $expected['databaseRow']['aField'] = $relationHandlerUids;
2254
2255 $this->assertEquals($expected, $this->subject->addData($input));
2256 }
2257
2258 /**
2259 * @test
2260 */
2261 public function processSelectFieldValueRemovesInvalidDynamicValues()
2262 {
2263 $languageService = $this->prophesize(LanguageService::class);
2264 $GLOBALS['LANG'] = $languageService->reveal();
2265 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2266
2267 $GLOBALS['TCA']['foreignTable'] = [];
2268
2269 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2270 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2271 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2272
2273 /** @var DatabaseConnection|ObjectProphecy $database */
2274 $database = $this->prophesize(DatabaseConnection::class);
2275 $GLOBALS['TYPO3_DB'] = $database->reveal();
2276
2277 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2278 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2279 $relationHandlerProphecy->start(Argument::cetera())->shouldBeCalled();
2280 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldBeCalled()->willReturn([1]);
2281
2282 $input = [
2283 'tableName' => 'aTable',
2284 'databaseRow' => [
2285 'aField' => '1,2,bar,foo',
2286 ],
2287 'processedTca' => [
2288 'columns' => [
2289 'aField' => [
2290 'config' => [
2291 'type' => 'select',
2292 'renderType' => 'selectSingle',
2293 'foreign_table' => 'foreignTable',
2294 'maxitems' => 999,
2295 'items' => [
2296 ['foo', 'foo', null, null],
2297 ],
2298 ],
2299 ],
2300 ],
2301 ],
2302 ];
2303
2304 $expected = $input;
2305 $expected['databaseRow']['aField'] = ['foo', 1];
2306
2307 $this->assertEquals($expected, $this->subject->addData($input));
2308 }
2309
2310 /**
2311 * @test
2312 */
2313 public function processSelectFieldValueKeepsValuesFromStaticItems()
2314 {
2315 $languageService = $this->prophesize(LanguageService::class);
2316 $GLOBALS['LANG'] = $languageService->reveal();
2317 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2318
2319 $input = [
2320 'tableName' => 'aTable',
2321 'databaseRow' => [
2322 'aField' => 'foo,bar',
2323 ],
2324 'processedTca' => [
2325 'columns' => [
2326 'aField' => [
2327 'config' => [
2328 'type' => 'select',
2329 'renderType' => 'selectSingle',
2330 'maxitems' => 999,
2331 'items' => [
2332 ['foo', 'foo', null, null],
2333 ['bar', 'bar', null, null],
2334 ],
2335 ],
2336 ],
2337 ],
2338 ],
2339 ];
2340
2341 $expected = $input;
2342 $expected['databaseRow']['aField'] = [
2343 'foo',
2344 'bar'
2345 ];
2346
2347 $this->assertEquals($expected, $this->subject->addData($input));
2348 }
2349
2350 /**
2351 * @test
2352 */
2353 public function processSelectFieldValueReturnsEmptyValueForSingleSelect()
2354 {
2355 $languageService = $this->prophesize(LanguageService::class);
2356 $GLOBALS['LANG'] = $languageService->reveal();
2357 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2358
2359 $input = [
2360 'tableName' => 'aTable',
2361 'databaseRow' => [
2362 'aField' => '',
2363 ],
2364 'processedTca' => [
2365 'columns' => [
2366 'aField' => [
2367 'config' => [
2368 'type' => 'select',
2369 'renderType' => 'selectSingle',
2370 'maxitems' => 1,
2371 'items' => [],
2372 ],
2373 ],
2374 ],
2375 ],
2376 ];
2377
2378 $expected = $input;
2379 $expected['databaseRow']['aField'] = [
2380 '',
2381 ];
2382
2383 $this->assertEquals($expected, $this->subject->addData($input));
2384 }
2385
2386 /**
2387 * @test
2388 */
2389 public function processSelectFieldValueTrimsEmptyValueForMultiValueSelect()
2390 {
2391 $languageService = $this->prophesize(LanguageService::class);
2392 $GLOBALS['LANG'] = $languageService->reveal();
2393 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2394
2395 $input = [
2396 'tableName' => 'aTable',
2397 'databaseRow' => [
2398 'aField' => 'b,,c',
2399 ],
2400 'processedTca' => [
2401 'columns' => [
2402 'aField' => [
2403 'config' => [
2404 'type' => 'select',
2405 'renderType' => 'selectSingle',
2406 'maxitems' => 999,
2407 'items' => [
2408 ['a', '', null, null],
2409 ['b', 'b', null, null],
2410 ['c', 'c', null, null],
2411 ],
2412 ],
2413 ],
2414 ],
2415 ],
2416 ];
2417
2418 $expected = $input;
2419 $expected['databaseRow']['aField'] = [
2420 'b',
2421 'c',
2422 ];
2423
2424 $this->assertEquals($expected, $this->subject->addData($input));
2425 }
2426
2427 /**
2428 * @test
2429 */
2430 public function processSelectFieldValueDoesNotCallRelationManagerForStaticOnlyItems()
2431 {
2432 $languageService = $this->prophesize(LanguageService::class);
2433 $GLOBALS['LANG'] = $languageService->reveal();
2434 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2435
2436 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2437 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2438 $relationHandlerProphecy->start(Argument::cetera())->shouldNotBeCalled();
2439 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldNotBeCalled();
2440
2441 $input = [
2442 'tableName' => 'aTable',
2443 'databaseRow' => [
2444 'aField' => '1,2,bar,foo',
2445 ],
2446 'processedTca' => [
2447 'columns' => [
2448 'aField' => [
2449 'config' => [
2450 'type' => 'select',
2451 'renderType' => 'selectSingle',
2452 'maxitems' => 999,
2453 'items' => [
2454 ['foo', 'foo', null, null],
2455 ],
2456 ],
2457 ],
2458 ],
2459 ],
2460 ];
2461
2462 $expected = $input;
2463 $expected['databaseRow']['aField'] = ['foo'];
2464
2465 $this->assertEquals($expected, $this->subject->addData($input));
2466 }
2467
2468 /**
2469 * @test
2470 */
2471 public function processSelectFieldValueDoesNotTouchValueForSingleSelects()
2472 {
2473 $languageService = $this->prophesize(LanguageService::class);
2474 $GLOBALS['LANG'] = $languageService->reveal();
2475 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2476
2477 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2478 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2479 $relationHandlerProphecy->start(Argument::cetera())->shouldNotBeCalled();
2480 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldNotBeCalled();
2481
2482 $input = [
2483 'tableName' => 'aTable',
2484 'databaseRow' => [
2485 'aField' => '1,2,bar,foo',
2486 ],
2487 'processedTca' => [
2488 'columns' => [
2489 'aField' => [
2490 'config' => [
2491 'type' => 'select',
2492 'renderType' => 'selectSingle',
2493 'maxitems' => 1,
2494 'items' => [
2495 ['foo', 'foo', null, null],
2496 ],
2497 ],
2498 ],
2499 ],
2500 ],
2501 ];
2502
2503 $expected = $input;
2504 $expected['databaseRow']['aField'] = ['1,2,bar,foo'];
2505
2506 $this->assertEquals($expected, $this->subject->addData($input));
2507 }
2508
2509
2510 /**
2511 * Data Provider
2512 *
2513 * @return array
2514 */
2515 public function processSelectFieldSetsCorrectValuesForMmRelationsDataProvider()
2516 {
2517 return array(
2518 'Relation with MM table and new status with default values' => [
2519 [
2520 'tableName' => 'aTable',
2521 'command' => 'new',
2522 'databaseRow' => [
2523 'uid' => 'NEW1234',
2524 'aField' => '24,35',
2525 ],
2526 'processedTca' => [
2527 'columns' => [
2528 'aField' => [
2529 'config' => [
2530 'type' => 'select',
2531 'renderType' => 'selectSingle',
2532 'maxitems' => 999,
2533 'MM' => 'mm_aTable_foreignTable',
2534 'foreign_table' => 'foreignTable',
2535 'items' => [],
2536 ],
2537 ],
2538 ],
2539 ],
2540 ],
2541 [
2542 'MM' => ''
2543 ],
2544 [
2545 24, 35
2546 ]
2547 ],
2548 'Relation with MM table and item array in list but no new status' => [
2549 [
2550 'tableName' => 'aTable',
2551 'databaseRow' => [
2552 'uid' => 'NEW1234',
2553 'aField' => '24,35',
2554 ],
2555 'processedTca' => [
2556 'columns' => [
2557 'aField' => [
2558 'config' => [
2559 'type' => 'select',
2560 'renderType' => 'selectSingle',
2561 'maxitems' => 999,
2562 'MM' => 'mm_aTable_foreignTable',
2563 'foreign_table' => 'foreignTable',
2564 'items' => [],
2565 ],
2566 ],
2567 ],
2568 ],
2569 ],
2570 [],
2571 []
2572 ],
2573 'Relation with MM table and maxitems = 1 processes field value (item count)' => [
2574 [
2575 'tableName' => 'aTable',
2576 'databaseRow' => [
2577 'uid' => 42,
2578 // MM relation with one item has 1 in field value
2579 'aField' => 1,
2580 ],
2581 'processedTca' => [
2582 'columns' => [
2583 'aField' => [
2584 'config' => [
2585 'type' => 'select',
2586 'renderType' => 'selectSingle',
2587 'maxitems' => 1,
2588 'MM' => 'mm_aTable_foreignTable',
2589 'foreign_table' => 'foreignTable',
2590 'items' => [],
2591 ],
2592 ],
2593 ],
2594 ],
2595 ],
2596 [],
2597 [
2598 24
2599 ]
2600 ],
2601 'Relation with MM table and maxitems = 1 results in empty array if no items are set' => [
2602 [
2603 'tableName' => 'aTable',
2604 'databaseRow' => [
2605 'uid' => 58,
2606 // MM relation with no items has 0 in field value
2607 'aField' => 0,
2608 ],
2609 'processedTca' => [
2610 'columns' => [
2611 'aField' => [
2612 'config' => [
2613 'type' => 'select',
2614 'renderType' => 'selectSingle',
2615 'maxitems' => 1,
2616 'MM' => 'mm_aTable_foreignTable',
2617 'foreign_table' => 'foreignTable',
2618 'items' => [],
2619 ],
2620 ],
2621 ],
2622 ],
2623 ],
2624 [],
2625 []
2626 ]
2627 );
2628 }
2629
2630 /**
2631 * @test
2632 * @dataProvider processSelectFieldSetsCorrectValuesForMmRelationsDataProvider
2633 *
2634 * @param array $input
2635 * @param array $overrideRelationHandlerSettings
2636 * @param array $relationHandlerUids
2637 */
2638 public function processSelectFieldSetsCorrectValuesForMmRelations(array $input, array $overrideRelationHandlerSettings, array $relationHandlerUids)
2639 {
2640 $field = $input['databaseRow']['aField'];
2641 $foreignTable = isset($overrideRelationHandlerSettings['foreign_table']) ? $overrideRelationHandlerSettings['foreign_table'] : $input['processedTca']['columns']['aField']['config']['foreign_table'];
2642 $mmTable = isset($overrideRelationHandlerSettings['MM']) ? $overrideRelationHandlerSettings['MM'] : $input['processedTca']['columns']['aField']['config']['MM'];
2643 $uid = $input['databaseRow']['uid'];
2644 $tableName = $input['tableName'];
2645 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2646
2647 $GLOBALS['TCA'][$foreignTable] = [];
2648
2649 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2650 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2651 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2652
2653 /** @var DatabaseConnection|ObjectProphecy $database */
2654 $database = $this->prophesize(DatabaseConnection::class);
2655 $GLOBALS['TYPO3_DB'] = $database->reveal();
2656
2657 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2658 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2659 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2660
2661 $relationHandlerProphecy->start($field, $foreignTable, $mmTable, $uid, $tableName, $fieldConfig)->shouldBeCalled();
2662 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2663
2664 $expected = $input;
2665 $expected['databaseRow']['aField'] = $relationHandlerUids;
2666
2667 $this->assertEquals($expected, $this->subject->addData($input));
2668 }
2669 }