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