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