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