‪TYPO3CMS  ‪main
BrokenLinkRepositoryTest.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;
25 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
26 
27 final class ‪BrokenLinkRepositoryTest extends FunctionalTestCase
28 {
29  protected array ‪$coreExtensionsToLoad = [
30  'info',
31  'linkvalidator',
32  'seo',
33  ];
34 
35  private const ‪beusers = [
36  'admin' => [
37  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
38  'uid' => 1,
39  'groupFixture' => '',
40  ],
41  'no group' => [
42  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
43  'uid' => 2,
44  'groupFixture' => '',
45  ],
46  // write access to pages, tt_content
47  'group 1' => [
48  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
49  'uid' => 3,
50  'groupFixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_groups.csv',
51  ],
52  // write access to pages, tt_content, exclude field pages.header_link
53  'group 2' => [
54  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
55  'uid' => 4,
56  'groupFixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_groups.csv',
57  ],
58  // write access to pages, tt_content (restricted to default language)
59  'group 3' => [
60  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
61  'uid' => 5,
62  'groupFixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_groups.csv',
63  ],
64  // group 6: access to all, but restricted via explicit allow to CType=texmedia and text
65  'group 6' => [
66  'fixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_users.csv',
67  'uid' => 6,
68  'groupFixture' => __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/be_groups.csv',
69  ],
70 
71  ];
72 
74  {
75  yield 'Admin user should see all broken links' =>
76  [
77  self::beusers['admin'], // backend user
78  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv', // input file for DB
79  [1], // pids
80  [ // expected result
81  'db' => 1,
82  'file' => 1,
83  'external' => 2,
84  'total' => 4,
85  ],
86  ];
87  yield 'User with no group should see none' =>
88  [
89  self::beusers['no group'],
90  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv',
91  [1],
92  [
93  'total' => 0,
94  ],
95  ];
96  yield 'User with permission to pages but not to specific tables should see none' =>
97  [
98  self::beusers['no group'],
99  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_2.csv',
100  [1],
101  [
102  'total' => 0,
103  ],
104  ];
105  yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' =>
106  [
107  self::beusers['group 1'],
108  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_3.csv',
109  [1],
110  [
111  'db' => 1,
112  'file' => 1,
113  'external' => 1,
114  'total' => 3,
115  ],
116  ];
117  yield 'User with permission to pages, specific tables and exclude fields should see all broken links' =>
118  [
119  self::beusers['group 2'],
120  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_4.csv',
121  [1],
122  [
123  'db' => 1,
124  'file' => 1,
125  'external' => 2,
126  'total' => 4,
127  ],
128  ];
129  yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' =>
130  [
131  self::beusers['group 6'],
132  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_6_explicit_allow.csv',
133  [1],
134  [
135  'external' => 1,
136  'total' => 1,
137  ],
138  ];
139 
140  yield 'User has write permission only for default language and should see only 1 of 2 broken links' =>
141  [
142  self::beusers['group 3'],
143  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_5.csv',
144  [1],
145  [
146  'external' => 1,
147  'total' => 1,
148  ],
149  ];
150  }
151 
152  #[DataProvider('getLinkCountsForPagesAndLinktypesReturnsCorrectCountForUserDataProvider')]
153  #[Test]
155  array $beuser,
156  string $inputFile,
157  array $pidList,
158  array $expectedOutput
159  ): void {
160  $tsConfig = [
161  'searchFields.' => [
162  'pages' => 'media,url,canonical_link',
163  'tt_content' => 'bodytext,header_link,records',
164  ],
165  'linktypes' => 'db,file,external',
166  'checkhidden' => '0',
167  ];
168  $linkTypes = explode(',', $tsConfig['linktypes']);
169 
170  $searchFields = $tsConfig['searchFields.'];
171  foreach ($searchFields as $table => ‪$fields) {
172  $searchFields[$table] = explode(',', ‪$fields);
173  }
174 
175  $this->‪setupBackendUserAndGroup($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']);
176 
177  $this->importCSVDataSet($inputFile);
178 
179  $linkAnalyzer = $this->get(LinkAnalyzer::class);
180  $linkAnalyzer->init($searchFields, $pidList, $tsConfig);
181  $linkAnalyzer->getLinkStatistics($linkTypes);
182  $result = (new ‪BrokenLinkRepository())->getNumberOfBrokenLinksForRecordsOnPages(
183  $pidList,
184  $searchFields
185  );
186 
187  self::assertEquals($expectedOutput, $result);
188  }
189 
191  {
192  yield 'Admin user should see all broken links' =>
193  [
194  self::beusers['admin'], // backend user
195  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv', // input file for DB
196  [1], // pids
197  4, // count
198  ];
199 
200  yield 'User with no group should see none' =>
201  [
202  self::beusers['no group'],
203  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv',
204  [1],
205  0,
206  ];
207  yield 'User with permission to pages but not to specific tables should see none' =>
208  [
209  self::beusers['no group'],
210  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_2.csv',
211  [1],
212  0,
213  ];
214  yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' =>
215  [
216  self::beusers['group 1'],
217  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_3.csv',
218  [1],
219  3,
220  ];
221  yield 'User with permission to pages, specific tables and exclude fields should see all broken links' =>
222  [
223  self::beusers['group 2'],
224  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_4.csv',
225  [1],
226  4,
227  ];
228  yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' =>
229  [
230  self::beusers['group 6'],
231  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_6_explicit_allow.csv',
232  [1],
233  1,
234  ];
235 
236  yield 'User has write permission only for default language and should see only 1 of 2 broken links' =>
237  [
238  self::beusers['group 3'],
239  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_5.csv',
240  [1],
241  1,
242  ];
243  }
244 
245  #[DataProvider('getAllBrokenLinksForPagesReturnsCorrectCountForUserDataProvider')]
246  #[Test]
248  array $beuser,
249  string $inputFile,
250  array $pidList,
251  int $expectedCount
252  ): void {
253  $tsConfig = [
254  'searchFields.' => [
255  'pages' => 'media,url,canonical_link',
256  'tt_content' => 'bodytext,header_link,records',
257  ],
258  'linktypes' => 'db,file,external',
259  'checkhidden' => '0',
260  ];
261  $linkTypes = explode(',', $tsConfig['linktypes']);
262 
263  $searchFields = $tsConfig['searchFields.'];
264  foreach ($searchFields as $table => ‪$fields) {
265  $searchFields[$table] = explode(',', ‪$fields);
266  }
267 
268  $this->‪setupBackendUserAndGroup($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']);
269 
270  $this->importCSVDataSet($inputFile);
271 
272  $linkAnalyzer = $this->get(LinkAnalyzer::class);
273  $linkAnalyzer->init($searchFields, $pidList, $tsConfig);
274  $linkAnalyzer->getLinkStatistics($linkTypes);
275 
276  $results = (new ‪BrokenLinkRepository())->getAllBrokenLinksForPages(
277  $pidList,
278  $linkTypes,
279  $searchFields
280  );
281 
282  self::assertCount($expectedCount, $results);
283  }
284 
286  {
287  yield 'Admin user should see all broken links' =>
288  [
289  self::beusers['admin'], // backend user
290  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv', // input file for DB
291  [1], // pids
292  [ // expected result
293  [
294  'record_uid' => 1,
295  'record_pid' => 1,
296  'language' => 0,
297  'headline' => 'link',
298  'field' => 'bodytext',
299  'table_name' => 'tt_content',
300  'element_type' => 'textmedia',
301  'link_title' => 'link',
302  'url' => 'http://localhost/iAmInvalid',
303  'link_type' => 'external',
304  'needs_recheck' => 0,
305  ],
306  [
307  'record_uid' => 2,
308  'record_pid' => 1,
309  'language' => 0,
310  'headline' => '[No title]',
311  'field' => 'header_link',
312  'table_name' => 'tt_content',
313  'element_type' => 'textmedia',
314  'link_title' => null,
315  'url' => 'http://localhost/iAmInvalid',
316  'link_type' => 'external',
317  'needs_recheck' => 0,
318  ],
319  [
320  'record_uid' => 3,
321  'record_pid' => 1,
322  'language' => 0,
323  'headline' => 'broken link',
324  'field' => 'bodytext',
325  'table_name' => 'tt_content',
326  'element_type' => 'textmedia',
327  'link_title' => 'broken link',
328  'url' => '85',
329  'link_type' => 'db',
330  'needs_recheck' => 0,
331  ],
332  [
333  'record_uid' => 5,
334  'record_pid' => 1,
335  'language' => 0,
336  'headline' => 'broken link',
337  'field' => 'bodytext',
338  'table_name' => 'tt_content',
339  'element_type' => 'textmedia',
340  'link_title' => 'broken link',
341  'url' => 'file:88',
342  'link_type' => 'file',
343  'needs_recheck' => 0,
344  ],
345  ],
346  ];
347 
348  yield 'User with no group should see none' =>
349  [
350  self::beusers['no group'],
351  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input.csv',
352  [1],
353  [],
354  ];
355  yield 'User with permission to pages but not to specific tables should see none' =>
356  [
357  self::beusers['no group'],
358  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_2.csv',
359  [1],
360  [],
361  ];
362  yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' =>
363  [
364  self::beusers['group 1'],
365  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_3.csv',
366  [1],
367  [
368  [
369  'record_uid' => 1,
370  'record_pid' => 1,
371  'language' => 0,
372  'headline' => 'link',
373  'field' => 'bodytext',
374  'table_name' => 'tt_content',
375  'element_type' => 'textmedia',
376  'link_title' => 'link',
377  'url' => 'http://localhost/iAmInvalid',
378  'link_type' => 'external',
379  'needs_recheck' => 0,
380  ],
381  [
382  'record_uid' => 3,
383  'record_pid' => 1,
384  'language' => 0,
385  'headline' => 'broken link',
386  'field' => 'bodytext',
387  'table_name' => 'tt_content',
388  'element_type' => 'textmedia',
389  'link_title' => 'broken link',
390  'url' => '85',
391  'link_type' => 'db',
392  'needs_recheck' => 0,
393  ],
394  [
395  'record_uid' => 5,
396  'record_pid' => 1,
397  'language' => 0,
398  'headline' => 'broken link',
399  'field' => 'bodytext',
400  'table_name' => 'tt_content',
401  'element_type' => 'textmedia',
402  'link_title' => 'broken link',
403  'url' => 'file:88',
404  'link_type' => 'file',
405  'needs_recheck' => 0,
406  ],
407  ],
408  ];
409  yield 'User with permission to pages, specific tables and exclude fields should see all broken links' =>
410  [
411  self::beusers['group 2'],
412  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_4.csv',
413  [1],
414  [
415  [
416  'record_uid' => 1,
417  'record_pid' => 1,
418  'language' => 0,
419  'headline' => 'link',
420  'field' => 'bodytext',
421  'table_name' => 'tt_content',
422  'element_type' => 'textmedia',
423  'link_title' => 'link',
424  'url' => 'http://localhost/iAmInvalid',
425  'link_type' => 'external',
426  'needs_recheck' => 0,
427  ],
428  [
429  'record_uid' => 2,
430  'record_pid' => 1,
431  'language' => 0,
432  'headline' => '[No title]',
433  'field' => 'header_link',
434  'table_name' => 'tt_content',
435  'element_type' => 'textmedia',
436  'link_title' => null,
437  'url' => 'http://localhost/iAmInvalid',
438  'link_type' => 'external',
439  'needs_recheck' => 0,
440  ],
441  [
442  'record_uid' => 3,
443  'record_pid' => 1,
444  'language' => 0,
445  'headline' => 'broken link',
446  'field' => 'bodytext',
447  'table_name' => 'tt_content',
448  'element_type' => 'textmedia',
449  'link_title' => 'broken link',
450  'url' => '85',
451  'link_type' => 'db',
452  'needs_recheck' => 0,
453  ],
454  [
455  'record_uid' => 5,
456  'record_pid' => 1,
457  'language' => 0,
458  'headline' => 'broken link',
459  'field' => 'bodytext',
460  'table_name' => 'tt_content',
461  'element_type' => 'textmedia',
462  'link_title' => 'broken link',
463  'url' => 'file:88',
464  'link_type' => 'file',
465  'needs_recheck' => 0,
466  ],
467  ],
468  ];
469  yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' =>
470  [
471  self::beusers['group 6'],
472  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_6_explicit_allow.csv',
473  [1],
474  [
475  [
476  'record_uid' => 1,
477  'record_pid' => 1,
478  'language' => 0,
479  'headline' => 'link',
480  'field' => 'bodytext',
481  'table_name' => 'tt_content',
482  'element_type' => 'textmedia',
483  'link_title' => 'link',
484  'url' => 'http://localhost/iAmInvalid',
485  'link_type' => 'external',
486  'needs_recheck' => 0,
487  ],
488  ],
489  ];
490 
491  yield 'User has write permission only for default language and should see only 1 of 2 broken links' =>
492  [
493  self::beusers['group 3'],
494  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_permissions_user_5.csv',
495  [1],
496  [
497  [
498  'record_uid' => 1,
499  'record_pid' => 1,
500  'language' => 0,
501  'headline' => 'link',
502  'field' => 'bodytext',
503  'table_name' => 'tt_content',
504  'element_type' => 'textmedia',
505  'link_title' => 'link',
506  'url' => 'http://localhost/iAmInvalid',
507  'link_type' => 'external',
508  'needs_recheck' => 0,
509  ],
510  ],
511  ];
512  }
513 
514  #[DataProvider('getAllBrokenLinksForPagesReturnsCorrectValuesForUserDataProvider')]
515  #[Test]
517  array $beuser,
518  string $inputFile,
519  array $pidList,
520  array $expectedResult
521  ): void {
522  $tsConfig = [
523  'searchFields.' => [
524  'pages' => 'media,url,canonical_link',
525  'tt_content' => 'bodytext,header_link,records',
526  ],
527  'linktypes' => 'db,file,external',
528  'checkhidden' => '0',
529  ];
530  $linkTypes = explode(',', $tsConfig['linktypes']);
531 
532  $searchFields = $tsConfig['searchFields.'];
533  foreach ($searchFields as $table => ‪$fields) {
534  $searchFields[$table] = explode(',', ‪$fields);
535  }
536 
537  $this->‪setupBackendUserAndGroup($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']);
538 
539  $this->importCSVDataSet($inputFile);
540 
541  $linkAnalyzer = $this->get(LinkAnalyzer::class);
542  $linkAnalyzer->init($searchFields, $pidList, $tsConfig);
543  $linkAnalyzer->getLinkStatistics($linkTypes);
544 
545  $results = (new ‪BrokenLinkRepository())->getAllBrokenLinksForPages(
546  $pidList,
547  $linkTypes,
548  $searchFields
549  );
550 
551  foreach ($results as &$result) {
552  unset($result['url_response']);
553  unset($result['uid']);
554  unset($result['last_check']);
555  }
556  self::assertEquals($expectedResult, $results);
557  }
558 
560  {
561  yield 'All languages should be returend' =>
562  [
563  self::beusers['admin'], // backend user
564  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_languages.csv', // input file for DB
565  [1, 2, 3], // pids
566  [], // languages
567  [ // expected result
568  [
569  'record_uid' => 1,
570  'record_pid' => 1,
571  'language' => 0,
572  'headline' => 'link',
573  'field' => 'bodytext',
574  'table_name' => 'tt_content',
575  'element_type' => 'textmedia',
576  'link_title' => 'link',
577  'url' => 'http://localhost/iAmInvalid',
578  'link_type' => 'external',
579  ],
580  [
581  'record_uid' => 2,
582  'record_pid' => 1,
583  'language' => 1,
584  'headline' => 'link',
585  'field' => 'bodytext',
586  'table_name' => 'tt_content',
587  'element_type' => 'textmedia',
588  'link_title' => 'link',
589  'url' => 'http://localhost/iAmInvalid',
590  'link_type' => 'external',
591  ],
592  [
593  'record_uid' => 3,
594  'record_pid' => 1,
595  'language' => 2,
596  'headline' => 'link',
597  'field' => 'bodytext',
598  'table_name' => 'tt_content',
599  'element_type' => 'textmedia',
600  'link_title' => 'link',
601  'url' => 'http://localhost/iAmInvalid',
602  'link_type' => 'external',
603  ],
604  ],
605  ];
606 
607  yield 'Only defined languages should be returend' =>
608  [
609  self::beusers['admin'],
610  __DIR__ . '/Fixtures/BrokenLinkRepositoryTest/input_languages.csv',
611  [1, 2, 3],
612  [0, 2],
613  [
614  [
615  'record_uid' => 1,
616  'record_pid' => 1,
617  'language' => 0,
618  'headline' => 'link',
619  'field' => 'bodytext',
620  'table_name' => 'tt_content',
621  'element_type' => 'textmedia',
622  'link_title' => 'link',
623  'url' => 'http://localhost/iAmInvalid',
624  'link_type' => 'external',
625  ],
626  [
627  'record_uid' => 3,
628  'record_pid' => 1,
629  'language' => 2,
630  'headline' => 'link',
631  'field' => 'bodytext',
632  'table_name' => 'tt_content',
633  'element_type' => 'textmedia',
634  'link_title' => 'link',
635  'url' => 'http://localhost/iAmInvalid',
636  'link_type' => 'external',
637  ],
638  ],
639  ];
640  }
641 
642  #[DataProvider('getAllBrokenLinksForPagesRespectsGivenLanguagesDataProvider')]
643  #[Test]
645  array $beuser,
646  string $inputFile,
647  array $pidList,
648  array ‪$languages,
649  array $expectedResult
650  ): void {
651  $tsConfig = [
652  'searchFields.' => [
653  'tt_content' => 'bodytext',
654  ],
655  'linktypes' => 'external',
656  'checkhidden' => '0',
657  ];
658  $linkTypes = explode(',', $tsConfig['linktypes']);
659 
660  $searchFields = $tsConfig['searchFields.'];
661  foreach ($searchFields as $table => ‪$fields) {
662  $searchFields[$table] = explode(',', ‪$fields);
663  }
664 
665  $this->‪setupBackendUserAndGroup($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']);
666  $this->importCSVDataSet($inputFile);
667 
668  $linkAnalyzer = $this->get(LinkAnalyzer::class);
669  $linkAnalyzer->init($searchFields, $pidList, $tsConfig);
670  $linkAnalyzer->getLinkStatistics($linkTypes);
671 
672  $results = (new ‪BrokenLinkRepository())->getAllBrokenLinksForPages(
673  $pidList,
674  $linkTypes,
675  $searchFields,
677  );
678 
679  foreach ($results as &$result) {
680  unset($result['url_response'], $result['uid'], $result['last_check'], $result['needs_recheck']);
681  }
682 
683  self::assertEquals($expectedResult, $results);
684  }
685 
689  private function ‪setupBackendUserAndGroup(int ‪$uid, string $fixtureFile, string $groupFixtureFile): void
690  {
691  if ($groupFixtureFile) {
692  $this->importCSVDataSet($groupFixtureFile);
693  }
694  $this->importCSVDataSet($fixtureFile);
695  $backendUser = $this->setUpBackendUser(‪$uid);
696  ‪$GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser);
697  }
698 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪$languages
‪$languages
Definition: updateIsoDatabase.php:104
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Linkvalidator\Tests\Functional\Repository
Definition: BrokenLinkRepositoryTest.php:18
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25