[FEATURE] New option to disable the age display of dates per field by TCA
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Utility / BackendUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Utility;
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\Utility\BackendUtility;
20 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForGroupWithOneAllowedTableFixture;
21 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForGroupWithMultipleAllowedTablesFixture;
22 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForSelectWithMMRelationFixture;
23 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\LabelFromItemListMergedReturnsCorrectFieldsFixture;
24 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ExcludeFieldsReturnsCorrectFieldListFixture;
25 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ExcludeFieldsReturnsCorrectListWithFlexFormFieldsFixture;
26 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27 use TYPO3\CMS\Core\Charset\CharsetConverter;
28 use TYPO3\CMS\Core\Database\DatabaseConnection;
29 use TYPO3\CMS\Core\Tests\UnitTestCase;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Lang\LanguageService;
32
33 /**
34 * Test case
35 */
36 class BackendUtilityTest extends UnitTestCase {
37
38 ///////////////////////////////////////
39 // Tests concerning calcAge
40 ///////////////////////////////////////
41 /**
42 * Data provider for calcAge function
43 *
44 * @return array
45 */
46 public function calcAgeDataProvider() {
47 return array(
48 'Single year' => array(
49 'seconds' => 60 * 60 * 24 * 365,
50 'expectedLabel' => '1 year'
51 ),
52 'Plural years' => array(
53 'seconds' => 60 * 60 * 24 * 365 * 2,
54 'expectedLabel' => '2 yrs'
55 ),
56 'Single negative year' => array(
57 'seconds' => 60 * 60 * 24 * 365 * -1,
58 'expectedLabel' => '-1 year'
59 ),
60 'Plural negative years' => array(
61 'seconds' => 60 * 60 * 24 * 365 * 2 * -1,
62 'expectedLabel' => '-2 yrs'
63 ),
64 'Single day' => array(
65 'seconds' => 60 * 60 * 24,
66 'expectedLabel' => '1 day'
67 ),
68 'Plural days' => array(
69 'seconds' => 60 * 60 * 24 * 2,
70 'expectedLabel' => '2 days'
71 ),
72 'Single negative day' => array(
73 'seconds' => 60 * 60 * 24 * -1,
74 'expectedLabel' => '-1 day'
75 ),
76 'Plural negative days' => array(
77 'seconds' => 60 * 60 * 24 * 2 * -1,
78 'expectedLabel' => '-2 days'
79 ),
80 'Single hour' => array(
81 'seconds' => 60 * 60,
82 'expectedLabel' => '1 hour'
83 ),
84 'Plural hours' => array(
85 'seconds' => 60 * 60 * 2,
86 'expectedLabel' => '2 hrs'
87 ),
88 'Single negative hour' => array(
89 'seconds' => 60 * 60 * -1,
90 'expectedLabel' => '-1 hour'
91 ),
92 'Plural negative hours' => array(
93 'seconds' => 60 * 60 * 2 * -1,
94 'expectedLabel' => '-2 hrs'
95 ),
96 'Single minute' => array(
97 'seconds' => 60,
98 'expectedLabel' => '1 min'
99 ),
100 'Plural minutes' => array(
101 'seconds' => 60 * 2,
102 'expectedLabel' => '2 min'
103 ),
104 'Single negative minute' => array(
105 'seconds' => 60 * -1,
106 'expectedLabel' => '-1 min'
107 ),
108 'Plural negative minutes' => array(
109 'seconds' => 60 * 2 * -1,
110 'expectedLabel' => '-2 min'
111 ),
112 'Zero seconds' => array(
113 'seconds' => 0,
114 'expectedLabel' => '0 min'
115 )
116 );
117 }
118
119 /**
120 * @test
121 * @dataProvider calcAgeDataProvider
122 *
123 * @param int $seconds
124 * @param string $expectedLabel
125 */
126 public function calcAgeReturnsExpectedValues($seconds, $expectedLabel) {
127 $this->assertSame($expectedLabel, BackendUtility::calcAge($seconds));
128 }
129
130 ///////////////////////////////////////
131 // Tests concerning getProcessedValue
132 ///////////////////////////////////////
133 /**
134 * @test
135 * @see http://forge.typo3.org/issues/20994
136 */
137 public function getProcessedValueForZeroStringIsZero() {
138 $GLOBALS['TCA'] = array(
139 'tt_content' => array(
140 'columns' => array(
141 'header' => array(
142 'config' => array(
143 'type' => 'input',
144 ),
145 ),
146 ),
147 ),
148 );
149 $this->assertEquals('0', BackendUtility::getProcessedValue('tt_content', 'header', '0'));
150 }
151
152 /**
153 * @test
154 */
155 public function getProcessedValueForGroup() {
156 $GLOBALS['TCA'] = array(
157 'tt_content' => array(
158 'columns' => array(
159 'multimedia' => array(
160 'config' => array(
161 'type' => 'group',
162 ),
163 ),
164 ),
165 ),
166 );
167 $this->assertSame('1, 2', BackendUtility::getProcessedValue('tt_content', 'multimedia', '1,2'));
168 }
169
170 /**
171 * @test
172 */
173 public function getProcessedValueForGroupWithOneAllowedTable() {
174 $GLOBALS['TCA'] = array(
175 'tt_content' => array(
176 'columns' => array(
177 'pages' => array(
178 'config' => array(
179 'type' => 'group',
180 'allowed' => 'pages',
181 'internal_type' => 'db',
182 'maxitems' => 22,
183 'minitems' => 0,
184 'show_thumbs' => 1,
185 'size' => 3,
186 ),
187 ),
188 ),
189 ),
190 );
191
192 $this->assertSame('Page 1, Page 2', ProcessedValueForGroupWithOneAllowedTableFixture::getProcessedValue('tt_content', 'pages', '1,2'));
193 }
194
195 /**
196 * @test
197 */
198 public function getProcessedValueForGroupWithMultipleAllowedTables() {
199 $GLOBALS['TCA'] = array(
200 'index_config' => array(
201 'columns' => array(
202 'indexcfgs' => array(
203 'config' => array(
204 'type' => 'group',
205 'internal_type' => 'db',
206 'allowed' => 'index_config,pages',
207 'size' => 5,
208 ),
209 ),
210 ),
211 ),
212 );
213
214 $this->assertSame('Page 1, Configuration 2', ProcessedValueForGroupWithMultipleAllowedTablesFixture::getProcessedValue('index_config', 'indexcfgs', 'pages_1,index_config_2'));
215 }
216
217 /**
218 * @test
219 */
220 public function getProcessedValueForSelectWithMMRelation() {
221 $GLOBALS['TYPO3_DB'] = $this->getMock(DatabaseConnection::class, array(), array(), '', FALSE);
222 $GLOBALS['TYPO3_DB']->expects($this->any())->method('fullQuoteStr')
223 ->will($this->returnCallback(
224 function ($quoteStr) {
225 return "'" . $quoteStr . "'";
226 }
227 )
228 );
229 $GLOBALS['TYPO3_DB']->expects($this->any())->method('exec_SELECTquery')->will($this->returnValue(0));
230 $GLOBALS['TYPO3_DB']->expects($this->any())->method('sql_free_result');
231 $GLOBALS['TYPO3_DB']->expects($this->any())->method('sql_fetch_assoc')
232 ->will($this->returnCallback(
233 function () {
234 static $called = 0;
235 ++$called;
236 switch ($called) {
237 // SELECT * FROM sys_category_record_mm
238 case 1:
239 return array(
240 'uid_local' => 1, // uid of a sys_category record
241 'uid_foreign' => 1, // uid of a pages record
242 );
243 case 2:
244 return array(
245 'uid_local' => 2, // uid of a sys_category record
246 'uid_foreign' => 1, // uid of a pages record
247 );
248 case 3:
249 return NULL;
250 // SELECT * FROM sys_catgory
251 case 4:
252 return array(
253 'uid' => 1,
254 'title' => 'Category 1',
255 );
256 case 5:
257 return array(
258 'uid' => 2,
259 'title' => 'Category 2',
260 );
261 case 6:
262 return NULL;
263 }
264 return NULL;
265 }
266 )
267 );
268
269 $GLOBALS['TCA'] = array(
270 'pages' => array(
271 'columns' => array(
272 'categories' => array(
273 'config' => array(
274 'type' => 'select',
275 'foreign_table' => 'sys_category',
276 'MM' => 'sys_category_record_mm',
277 'MM_match_fields' => array(
278 'fieldname' => 'categories',
279 'tablesnames' => 'pages',
280 ),
281 'MM_opposite_field' => 'items',
282 ),
283 ),
284 ),
285 ),
286 'sys_category' => array(
287 'columns' => array(
288 'items' => array(
289 'config' => array(
290 'type' => 'group',
291 'internal_type' => 'db',
292 'allowed' => '*',
293 'MM' => 'sys_category_record_mm',
294 'MM_oppositeUsage' => array(),
295 )
296 )
297 ),
298 ),
299 );
300
301 $this->assertSame('Category 1; Category 2', ProcessedValueForSelectWithMMRelationFixture::getProcessedValue('pages', 'categories', '2', 0, FALSE, FALSE, 1));
302 }
303
304 /**
305 * @test
306 */
307 public function getProcessedValueDisplaysAgeForDateInputFieldsIfSettingAbsent() {
308 /** @var ObjectProphecy $languageServiceProphecy */
309 $languageServiceProphecy = $this->prophesize(LanguageService::class);
310 $languageServiceProphecy->sL(Argument::cetera())->willReturn(' min| hrs| days| yrs| min| hour| day| year');
311 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
312
313 $GLOBALS['EXEC_TIME'] = mktime(0, 0, 0, 8, 30, 2015);
314
315 $GLOBALS['TCA'] = [
316 'tt_content' => [
317 'columns' => [
318 'date' => [
319 'config' => [
320 'type' => 'input',
321 'eval' => 'date',
322 ],
323 ],
324 ],
325 ],
326 ];
327 $this->assertSame('28-08-15 (-2 days)', BackendUtility::getProcessedValue('tt_content', 'date', mktime(0, 0, 0, 8, 28, 2015)));
328 }
329
330 /**
331 * @return array
332 */
333 public function inputTypeDateDisplayOptions() {
334 return [
335 'typeSafe Setting' => [
336 TRUE,
337 '28-08-15',
338 ],
339 'non typesafe setting' => [
340 1,
341 '28-08-15',
342 ],
343 'setting disabled typesafe' => [
344 FALSE,
345 '28-08-15 (-2 days)',
346 ],
347 'setting disabled not typesafe' => [
348 0,
349 '28-08-15 (-2 days)',
350 ],
351 ];
352 }
353
354 /**
355 * @test
356 *
357 * @dataProvider inputTypeDateDisplayOptions
358 *
359 * @param string $input
360 * @param string $expected
361 */
362 public function getProcessedValueHandlesAgeDisplayCorrectly($input, $expected) {
363 /** @var ObjectProphecy $languageServiceProphecy */
364 $languageServiceProphecy = $this->prophesize(LanguageService::class);
365 $languageServiceProphecy->sL(Argument::cetera())->willReturn(' min| hrs| days| yrs| min| hour| day| year');
366 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
367
368 $GLOBALS['EXEC_TIME'] = mktime(0, 0, 0, 8, 30, 2015);
369
370 $GLOBALS['TCA'] = [
371 'tt_content' => [
372 'columns' => [
373 'date' => [
374 'config' => [
375 'type' => 'input',
376 'eval' => 'date',
377 'disableAgeDisplay' => $input,
378 ],
379 ],
380 ],
381 ],
382 ];
383 $this->assertSame($expected, BackendUtility::getProcessedValue('tt_content', 'date', mktime(0, 0, 0, 8, 28, 2015)));
384 }
385
386 /**
387 * Tests concerning getCommonSelectFields
388 */
389
390 /**
391 * Data provider for getCommonSelectFieldsReturnsCorrectFields
392 *
393 * @return array The test data with $table, $prefix, $presetFields, $tca, $expectedFields
394 */
395 public function getCommonSelectFieldsReturnsCorrectFieldsDataProvider() {
396 return array(
397 'only uid' => array(
398 'table' => 'test_table',
399 'prefix' => '',
400 'presetFields' => array(),
401 'tca' => array(),
402 'expectedFields' => 'uid'
403 ),
404 'label set' => array(
405 'table' => 'test_table',
406 'prefix' => '',
407 'presetFields' => array(),
408 'tca' => array(
409 'ctrl' => array(
410 'label' => 'label'
411 )
412 ),
413 'expectedFields' => 'uid,label'
414 ),
415 'label_alt set' => array(
416 'table' => 'test_table',
417 'prefix' => '',
418 'presetFields' => array(),
419 'tca' => array(
420 'ctrl' => array(
421 'label_alt' => 'label,label2'
422 )
423 ),
424 'expectedFields' => 'uid,label,label2'
425 ),
426 'versioningWS set' => array(
427 'table' => 'test_table',
428 'prefix' => '',
429 'presetFields' => array(),
430 'tca' => array(
431 'ctrl' => array(
432 'versioningWS' => '2'
433 )
434 ),
435 'expectedFields' => 'uid,t3ver_id,t3ver_state,t3ver_wsid,t3ver_count'
436 ),
437 'selicon_field set' => array(
438 'table' => 'test_table',
439 'prefix' => '',
440 'presetFields' => array(),
441 'tca' => array(
442 'ctrl' => array(
443 'selicon_field' => 'field'
444 )
445 ),
446 'expectedFields' => 'uid,field'
447 ),
448 'typeicon_column set' => array(
449 'table' => 'test_table',
450 'prefix' => '',
451 'presetFields' => array(),
452 'tca' => array(
453 'ctrl' => array(
454 'typeicon_column' => 'field'
455 )
456 ),
457 'expectedFields' => 'uid,field'
458 ),
459 'enablecolumns set' => array(
460 'table' => 'test_table',
461 'prefix' => '',
462 'presetFields' => array(),
463 'tca' => array(
464 'ctrl' => array(
465 'enablecolumns' => array(
466 'disabled' => 'hidden',
467 'starttime' => 'start',
468 'endtime' => 'stop',
469 'fe_group' => 'groups'
470 )
471 )
472 ),
473 'expectedFields' => 'uid,hidden,start,stop,groups'
474 ),
475 'label set to uid' => array(
476 'table' => 'test_table',
477 'prefix' => '',
478 'presetFields' => array(),
479 'tca' => array(
480 'ctrl' => array(
481 'label' => 'uid'
482 )
483 ),
484 'expectedFields' => 'uid'
485 )
486 );
487 }
488
489 /**
490 * @test
491 * @dataProvider getCommonSelectFieldsReturnsCorrectFieldsDataProvider
492 *
493 * @param string $table
494 * @param string $prefix
495 * @param array $presetFields
496 * @param array $tca
497 * @param string $expectedFields
498 */
499 public function getCommonSelectFieldsReturnsCorrectFields($table, $prefix = '', array $presetFields, array $tca, $expectedFields = '') {
500 $GLOBALS['TCA'][$table] = $tca;
501 $selectFields = BackendUtility::getCommonSelectFields($table, $prefix, $presetFields);
502 $this->assertEquals($selectFields, $expectedFields);
503 }
504
505 /**
506 * Tests concerning getLabelFromItemlist
507 */
508
509 /**
510 * Data provider for getLabelFromItemlistReturnsCorrectFields
511 *
512 * @return array The test data with $table, $col, $key, $expectedLabel
513 */
514 public function getLabelFromItemlistReturnsCorrectFieldsDataProvider() {
515 return array(
516 'item set' => array(
517 'table' => 'tt_content',
518 'col' => 'menu_type',
519 'key' => '1',
520 'tca' => array(
521 'columns' => array(
522 'menu_type' => array(
523 'config' => array(
524 'items' => array(
525 array('Item 1', '0'),
526 array('Item 2', '1'),
527 array('Item 3', '3')
528 )
529 )
530 )
531 )
532 ),
533 'expectedLabel' => 'Item 2'
534 ),
535 'item set twice' => array(
536 'table' => 'tt_content',
537 'col' => 'menu_type',
538 'key' => '1',
539 'tca' => array(
540 'columns' => array(
541 'menu_type' => array(
542 'config' => array(
543 'items' => array(
544 array('Item 1', '0'),
545 array('Item 2a', '1'),
546 array('Item 2b', '1'),
547 array('Item 3', '3')
548 )
549 )
550 )
551 )
552 ),
553 'expectedLabel' => 'Item 2a'
554 ),
555 'item not found' => array(
556 'table' => 'tt_content',
557 'col' => 'menu_type',
558 'key' => '5',
559 'tca' => array(
560 'columns' => array(
561 'menu_type' => array(
562 'config' => array(
563 'items' => array(
564 array('Item 1', '0'),
565 array('Item 2', '1'),
566 array('Item 3', '2')
567 )
568 )
569 )
570 )
571 ),
572 'expectedLabel' => NULL
573 )
574 );
575 }
576
577 /**
578 * @test
579 * @dataProvider getLabelFromItemlistReturnsCorrectFieldsDataProvider
580 *
581 * @param string $table
582 * @param string $col
583 * @param string $key
584 * @param array $tca
585 * @param string $expectedLabel
586 */
587 public function getLabelFromItemlistReturnsCorrectFields($table, $col = '', $key = '', array $tca, $expectedLabel = '') {
588 $GLOBALS['TCA'][$table] = $tca;
589 $label = BackendUtility::getLabelFromItemlist($table, $col, $key);
590 $this->assertEquals($label, $expectedLabel);
591 }
592
593 /**
594 * Tests concerning getLabelFromItemListMerged
595 */
596
597 /**
598 * Data provider for getLabelFromItemListMerged
599 *
600 * @return array The test data with $pageId, $table, $column, $key, $expectedLabel
601 */
602 public function getLabelFromItemListMergedReturnsCorrectFieldsDataProvider() {
603 return array(
604 'no field found' => array(
605 'pageId' => '123',
606 'table' => 'tt_content',
607 'col' => 'menu_type',
608 'key' => '10',
609 'tca' => array(
610 'columns' => array(
611 'menu_type' => array(
612 'config' => array(
613 'items' => array(
614 array('Item 1', '0'),
615 array('Item 2', '1'),
616 array('Item 3', '3')
617 )
618 )
619 )
620 )
621 ),
622 'expectedLabel' => ''
623 ),
624 'no tsconfig set' => array(
625 'pageId' => '123',
626 'table' => 'tt_content',
627 'col' => 'menu_type',
628 'key' => '1',
629 'tca' => array(
630 'columns' => array(
631 'menu_type' => array(
632 'config' => array(
633 'items' => array(
634 array('Item 1', '0'),
635 array('Item 2', '1'),
636 array('Item 3', '3')
637 )
638 )
639 )
640 )
641 ),
642 'expectedLabel' => 'Item 2'
643 )
644 );
645 }
646
647 /**
648 * @test
649 * @dataProvider getLabelFromItemListMergedReturnsCorrectFieldsDataProvider
650 *
651 * @param int $pageId
652 * @param string $table
653 * @param string $column
654 * @param string $key
655 * @param array $tca
656 * @param string $expectedLabel
657 */
658 public function getLabelFromItemListMergedReturnsCorrectFields($pageId, $table, $column = '', $key = '', array $tca, $expectedLabel = '') {
659 $GLOBALS['TCA'][$table] = $tca;
660
661 $this->assertEquals($expectedLabel, LabelFromItemListMergedReturnsCorrectFieldsFixture::getLabelFromItemListMerged($pageId, $table, $column, $key));
662 }
663
664 /**
665 * Tests concerning getFuncCheck
666 */
667
668 /**
669 * @test
670 */
671 public function getFuncCheckReturnsInputTagWithValueAttribute() {
672 $this->assertStringMatchesFormat('<input %Svalue="1"%S/>', BackendUtility::getFuncCheck('params', 'test', TRUE));
673 }
674
675 /*
676 * Tests concerning getLabelsFromItemsList
677 */
678
679 /**
680 * @return array
681 */
682 public function getLabelsFromItemsListDataProvider() {
683 return array(
684 'return value if found' => array(
685 'foobar', // table
686 'someColumn', // col
687 'foo, bar', // keyList
688 array( // TCA
689 'columns' => array(
690 'someColumn' => array(
691 'config' => array(
692 'items' => array(
693 '0' => array('aFooLabel', 'foo'),
694 '1' => array('aBarLabel', 'bar')
695 )
696 )
697 )
698 )
699 ),
700 array(), // page TSconfig
701 'aFooLabel, aBarLabel' // expected
702 ),
703 'page TSconfig overrules TCA' => array(
704 'foobar', // table
705 'someColumn', // col
706 'foo,bar, add', // keyList
707 array( // TCA
708 'columns' => array(
709 'someColumn' => array(
710 'config' => array(
711 'items' => array(
712 '0' => array('aFooLabel', 'foo'),
713 '1' => array('aBarLabel', 'bar')
714 )
715 )
716 )
717 )
718 ),
719 array( // page TSconfig
720 'addItems.' => array('add' => 'aNewLabel'),
721 'altLabels.' => array('bar' => 'aBarDiffLabel'),
722 ),
723 'aFooLabel, aBarDiffLabel, aNewLabel' // expected
724 )
725 );
726 }
727
728 /**
729 * @test
730 * @dataProvider getLabelsFromItemsListDataProvider
731 *
732 * @param string $table
733 * @param string $col
734 * @param string $keyList
735 * @param array $tca
736 * @param array $pageTsConfig
737 * @param string $expectedLabel
738 */
739 public function getLabelsFromItemsListReturnsCorrectValue($table, $col, $keyList, $tca, array $pageTsConfig, $expectedLabel) {
740 // Stub LanguageService and let sL() return the same value that came in again
741 $GLOBALS['LANG'] = $this->getMock(LanguageService::class, array(), array(), '', FALSE);
742 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
743
744 $GLOBALS['TCA'][$table] = $tca;
745 $label = BackendUtility::getLabelsFromItemsList($table, $col, $keyList, $pageTsConfig);
746 $this->assertEquals($expectedLabel, $label);
747 }
748
749 /**
750 * @test
751 */
752 public function getProcessedValueReturnsLabelsForExistingValuesSolely() {
753 $table = 'foobar';
754 $col = 'someColumn';
755 $tca = array(
756 'columns' => array(
757 'someColumn' => array(
758 'config' => array(
759 'type' => 'select',
760 'items' => array(
761 '0' => array('aFooLabel', 'foo'),
762 '1' => array('aBarLabel', 'bar')
763 )
764 )
765 )
766 )
767 );
768 // Stub LanguageService and let sL() return the same value that came in again
769 $GLOBALS['LANG'] = $this->getMock(LanguageService::class, array(), array(), '', FALSE);
770 $GLOBALS['LANG']->charSet = 'utf-8';
771 $GLOBALS['LANG']->csConvObj = $this->getMock(CharsetConverter::class);
772 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
773
774 $GLOBALS['LANG']->csConvObj->expects($this->any())->method('crop')->will($this->returnArgument(1));
775
776 $GLOBALS['TCA'][$table] = $tca;
777 $label = BackendUtility::getProcessedValue($table, $col, 'foo,invalidKey,bar');
778 $this->assertEquals('aFooLabel, aBarLabel', $label);
779 }
780
781 /**
782 * @test
783 */
784 public function getProcessedValueReturnsPlainValueIfItemIsNotFound() {
785 $table = 'foobar';
786 $col = 'someColumn';
787 $tca = array(
788 'columns' => array(
789 'someColumn' => array(
790 'config' => array(
791 'type' => 'select',
792 'items' => array(
793 '0' => array('aFooLabel', 'foo')
794 )
795 )
796 )
797 )
798 );
799 // Stub LanguageService and let sL() return the same value that came in again
800 $GLOBALS['LANG'] = $this->getMock(LanguageService::class, array(), array(), '', FALSE);
801 $GLOBALS['LANG']->charSet = 'utf-8';
802 $GLOBALS['LANG']->csConvObj = $this->getMock(CharsetConverter::class);
803 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
804
805 $GLOBALS['LANG']->csConvObj->expects($this->any())->method('crop')->will($this->returnArgument(1));
806
807 $GLOBALS['TCA'][$table] = $tca;
808 $label = BackendUtility::getProcessedValue($table, $col, 'invalidKey');
809 $this->assertEquals('invalidKey', $label);
810 }
811
812 /**
813 * Tests concerning getExcludeFields
814 */
815
816 /**
817 * @return array
818 */
819 public function getExcludeFieldsDataProvider() {
820 return array(
821 'getExcludeFields does not return fields not configured as exclude field' => array(
822 array(
823 'tx_foo' => array(
824 'ctrl' => array(
825 'title' => 'foo',
826 ),
827 'columns' => array(
828 'bar' => array(
829 'label' => 'bar',
830 'exclude' => 1
831 ),
832 'baz' => array(
833 'label' => 'bar',
834 ),
835 )
836 )
837 ),
838 array(
839 array(
840 'foo: bar',
841 'tx_foo:bar',
842 ),
843 )
844 ),
845 'getExcludeFields returns fields from root level tables if root level restriction should be ignored' => array(
846 array(
847 'tx_foo' => array(
848 'ctrl' => array(
849 'title' => 'foo',
850 'rootLevel' => TRUE,
851 'security' => array(
852 'ignoreRootLevelRestriction' => TRUE,
853 ),
854 ),
855 'columns' => array(
856 'bar' => array(
857 'label' => 'bar',
858 'exclude' => 1
859 ),
860 )
861 )
862 ),
863 array(
864 array(
865 'foo: bar',
866 'tx_foo:bar',
867 ),
868 )
869 ),
870 'getExcludeFields does not return fields from root level tables' => array(
871 array(
872 'tx_foo' => array(
873 'ctrl' => array(
874 'title' => 'foo',
875 'rootLevel' => TRUE,
876 ),
877 'columns' => array(
878 'bar' => array(
879 'label' => 'bar',
880 'exclude' => 1
881 ),
882 )
883 )
884 ),
885 array()
886 ),
887 'getExcludeFields does not return fields from admin only level tables' => array(
888 array(
889 'tx_foo' => array(
890 'ctrl' => array(
891 'title' => 'foo',
892 'adminOnly' => TRUE,
893 ),
894 'columns' => array(
895 'bar' => array(
896 'label' => 'bar',
897 'exclude' => 1
898 ),
899 )
900 )
901 ),
902 array()
903 ),
904 );
905 }
906
907 /**
908 * @param $tca
909 * @param $expected
910 *
911 * @test
912 * @dataProvider getExcludeFieldsDataProvider
913 */
914 public function getExcludeFieldsReturnsCorrectFieldList($tca, $expected) {
915 $GLOBALS['TCA'] = $tca;
916
917 // Stub LanguageService and let sL() return the same value that came in again
918 $GLOBALS['LANG'] = $this->getMock(LanguageService::class, array(), array(), '', FALSE);
919 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
920
921 $this->assertSame($expected, ExcludeFieldsReturnsCorrectFieldListFixture::getExcludeFields());
922 }
923
924 /**
925 * @test
926 */
927 public function getExcludeFieldsReturnsCorrectListWithFlexFormFields() {
928 $GLOBALS['TCA'] = array(
929 'tx_foo' => array(
930 'ctrl' => array(
931 'title' => 'foo'
932 ),
933 'columns' => array(
934 'foo' => array(
935 'label' => 'foo',
936 'exclude' => 1
937 ),
938 'bar' => array(
939 'label' => 'bar',
940 'exclude' => 1
941 ),
942 'abarfoo' => array(
943 'label' => 'abarfoo',
944 'config' => array(
945 'type' => 'flex',
946 'ds' => array(
947 '*,dummy' => '',
948 )
949 )
950 )
951 )
952 ),
953 'tx_foobar' => array(
954 'ctrl' => array(
955 'title' => 'foobar'
956 ),
957 'columns' => array(
958 'foo' => array(
959 'label' => 'foo',
960 'exclude' => 1
961 ),
962 'bar' => array(
963 'label' => 'bar',
964 'exclude' => 1
965 )
966 )
967 ),
968 'tx_bar' => array(
969 'ctrl' => array(
970 'title' => 'bar'
971 ),
972 'columns' => array(
973 'foo' => array(
974 'label' => 'foo',
975 'exclude' => 1
976 ),
977 'bar' => array(
978 'label' => 'bar',
979 'exclude' => 1
980 )
981 )
982 )
983 );
984
985 $expectedResult = array(
986 array(
987 'bar: bar',
988 'tx_bar:bar'
989 ),
990 array(
991 'bar: foo',
992 'tx_bar:foo'
993 ),
994 array(
995 'abarfoo dummy: The Title:',
996 'tx_foo:abarfoo;dummy;sGeneral;xmlTitle'
997 ),
998 array(
999 'foo: bar',
1000 'tx_foo:bar'
1001 ),
1002 array(
1003 'foo: foo',
1004 'tx_foo:foo'
1005 ),
1006 array(
1007 'foobar: bar',
1008 'tx_foobar:bar'
1009 ),
1010 array(
1011 'foobar: foo',
1012 'tx_foobar:foo'
1013 ),
1014 );
1015
1016 // Stub LanguageService and let sL() return the same value that came in again
1017 $GLOBALS['LANG'] = $this->getMock(LanguageService::class, array(), array(), '', FALSE);
1018 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
1019
1020 $this->assertSame($expectedResult, ExcludeFieldsReturnsCorrectListWithFlexFormFieldsFixture::getExcludeFields());
1021 }
1022
1023 /**
1024 * Tests concerning viewOnClick
1025 */
1026
1027 /**
1028 * @test
1029 */
1030 public function viewOnClickReturnsOnClickCodeWithAlternativeUrl() {
1031 // Make sure the hook inside viewOnClick is not fired. This may be removed if unit tests
1032 // bootstrap does not initialize TYPO3_CONF_VARS anymore.
1033 unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass']);
1034
1035 $alternativeUrl = 'https://typo3.org/about/typo3-the-cms/the-history-of-typo3/#section';
1036 $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($alternativeUrl) . ',\'newTYPO3frontendWindow\');';
1037 $this->assertStringMatchesFormat(
1038 $onclickCode,
1039 BackendUtility::viewOnClick(NULL, NULL, NULL, NULL, $alternativeUrl, NULL, FALSE)
1040 );
1041 }
1042
1043 /**
1044 * Tests concerning replaceMarkersInWhereClause
1045 */
1046
1047 /**
1048 * @return array
1049 */
1050 public function replaceMarkersInWhereClauseDataProvider() {
1051 return array(
1052 'replaceMarkersInWhereClause replaces record field marker with quoted string' => array(
1053 ' AND dummytable.title=\'###REC_FIELD_dummyfield###\'',
1054 array(
1055 '_THIS_ROW' => array(
1056 'dummyfield' => 'Hello World'
1057 )
1058 ),
1059 ' AND dummytable.title=\'Hello World\''
1060 ),
1061 'replaceMarkersInWhereClause replaces record field marker with fullquoted string' => array(
1062 ' AND dummytable.title=###REC_FIELD_dummyfield###',
1063 array(
1064 '_THIS_ROW' => array(
1065 'dummyfield' => 'Hello World'
1066 )
1067 ),
1068 ' AND dummytable.title=\'Hello World\''
1069 ),
1070 'replaceMarkersInWhereClause replaces multiple record field markers' => array(
1071 ' AND dummytable.title=\'###REC_FIELD_dummyfield###\' AND dummytable.pid=###REC_FIELD_pid###',
1072 array(
1073 '_THIS_ROW' => array(
1074 'dummyfield' => 'Hello World',
1075 'pid' => 42
1076 )
1077 ),
1078 ' AND dummytable.title=\'Hello World\' AND dummytable.pid=\'42\''
1079 ),
1080 'replaceMarkersInWhereClause replaces current pid with integer' => array(
1081 ' AND dummytable.uid=###CURRENT_PID###',
1082 array(
1083 '_CURRENT_PID' => 42
1084 ),
1085 ' AND dummytable.uid=42'
1086 ),
1087 'replaceMarkersInWhereClause replaces current pid with string' => array(
1088 ' AND dummytable.uid=###CURRENT_PID###',
1089 array(
1090 '_CURRENT_PID' => '42string'
1091 ),
1092 ' AND dummytable.uid=42'
1093 ),
1094 'replaceMarkersInWhereClause replaces current record uid with integer' => array(
1095 ' AND dummytable.uid=###THIS_UID###',
1096 array(
1097 '_THIS_UID' => 42
1098 ),
1099 ' AND dummytable.uid=42'
1100 ),
1101 'replaceMarkersInWhereClause replaces current record uid with string' => array(
1102 ' AND dummytable.uid=###THIS_UID###',
1103 array(
1104 '_THIS_UID' => '42string'
1105 ),
1106 ' AND dummytable.uid=42'
1107 ),
1108 'replaceMarkersInWhereClause replaces storage pid with integer' => array(
1109 ' AND dummytable.uid=###STORAGE_PID###',
1110 array(
1111 '_STORAGE_PID' => 42
1112 ),
1113 ' AND dummytable.uid=42'
1114 ),
1115 'replaceMarkersInWhereClause replaces storage pid with string' => array(
1116 ' AND dummytable.uid=###STORAGE_PID###',
1117 array(
1118 '_STORAGE_PID' => '42string'
1119 ),
1120 ' AND dummytable.uid=42'
1121 ),
1122 'replaceMarkersInWhereClause replaces siteroot uid with integer' => array(
1123 ' AND dummytable.uid=###SITEROOT###',
1124 array(
1125 '_SITEROOT' => 42
1126 ),
1127 ' AND dummytable.uid=42'
1128 ),
1129 'replaceMarkersInWhereClause replaces siteroot uid with string' => array(
1130 ' AND dummytable.uid=###SITEROOT###',
1131 array(
1132 '_SITEROOT' => '42string'
1133 ),
1134 ' AND dummytable.uid=42'
1135 ),
1136 'replaceMarkersInWhereClause replaces page tsconfig id with integer' => array(
1137 ' AND dummytable.uid=###PAGE_TSCONFIG_ID###',
1138 array(
1139 'dummyfield' => array(
1140 'PAGE_TSCONFIG_ID' => 42
1141 )
1142 ),
1143 ' AND dummytable.uid=42'
1144 ),
1145 'replaceMarkersInWhereClause replaces page tsconfig id with string' => array(
1146 ' AND dummytable.uid=###PAGE_TSCONFIG_ID###',
1147 array(
1148 'dummyfield' => array(
1149 'PAGE_TSCONFIG_ID' => '42string'
1150 )
1151 ),
1152 ' AND dummytable.uid=42'
1153 ),
1154 'replaceMarkersInWhereClause replaces page tsconfig string' => array(
1155 ' AND dummytable.title=\'###PAGE_TSCONFIG_STR###\'',
1156 array(
1157 'dummyfield' => array(
1158 'PAGE_TSCONFIG_STR' => '42'
1159 )
1160 ),
1161 ' AND dummytable.title=\'42\''
1162 ),
1163 'replaceMarkersInWhereClause replaces all markers' => array(
1164 ' AND dummytable.title=\'###REC_FIELD_dummyfield###\'' .
1165 ' AND dummytable.uid=###REC_FIELD_uid###' .
1166 ' AND dummytable.pid=###CURRENT_PID###' .
1167 ' AND dummytable.l18n_parent=###THIS_UID###' .
1168 ' AND dummytable.storage_pid=###STORAGE_PID###' .
1169 ' AND dummytable.siteroot=###SITEROOT###' .
1170 ' AND dummytable.config_uid=###PAGE_TSCONFIG_ID###' .
1171 ' AND dummytable.idlist IN (###PAGE_TSCONFIG_IDLIST###)' .
1172 ' AND dummytable.string=\'###PAGE_TSCONFIG_STR###\'',
1173 array(
1174 '_THIS_ROW' => array(
1175 'dummyfield' => 'Hello World',
1176 'uid' => 42
1177 ),
1178 '_CURRENT_PID' => '1',
1179 '_THIS_UID' => 2,
1180 '_STORAGE_PID' => 4,
1181 '_SITEROOT' => 5,
1182 'dummyfield' => array(
1183 'PAGE_TSCONFIG_ID' => 6,
1184 'PAGE_TSCONFIG_IDLIST' => '1,2,3',
1185 'PAGE_TSCONFIG_STR' => 'string'
1186 )
1187 ),
1188 ' AND dummytable.title=\'Hello World\' AND dummytable.uid=\'42\' AND dummytable.pid=1' .
1189 ' AND dummytable.l18n_parent=2 AND dummytable.storage_pid=4' .
1190 ' AND dummytable.siteroot=5 AND dummytable.config_uid=6 AND dummytable.idlist IN (1,2,3)' .
1191 ' AND dummytable.string=\'string\'',
1192 ),
1193 );
1194 }
1195
1196 /**
1197 * @test
1198 * @dataProvider replaceMarkersInWhereClauseDataProvider
1199 *
1200 * @param string $whereClause
1201 * @param array $tsConfig
1202 * @param string $expected
1203 *
1204 * @throws \PHPUnit_Framework_Exception
1205 */
1206 public function replaceMarkersInWhereClauseReturnsValidWhereClause($whereClause, array $tsConfig, $expected) {
1207 // Mock TYPO3_DB and let it return same values that came in
1208 $GLOBALS['TYPO3_DB'] = $this->getMock(DatabaseConnection::class, array(), array(), '', FALSE);
1209 $GLOBALS['TYPO3_DB']->expects($this->any())->method('quoteStr')->will($this->returnArgument(0));
1210 $GLOBALS['TYPO3_DB']->expects($this->any())->method('fullQuoteStr')
1211 ->will($this->returnCallback(
1212 function ($quoteStr) {
1213 return "'" . $quoteStr . "'";
1214 }
1215 )
1216 );
1217 $GLOBALS['TYPO3_DB']->expects($this->any())->method('cleanIntList')->will($this->returnArgument(0));
1218 $this->assertSame($expected, BackendUtility::replaceMarkersInWhereClause($whereClause, 'dummytable', 'dummyfield', $tsConfig));
1219 }
1220
1221 /**
1222 * @test
1223 */
1224 public function replaceMarkersInWhereClauseCleansIdList() {
1225 $GLOBALS['TYPO3_DB'] = $this->getMock(DatabaseConnection::class, array(), array(), '', FALSE);
1226 $GLOBALS['TYPO3_DB']->expects($this->once())->method('cleanIntList')->with('1,a,2,b,3,c');
1227 $where = ' AND dummytable.uid IN (###PAGE_TSCONFIG_IDLIST###)';
1228 $tsConfig = array(
1229 'dummyfield' => array(
1230 'PAGE_TSCONFIG_IDLIST' => '1,a,2,b,3,c'
1231 ),
1232 );
1233 BackendUtility::replaceMarkersInWhereClause($where, 'dummytable', 'dummyfield', $tsConfig);
1234 }
1235
1236 /**
1237 * @test
1238 */
1239 public function getModTSconfigIgnoresValuesFromUserTsConfigIfNoSet() {
1240 $completeConfiguration = array(
1241 'value' => 'bar',
1242 'properties' => array(
1243 'permissions.' => array(
1244 'file.' => array(
1245 'default.' => array('readAction' => '1'),
1246 '1.' => array('writeAction' => '1'),
1247 '0.' => array('readAction' => '0'),
1248 ),
1249 )
1250 )
1251 );
1252
1253 $GLOBALS['BE_USER'] = $this->getMock(BackendUserAuthentication::class, array(), array(), '', FALSE);
1254 $GLOBALS['BE_USER']->expects($this->at(0))->method('getTSConfig')->will($this->returnValue($completeConfiguration));
1255 $GLOBALS['BE_USER']->expects($this->at(1))->method('getTSConfig')->will($this->returnValue(array('value' => NULL, 'properties' => NULL)));
1256
1257 $className = $this->getUniqueId('BackendUtility');
1258 /** @var \PHPUnit_Framework_MockObject_MockObject|BackendUtility $subject */
1259 $subject = __NAMESPACE__ . '\\' . $className;
1260 eval(
1261 'namespace ' . __NAMESPACE__ . ';' .
1262 'class ' . $className . ' extends \\TYPO3\\CMS\\Backend\\Utility\\BackendUtility {' .
1263 ' static public function getPagesTSconfig($id, $rootLine = NULL, $returnPartArray = false) {' .
1264 ' return array();' .
1265 ' }' .
1266 '}'
1267 );
1268
1269 $this->assertSame($completeConfiguration, $subject::getModTSconfig(42, 'notrelevant'));
1270 }
1271
1272 /**
1273 * Data provider for replaceL10nModeFieldsReplacesFields
1274 *
1275 * @return array
1276 */
1277 public function replaceL10nModeFieldsReplacesFieldsDataProvider() {
1278 return array(
1279 'same table: mergeIfNotBlank' => array(
1280 'foo',
1281 array(
1282 'origUid' => 1,
1283 'field2' => 'fdas',
1284 'field3' => 'trans',
1285 ),
1286 array(
1287 'foo' => array(
1288 'ctrl' => array(
1289 'transOrigPointerTable' => '',
1290 'transOrigPointerField' => 'origUid'
1291 ),
1292 'columns' => array(
1293 'field2' => array('l10n_mode' => 'mergeIfNotBlank'),
1294 'field3' => array('l10n_mode' => 'mergeIfNotBlank')
1295 )
1296 )
1297 ),
1298 array(
1299 'origUid' => 0,
1300 'field2' => 'basic',
1301 'field3' => '',
1302 ),
1303 array(
1304 'origUid' => 1,
1305 'field2' => 'fdas',
1306 'field3' => 'trans',
1307 )
1308 ),
1309 'other table: mergeIfNotBlank' => array(
1310 'foo',
1311 array(
1312 'origUid' => 1,
1313 'field2' => '',
1314 'field3' => 'trans',
1315 ),
1316 array(
1317 'foo' => array(
1318 'ctrl' => array(
1319 'transOrigPointerTable' => 'bar',
1320 'transOrigPointerField' => 'origUid'
1321 )
1322 ),
1323 'bar' => array(
1324 'columns' => array(
1325 'field2' => array('l10n_mode' => 'mergeIfNotBlank'),
1326 'field3' => array('l10n_mode' => 'mergeIfNotBlank')
1327 )
1328 )
1329 ),
1330 array(
1331 'origUid' => 0,
1332 'field2' => 'basic',
1333 'field3' => '',
1334 ),
1335 array(
1336 'origUid' => 1,
1337 'field2' => 'basic',
1338 'field3' => 'trans',
1339 )
1340 ),
1341 'same table: exclude' => array(
1342 'foo',
1343 array(
1344 'origUid' => 1,
1345 'field2' => 'fdas',
1346 'field3' => 'trans',
1347 ),
1348 array(
1349 'foo' => array(
1350 'ctrl' => array(
1351 'transOrigPointerTable' => '',
1352 'transOrigPointerField' => 'origUid'
1353 ),
1354 'columns' => array(
1355 'field2' => array('l10n_mode' => 'exclude'),
1356 'field3' => array('l10n_mode' => 'exclude')
1357 )
1358 )
1359 ),
1360 array(
1361 'origUid' => 0,
1362 'field2' => 'basic',
1363 'field3' => '',
1364 ),
1365 array(
1366 'origUid' => 1,
1367 'field2' => 'basic',
1368 'field3' => '',
1369 )
1370 ),
1371 'other table: exclude' => array(
1372 'foo',
1373 array(
1374 'origUid' => 1,
1375 'field2' => 'fdas',
1376 'field3' => 'trans',
1377 ),
1378 array(
1379 'foo' => array(
1380 'ctrl' => array(
1381 'transOrigPointerTable' => 'bar',
1382 'transOrigPointerField' => 'origUid'
1383 )
1384 ),
1385 'bar' => array(
1386 'columns' => array(
1387 'field2' => array('l10n_mode' => 'exclude'),
1388 'field3' => array('l10n_mode' => 'exclude')
1389 )
1390 )
1391 ),
1392 array(
1393 'origUid' => 0,
1394 'field2' => 'basic',
1395 'field3' => '',
1396 ),
1397 array(
1398 'origUid' => 1,
1399 'field2' => 'basic',
1400 'field3' => '',
1401 )
1402 ),
1403 );
1404 }
1405
1406 /**
1407 * @test
1408 * @dataProvider replaceL10nModeFieldsReplacesFieldsDataProvider
1409 *
1410 * @param string $table
1411 * @param array $row
1412 * @param array $tca
1413 * @param array $originalRow
1414 * @param array $expected
1415 *
1416 * @throws \InvalidArgumentException
1417 * @throws \PHPUnit_Framework_Exception
1418 */
1419 public function replaceL10nModeFieldsReplacesFields($table, array $row, array $tca, array $originalRow, $expected) {
1420 $GLOBALS['TCA'] = $tca;
1421 $GLOBALS['TYPO3_DB'] = $this->getMock(DatabaseConnection::class);
1422 $GLOBALS['TYPO3_DB']->expects($this->any())->method('exec_SELECTgetSingleRow')->will($this->returnValue($originalRow));
1423
1424 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|BackendUtility $subject */
1425 $subject = $this->getAccessibleMock(BackendUtility::class, array('dummy'));
1426 $this->assertSame($expected, $subject->_call('replaceL10nModeFields', $table, $row));
1427 }
1428
1429 /**
1430 * @test
1431 */
1432 public function getSpecConfPartsSplitsDefaultExtras() {
1433 $defaultExtras = 'nowrap:wizards[foo|bar]:anotherDefaultExtras:some[other|setting|with|parameters]';
1434 $expected = array(
1435 'nowrap' => 1,
1436 'wizards' => array(
1437 'parameters' => array(
1438 0 => 'foo',
1439 1 => 'bar',
1440 ),
1441 ),
1442 'anotherDefaultExtras' => 1,
1443 'some' => array(
1444 'parameters' => array(
1445 0 => 'other',
1446 1 => 'setting',
1447 2 => 'with',
1448 3 => 'parameters',
1449 ),
1450 ),
1451 );
1452 $this->assertEquals($expected, BackendUtility::getSpecConfParts($defaultExtras));
1453 }
1454 }