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