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