‪TYPO3CMS  ‪main
TypoLinkGeneratorTest.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\Http\Message\ResponseInterface;
30 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
31 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
32 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\AbstractInstruction;
33 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\ArrayValueInstruction;
34 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\TypoScriptInstruction;
35 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
36 
38 {
40  'typo3/sysext/frontend/Tests/Functional/Fixtures/Images/typo3-logo.png' => 'fileadmin/logo.png',
41  ];
42 
43  protected function ‪setUp(): void
44  {
45  parent::setUp();
46 
48  'acme-com',
49  $this->‪buildSiteConfiguration(1000, 'https://acme.com/'),
50  [
51  $this->‪buildDefaultLanguageConfiguration('EN', 'https://acme.us/'),
52  $this->‪buildLanguageConfiguration('FR', 'https://acme.fr/', ['EN']),
53  $this->‪buildLanguageConfiguration('FR-CA', 'https://acme.ca/', ['FR', 'EN']),
54  ]
55  );
56 
57  $this->withDatabaseSnapshot(function () {
58  $this->importCSVDataSet(__DIR__ . '/../Fixtures/be_users.csv');
59  $backendUser = $this->setUpBackendUser(1);
60  ‪$GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser);
61  $scenarioFile = __DIR__ . '/Fixtures/TypoLinkScenario.yaml';
62  $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
63  $writer = DataHandlerWriter::withBackendUser($backendUser);
64  $writer->invokeFactory($factory);
65  static::failIfArrayIsNotEmpty($writer->getErrors());
66  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
67  $connection->update(
68  'pages',
69  ['TSconfig' => implode(chr(10), [
70  'TCEMAIN.linkHandler.content {',
71  ' configuration.table = tt_content',
72  '}',
73  ])],
74  ['uid' => 1000]
75  );
76  $this->‪setUpFileStorage();
77  $this->setUpFrontendRootPage(1000, ['EXT:frontend/Tests/Functional/SiteHandling/Fixtures/LinkGenerator.typoscript'], ['title' => 'ACME Root']);
78  });
79  }
80 
81  private function ‪setUpFileStorage(): void
82  {
83  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
84  $storageId = $storageRepository->createLocalStorage(
85  'fileadmin',
86  'fileadmin/',
87  'relative',
88  'Default storage created in TypoLinkTest',
89  true
90  );
91  $storage = $storageRepository->findByUid($storageId);
92  (new ‪Indexer($storage))->processChangesInStorages();
93  }
94 
95  public static function ‪linkIsGeneratedDataProvider(): array
96  {
97  $instructions = [
98  [
99  't3://email?email=mailto:user@example.org&other=other#other',
100  '<a href="mailto:user@example.org">user@example.org</a>',
101  ],
102  [
103  't3://email?email=user@example.org&other=other#other',
104  '<a href="mailto:user@example.org">user@example.org</a>',
105  ],
106  [
107  't3://email?email=user@example.org?subject=Hello%20World#other',
108  '<a href="mailto:user@example.org?subject=Hello%20World">user@example.org</a>',
109  ],
110  [
111  't3://file?uid=1&type=1&other=other#other',
112  '<a href="/fileadmin/logo.png#other">/fileadmin/logo.png</a>',
113  ],
114  [
115  't3://file?identifier=1:/logo.png&other=other#other',
116  '<a href="/fileadmin/logo.png#other">/fileadmin/logo.png</a>',
117  ],
118  [
119  't3://file?identifier=fileadmin/logo.png&other=other#other',
120  '<a href="/fileadmin/logo.png#other">/fileadmin/logo.png</a>',
121  ],
122  [
123  't3://folder?identifier=fileadmin&other=other#other',
124  '<a href="/fileadmin/#other">/fileadmin/</a>',
125  ],
126  [
127  't3://page?uid=1200&type=1&param-a=a&param-b=b#fragment',
128  '<a href="/features?param-a=a&amp;param-b=b&amp;type=1&amp;cHash=92aa5284d0ad18f7934fe94b52f6c1a5#fragment">EN: Features</a>',
129  ],
130  [
131  't3://page?uid=1300&additional=1&param-a=a#fragment',
132  '<a href="http://typo3.org">Go to TYPO3.org</a>',
133  ],
134  [
135  't3://record?identifier=content&uid=400&other=other#fragment',
136  '<a href="/features#c400">EN: Features</a>',
137  ],
138  'Non-existent record' => [
139  't3://record?identifier=content&uid=400000',
140  '',
141  ],
142  'Translated record on default language page' => [
143  't3://record?identifier=content&uid=402',
144  '<a href="/features#c402">EN: Features</a>',
145  ],
146  'Record with language ALL on default language page' => [
147  't3://record?identifier=content&uid=410',
148  '<a href="/features#c410">EN: Features</a>',
149  ],
150  [
151  't3://url?url=https://typo3.org%3f%26param-a=a%26param-b=b&other=other#other',
152  '<a href="https://typo3.org?&amp;param-a=a&amp;param-b=b">https://typo3.org?&amp;param-a=a&amp;param-b=b</a>',
153  ],
154  [
155  '1200,1 target class title &param-a=a',
156  '<a href="/features?param-a=a&amp;type=1&amp;cHash=62ac35c73f425af5e13cfff14c04424e" target="target" title="title" class="class">EN: Features</a>',
157  ],
158  [
159  'user@example.org target class title &other=other',
160  '<a href="mailto:user@example.org" target="target" title="title" class="class">user@example.org</a>',
161  ],
162  // check link with language parameters
163  [
164  't3://page?uid=1200&L=0',
165  '<a href="/features">EN: Features</a>',
166  ],
167  [
168  't3://page?uid=1200&_language=0',
169  '<a href="/features">EN: Features</a>',
170  ],
171  [
172  't3://page?uid=1200&L=1',
173  '<a href="https://acme.fr/features-fr">FR: Features</a>',
174  ],
175  [
176  't3://page?uid=1200&_language=1',
177  '<a href="https://acme.fr/features-fr">FR: Features</a>',
178  ],
179  [
180  't3://page?uid=1201&L=1',
181  '<a href="https://acme.fr/features-fr">FR: Features</a>',
182  ],
183  [
184  't3://page?uid=1201&_language=1',
185  '<a href="https://acme.fr/features-fr">FR: Features</a>',
186  ],
187  // localized page language overrule language arguments (new and old).
188  // This has also test coverage through SlugGeneratorTests.
189  [
190  't3://page?uid=1202&L=1',
191  '<a href="https://acme.ca/features-ca">FR-CA: Features</a>',
192  ],
193  [
194  't3://page?uid=1202&_language=1',
195  '<a href="https://acme.ca/features-ca">FR-CA: Features</a>',
196  ],
197  // check precedence order correctness if old and modern are provided
198  [
199  't3://page?uid=1200&L=2&_language=1',
200  '<a href="https://acme.fr/features-fr">FR: Features</a>',
201  ],
202  [
203  't3://page?uid=1200&_language=1&L=2',
204  '<a href="https://acme.fr/features-fr">FR: Features</a>',
205  ],
206  [
207  't3://page?uid=1200&L=1&_language=2',
208  '<a href="https://acme.ca/features-ca">FR-CA: Features</a>',
209  ],
210  [
211  't3://page?uid=1200&_language=2&L=1',
212  '<a href="https://acme.ca/features-ca">FR-CA: Features</a>',
213  ],
214  ];
215  return self::keysFromTemplate($instructions, '%1$s;');
216  }
217 
218  #[DataProvider('linkIsGeneratedDataProvider')]
219  #[Test]
220  public function ‪linkIsGenerated(string $parameter, string $expectation): void
221  {
222  $response = $this->‪invokeTypoLink($parameter);
223  self::assertSame($expectation, (string)$response->getBody());
224  }
225 
226  public static function ‪ATagParamsAreAddedInOrderDataProvider(): array
227  {
228  return [
229  'No ATag Params' => [
230  [
231  'ATagParams' => '',
232  ],
233  '',
234  'text',
235  '<a href="/welcome">text</a>',
236  ],
237  'specific ATagParams' => [
238  [
239  'ATagParams' => 'data-any="where"',
240  ],
241  '',
242  'text',
243  '<a href="/welcome" data-any="where">text</a>',
244  ],
245  'specific ATagParams with a target' => [
246  [
247  'ATagParams' => 'data-any="where" target="from-a-tags"',
248  ],
249  '',
250  'text',
251  '<a href="/welcome" target="from-a-tags" data-any="where">text</a>',
252  ],
253  'specific ATagParams with target in parameter' => [
254  [
255  'parameter' => '1100 from-parameter',
256  'ATagParams' => 'data-any="where"',
257  ],
258  '',
259  'text',
260  '<a href="/welcome" target="from-parameter" data-any="where">text</a>',
261  ],
262  'specific ATagParams with a target and target in parameter' => [
263  [
264  'parameter' => '1100 from-parameter',
265  'ATagParams' => 'data-any="where" target="from-a-tags"',
266  ],
267  '',
268  'text',
269  '<a href="/welcome" target="from-a-tags" data-any="where">text</a>',
270  ],
271  'specific ATagParams with a target and target as option' => [
272  [
273  'parameter' => '1100 from-parameter',
274  'ATagParams' => 'data-any="where" target="from-a-tags"',
275  'target' => 'from-option',
276  ],
277  '',
278  'text',
279  '<a href="/welcome" target="from-a-tags" data-any="where">text</a>',
280  ],
281  'specific ATagParams with a target and target in parameter and global attributes' => [
282  [
283  'parameter' => '1100 from-parameter',
284  'ATagParams' => 'data-any="where" target="from-a-tags"',
285  ],
286  'tabindex="from-global"',
287  'text',
288  '<a href="/welcome" target="from-a-tags" tabindex="from-global" data-any="where">text</a>',
289  ],
290  'specific ATagParams with global attributes and local ATagParams overridden mixed, and href removed' => [
291  [
292  'ATagParams' => 'data-any="where" target="from-a-tags"',
293  ],
294  'tabindex="from-global" target="_blank" data-any="global" data-global="1" href="#"',
295  'text',
296  '<a href="/welcome" target="from-a-tags" tabindex="from-global" data-any="where" data-global="1">text</a>',
297  ],
308  ];
309  }
310 
311  #[DataProvider('ATagParamsAreAddedInOrderDataProvider')]
312  #[Test]
313  public function ‪ATagParamsAreAddedInOrder(array $instructions, string $globalATagParams, string $linkText, string $expectation): void
314  {
315  $sourcePageId = 1100;
316  $instructions['parameter'] = $instructions['parameter'] ?? $sourcePageId;
317  $request = (new InternalRequest('https://acme.us/'))
318  ->withPageId($sourcePageId)
319  ->withInstructions(
320  [
321  $this->‪createTypoLinkInstruction($instructions, $linkText),
322  (new TypoScriptInstruction())
323  ->withTypoScript([
324  'config.' => [
325  'ATagParams' => $globalATagParams,
326  ],
327  ]),
328  ],
329  );
330  $response = $this->executeFrontendSubRequest($request);
331  self::assertSame($expectation, (string)$response->getBody());
332  }
333 
334  public static function ‪linkIsEncodedDataProvider(): array
335  {
336  $instructions = [
337  [
338  't3://email?email=mailto:<bad>thing(1)</bad>',
339  '<a href="mailto:&lt;bad&gt;thing(1)&lt;/bad&gt;">&lt;bad&gt;thing(1)&lt;/bad&gt;</a>',
340  ],
341  [
342  't3://email?email=mailto:<good%20a="a/"%20b="thing(1)">',
343  '<a href="mailto:&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;">&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;</a>',
344  ],
345  [
346  't3://email?email=<bad>thing(1)</bad>',
347  '<a href="mailto:&lt;bad&gt;thing(1)&lt;/bad&gt;">&lt;bad&gt;thing(1)&lt;/bad&gt;</a>',
348  ],
349  [
350  't3://email?email=<good%20a="a/"%20b="thing(1)">',
351  '<a href="mailto:&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;">&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;</a>',
352  ],
353  [
354  't3://folder?identifier=<any>',
355  '',
356  ],
357  [
358  't3://page?uid=<any>',
359  '',
360  ],
361  [
362  't3://record?identifier=content&uid=<any>',
363  '',
364  ],
365  [
366  't3://url?url=<bad>thing(1)</bad>',
367  '<a href="http://&lt;bad&gt;thing(1)&lt;/bad&gt;">http://&lt;bad&gt;thing(1)&lt;/bad&gt;</a>',
368  ],
369  [
370  't3://url?url=<good%20a="a/"%20b="thing(1)">',
371  '<a href="http://&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;">http://&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;</a>',
372  ],
373  [
374  't3://url?url=javascript:good()',
375  '<a ></a>',
376  ],
377  [
378  "t3://url?url=java\tscript:good()",
379  '<a href="http://java_script:good()">http://java_script:good()</a>',
380  ],
381  [
382  't3://url?url=java&#09;script:good()',
383  '<a href="http://java">http://java</a>',
384  ],
385  [
386  't3://url?url=javascript&colon;good()',
387  '<a href="http://javascript">http://javascript</a>',
388  ],
389  [
390  't3://url?url=data:text/html,<good>',
391  '<a ></a>',
392  ],
393  [
394  "t3://url?url=da\tsta:text/html,<good>",
395  '<a href="http://da_sta:text/html,&lt;good&gt;">http://da_sta:text/html,&lt;good&gt;</a>',
396  ],
397  [
398  't3://url?url=da&#09;ta:text/html,<good>',
399  '<a href="http://da">http://da</a>',
400  ],
401  [
402  't3://url?url=data&colon;text/html,<good>',
403  '<a href="http://data">http://data</a>',
404  ],
405  [
406  't3://url?url=%26%23106%3B%26%2397%3B%26%23118%3B%26%2397%3B%26%23115%3B%26%2399%3B%26%23114%3B%26%23105%3B%26%23112%3B%26%23116%3B%26%2358%3B%26%23103%3B%26%23111%3B%26%23111%3B%26%23100%3B%26%2340%3B%26%2341%3B',
407  '<a href="http://&amp;#106;&amp;#97;&amp;#118;&amp;#97;&amp;#115;&amp;#99;&amp;#114;&amp;#105;&amp;#112;&amp;#116;&amp;#58;&amp;#103;&amp;#111;&amp;#111;&amp;#100;&amp;#40;&amp;#41;">http://&amp;#106;&amp;#97;&amp;#118;&amp;#97;&amp;#115;&amp;#99;&amp;#114;&amp;#105;&amp;#112;&amp;#116;&amp;#58;&amp;#103;&amp;#111;&amp;#111;&amp;#100;&amp;#40;&amp;#41;</a>',
408  ],
409  [
410  '<bad>thing(1)</bad>',
411  '<a href="/&lt;bad&gt;thing(1)&lt;/bad&gt;">&lt;bad&gt;thing(1)&lt;/bad&gt;</a>',
412  ],
413  [
414  '<good%20a="a/"%20b="thing(1)">',
415  '<a href="/&lt;good%20a=&quot;a/&quot;%20b=&quot;thing(1)&quot;&gt;">&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;</a>',
416  ],
417  [
418  '<good/a="a/"/b="thing(1)"> target class title &other=other',
419  '<a href="/&lt;good/a=&quot;a/&quot;/b=&quot;thing(1)&quot;&gt;" target="target" title="title" class="class">&lt;good/a=&quot;a/&quot;/b=&quot;thing(1)&quot;&gt;</a>',
420  ],
421  [
422  'javascript:good()',
423  '',
424  ],
425  [
426  "java\tscript:good()",
427  '',
428  ],
429  [
430  'java&#09;script:good()',
431  '<a href="java&amp;#09;script:good()"></a>',
432  ],
433  [
434  'javascript&colon;good()',
435  '',
436  ],
437  [
438  'data:text/html,<good>',
439  '',
440  ],
441  [
442  "da\tta:text/html,<good>",
443  '',
444  ],
445  [
446  'da&#09;ta:text/html,<good>',
447  '<a href="da&amp;#09;ta:text/html,&lt;good&gt;"></a>',
448  ],
449  [
450  'data&colon;text/html,<good>',
451  '<a href="/data&amp;colon;text/html,&lt;good&gt;">data&amp;colon;text/html,&lt;good&gt;</a>',
452  ],
453  [
454  '%26%23106%3B%26%2397%3B%26%23118%3B%26%2397%3B%26%23115%3B%26%2399%3B%26%23114%3B%26%23105%3B%26%23112%3B%26%23116%3B%26%2358%3B%26%23103%3B%26%23111%3B%26%23111%3B%26%23100%3B%26%2340%3B%26%2341%3B',
455  '',
456  ],
457  [
458  '</> <"> <"> <">',
459  '<a href="/&lt;/&gt;" target="&lt;&quot;&gt;" title="&lt;&quot;&gt;" class="&lt;&quot;&gt;">&lt;/&gt;</a>',
460  ],
461  ];
462  return self::keysFromTemplate($instructions, '%1$s;');
463  }
464 
465  #[DataProvider('linkIsEncodedDataProvider')]
466  #[Test]
467  public function ‪linkIsEncodedPerDefault(string $parameter, string $expectation): void
468  {
469  $response = $this->‪invokeTypoLink($parameter);
470  self::assertSame($expectation, (string)$response->getBody());
471  }
472 
473  #[DataProvider('linkIsEncodedDataProvider')]
474  #[Test]
475  public function ‪linkIsEncodedHavingParseFunc(string $parameter, string $expectation): void
476  {
477  $response = $this->‪invokeTypoLink($parameter, $this->‪createParseFuncInstruction([
478  'allowTags' => 'good',
479  'denyTags' => '*',
480  'nonTypoTagStdWrap.' => [
481  'HTMLparser.' => [
482  'tags.' => [
483  'good.' => [
484  'allowedAttribs' => 0,
485  ],
486  ],
487  ],
488  ],
489  ]));
490  self::assertSame($expectation, (string)$response->getBody());
491  }
492 
493  public static function ‪linkToPageIsProcessedDataProvider(): array
494  {
495  return [
496  [
497  't3://page?uid=9911',
498  '<a href="/test/good">&lt;good&gt;</a>',
499  false,
500  ],
501  [
502  't3://page?uid=9911',
503  '<a href="/test/good"><good></good></a>', // expanded from `<good>` to `<good></good>`
504  true,
505  ],
506  [
507  't3://page?uid=9912',
508  '<a href="/test/good-a-b-spaced">&lt;good a=&quot;a/&quot; b=&quot;thing(1)&quot;&gt;</a>',
509  false,
510  ],
511  [
512  't3://page?uid=9912',
513  '<a href="/test/good-a-b-spaced"><good></good></a>', // expanded from `<good>` to `<good></good>`
514  true,
515  ],
516  [
517  't3://page?uid=9913',
518  '<a href="/test/good-a-b-enc-a">&lt;good%20a=&quot;a/&quot;%20b=&quot;thing(1)&quot;&gt;</a>',
519  false,
520  ],
521  [
522  't3://page?uid=9913',
523  '<a href="/test/good-a-b-enc-a">&lt;good%20a="a/"%20b="thing(1)"&gt;</a>',
524  true,
525  ],
526  [
527  't3://page?uid=9914',
528  '<a href="/test/good-a-b-enc-b">&lt;good/a=&quot;a/&quot;/b=&quot;thing(1)&quot;&gt;</a>',
529  false,
530  ],
531  [
532  't3://page?uid=9914',
533  '<a href="/test/good-a-b-enc-b">&lt;good/a="a/"/b="thing(1)"&gt;</a>',
534  true,
535  ],
536  [
537  't3://page?uid=9921',
538  '<a href="/test/bad">&lt;bad&gt;</a>',
539  false,
540  ],
541  [
542  't3://page?uid=9921',
543  '<a href="/test/bad">&lt;bad&gt;</a>',
544  true,
545  ],
546  ];
547  }
548 
549  #[DataProvider('linkToPageIsProcessedDataProvider')]
550  #[Test]
551  public function ‪linkToPageIsProcessed(string $parameter, string $expectation, bool $parseFuncEnabled): void
552  {
553  $instructions = [];
554  if ($parseFuncEnabled) {
555  $instructions[] = $this->‪createParseFuncInstruction([
556  'allowTags' => 'good',
557  'denyTags' => '*',
558  'nonTypoTagStdWrap.' => [
559  'HTMLparser.' => [
560  'tags.' => [
561  'good.' => [
562  'allowedAttribs' => 0,
563  ],
564  ],
565  ],
566  ],
567  'htmlSanitize' => true,
568  'htmlSanitize.' => [
569  'build' => TestSanitizerBuilder::class,
570  ],
571  ]);
572  }
573  $response = $this->‪invokeTypoLink($parameter, ...$instructions);
574  self::assertSame($expectation, (string)$response->getBody());
575  }
576 
577  public static function ‪linkWithoutAnchorIsGeneratedDataProvider(): array
578  {
579  return [
580  'empty parameter does not create a link' => [
581  [
582  'parameter' => '',
583  ],
584  'no link',
585  'no link',
586  ],
587  'empty parameter with additional ATagParams does not create a link' => [
588  [
589  'parameter' => '',
590  'ATagParams' => 'data-any="where"',
591  ],
592  'no link',
593  'no link',
594  ],
595  'empty parameter with additional ATagParams with id creates a link' => [
596  [
597  'parameter' => '',
598  'ATagParams' => 'id="c13" data-any="where"',
599  ],
600  'a link',
601  '<a id="c13" data-any="where">a link</a>',
602  ],
603  'empty parameter with additional ATagParams with id creates a link and adds title' => [
604  [
605  'parameter' => '',
606  'ATagParams' => 'id="c13" data-any="where"',
607  'title' => 'custom title',
608  ],
609  'a link',
610  '<a id="c13" data-any="where" title="custom title">a link</a>',
611  ],
612  ];
613  }
614 
615  #[DataProvider('linkWithoutAnchorIsGeneratedDataProvider')]
616  #[Test]
617  public function ‪linkWithoutAnchorIsGenerated(array $instructions, string $linkText, string $expectation): void
618  {
619  $sourcePageId = 1100;
620  $request = (new InternalRequest('https://acme.us/'))
621  ->withPageId($sourcePageId)
622  ->withInstructions(
623  [
624  $this->‪createTypoLinkInstruction($instructions, $linkText),
625  ]
626  );
627  $response = $this->executeFrontendSubRequest($request);
628  self::assertSame($expectation, (string)$response->getBody());
629  }
630 
631  private function ‪invokeTypoLink(string $parameter, AbstractInstruction ...$instructions): ResponseInterface
632  {
633  $sourcePageId = 1100;
634 
635  $request = (new InternalRequest('https://acme.us/'))
636  ->withPageId($sourcePageId)
637  ->withInstructions(
638  [
640  'parameter.' => [
641  'data' => 'field:pid',
642  ],
643  'section.' => [
644  'data' => 'field:uid',
645  'wrap' => 'c|',
646  ],
647  ]),
649  'parameter' => $parameter,
650  ]),
651  ]
652  );
653 
654  if (count($instructions) > 0) {
655  $request = $this->‪applyInstructions($request, ...$instructions);
656  }
657 
658  return $this->executeFrontendSubRequest($request);
659  }
660 
661  private function ‪createTypoLinkInstruction(array $typoLink, ?string $linkText = null): ArrayValueInstruction
662  {
663  return (new ArrayValueInstruction(LinkHandlingController::class))
664  ->withArray([
665  '10' => 'TEXT',
666  '10.' => array_merge([
667  'typolink.' => $typoLink,
668  ], ($linkText ? ['value' => $linkText] : [])),
669  ]);
670  }
671 
672  private function ‪createRecordLinksInstruction(array $typoLink): TypoScriptInstruction
673  {
674  return (new TypoScriptInstruction())
675  ->withTypoScript([
676  'config.' => [
677  'recordLinks.' => [
678  'content.' => [
679  'typolink.' => $typoLink,
680  ],
681  ],
682  ],
683  ]);
684  }
685 
686  private function ‪createParseFuncInstruction(array $parseFunc): TypoScriptInstruction
687  {
688  return (new TypoScriptInstruction())
689  ->withTypoScript([
690  'lib.' => [
691  'parseFunc.' => array_replace_recursive(
692  [
693  'makelinks' => 1,
694  'makelinks.' => [
695  'http.' => [
696  'keep' => 'path',
697  'extTarget' => '_blank',
698  ],
699  'mailto.' => [
700  'keep' => 'path',
701  ],
702  ],
703  'allowTags' => '',
704  'denyTags' => '',
705  'constants' => 1,
706  'nonTypoTagStdWrap.' => [
707  'HTMLparser' => 1,
708  'HTMLparser.' => [
709  'keepNonMatchedTags' => 1,
710  'htmlSpecialChars' => 2,
711  ],
712  ],
713  ],
714  $parseFunc
715  ),
716  ],
717  ]);
718  }
719 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildLanguageConfiguration
‪buildLanguageConfiguration(string $identifier, string $base, array $fallbackIdentifiers=[], string $fallbackType=null)
Definition: SiteBasedTestTrait.php:108
‪TYPO3\CMS\Core\Resource\Index\Indexer
Definition: Indexer.php:35
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\writeSiteConfiguration
‪writeSiteConfiguration(string $identifier, array $site=[], array $languages=[], array $errorHandling=[])
Definition: SiteBasedTestTrait.php:50
‪TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\AbstractTestCase
Definition: AbstractTestCase.php:29
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildSiteConfiguration
‪buildSiteConfiguration(int $rootPageId, string $base='')
Definition: SiteBasedTestTrait.php:88
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\applyInstructions
‪applyInstructions(InternalRequest $request, AbstractInstruction ... $instructions)
Definition: SiteBasedTestTrait.php:205
‪TYPO3\CMS\Core\Resource\StorageRepository
Definition: StorageRepository.php:38
‪TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\Fixtures\TestSanitizerBuilder
Definition: TestSanitizerBuilder.php:24
‪TYPO3\CMS\Frontend\Tests\Functional\SiteHandling
Definition: AbstractTestCase.php:18
‪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\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52