[BUGFIX] Show record title for inline element
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / TcaRecordTitleTest.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\TcaRecordTitle;
20 use TYPO3\CMS\Core\Tests\UnitTestCase;
21 use TYPO3\CMS\Lang\LanguageService;
22
23 /**
24 * Test case
25 */
26 class TcaRecordTitleTest extends UnitTestCase
27 {
28 /**
29 * @var TcaRecordTitle
30 */
31 protected $subject;
32
33 /**
34 * @var string
35 */
36 protected $timeZone;
37
38 public function setUp()
39 {
40 $this->subject = new TcaRecordTitle();
41 $this->timeZone = date_default_timezone_get();
42 date_default_timezone_set('UTC');
43 }
44
45 protected function tearDown()
46 {
47 date_default_timezone_set($this->timeZone);
48 }
49
50 /**
51 * @test
52 */
53 public function addDataThrowsExceptionWithMissingLabel()
54 {
55 $input = [
56 'tableName' => 'aTable',
57 'databaseRew' => [],
58 'processedTca' => [
59 'ctrl' => [],
60 ],
61 ];
62 $this->setExpectedException(\UnexpectedValueException::class, $this->any(), 1443706103);
63 $this->subject->addData($input);
64 }
65
66 /**
67 * @test
68 */
69 public function addDataReturnsRecordTitleForLabelUserFunction()
70 {
71 $input = [
72 'tableName' => 'aTable',
73 'databaseRow' => [],
74 'processedTca' => [
75 'ctrl' => [
76 'label' => 'uid',
77 'label_userFunc' => function (&$parameters) {
78 $parameters['title'] = 'Test';
79 }
80 ],
81 'columns' => [],
82 ],
83 ];
84
85 $expected = $input;
86 $expected['recordTitle'] = 'Test';
87
88 $this->assertSame($expected, $this->subject->addData($input));
89 }
90
91 /**
92 * @test
93 */
94 public function addDataReturnsRecordTitleForFormattedLabelUserFunction()
95 {
96 $input = [
97 'tableName' => 'aTable',
98 'databaseRow' => [],
99 'isInlineChild' => true,
100 'processedTca' => [
101 'ctrl' => [
102 'label' => 'uid',
103 'formattedLabel_userFunc' => function (&$parameters) {
104 $parameters['title'] = 'Test';
105 }
106 ],
107 'columns' => [],
108 ],
109 ];
110
111 $expected = $input;
112 $expected['recordTitle'] = 'Test';
113
114 $this->assertSame($expected, $this->subject->addData($input));
115 }
116
117 /**
118 * @test
119 */
120 public function addDataReturnsRecordTitleForInlineChildWithForeignLabel()
121 {
122 $input = [
123 'tableName' => 'aTable',
124 'databaseRow' => [
125 'aField' => 'aValue',
126 ],
127 'processedTca' => [
128 'ctrl' => [
129 'label' => 'foo',
130 'label_userFunc' => function (&$parameters) {
131 $parameters['title'] = 'Value that MUST NOT be used, otherwise the code is broken.';
132 }
133 ],
134 'columns' => [
135 'aField' => [
136 'config' => [
137 'type' => 'input',
138 ],
139 ],
140 ],
141 ],
142 'isInlineChild' => true,
143 'inlineParentConfig' => [
144 'foreign_label' => 'aField',
145 ],
146 ];
147 $expected = $input;
148 $expected['recordTitle'] = 'aValue';
149 $this->assertSame($expected, $this->subject->addData($input));
150 }
151
152 /**
153 * @test
154 */
155 public function addDataOverridesRecordTitleWithFormattedLabelUserFuncForInlineChildWithForeignLabel()
156 {
157 $input = [
158 'tableName' => 'aTable',
159 'databaseRow' => [
160 'aField' => 'aValue',
161 ],
162 'processedTca' => [
163 'ctrl' => [
164 'label' => 'foo',
165 'formattedLabel_userFunc' => function (&$parameters) {
166 $parameters['title'] = 'aFormattedLabel';
167 },
168 ],
169 'columns' => [
170 'aField' => [
171 'config' => [
172 'type' => 'input',
173 ],
174 ],
175 ],
176 ],
177 'isInlineChild' => true,
178 'inlineParentConfig' => [
179 'foreign_label' => 'aField',
180 ],
181 ];
182 $expected = $input;
183 $expected['recordTitle'] = 'aFormattedLabel';
184 $this->assertSame($expected, $this->subject->addData($input));
185 }
186
187 /**
188 * @test
189 */
190 public function addDataReturnsRecordTitleForInlineChildWithSymmetricLabel()
191 {
192 $input = [
193 'tableName' => 'aTable',
194 'databaseRow' => [
195 'aField' => 'aValue',
196 ],
197 'processedTca' => [
198 'ctrl' => [
199 'label' => 'foo',
200 ],
201 'columns' => [
202 'aField' => [
203 'config' => [
204 'type' => 'input',
205 ],
206 ],
207 ],
208 ],
209 'isInlineChild' => true,
210 'inlineParentConfig' => [
211 'symmetric_label' => 'aField',
212 ],
213 'isOnSymmetricSide' => true,
214 ];
215 $expected = $input;
216 $expected['recordTitle'] = 'aValue';
217 $this->assertSame($expected, $this->subject->addData($input));
218 }
219
220 /**
221 * @test
222 */
223 public function addDataReturnsRecordTitleForUid()
224 {
225 $input = [
226 'tableName' => 'aTable',
227 'databaseRow' => [
228 'uid' => 'NEW56017ee37d10e587251374',
229 ],
230 'processedTca' => [
231 'ctrl' => [
232 'label' => 'uid'
233 ],
234 'columns' => [],
235 ]
236 ];
237
238 /** @var LanguageService|ObjectProphecy $languageService */
239 $languageService = $this->prophesize(LanguageService::class);
240 $GLOBALS['LANG'] = $languageService->reveal();
241 $languageService->sL(Argument::cetera())->willReturnArgument(0);
242
243 $expected = $input;
244 $expected['recordTitle'] = 'NEW56017ee37d10e587251374';
245 $this->assertSame($expected, $this->subject->addData($input));
246 }
247
248 /**
249 * Data provider for addDataReturnsRecordTitleForInputType
250 * Each data set is an array with the following elements:
251 * - TCA field ['config'] section
252 * - Database value for field
253 * - expected title to be generated
254 *
255 * @returns array
256 */
257 public function addDataReturnsRecordTitleForInputTypeDataProvider()
258 {
259 return [
260 'new record' => [
261 [
262 'type' => 'input',
263 ],
264 '',
265 '',
266 ],
267 'plain text input' => [
268 [
269 'type' => 'input',
270 ],
271 'aValue',
272 'aValue',
273 ],
274 'date input' => [
275 [
276 'type' => 'input',
277 'eval' => 'date'
278 ],
279 '978307261',
280 '01-01-01 (-7 days)',
281 ],
282 'date input (dbType: date)' => [
283 [
284 'type' => 'input',
285 'eval' => 'date',
286 'dbType' => 'date'
287 ],
288 '2001-01-01',
289 '01-01-01 (-7 days)',
290 ],
291 'date input (disableAgeDisplay: TRUE)' => [
292 [
293 'type' => 'input',
294 'eval' => 'date',
295 'disableAgeDisplay' => true
296 ],
297 '978307261',
298 '01-01-01',
299 ],
300 'time input' => [
301 [
302 'type' => 'input',
303 'eval' => 'time',
304 ],
305 '44100',
306 '12:15',
307 ],
308 'timesec input' => [
309 [
310 'type' => 'input',
311 'eval' => 'timesec',
312 ],
313 '44130',
314 '12:15:30',
315 ],
316 'datetime input' => [
317 [
318 'type' => 'input',
319 'eval' => 'datetime',
320 'dbType' => 'date'
321 ],
322 '978307261',
323 '01-01-01 00:01',
324 ],
325 'datetime input (dbType: datetime)' => [
326 [
327 'type' => 'input',
328 'eval' => 'datetime',
329 'dbType' => 'datetime'
330 ],
331 '2014-12-31 23:59:59',
332 '31-12-14 23:59',
333 ],
334 ];
335 }
336
337 /**
338 * @test
339 * @dataProvider addDataReturnsRecordTitleForInputTypeDataProvider
340 *
341 * @param array $fieldConfig
342 * @param string $fieldValue
343 * @param string $expectedTitle
344 */
345 public function addDataReturnsRecordTitleForInputType($fieldConfig, $fieldValue, $expectedTitle)
346 {
347 $input = [
348 'tableName' => 'aTable',
349 'databaseRow' => [
350 'uid' => '1',
351 'aField' => $fieldValue,
352 ],
353 'processedTca' => [
354 'ctrl' => [
355 'label' => 'aField'
356 ],
357 'columns' => [
358 'aField' => [
359 'config' => $fieldConfig,
360 ]
361 ],
362 ]
363 ];
364
365 /** @var LanguageService|ObjectProphecy $languageService */
366 $languageService = $this->prophesize(LanguageService::class);
367 $GLOBALS['LANG'] = $languageService->reveal();
368 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')
369 ->willReturn(' min| hrs| days| yrs| min| hour| day| year');
370 $languageService->sL(Argument::cetera())->willReturnArgument(0);
371 $GLOBALS['EXEC_TIME'] = 978912061;
372
373 $expected = $input;
374 $expected['recordTitle'] = $expectedTitle;
375 $this->assertSame($expected, $this->subject->addData($input));
376 }
377
378 /**
379 * @test
380 */
381 public function addDataReturnsRecordTitleWithAlternativeLabel()
382 {
383 $input = [
384 'tableName' => 'aTable',
385 'databaseRow' => [
386 'uid' => '1',
387 'aField' => '',
388 'anotherField' => 'anotherValue',
389 ],
390 'processedTca' => [
391 'ctrl' => [
392 'label' => 'aField',
393 'label_alt' => 'anotherField',
394 ],
395 'columns' => [
396 'aField' => [
397 'config' => [
398 'type' => 'input'
399 ]
400 ],
401 'anotherField' => [
402 'config' => [
403 'type' => 'input'
404 ]
405 ]
406 ],
407 ]
408 ];
409
410 $expected = $input;
411 $expected['recordTitle'] = 'anotherValue';
412 $this->assertSame($expected, $this->subject->addData($input));
413 }
414
415 /**
416 * @test
417 */
418 public function addDataReturnsRecordTitleWithMultipleAlternativeLabels()
419 {
420 $input = [
421 'tableName' => 'aTable',
422 'databaseRow' => [
423 'uid' => '1',
424 'aField' => '',
425 'anotherField' => '',
426 'additionalField' => 'additionalValue'
427 ],
428 'processedTca' => [
429 'ctrl' => [
430 'label' => 'aField',
431 'label_alt' => 'anotherField,additionalField',
432 ],
433 'columns' => [
434 'aField' => [
435 'config' => [
436 'type' => 'input'
437 ]
438 ],
439 'anotherField' => [
440 'config' => [
441 'type' => 'input'
442 ]
443 ],
444 'additionalField' => [
445 'config' => [
446 'type' => 'input'
447 ]
448 ],
449 ],
450 ]
451 ];
452
453 $expected = $input;
454 $expected['recordTitle'] = 'additionalValue';
455 $this->assertSame($expected, $this->subject->addData($input));
456 }
457
458 /**
459 * @test
460 */
461 public function addDataReturnsRecordTitleWithForcedAlternativeLabel()
462 {
463 $input = [
464 'tableName' => 'aTable',
465 'databaseRow' => [
466 'uid' => '1',
467 'aField' => 'aField',
468 'anotherField' => 'anotherField'
469 ],
470 'processedTca' => [
471 'ctrl' => [
472 'label' => 'aField',
473 'label_alt' => 'anotherField',
474 'label_alt_force' => true,
475 ],
476 'columns' => [
477 'aField' => [
478 'config' => [
479 'type' => 'input'
480 ]
481 ],
482 'anotherField' => [
483 'config' => [
484 'type' => 'input'
485 ]
486 ],
487 ],
488 ]
489 ];
490
491 $expected = $input;
492 $expected['recordTitle'] = 'aField, anotherField';
493 $this->assertSame($expected, $this->subject->addData($input));
494 }
495
496 /**
497 * @test
498 */
499 public function addDataReturnsRecordTitleWithMultipleForcedAlternativeLabels()
500 {
501 $input = [
502 'tableName' => 'aTable',
503 'databaseRow' => [
504 'uid' => '1',
505 'aField' => 'aField',
506 'anotherField' => 'anotherField',
507 'additionalField' => 'additionalValue'
508 ],
509 'processedTca' => [
510 'ctrl' => [
511 'label' => 'aField',
512 'label_alt' => 'anotherField,additionalField',
513 'label_alt_force' => true,
514 ],
515 'columns' => [
516 'aField' => [
517 'config' => [
518 'type' => 'input'
519 ]
520 ],
521 'anotherField' => [
522 'config' => [
523 'type' => 'input'
524 ]
525 ],
526 'additionalField' => [
527 'config' => [
528 'type' => 'input'
529 ]
530 ],
531 ],
532 ]
533 ];
534
535 $expected = $input;
536 $expected['recordTitle'] = 'aField, anotherField, additionalValue';
537 $this->assertSame($expected, $this->subject->addData($input));
538 }
539
540 /**
541 * @test
542 */
543 public function addDataReturnsRecordTitleIgnoresEmptyAlternativeLabels()
544 {
545 $input = [
546 'tableName' => 'aTable',
547 'databaseRow' => [
548 'uid' => '1',
549 'aField' => 'aField',
550 'anotherField' => '',
551 'additionalField' => 'additionalValue'
552 ],
553 'processedTca' => [
554 'ctrl' => [
555 'label' => 'aField',
556 'label_alt' => 'anotherField,additionalField',
557 'label_alt_force' => true,
558 ],
559 'columns' => [
560 'aField' => [
561 'config' => [
562 'type' => 'input'
563 ]
564 ],
565 'anotherField' => [
566 'config' => [
567 'type' => 'input'
568 ]
569 ],
570 'additionalField' => [
571 'config' => [
572 'type' => 'input'
573 ]
574 ],
575 ],
576 ]
577 ];
578
579 $expected = $input;
580 $expected['recordTitle'] = 'aField, additionalValue';
581 $this->assertSame($expected, $this->subject->addData($input));
582 }
583
584 /**
585 * @test
586 */
587 public function addDataReturnsRecordTitleForRadioType()
588 {
589 $input = [
590 'tableName' => 'aTable',
591 'databaseRow' => [
592 'uid' => '1',
593 'aField' => '2',
594 ],
595 'processedTca' => [
596 'ctrl' => [
597 'label' => 'aField'
598 ],
599 'columns' => [
600 'aField' => [
601 'config' => [
602 'type' => 'radio',
603 'items' => [
604 ['foo', 1],
605 ['bar', 2],
606 ['baz', 3],
607 ]
608 ]
609 ]
610 ],
611 ]
612 ];
613
614 $expected = $input;
615 $expected['recordTitle'] = 'bar';
616 $this->assertSame($expected, $this->subject->addData($input));
617 }
618
619 /**
620 * @test
621 */
622 public function addDataReturnsRecordTitleForInlineType()
623 {
624 $input = [
625 'tableName' => 'aTable',
626 'databaseRow' => [
627 'uid' => '1',
628 'aField' => '2',
629 ],
630 'processedTca' => [
631 'ctrl' => [
632 'label' => 'aField'
633 ],
634 'columns' => [
635 'aField' => [
636 'config' => [
637 'type' => 'inline'
638 ],
639 'children' => [
640 [
641 'recordTitle' => 'foo',
642 'vanillaUid' => 2
643 ]
644 ]
645 ]
646 ],
647 ]
648 ];
649
650 $expected = $input;
651 $expected['recordTitle'] = 'foo';
652 $this->assertSame($expected, $this->subject->addData($input));
653 }
654
655 /**
656 * Data provider for addDataReturnsRecordTitleForGroupType
657 * Each data set is an array with the following elements:
658 * - TCA field configuration (merged with base config)
659 * - Database value for field
660 * - expected title to be generated
661 *
662 * @returns array
663 */
664 public function addDataReturnsRecordTitleForGroupTypeDataProvider()
665 {
666 return [
667 'new record' => [
668 [
669 'internal_type' => 'db',
670 ],
671 '',
672 ''
673 ],
674 'internal_type: file' => [
675 [
676 'internal_type' => 'file',
677 ],
678 'somePath/aFile.jpg,someOtherPath/anotherFile.png',
679 'somePath/aFile.jpg, someOtherPath/anotherFile.png',
680 ],
681 'internal_type: db, single table, single record' => [
682 [
683 'internal_type' => 'db',
684 'allowed' => 'aTable'
685 ],
686 '1|aValue',
687 'aValue',
688 ],
689 'internal_type: db, single table, multiple records' => [
690 [
691 'internal_type' => 'db',
692 'allowed' => 'aTable'
693 ],
694 '1|aValue,3|anotherValue',
695 'aValue, anotherValue',
696 ],
697 'internal_type: db, multiple tables, single record' => [
698 [
699 'internal_type' => 'db',
700 'allowed' => 'aTable,anotherTable'
701 ],
702 'anotherTable_1|anotherValue',
703 'anotherValue',
704 ],
705 'internal_type: db, multiple tables, multiple records' => [
706 [
707 'internal_type' => 'db',
708 'allowed' => 'aTable,anotherTable'
709 ],
710 'anotherTable_1|anotherValue,aTable_1|aValue',
711 'aValue, anotherValue',
712 ],
713 ];
714 }
715
716 /**
717 * @test
718 * @dataProvider addDataReturnsRecordTitleForGroupTypeDataProvider
719 *
720 * @param array $fieldConfig
721 * @param string $fieldValue
722 * @param string $expectedTitle
723 */
724 public function addDataReturnsRecordTitleForGroupType($fieldConfig, $fieldValue, $expectedTitle)
725 {
726 $input = [
727 'tableName' => 'aTable',
728 'databaseRow' => [
729 'uid' => '1',
730 'aField' => $fieldValue,
731 ],
732 'processedTca' => [
733 'ctrl' => [
734 'label' => 'aField'
735 ],
736 'columns' => [
737 'aField' => [
738 'config' => array_merge(
739 [
740 'type' => 'group',
741 ],
742 $fieldConfig
743 ),
744 ]
745 ],
746 ]
747 ];
748
749 /** @var LanguageService|ObjectProphecy $languageService */
750 $languageService = $this->prophesize(LanguageService::class);
751 $GLOBALS['LANG'] = $languageService->reveal();
752 $languageService->sL(Argument::cetera())->willReturnArgument(0);
753
754 $expected = $input;
755 $expected['recordTitle'] = $expectedTitle;
756 $this->assertSame($expected, $this->subject->addData($input));
757 }
758
759 /**
760 * @test
761 */
762 public function addDataReturnsRecordTitleForGroupTypeWithInternalTypeDb()
763 {
764 $input = [
765 'tableName' => 'aTable',
766 'databaseRow' => [
767 'uid' => '1',
768 'aField' => 'aTable_1|aValue,anotherTable_2|anotherValue',
769 ],
770 'processedTca' => [
771 'ctrl' => [
772 'label' => 'aField'
773 ],
774 'columns' => [
775 'aField' => [
776 'config' => [
777 'type' => 'group',
778 'internal_type' => 'db',
779 'allowed' => 'aTable,anotherTable',
780 ]
781 ]
782 ],
783 ]
784 ];
785
786 $expected = $input;
787 $expected['recordTitle'] = 'aValue, anotherValue';
788 $this->assertSame($expected, $this->subject->addData($input));
789 }
790
791 /**
792 * @test
793 */
794 public function addDataReturnsRecordTitleForSingleCheckboxType()
795 {
796 $input = [
797 'tableName' => 'aTable',
798 'databaseRow' => [
799 'aField' => 1,
800 ],
801 'processedTca' => [
802 'ctrl' => [
803 'label' => 'aField'
804 ],
805 'columns' => [
806 'aField' => [
807 'config' => [
808 'type' => 'check',
809 ]
810 ]
811 ],
812 ]
813 ];
814
815 /** @var LanguageService|ObjectProphecy $languageService */
816 $languageService = $this->prophesize(LanguageService::class);
817 $GLOBALS['LANG'] = $languageService->reveal();
818 $languageService->sL(Argument::cetera())->willReturnArgument(0)->shouldBeCalled();
819
820 $expected = $input;
821 $expected['recordTitle'] = 'LLL:EXT:lang/locallang_common.xlf:yes';
822 $this->assertSame($expected, $this->subject->addData($input));
823 }
824
825 /**
826 * @test
827 */
828 public function addDataReturnsRecordTitleForArrayCheckboxType()
829 {
830 $input = [
831 'tableName' => 'aTable',
832 'databaseRow' => [
833 'aField' => '5'
834 ],
835 'processedTca' => [
836 'ctrl' => [
837 'label' => 'aField'
838 ],
839 'columns' => [
840 'aField' => [
841 'config' => [
842 'type' => 'check',
843 'items' => [
844 ['foo', ''],
845 ['bar', ''],
846 ['baz', ''],
847 ]
848 ]
849 ]
850 ],
851 ]
852 ];
853
854 $expected = $input;
855 $expected['recordTitle'] = 'foo, baz';
856 $this->assertSame($expected, $this->subject->addData($input));
857 }
858
859 /**
860 * @test
861 */
862 public function addDataReturnsEmptyRecordTitleForFlexType()
863 {
864 $input = [
865 'tableName' => 'aTable',
866 'databaseRow' => [
867 'aField' => [
868 'data' => [
869 'sDEF' => [
870 'lDEF' => [
871 'aFlexField' => [
872 'vDEF' => 'aFlexValue',
873 ]
874 ]
875 ]
876 ]
877 ]
878 ],
879 'processedTca' => [
880 'ctrl' => [
881 'label' => 'aField'
882 ],
883 'columns' => [
884 'aField' => [
885 'config' => [
886 'type' => 'flex',
887 'ds' => [
888 'sheets' => [
889 'sDEF' => [
890 'ROOT' => [
891 'type' => 'array',
892 'el' => [
893 'aFlexField' => [
894 'label' => 'Some input field',
895 'config' => [
896 'type' => 'input',
897 ],
898 ],
899 ],
900 ],
901 ],
902 ],
903 ]
904
905 ]
906 ]
907 ],
908 ]
909 ];
910
911 $expected = $input;
912 $expected['recordTitle'] = '';
913 $this->assertSame($expected, $this->subject->addData($input));
914 }
915
916 /**
917 * @test
918 */
919 public function addDataReturnsRecordTitleForSelectType()
920 {
921 $input = [
922 'tableName' => 'aTable',
923 'databaseRow' => [
924 'aField' => [
925 '1',
926 '2'
927 ]
928 ],
929 'processedTca' => [
930 'ctrl' => [
931 'label' => 'aField'
932 ],
933 'columns' => [
934 'aField' => [
935 'config' => [
936 'type' => 'select',
937 'items' => [
938 ['foo', 1, null, null],
939 ['bar', 2, null, null],
940 ['baz', 4, null, null],
941 ]
942 ]
943 ]
944 ],
945 ]
946 ];
947
948 $expected = $input;
949 $expected['recordTitle'] = 'foo, bar';
950 $this->assertSame($expected, $this->subject->addData($input));
951 }
952
953 /**
954 * @test
955 */
956 public function addDataReturnsStrippedAndTrimmedValueForTextType()
957 {
958 $input = [
959 'tableName' => 'aTable',
960 'databaseRow' => [
961 'aField' => '<p> text </p>',
962 ],
963 'processedTca' => [
964 'ctrl' => [
965 'label' => 'aField',
966 ],
967 'columns' => [
968 'aField' => [
969 'config' => [
970 'type' => 'text',
971 ],
972 ],
973 ],
974 ],
975 ];
976
977 $expected = $input;
978 $expected['recordTitle'] = 'text';
979 $this->assertSame($expected, $this->subject->addData($input));
980 }
981 }