3047ef5ed1606479c062cb401d2033be98346249
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / EvaluateDisplayConditionsTest.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\Prophecy\ObjectProphecy;
18 use TYPO3\CMS\Backend\Form\FormDataProvider\EvaluateDisplayConditions;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Tests\UnitTestCase;
21
22 /**
23 * Test case
24 */
25 class EvaluateDisplayConditionsTest extends UnitTestCase
26 {
27 /**
28 * @var EvaluateDisplayConditions
29 */
30 protected $subject;
31
32 protected function setUp()
33 {
34 $this->subject = new EvaluateDisplayConditions();
35 }
36
37 /**
38 * @test
39 */
40 public function addDataRemovesTcaColumnsHiddenByDisplayCondition()
41 {
42 $input = [
43 'databaseRow' => [
44 'aField' => 'aField',
45 'bField' => 'bField',
46 'cField' => 1,
47 ],
48 'recordTypeValue' => 'aType',
49 'processedTca' => [
50 'types' => [
51 'aType' => [
52 'showitem' => '--palette--;aPalette;2,bField,cField'
53 ],
54 ],
55 'palettes' => [
56 '2' => array(
57 'showitem' => 'aField',
58 ),
59 ],
60 'columns' => [
61 'aField' => [
62 'displayCond' => 'FIELD:cField:=:0',
63 'config' => [
64 'type' => 'input',
65 ]
66 ],
67 'bField' => [
68 'displayCond' => 'FIELD:cField:=:1',
69 'config' => [
70 'type' => 'input',
71 ]
72 ],
73 'cField' => [
74 'config' => [
75 'type' => 'input',
76 ]
77 ]
78 ]
79 ]
80 ];
81
82 $expected = $input;
83 unset($expected['processedTca']['columns']['aField']);
84
85 $this->assertSame($expected, $this->subject->addData($input));
86 }
87
88 /**
89 * @test
90 */
91 public function addDataRemovesFlexformSheetsHiddenByDisplayCondition()
92 {
93 $input = [
94 'databaseRow' => [
95 'aField' => [
96 'data' => [
97 'sGeneral' => [
98 'lDEF' => [
99 'mmType' => [
100 'vDEF' => [
101 0 => 'video',
102 ],
103 ],
104 'mmUseHTML5' => [
105 'vDEF' => '0',
106 ],
107 ],
108 ],
109 'sVideo' => [
110 'lDEF' => [],
111 ],
112 'sAudio' => [
113 'lDEF' => []
114 ],
115 ]
116 ]
117 ],
118 'processedTca' => [
119 'columns' => [
120 'aField' => [
121 'config' => [
122 'type' => 'flex',
123 'ds' => [
124 'meta' => [],
125 'sheets' => [
126 'sGeneral' => [
127 'ROOT' => [
128 'type' => 'array',
129 'el' => [
130 'mmType' => [
131 'config' => [
132 'type' => 'select',
133 'items' => [],
134 ],
135 ],
136 'mmUseHTML5' => [
137 'displayCond' => 'FIELD:mmType:!=:audio',
138 'config' => [
139 'type' => 'check',
140 'default' => '0',
141 'items' => [],
142 ],
143 ],
144 ],
145 'sheetTitle' => 'sGeneral',
146 ],
147 ],
148 'sVideo' => [
149 'ROOT' => [
150 'type' => 'array',
151 'el' => [],
152 'sheetTitle' => 'sVideo',
153 'displayCond' => 'FIELD:sGeneral.mmType:!=:audio',
154 ],
155 ],
156 'sAudio' => [
157 'ROOT' => [
158 'type' => 'array',
159 'el' => [],
160 'sheetTitle' => 'sAudio',
161 'displayCond' => 'FIELD:sGeneral.mmType:=:audio',
162 ],
163 ],
164 ],
165 ],
166 ],
167 ],
168 ],
169 ],
170 ];
171
172 $expected = $input;
173 unset($expected['processedTca']['columns']['aField']['config']['ds']['sheets']['sAudio']);
174 $this->assertSame($expected, $this->subject->addData($input));
175 }
176
177 /**
178 * @test
179 */
180 public function addDataRemovesFlexformFieldsHiddenByDisplayCondition()
181 {
182 $input = [
183 'databaseRow' => [
184 'aField' => [
185 'data' => [
186 'sGeneral' => [
187 'lDEF' => [
188 'mmType' => [
189 'vDEF' => [
190 0 => 'audio',
191 ],
192 ],
193 'mmUseHTML5' => [
194 'vDEF' => '0',
195 ],
196 ],
197 ],
198 ]
199 ]
200 ],
201 'processedTca' => [
202 'columns' => [
203 'aField' => [
204 'config' => [
205 'type' => 'flex',
206 'ds_pointerField' => 'list_type,CType',
207 'ds' => [
208 'meta' => [],
209 'sheets' => [
210 'sGeneral' => [
211 'ROOT' => [
212 'type' => 'array',
213 'el' => [
214 'mmType' => [
215 'config' => [
216 'type' => 'select',
217 'items' => [],
218 ],
219 ],
220 'mmUseHTML5' => [
221 'displayCond' => 'FIELD:mmType:!=:audio',
222 'config' => [
223 'type' => 'check',
224 'default' => '0',
225 'items' => [],
226 ],
227 ],
228 'mmUseCurl' => [
229 'displayCond' => 'FIELD:mmType:=:audio',
230 'config' => [
231 'type' => 'check',
232 'default' => '0',
233 'items' => [],
234 ],
235 ],
236 ],
237 'sheetTitle' => 'aTitle',
238 ],
239 ],
240 'secondSheet' => [
241 'ROOT' => [
242 'type' => 'array',
243 'el' => [
244 'foo' => [
245 'config' => [
246 'type' => 'select',
247 'items' => [],
248 ],
249 ],
250 ],
251 'sheetTitle' => 'bTitle',
252 ],
253 ],
254 ],
255 ],
256 ],
257 ],
258 ],
259 ],
260 ];
261
262 $expected = $input;
263 unset($expected['processedTca']['columns']['aField']['config']['ds']['sheets']['sGeneral']['ROOT']['el']['mmUseHTML5']);
264
265 $this->assertSame($expected, $this->subject->addData($input));
266 }
267
268 /**
269 * @test
270 */
271 public function matchHideForNonAdminsReturnsTrueIfBackendUserIsAdmin()
272 {
273 $input = [
274 'databaseRow' => [],
275 'processedTca' => [
276 'columns' => [
277 'aField' => [
278 'displayCond' => 'HIDE_FOR_NON_ADMINS',
279 'config' => [
280 'type' => 'input',
281 ]
282 ],
283 ]
284 ]
285 ];
286
287 /** @var BackendUserAuthentication|ObjectProphecy backendUserProphecy */
288 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
289 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
290 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
291
292 $expected = $input;
293
294 $this->assertSame($expected, $this->subject->addData($input));
295 }
296
297 /**
298 * @test
299 */
300 public function matchHideForNonAdminsReturnsFalseIfBackendUserIsNotAdmin()
301 {
302 $input = [
303 'databaseRow' => [],
304 'processedTca' => [
305 'columns' => [
306 'aField' => [
307 'displayCond' => 'HIDE_FOR_NON_ADMINS',
308 'config' => [
309 'type' => 'input',
310 ]
311 ],
312 ]
313 ]
314 ];
315
316 /** @var BackendUserAuthentication|ObjectProphecy backendUserProphecy */
317 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
318 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
319 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
320
321 $expected = $input;
322 unset($expected['processedTca']['columns']['aField']);
323 $this->assertSame($expected, $this->subject->addData($input));
324 }
325
326 /**
327 * Returns data sets for the test matchConditionStrings
328 * Each data set is an array with the following elements:
329 * - the condition string
330 * - the current record
331 * - the expected result
332 *
333 * @return array
334 */
335 public function conditionStringDataProvider()
336 {
337 return [
338 'Invalid condition string' => [
339 'xINVALIDx:',
340 [],
341 false,
342 ],
343 'Field is not greater zero if not given' => [
344 'FIELD:uid:>:0',
345 [],
346 false,
347 ],
348 'Field is not equal 0 if not given' => [
349 'FIELD:uid:=:0',
350 [],
351 false,
352 ],
353 'Field is not present if empty array given' => [
354 'REQ:foo:TRUE',
355 ['foo' => []],
356 false,
357 ],
358 'Field is not greater zero if empty array given' => [
359 'FIELD:foo:>:0',
360 ['foo' => []],
361 false,
362 ],
363 'Field is not greater than or equal to zero if empty array given' => [
364 'FIELD:foo:>=:0',
365 ['foo' => []],
366 false,
367 ],
368 'Field is less than 1 if empty array given' => [
369 'FIELD:foo:<:1',
370 ['foo' => []],
371 true,
372 ],
373 'Field is less than or equal to 1 if empty array given' => [
374 'FIELD:foo:<=:1',
375 ['foo' => []],
376 true,
377 ],
378 'Field does not equal 0 if empty array given' => [
379 'FIELD:foo:=:0',
380 ['foo' => []],
381 false,
382 ],
383 'Field value string comparison' => [
384 'FIELD:foo:=:bar',
385 ['foo' => 'bar'],
386 true,
387 ],
388 'Field value string comparison against list' => [
389 'FIELD:foo:IN:bar,baz',
390 ['foo' => 'baz'],
391 true,
392 ],
393 'Field value comparison of 1 against multi-value field of 5 returns true' => [
394 'FIELD:content:BIT:1',
395 ['content' => '5'],
396 true
397 ],
398 'Field value comparison of 2 against multi-value field of 5 returns false' => [
399 'FIELD:content:BIT:2',
400 ['content' => '5'],
401 false
402 ],
403 'Field value of 5 negated comparison against multi-value field of 5 returns false' => [
404 'FIELD:content:!BIT:5',
405 ['content' => '5'],
406 false
407 ],
408 'Field value comparison for required value is false for different value' => [
409 'FIELD:foo:REQ:FALSE',
410 ['foo' => 'bar'],
411 false,
412 ],
413 'Field value string not equal comparison' => [
414 'FIELD:foo:!=:baz',
415 ['foo' => 'bar'],
416 true,
417 ],
418 'Field value string not equal comparison against list' => [
419 'FIELD:foo:!IN:bar,baz',
420 ['foo' => 'foo'],
421 true,
422 ],
423 'Field value in range' => [
424 'FIELD:uid:-:3-42',
425 ['uid' => '23'],
426 true,
427 ],
428 'Field value greater than' => [
429 'FIELD:uid:>=:42',
430 ['uid' => '23'],
431 false,
432 ],
433 'New is TRUE for new comparison with TRUE' => [
434 'REC:NEW:TRUE',
435 ['uid' => null],
436 true,
437 ],
438 'New is FALSE for new comparison with FALSE' => [
439 'REC:NEW:FALSE',
440 ['uid' => null],
441 false,
442 ],
443 'New is FALSE for not new element' => [
444 'REC:NEW:TRUE',
445 ['uid' => 42],
446 false,
447 ],
448 'New is TRUE for not new element compared to FALSE' => [
449 'REC:NEW:FALSE',
450 ['uid' => 42],
451 true,
452 ],
453 'Version is TRUE for versioned row' => [
454 'VERSION:IS:TRUE',
455 [
456 'uid' => 42,
457 'pid' => -1
458 ],
459 true,
460 ],
461 'Version is TRUE for not versioned row compared with FALSE' => [
462 'VERSION:IS:FALSE',
463 [
464 'uid' => 42,
465 'pid' => 1
466 ],
467 true,
468 ],
469 'Version is TRUE for NULL row compared with TRUE' => [
470 'VERSION:IS:TRUE',
471 [
472 'uid' => null,
473 'pid' => null,
474 ],
475 false,
476 ],
477 'Multiple conditions with AND compare to TRUE if all are OK' => [
478 [
479 'AND' => [
480 'FIELD:testField:>:9',
481 'FIELD:testField:<:11',
482 ],
483 ],
484 [
485 'testField' => 10
486 ],
487 true,
488 ],
489 'Multiple conditions with AND compare to FALSE if one fails' => [
490 [
491 'AND' => [
492 'FIELD:testField:>:9',
493 'FIELD:testField:<:11',
494 ]
495 ],
496 [
497 'testField' => 99
498 ],
499 false,
500 ],
501 'Multiple conditions with OR compare to TRUE if one is OK' => [
502 [
503 'OR' => [
504 'FIELD:testField:<:9',
505 'FIELD:testField:<:11',
506 ],
507 ],
508 [
509 'testField' => 10
510 ],
511 true,
512 ],
513 'Multiple conditions with OR compare to FALSE is all fail' => [
514 [
515 'OR' => [
516 'FIELD:testField:<:9',
517 'FIELD:testField:<:11',
518 ],
519 ],
520 [
521 'testField' => 99
522 ],
523 false,
524 ],
525 'Multiple conditions without operator due to misconfiguration compare to TRUE' => [
526 [
527 '' => [
528 'FIELD:testField:<:9',
529 'FIELD:testField:>:11',
530 ]
531 ],
532 [
533 'testField' => 99
534 ],
535 true,
536 ],
537 'Multiple nested conditions evaluate to TRUE' => [
538 [
539 'AND' => [
540 'FIELD:testField:>:9',
541 'OR' => [
542 'FIELD:testField:<:100',
543 'FIELD:testField:>:-100',
544 ],
545 ],
546 ],
547 [
548 'testField' => 10
549 ],
550 true,
551 ],
552 'Multiple nested conditions evaluate to FALSE' => [
553 [
554 'AND' => [
555 'FIELD:testField:>:9',
556 'OR' => [
557 'FIELD:testField:<:100',
558 'FIELD:testField:>:-100',
559 ],
560 ],
561 ],
562 [
563 'testField' => -999
564 ],
565 false,
566 ],
567 ];
568 }
569
570 /**
571 * @param string $condition
572 * @param array $record
573 * @param string $expectedResult
574 * @dataProvider conditionStringDataProvider
575 * @test
576 */
577 public function matchConditionStrings($condition, array $record, $expectedResult)
578 {
579 $input = [
580 'databaseRow' => $record,
581 'processedTca' => [
582 'columns' => [
583 'testField' => [
584 'displayCond' => $condition,
585 'config' => [
586 'type' => 'input',
587 ]
588 ],
589 ]
590 ]
591 ];
592
593 $expected = $input;
594 if (!$expectedResult) {
595 unset($expected['processedTca']['columns']['testField']);
596 }
597 $this->assertSame($expected, $this->subject->addData($input));
598 }
599
600 /**
601 * @param string $condition
602 * @param array $record
603 * @param string $expectedResult
604 *
605 * @dataProvider conditionStringDataProvider
606 * @test
607 */
608 public function matchConditionStringsWithRecordTestFieldBeingArray($condition, array $record, $expectedResult)
609 {
610 $input = [
611 'processedTca' => [
612 'columns' => [
613 'testField' => [
614 'displayCond' => $condition,
615 'config' => [
616 'type' => 'input',
617 ],
618 ],
619 ],
620 ],
621 ];
622 $input['databaseRow'] = $record ?: ['testField' => ['key' => $record['testField']]];
623
624 $expected = $input;
625 if (!$expectedResult) {
626 unset($expected['processedTca']['columns']['testField']);
627 }
628 $this->assertSame($expected, $this->subject->addData($input));
629 }
630
631 /**
632 * Returns data sets for the test matchConditionStrings
633 * Each data set is an array with the following elements:
634 * - the condition string
635 * - the current record
636 * - the expected result
637 *
638 * @return array
639 */
640 public function flexformConditionStringDataProvider()
641 {
642 return [
643 'Flexform value invalid comparison' => [
644 'FIELD:foo:=:bar',
645 [
646 'foo' => 'bar',
647 'testField' => [
648 'data' => [
649 'sDEF' => [
650 'lDEF' => [],
651 ],
652 ],
653 ],
654 ],
655 false,
656 ],
657 'Flexform value valid comparison' => [
658 'FIELD:parentRec.foo:=:bar',
659 [
660 'foo' => 'bar',
661 'testField' => [
662 'data' => [
663 'sDEF' => [
664 'lDEF' => [],
665 ],
666 ],
667 ],
668 ],
669 true,
670 ],
671 ];
672 }
673
674 /**
675 * @param string $condition
676 * @param array $record
677 * @param string $expectedResult
678 * @dataProvider flexformConditionStringDataProvider
679 * @test
680 */
681 public function matchFlexformConditionStrings($condition, array $record, $expectedResult)
682 {
683 $input = [
684 'databaseRow' => $record,
685 'processedTca' => [
686 'columns' => [
687 'testField' => [
688 'config' => [
689 'type' => 'flex',
690 'ds' => [
691 'meta' => [],
692 'sheets' => [
693 'sDEF' => [
694 'ROOT' => [
695 'type' => 'array',
696 'el' => [
697 'flexTestField' => [
698 'displayCond' => $condition,
699 'config' => [
700 'type' => 'input',
701 ],
702 ],
703 ],
704 ],
705 ],
706 ]
707 ],
708 ],
709 ],
710 ],
711 ],
712 ];
713
714 $expected = $input;
715 if (!$expectedResult) {
716 unset($expected['processedTca']['columns']['testField']['config']['ds']['sheets']['sDEF']['ROOT']['el']['flexTestField']);
717 }
718 $this->assertSame($expected, $this->subject->addData($input));
719 }
720
721 /**
722 * @test
723 */
724 public function matchFlexformSheetConditionStringsForFieldsWithDotInName()
725 {
726 $input = [
727 'databaseRow' => [
728 'foo' => 'bar',
729 'testField' => [
730 'data' => [
731 'sDEF' => [
732 'lDEF' => [
733 'flex.TestField' => [
734 'vDEF' => [
735 0 => 0,
736 ],
737 ],
738 ],
739 ],
740 ],
741 ],
742 ],
743 'processedTca' => [
744 'columns' => [
745 'testField' => [
746 'config' => [
747 'type' => 'flex',
748 'ds' => [
749 'meta' => [],
750 'sheets' => [
751 'sDEF' => [
752 'ROOT' => [
753 'type' => 'array',
754 'el' => [
755 'flex.TestField' => [
756 'config' => [
757 'type' => 'input',
758 ],
759 ],
760 ],
761 ],
762 ],
763 'sTest' => [
764 'ROOT' => [
765 'type' => 'array',
766 'el' => [],
767 'sheetTitle' => 'sVideo',
768 'displayCond' => 'FIELD:sDEF.flex.TestField:!=:0',
769 ],
770 ],
771 ]
772 ],
773 ],
774 ],
775 ],
776 ],
777 ];
778
779 $expected = $input;
780 unset($expected['processedTca']['columns']['testField']['config']['ds']['sheets']['sTest']);
781 $this->assertSame($expected, $this->subject->addData($input));
782 }
783
784 /**
785 * @test
786 */
787 public function matchFlexformSheetConditionStringsWithLogicalOperatorForFieldsWithDotInName()
788 {
789 $input = [
790 'databaseRow' => [
791 'foo' => 'bar',
792 'testField' => [
793 'data' => [
794 'sDEF' => [
795 'lDEF' => [
796 'testField' => [
797 'vDEF' => [
798 0 => '',
799 ],
800 ],
801 ],
802 ],
803 ],
804 ],
805 ],
806 'processedTca' => [
807 'columns' => [
808 'testField' => [
809 'config' => [
810 'type' => 'flex',
811 'ds' => [
812 'meta' => [],
813 'sheets' => [
814 'sDEF' => [
815 'ROOT' => [
816 'type' => 'array',
817 'el' => [
818 'testField' => [
819 'config' => [
820 'type' => 'input',
821 ],
822 ],
823 ],
824 ],
825 ],
826 'sTest' => [
827 'ROOT' => [
828 'type' => 'array',
829 'el' => [],
830 'sheetTitle' => 'sVideo',
831 'displayCond' => [
832 'OR' => [
833 'FIELD:sDEF.testField:=:LIST',
834 'FIELD:sDEF.testField:REQ:false',
835 ],
836 ] ,
837 ],
838 ],
839 ]
840 ],
841 ],
842 ],
843 ],
844 ],
845 ];
846
847 $expected = $input;
848 $this->assertSame($expected, $this->subject->addData($input));
849 }
850 }