2 namespace TYPO3\CMS\Frontend\Tests\Unit\ContentObject
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use Prophecy\Argument
;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
19 use TYPO3\CMS\Core\Cache\CacheManager
;
20 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
as CacheFrontendInterface
;
21 use TYPO3\CMS\Core\Context\Context
;
22 use TYPO3\CMS\Core\Context\UserAspect
;
23 use TYPO3\CMS\Core\Core\ApplicationContext
;
24 use TYPO3\CMS\Core\Core\Environment
;
25 use TYPO3\CMS\Core\LinkHandling\LinkService
;
26 use TYPO3\CMS\Core\Log\Logger
;
27 use TYPO3\CMS\Core\Resource\Exception\InvalidPathException
;
28 use TYPO3\CMS\Core\Resource\File
;
29 use TYPO3\CMS\Core\Resource\ResourceFactory
;
30 use TYPO3\CMS\Core\Resource\ResourceStorage
;
31 use TYPO3\CMS\Core\TimeTracker\TimeTracker
;
32 use TYPO3\CMS\Core\TypoScript\TemplateService
;
33 use TYPO3\CMS\Core\Utility\DebugUtility
;
34 use TYPO3\CMS\Core\Utility\GeneralUtility
;
35 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
;
36 use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject
;
37 use TYPO3\CMS\Frontend\ContentObject\CaseContentObject
;
38 use TYPO3\CMS\Frontend\ContentObject\ContentContentObject
;
39 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayContentObject
;
40 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayInternalContentObject
;
41 use TYPO3\CMS\Frontend\ContentObject\ContentObjectOneSourceCollectionHookInterface
;
42 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
;
43 use TYPO3\CMS\Frontend\ContentObject\ContentObjectStdWrapHookInterface
;
44 use TYPO3\CMS\Frontend\ContentObject\EditPanelContentObject
;
45 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException
;
46 use TYPO3\CMS\Frontend\ContentObject\FileContentObject
;
47 use TYPO3\CMS\Frontend\ContentObject\FilesContentObject
;
48 use TYPO3\CMS\Frontend\ContentObject\FluidTemplateContentObject
;
49 use TYPO3\CMS\Frontend\ContentObject\HierarchicalMenuContentObject
;
50 use TYPO3\CMS\Frontend\ContentObject\ImageContentObject
;
51 use TYPO3\CMS\Frontend\ContentObject\ImageResourceContentObject
;
52 use TYPO3\CMS\Frontend\ContentObject\LoadRegisterContentObject
;
53 use TYPO3\CMS\Frontend\ContentObject\RecordsContentObject
;
54 use TYPO3\CMS\Frontend\ContentObject\RestoreRegisterContentObject
;
55 use TYPO3\CMS\Frontend\ContentObject\ScalableVectorGraphicsContentObject
;
56 use TYPO3\CMS\Frontend\ContentObject\TemplateContentObject
;
57 use TYPO3\CMS\Frontend\ContentObject\TextContentObject
;
58 use TYPO3\CMS\Frontend\ContentObject\UserContentObject
;
59 use TYPO3\CMS\Frontend\ContentObject\UserInternalContentObject
;
60 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
;
61 use TYPO3\CMS\Frontend\Page\PageRepository
;
62 use TYPO3\TestingFramework\Core\Unit\UnitTestCase
;
67 class ContentObjectRendererTest
extends UnitTestCase
70 * Subject is not notice free, disable E_NOTICES
72 protected static $suppressNotices = true
;
75 * @var bool Reset singletons created by subject
77 protected $resetSingletonInstances = true
;
80 * @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
85 * @var \PHPUnit_Framework_MockObject_MockObject|TypoScriptFrontendController|\TYPO3\TestingFramework\Core\AccessibleObjectInterface
87 protected $frontendControllerMock;
90 * @var \PHPUnit_Framework_MockObject_MockObject|CacheFrontendInterface
92 protected $cacheFrontendMock;
95 * @var \PHPUnit_Framework_MockObject_MockObject|TemplateService
97 protected $templateServiceMock;
100 * Default content object name -> class name map, shipped with TYPO3 CMS
104 protected $contentObjectMap = [
105 'TEXT' => TextContentObject
::class,
106 'CASE' => CaseContentObject
::class,
107 'COBJ_ARRAY' => ContentObjectArrayContentObject
::class,
108 'COA' => ContentObjectArrayContentObject
::class,
109 'COA_INT' => ContentObjectArrayInternalContentObject
::class,
110 'USER' => UserContentObject
::class,
111 'USER_INT' => UserInternalContentObject
::class,
112 'FILE' => FileContentObject
::class,
113 'FILES' => FilesContentObject
::class,
114 'IMAGE' => ImageContentObject
::class,
115 'IMG_RESOURCE' => ImageResourceContentObject
::class,
116 'CONTENT' => ContentContentObject
::class,
117 'RECORDS' => RecordsContentObject
::class,
118 'HMENU' => HierarchicalMenuContentObject
::class,
119 'CASEFUNC' => CaseContentObject
::class,
120 'LOAD_REGISTER' => LoadRegisterContentObject
::class,
121 'RESTORE_REGISTER' => RestoreRegisterContentObject
::class,
122 'TEMPLATE' => TemplateContentObject
::class,
123 'FLUIDTEMPLATE' => FluidTemplateContentObject
::class,
124 'SVG' => ScalableVectorGraphicsContentObject
::class,
125 'EDITPANEL' => EditPanelContentObject
::class
131 protected function setUp()
133 $this->templateServiceMock
=
134 $this->getMockBuilder(TemplateService
::class)
135 ->setMethods(['getFileName', 'linkData'])->getMock();
136 $pageRepositoryMock =
137 $this->getAccessibleMock(PageRepository
::class, ['getRawRecord', 'getMountPointInfo']);
138 $this->frontendControllerMock
=
139 $this->getAccessibleMock(
140 TypoScriptFrontendController
::class,
146 $this->frontendControllerMock
->_set('context', GeneralUtility
::makeInstance(Context
::class));
147 $this->frontendControllerMock
->tmpl
= $this->templateServiceMock
;
148 $this->frontendControllerMock
->config
= [];
149 $this->frontendControllerMock
->page
= [];
150 $this->frontendControllerMock
->sys_page
= $pageRepositoryMock;
151 $GLOBALS['TSFE'] = $this->frontendControllerMock
;
153 $this->subject
= $this->getAccessibleMock(
154 ContentObjectRenderer
::class,
155 ['getResourceFactory', 'getEnvironmentVariable'],
156 [$this->frontendControllerMock
]
159 $logger = $this->prophesize(Logger
::class);
160 $this->subject
->setLogger($logger->reveal());
161 $this->subject
->setContentObjectClassMap($this->contentObjectMap
);
162 $this->subject
->start([], 'tt_content');
165 //////////////////////
167 //////////////////////
170 * @return TypoScriptFrontendController
172 protected function getFrontendController()
174 return $GLOBALS['TSFE'];
178 * Converts the subject and the expected result into utf-8.
180 * @param string $subject the subject, will be modified
181 * @param string $expected the expected result, will be modified
183 protected function handleCharset(&$subject, &$expected)
185 $subject = mb_convert_encoding($subject, 'utf-8', 'iso-8859-1');
186 $expected = mb_convert_encoding($expected, 'utf-8', 'iso-8859-1');
189 /////////////////////////////////////////////
190 // Tests concerning the getImgResource hook
191 /////////////////////////////////////////////
195 public function getImgResourceCallsGetImgResourcePostProcessHook()
197 $this->templateServiceMock
198 ->expects($this->atLeastOnce())
199 ->method('getFileName')
200 ->with('typo3/clear.gif')
201 ->will($this->returnValue('typo3/clear.gif'));
203 $resourceFactory = $this->createMock(ResourceFactory
::class);
204 $this->subject
->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
206 $className = $this->getUniqueId('tx_coretest');
207 $getImgResourceHookMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\ContentObject\ContentObjectGetImageResourceHookInterface
::class)
208 ->setMethods(['getImgResourcePostProcess'])
209 ->setMockClassName($className)
211 $getImgResourceHookMock
212 ->expects($this->once())
213 ->method('getImgResourcePostProcess')
214 ->will($this->returnCallback([$this, 'isGetImgResourceHookCalledCallback']));
215 $getImgResourceHookObjects = [$getImgResourceHookMock];
216 $this->subject
->_setRef('getImgResourceHookObjects', $getImgResourceHookObjects);
217 $this->subject
->getImgResource('typo3/clear.gif', []);
221 * Handles the arguments that have been sent to the getImgResource hook.
223 * @param string $file
224 * @param array $fileArray
225 * @param $imageResource
226 * @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $parent
228 * @see getImgResourceHookGetsCalled
230 public function isGetImgResourceHookCalledCallback($file, $fileArray, $imageResource, $parent)
232 $this->assertEquals('typo3/clear.gif', $file);
233 $this->assertEquals('typo3/clear.gif', $imageResource['origFile']);
234 $this->assertTrue(is_array($fileArray));
235 $this->assertTrue($parent instanceof ContentObjectRenderer
);
236 return $imageResource;
239 //////////////////////////////////////
240 // Tests related to getContentObject
241 //////////////////////////////////////
244 * Show registration of a class for a TypoScript object name and getting
245 * the registered content object is working.
247 * Prove is done by successfully creating an object based on the mapping.
248 * Note two conditions in contrast to other tests, where the creation
251 * 1. The type must be of AbstractContentObject.
252 * 2. Registration can only be done by public methods.
256 public function canRegisterAContentObjectClassForATypoScriptName()
258 $className = TextContentObject
::class;
259 $contentObjectName = 'TEST_TEXT';
260 $this->subject
->registerContentObjectClass(
264 $object = $this->subject
->getContentObject($contentObjectName);
265 $this->assertInstanceOf($className, $object);
269 * Show that setting of the class map and getting a registered content
272 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
275 public function canSetTheContentObjectClassMapAndGetARegisteredContentObject()
277 $className = TextContentObject
::class;
278 $contentObjectName = 'TEST_TEXT';
279 $classMap = [$contentObjectName => $className];
280 $this->subject
->setContentObjectClassMap($classMap);
281 $object = $this->subject
->getContentObject($contentObjectName);
282 $this->assertInstanceOf($className, $object);
286 * Show that the map is not set as an externally accessible reference.
288 * Prove is done by missing success when trying to use it this way.
290 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
293 public function canNotAccessInternalContentObjectMapByReference()
295 $className = TextContentObject
::class;
296 $contentObjectName = 'TEST_TEXT';
298 $this->subject
->setContentObjectClassMap($classMap);
299 $classMap[$contentObjectName] = $className;
300 $object = $this->subject
->getContentObject($contentObjectName);
301 $this->assertNull($object);
305 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
308 public function willReturnNullForUnregisteredObject()
310 $object = $this->subject
->getContentObject('FOO');
311 $this->assertNull($object);
315 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
318 public function willThrowAnExceptionForARegisteredNonContentObject()
320 $this->expectException(ContentRenderingException
::class);
321 $this->subject
->registerContentObjectClass(
325 $this->subject
->getContentObject('STDCLASS');
329 * @return string[][] [[$name, $fullClassName],]
331 public function registersAllDefaultContentObjectsDataProvider(): array
334 foreach ($this->contentObjectMap
as $name => $className) {
335 $dataProvider[] = [$name, $className];
337 return $dataProvider;
341 * Prove that all content objects are registered and a class is available
345 * @dataProvider registersAllDefaultContentObjectsDataProvider
346 * @param string $objectName TypoScript name of content object
347 * @param string $className Expected class name
349 public function registersAllDefaultContentObjects(
354 is_subclass_of($className, AbstractContentObject
::class)
356 $object = $this->subject
->getContentObject($objectName);
357 $this->assertInstanceOf($className, $object);
360 /////////////////////////////////////////
361 // Tests concerning getQueryArguments()
362 /////////////////////////////////////////
366 public function getQueryArgumentsExcludesParameters()
368 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
369 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
371 $getQueryArgumentsConfiguration = [];
372 $getQueryArgumentsConfiguration['exclude'] = [];
373 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
374 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
375 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
376 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
377 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
378 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
379 $this->assertEquals($expectedResult, $actualResult);
385 public function getQueryArgumentsExcludesGetParameters()
391 'key31' => 'value31',
393 'key321' => 'value321',
394 'key322' => 'value322'
398 $getQueryArgumentsConfiguration = [];
399 $getQueryArgumentsConfiguration['method'] = 'GET';
400 $getQueryArgumentsConfiguration['exclude'] = [];
401 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
402 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
403 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
404 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
405 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
406 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
407 $this->assertEquals($expectedResult, $actualResult);
413 public function getQueryArgumentsOverrulesSingleParameter()
415 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
416 $this->returnValue('key1=value1')
418 $getQueryArgumentsConfiguration = [];
419 $overruleArguments = [
420 // Should be overridden
421 'key1' => 'value1Overruled',
422 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
423 'key2' => 'value2Overruled'
425 $expectedResult = '&key1=value1Overruled';
426 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
427 $this->assertEquals($expectedResult, $actualResult);
433 public function getQueryArgumentsOverrulesMultiDimensionalParameters()
439 'key31' => 'value31',
441 'key321' => 'value321',
442 'key322' => 'value322'
446 $getQueryArgumentsConfiguration = [];
447 $getQueryArgumentsConfiguration['method'] = 'POST';
448 $getQueryArgumentsConfiguration['exclude'] = [];
449 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
450 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
451 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
452 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
453 $overruleArguments = [
454 // Should be overridden
455 'key2' => 'value2Overruled',
458 // Shouldn't be set: Parameter is excluded and not forced
459 'key321' => 'value321Overruled',
460 // Should be overridden: Parameter is not excluded
461 'key322' => 'value322Overruled',
462 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
463 'key323' => 'value323Overruled'
467 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key322]=value322Overruled');
468 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
469 $this->assertEquals($expectedResult, $actualResult);
475 public function getQueryArgumentsOverrulesMultiDimensionalForcedParameters()
477 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
478 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
484 'key31' => 'value31',
486 'key321' => 'value321',
487 'key322' => 'value322'
491 $getQueryArgumentsConfiguration = [];
492 $getQueryArgumentsConfiguration['exclude'] = [];
493 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
494 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
495 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
496 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key322]';
497 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
498 $overruleArguments = [
499 // Should be overridden
500 'key2' => 'value2Overruled',
503 // Should be set: Parameter is excluded but forced
504 'key321' => 'value321Overruled',
505 // Should be set: Parameter doesn't exist in source array but is forced
506 'key323' => 'value323Overruled'
510 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key321]=value321Overruled&key3[key32][key323]=value323Overruled');
511 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true
);
512 $this->assertEquals($expectedResult, $actualResult);
513 $getQueryArgumentsConfiguration['method'] = 'POST';
514 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true
);
515 $this->assertEquals($expectedResult, $actualResult);
521 public function getQueryArgumentsWithMethodPostGetMergesParameters()
530 'key331' => 'POST331',
531 'key332' => 'POST332',
540 'key331' => 'GET331',
544 $getQueryArgumentsConfiguration = [];
545 $getQueryArgumentsConfiguration['method'] = 'POST,GET';
546 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
547 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
548 $this->assertEquals($expectedResult, $actualResult);
554 public function getQueryArgumentsWithMethodGetPostMergesParameters()
563 'key331' => 'GET331',
564 'key332' => 'GET332',
573 'key331' => 'POST331',
577 $getQueryArgumentsConfiguration = [];
578 $getQueryArgumentsConfiguration['method'] = 'GET,POST';
579 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
580 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
581 $this->assertEquals($expectedResult, $actualResult);
585 * Encodes square brackets in URL.
587 * @param string $string
590 private function rawUrlEncodeSquareBracketsInUrl($string)
592 return str_replace(['[', ']'], ['%5B', '%5D'], $string);
595 //////////////////////////
596 // Tests concerning crop
597 //////////////////////////
601 public function cropIsMultibyteSafe()
603 $this->assertEquals('бла', $this->subject
->crop('бла', '3|...'));
606 //////////////////////////////
608 //////////////////////////////
609 // Tests concerning cropHTML
610 //////////////////////////////
613 * Data provider for cropHTML.
615 * Provides combinations of text type and configuration.
617 * @return array [$expect, $conf, $content]
619 public function cropHTMLDataProvider()
621 $plainText = 'Kasper Sk' . chr(229) . 'rh' . chr(248)
622 . 'j implemented the original version of the crop function.';
623 $textWithMarkup = '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
624 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> the '
625 . 'original version of the crop function.';
626 $textWithEntities = 'Kasper Skårhøj implemented the; '
627 . 'original ' . 'version of the crop function.';
628 $textWithLinebreaks = "Lorem ipsum dolor sit amet,\n"
629 . "consetetur sadipscing elitr,\n"
630 . 'sed diam nonumy eirmod tempor invidunt ut labore e'
631 . 't dolore magna aliquyam';
634 'plain text; 11|...' => [
635 'Kasper Sk' . chr(229) . 'r...',
636 $plainText, '11|...',
638 'plain text; -58|...' => [
639 '...h' . chr(248) . 'j implemented the original version of '
640 . 'the crop function.',
641 $plainText, '-58|...',
643 'plain text; 4|...|1' => [
645 $plainText, '4|...|1',
647 'plain text; 20|...|1' => [
648 'Kasper Sk' . chr(229) . 'rh' . chr(248) . 'j...',
649 $plainText, '20|...|1',
651 'plain text; -5|...|1' => [
653 $plainText, '-5|...|1',
655 'plain text; -49|...|1' => [
656 '...the original version of the crop function.',
657 $plainText, '-49|...|1',
659 'text with markup; 11|...' => [
660 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
661 . chr(229) . 'r...</a></strong>',
662 $textWithMarkup, '11|...',
664 'text with markup; 13|...' => [
665 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
666 . chr(229) . 'rh' . chr(248) . '...</a></strong>',
667 $textWithMarkup, '13|...',
669 'text with markup; 14|...' => [
670 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
671 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
672 $textWithMarkup, '14|...',
674 'text with markup; 15|...' => [
675 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
676 . chr(229) . 'rh' . chr(248) . 'j</a> ...</strong>',
677 $textWithMarkup, '15|...',
679 'text with markup; 29|...' => [
680 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
681 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> '
683 $textWithMarkup, '29|...',
685 'text with markup; -58|...' => [
686 '<strong><a href="mailto:kasper@typo3.org">...h' . chr(248)
687 . 'j</a> implemented</strong> the original version of the crop '
689 $textWithMarkup, '-58|...',
691 'text with markup 4|...|1' => [
692 '<strong><a href="mailto:kasper@typo3.org">Kasp...</a>'
694 $textWithMarkup, '4|...|1',
696 'text with markup; 11|...|1' => [
697 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
699 $textWithMarkup, '11|...|1',
701 'text with markup; 13|...|1' => [
702 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
704 $textWithMarkup, '13|...|1',
706 'text with markup; 14|...|1' => [
707 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
708 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
709 $textWithMarkup, '14|...|1',
711 'text with markup; 15|...|1' => [
712 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
713 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
714 $textWithMarkup, '15|...|1',
716 'text with markup; 29|...|1' => [
717 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
718 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong>...',
719 $textWithMarkup, '29|...|1',
721 'text with markup; -66|...|1' => [
722 '<strong><a href="mailto:kasper@typo3.org">...Sk' . chr(229)
723 . 'rh' . chr(248) . 'j</a> implemented</strong> the original v'
724 . 'ersion of the crop function.',
725 $textWithMarkup, '-66|...|1',
727 'text with entities 9|...' => [
729 $textWithEntities, '9|...',
731 'text with entities 10|...' => [
732 'Kasper Skå...',
733 $textWithEntities, '10|...',
735 'text with entities 11|...' => [
736 'Kasper Skår...',
737 $textWithEntities, '11|...',
739 'text with entities 13|...' => [
740 'Kasper Skårhø...',
741 $textWithEntities, '13|...',
743 'text with entities 14|...' => [
744 'Kasper Skårhøj...',
745 $textWithEntities, '14|...',
747 'text with entities 15|...' => [
748 'Kasper Skårhøj ...',
749 $textWithEntities, '15|...',
751 'text with entities 16|...' => [
752 'Kasper Skårhøj i...',
753 $textWithEntities, '16|...',
755 'text with entities -57|...' => [
756 '...j implemented the; original version of the crop function.',
757 $textWithEntities, '-57|...',
759 'text with entities -58|...' => [
760 '...øj implemented the; original version of the crop '
762 $textWithEntities, '-58|...',
764 'text with entities -59|...' => [
765 '...høj implemented the; original version of the crop '
767 $textWithEntities, '-59|...',
769 'text with entities 4|...|1' => [
771 $textWithEntities, '4|...|1',
773 'text with entities 9|...|1' => [
775 $textWithEntities, '9|...|1',
777 'text with entities 10|...|1' => [
779 $textWithEntities, '10|...|1',
781 'text with entities 11|...|1' => [
783 $textWithEntities, '11|...|1',
785 'text with entities 13|...|1' => [
787 $textWithEntities, '13|...|1',
789 'text with entities 14|...|1' => [
790 'Kasper Skårhøj...',
791 $textWithEntities, '14|...|1',
793 'text with entities 15|...|1' => [
794 'Kasper Skårhøj...',
795 $textWithEntities, '15|...|1',
797 'text with entities 16|...|1' => [
798 'Kasper Skårhøj...',
799 $textWithEntities, '16|...|1',
801 'text with entities -57|...|1' => [
802 '...implemented the; original version of the crop function.',
803 $textWithEntities, '-57|...|1',
805 'text with entities -58|...|1' => [
806 '...implemented the; original version of the crop function.',
807 $textWithEntities, '-58|...|1',
809 'text with entities -59|...|1' => [
810 '...implemented the; original version of the crop function.',
811 $textWithEntities, '-59|...|1',
813 'text with dash in html-element 28|...|1' => [
814 'Some text with a link to <link email.address@example.org - '
815 . 'mail "Open email window">my...</link>',
816 'Some text with a link to <link email.address@example.org - m'
817 . 'ail "Open email window">my email.address@example.org<'
818 . '/link> and text after it',
821 'html elements with dashes in attributes' => [
822 '<em data-foo="x">foobar</em>foo',
823 '<em data-foo="x">foobar</em>foobaz',
826 'html elements with iframe embedded 24|...|1' => [
827 'Text with iframe <iframe src="//what.ever/"></iframe> and...',
828 'Text with iframe <iframe src="//what.ever/">'
829 . '</iframe> and text after it',
832 'html elements with script tag embedded 24|...|1' => [
833 'Text with script <script>alert(\'foo\');</script> and...',
834 'Text with script <script>alert(\'foo\');</script> '
835 . 'and text after it',
838 'text with linebreaks' => [
839 "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\ns"
840 . 'ed diam nonumy eirmod tempor invidunt ut labore e'
842 $textWithLinebreaks, '121',
848 * Check if cropHTML works properly.
851 * @dataProvider cropHTMLDataProvider
852 * @param string $expect The expected cropped output.
853 * @param string $content The given input.
854 * @param string $conf The given configuration.
856 public function cropHTML($expect, $content, $conf)
858 $this->handleCharset($content, $expect);
861 $this->subject
->cropHTML($content, $conf)
866 * Data provider for round
868 * @return array [$expect, $contet, $conf]
870 public function roundDataProvider()
874 'down' => [1.0, 1.11, []],
875 'up' => [2.0, 1.51, []],
876 'rounds up from x.50' => [2.0, 1.50, []],
877 'down with decimals' => [0.12, 0.1231, ['decimals' => 2]],
878 'up with decimals' => [0.13, 0.1251, ['decimals' => 2]],
879 'ceil' => [1.0, 0.11, ['roundType' => 'ceil']],
880 'ceil does not accept decimals' => [
882 'roundType' => 'ceil',
886 'floor' => [2.0, 2.99, ['roundType' => 'floor']],
887 'floor does not accept decimals' => [
889 'roundType' => 'floor',
893 'round, down' => [1.0, 1.11, ['roundType' => 'round']],
894 'round, up' => [2.0, 1.55, ['roundType' => 'round']],
895 'round does accept decimals' => [
897 'roundType' => 'round',
902 'emtpy string' => [0.0, '', []],
903 'word string' => [0.0, 'word', []],
904 'float string' => [1.0, '1.123456789', []],
906 'null' => [0.0, null
, []],
907 'false' => [0.0, false
, []],
908 'true' => [1.0, true
, []]
913 * Check if round works properly
917 * - Different types of input are casted to float.
918 * - Configuration ceil rounds like ceil().
919 * - Configuration floor rounds like floor().
920 * - Otherwise rounds like round() and decimals can be applied.
921 * - Always returns float.
923 * @param float $expect The expected output.
924 * @param mixed $content The given content.
925 * @param array $conf The given configuration of 'round.'.
926 * @dataProvider roundDataProvider
929 public function round($expect, $content, $conf)
933 $this->subject
->_call('round', $content, $conf)
940 public function recursiveStdWrapProperlyRendersBasicString()
942 $stdWrapConfiguration = [
943 'noTrimWrap' => '|| 123|',
950 $this->subject
->stdWrap('Test', $stdWrapConfiguration)
957 public function recursiveStdWrapIsOnlyCalledOnce()
959 $stdWrapConfiguration = [
962 'data' => 'register:Counter'
965 'append' => 'LOAD_REGISTER',
968 'prioriCalc' => 'intval',
971 'data' => 'register:Counter',
980 $this->subject
->stdWrap('Counter:', $stdWrapConfiguration)
985 * Data provider for numberFormat.
987 * @return array [$expect, $content, $conf]
989 public function numberFormatDataProvider()
992 'testing decimals' => [
996 'testing decimals with input as string' => [
1000 'testing dec_point' => [
1002 ['decimals' => 1, 'dec_point' => ',']
1004 'testing thousands_sep' => [
1008 'thousands_sep.' => ['char' => 46]
1011 'testing mixture' => [
1012 '1.281.731,5', 1281731.45,
1015 'dec_point.' => ['char' => 44],
1016 'thousands_sep.' => ['char' => 46]
1023 * Check if numberFormat works properly.
1025 * @dataProvider numberFormatDataProvider
1028 public function numberFormat($expects, $content, $conf)
1032 $this->subject
->numberFormat($content, $conf)
1037 * Data provider replacement
1039 * @return array [$expect, $content, $conf]
1041 public function replacementDataProvider()
1044 'multiple replacements, including regex' => [
1045 'There is an animal, an animal and an animal around the block! Yeah!',
1046 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
1050 'replace.' => ['char' => '32']
1053 'search' => 'in da hood',
1054 'replace' => 'around the block'
1057 'search' => '#a (Cat|Dog|Tiger)#i',
1058 'replace' => 'an animal',
1063 'replacement with optionSplit, normal pattern' => [
1064 'There1is2a3cat,3a3dog3and3a3tiger3in3da3hood!3Yeah!',
1065 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
1069 'replace' => '1 || 2 || 3',
1070 'useOptionSplitReplace' => '1'
1074 'replacement with optionSplit, using regex' => [
1075 'There is a tiny cat, a midsized dog and a big tiger in da hood! Yeah!',
1076 'There is a cat, a dog and a tiger in da hood! Yeah!',
1079 'search' => '#(a) (Cat|Dog|Tiger)#i',
1080 'replace' => '${1} tiny ${2} || ${1} midsized ${2} || ${1} big ${2}',
1081 'useOptionSplitReplace' => '1',
1090 * Check if stdWrap.replacement and all of its properties work properly
1093 * @dataProvider replacementDataProvider
1094 * @param string $content The given input.
1095 * @param string $expects The expected result.
1096 * @param array $conf The given configuration.
1098 public function replacement($expects, $content, $conf)
1102 $this->subject
->_call('replacement', $content, $conf)
1107 * Data provider for calcAge.
1109 * @return array [$expect, $timestamp, $labels]
1111 public function calcAgeDataProvider()
1115 '2 min', 120, ' min| hrs| days| yrs',
1118 '2 hrs', 7200, ' min| hrs| days| yrs',
1121 '7 days', 604800, ' min| hrs| days| yrs',
1123 'day with provided singular labels' => [
1124 '1 day', 86400, ' min| hrs| days| yrs| min| hour| day| year',
1127 '45 yrs', 1417997800, ' min| hrs| days| yrs',
1129 'different labels' => [
1130 '2 Minutes', 120, ' Minutes| Hrs| Days| Yrs',
1132 'negative values' => [
1133 '-7 days', -604800, ' min| hrs| days| yrs',
1135 'default label values for wrong label input' => [
1138 'default singular label values for wrong label input' => [
1139 '1 year', 31536000, 10,
1145 * Check if calcAge works properly.
1148 * @dataProvider calcAgeDataProvider
1149 * @param int $expect
1150 * @param int $timestamp
1151 * @param string $labels
1153 public function calcAge($expect, $timestamp, $labels)
1157 $this->subject
->calcAge($timestamp, $labels)
1164 public function stdWrapReturnsExpectationDataProvider()
1167 'Prevent silent bool conversion' => [
1180 * @param string $content
1181 * @param array $configuration
1182 * @param string $expectation
1183 * @dataProvider stdWrapReturnsExpectationDataProvider
1186 public function stdWrapReturnsExpectation($content, array $configuration, $expectation)
1188 $this->assertSame($expectation, $this->subject
->stdWrap($content, $configuration));
1192 * Data provider for substring
1194 * @return array [$expect, $content, $conf]
1196 public function substringDataProvider()
1199 'sub -1' => ['g', 'substring', '-1'],
1200 'sub -1,0' => ['g', 'substring', '-1,0'],
1201 'sub -1,-1' => ['', 'substring', '-1,-1'],
1202 'sub -1,1' => ['g', 'substring', '-1,1'],
1203 'sub 0' => ['substring', 'substring', '0'],
1204 'sub 0,0' => ['substring', 'substring', '0,0'],
1205 'sub 0,-1' => ['substrin', 'substring', '0,-1'],
1206 'sub 0,1' => ['s', 'substring', '0,1'],
1207 'sub 1' => ['ubstring', 'substring', '1'],
1208 'sub 1,0' => ['ubstring', 'substring', '1,0'],
1209 'sub 1,-1' => ['ubstrin', 'substring', '1,-1'],
1210 'sub 1,1' => ['u', 'substring', '1,1'],
1211 'sub' => ['substring', 'substring', ''],
1216 * Check if substring works properly.
1219 * @dataProvider substringDataProvider
1220 * @param string $expect The expected output.
1221 * @param string $content The given input.
1222 * @param array $conf The given configutation.
1224 public function substring($expect, $content, $conf)
1226 $this->assertSame($expect, $this->subject
->substring($content, $conf));
1229 ///////////////////////////////
1230 // Tests concerning getData()
1231 ///////////////////////////////
1236 public function getDataWithTypeGpDataProvider()
1239 'Value in get-data' => ['onlyInGet', 'GetValue'],
1240 'Value in post-data' => ['onlyInPost', 'PostValue'],
1241 'Value in post-data overriding get-data' => ['inGetAndPost', 'ValueInPost'],
1246 * Checks if getData() works with type "gp"
1249 * @dataProvider getDataWithTypeGpDataProvider
1251 public function getDataWithTypeGp($key, $expectedValue)
1254 'onlyInGet' => 'GetValue',
1255 'inGetAndPost' => 'ValueInGet',
1258 'onlyInPost' => 'PostValue',
1259 'inGetAndPost' => 'ValueInPost',
1261 $this->assertEquals($expectedValue, $this->subject
->getData('gp:' . $key));
1265 * Checks if getData() works with type "tsfe"
1269 public function getDataWithTypeTsfe()
1271 $this->assertEquals($GLOBALS['TSFE']->metaCharset
, $this->subject
->getData('tsfe:metaCharset'));
1275 * Checks if getData() works with type "getenv"
1279 public function getDataWithTypeGetenv()
1281 $envName = $this->getUniqueId('frontendtest');
1282 $value = $this->getUniqueId('someValue');
1283 putenv($envName . '=' . $value);
1284 $this->assertEquals($value, $this->subject
->getData('getenv:' . $envName));
1288 * Checks if getData() works with type "getindpenv"
1292 public function getDataWithTypeGetindpenv()
1294 $this->subject
->expects($this->once())->method('getEnvironmentVariable')
1295 ->with($this->equalTo('SCRIPT_FILENAME'))->will($this->returnValue('dummyPath'));
1296 $this->assertEquals('dummyPath', $this->subject
->getData('getindpenv:SCRIPT_FILENAME'));
1300 * Checks if getData() works with type "field"
1304 public function getDataWithTypeField()
1307 $value = 'someValue';
1308 $field = [$key => $value];
1310 $this->assertEquals($value, $this->subject
->getData('field:' . $key, $field));
1314 * Checks if getData() works with type "field" of the field content
1315 * is multi-dimensional (e.g. an array)
1319 public function getDataWithTypeFieldAndFieldIsMultiDimensional()
1321 $key = 'somekey|level1|level2';
1322 $value = 'somevalue';
1323 $field = ['somekey' => ['level1' => ['level2' => 'somevalue']]];
1325 $this->assertEquals($value, $this->subject
->getData('field:' . $key, $field));
1329 * Basic check if getData gets the uid of a file object
1333 public function getDataWithTypeFileReturnsUidOfFileObject()
1335 $uid = $this->getUniqueId();
1336 $file = $this->createMock(File
::class);
1337 $file->expects($this->once())->method('getUid')->will($this->returnValue($uid));
1338 $this->subject
->setCurrentFile($file);
1339 $this->assertEquals($uid, $this->subject
->getData('file:current:uid'));
1343 * Checks if getData() works with type "parameters"
1347 public function getDataWithTypeParameters()
1349 $key = $this->getUniqueId('someKey');
1350 $value = $this->getUniqueId('someValue');
1351 $this->subject
->parameters
[$key] = $value;
1353 $this->assertEquals($value, $this->subject
->getData('parameters:' . $key));
1357 * Checks if getData() works with type "register"
1361 public function getDataWithTypeRegister()
1363 $key = $this->getUniqueId('someKey');
1364 $value = $this->getUniqueId('someValue');
1365 $GLOBALS['TSFE']->register
[$key] = $value;
1367 $this->assertEquals($value, $this->subject
->getData('register:' . $key));
1371 * Checks if getData() works with type "session"
1375 public function getDataWithTypeSession()
1377 $frontendUser = $this->getMockBuilder(FrontendUserAuthentication
::class)
1378 ->setMethods(['getSessionData'])
1380 $frontendUser->expects($this->once())->method('getSessionData')->with('myext')->willReturn([
1385 $GLOBALS['TSFE']->fe_user
= $frontendUser;
1387 $this->assertEquals(42, $this->subject
->getData('session:myext|mydata|someValue'));
1391 * Checks if getData() works with type "level"
1395 public function getDataWithTypeLevel()
1398 0 => ['uid' => 1, 'title' => 'title1'],
1399 1 => ['uid' => 2, 'title' => 'title2'],
1400 2 => ['uid' => 3, 'title' => 'title3'],
1403 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1404 $this->assertEquals(2, $this->subject
->getData('level'));
1408 * Checks if getData() works with type "global"
1412 public function getDataWithTypeGlobal()
1414 $this->assertEquals($GLOBALS['TSFE']->metaCharset
, $this->subject
->getData('global:TSFE|metaCharset'));
1418 * Checks if getData() works with type "leveltitle"
1422 public function getDataWithTypeLeveltitle()
1425 0 => ['uid' => 1, 'title' => 'title1'],
1426 1 => ['uid' => 2, 'title' => 'title2'],
1427 2 => ['uid' => 3, 'title' => ''],
1430 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1431 $this->assertEquals('', $this->subject
->getData('leveltitle:-1'));
1432 // since "title3" is not set, it will slide to "title2"
1433 $this->assertEquals('title2', $this->subject
->getData('leveltitle:-1,slide'));
1437 * Checks if getData() works with type "levelmedia"
1441 public function getDataWithTypeLevelmedia()
1444 0 => ['uid' => 1, 'title' => 'title1', 'media' => 'media1'],
1445 1 => ['uid' => 2, 'title' => 'title2', 'media' => 'media2'],
1446 2 => ['uid' => 3, 'title' => 'title3', 'media' => ''],
1449 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1450 $this->assertEquals('', $this->subject
->getData('levelmedia:-1'));
1451 // since "title3" is not set, it will slide to "title2"
1452 $this->assertEquals('media2', $this->subject
->getData('levelmedia:-1,slide'));
1456 * Checks if getData() works with type "leveluid"
1460 public function getDataWithTypeLeveluid()
1463 0 => ['uid' => 1, 'title' => 'title1'],
1464 1 => ['uid' => 2, 'title' => 'title2'],
1465 2 => ['uid' => 3, 'title' => 'title3'],
1468 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1469 $this->assertEquals(3, $this->subject
->getData('leveluid:-1'));
1470 // every element will have a uid - so adding slide doesn't really make sense, just for completeness
1471 $this->assertEquals(3, $this->subject
->getData('leveluid:-1,slide'));
1475 * Checks if getData() works with type "levelfield"
1479 public function getDataWithTypeLevelfield()
1482 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1483 1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1484 2 => ['uid' => 3, 'title' => 'title3', 'testfield' => ''],
1487 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1488 $this->assertEquals('', $this->subject
->getData('levelfield:-1,testfield'));
1489 $this->assertEquals('field2', $this->subject
->getData('levelfield:-1,testfield,slide'));
1493 * Checks if getData() works with type "fullrootline"
1497 public function getDataWithTypeFullrootline()
1500 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1503 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1504 1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1505 2 => ['uid' => 3, 'title' => 'title3', 'testfield' => 'field3'],
1508 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline1;
1509 $GLOBALS['TSFE']->rootLine
= $rootline2;
1510 $this->assertEquals('field2', $this->subject
->getData('fullrootline:-1,testfield'));
1514 * Checks if getData() works with type "date"
1518 public function getDataWithTypeDate()
1521 $defaultFormat = 'd/m Y';
1523 $this->assertEquals(date($format, $GLOBALS['EXEC_TIME']), $this->subject
->getData('date:' . $format));
1524 $this->assertEquals(date($defaultFormat, $GLOBALS['EXEC_TIME']), $this->subject
->getData('date'));
1528 * Checks if getData() works with type "page"
1532 public function getDataWithTypePage()
1535 $GLOBALS['TSFE']->page
['uid'] = $uid;
1536 $this->assertEquals($uid, $this->subject
->getData('page:uid'));
1540 * Checks if getData() works with type "current"
1544 public function getDataWithTypeCurrent()
1546 $key = $this->getUniqueId('someKey');
1547 $value = $this->getUniqueId('someValue');
1548 $this->subject
->data
[$key] = $value;
1549 $this->subject
->currentValKey
= $key;
1550 $this->assertEquals($value, $this->subject
->getData('current'));
1554 * Checks if getData() works with type "db"
1558 public function getDataWithTypeDb()
1560 $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1562 $GLOBALS['TSFE']->sys_page
->expects($this->atLeastOnce())->method('getRawRecord')->with('tt_content', '106')->will($this->returnValue($dummyRecord));
1563 $this->assertEquals($dummyRecord['title'], $this->subject
->getData('db:tt_content:106:title'));
1567 * Checks if getData() works with type "lll"
1571 public function getDataWithTypeLll()
1573 $key = $this->getUniqueId('someKey');
1574 $value = $this->getUniqueId('someValue');
1575 $GLOBALS['TSFE']->expects($this->once())->method('sL')->with('LLL:' . $key)->will($this->returnValue($value));
1576 $this->assertEquals($value, $this->subject
->getData('lll:' . $key));
1580 * Checks if getData() works with type "path"
1584 public function getDataWithTypePath()
1586 $filenameIn = $this->getUniqueId('someValue');
1587 $filenameOut = $this->getUniqueId('someValue');
1588 $this->templateServiceMock
->expects($this->atLeastOnce())->method('getFileName')->with($filenameIn)->will($this->returnValue($filenameOut));
1589 $this->assertEquals($filenameOut, $this->subject
->getData('path:' . $filenameIn));
1593 * Checks if getData() works with type "parentRecordNumber"
1597 public function getDataWithTypeParentRecordNumber()
1599 $recordNumber = rand();
1600 $this->subject
->parentRecordNumber
= $recordNumber;
1601 $this->assertEquals($recordNumber, $this->subject
->getData('cobj:parentRecordNumber'));
1605 * Checks if getData() works with type "debug:rootLine"
1609 public function getDataWithTypeDebugRootline()
1612 0 => ['uid' => 1, 'title' => 'title1'],
1613 1 => ['uid' => 2, 'title' => 'title2'],
1614 2 => ['uid' => 3, 'title' => ''],
1616 $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
1617 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1619 DebugUtility
::useAnsiColor(false
);
1620 $result = $this->subject
->getData('debug:rootLine');
1621 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1623 $this->assertEquals($expectedResult, $cleanedResult);
1627 * Checks if getData() works with type "debug:fullRootLine"
1631 public function getDataWithTypeDebugFullRootline()
1634 0 => ['uid' => 1, 'title' => 'title1'],
1635 1 => ['uid' => 2, 'title' => 'title2'],
1636 2 => ['uid' => 3, 'title' => ''],
1638 $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
1639 $GLOBALS['TSFE']->rootLine
= $rootline;
1641 DebugUtility
::useAnsiColor(false
);
1642 $result = $this->subject
->getData('debug:fullRootLine');
1643 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1645 $this->assertEquals($expectedResult, $cleanedResult);
1649 * Checks if getData() works with type "debug:data"
1653 public function getDataWithTypeDebugData()
1655 $key = $this->getUniqueId('someKey');
1656 $value = $this->getUniqueId('someValue');
1657 $this->subject
->data
= [$key => $value];
1659 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1661 DebugUtility
::useAnsiColor(false
);
1662 $result = $this->subject
->getData('debug:data');
1663 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1665 $this->assertEquals($expectedResult, $cleanedResult);
1669 * Checks if getData() works with type "debug:register"
1673 public function getDataWithTypeDebugRegister()
1675 $key = $this->getUniqueId('someKey');
1676 $value = $this->getUniqueId('someValue');
1677 $GLOBALS['TSFE']->register
= [$key => $value];
1679 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1681 DebugUtility
::useAnsiColor(false
);
1682 $result = $this->subject
->getData('debug:register');
1683 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1685 $this->assertEquals($expectedResult, $cleanedResult);
1689 * Checks if getData() works with type "data:page"
1693 public function getDataWithTypeDebugPage()
1696 $GLOBALS['TSFE']->page
= ['uid' => $uid];
1698 $expectedResult = 'array(1item)uid=>' . $uid . '(integer)';
1700 DebugUtility
::useAnsiColor(false
);
1701 $result = $this->subject
->getData('debug:page');
1702 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1704 $this->assertEquals($expectedResult, $cleanedResult);
1710 public function aTagParamsHasLeadingSpaceIfNotEmpty()
1712 $aTagParams = $this->subject
->getATagParams(['ATagParams' => 'data-test="testdata"']);
1713 $this->assertEquals(' data-test="testdata"', $aTagParams);
1719 public function aTagParamsHaveSpaceBetweenLocalAndGlobalParams()
1721 $GLOBALS['TSFE']->ATagParams
= 'data-global="dataglobal"';
1722 $aTagParams = $this->subject
->getATagParams(['ATagParams' => 'data-test="testdata"']);
1723 $this->assertEquals(' data-global="dataglobal" data-test="testdata"', $aTagParams);
1729 public function aTagParamsHasNoLeadingSpaceIfEmpty()
1731 // make sure global ATagParams are empty
1732 $GLOBALS['TSFE']->ATagParams
= '';
1733 $aTagParams = $this->subject
->getATagParams(['ATagParams' => '']);
1734 $this->assertEquals('', $aTagParams);
1740 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider()
1746 ['fooo', ['foo' => 'bar']]
1751 * Make sure that the rendering falls back to the classic <img style if nothing else is found
1754 * @dataProvider getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider
1755 * @param string $key
1756 * @param array $configuration
1758 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFound($key, $configuration)
1760 $defaultImgTagTemplate = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1761 $result = $this->subject
->getImageTagTemplate($key, $configuration);
1762 $this->assertEquals($result, $defaultImgTagTemplate);
1768 public function getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider()
1776 'element' => '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1780 '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1787 * Assure if a layoutKey and layout is given the selected layout is returned
1790 * @dataProvider getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider
1791 * @param string $key
1792 * @param array $configuration
1793 * @param string $expectation
1795 public function getImageTagTemplateReturnTemplateElementIdentifiedByKey($key, $configuration, $expectation)
1797 $result = $this->subject
->getImageTagTemplate($key, $configuration);
1798 $this->assertEquals($result, $expectation);
1804 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider()
1808 ['foo', null
, null
],
1809 ['foo', ['sourceCollection.' => 1], 'bar']
1814 * Make sure the source collection is empty if no valid configuration or source collection is defined
1817 * @dataProvider getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider
1818 * @param string $layoutKey
1819 * @param array $configuration
1820 * @param string $file
1822 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefined($layoutKey, $configuration, $file)
1824 $result = $this->subject
->getImageSourceCollection($layoutKey, $configuration, $file);
1825 $this->assertSame($result, '');
1829 * Make sure the generation of subimages calls the generation of the subimages and uses the layout -> source template
1833 public function getImageSourceCollectionRendersDefinedSources()
1835 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
1836 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
1837 ->setMethods(['stdWrap', 'getImgResource'])
1840 $cObj->start([], 'tt_content');
1842 $layoutKey = 'test';
1845 'layoutKey' => 'test',
1848 'element' => '<img ###SRC### ###SRCCOLLECTION### ###SELFCLOSINGTAGSLASH###>',
1849 'source' => '---###SRC###---'
1852 'sourceCollection.' => [
1859 $file = 'testImageName';
1861 // Avoid calling of stdWrap
1863 ->expects($this->any())
1865 ->will($this->returnArgument(0));
1867 // Avoid calling of imgResource
1869 ->expects($this->exactly(1))
1870 ->method('getImgResource')
1871 ->with($this->equalTo('testImageName'))
1872 ->will($this->returnValue([100, 100, null
, 'bar']));
1874 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
1876 $this->assertEquals('---bar---', $result);
1880 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyDefault test
1882 * @return array multi-dimensional array with the second level like this:
1883 * @see getImageSourceCollectionRendersDefinedLayoutKeyDefault
1885 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider()
1887 $sourceCollectionArray = [
1890 'srcsetCandidate' => '600w',
1891 'mediaQuery' => '(max-device-width: 600px)',
1892 'dataKey' => 'small',
1895 'if.directReturn' => 0,
1897 'pixelDensity' => '2',
1898 'srcsetCandidate' => '600w 2x',
1899 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
1900 'dataKey' => 'smallRetina',
1907 'layoutKey' => 'default',
1910 'element' => '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>',
1914 'sourceCollection.' => $sourceCollectionArray
1921 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
1924 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider
1925 * @param string $layoutKey
1926 * @param array $configuration
1928 public function getImageSourceCollectionRendersDefinedLayoutKeyDefault($layoutKey, $configuration)
1930 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
1931 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
1932 ->setMethods(['stdWrap', 'getImgResource'])
1935 $cObj->start([], 'tt_content');
1937 $file = 'testImageName';
1939 // Avoid calling of stdWrap
1941 ->expects($this->any())
1943 ->will($this->returnArgument(0));
1945 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
1947 $this->assertEmpty($result);
1951 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyData test
1953 * @return array multi-dimensional array with the second level like this:
1954 * @see getImageSourceCollectionRendersDefinedLayoutKeyData
1956 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider()
1958 $sourceCollectionArray = [
1961 'srcsetCandidate' => '600w',
1962 'mediaQuery' => '(max-device-width: 600px)',
1963 'dataKey' => 'small',
1966 'if.directReturn' => 1,
1968 'pixelDensity' => '2',
1969 'srcsetCandidate' => '600w 2x',
1970 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
1971 'dataKey' => 'smallRetina',
1978 'layoutKey' => 'srcset',
1981 'element' => '<img src="###SRC###" srcset="###SOURCECOLLECTION###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
1982 'source' => '|*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###'
1985 'sourceCollection.' => $sourceCollectionArray
1988 'bar-file.jpg 600w,bar-file.jpg 600w 2x',
1993 'layoutKey' => 'picture',
1996 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
1997 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
2000 'sourceCollection.' => $sourceCollectionArray,
2003 '<source src="bar-file.jpg" media="(max-device-width: 600px)" /><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)" />',
2008 'layoutKey' => 'picture',
2011 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
2012 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
2015 'sourceCollection.' => $sourceCollectionArray,
2018 '<source src="bar-file.jpg" media="(max-device-width: 600px)"><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)">',
2023 'layoutKey' => 'data',
2026 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2027 'source' => 'data-###DATAKEY###="###SRC###"'
2030 'sourceCollection.' => $sourceCollectionArray
2033 'data-small="bar-file.jpg"data-smallRetina="bar-file.jpg"',
2039 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
2042 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider
2043 * @param string $layoutKey
2044 * @param array $configuration
2045 * @param string $xhtmlDoctype
2046 * @param string $expectedHtml
2048 public function getImageSourceCollectionRendersDefinedLayoutKeyData($layoutKey, $configuration, $xhtmlDoctype, $expectedHtml)
2050 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
2051 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
2052 ->setMethods(['stdWrap', 'getImgResource'])
2055 $cObj->start([], 'tt_content');
2057 $file = 'testImageName';
2059 $GLOBALS['TSFE']->xhtmlDoctype
= $xhtmlDoctype;
2061 // Avoid calling of stdWrap
2063 ->expects($this->any())
2065 ->will($this->returnArgument(0));
2067 // Avoid calling of imgResource
2069 ->expects($this->exactly(2))
2070 ->method('getImgResource')
2071 ->with($this->equalTo('testImageName'))
2072 ->will($this->returnValue([100, 100, null
, 'bar-file.jpg']));
2074 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
2076 $this->assertEquals($expectedHtml, $result);
2080 * Make sure the hook in get sourceCollection is called
2084 public function getImageSourceCollectionHookCalled()
2086 $this->subject
= $this->getAccessibleMock(
2087 ContentObjectRenderer
::class,
2088 ['getResourceFactory', 'stdWrap', 'getImgResource']
2090 $this->subject
->start([], 'tt_content');
2092 // Avoid calling stdwrap and getImgResource
2093 $this->subject
->expects($this->any())
2095 ->will($this->returnArgument(0));
2097 $this->subject
->expects($this->any())
2098 ->method('getImgResource')
2099 ->will($this->returnValue([100, 100, null
, 'bar-file.jpg']));
2101 $resourceFactory = $this->createMock(ResourceFactory
::class);
2102 $this->subject
->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
2104 $className = $this->getUniqueId('tx_coretest_getImageSourceCollectionHookCalled');
2105 $getImageSourceCollectionHookMock = $this->getMockBuilder(
2106 ContentObjectOneSourceCollectionHookInterface
::class
2108 ->setMethods(['getOneSourceCollection'])
2109 ->setMockClassName($className)
2111 GeneralUtility
::addInstance($className, $getImageSourceCollectionHookMock);
2112 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'][] = $className;
2114 $getImageSourceCollectionHookMock
2115 ->expects($this->exactly(1))
2116 ->method('getOneSourceCollection')
2117 ->will($this->returnCallback([$this, 'isGetOneSourceCollectionCalledCallback']));
2120 'layoutKey' => 'data',
2123 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2124 'source' => 'data-###DATAKEY###="###SRC###"'
2127 'sourceCollection.' => [
2130 'srcsetCandidate' => '600w',
2131 'mediaQuery' => '(max-device-width: 600px)',
2132 'dataKey' => 'small',
2137 $result = $this->subject
->getImageSourceCollection('data', $configuration, $this->getUniqueId('testImage-'));
2139 $this->assertSame($result, 'isGetOneSourceCollectionCalledCallback');
2143 * Handles the arguments that have been sent to the getImgResource hook.
2145 * @param array $sourceRenderConfiguration
2146 * @param array $sourceConfiguration
2147 * @param $oneSourceCollection
2150 * @see getImageSourceCollectionHookCalled
2152 public function isGetOneSourceCollectionCalledCallback($sourceRenderConfiguration, $sourceConfiguration, $oneSourceCollection, $parent)
2154 $this->assertTrue(is_array($sourceRenderConfiguration));
2155 $this->assertTrue(is_array($sourceConfiguration));
2156 return 'isGetOneSourceCollectionCalledCallback';
2162 public function renderingContentObjectThrowsException()
2164 $this->expectException(\LogicException
::class);
2165 $this->expectExceptionCode(1414513947);
2166 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2167 $this->subject
->render($contentObjectFixture, []);
2173 public function exceptionHandlerIsEnabledByDefaultInProductionContext()
2175 $backupApplicationContext = GeneralUtility
::getApplicationContext();
2176 Fixtures\GeneralUtilityFixture
::setApplicationContext(new ApplicationContext('Production'));
2178 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2179 $this->subject
->render($contentObjectFixture, []);
2181 Fixtures\GeneralUtilityFixture
::setApplicationContext($backupApplicationContext);
2187 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredLocally()
2189 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2192 'exceptionHandler' => '1'
2194 $this->subject
->render($contentObjectFixture, $configuration);
2200 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredGlobally()
2202 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2204 $this->frontendControllerMock
->config
['config']['contentObjectExceptionHandler'] = '1';
2205 $this->subject
->render($contentObjectFixture, []);
2211 public function globalExceptionHandlerConfigurationCanBeOverriddenByLocalConfiguration()
2213 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2214 $this->expectException(\LogicException
::class);
2215 $this->expectExceptionCode(1414513947);
2216 $this->frontendControllerMock
->config
['config']['contentObjectExceptionHandler'] = '1';
2218 'exceptionHandler' => '0'
2220 $this->subject
->render($contentObjectFixture, $configuration);
2226 public function renderedErrorMessageCanBeCustomized()
2228 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2231 'exceptionHandler' => '1',
2232 'exceptionHandler.' => [
2233 'errorMessage' => 'New message for testing',
2237 $this->assertSame('New message for testing', $this->subject
->render($contentObjectFixture, $configuration));
2243 public function localConfigurationOverridesGlobalConfiguration()
2245 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2247 $this->frontendControllerMock
2248 ->config
['config']['contentObjectExceptionHandler.'] = [
2249 'errorMessage' => 'Global message for testing',
2252 'exceptionHandler' => '1',
2253 'exceptionHandler.' => [
2254 'errorMessage' => 'New message for testing',
2258 $this->assertSame('New message for testing', $this->subject
->render($contentObjectFixture, $configuration));
2264 public function specificExceptionsCanBeIgnoredByExceptionHandler()
2266 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2269 'exceptionHandler' => '1',
2270 'exceptionHandler.' => [
2271 'ignoreCodes.' => ['10.' => '1414513947'],
2274 $this->expectException(\LogicException
::class);
2275 $this->expectExceptionCode(1414513947);
2276 $this->subject
->render($contentObjectFixture, $configuration);
2280 * @return \PHPUnit_Framework_MockObject_MockObject | AbstractContentObject
2282 protected function createContentObjectThrowingExceptionFixture()
2284 $contentObjectFixture = $this->getMockBuilder(AbstractContentObject
::class)
2285 ->setConstructorArgs([$this->subject
])
2287 $contentObjectFixture->expects($this->once())
2289 ->willReturnCallback(function () {
2290 throw new \
LogicException('Exception during rendering', 1414513947);
2292 return $contentObjectFixture;
2298 protected function getLibParseFunc()
2304 'keep' => '{$styles.content.links.keep}',
2317 'data' => 'parameters : allParams',
2326 'allowTags' => 'a, abbr, acronym, address, article, aside, b, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, dfn, dl, div, dt, em, font, footer, header, h1, h2, h3, h4, h5, h6, hr, i, img, ins, kbd, label, li, link, meta, nav, ol, p, pre, q, samp, sdfield, section, small, span, strike, strong, style, sub, sup, table, thead, tbody, tfoot, td, th, tr, title, tt, u, ul, var',
2328 'sword' => '<span class="csc-sword">|</span>',
2330 'nonTypoTagStdWrap.' => [
2331 'HTMLparser' => '1',
2333 'keepNonMatchedTags' => '1',
2334 'htmlSpecialChars' => '2',
2343 protected function getLibParseFunc_RTE()
2348 'allowTags' => 'a, abbr, acronym, address, article, aside, b, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, dfn, dl, div, dt, em, font, footer, header, h1, h2, h3, h4, h5, h6, hr, i, img, ins, kbd, label, li, link, meta, nav, ol, p, pre, q, samp, sdfield, section, small, span, strike, strong, style, sub, sup, table, thead, tbody, tfoot, td, th, tr, title, tt, u, ul, var',
2351 'externalBlocks' => 'article, aside, blockquote, div, dd, dl, footer, header, nav, ol, section, table, ul, pre',
2352 'externalBlocks.' => [
2354 'callRecursive' => '1',
2358 'callRecursive' => '1',
2362 'callRecursive' => '1',
2366 'callRecursive' => '1',
2370 'callRecursive' => '1',
2374 'callRecursive' => '1',
2378 'callRecursive' => '1',
2382 'callRecursive' => '1',
2386 'callRecursive' => '1',
2390 'callRecursive' => '1',
2394 'callRecursive' => '1',
2398 'HTMLtableCells' => '1',
2399 'HTMLtableCells.' => [
2400 'addChr10BetweenParagraphs' => '1',
2403 'parseFunc' => '=< lib.parseFunc_RTE',
2405 'nonTypoTagStdWrap.' => [
2407 'nonWrappedTag' => '',
2415 'HTMLparser' => '1',
2417 'keepNonMatchedTags' => '1',
2423 'default' => 'contenttable',
2424 'list' => 'contenttable',
2434 'callRecursive' => '1',
2442 'override' => '_blank',
2447 'nonTypoTagStdWrap.' => [
2449 'addAttributes.' => [
2451 'class' => 'bodytext',
2453 'setOnly' => 'blank',
2457 'encapsTagList' => 'p,pre,h1,h2,h3,h4,h5,h6,hr,dt,li',
2458 'innerStdWrap_all.' => [
2459 'ifBlank' => ' ',
2461 'nonWrappedTag' => 'P',
2466 'HTMLparser' => '1',
2468 'htmlSpecialChars' => '2',
2469 'keepNonMatchedTags' => '1',
2472 'sword' => '<span class="csc-sword">|</span>',
2485 'data' => 'parameters : allParams',
2500 public function _parseFuncReturnsCorrectHtmlDataProvider()
2503 'Text without tag is wrapped with <p> tag' => [
2505 $this->getLibParseFunc_RTE(),
2506 '<p class="bodytext">Text without tag</p>',
2508 'Text wrapped with <p> tag remains the same' => [
2509 '<p class="myclass">Text with <p> tag</p>',
2510 $this->getLibParseFunc_RTE(),
2511 '<p class="myclass">Text with <p> tag</p>',
2513 'Text with absolute external link' => [
2514 'Text with <link http://example.com/foo/>external link</link>',
2515 $this->getLibParseFunc_RTE(),
2516 '<p class="bodytext">Text with <a href="http://example.com/foo/">external link</a></p>',
2518 'Empty lines are not duplicated' => [
2520 $this->getLibParseFunc_RTE(),
2521 '<p class="bodytext"> </p>',
2523 'Multiple empty lines with no text' => [
2525 $this->getLibParseFunc_RTE(),
2526 '<p class="bodytext"> </p>' . LF
. '<p class="bodytext"> </p>' . LF
. '<p class="bodytext"> </p>',
2528 'Empty lines are not duplicated at the end of content' => [
2530 $this->getLibParseFunc_RTE(),
2531 '<p class="bodytext">test</p>' . LF
. '<p class="bodytext"> </p>',
2533 'Empty lines are not trimmed' => [
2535 $this->getLibParseFunc_RTE(),
2536 '<p class="bodytext"> </p>' . LF
. '<p class="bodytext">test</p>' . LF
. '<p class="bodytext"> </p>',
2543 * @dataProvider _parseFuncReturnsCorrectHtmlDataProvider
2544 * @param string $value
2545 * @param array $configuration
2546 * @param string $expectedResult
2548 public function stdWrap_parseFuncReturnsParsedHtml($value, $configuration, $expectedResult)
2550 $this->assertEquals($expectedResult, $this->subject
->stdWrap_parseFunc($value, $configuration));
2556 public function typolinkReturnsCorrectLinksForEmailsAndUrlsDataProvider()
2562 'parameter' => 'http://typo3.org',
2564 '<a href="http://typo3.org">TYPO3</a>',
2566 'Link to url without schema' => [
2569 'parameter' => 'typo3.org',
2571 '<a href="http://typo3.org">TYPO3</a>',
2573 'Link to url without link text' => [
2576 'parameter' => 'http://typo3.org',
2578 '<a href="http://typo3.org">http://typo3.org</a>',
2580 'Link to url with attributes' => [
2583 'parameter' => 'http://typo3.org',
2584 'ATagParams' => 'class="url-class"',
2585 'extTarget' => '_blank',
2586 'title' => 'Open new window',
2588 '<a href="http://typo3.org" title="Open new window" target="_blank" class="url-class">TYPO3</a>',
2590 'Link to url with attributes in parameter' => [
2593 'parameter' => 'http://typo3.org _blank url-class "Open new window"',
2595 '<a href="http://typo3.org" title="Open new window" target="_blank" class="url-class">TYPO3</a>',
2597 'Link to url with script tag' => [
2600 'parameter' => 'http://typo3.org<script>alert(123)</script>',
2602 '<a href="http://typo3.org<script>alert(123)</script>">http://typo3.org<script>alert(123)</script></a>',
2604 'Link to email address' => [
2607 'parameter' => 'foo@bar.org',
2609 '<a href="mailto:foo@bar.org">Email address</a>',
2611 'Link to email address without link text' => [
2614 'parameter' => 'foo@bar.org',
2616 '<a href="mailto:foo@bar.org">foo@bar.org</a>',
2618 'Link to email with attributes' => [
2621 'parameter' => 'foo@bar.org',
2622 'ATagParams' => 'class="email-class"',
2623 'title' => 'Write an email',
2625 '<a href="mailto:foo@bar.org" title="Write an email" class="email-class">Email address</a>',
2627 'Link to email with attributes in parameter' => [
2630 'parameter' => 'foo@bar.org - email-class "Write an email"',
2632 '<a href="mailto:foo@bar.org" title="Write an email" class="email-class">Email address</a>',
2639 * @param string $linkText
2640 * @param array $configuration
2641 * @param string $expectedResult
2642 * @dataProvider typolinkReturnsCorrectLinksForEmailsAndUrlsDataProvider
2644 public function typolinkReturnsCorrectLinksForEmailsAndUrls($linkText, $configuration, $expectedResult)
2646 $templateServiceObjectMock = $this->getMockBuilder(TemplateService
::class)
2647 ->setMethods(['dummy'])
2649 $templateServiceObjectMock->setup
= [
2651 'parseFunc.' => $this->getLibParseFunc(),
2654 $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController
::class);
2655 $typoScriptFrontendControllerMockObject->config
= [
2658 $typoScriptFrontendControllerMockObject->tmpl
= $templateServiceObjectMock;
2659 $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
2660 $this->subject
->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
2662 $this->assertEquals($expectedResult, $this->subject
->typoLink($linkText, $configuration));
2666 * @param array $settings
2667 * @param string $linkText
2668 * @param string $mailAddress
2669 * @param string $expected
2670 * @dataProvider typoLinkEncodesMailAddressForSpamProtectionDataProvider
2673 public function typoLinkEncodesMailAddressForSpamProtection(array $settings, $linkText, $mailAddress, $expected)
2675 $this->getFrontendController()->spamProtectEmailAddresses
= $settings['spamProtectEmailAddresses'];
2676 $this->getFrontendController()->config
['config'] = $settings;
2677 $typoScript = ['parameter' => $mailAddress];
2679 $this->assertEquals($expected, $this->subject
->typoLink($linkText, $typoScript));
2685 public function typoLinkEncodesMailAddressForSpamProtectionDataProvider()
2688 'plain mail without mailto scheme' => [
2690 'spamProtectEmailAddresses' => '',
2691 'spamProtectEmailAddresses_atSubst' => '',
2692 'spamProtectEmailAddresses_lastDotSubst' => '',
2694 'some.body@test.typo3.org',
2695 'some.body@test.typo3.org',
2696 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2698 'plain mail with mailto scheme' => [
2700 'spamProtectEmailAddresses' => '',
2701 'spamProtectEmailAddresses_atSubst' => '',
2702 'spamProtectEmailAddresses_lastDotSubst' => '',
2704 'some.body@test.typo3.org',
2705 'mailto:some.body@test.typo3.org',
2706 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2708 'plain with at and dot substitution' => [
2710 'spamProtectEmailAddresses' => '0',
2711 'spamProtectEmailAddresses_atSubst' => '(at)',
2712 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2714 'some.body@test.typo3.org',
2715 'mailto:some.body@test.typo3.org',
2716 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2718 'mono-alphabetic substitution offset +1' => [
2720 'spamProtectEmailAddresses' => '1',
2721 'spamProtectEmailAddresses_atSubst' => '',
2722 'spamProtectEmailAddresses_lastDotSubst' => '',
2724 'some.body@test.typo3.org',
2725 'mailto:some.body@test.typo3.org',
2726 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body(at)test.typo3.org</a>',
2728 'mono-alphabetic substitution offset +1 with at substitution' => [
2730 'spamProtectEmailAddresses' => '1',
2731 'spamProtectEmailAddresses_atSubst' => '@',
2732 'spamProtectEmailAddresses_lastDotSubst' => '',
2734 'some.body@test.typo3.org',
2735 'mailto:some.body@test.typo3.org',
2736 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body@test.typo3.org</a>',
2738 'mono-alphabetic substitution offset +1 with at and dot substitution' => [
2740 'spamProtectEmailAddresses' => '1',
2741 'spamProtectEmailAddresses_atSubst' => '(at)',
2742 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2744 'some.body@test.typo3.org',
2745 'mailto:some.body@test.typo3.org',
2746 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body(at)test.typo3(dot)org</a>',
2748 'mono-alphabetic substitution offset -1 with at and dot substitution' => [
2750 'spamProtectEmailAddresses' => '-1',
2751 'spamProtectEmailAddresses_atSubst' => '(at)',
2752 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2754 'some.body@test.typo3.org',
2755 'mailto:some.body@test.typo3.org',
2756 '<a href="javascript:linkTo_UnCryptMailto(\'lzhksn9rnld-ancxZsdrs-sxon2-nqf\');">some.body(at)test.typo3(dot)org</a>',
2758 'entity substitution with at and dot substitution' => [
2760 'spamProtectEmailAddresses' => 'ascii',
2761 'spamProtectEmailAddresses_atSubst' => '',
2762 'spamProtectEmailAddresses_lastDotSubst' => '',
2764 'some.body@test.typo3.org',
2765 'mailto:some.body@test.typo3.org',
2766 '<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3.org</a>',
2768 'entity substitution with at and dot substitution with at and dot substitution' => [
2770 'spamProtectEmailAddresses' => 'ascii',
2771 'spamProtectEmailAddresses_atSubst' => '(at)',
2772 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2774 'some.body@test.typo3.org',
2775 'mailto:some.body@test.typo3.org',
2776 '<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3(dot)org</a>',
2784 public function typolinkReturnsCorrectLinksFilesDataProvider()
2790 'parameter' => 'fileadmin/foo.bar',
2792 '<a href="fileadmin/foo.bar">My file</a>',
2794 'Link to file without link text' => [
2797 'parameter' => 'fileadmin/foo.bar',
2799 '<a href="fileadmin/foo.bar">fileadmin/foo.bar</a>',
2801 'Link to file with attributes' => [
2804 'parameter' => 'fileadmin/foo.bar',
2805 'ATagParams' => 'class="file-class"',
2806 'fileTarget' => '_blank',
2807 'title' => 'Title of the file',
2809 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2811 'Link to file with attributes and additional href' => [
2814 'parameter' => 'fileadmin/foo.bar',
2815 'ATagParams' => 'href="foo-bar"',
2816 'fileTarget' => '_blank',
2817 'title' => 'Title of the file',
2819 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank">My file</a>',
2821 'Link to file with attributes and additional href and class' => [
2824 'parameter' => 'fileadmin/foo.bar',
2825 'ATagParams' => 'href="foo-bar" class="file-class"',
2826 'fileTarget' => '_blank',
2827 'title' => 'Title of the file',
2829 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2831 'Link to file with attributes and additional class and href' => [
2834 'parameter' => 'fileadmin/foo.bar',
2835 'ATagParams' => 'class="file-class" href="foo-bar"',
2836 'fileTarget' => '_blank',
2837 'title' => 'Title of the file',
2839 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2841 'Link to file with attributes and additional class and href and title' => [
2844 'parameter' => 'fileadmin/foo.bar',
2845 'ATagParams' => 'class="file-class" href="foo-bar" title="foo-bar"',
2846 'fileTarget' => '_blank',
2847 'title' => 'Title of the file',
2849 '<a href="fileadmin/foo.bar" title="foo-bar" target="_blank" class="file-class">My file</a>',
2851 'Link to file with attributes and empty ATagParams' => [
2854 'parameter' => 'fileadmin/foo.bar',
2856 'fileTarget' => '_blank',
2857 'title' => 'Title of the file',
2859 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank">My file</a>',
2861 'Link to file with attributes in parameter' => [
2864 'parameter' => 'fileadmin/foo.bar _blank file-class "Title of the file"',
2866 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2868 'Link to file with script tag in name' => [
2871 'parameter' => 'fileadmin/<script>alert(123)</script>',
2873 '<a href="fileadmin/<script>alert(123)</script>">fileadmin/<script>alert(123)</script></a>',
2880 * @param string $linkText
2881 * @param array $configuration
2882 * @param string $expectedResult
2883 * @dataProvider typolinkReturnsCorrectLinksFilesDataProvider
2885 public function typolinkReturnsCorrectLinksFiles($linkText, $configuration, $expectedResult)
2887 $templateServiceObjectMock = $this->getMockBuilder(TemplateService
::class)
2888 ->setMethods(['dummy'])
2890 $templateServiceObjectMock->setup
= [
2892 'parseFunc.' => $this->getLibParseFunc(),
2895 $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController
::class);
2896 $typoScriptFrontendControllerMockObject->config
= [
2899 $typoScriptFrontendControllerMockObject->tmpl
= $templateServiceObjectMock;
2900 $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
2902 $this->subject
->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
2904 $this->assertEquals($expectedResult, $this->subject
->typoLink($linkText, $configuration));
2910 public function typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider()
2916 'parameter' => 'fileadmin/foo.bar',
2919 '<a href="/fileadmin/foo.bar">My file</a>',
2921 'Link to file with longer absRefPrefix' => [
2924 'parameter' => 'fileadmin/foo.bar',
2927 '<a href="/sub/fileadmin/foo.bar">My file</a>',
2929 'Link to absolute file' => [
2932 'parameter' => '/images/foo.bar',
2935 '<a href="/images/foo.bar">My file</a>',
2937 'Link to absolute file with longer absRefPrefix' => [
2940 'parameter' => '/images/foo.bar',
2943 '<a href="/images/foo.bar">My file</a>',
2945 'Link to absolute file with identical longer absRefPrefix' => [
2948 'parameter' => '/sub/fileadmin/foo.bar',
2951 '<a href="/sub/fileadmin/foo.bar">My file</a>',
2953 'Link to file with empty absRefPrefix' => [
2956 'parameter' => 'fileadmin/foo.bar',
2959 '<a href="fileadmin/foo.bar">My file</a>',
2961 'Link to absolute file with empty absRefPrefix' => [
2964 'parameter' => '/fileadmin/foo.bar',
2967 '<a href="/fileadmin/foo.bar">My file</a>',
2969 'Link to file with attributes with absRefPrefix' => [
2972 'parameter' => 'fileadmin/foo.bar',
2973 'ATagParams' => 'class="file-class"',
2974 'fileTarget' => '_blank',
2975 'title' => 'Title of the file',
2978 '<a href="/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2980 'Link to file with attributes with longer absRefPrefix' => [
2983 'parameter' => 'fileadmin/foo.bar',
2984 'ATagParams' => 'class="file-class"',
2985 'fileTarget' => '_blank',
2986 'title' => 'Title of the file',
2989 '<a href="/sub/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2991 'Link to absolute file with attributes with absRefPrefix' => [
2994 'parameter' => '/images/foo.bar',
2995 'ATagParams' => 'class="file-class"',
2996 'fileTarget' => '_blank',
2997 'title' => 'Title of the file',
3000 '<a href="/images/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
3002 'Link to absolute file with attributes with longer absRefPrefix' => [
3005 'parameter' => '/images/foo.bar',
3006 'ATagParams' => 'class="file-class"',
3007 'fileTarget' => '_blank',
3008 'title' => 'Title of the file',
3011 '<a href="/images/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
3013 'Link to absolute file with attributes with identical longer absRefPrefix' => [
3016 'parameter' => '/sub/fileadmin/foo.bar',
3017 'ATagParams' => 'class="file-class"',
3018 'fileTarget' => '_blank',
3019 'title' => 'Title of the file',
3022 '<a href="/sub/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
3029 * @param string $linkText
3030 * @param array $configuration
3031 * @param string $absRefPrefix
3032 * @param string $expectedResult
3033 * @dataProvider typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider
3035 public function typolinkReturnsCorrectLinksForFilesWithAbsRefPrefix($linkText, $configuration, $absRefPrefix, $expectedResult)