[BUGFIX] Add check for valid directory name for TCA select fileFolder
[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 'anotherKey' => [
1011 0 => 'anotherTitle',
1012 1 => 'status-status-permission-denied',
1013 2 => 'aDescription',
1014 ],
1015 ],
1016 ]
1017 ];
1018
1019 $expectedItems = [
1020 0 => [
1021 0 => 'aHeader',
1022 1 => '--div--',
1023 null,
1024 null,
1025 ],
1026 1 => [
1027 0 => 'anItemTitle',
1028 1 => 'aKey:anItemKey',
1029 2 => 'empty-empty',
1030 3 => null,
1031 ],
1032 2 => [
1033 0 => 'anotherTitle',
1034 1 => 'aKey:anotherKey',
1035 2 => 'status-status-permission-denied',
1036 3 => [ 'description' => 'aDescription' ],
1037 ],
1038 ];
1039
1040 $result = $this->subject->addData($input);
1041
1042 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1043 }
1044
1045 /**
1046 * @test
1047 */
1048 public function addDataAddsGroupItemsWithSpecialModListGroup()
1049 {
1050 $input = [
1051 'tableName' => 'aTable',
1052 'databaseRow' => [],
1053 'processedTca' => [
1054 'columns' => [
1055 'aField' => [
1056 'config' => [
1057 'type' => 'select',
1058 'renderType' => 'selectSingle',
1059 'special' => 'modListGroup',
1060 ],
1061 ],
1062 ],
1063 ],
1064 ];
1065
1066 $GLOBALS['TBE_MODULES'] = [];
1067
1068 /** @var LanguageService|ObjectProphecy $languageService */
1069 $languageService = $this->prophesize(LanguageService::class);
1070 $GLOBALS['LANG'] = $languageService->reveal();
1071 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1072 $languageService->moduleLabels = [
1073 'tabs_images' => [
1074 'aModule_tab' => PATH_site . 'aModuleTabIcon.gif',
1075 ],
1076 'labels' => [
1077 'aModule_tablabel' => 'aModuleTabLabel',
1078 'aModule_tabdescr' => 'aModuleTabDescription',
1079 ],
1080 'tabs' => [
1081 'aModule_tab' => 'aModuleLabel',
1082 ]
1083 ];
1084
1085 /** @var ModuleLoader|ObjectProphecy $moduleLoaderProphecy */
1086 $moduleLoaderProphecy = $this->prophesize(ModuleLoader::class);
1087 GeneralUtility::addInstance(ModuleLoader::class, $moduleLoaderProphecy->reveal());
1088 $moduleLoaderProphecy->load([])->shouldBeCalled();
1089 $moduleLoaderProphecy->modListGroup = [
1090 'aModule',
1091 ];
1092
1093 $expectedItems = [
1094 0 => [
1095 0 => 'aModuleLabel',
1096 1 => 'aModule',
1097 2 => '../aModuleTabIcon.gif',
1098 3 => [
1099 'title' => 'aModuleTabLabel',
1100 'description' => 'aModuleTabDescription',
1101 ],
1102 ],
1103 ];
1104
1105 $result = $this->subject->addData($input);
1106
1107 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1108 }
1109
1110 /**
1111 * @test
1112 */
1113 public function addDataAddsFileItemsWithConfiguredFileFolder()
1114 {
1115 $directory = $this->getUniqueId('typo3temp/test-') . '/';
1116 $input = [
1117 'tableName' => 'aTable',
1118 'databaseRow' => [],
1119 'processedTca' => [
1120 'columns' => [
1121 'aField' => [
1122 'config' => [
1123 'type' => 'select',
1124 'renderType' => 'selectSingle',
1125 'fileFolder' => $directory,
1126 'fileFolder_extList' => 'gif',
1127 'fileFolder_recursions' => 1,
1128 ],
1129 ],
1130 ],
1131 ],
1132 ];
1133
1134 /** @var LanguageService|ObjectProphecy $languageService */
1135 $languageService = $this->prophesize(LanguageService::class);
1136 $GLOBALS['LANG'] = $languageService->reveal();
1137 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1138
1139 mkdir(PATH_site . $directory);
1140 $this->testFilesToDelete[] = PATH_site . $directory;
1141 touch(PATH_site . $directory . 'anImage.gif');
1142 touch(PATH_site . $directory . 'aFile.txt');
1143 mkdir(PATH_site . $directory . '/subdir');
1144 touch(PATH_site . $directory . '/subdir/anotherImage.gif');
1145
1146 $expectedItems = [
1147 0 => [
1148 0 => 'anImage.gif',
1149 1 => 'anImage.gif',
1150 2 => '../' . $directory . 'anImage.gif',
1151 3 => null,
1152 ],
1153 1 => [
1154 0 => 'subdir/anotherImage.gif',
1155 1 => 'subdir/anotherImage.gif',
1156 2 => '../' . $directory . 'subdir/anotherImage.gif',
1157 3 => null,
1158 ],
1159 ];
1160
1161 $result = $this->subject->addData($input);
1162
1163 $this->assertSame($expectedItems, $result['processedTca']['columns']['aField']['config']['items']);
1164 }
1165
1166 /**
1167 * @test
1168 */
1169 public function addDataThrowsExceptionForInvalidFileFolder()
1170 {
1171 $input = [
1172 'tableName' => 'aTable',
1173 'databaseRow' => [],
1174 'processedTca' => [
1175 'columns' => [
1176 'aField' => [
1177 'config' => [
1178 'type' => 'select',
1179 'renderType' => 'selectSingle',
1180 'fileFolder' => 'EXT:non_existing/Resources/Public/',
1181 ],
1182 ],
1183 ],
1184 ],
1185 ];
1186
1187 $this->setExpectedException(\RuntimeException::class, $this->anything(), 1479399227);
1188 $this->subject->addData($input);
1189 }
1190
1191 /**
1192 * @test
1193 */
1194 public function addDataAddsItemsByAddItemsFromPageTsConfig()
1195 {
1196 $input = [
1197 'databaseRow' => [
1198 'aField' => '',
1199 ],
1200 'tableName' => 'aTable',
1201 'processedTca' => [
1202 'columns' => [
1203 'aField' => [
1204 'config' => [
1205 'type' => 'select',
1206 'renderType' => 'selectSingle',
1207 'items' => [
1208 0 => [
1209 0 => 'keepMe',
1210 1 => 'keep',
1211 null,
1212 null,
1213 ],
1214 ],
1215 'maxitems' => 1,
1216 ],
1217 ],
1218 ]
1219 ],
1220 'pageTsConfig' => [
1221 'TCEFORM.' => [
1222 'aTable.' => [
1223 'aField.' => [
1224 'addItems.' => [
1225 '1' => 'addMe'
1226 ],
1227 ],
1228 ],
1229 ],
1230 ],
1231 ];
1232
1233 /** @var LanguageService|ObjectProphecy $languageService */
1234 $languageService = $this->prophesize(LanguageService::class);
1235 $GLOBALS['LANG'] = $languageService->reveal();
1236 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1237
1238 $expected = $input;
1239 $expected['databaseRow']['aField'] = [];
1240 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1241 0 => 'addMe',
1242 1 => '1',
1243 null,
1244 null,
1245 ];
1246
1247 $this->assertEquals($expected, $this->subject->addData($input));
1248 }
1249
1250 /**
1251 * @test
1252 */
1253 public function addDataAddsItemsByAddItemsWithDuplicateValuesFromPageTsConfig()
1254 {
1255 $input = [
1256 'databaseRow' => [
1257 'aField' => '',
1258 ],
1259 'tableName' => 'aTable',
1260 'processedTca' => [
1261 'columns' => [
1262 'aField' => [
1263 'config' => [
1264 'type' => 'select',
1265 'renderType' => 'selectSingle',
1266 'items' => [
1267 0 => [
1268 0 => 'keepMe',
1269 1 => 'keep',
1270 null,
1271 null,
1272 ],
1273 ],
1274 'maxitems' => 1,
1275 ],
1276 ],
1277 ]
1278 ],
1279 'pageTsConfig' => [
1280 'TCEFORM.' => [
1281 'aTable.' => [
1282 'aField.' => [
1283 'addItems.' => [
1284 'keep' => 'addMe'
1285 ],
1286 ],
1287 ],
1288 ],
1289 ],
1290 ];
1291
1292 /** @var LanguageService|ObjectProphecy $languageService */
1293 $languageService = $this->prophesize(LanguageService::class);
1294 $GLOBALS['LANG'] = $languageService->reveal();
1295 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1296
1297 $expected = $input;
1298 $expected['databaseRow']['aField'] = [];
1299 $expected['processedTca']['columns']['aField']['config']['items'][1] = [
1300 0 => 'addMe',
1301 1 => 'keep',
1302 null,
1303 null,
1304 ];
1305
1306 $this->assertEquals($expected, $this->subject->addData($input));
1307 }
1308
1309 /**
1310 * Data provider
1311 */
1312 public function addDataReplacesMarkersInForeignTableClauseDataProvider()
1313 {
1314 return [
1315 'replace REC_FIELD' => [
1316 'AND fTable.title=\'###REC_FIELD_rowField###\'',
1317 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldValue\'',
1318 [],
1319 ],
1320 'replace REC_FIELD within FlexForm' => [
1321 'AND fTable.title=###REC_FIELD_rowFieldFlexForm###',
1322 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldFlexFormValue\'',
1323 [
1324 'databaseRow' => [
1325 'rowFieldThree' => [
1326 0 => 'rowFieldThreeValue'
1327 ]
1328 ],
1329 'flexParentDatabaseRow' => [
1330 'rowFieldFlexForm' => [
1331 0 => 'rowFieldFlexFormValue'
1332 ]
1333 ],
1334 ],
1335 ],
1336 'replace REC_FIELD fullQuote' => [
1337 'AND fTable.title=###REC_FIELD_rowField###',
1338 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldValue\'',
1339 [],
1340 ],
1341 'replace REC_FIELD fullQuoteWithArray' => [
1342 'AND fTable.title=###REC_FIELD_rowFieldThree###',
1343 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldThreeValue\'',
1344 [
1345 'databaseRow' => [
1346 'rowFieldThree' => [
1347 0 => 'rowFieldThreeValue'
1348 ]
1349 ],
1350 ],
1351 ],
1352 'replace REC_FIELD multiple markers' => [
1353 'AND fTable.title=\'###REC_FIELD_rowField###\' AND fTable.pid=###REC_FIELD_rowFieldTwo###',
1354 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.title=\'rowFieldValue\' AND fTable.pid=\'rowFieldTwoValue\'',
1355 [],
1356 ],
1357 'replace CURRENT_PID' => [
1358 'AND fTable.uid=###CURRENT_PID###',
1359 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=43',
1360 [],
1361 ],
1362 'replace CURRENT_PID within FlexForm' => [
1363 'AND fTable.uid=###CURRENT_PID###',
1364 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=77',
1365 [
1366 'flexParentDatabaseRow' => [
1367 'pid' => '77',
1368 ],
1369 ],
1370 ],
1371 'replace CURRENT_PID integer cast' => [
1372 'AND fTable.uid=###CURRENT_PID###',
1373 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=431',
1374 [
1375 'effectivePid' => '431string',
1376 ],
1377 ],
1378 'replace THIS_UID' => [
1379 'AND fTable.uid=###THIS_UID###',
1380 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=42',
1381 [],
1382 ],
1383 'replace THIS_UID integer cast' => [
1384 'AND fTable.uid=###THIS_UID###',
1385 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=421',
1386 [
1387 'databaseRow' => [
1388 'uid' => '421string',
1389 ],
1390 ],
1391 ],
1392 'replace SITEROOT' => [
1393 'AND fTable.uid=###SITEROOT###',
1394 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=44',
1395 [],
1396 ],
1397 'replace SITEROOT integer cast' => [
1398 'AND fTable.uid=###SITEROOT###',
1399 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=441',
1400 [
1401 'rootline' => [
1402 1 => [
1403 'uid' => '441string',
1404 ],
1405 ],
1406 ],
1407 ],
1408 'replace PAGE_TSCONFIG_ID' => [
1409 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1410 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=45',
1411 [
1412 'pageTsConfig' => [
1413 'TCEFORM.' => [
1414 'aTable.' => [
1415 'aField.' => [
1416 'PAGE_TSCONFIG_ID' => '45',
1417 ],
1418 ],
1419 ],
1420 ],
1421 ],
1422 ],
1423 'replace PAGE_TSCONFIG_ID integer cast' => [
1424 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1425 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=451',
1426 [
1427 'pageTsConfig' => [
1428 'TCEFORM.' => [
1429 'aTable.' => [
1430 'aField.' => [
1431 'PAGE_TSCONFIG_ID' => '451string'
1432 ],
1433 ],
1434 ],
1435 ],
1436 ],
1437 ],
1438 'replace PAGE_TSCONFIG_STR' => [
1439 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1440 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=\'46\'',
1441 [
1442 'pageTsConfig' => [
1443 'TCEFORM.' => [
1444 'aTable.' => [
1445 'aField.' => [
1446 'PAGE_TSCONFIG_STR' => '46',
1447 ],
1448 ],
1449 ],
1450 ],
1451 ],
1452 ],
1453 'replace PAGE_TSCONFIG_IDLIST' => [
1454 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1455 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid IN (47,48)',
1456 [
1457 'pageTsConfig' => [
1458 'TCEFORM.' => [
1459 'aTable.' => [
1460 'aField.' => [
1461 'PAGE_TSCONFIG_IDLIST' => '47,48',
1462 ],
1463 ],
1464 ],
1465 ],
1466 ],
1467 ],
1468 'replace PAGE_TSCONFIG_IDLIST cleans list' => [
1469 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1470 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid IN (471,481)',
1471 [
1472 'pageTsConfig' => [
1473 'TCEFORM.' => [
1474 'aTable.' => [
1475 'aField.' => [
1476 'PAGE_TSCONFIG_IDLIST' => 'a, 471, b, 481, c',
1477 ],
1478 ],
1479 ],
1480 ],
1481 ],
1482 ],
1483 'deprecated flexHack PAGE_TSCONFIG_ID is substituted' => [
1484 'AND fTable.uid=###PAGE_TSCONFIG_ID###',
1485 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=123',
1486 [
1487 'pageTsConfig' => [
1488 'flexHack.' => [
1489 'PAGE_TSCONFIG_ID' => '123',
1490 ],
1491 ],
1492 ],
1493 ],
1494 'deprecated flexHack PAGE_TSCONFIG_IDLIST is substituted' => [
1495 'AND fTable.uid IN (###PAGE_TSCONFIG_IDLIST###)',
1496 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid IN (123,124)',
1497 [
1498 'pageTsConfig' => [
1499 'flexHack.' => [
1500 'PAGE_TSCONFIG_IDLIST' => '123,124',
1501 ],
1502 ],
1503 ],
1504 ],
1505 'deprecated flexHack PAGE_TSCONFIG_STR is substituted' => [
1506 'AND fTable.uid=\'###PAGE_TSCONFIG_STR###\'',
1507 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND fTable.uid=\'aString\'',
1508 [
1509 'pageTsConfig' => [
1510 'flexHack.' => [
1511 'PAGE_TSCONFIG_STR' => 'aString',
1512 ],
1513 ],
1514 ],
1515 ],
1516 ];
1517 }
1518
1519 /**
1520 * @test
1521 * @dataProvider addDataReplacesMarkersInForeignTableClauseDataProvider
1522 */
1523 public function addDataReplacesMarkersInForeignTableClause($foreignTableWhere, $expectedWhere, array $inputOverride)
1524 {
1525 $input = [
1526 'tableName' => 'aTable',
1527 'effectivePid' => 43,
1528 'databaseRow' => [
1529 'uid' => 42,
1530 'rowField' => 'rowFieldValue',
1531 'rowFieldTwo' => 'rowFieldTwoValue',
1532 ],
1533 'processedTca' => [
1534 'columns' => [
1535 'aField' => [
1536 'config' => [
1537 'type' => 'select',
1538 'renderType' => 'selectSingle',
1539 'foreign_table' => 'fTable',
1540 'foreign_table_where' => $foreignTableWhere,
1541 ],
1542 ],
1543 ]
1544 ],
1545 'rootline' => [
1546 2 => [
1547 'uid' => 999,
1548 'is_siteroot' => 0,
1549 ],
1550 1 => [
1551 'uid' => 44,
1552 'is_siteroot' => 1,
1553 ],
1554 0 => [
1555 'uid' => 0,
1556 'is_siteroot' => null,
1557 ],
1558 ],
1559 'pageTsConfig' => [],
1560 ];
1561 ArrayUtility::mergeRecursiveWithOverrule($input, $inputOverride);
1562
1563 $GLOBALS['TCA']['fTable'] = [];
1564
1565 $expectedQueryArray = [
1566 'SELECT' => 'fTable.uid',
1567 'FROM' => 'fTable, pages',
1568 'WHERE' => $expectedWhere,
1569 'GROUPBY' => '',
1570 'ORDERBY' => '',
1571 'LIMIT' => '',
1572 ];
1573
1574 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1575 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1576 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1577 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1578
1579 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1580 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1581 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1582 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1583 $databaseProphecy->quoteStr(Argument::cetera())->willReturnArgument(0);
1584 $databaseProphecy->fullQuoteStr(Argument::cetera())->will(function ($args) {
1585 return '\'' . $args[0] . '\'';
1586 });
1587 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn(false);
1588 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1589
1590 $databaseProphecy->exec_SELECT_queryArray($expectedQueryArray)->shouldBeCalled()->willReturn(false);
1591
1592 $this->subject->addData($input);
1593 }
1594
1595 /**
1596 * @test
1597 */
1598 public function addDataThrowsExceptionIfForeignTableIsNotDefinedInTca()
1599 {
1600 $input = [
1601 'tableName' => 'aTable',
1602 'processedTca' => [
1603 'columns' => [
1604 'aField' => [
1605 'config' => [
1606 'type' => 'select',
1607 'renderType' => 'selectSingle',
1608 'foreign_table' => 'fTable',
1609 ],
1610 ],
1611 ]
1612 ],
1613 ];
1614
1615 $this->setExpectedException(\UnexpectedValueException::class, $this->anything(), 1439569743);
1616
1617 $this->subject->addData($input);
1618 }
1619
1620 /**
1621 * @test
1622 */
1623 public function addDataForeignTableSplitsGroupOrderAndLimit()
1624 {
1625 $input = [
1626 'tableName' => 'aTable',
1627 'databaseRow' => [],
1628 'processedTca' => [
1629 'columns' => [
1630 'aField' => [
1631 'config' => [
1632 'type' => 'select',
1633 'renderType' => 'selectSingle',
1634 'foreign_table' => 'fTable',
1635 'foreign_table_where' => 'AND ftable.uid=1 GROUP BY groupField ORDER BY orderField LIMIT 1,2',
1636 ],
1637 ],
1638 ]
1639 ],
1640 'rootline' => [],
1641 ];
1642
1643 $GLOBALS['TCA']['fTable'] = [];
1644
1645 $expectedQueryArray = [
1646 'SELECT' => 'fTable.uid',
1647 'FROM' => 'fTable, pages',
1648 'WHERE' => 'pages.uid=fTable.pid AND pages.deleted=0 AND 1=1 AND ftable.uid=1',
1649 'GROUPBY' => 'groupField',
1650 'ORDERBY' => 'orderField',
1651 'LIMIT' => '1,2',
1652 ];
1653
1654 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1655 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1656 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1657 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1658
1659 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1660 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1661 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1662 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1663 $databaseProphecy->quoteStr(Argument::cetera())->willReturnArgument(0);
1664 $databaseProphecy->fullQuoteStr(Argument::cetera())->will(function ($args) {
1665 return '\'' . $args[0] . '\'';
1666 });
1667 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn(false);
1668 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1669
1670 $databaseProphecy->exec_SELECT_queryArray($expectedQueryArray)->shouldBeCalled()->willReturn(false);
1671
1672 $this->subject->addData($input);
1673 }
1674
1675 /**
1676 * @test
1677 */
1678 public function addDataForeignTableQueuesFlashMessageOnDatabaseError()
1679 {
1680 $input = [
1681 'databaseRow' => [
1682 'aField' => '',
1683 ],
1684 'tableName' => 'aTable',
1685 'processedTca' => [
1686 'columns' => [
1687 'aField' => [
1688 'config' => [
1689 'type' => 'select',
1690 'renderType' => 'selectSingle',
1691 'foreign_table' => 'fTable',
1692 'items' => [
1693 0 => [
1694 0 => 'itemLabel',
1695 1 => 'itemValue',
1696 2 => null,
1697 3 => null,
1698 ],
1699 ],
1700 'maxitems' => 1,
1701 ],
1702 ],
1703 ]
1704 ],
1705 'rootline' => [],
1706 ];
1707
1708 $GLOBALS['TCA']['fTable'] = [];
1709
1710 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1711 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1712 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1713 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1714
1715 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1716 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1717 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1718 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1719
1720 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1721 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1722 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1723 $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(false);
1724
1725 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn('anError');
1726 $databaseProphecy->sql_free_result(Argument::cetera())->shouldBeCalled()->willReturn(null);
1727
1728 /** @var FlashMessage|ObjectProphecy $flashMessage */
1729 $flashMessage = $this->prophesize(FlashMessage::class);
1730 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
1731 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
1732 $flashMessageService = $this->prophesize(FlashMessageService::class);
1733 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
1734 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
1735 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
1736 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
1737
1738 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
1739
1740 $expected = $input;
1741 $expected['databaseRow']['aField'] = [];
1742
1743 $this->assertEquals($expected, $this->subject->addData($input));
1744 }
1745
1746 /**
1747 * @test
1748 */
1749 public function addDataForeignTableHandlesForeignTableRows()
1750 {
1751 $input = [
1752 'databaseRow' => [
1753 'aField' => '',
1754 ],
1755 'tableName' => 'aTable',
1756 'processedTca' => [
1757 'columns' => [
1758 'aField' => [
1759 'config' => [
1760 'type' => 'select',
1761 'renderType' => 'selectSingle',
1762 'foreign_table' => 'fTable',
1763 'foreign_table_prefix' => 'aPrefix',
1764 'items' => [],
1765 'maxitems' => 1,
1766 ],
1767 ],
1768 ]
1769 ],
1770 'rootline' => [],
1771 ];
1772
1773 $GLOBALS['TCA']['fTable'] = [];
1774
1775 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1776 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1777 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1778 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1779
1780 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1781 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1782 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1783 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1784
1785 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1786 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1787 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1788 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1789 $databaseProphecy->sql_free_result(Argument::cetera())->willReturn(null);
1790 $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(true);
1791
1792 $counter = 0;
1793 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->will(function ($args) use (&$counter) {
1794 $counter++;
1795 if ($counter >= 3) {
1796 return false;
1797 }
1798 return [
1799 'uid' => $counter,
1800 'aValue' => 'bar,',
1801 ];
1802 });
1803
1804 $expected = $input;
1805 $expected['processedTca']['columns']['aField']['config']['items'] = [
1806 0 => [
1807 0 => 'aPrefix[LLL:EXT:lang/locallang_core.xlf:labels.no_title]',
1808 1 => 1,
1809 2 => 'default-not-found',
1810 3 => null,
1811 ],
1812 1 => [
1813 0 => 'aPrefix[LLL:EXT:lang/locallang_core.xlf:labels.no_title]',
1814 1 => 2,
1815 2 => 'default-not-found',
1816 3 => null,
1817 ],
1818 ];
1819
1820 $expected['databaseRow']['aField'] = [];
1821
1822 $this->assertEquals($expected, $this->subject->addData($input));
1823 }
1824
1825 /**
1826 * @test
1827 */
1828 public function addDataForeignTableResolvesIconFromSelicon()
1829 {
1830 $input = [
1831 'databaseRow' => [
1832 'aField' => '',
1833 ],
1834 'tableName' => 'aTable',
1835 'processedTca' => [
1836 'columns' => [
1837 'aField' => [
1838 'config' => [
1839 'type' => 'select',
1840 'renderType' => 'selectSingle',
1841 'foreign_table' => 'fTable',
1842 'maxitems' => 1,
1843 ],
1844 ],
1845 ]
1846 ],
1847 'rootline' => [],
1848 ];
1849
1850 // Fake the foreign_table
1851 $GLOBALS['TCA']['fTable'] = [
1852 'ctrl' => [
1853 'label' => 'icon',
1854 'selicon_field' => 'icon',
1855 'selicon_field_path' => 'uploads/media',
1856 ],
1857 'columns' =>[
1858 'icon' => [
1859 'config' => [
1860 'type' => 'group',
1861 ],
1862 ],
1863 ],
1864 ];
1865
1866 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
1867 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
1868 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
1869 $backendUserProphecy->getPagePermsClause(1)->shouldBeCalled()->willReturn(' 1=1');
1870
1871 /** @var LanguageService|ObjectProphecy $languageServiceProphecy */
1872 $languageServiceProphecy = $this->prophesize(LanguageService::class);
1873 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
1874 $languageServiceProphecy->sL(Argument::cetera())->willReturnArgument(0);
1875
1876 /** @var DatabaseConnection|ObjectProphecy $databaseProphecy */
1877 $databaseProphecy = $this->prophesize(DatabaseConnection::class);
1878 $GLOBALS['TYPO3_DB'] = $databaseProphecy->reveal();
1879 $databaseProphecy->sql_error()->shouldBeCalled()->willReturn(false);
1880 $databaseProphecy->sql_free_result(Argument::cetera())->willReturn(null);
1881 // Query on foreign table is successful
1882 $databaseProphecy->exec_SELECT_queryArray(Argument::cetera())->willReturn(true);
1883 // Query returns one row, then false on second call
1884 $foreignTableRowResultOne = [
1885 'uid' => 1,
1886 'icon' => 'foo.jpg',
1887 ];
1888 $databaseProphecy->sql_fetch_assoc(Argument::cetera())->shouldBeCalled()->willReturn($foreignTableRowResultOne, false);
1889
1890 $expected = $input;
1891 $expected['processedTca']['columns']['aField']['config']['items'] = [
1892 0 => [
1893 0 => 'foo.jpg',
1894 1 => 1,
1895 2 => '../uploads/media/foo.jpg', // combination of selicon_field_path and the row value of field 'icon'
1896 3 => null,
1897 ],
1898 ];
1899 $expected['databaseRow']['aField'] = [];
1900
1901 $this->assertEquals($expected, $this->subject->addData($input));
1902 }
1903
1904 /**
1905 * @test
1906 */
1907 public function addDataRemovesItemsByKeepItemsPageTsConfig()
1908 {
1909 $input = [
1910 'databaseRow' => [
1911 'aField' => '',
1912 ],
1913 'tableName' => 'aTable',
1914 'processedTca' => [
1915 'columns' => [
1916 'aField' => [
1917 'config' => [
1918 'type' => 'select',
1919 'renderType' => 'selectSingle',
1920 'items' => [
1921 0 => [
1922 0 => 'keepMe',
1923 1 => 'keep',
1924 null,
1925 null,
1926 ],
1927 1 => [
1928 0 => 'removeMe',
1929 1 => 'remove',
1930 ],
1931 ],
1932 'maxitems' => 1,
1933 ],
1934 ],
1935 ]
1936 ],
1937 'pageTsConfig' => [
1938 'TCEFORM.' => [
1939 'aTable.' => [
1940 'aField.' => [
1941 'keepItems' => 'keep',
1942 ],
1943 ],
1944 ],
1945 ],
1946 ];
1947
1948 /** @var LanguageService|ObjectProphecy $languageService */
1949 $languageService = $this->prophesize(LanguageService::class);
1950 $GLOBALS['LANG'] = $languageService->reveal();
1951 $languageService->sL(Argument::cetera())->willReturnArgument(0);
1952
1953 $expected = $input;
1954 $expected['databaseRow']['aField'] = [];
1955 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
1956
1957 $this->assertEquals($expected, $this->subject->addData($input));
1958 }
1959
1960 /**
1961 * @test
1962 */
1963 public function addDataRemovesAllItemsByEmptyKeepItemsPageTsConfig()
1964 {
1965 $input = [
1966 'databaseRow' => [
1967 'aField' => '',
1968 ],
1969 'tableName' => 'aTable',
1970 'processedTca' => [
1971 'columns' => [
1972 'aField' => [
1973 'config' => [
1974 'type' => 'select',
1975 'renderType' => 'selectSingle',
1976 'items' => [
1977 0 => [
1978 0 => 'keepMe',
1979 1 => 'keep',
1980 null,
1981 null,
1982 ],
1983 1 => [
1984 0 => 'removeMe',
1985 1 => 'remove',
1986 ],
1987 ],
1988 'maxitems' => 1,
1989 ],
1990 ],
1991 ]
1992 ],
1993 'pageTsConfig' => [
1994 'TCEFORM.' => [
1995 'aTable.' => [
1996 'aField.' => [
1997 'keepItems' => '',
1998 ],
1999 ],
2000 ],
2001 ],
2002 ];
2003
2004 /** @var LanguageService|ObjectProphecy $languageService */
2005 $languageService = $this->prophesize(LanguageService::class);
2006 $GLOBALS['LANG'] = $languageService->reveal();
2007 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2008
2009 $expected = $input;
2010 $expected['databaseRow']['aField'] = [];
2011 $expected['processedTca']['columns']['aField']['config']['items'] = [];
2012
2013 $this->assertEquals($expected, $this->subject->addData($input));
2014 }
2015
2016 /**
2017 * @test
2018 */
2019 public function addDataEvaluatesKeepItemsBeforeAddItemsFromPageTsConfig()
2020 {
2021 $input = [
2022 'databaseRow' => [
2023 'aField' => '',
2024 ],
2025 'tableName' => 'aTable',
2026 'processedTca' => [
2027 'columns' => [
2028 'aField' => [
2029 'config' => [
2030 'type' => 'select',
2031 'renderType' => 'selectSingle',
2032 'items' => [
2033 0 => [
2034 0 => 'keepMe',
2035 1 => '1',
2036 null,
2037 null,
2038 ],
2039 1 => [
2040 0 => 'removeMe',
2041 1 => 'remove',
2042 ],
2043 ],
2044 'maxitems' => 1,
2045 ],
2046 ],
2047 ]
2048 ],
2049 'pageTsConfig' => [
2050 'TCEFORM.' => [
2051 'aTable.' => [
2052 'aField.' => [
2053 'keepItems' => '1',
2054 'addItems.' => [
2055 '1' => 'addItem #1',
2056 '12' => 'addItem #12',
2057 ],
2058 ],
2059 ],
2060 ],
2061 ],
2062 ];
2063
2064 /** @var LanguageService|ObjectProphecy $languageService */
2065 $languageService = $this->prophesize(LanguageService::class);
2066 $GLOBALS['LANG'] = $languageService->reveal();
2067 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2068
2069 $expected = $input;
2070 $expected['databaseRow']['aField'] = [];
2071 $expected['processedTca']['columns']['aField']['config']['items'] = [
2072 0 => [
2073 0 => 'keepMe',
2074 1 => '1',
2075 null,
2076 null,
2077 ],
2078 1 => [
2079 0 => 'addItem #1',
2080 1 => '1',
2081 null,
2082 null,
2083 ],
2084 2 => [
2085 0 => 'addItem #12',
2086 1 => '12',
2087 null,
2088 null,
2089 ],
2090 ];
2091
2092 $this->assertEquals($expected, $this->subject->addData($input));
2093 }
2094
2095 /**
2096 * @test
2097 */
2098 public function addDataRemovesItemsByRemoveItemsPageTsConfig()
2099 {
2100 $input = [
2101 'databaseRow' => [
2102 'aField' => ''
2103 ],
2104 'tableName' => 'aTable',
2105 'processedTca' => [
2106 'columns' => [
2107 'aField' => [
2108 'config' => [
2109 'type' => 'select',
2110 'renderType' => 'selectSingle',
2111 'items' => [
2112 0 => [
2113 0 => 'keepMe',
2114 1 => 'keep',
2115 null,
2116 null,
2117 ],
2118 1 => [
2119 0 => 'removeMe',
2120 1 => 'remove',
2121 ],
2122 ],
2123 'maxitems' => 1,
2124 ],
2125 ],
2126 ]
2127 ],
2128 'pageTsConfig' => [
2129 'TCEFORM.' => [
2130 'aTable.' => [
2131 'aField.' => [
2132 'removeItems' => 'remove',
2133 ],
2134 ],
2135 ],
2136 ],
2137 ];
2138
2139 /** @var LanguageService|ObjectProphecy $languageService */
2140 $languageService = $this->prophesize(LanguageService::class);
2141 $GLOBALS['LANG'] = $languageService->reveal();
2142 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2143
2144 $expected = $input;
2145 $expected['databaseRow']['aField'] = [];
2146 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2147
2148 $this->assertEquals($expected, $this->subject->addData($input));
2149 }
2150
2151 /**
2152 * @test
2153 */
2154 public function addDataRemovesItemsAddedByAddItemsFromPageTsConfigByRemoveItemsPageTsConfig()
2155 {
2156 $input = [
2157 'databaseRow' => [
2158 'aField' => ''
2159 ],
2160 'tableName' => 'aTable',
2161 'processedTca' => [
2162 'columns' => [
2163 'aField' => [
2164 'config' => [
2165 'type' => 'select',
2166 'renderType' => 'selectSingle',
2167 'items' => [
2168 0 => [
2169 0 => 'keepMe',
2170 1 => 'keep',
2171 null,
2172 null,
2173 ],
2174 1 => [
2175 0 => 'removeMe',
2176 1 => 'remove',
2177 ],
2178 ],
2179 'maxitems' => 1,
2180 ],
2181 ],
2182 ]
2183 ],
2184 'pageTsConfig' => [
2185 'TCEFORM.' => [
2186 'aTable.' => [
2187 'aField.' => [
2188 'removeItems' => 'remove,add',
2189 'addItems.' => [
2190 'add' => 'addMe'
2191 ]
2192 ],
2193 ],
2194 ],
2195 ],
2196 ];
2197
2198 /** @var LanguageService|ObjectProphecy $languageService */
2199 $languageService = $this->prophesize(LanguageService::class);
2200 $GLOBALS['LANG'] = $languageService->reveal();
2201 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2202
2203 $expected = $input;
2204 $expected['databaseRow']['aField'] = [];
2205 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2206
2207 $this->assertEquals($expected, $this->subject->addData($input));
2208 }
2209
2210 /**
2211 * @test
2212 */
2213 public function addDataRemovesItemsByLanguageFieldUserRestriction()
2214 {
2215 $input = [
2216 'databaseRow' => [
2217 'aField' => 'aValue,remove'
2218 ],
2219 'tableName' => 'aTable',
2220 'processedTca' => [
2221 'ctrl' => [
2222 'languageField' => 'aField',
2223 ],
2224 'columns' => [
2225 'aField' => [
2226 'config' => [
2227 'type' => 'select',
2228 'renderType' => 'selectSingle',
2229 'items' => [
2230 0 => [
2231 0 => 'keepMe',
2232 1 => 'keep',
2233 null,
2234 null,
2235 ],
2236 1 => [
2237 0 => 'removeMe',
2238 1 => 'remove',
2239 ],
2240 ],
2241 'maxitems' => 1,
2242 ],
2243 ],
2244 ]
2245 ],
2246 ];
2247
2248 /** @var LanguageService|ObjectProphecy $languageService */
2249 $languageService = $this->prophesize(LanguageService::class);
2250 $GLOBALS['LANG'] = $languageService->reveal();
2251 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2252 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2253
2254 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2255 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2256 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2257 $backendUserProphecy->checkLanguageAccess('keep')->shouldBeCalled()->willReturn(true);
2258 $backendUserProphecy->checkLanguageAccess('remove')->shouldBeCalled()->willReturn(false);
2259
2260 $expected = $input;
2261 $expected['databaseRow']['aField'] = [];
2262 $expected['processedTca']['columns']['aField']['config']['items'] = [
2263 [ '[ INVALID VALUE "aValue" ]', 'aValue', null, null ],
2264 [ 'keepMe', 'keep', null, null ],
2265 ];
2266
2267 $this->assertEquals($expected, $this->subject->addData($input));
2268 }
2269
2270 /**
2271 * @test
2272 */
2273 public function addDataRemovesItemsByUserAuthModeRestriction()
2274 {
2275 $input = [
2276 'databaseRow' => [
2277 'aField' => 'keep,remove'
2278 ],
2279 'tableName' => 'aTable',
2280 'processedTca' => [
2281 'columns' => [
2282 'aField' => [
2283 'config' => [
2284 'type' => 'select',
2285 'renderType' => 'selectSingle',
2286 'authMode' => 'explicitAllow',
2287 'items' => [
2288 0 => [
2289 0 => 'keepMe',
2290 1 => 'keep',
2291 null,
2292 null,
2293 ],
2294 1 => [
2295 0 => 'removeMe',
2296 1 => 'remove',
2297 ],
2298 ],
2299 'maxitems' => 1,
2300 ],
2301 ],
2302 ]
2303 ],
2304 ];
2305
2306 /** @var LanguageService|ObjectProphecy $languageService */
2307 $languageService = $this->prophesize(LanguageService::class);
2308 $GLOBALS['LANG'] = $languageService->reveal();
2309 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2310
2311 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2312 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2313 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2314 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'keep', 'explicitAllow')->shouldBeCalled()->willReturn(true);
2315 $backendUserProphecy->checkAuthMode('aTable', 'aField', 'remove', 'explicitAllow')->shouldBeCalled()->willReturn(false);
2316
2317 $expected = $input;
2318 $expected['databaseRow']['aField'] = ['keep'];
2319 unset($expected['processedTca']['columns']['aField']['config']['items'][1]);
2320
2321 $this->assertEquals($expected, $this->subject->addData($input));
2322 }
2323
2324 /**
2325 * @test
2326 */
2327 public function addDataKeepsAllPagesDoktypesForAdminUser()
2328 {
2329 $input = [
2330 'databaseRow' => [
2331 'doktype' => 'keep'
2332 ],
2333 'tableName' => 'pages',
2334 'processedTca' => [
2335 'columns' => [
2336 'doktype' => [
2337 'config' => [
2338 'type' => 'select',
2339 'renderType' => 'selectSingle',
2340 'items' => [
2341 0 => [
2342 0 => 'keepMe',
2343 1 => 'keep',
2344 null,
2345 null,
2346 ],
2347 ],
2348 'maxitems' => 1,
2349 ],
2350 ],
2351 ],
2352 ],
2353 ];
2354
2355 /** @var LanguageService|ObjectProphecy $languageService */
2356 $languageService = $this->prophesize(LanguageService::class);
2357 $GLOBALS['LANG'] = $languageService->reveal();
2358 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2359
2360 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2361 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2362 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2363 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
2364
2365 $expected = $input;
2366 $expected['databaseRow']['doktype'] = ['keep'];
2367
2368 $this->assertEquals($expected, $this->subject->addData($input));
2369 }
2370
2371 /**
2372 * @test
2373 */
2374 public function addDataKeepsAllowedPageTypesForNonAdminUser()
2375 {
2376 $input = [
2377 'databaseRow' => [
2378 'doktype' => 'keep',
2379 ],
2380 'tableName' => 'pages',
2381 'processedTca' => [
2382 'columns' => [
2383 'doktype' => [
2384 'config' => [
2385 'type' => 'select',
2386 'renderType' => 'selectSingle',
2387 'items' => [
2388 0 => [
2389 0 => 'keepMe',
2390 1 => 'keep',
2391 null,
2392 null,
2393 ],
2394 1 => [
2395 0 => 'removeMe',
2396 1 => 'remove',
2397 ],
2398 ],
2399 'maxitems' => 1,
2400 ],
2401 ],
2402 ],
2403 ],
2404 ];
2405
2406 /** @var LanguageService|ObjectProphecy $languageService */
2407 $languageService = $this->prophesize(LanguageService::class);
2408 $GLOBALS['LANG'] = $languageService->reveal();
2409 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2410
2411 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2412 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2413 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2414 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
2415 $backendUserProphecy->groupData = [
2416 'pagetypes_select' => 'foo,keep,anotherAllowedDoktype',
2417 ];
2418
2419 $expected = $input;
2420 $expected['databaseRow']['doktype'] = ['keep'];
2421 unset($expected['processedTca']['columns']['doktype']['config']['items'][1]);
2422
2423 $this->assertEquals($expected, $this->subject->addData($input));
2424 }
2425
2426 /**
2427 * @test
2428 */
2429 public function addDataCallsItemsProcFunc()
2430 {
2431 $input = [
2432 'tableName' => 'aTable',
2433 'databaseRow' => [
2434 'aField' => 'aValue'
2435 ],
2436 'processedTca' => [
2437 'columns' => [
2438 'aField' => [
2439 'config' => [
2440 'type' => 'select',
2441 'renderType' => 'selectSingle',
2442 'items' => [],
2443 'itemsProcFunc' => function (array $parameters, $pObj) {
2444 $parameters['items'] = [
2445 0 => [
2446 0 => 'aLabel',
2447 1 => 'aValue',
2448 2 => null,
2449 3 => null,
2450 ],
2451 ];
2452 },
2453 ],
2454 ],
2455 ],
2456 ],
2457 ];
2458
2459 /** @var LanguageService|ObjectProphecy $languageService */
2460 $languageService = $this->prophesize(LanguageService::class);
2461 $GLOBALS['LANG'] = $languageService->reveal();
2462 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2463
2464 $expected = $input;
2465 $expected['databaseRow']['aField'] = ['aValue'];
2466 $expected['processedTca']['columns']['aField']['config'] = [
2467 'type' => 'select',
2468 'renderType' => 'selectSingle',
2469 'items' => [
2470 0 => [
2471 0 => 'aLabel',
2472 1 => 'aValue',
2473 2 => null,
2474 3 => null,
2475 ],
2476 ],
2477 'maxitems' => 1,
2478 ];
2479
2480 $this->assertSame($expected, $this->subject->addData($input));
2481 }
2482
2483 /**
2484 * @test
2485 */
2486 public function addDataItemsProcFuncReceivesParameters()
2487 {
2488 $input = [
2489 'tableName' => 'aTable',
2490 'databaseRow' => [
2491 'aField' => 'aValue',
2492 ],
2493 'pageTsConfig' => [
2494 'TCEFORM.' => [
2495 'aTable.' => [
2496 'aField.' => [
2497 'itemsProcFunc.' => [
2498 'itemParamKey' => 'itemParamValue',
2499 ],
2500 ]
2501 ],
2502 ],
2503 ],
2504 'processedTca' => [
2505 'columns' => [
2506 'aField' => [
2507 'config' => [
2508 'type' => 'select',
2509 'renderType' => 'selectSingle',
2510 'aKey' => 'aValue',
2511 'items' => [
2512 0 => [
2513 0 => 'aLabel',
2514 1 => 'aValue',
2515 ],
2516 ],
2517 'itemsProcFunc' => function (array $parameters, $pObj) {
2518 if ($parameters['items'] !== [ 0 => [ 'aLabel', 'aValue'] ]
2519 || $parameters['config']['aKey'] !== 'aValue'
2520 || $parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
2521 || $parameters['table'] !== 'aTable'
2522 || $parameters['row'] !== [ 'aField' => 'aValue' ]
2523 || $parameters['field'] !== 'aField'
2524 ) {
2525 throw new \UnexpectedValueException('broken', 1438604329);
2526 }
2527 },
2528 ],
2529 ],
2530 ],
2531 ],
2532 ];
2533
2534 $languageService = $this->prophesize(LanguageService::class);
2535 $GLOBALS['LANG'] = $languageService->reveal();
2536 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2537 /** @var FlashMessage|ObjectProphecy $flashMessage */
2538 $flashMessage = $this->prophesize(FlashMessage::class);
2539 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2540 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2541 $flashMessageService = $this->prophesize(FlashMessageService::class);
2542 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2543 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2544 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2545 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2546
2547 // itemsProcFunc must NOT have raised an exception
2548 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
2549
2550 $this->subject->addData($input);
2551 }
2552
2553 /**
2554 * @test
2555 */
2556 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
2557 {
2558 $input = [
2559 'tableName' => 'aTable',
2560 'databaseRow' => [
2561 'aField' => 'aValue',
2562 ],
2563 'pageTsConfig' => [
2564 'TCEFORM.' => [
2565 'aTable.' => [
2566 'aField.' => [
2567 'itemsProcFunc.' => [
2568 'itemParamKey' => 'itemParamValue',
2569 ],
2570 ]
2571 ],
2572 ],
2573 ],
2574 'processedTca' => [
2575 'columns' => [
2576 'aField' => [
2577 'config' => [
2578 'type' => 'select',
2579 'renderType' => 'selectSingle',
2580 'aKey' => 'aValue',
2581 'items' => [
2582 0 => [
2583 0 => 'aLabel',
2584 1 => 'aValue',
2585 ],
2586 ],
2587 'itemsProcFunc' => function (array $parameters, $pObj) {
2588 throw new \UnexpectedValueException('anException', 1438604329);
2589 },
2590 ],
2591 ],
2592 ],
2593 ],
2594 ];
2595
2596 $languageService = $this->prophesize(LanguageService::class);
2597 $GLOBALS['LANG'] = $languageService->reveal();
2598 /** @var FlashMessage|ObjectProphecy $flashMessage */
2599 $flashMessage = $this->prophesize(FlashMessage::class);
2600 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
2601 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
2602 $flashMessageService = $this->prophesize(FlashMessageService::class);
2603 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
2604 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
2605 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
2606 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
2607
2608 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
2609
2610 $this->subject->addData($input);
2611 }
2612
2613 /**
2614 * @test
2615 */
2616 public function addDataTranslatesItemLabelsFromPageTsConfig()
2617 {
2618 $input = [
2619 'databaseRow' => [
2620 'aField' => 'aValue',
2621 ],
2622 'tableName' => 'aTable',
2623 'processedTca' => [
2624 'columns' => [
2625 'aField' => [
2626 'config' => [
2627 'type' => 'select',
2628 'renderType' => 'selectSingle',
2629 'items' => [
2630 0 => [
2631 0 => 'aLabel',
2632 1 => 'aValue',
2633 null,
2634 null,
2635 ],
2636 ],
2637 'maxitems' => 1,
2638 ],
2639 ],
2640 ],
2641 ],
2642 'pageTsConfig' => [
2643 'TCEFORM.' => [
2644 'aTable.' => [
2645 'aField.' => [
2646 'altLabels.' => [
2647 'aValue' => 'labelOverride',
2648 ],
2649 ]
2650 ],
2651 ],
2652 ],
2653 ];
2654
2655 /** @var LanguageService|ObjectProphecy $languageService */
2656 $languageService = $this->prophesize(LanguageService::class);
2657 $GLOBALS['LANG'] = $languageService->reveal();
2658 $languageService->sL('aLabel')->willReturnArgument(0);
2659 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
2660 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2661
2662 $expected = $input;
2663 $expected['databaseRow']['aField'] = ['aValue'];
2664 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
2665
2666 $this->assertSame($expected, $this->subject->addData($input));
2667 $this->subject->addData($input);
2668 }
2669
2670 /**
2671 * @test
2672 */
2673 public function processSelectFieldValueSetsMmForeignRelationValues()
2674 {
2675 $GLOBALS['TCA']['foreignTable'] = [];
2676
2677 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2678 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2679 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2680
2681 /** @var DatabaseConnection|ObjectProphecy $database */
2682 $database = $this->prophesize(DatabaseConnection::class);
2683 $GLOBALS['TYPO3_DB'] = $database->reveal();
2684
2685 $input = [
2686 'tableName' => 'aTable',
2687 'databaseRow' => [
2688 'uid' => 42,
2689 // Two connected rows
2690 'aField' => 2,
2691 ],
2692 'processedTca' => [
2693 'columns' => [
2694 'aField' => [
2695 'config' => [
2696 'type' => 'select',
2697 'renderType' => 'selectSingle',
2698 'maxitems' => 999,
2699 'foreign_table' => 'foreignTable',
2700 'MM' => 'aTable_foreignTable_mm',
2701 'items' => [],
2702 ],
2703 ],
2704 ],
2705 ],
2706 ];
2707 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2708 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2709 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2710 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2711
2712 $relationHandlerUids = [
2713 23,
2714 24
2715 ];
2716
2717 $relationHandlerProphecy->start(2, 'foreignTable', 'aTable_foreignTable_mm', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2718 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2719
2720 $expected = $input;
2721 $expected['databaseRow']['aField'] = $relationHandlerUids;
2722
2723 $this->assertEquals($expected, $this->subject->addData($input));
2724 }
2725
2726 /**
2727 * @test
2728 */
2729 public function processSelectFieldValueSetsForeignRelationValues()
2730 {
2731 $GLOBALS['TCA']['foreignTable'] = [];
2732
2733 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2734 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2735 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2736
2737 /** @var DatabaseConnection|ObjectProphecy $database */
2738 $database = $this->prophesize(DatabaseConnection::class);
2739 $GLOBALS['TYPO3_DB'] = $database->reveal();
2740
2741 $input = [
2742 'tableName' => 'aTable',
2743 'databaseRow' => [
2744 'uid' => 42,
2745 // Two connected rows
2746 'aField' => '22,23,24,25',
2747 ],
2748 'processedTca' => [
2749 'columns' => [
2750 'aField' => [
2751 'config' => [
2752 'type' => 'select',
2753 'renderType' => 'selectSingle',
2754 'maxitems' => 999,
2755 'foreign_table' => 'foreignTable',
2756 'items' => [],
2757 ],
2758 ],
2759 ],
2760 ],
2761 ];
2762 $fieldConfig = $input['processedTca']['columns']['aField']['config'];
2763 /** @var RelationHandler|ObjectProphecy $relationHandlerProphecy */
2764 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2765 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2766
2767 $relationHandlerUids = [
2768 23,
2769 24
2770 ];
2771
2772 $relationHandlerProphecy->start('22,23,24,25', 'foreignTable', '', 42, 'aTable', $fieldConfig)->shouldBeCalled();
2773 $relationHandlerProphecy->getValueArray()->shouldBeCalled()->willReturn($relationHandlerUids);
2774
2775 $expected = $input;
2776 $expected['databaseRow']['aField'] = $relationHandlerUids;
2777
2778 $this->assertEquals($expected, $this->subject->addData($input));
2779 }
2780
2781 /**
2782 * @test
2783 */
2784 public function processSelectFieldValueRemovesInvalidDynamicValues()
2785 {
2786 $languageService = $this->prophesize(LanguageService::class);
2787 $GLOBALS['LANG'] = $languageService->reveal();
2788 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2789
2790 $GLOBALS['TCA']['foreignTable'] = [];
2791
2792 /** @var BackendUserAuthentication|ObjectProphecy $backendUserProphecy */
2793 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
2794 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
2795
2796 /** @var DatabaseConnection|ObjectProphecy $database */
2797 $database = $this->prophesize(DatabaseConnection::class);
2798 $GLOBALS['TYPO3_DB'] = $database->reveal();
2799
2800 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2801 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2802 $relationHandlerProphecy->start(Argument::cetera())->shouldBeCalled();
2803 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldBeCalled()->willReturn([1]);
2804
2805 $input = [
2806 'tableName' => 'aTable',
2807 'databaseRow' => [
2808 'aField' => '1,2,bar,foo',
2809 ],
2810 'processedTca' => [
2811 'columns' => [
2812 'aField' => [
2813 'config' => [
2814 'type' => 'select',
2815 'renderType' => 'selectSingleBox',
2816 'foreign_table' => 'foreignTable',
2817 'maxitems' => 999,
2818 'items' => [
2819 ['foo', 'foo', null, null],
2820 ],
2821 ],
2822 ],
2823 ],
2824 ],
2825 ];
2826
2827 $expected = $input;
2828 $expected['databaseRow']['aField'] = ['foo', 1];
2829
2830 $this->assertEquals($expected, $this->subject->addData($input));
2831 }
2832
2833 /**
2834 * @test
2835 */
2836 public function processSelectFieldValueKeepsValuesFromStaticItems()
2837 {
2838 $languageService = $this->prophesize(LanguageService::class);
2839 $GLOBALS['LANG'] = $languageService->reveal();
2840 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2841
2842 $input = [
2843 'tableName' => 'aTable',
2844 'databaseRow' => [
2845 'aField' => 'foo,bar',
2846 ],
2847 'processedTca' => [
2848 'columns' => [
2849 'aField' => [
2850 'config' => [
2851 'type' => 'select',
2852 'renderType' => 'selectSingle',
2853 'maxitems' => 999,
2854 'items' => [
2855 ['foo', 'foo', null, null],
2856 ['bar', 'bar', null, null],
2857 ],
2858 ],
2859 ],
2860 ],
2861 ],
2862 ];
2863
2864 $expected = $input;
2865 $expected['databaseRow']['aField'] = [
2866 'foo',
2867 'bar'
2868 ];
2869
2870 $this->assertEquals($expected, $this->subject->addData($input));
2871 }
2872
2873 /**
2874 * @test
2875 */
2876 public function processSelectFieldValueReturnsEmptyValueForSingleSelect()
2877 {
2878 $languageService = $this->prophesize(LanguageService::class);
2879 $GLOBALS['LANG'] = $languageService->reveal();
2880 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2881
2882 $input = [
2883 'tableName' => 'aTable',
2884 'databaseRow' => [
2885 'aField' => '',
2886 ],
2887 'processedTca' => [
2888 'columns' => [
2889 'aField' => [
2890 'config' => [
2891 'type' => 'select',
2892 'renderType' => 'selectSingle',
2893 'maxitems' => 1,
2894 'items' => [],
2895 ],
2896 ],
2897 ],
2898 ],
2899 ];
2900
2901 $expected = $input;
2902 $expected['databaseRow']['aField'] = [];
2903
2904 $this->assertEquals($expected, $this->subject->addData($input));
2905 }
2906
2907 /**
2908 * @test
2909 */
2910 public function processSelectFieldValueTrimsEmptyValueForMultiValueSelect()
2911 {
2912 $languageService = $this->prophesize(LanguageService::class);
2913 $GLOBALS['LANG'] = $languageService->reveal();
2914 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2915
2916 $input = [
2917 'tableName' => 'aTable',
2918 'databaseRow' => [
2919 'aField' => 'b,,c',
2920 ],
2921 'processedTca' => [
2922 'columns' => [
2923 'aField' => [
2924 'config' => [
2925 'type' => 'select',
2926 'renderType' => 'selectSingle',
2927 'maxitems' => 999,
2928 'items' => [
2929 ['a', '', null, null],
2930 ['b', 'b', null, null],
2931 ['c', 'c', null, null],
2932 ],
2933 ],
2934 ],
2935 ],
2936 ],
2937 ];
2938
2939 $expected = $input;
2940 $expected['databaseRow']['aField'] = [
2941 'b',
2942 'c',
2943 ];
2944
2945 $this->assertEquals($expected, $this->subject->addData($input));
2946 }
2947
2948 /**
2949 * @test
2950 */
2951 public function processSelectFieldValueDoesNotCallRelationManagerForStaticOnlyItems()
2952 {
2953 $languageService = $this->prophesize(LanguageService::class);
2954 $GLOBALS['LANG'] = $languageService->reveal();
2955 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2956
2957 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
2958 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
2959 $relationHandlerProphecy->start(Argument::cetera())->shouldNotBeCalled();
2960 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldNotBeCalled();
2961
2962 $input = [
2963 'tableName' => 'aTable',
2964 'databaseRow' => [
2965 'aField' => 'foo',
2966 ],
2967 'processedTca' => [
2968 'columns' => [
2969 'aField' => [
2970 'config' => [
2971 'type' => 'select',
2972 'renderType' => 'selectSingle',
2973 'maxitems' => 999,
2974 'items' => [
2975 ['foo', 'foo', null, null],
2976 ],
2977 ],
2978 ],
2979 ],
2980 ],
2981 ];
2982
2983 $expected = $input;
2984 $expected['databaseRow']['aField'] = ['foo'];
2985
2986 $this->assertEquals($expected, $this->subject->addData($input));
2987 }
2988
2989 /**
2990 * @test
2991 */
2992 public function processSelectFieldValueAddsInvalidValuesToItemsForSingleSelects()
2993 {
2994 $languageService = $this->prophesize(LanguageService::class);
2995 $GLOBALS['LANG'] = $languageService->reveal();
2996 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.noMatchingValue')->willReturn('INVALID VALUE "%s"');
2997 $languageService->sL(Argument::cetera())->willReturnArgument(0);
2998
2999 $relationHandlerProphecy = $this->prophesize(RelationHandler::class);
3000 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerProphecy->reveal());
3001 $relationHandlerProphecy->start(Argument::cetera())->shouldNotBeCalled();
3002 $relationHandlerProphecy->getValueArray(Argument::cetera())->shouldNotBeCalled();
3003
3004 $input = [
3005 'tableName' => 'aTable',
3006 'databaseRow' => [
3007 'aField' => '1,2,bar,foo',
3008 ],
3009 'processedTca' => [
3010 'columns' => [
3011 'aField' => [
3012 'config' => [
3013 'type' => 'select',
3014 'renderType' => 'selectSingle',
3015 'maxitems' => 1,
3016 'items' => [
3017 ['foo', 'foo', null, null],
3018 ],
3019 ],
3020 ],
3021 ],
3022 ],
3023 ];
3024
3025 $expected = $input;
3026 $expected['databaseRow']['aField'] = ['foo'];
3027 $expected['processedTca']['columns']['aField']['config']['items'] = [
3028 ['[ INVALID VALUE "bar" ]', 'bar', null, null],
3029 ['[ INVALID VALUE "2" ]', '2', null, null],
3030 ['[ INVALID VALUE "1" ]', '1', null, null],
3031 ['foo', 'foo', null, null],
3032 ];
3033 $this->assertEquals($expected, $this->subject->addData($input));
3034 }
3035
3036 /**
3037 * @test
3038 */
3039 public function processSelectFieldValueReturnsDuplicateValuesForMultipleSelect()
3040 {
3041 $languageService = $this->prophesize(LanguageService::class);
3042 $GLOBALS['LANG'] = $languageService->reveal();
3043 $languageService->sL(Argument::cetera())->willReturnArgument(0);
3044
3045 $input = [
3046 'tableName' => 'aTable',