‪TYPO3CMS  ‪main
ContentObjectRendererTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use Psr\EventDispatcher\EventDispatcherInterface;
23 use Psr\Http\Message\ServerRequestInterface;
24 use Psr\Log\NullLogger;
25 use Symfony\Component\DependencyInjection\Container;
32 use TYPO3\CMS\Core\LinkHandling\TypoLinkCodecService;
50 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
51 
52 final class ‪ContentObjectRendererTest extends FunctionalTestCase
53 {
55 
56  protected const ‪LANGUAGE_PRESETS = [
57  'EN' => ['id' => 0, 'title' => 'English', 'locale' => 'en_US.UTF8'],
58  ];
59 
60  protected array ‪$pathsToProvideInTestInstance = ['typo3/sysext/frontend/Tests/Functional/Fixtures/Images' => 'fileadmin/user_upload'];
61 
62  protected function ‪setUp(): void
63  {
64  parent::setUp();
65  $this->importCSVDataSet(__DIR__ . '/../Fixtures/be_users.csv');
66  $this->setUpBackendUser(1);
68  'test',
69  $this->‪buildSiteConfiguration(1, '/'),
70  [
71  $this->‪buildDefaultLanguageConfiguration('EN', '/en/'),
72  ],
73  $this->‪buildErrorHandlingConfiguration('Fluid', [404]),
74  );
75  GeneralUtility::flushInternalRuntimeCaches();
76  }
77 
78  protected function ‪getPreparedRequest(): ServerRequestInterface
79  {
80  $request = new ‪ServerRequest('http://example.com/en/', 'GET', null, [], ['HTTP_HOST' => 'example.com', 'REQUEST_URI' => '/en/']);
81  return $request->withQueryParams(['id' => 1])->withAttribute('normalizedParams', ‪NormalizedParams::createFromRequest($request));
82  }
83 
84  public static function ‪getQueryDataProvider(): array
85  {
86  return [
87  'testing empty conf' => [
88  'tt_content',
89  [],
90  '*',
91  ],
92  'testing #17284: adding uid/pid for workspaces' => [
93  'tt_content',
94  [
95  'selectFields' => 'header,bodytext',
96  ],
97  'header,bodytext, [tt_content].[uid] AS [uid], [tt_content].[pid] AS [pid], [tt_content].[t3ver_state] AS [t3ver_state]',
98  ],
99  'testing #17284: no need to add' => [
100  'tt_content',
101  [
102  'selectFields' => 'tt_content.*',
103  ],
104  'tt_content.*',
105  ],
106  'testing #17284: no need to add #2' => [
107  'tt_content',
108  [
109  'selectFields' => '*',
110  ],
111  '*',
112  ],
113  'testing #29783: joined tables, prefix tablename' => [
114  'tt_content',
115  [
116  'selectFields' => 'tt_content.header,be_users.username',
117  'join' => 'be_users ON tt_content.cruser_id = be_users.uid',
118  ],
119  'tt_content.header,be_users.username, [tt_content].[uid] AS [uid], [tt_content].[pid] AS [pid], [tt_content].[t3ver_state] AS [t3ver_state]',
120  ],
121  'testing #34152: single count(*), add nothing' => [
122  'tt_content',
123  [
124  'selectFields' => 'count(*)',
125  ],
126  'count(*)',
127  ],
128  'testing #34152: single max(crdate), add nothing' => [
129  'tt_content',
130  [
131  'selectFields' => 'max(crdate)',
132  ],
133  'max(crdate)',
134  ],
135  'testing #34152: single min(crdate), add nothing' => [
136  'tt_content',
137  [
138  'selectFields' => 'min(crdate)',
139  ],
140  'min(crdate)',
141  ],
142  'testing #34152: single sum(is_siteroot), add nothing' => [
143  'tt_content',
144  [
145  'selectFields' => 'sum(is_siteroot)',
146  ],
147  'sum(is_siteroot)',
148  ],
149  'testing #34152: single avg(crdate), add nothing' => [
150  'tt_content',
151  [
152  'selectFields' => 'avg(crdate)',
153  ],
154  'avg(crdate)',
155  ],
156  'single distinct, add nothing' => [
157  'tt_content',
158  [
159  'selectFields' => 'DISTINCT crdate',
160  ],
161  'DISTINCT crdate',
162  ],
163  'testing #96321: pidInList=root does not raise PHP 8 warning' => [
164  'tt_content',
165  [
166  'selectFields' => '*',
167  'recursive' => '5',
168  'pidInList' => 'root',
169  ],
170  '*',
171  ],
172  ];
173  }
174 
178  #[DataProvider('getQueryDataProvider')]
179  #[Test]
180  public function ‪getQuery(string $table, array $conf, string $expected): void
181  {
182  ‪$GLOBALS['TCA'] = [
183  'pages' => [
184  'ctrl' => [
185  'enablecolumns' => [
186  'disabled' => 'hidden',
187  ],
188  ],
189  ],
190  'tt_content' => [
191  'ctrl' => [
192  'enablecolumns' => [
193  'disabled' => 'hidden',
194  ],
195  'versioningWS' => true,
196  ],
197  ],
198  ];
199 
200  $typoScriptFrontendController = GeneralUtility::makeInstance(TypoScriptFrontendController::class);
201  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class, $typoScriptFrontendController);
202  $request = $this->‪getPreparedRequest();
203  $pageInformation = new ‪PageInformation();
204  $pageInformation->setId(0);
205  $pageInformation->setContentFromPid(0);
206  $request = $request->withAttribute('frontend.page.information', $pageInformation);
207  $subject->setRequest($request);
208 
209  $connection = (new ‪ConnectionPool())->getConnectionForTable('tt_content');
210  $result = $subject->getQuery($connection, $table, $conf);
211 
212  $databasePlatform = $connection->getDatabasePlatform();
213  $identifierQuoteCharacter = (new ‪PlatformHelper())->getIdentifierQuoteCharacter($databasePlatform);
214  // strip select * from part between SELECT and FROM
215  $selectValue = preg_replace('/SELECT (.*) FROM.*/', '$1', $result);
216  // Replace the TYPO3 quote character with the actual quote character for the DBMS
217  $quoteChar = $identifierQuoteCharacter;
218  $expected = str_replace(['[', ']'], [$quoteChar, $quoteChar], $expected);
219  self::assertEquals($expected, $selectValue);
220  }
221 
222  #[Test]
224  {
225  $subject = new ‪ContentObjectRenderer();
226  $subject->setRequest($this->‪getPreparedRequest());
227  $linkResult = $subject->typoLink('', ['parameter' => 'https://example.tld', 'returnLast' => 'result']);
228  self::assertInstanceOf(LinkResultInterface::class, $linkResult);
229  }
230 
231  #[Test]
233  {
234  $linkService = $this->getMockBuilder(LinkService::class)->disableOriginalConstructor()->getMock();
235  $linkService->method('resolve')->with('foo')->willThrowException(new ‪InvalidPathException('', 1666303735));
236  $linkFactory = new ‪LinkFactory($linkService, $this->get(EventDispatcherInterface::class), $this->get(TypoLinkCodecService::class), $this->get('cache.runtime'), $this->get(SiteFinder::class));
237  $linkFactory->setLogger(new NullLogger());
238  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
239  $subject = new ‪ContentObjectRenderer();
240  $subject->setRequest($this->‪getPreparedRequest());
241  self::assertSame('foo', $subject->typoLink('foo', ['parameter' => 'foo']));
242  }
243 
244  #[Test]
246  {
247  $linkService = $this->getMockBuilder(LinkService::class)->disableOriginalConstructor()->getMock();
248  $linkService->method('resolve')->with('foo')->willThrowException(new ‪InvalidPathException('', 1666303765));
249  $linkFactory = new ‪LinkFactory($linkService, $this->get(EventDispatcherInterface::class), $this->get(TypoLinkCodecService::class), $this->get('cache.runtime'), $this->get(SiteFinder::class));
250  $logger = $this->getMockBuilder(Logger::class)->disableOriginalConstructor()->getMock();
251  $logger->expects(self::atLeastOnce())->method('warning')->with('The link could not be generated', self::anything());
252  $linkFactory->setLogger($logger);
253  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
254  $subject = new ‪ContentObjectRenderer();
255  $subject->setRequest($this->‪getPreparedRequest());
256  self::assertSame('foo', $subject->typoLink('foo', ['parameter' => 'foo']));
257  }
258 
259  public static function ‪typolinkReturnsCorrectLinksDataProvider(): array
260  {
261  return [
262  'Link to url' => [
263  'TYPO3',
264  [
265  'directImageLink' => false,
266  'parameter' => 'http://typo3.org',
267  ],
268  '<a href="http://typo3.org">TYPO3</a>',
269  ],
270  'Link to url without schema' => [
271  'TYPO3',
272  [
273  'directImageLink' => false,
274  'parameter' => 'typo3.org',
275  ],
276  '<a href="http://typo3.org">TYPO3</a>',
277  ],
278  'Link to url without link text' => [
279  '',
280  [
281  'directImageLink' => false,
282  'parameter' => 'http://typo3.org',
283  ],
284  '<a href="http://typo3.org">http://typo3.org</a>',
285  ],
286  'Link to url with attributes' => [
287  'TYPO3',
288  [
289  'parameter' => 'http://typo3.org',
290  'ATagParams' => 'class="url-class"',
291  'extTarget' => '_blank',
292  'title' => 'Open new window',
293  ],
294  '<a href="http://typo3.org" target="_blank" class="url-class" rel="noreferrer" title="Open new window">TYPO3</a>',
295  ],
296  'Link to url with attributes and custom target name' => [
297  'TYPO3',
298  [
299  'parameter' => 'http://typo3.org',
300  'ATagParams' => 'class="url-class"',
301  'extTarget' => 'someTarget',
302  'title' => 'Open new window',
303  ],
304  '<a href="http://typo3.org" target="someTarget" class="url-class" rel="noreferrer" title="Open new window">TYPO3</a>',
305  ],
306  'Link to url with attributes in parameter' => [
307  'TYPO3',
308  [
309  'parameter' => 'http://typo3.org _blank url-class "Open new window"',
310  ],
311  '<a href="http://typo3.org" target="_blank" rel="noreferrer" title="Open new window" class="url-class">TYPO3</a>',
312  ],
313  'Link to url with attributes in parameter and custom target name' => [
314  'TYPO3',
315  [
316  'parameter' => 'http://typo3.org someTarget url-class "Open new window"',
317  ],
318  '<a href="http://typo3.org" target="someTarget" rel="noreferrer" title="Open new window" class="url-class">TYPO3</a>',
319  ],
320  'Link to url with script tag' => [
321  '',
322  [
323  'directImageLink' => false,
324  'parameter' => 'http://typo3.org<script>alert(123)</script>',
325  ],
326  '<a href="http://typo3.org&lt;script&gt;alert(123)&lt;/script&gt;">http://typo3.org&lt;script&gt;alert(123)&lt;/script&gt;</a>',
327  ],
328  'Link to email address' => [
329  'Email address',
330  [
331  'parameter' => 'foo@example.com',
332  ],
333  '<a href="mailto:foo@example.com">Email address</a>',
334  ],
335  'Link to email with mailto' => [
336  'Send me an email',
337  [
338  'parameter' => 'mailto:test@example.com',
339  ],
340  '<a href="mailto:test@example.com">Send me an email</a>',
341  ],
342  'Link to email address with subject + cc' => [
343  'Email address',
344  [
345  'parameter' => 'foo@bar.org?subject=This%20is%20a%20test',
346  ],
347  '<a href="mailto:foo@bar.org?subject=This%20is%20a%20test">Email address</a>',
348  ],
349  'Link to email address without link text' => [
350  '',
351  [
352  'parameter' => 'foo@bar.org',
353  ],
354  '<a href="mailto:foo@bar.org">foo@bar.org</a>',
355  ],
356  'Link to email with attributes' => [
357  'Email address',
358  [
359  'parameter' => 'foo@bar.org',
360  'ATagParams' => 'class="email-class"',
361  'title' => 'Write an email',
362  ],
363  '<a href="mailto:foo@bar.org" class="email-class" title="Write an email">Email address</a>',
364  ],
365  'Link to email with attributes in parameter' => [
366  'Email address',
367  [
368  'parameter' => 'foo@bar.org - email-class "Write an email"',
369  ],
370  '<a href="mailto:foo@bar.org" title="Write an email" class="email-class">Email address</a>',
371  ],
372  'Link url using stdWrap' => [
373  'TYPO3',
374  [
375  'parameter' => 'http://typo3.org',
376  'parameter.' => [
377  'cObject' => 'TEXT',
378  'cObject.' => [
379  'value' => 'http://typo3.com',
380  ],
381  ],
382  ],
383  '<a href="http://typo3.com">TYPO3</a>',
384  ],
385  'Link url using stdWrap with class attribute in parameter' => [
386  'TYPO3',
387  [
388  'parameter' => 'http://typo3.org - url-class',
389  'parameter.' => [
390  'cObject' => 'TEXT',
391  'cObject.' => [
392  'value' => 'http://typo3.com',
393  ],
394  ],
395  ],
396  '<a href="http://typo3.com" class="url-class">TYPO3</a>',
397  ],
398  'Link url using stdWrap with class attribute in parameter and overridden target' => [
399  'TYPO3',
400  [
401  'parameter' => 'http://typo3.org default-target url-class',
402  'parameter.' => [
403  'cObject' => 'TEXT',
404  'cObject.' => [
405  'value' => 'http://typo3.com new-target different-url-class',
406  ],
407  ],
408  ],
409  '<a href="http://typo3.com" target="new-target" rel="noreferrer" class="different-url-class">TYPO3</a>',
410  ],
411  'Link url using stdWrap with class attribute in parameter and overridden target and returnLast' => [
412  'TYPO3',
413  [
414  'parameter' => 'http://typo3.org default-target url-class',
415  'parameter.' => [
416  'cObject' => 'TEXT',
417  'cObject.' => [
418  'value' => 'http://typo3.com new-target different-url-class',
419  ],
420  ],
421  'returnLast' => 'url',
422  ],
423  'http://typo3.com',
424  ],
425  'Open in new window' => [
426  'Nice Text',
427  [
428  'parameter' => 'https://example.com 13x84:target=myexample',
429  ],
430  '<a href="https://example.com" target="myexample" data-window-url="https://example.com" data-window-target="myexample" data-window-features="width=13,height=84" rel="noreferrer">Nice Text</a>',
431  ],
432  'Open in new window with window name' => [
433  'Nice Text',
434  [
435  'parameter' => 'https://example.com 13x84',
436  ],
437  '<a href="https://example.com" target="FEopenLink" data-window-url="https://example.com" data-window-target="FEopenLink" data-window-features="width=13,height=84" rel="noreferrer">Nice Text</a>',
438  ],
439  'Link to file' => [
440  'My file',
441  [
442  'directImageLink' => false,
443  'parameter' => 'fileadmin/foo.bar',
444  ],
445  '<a href="fileadmin/foo.bar">My file</a>',
446  ],
447  'Link to file without link text' => [
448  '',
449  [
450  'directImageLink' => false,
451  'parameter' => 'fileadmin/foo.bar',
452  ],
453  '<a href="fileadmin/foo.bar">fileadmin/foo.bar</a>',
454  ],
455  'Link to file with attributes' => [
456  'My file',
457  [
458  'parameter' => 'fileadmin/foo.bar',
459  'ATagParams' => 'class="file-class"',
460  'fileTarget' => '_blank',
461  'title' => 'Title of the file',
462  ],
463  '<a href="fileadmin/foo.bar" target="_blank" class="file-class" title="Title of the file">My file</a>',
464  ],
465  'Link to file with attributes and additional href' => [
466  'My file',
467  [
468  'parameter' => 'fileadmin/foo.bar',
469  'ATagParams' => 'href="foo-bar"',
470  'fileTarget' => '_blank',
471  'title' => 'Title of the file',
472  ],
473  '<a href="fileadmin/foo.bar" target="_blank" title="Title of the file">My file</a>',
474  ],
475  'Link to file with attributes and additional href and class' => [
476  'My file',
477  [
478  'parameter' => 'fileadmin/foo.bar',
479  'ATagParams' => 'href="foo-bar" class="file-class"',
480  'fileTarget' => '_blank',
481  'title' => 'Title of the file',
482  ],
483  '<a href="fileadmin/foo.bar" target="_blank" class="file-class" title="Title of the file">My file</a>',
484  ],
485  'Link to file with attributes and additional class and href' => [
486  'My file',
487  [
488  'parameter' => 'fileadmin/foo.bar',
489  'ATagParams' => 'class="file-class" href="foo-bar"',
490  'fileTarget' => '_blank',
491  'title' => 'Title of the file',
492  ],
493  '<a href="fileadmin/foo.bar" target="_blank" class="file-class" title="Title of the file">My file</a>',
494  ],
495  'Link to file with attributes and additional class and href and title' => [
496  'My file',
497  [
498  'parameter' => 'fileadmin/foo.bar',
499  'ATagParams' => 'class="file-class" href="foo-bar" title="foo-bar"',
500  'fileTarget' => '_blank',
501  'title' => 'Title of the file',
502  ],
503  '<a href="fileadmin/foo.bar" target="_blank" class="file-class" title="Title of the file">My file</a>',
504  ],
505  'Link to file with attributes and empty ATagParams' => [
506  'My file',
507  [
508  'parameter' => 'fileadmin/foo.bar',
509  'ATagParams' => '',
510  'fileTarget' => '_blank',
511  'title' => 'Title of the file',
512  ],
513  '<a href="fileadmin/foo.bar" target="_blank" title="Title of the file">My file</a>',
514  ],
515  'Link to file with attributes in parameter' => [
516  'My file',
517  [
518  'parameter' => 'fileadmin/foo.bar _blank file-class "Title of the file"',
519  ],
520  '<a href="fileadmin/foo.bar" target="_blank" title="Title of the file" class="file-class">My file</a>',
521  ],
522  'Link to file with script tag in name' => [
523  '',
524  [
525  'directImageLink' => false,
526  'parameter' => 'fileadmin/<script>alert(123)</script>',
527  ],
528  '<a href="fileadmin/&lt;script&gt;alert(123)&lt;/script&gt;">fileadmin/&lt;script&gt;alert(123)&lt;/script&gt;</a>',
529  ],
530  ];
531  }
532 
533  #[DataProvider('typolinkReturnsCorrectLinksDataProvider')]
534  #[Test]
535  public function ‪typolinkReturnsCorrectLinksAndUrls(string $linkText, array $configuration, string $expectedResult): void
536  {
537  $subject = new ‪ContentObjectRenderer();
538  $subject->setRequest($this->‪getPreparedRequest());
539  self::assertEquals($expectedResult, $subject->typoLink($linkText, $configuration));
540  }
541 
543  {
544  return [
545  'plain mail without mailto scheme' => [
546  [
547  'spamProtectEmailAddresses' => 0,
548  'spamProtectEmailAddresses_atSubst' => '',
549  'spamProtectEmailAddresses_lastDotSubst' => '',
550  ],
551  'some.body@test.typo3.org',
552  'some.body@test.typo3.org',
553  '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
554  ],
555  'plain mail with mailto scheme' => [
556  [
557  'spamProtectEmailAddresses' => 0,
558  'spamProtectEmailAddresses_atSubst' => '',
559  'spamProtectEmailAddresses_lastDotSubst' => '',
560  ],
561  'some.body@test.typo3.org',
562  'mailto:some.body@test.typo3.org',
563  '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
564  ],
565  'plain with at and dot substitution' => [
566  [
567  'spamProtectEmailAddresses' => 0,
568  'spamProtectEmailAddresses_atSubst' => '(at)',
569  'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
570  ],
571  'some.body@test.typo3.org',
572  'mailto:some.body@test.typo3.org',
573  '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
574  ],
575  'mono-alphabetic substitution offset +1' => [
576  [
577  'spamProtectEmailAddresses' => 1,
578  'spamProtectEmailAddresses_atSubst' => '',
579  'spamProtectEmailAddresses_lastDotSubst' => '',
580  ],
581  'some.body@test.typo3.org',
582  'mailto:some.body@test.typo3.org',
583  '<a href="#" data-mailto-token="nbjmup+tpnf/cpezAuftu/uzqp4/psh" data-mailto-vector="1">some.body(at)test.typo3.org</a>',
584  ],
585  'mono-alphabetic substitution offset +1 with at substitution' => [
586  [
587  'spamProtectEmailAddresses' => 1,
588  'spamProtectEmailAddresses_atSubst' => '@',
589  'spamProtectEmailAddresses_lastDotSubst' => '',
590  ],
591  'some.body@test.typo3.org',
592  'mailto:some.body@test.typo3.org',
593  '<a href="#" data-mailto-token="nbjmup+tpnf/cpezAuftu/uzqp4/psh" data-mailto-vector="1">some.body@test.typo3.org</a>',
594  ],
595  'mono-alphabetic substitution offset +1 with at and dot substitution' => [
596  [
597  'spamProtectEmailAddresses' => 1,
598  'spamProtectEmailAddresses_atSubst' => '(at)',
599  'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
600  ],
601  'some.body@test.typo3.org',
602  'mailto:some.body@test.typo3.org',
603  '<a href="#" data-mailto-token="nbjmup+tpnf/cpezAuftu/uzqp4/psh" data-mailto-vector="1">some.body(at)test.typo3(dot)org</a>',
604  ],
605  'mono-alphabetic substitution offset -1 with at and dot substitution' => [
606  [
607  'spamProtectEmailAddresses' => -1,
608  'spamProtectEmailAddresses_atSubst' => '(at)',
609  'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
610  ],
611  'some.body@test.typo3.org',
612  'mailto:some.body@test.typo3.org',
613  '<a href="#" data-mailto-token="lzhksn9rnld-ancxZsdrs-sxon2-nqf" data-mailto-vector="-1">some.body(at)test.typo3(dot)org</a>',
614  ],
615  'mono-alphabetic substitution offset -1 with at and dot markup substitution' => [
616  [
617  'spamProtectEmailAddresses' => -1,
618  'spamProtectEmailAddresses_atSubst' => '<span class="at"></span>',
619  'spamProtectEmailAddresses_lastDotSubst' => '<span class="dot"></span>',
620  ],
621  'some.body@test.typo3.org',
622  'mailto:some.body@test.typo3.org',
623  '<a href="#" data-mailto-token="lzhksn9rnld-ancxZsdrs-sxon2-nqf" data-mailto-vector="-1">some.body<span class="at"></span>test.typo3<span class="dot"></span>org</a>',
624  ],
625  'mono-alphabetic substitution offset 2 with at and dot substitution and encoded subject' => [
626  [
627  'spamProtectEmailAddresses' => 2,
628  'spamProtectEmailAddresses_atSubst' => '(at)',
629  'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
630  ],
631  'some.body@test.typo3.org',
632  'mailto:some.body@test.typo3.org?subject=foo%20bar',
633  '<a href="#" data-mailto-token="ocknvq,uqog0dqfaBvguv0varq50qti?uwdlgev=hqq%42dct" data-mailto-vector="2">some.body(at)test.typo3(dot)org</a>',
634  ],
635  ];
636  }
637 
638  #[DataProvider('typolinkReturnsCorrectLinkForSpamEncryptedEmailsDataProvider')]
639  #[Test]
640  public function ‪typolinkReturnsCorrectLinkForSpamEncryptedEmails(array $tsfeConfig, string $linkText, string $parameter, string $expected): void
641  {
642  $frontendTypoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
643  $frontendTypoScript->setConfigArray($tsfeConfig);
644  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $frontendTypoScript);
645  $subject = new ‪ContentObjectRenderer();
646  $subject->setRequest($request);
647  self::assertEquals($expected, $subject->typoLink($linkText, ['parameter' => $parameter]));
648  }
649 
651  {
652  return [
653  'Link to file' => [
654  'My file',
655  [
656  'directImageLink' => false,
657  'parameter' => 'fileadmin/foo.bar',
658  ],
659  '/',
660  '<a href="/fileadmin/foo.bar">My file</a>',
661  ],
662  'Link to file with longer absRefPrefix' => [
663  'My file',
664  [
665  'directImageLink' => false,
666  'parameter' => 'fileadmin/foo.bar',
667  ],
668  '/sub/',
669  '<a href="/sub/fileadmin/foo.bar">My file</a>',
670  ],
671  'Link to absolute file' => [
672  'My file',
673  [
674  'directImageLink' => false,
675  'parameter' => '/images/foo.bar',
676  ],
677  '/',
678  '<a href="/images/foo.bar">My file</a>',
679  ],
680  'Link to absolute file with longer absRefPrefix' => [
681  'My file',
682  [
683  'directImageLink' => false,
684  'parameter' => '/images/foo.bar',
685  ],
686  '/sub/',
687  '<a href="/images/foo.bar">My file</a>',
688  ],
689  'Link to absolute file with identical longer absRefPrefix' => [
690  'My file',
691  [
692  'directImageLink' => false,
693  'parameter' => '/sub/fileadmin/foo.bar',
694  ],
695  '/sub/',
696  '<a href="/sub/fileadmin/foo.bar">My file</a>',
697  ],
698  'Link to file with empty absRefPrefix' => [
699  'My file',
700  [
701  'directImageLink' => false,
702  'parameter' => 'fileadmin/foo.bar',
703  ],
704  '',
705  '<a href="fileadmin/foo.bar">My file</a>',
706  ],
707  'Link to absolute file with empty absRefPrefix' => [
708  'My file',
709  [
710  'directImageLink' => false,
711  'parameter' => '/fileadmin/foo.bar',
712  ],
713  '',
714  '<a href="/fileadmin/foo.bar">My file</a>',
715  ],
716  'Link to file with attributes with absRefPrefix' => [
717  'My file',
718  [
719  'parameter' => 'fileadmin/foo.bar',
720  'ATagParams' => 'class="file-class"',
721  'title' => 'Title of the file',
722  ],
723  '/',
724  '<a href="/fileadmin/foo.bar" class="file-class" title="Title of the file">My file</a>',
725  ],
726  'Link to file with attributes with longer absRefPrefix' => [
727  'My file',
728  [
729  'parameter' => 'fileadmin/foo.bar',
730  'ATagParams' => 'class="file-class"',
731  'title' => 'Title of the file',
732  ],
733  '/sub/',
734  '<a href="/sub/fileadmin/foo.bar" class="file-class" title="Title of the file">My file</a>',
735  ],
736  'Link to absolute file with attributes with absRefPrefix' => [
737  'My file',
738  [
739  'parameter' => '/images/foo.bar',
740  'ATagParams' => 'class="file-class"',
741  'title' => 'Title of the file',
742  ],
743  '/',
744  '<a href="/images/foo.bar" class="file-class" title="Title of the file">My file</a>',
745  ],
746  'Link to absolute file with attributes with longer absRefPrefix' => [
747  'My file',
748  [
749  'parameter' => '/images/foo.bar',
750  'ATagParams' => 'class="file-class"',
751  'title' => 'Title of the file',
752  ],
753  '/sub/',
754  '<a href="/images/foo.bar" class="file-class" title="Title of the file">My file</a>',
755  ],
756  'Link to absolute file with attributes with identical longer absRefPrefix' => [
757  'My file',
758  [
759  'parameter' => '/sub/fileadmin/foo.bar',
760  'ATagParams' => 'class="file-class"',
761  'title' => 'Title of the file',
762  ],
763  '/sub/',
764  '<a href="/sub/fileadmin/foo.bar" class="file-class" title="Title of the file">My file</a>',
765  ],
766  ];
767  }
768 
769  #[DataProvider('typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider')]
770  #[Test]
771  public function ‪typolinkReturnsCorrectLinksForFilesWithAbsRefPrefix(string $linkText, array $configuration, string $absRefPrefix, string $expectedResult): void
772  {
773  $tsfe = $this->getMockBuilder(TypoScriptFrontendController::class)->disableOriginalConstructor()->getMock();
774  $tsfe->absRefPrefix = $absRefPrefix;
775  $request = new ‪ServerRequest();
776  $subject = new ‪ContentObjectRenderer($tsfe);
777  $subject->setRequest($request);
778  self::assertEquals($expectedResult, $subject->typoLink($linkText, $configuration));
779  }
780 
781  public static function ‪typoLinkProperlyEncodesLinkResultDataProvider(): array
782  {
783  return [
784  'Link to file' => [
785  'My file',
786  [
787  'directImageLink' => false,
788  'parameter' => '/fileadmin/foo.bar',
789  'returnLast' => 'result',
790  ],
791  json_encode([
792  'href' => '/fileadmin/foo.bar',
793  'target' => null,
794  'class' => null,
795  'title' => null,
796  'linkText' => 'My file',
797  'additionalAttributes' => [],
798  ]),
799  ],
800  'Link example' => [
801  'My example',
802  [
803  'directImageLink' => false,
804  'parameter' => 'https://example.tld',
805  'returnLast' => 'result',
806  ],
807  json_encode([
808  'href' => 'https://example.tld',
809  'target' => null,
810  'class' => null,
811  'title' => null,
812  'linkText' => 'My example',
813  'additionalAttributes' => [],
814  ]),
815  ],
816  'Link to file with attributes' => [
817  'My file',
818  [
819  'parameter' => '/fileadmin/foo.bar',
820  'ATagParams' => 'class="file-class"',
821  'returnLast' => 'result',
822  ],
823  json_encode([
824  'href' => '/fileadmin/foo.bar',
825  'target' => null,
826  'class' => 'file-class',
827  'title' => null,
828  'linkText' => 'My file',
829  'additionalAttributes' => [],
830  ]),
831  ],
832  'Link parsing' => [
833  'Url',
834  [
835  'parameter' => 'https://example.com _blank css-class "test title"',
836  'returnLast' => 'result',
837  ],
838  json_encode([
839  'href' => 'https://example.com',
840  'target' => '_blank',
841  'class' => 'css-class',
842  'title' => 'test title',
843  'linkText' => 'Url',
844  'additionalAttributes' => ['rel' => 'noreferrer'],
845  ]),
846  ],
847  ];
848  }
849 
850  #[DataProvider('typoLinkProperlyEncodesLinkResultDataProvider')]
851  #[Test]
852  public function ‪typoLinkProperlyEncodesLinkResult(string $linkText, array $configuration, string $expectedResult): void
853  {
854  $subject = new ‪ContentObjectRenderer();
855  $subject->setRequest($this->‪getPreparedRequest());
856  self::assertEquals($expectedResult, $subject->typoLink($linkText, $configuration));
857  }
858 
859  #[Test]
861  {
862  $tsfe = $this->getMockBuilder(TypoScriptFrontendController::class)->disableOriginalConstructor()->getMock();
863  $subject = new ‪ContentObjectRenderer($tsfe);
864  $subject->setRequest($this->‪getPreparedRequest());
865  $subject->start([], 'tt_content');
866 
867  $expected = '';
868  $actual = $subject->searchWhere('ab', 'header,bodytext', 'tt_content');
869  self::assertEquals($expected, $actual);
870  }
871 
872  #[Test]
873  public function ‪libParseFuncProperlyKeepsTagsUnescaped(): void
874  {
875  $tsfe = $this->getMockBuilder(TypoScriptFrontendController::class)->disableOriginalConstructor()->getMock();
876  $subject = new ‪ContentObjectRenderer($tsfe);
877  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
878  $typoScript->setConfigArray([]);
879  $request = $this->‪getPreparedRequest()->withAttribute('frontend.typoscript', $typoScript);
880  $subject->setRequest($request);
881  $subject->setLogger(new NullLogger());
882  $input = 'This is a simple inline text, no wrapping configured';
883  $result = $subject->parseFunc($input, $this->‪getLibParseFunc());
884  self::assertEquals($input, $result);
885 
886  $input = '<p>A one liner paragraph</p>';
887  $result = $subject->parseFunc($input, $this->‪getLibParseFunc());
888  self::assertEquals($input, $result);
889 
890  $input = 'A one liner paragraph
891 And another one';
892  $result = $subject->parseFunc($input, $this->‪getLibParseFunc());
893  self::assertEquals($input, $result);
894 
895  $input = '<p>A one liner paragraph</p><p>And another one and the spacing is kept</p>';
896  $result = $subject->parseFunc($input, $this->‪getLibParseFunc());
897  self::assertEquals($input, $result);
898 
899  $input = '<p>text to a <a href="https://www.example.com">an external page</a>.</p>';
900  $result = $subject->parseFunc($input, $this->‪getLibParseFunc());
901  self::assertEquals($input, $result);
902  }
903 
904  protected function ‪getLibParseFunc(): array
905  {
906  return [
907  'htmlSanitize' => '1',
908  'makelinks' => '1',
909  'makelinks.' => [
910  'http.' => [
911  'keep' => '{$styles.content.links.keep}',
912  'extTarget' => '',
913  'mailto.' => [
914  'keep' => 'path',
915  ],
916  ],
917  ],
918  'tags.' => [
919  'link' => 'TEXT',
920  'link.' => [
921  'current' => '1',
922  'typolink.' => [
923  'parameter.' => [
924  'data' => 'parameters : allParams',
925  ],
926  ],
927  'parseFunc.' => [
928  'constants' => '1',
929  ],
930  ],
931  'a' => 'TEXT',
932  'a.' => [
933  'current' => '1',
934  'typolink.' => [
935  'parameter.' => [
936  'data' => 'parameters:href',
937  ],
938  ],
939  ],
940  ],
941 
942  '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',
943  'denyTags' => '*',
944  'sword' => '<span class="csc-sword">|</span>',
945  'constants' => '1',
946  'nonTypoTagStdWrap.' => [
947  'HTMLparser' => '1',
948  'HTMLparser.' => [
949  'keepNonMatchedTags' => '1',
950  'htmlSpecialChars' => '2',
951  ],
952  ],
953  ];
954  }
955 
956  public static function ‪checkIfReturnsExpectedValuesDataProvider(): iterable
957  {
958  yield 'isNull returns true if stdWrap returns null' => [
959  'configuration' => [
960  'isNull.' => [
961  'field' => 'unknown',
962  ],
963  ],
964  'expected' => true,
965  ];
966 
967  yield 'isNull returns false if stdWrap returns not null' => [
968  'configuration' => [
969  'isNull.' => [
970  'field' => 'known',
971  ],
972  ],
973  'expected' => false,
974  ];
975  }
976 
977  #[DataProvider('checkIfReturnsExpectedValuesDataProvider')]
978  #[Test]
979  public function ‪checkIfReturnsExpectedValues(array $configuration, bool $expected): void
980  {
981  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
982  $subject->data = [
983  'known' => 'somevalue',
984  ];
985  self::assertSame($expected, $subject->checkIf($configuration));
986  }
987 
988  public static function ‪imageLinkWrapWrapsTheContentAsConfiguredDataProvider(): iterable
989  {
990  $width = 900;
991  $height = 600;
992  $processingWidth = $width . 'm';
993  $processingHeight = $height . 'm';
994  $defaultConfiguration = [
995  'wrap' => '<a href="javascript:close();"> | </a>',
996  'width' => $processingWidth,
997  'height' => $processingHeight,
998  'JSwindow' => '1',
999  'JSwindow.' => [
1000  'newWindow' => '0',
1001  ],
1002  'crop.' => [
1003  'data' => 'file:current:crop',
1004  ],
1005  'linkParams.' => [
1006  'ATagParams.' => [
1007  'dataWrap' => 'class="lightbox" rel="lightbox[{field:uid}]"',
1008  ],
1009  ],
1010  'enable' => true,
1011  ];
1012  $imageTag = '<img class="image-embed-item" src="/fileadmin/_processed_/team-t3board10-processed.jpg" width="500" height="300" loading="lazy" alt="" />';
1013  $windowFeatures = 'width=' . $width . ',height=' . $height . ',status=0,menubar=0';
1014 
1015  $configurationEnableFalse = $defaultConfiguration;
1016  $configurationEnableFalse['enable'] = false;
1017  yield 'enable => false configuration returns image tag as is.' => [
1018  'content' => $imageTag,
1019  'configuration' => $configurationEnableFalse,
1020  'expected' => [$imageTag => true],
1021  ];
1022 
1023  yield 'image is wrapped with link tag.' => [
1024  'content' => $imageTag,
1025  'configuration' => $defaultConfiguration,
1026  'expected' => [
1027  '<a href="index.php?eID=tx_cms_showpic&amp;file=1' => true,
1028  $imageTag . '</a>' => true,
1029  'data-window-features="' . $windowFeatures => true,
1030  'data-window-target="thePicture"' => true,
1031  ' target="thePicture' => true,
1032  ],
1033  ];
1034 
1035  $paramsConfiguration = $defaultConfiguration;
1036  $windowFeaturesOverrides = 'width=420,status=1,menubar=1,foo=bar';
1037  $windowFeaturesOverriddenExpected = 'width=420,height=' . $height . ',status=1,menubar=1,foo=bar';
1038  $paramsConfiguration['JSwindow.']['params'] = $windowFeaturesOverrides;
1039  yield 'JSWindow.params overrides windowParams' => [
1040  'content' => $imageTag,
1041  'configuration' => $paramsConfiguration,
1042  'expected' => [
1043  'data-window-features="' . $windowFeaturesOverriddenExpected => true,
1044  ],
1045  ];
1046 
1047  $newWindowConfiguration = $defaultConfiguration;
1048  $newWindowConfiguration['JSwindow.']['newWindow'] = '1';
1049  yield 'data-window-target is not "thePicture" if newWindow = 1 but an md5 hash of the url.' => [
1050  'content' => $imageTag,
1051  'configuration' => $newWindowConfiguration,
1052  'expected' => [
1053  'data-window-target="thePicture' => false,
1054  ],
1055  ];
1056 
1057  $newWindowConfiguration = $defaultConfiguration;
1058  $newWindowConfiguration['JSwindow.']['expand'] = '20,40';
1059  $windowFeaturesExpand = 'width=' . ($width + 20) . ',height=' . ($height + 40) . ',status=0,menubar=0';
1060  yield 'expand increases the window size by its value' => [
1061  'content' => $imageTag,
1062  'configuration' => $newWindowConfiguration,
1063  'expected' => [
1064  'data-window-features="' . $windowFeaturesExpand => true,
1065  ],
1066  ];
1067 
1068  $directImageLinkConfiguration = $defaultConfiguration;
1069  $directImageLinkConfiguration['directImageLink'] = '1';
1070  yield 'Direct image link does not use eID and links directly to the image.' => [
1071  'content' => $imageTag,
1072  'configuration' => $directImageLinkConfiguration,
1073  'expected' => [
1074  'index.php?eID=tx_cms_showpic&amp;file=1' => false,
1075  '<a href="fileadmin/_processed_' => true,
1076  'data-window-url="fileadmin/_processed_' => true,
1077  ],
1078  ];
1079 
1080  // @todo Error: Object of class TYPO3\CMS\Core\Resource\FileReference could not be converted to string
1081  // $altUrlConfiguration = $defaultConfiguration;
1082  // $altUrlConfiguration['JSwindow.']['altUrl'] = '/alternative-url';
1083  // yield 'JSwindow.altUrl forces an alternative url.' => [
1084  // 'content' => $imageTag,
1085  // 'configuration' => $altUrlConfiguration,
1086  // 'expected' => [
1087  // '<a href="/alternative-url' => true,
1088  // 'data-window-url="/alternative-url' => true,
1089  // ],
1090  // ];
1091 
1092  $altUrlConfigurationNoDefault = $defaultConfiguration;
1093  $altUrlConfigurationNoDefault['JSwindow.']['altUrl'] = '/alternative-url';
1094  $altUrlConfigurationNoDefault['JSwindow.']['altUrl_noDefaultParams'] = '1';
1095  yield 'JSwindow.altUrl_noDefaultParams removes the default ?file= params' => [
1096  'content' => $imageTag,
1097  'configuration' => $altUrlConfigurationNoDefault,
1098  'expected' => [
1099  '<a href="/alternative-url' => true,
1100  'data-window-url="/alternative-url' => true,
1101  'data-window-url="/alternative-url?file=' => false,
1102  ],
1103  ];
1104 
1105  $targetConfiguration = $defaultConfiguration;
1106  $targetConfiguration['target'] = 'myTarget';
1107  yield 'Setting target overrides the default target "thePicture.' => [
1108  'content' => $imageTag,
1109  'configuration' => $targetConfiguration,
1110  'expected' => [
1111  ' target="myTarget"' => true,
1112  'data-window-target="thePicture"' => true,
1113  ],
1114  ];
1115 
1116  $parameters = [
1117  'sample' => '1',
1118  'width' => $processingWidth,
1119  'height' => $processingHeight,
1120  'effects' => 'gamma=1.3 | flip | rotate=180',
1121  'bodyTag' => '<body style="margin:0; background:#fff;">',
1122  'title' => 'My Title',
1123  'wrap' => '<div class="my-wrap">|</div>',
1124  'crop' => '{"default":{"cropArea":{"x":0,"y":0,"width":1,"height":1},"selectedRatio":"NaN","focusArea":null}}',
1125  ];
1126  $parameterConfiguration = array_replace($defaultConfiguration, $parameters);
1127  $expectedParameters = $parameters;
1128  $expectedParameters['sample'] = 1;
1129  yield 'Setting one of [width, height, effects, bodyTag, title, wrap, crop, sample] will add them to the parameter list.' => [
1130  'content' => $imageTag,
1131  'configuration' => $parameterConfiguration,
1132  'expected' => [],
1133  'expectedParams' => $expectedParameters,
1134  ];
1135 
1136  $stdWrapConfiguration = $defaultConfiguration;
1137  $stdWrapConfiguration['stdWrap.'] = [
1138  'append' => 'TEXT',
1139  'append.' => [
1140  'value' => 'appendedString',
1141  ],
1142  ];
1143  yield 'stdWrap is called upon the whole content.' => [
1144  'content' => $imageTag,
1145  'configuration' => $stdWrapConfiguration,
1146  'expected' => [
1147  'appendedString' => true,
1148  ],
1149  ];
1150  }
1151 
1152  #[DataProvider('imageLinkWrapWrapsTheContentAsConfiguredDataProvider')]
1153  #[Test]
1154  public function ‪imageLinkWrapWrapsTheContentAsConfigured(string $content, array $configuration, array $expected, array $expectedParams = []): void
1155  {
1156  $this->importCSVDataSet(__DIR__ . '/DataSet/FileReferences.csv');
1157  $fileReferenceData = [
1158  'uid' => 1,
1159  'uid_local' => 1,
1160  'crop' => '{"default":{"cropArea":{"x":0,"y":0,"width":1,"height":1},"selectedRatio":"NaN","focusArea":null}}',
1161  ];
1162  $fileReference = new ‪FileReference($fileReferenceData);
1163  $typoScriptFrontendController = GeneralUtility::makeInstance(TypoScriptFrontendController::class);
1164  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class, $typoScriptFrontendController);
1165  $subject->setCurrentFile($fileReference);
1166  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1167  $typoScript->setConfigArray([]);
1168  $request = $this->‪getPreparedRequest()->withAttribute('frontend.typoscript', $typoScript);
1169  $subject->setRequest($request);
1170  $result = $subject->imageLinkWrap($content, $fileReference, $configuration);
1171 
1172  foreach ($expected as $expectedString => $shouldContain) {
1173  if ($shouldContain) {
1174  self::assertStringContainsString($expectedString, $result);
1175  } else {
1176  self::assertStringNotContainsString($expectedString, $result);
1177  }
1178  }
1179 
1180  if ($expectedParams !== []) {
1181  preg_match('@href="(.*)"@U', $result, $matches);
1182  self::assertArrayHasKey(1, $matches);
1183  ‪$url = parse_url(html_entity_decode($matches[1]));
1184  parse_str(‪$url['query'], $queryResult);
1185  $base64_string = implode('', $queryResult['parameters']);
1186  $base64_decoded = base64_decode($base64_string);
1187  $jsonDecodedArray = json_decode($base64_decoded, true);
1188  self::assertSame($expectedParams, $jsonDecodedArray);
1189  }
1190  }
1191 
1192  #[Test]
1194  {
1195  $this->importCSVDataSet(__DIR__ . '/DataSet/FileReferences.csv');
1196  $fileReferenceData = [
1197  'uid' => 1,
1198  'uid_local' => 1,
1199  'crop' => '{"default":{"cropArea":{"x":0,"y":0,"width":0.5,"height":0.5},"selectedRatio":"NaN","focusArea":null}}',
1200  ];
1201  $fileReference = new ‪FileReference($fileReferenceData);
1202 
1203  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
1204  $result = $subject->getImgResource($fileReference, []);
1205 
1206  $expectedWidth = 512;
1207  $expectedHeight = 342;
1208 
1209  self::assertEquals($expectedWidth, $result->getWidth());
1210  self::assertEquals($expectedHeight, $result->getHeight());
1211  }
1212 
1213  #[Test]
1215  {
1216  $afterContentObjectRendererInitializedEvent = null;
1217 
1219  $container = $this->get('service_container');
1220  $container->set(
1221  'after-content-object-renderer-initialized-listener',
1222  static function (‪AfterContentObjectRendererInitializedEvent $event) use (&$afterContentObjectRendererInitializedEvent) {
1223  $afterContentObjectRendererInitializedEvent = $event;
1224  $afterContentObjectRendererInitializedEvent->‪getContentObjectRenderer()->data['foo'] = 'baz';
1225  $afterContentObjectRendererInitializedEvent->getContentObjectRenderer()->setCurrentVal('foo current val');
1226  }
1227  );
1228 
1229  $eventListener = $container->get(ListenerProvider::class);
1230  $eventListener->addListener(AfterContentObjectRendererInitializedEvent::class, 'after-content-object-renderer-initialized-listener');
1231 
1232  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
1233  $subject->start(['foo' => 'bar'], 'aTable');
1234 
1235  self::assertInstanceOf(AfterContentObjectRendererInitializedEvent::class, $afterContentObjectRendererInitializedEvent);
1236 
1237  $modifiedContentObjectRenderer = $afterContentObjectRendererInitializedEvent->getContentObjectRenderer();
1238 
1239  self::assertEquals($subject, $modifiedContentObjectRenderer);
1240  self::assertEquals(
1241  [
1242  'foo' => 'baz',
1243  $modifiedContentObjectRenderer->currentValKey => 'foo current val',
1244  ],
1245  $modifiedContentObjectRenderer->data
1246  );
1247  self::assertEquals('aTable', $modifiedContentObjectRenderer->getCurrentTable());
1248  self::assertEquals('foo current val', $modifiedContentObjectRenderer->getCurrentVal());
1249  }
1250 
1251  #[Test]
1252  public function ‪afterGetDataResolvedEventIsCalled(): void
1253  {
1254  $afterGetDataResolvedEvent = null;
1255 
1257  $container = $this->get('service_container');
1258  $container->set(
1259  'after-get-data-resolved-listener',
1260  static function (‪AfterGetDataResolvedEvent $event) use (&$afterGetDataResolvedEvent) {
1261  $afterGetDataResolvedEvent = $event;
1262  $event->‪setResult('modified-result');
1263  }
1264  );
1265 
1266  $eventListener = $container->get(ListenerProvider::class);
1267  $eventListener->addListener(AfterGetDataResolvedEvent::class, 'after-get-data-resolved-listener');
1268 
1269  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
1270  $subject->start(['foo' => 'bar'], 'aTable');
1271  $subject->getData('field:title', ['title' => 'title']);
1272 
1273  self::assertInstanceOf(AfterGetDataResolvedEvent::class, $afterGetDataResolvedEvent);
1274  self::assertEquals($subject, $afterGetDataResolvedEvent->getContentObjectRenderer());
1275  self::assertEquals('field:title', $afterGetDataResolvedEvent->getParameterString());
1276  self::assertEquals(['title' => 'title'], $afterGetDataResolvedEvent->getAlternativeFieldArray());
1277  self::assertEquals('modified-result', $afterGetDataResolvedEvent->getResult());
1278  }
1279 
1280  #[Test]
1281  public function ‪afterImageResourceResolvedEventIsCalled(): void
1282  {
1283  $afterImageResourceResolvedEvent = null;
1284 
1286  $container = $this->get('service_container');
1287  $container->set(
1288  'after-image-resource-resolved-listener',
1289  static function (‪AfterImageResourceResolvedEvent $event) use (&$afterImageResourceResolvedEvent) {
1290  $afterImageResourceResolvedEvent = $event;
1291  $modifiedImageResource = $afterImageResourceResolvedEvent->‪getImageResource()?->withPublicUrl('modified-public-url');
1292  $event->‪setImageResource($modifiedImageResource);
1293  }
1294  );
1295 
1296  $eventListener = $container->get(ListenerProvider::class);
1297  $eventListener->addListener(AfterImageResourceResolvedEvent::class, 'after-image-resource-resolved-listener');
1298 
1299  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
1300  $subject->start(['foo' => 'bar'], 'aTable');
1301  $subject->getImgResource('GIFBUILDER', ['foo' => 'bar']);
1302 
1303  self::assertInstanceOf(AfterImageResourceResolvedEvent::class, $afterImageResourceResolvedEvent);
1304  self::assertEquals('GIFBUILDER', $afterImageResourceResolvedEvent->getFile());
1305  self::assertEquals(['foo' => 'bar'], $afterImageResourceResolvedEvent->getFileArray());
1306  self::assertEquals('modified-public-url', $afterImageResourceResolvedEvent->getImageResource()->getPublicUrl());
1307  }
1308 
1309  #[Test]
1310  public function ‪enhanceStdWrapEventIsCalled(): void
1311  {
1312  $wrap = '<h1>|</h1>';
1313  $content = 'modified content';
1314  $enhanceStdWrapEvent = null;
1315 
1317  $container = $this->get('service_container');
1318  $container->set(
1319  'enhance-stdWrap-listener',
1320  static function (‪EnhanceStdWrapEvent $event) use (&$enhanceStdWrapEvent, $content) {
1321  $enhanceStdWrapEvent = $event;
1322  $event->‪setContent($content);
1323  }
1324  );
1325 
1326  $eventListener = $container->get(ListenerProvider::class);
1327  $eventListener->addListener(EnhanceStdWrapEvent::class, 'enhance-stdWrap-listener');
1328 
1329  $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
1330  $result = $subject->stdWrap('Test', ['wrap' => $wrap]);
1331 
1332  self::assertInstanceOf(EnhanceStdWrapEvent::class, $enhanceStdWrapEvent);
1333  self::assertEquals($content, $result);
1334  self::assertEquals($content, $enhanceStdWrapEvent->getContent());
1335  self::assertEquals($wrap, $enhanceStdWrapEvent->getConfiguration()['wrap']);
1336  self::assertEquals($subject, $enhanceStdWrapEvent->getContentObjectRenderer());
1337  }
1338 }
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typoLinkProperlyEncodesLinkResult
‪typoLinkProperlyEncodesLinkResult(string $linkText, array $configuration, string $expectedResult)
Definition: ContentObjectRendererTest.php:851
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinksAndUrls
‪typolinkReturnsCorrectLinksAndUrls(string $linkText, array $configuration, string $expectedResult)
Definition: ContentObjectRendererTest.php:534
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\enhanceStdWrapEventIsCalled
‪enhanceStdWrapEventIsCalled()
Definition: ContentObjectRendererTest.php:1309
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\imageLinkWrapWrapsTheContentAsConfiguredDataProvider
‪static imageLinkWrapWrapsTheContentAsConfiguredDataProvider()
Definition: ContentObjectRendererTest.php:987
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinkForSpamEncryptedEmailsDataProvider
‪static typolinkReturnsCorrectLinkForSpamEncryptedEmailsDataProvider()
Definition: ContentObjectRendererTest.php:541
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typoLinkProperlyEncodesLinkResultDataProvider
‪static typoLinkProperlyEncodesLinkResultDataProvider()
Definition: ContentObjectRendererTest.php:780
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\checkIfReturnsExpectedValues
‪checkIfReturnsExpectedValues(array $configuration, bool $expected)
Definition: ContentObjectRendererTest.php:978
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\getLibParseFunc
‪getLibParseFunc()
Definition: ContentObjectRendererTest.php:903
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait
Definition: SiteBasedTestTrait.php:37
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterContentObjectRendererInitializedEvent
Definition: AfterContentObjectRendererInitializedEvent.php:26
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\afterContentObjectRendererInitializedEventIsCalled
‪afterContentObjectRendererInitializedEventIsCalled()
Definition: ContentObjectRendererTest.php:1213
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\writeSiteConfiguration
‪writeSiteConfiguration(string $identifier, array $site=[], array $languages=[], array $errorHandling=[])
Definition: SiteBasedTestTrait.php:50
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterImageResourceResolvedEvent
Definition: AfterImageResourceResolvedEvent.php:28
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:37
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildSiteConfiguration
‪buildSiteConfiguration(int $rootPageId, string $base='')
Definition: SiteBasedTestTrait.php:88
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\setUp
‪setUp()
Definition: ContentObjectRendererTest.php:61
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\getImgResourceRespectsFileReferenceObjectCropData
‪getImgResourceRespectsFileReferenceObjectCropData()
Definition: ContentObjectRendererTest.php:1192
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildErrorHandlingConfiguration
‪buildErrorHandlingConfiguration(string $handler, array $codes)
Definition: SiteBasedTestTrait.php:142
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\afterGetDataResolvedEventIsCalled
‪afterGetDataResolvedEventIsCalled()
Definition: ContentObjectRendererTest.php:1251
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\$pathsToProvideInTestInstance
‪array $pathsToProvideInTestInstance
Definition: ContentObjectRendererTest.php:59
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\afterImageResourceResolvedEventIsCalled
‪afterImageResourceResolvedEventIsCalled()
Definition: ContentObjectRendererTest.php:1280
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterImageResourceResolvedEvent\getImageResource
‪getImageResource()
Definition: AfterImageResourceResolvedEvent.php:45
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinkForSpamEncryptedEmails
‪typolinkReturnsCorrectLinkForSpamEncryptedEmails(array $tsfeConfig, string $linkText, string $parameter, string $expected)
Definition: ContentObjectRendererTest.php:639
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\getPreparedRequest
‪getPreparedRequest()
Definition: ContentObjectRendererTest.php:77
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typoLinkLogsErrorIfNoLinkResolvingIsPossible
‪typoLinkLogsErrorIfNoLinkResolvingIsPossible()
Definition: ContentObjectRendererTest.php:244
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest
Definition: ContentObjectRendererTest.php:53
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\LANGUAGE_PRESETS
‪const LANGUAGE_PRESETS
Definition: ContentObjectRendererTest.php:55
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinksForFilesWithAbsRefPrefix
‪typolinkReturnsCorrectLinksForFilesWithAbsRefPrefix(string $linkText, array $configuration, string $absRefPrefix, string $expectedResult)
Definition: ContentObjectRendererTest.php:770
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:39
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typoLinkReturnsOnlyLinkTextIfNoLinkResolvingIsPossible
‪typoLinkReturnsOnlyLinkTextIfNoLinkResolvingIsPossible()
Definition: ContentObjectRendererTest.php:231
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\libParseFuncProperlyKeepsTagsUnescaped
‪libParseFuncProperlyKeepsTagsUnescaped()
Definition: ContentObjectRendererTest.php:872
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinksDataProvider
‪static typolinkReturnsCorrectLinksDataProvider()
Definition: ContentObjectRendererTest.php:258
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\checkIfReturnsExpectedValuesDataProvider
‪static checkIfReturnsExpectedValuesDataProvider()
Definition: ContentObjectRendererTest.php:955
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:58
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildDefaultLanguageConfiguration
‪buildDefaultLanguageConfiguration(string $identifier, string $base)
Definition: SiteBasedTestTrait.php:98
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\TypoScript\AST\Node\RootNode
Definition: RootNode.php:26
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkLinkResultIsInstanceOfLinkResultInterface
‪typolinkLinkResultIsInstanceOfLinkResultInterface()
Definition: ContentObjectRendererTest.php:222
‪TYPO3\CMS\Frontend\ContentObject\Event\EnhanceStdWrapEvent\setContent
‪setContent(string $content)
Definition: EnhanceStdWrapEvent.php:48
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterGetDataResolvedEvent\setResult
‪setResult(mixed $result)
Definition: AfterGetDataResolvedEvent.php:39
‪TYPO3\CMS\Core\Log\Logger
Definition: Logger.php:28
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\searchWhereWithTooShortSearchWordWillReturnValidWhereStatement
‪searchWhereWithTooShortSearchWordWillReturnValidWhereStatement()
Definition: ContentObjectRendererTest.php:859
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\getQueryDataProvider
‪static getQueryDataProvider()
Definition: ContentObjectRendererTest.php:83
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\imageLinkWrapWrapsTheContentAsConfigured
‪imageLinkWrapWrapsTheContentAsConfigured(string $content, array $configuration, array $expected, array $expectedParams=[])
Definition: ContentObjectRendererTest.php:1153
‪TYPO3\CMS\Frontend\ContentObject\Event\EnhanceStdWrapEvent
Definition: EnhanceStdWrapEvent.php:36
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject
Definition: ContentContentObjectTest.php:18
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\TypoScript\FrontendTypoScript
Definition: FrontendTypoScript.php:30
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider
‪static typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider()
Definition: ContentObjectRendererTest.php:649
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\Tests\Functional\ContentObject\ContentObjectRendererTest\getQuery
‪getQuery(string $table, array $conf, string $expected)
Definition: ContentObjectRendererTest.php:179
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterGetDataResolvedEvent
Definition: AfterGetDataResolvedEvent.php:26
‪TYPO3\CMS\Core\EventDispatcher\ListenerProvider
Definition: ListenerProvider.php:30
‪TYPO3\CMS\Core\Http\NormalizedParams\createFromRequest
‪static static createFromRequest(ServerRequestInterface $request, array $systemConfiguration=null)
Definition: NormalizedParams.php:840
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterImageResourceResolvedEvent\setImageResource
‪setImageResource(?ImageResource $imageResource)
Definition: AfterImageResourceResolvedEvent.php:50
‪TYPO3\CMS\Core\Resource\Exception\InvalidPathException
Definition: InvalidPathException.php:23
‪TYPO3\CMS\Frontend\Page\PageInformation
Definition: PageInformation.php:28
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterContentObjectRendererInitializedEvent\getContentObjectRenderer
‪getContentObjectRenderer()
Definition: AfterContentObjectRendererInitializedEvent.php:31
‪TYPO3\CMS\Core\Database\Platform\PlatformHelper
Definition: PlatformHelper.php:26