‪TYPO3CMS  10.4
ValidatorTask.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Symfony\Component\Mime\Address;
29 
35 {
39  protected ‪$sleepTime;
40 
44  protected ‪$sleepAfterFinish;
45 
49  protected ‪$countInARun;
50 
56  protected ‪$totalBrokenLink = 0;
57 
63  protected ‪$oldTotalBrokenLink = 0;
64 
70  protected ‪$templateMail;
71 
77  protected ‪$configuration = [];
78 
84  protected ‪$isDifferentToLastRun;
85 
91  protected ‪$emailTemplateFile;
92 
98  protected ‪$depth;
99 
105  protected ‪$page;
106 
112  protected ‪$email;
113 
119  protected ‪$emailOnBrokenLinkOnly;
120 
124  protected ‪$templateService;
125 
131  protected ‪$languageFile = 'LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf';
132 
134  protected ‪$brokenLinkRepository;
135 
141  public function ‪getEmail()
142  {
143  return ‪$this->email;
144  }
145 
151  public function ‪setEmail(‪$email)
152  {
153  $this->email = ‪$email;
154  }
155 
161  public function ‪getEmailOnBrokenLinkOnly()
162  {
164  }
165 
172  {
173  $this->emailOnBrokenLinkOnly = ‪$emailOnBrokenLinkOnly;
174  }
175 
181  public function ‪getPage()
182  {
183  return ‪$this->page;
184  }
185 
191  public function ‪setPage(‪$page)
192  {
193  $this->page = ‪$page;
194  }
195 
201  public function ‪getDepth()
202  {
203  return ‪$this->depth;
204  }
205 
211  public function ‪setDepth(‪$depth)
212  {
213  $this->depth = ‪$depth;
214  }
215 
221  public function ‪getEmailTemplateFile()
222  {
224  }
225 
232  {
233  $this->emailTemplateFile = ‪$emailTemplateFile;
234  }
235 
241  public function ‪getConfiguration()
242  {
244  }
245 
251  public function ‪setConfiguration(‪$configuration)
252  {
253  $this->configuration = ‪$configuration;
254  }
255 
262  public function ‪execute()
263  {
264  $this->brokenLinkRepository = GeneralUtility::makeInstance(BrokenLinkRepository::class);
265  $this->‪setCliArguments();
266  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
267  $successfullyExecuted = true;
268  if (!file_exists($file = GeneralUtility::getFileAbsFileName($this->emailTemplateFile))
269  && !empty($this->email)
270  ) {
271  if ($this->emailTemplateFile === 'EXT:linkvalidator/res/mailtemplate.html') {
272  // Update the default email template file path
273  $this->emailTemplateFile = 'EXT:linkvalidator/Resources/Private/Templates/mailtemplate.html';
274  $this->‪save();
275  } else {
276  $lang = $this->‪getLanguageService();
277  throw new \InvalidArgumentException(
278  $lang->sL($this->languageFile . ':tasks.error.invalidEmailTemplateFile'),
279  1295476972
280  );
281  }
282  }
283  $htmlFile = file_get_contents($file);
284  $this->templateMail = $this->templateService->getSubpart($htmlFile, '###REPORT_TEMPLATE###');
285  // The array to put the content into
286  $pageSections = '';
287  $this->isDifferentToLastRun = false;
288  $pageList = ‪GeneralUtility::trimExplode(',', (string)$this->page, true);
289  $modTs = $this->‪loadModTsConfig($this->page);
290  if (is_array($pageList)) {
291  // reset broken link counts as they were stored in the serialized object
292  $this->oldTotalBrokenLink = 0;
293  $this->totalBrokenLink = 0;
294  foreach ($pageList as ‪$page) {
295  $pageSections .= $this->‪checkPageLinks((int)$page);
296  }
297  }
298  if ($this->totalBrokenLink != $this->oldTotalBrokenLink) {
299  $this->isDifferentToLastRun = true;
300  }
301  if ($this->totalBrokenLink > 0
302  && (!$this->emailOnBrokenLinkOnly || $this->isDifferentToLastRun)
303  && !empty($this->email)
304  ) {
305  $successfullyExecuted = $this->‪reportEmail($pageSections, $modTs);
306  }
307  return $successfullyExecuted;
308  }
309 
317  protected function ‪checkPageLinks(‪$page)
318  {
319  $pageRow = null;
320  ‪$page = (int)‪$page;
321  $pageSections = '';
322  $pageIds = '';
323  $oldLinkCounts = [];
324  $modTs = $this->‪loadModTsConfig($page);
325  $searchFields = $this->‪getSearchField($modTs);
326  $linkTypes = $this->‪getLinkTypes($modTs);
328  $processor = GeneralUtility::makeInstance(LinkAnalyzer::class);
329  if (‪$page === 0) {
330  $rootLineHidden = false;
331  } else {
332  $pageRow = ‪BackendUtility::getRecord('pages', ‪$page, '*', '', false);
333  if ($pageRow === null) {
334  throw new \InvalidArgumentException(
335  sprintf($this->‪getLanguageService()->sL($this->languageFile . ':tasks.error.invalidPageUid'), ‪$page),
336  1502800555
337  );
338  }
339  $rootLineHidden = $processor->getRootLineIsHidden($pageRow);
340  }
341  if (!$rootLineHidden || $modTs['checkhidden'] == 1) {
342  $pageIds = $processor->extGetTreeList(‪$page, $this->depth, 0, '1=1', $modTs['checkhidden']);
343  if (isset($pageRow) && $pageRow['hidden'] == 0 || $modTs['checkhidden'] == 1) {
344  // \TYPO3\CMS\Linkvalidator\LinkAnalyzer->extGetTreeList() always adds trailing comma
345  $pageIds .= ‪$page;
346  }
347  }
348  if (!empty($pageIds)) {
349  $processor->init($searchFields, $pageIds, $modTs);
350  if (!empty($this->email)) {
351  $oldLinkCounts = $processor->getLinkCounts();
352  $this->oldTotalBrokenLink += $oldLinkCounts['total'];
353  }
354  $processor->getLinkStatistics($linkTypes, $modTs['checkhidden']);
355  if (!empty($this->email)) {
356  $linkCounts = $processor->getLinkCounts();
357  $this->totalBrokenLink += $linkCounts['total'];
358  $pageSections = $this->‪buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
359  }
360  }
361  return $pageSections;
362  }
363 
371  protected function ‪loadModTsConfig(‪$page)
372  {
373  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
374  $parseObj->parse($this->configuration);
375  if (!empty($parseObj->errors)) {
376  $languageService = $this->‪getLanguageService();
377  $parseErrorMessage = $languageService->sL($this->languageFile . ':tasks.error.invalidTSconfig')
378  . '<br />';
379  foreach ($parseObj->errors as $errorInfo) {
380  $parseErrorMessage .= $errorInfo[0] . '<br />';
381  }
382  throw new \Exception($parseErrorMessage, 1295476989);
383  }
384  $modTs = ‪BackendUtility::getPagesTSconfig(‪$page)['mod.']['linkvalidator.'] ?? [];
385  $tsConfig = $parseObj->setup;
386  $overrideTs = $tsConfig['mod.']['linkvalidator.'];
387  if (is_array($overrideTs)) {
388  ‪ArrayUtility::mergeRecursiveWithOverrule($modTs, $overrideTs);
389  }
390  return $modTs;
391  }
392 
399  protected function ‪getSearchField(array $modTS)
400  {
401  $searchFields = [];
402  // Get the searchFields from TypoScript
403  foreach ($modTS['searchFields.'] as $table => $fieldList) {
404  ‪$fields = ‪GeneralUtility::trimExplode(',', $fieldList);
405  foreach (‪$fields as $field) {
406  $searchFields[$table][] = $field;
407  }
408  }
409  return $searchFields;
410  }
411 
418  protected function ‪getLinkTypes(array $modTS)
419  {
420  $linkTypes = [];
421  $typesTmp = ‪GeneralUtility::trimExplode(',', $modTS['linktypes'], true);
422  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] ?? [] as $type => $value) {
423  if (in_array($type, $typesTmp)) {
424  $linkTypes[$type] = 1;
425  }
426  }
427  return $linkTypes;
428  }
429 
438  protected function ‪reportEmail($pageSections, array $modTsConfig)
439  {
440  $lang = $this->‪getLanguageService();
441  $content = $this->templateService->substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
442  $markerArray = [];
443  $validEmailList = [];
444  $markerArray['totalBrokenLink'] = ‪$this->totalBrokenLink;
445  $markerArray['totalBrokenLink_old'] = ‪$this->oldTotalBrokenLink;
446 
447  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'] ?? [] as $userFunc) {
448  $params = [
449  'pObj' => &$this,
450  'markerArray' => $markerArray
451  ];
452  $ref = $this; // introduced for phpstan to not lose type information when passing $this into callUserFunction
453  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $ref);
454  if (is_array($newMarkers)) {
455  $markerArray = $newMarkers + $markerArray;
456  }
457  unset($params);
458  }
459  $content = $this->templateService->substituteMarkerArray($content, $markerArray, '###|###', true, true);
460  $mail = GeneralUtility::makeInstance(MailMessage::class);
461  if (empty($modTsConfig['mail.']['fromemail'])) {
462  $modTsConfig['mail.']['fromemail'] = ‪MailUtility::getSystemFromAddress();
463  }
464  if (empty($modTsConfig['mail.']['fromname'])) {
465  $modTsConfig['mail.']['fromname'] = ‪MailUtility::getSystemFromName();
466  }
467  if (GeneralUtility::validEmail($modTsConfig['mail.']['fromemail'])) {
468  $mail->from(new Address($modTsConfig['mail.']['fromemail'], $modTsConfig['mail.']['fromname']));
469  } else {
470  throw new \Exception(
471  $lang->sL($this->languageFile . ':tasks.error.invalidFromEmail'),
472  1295476760
473  );
474  }
475  if (GeneralUtility::validEmail($modTsConfig['mail.']['replytoemail'])) {
476  $mail->replyTo(new Address($modTsConfig['mail.']['replytoemail'], $modTsConfig['mail.']['replytoname']));
477  }
478  if (!empty($modTsConfig['mail.']['subject'])) {
479  $mail->subject($modTsConfig['mail.']['subject']);
480  } else {
481  throw new \Exception(
482  $lang->sL($this->languageFile . ':tasks.error.noSubject'),
483  1295476808
484  );
485  }
486  if (!empty($this->email)) {
487  // Check if old input field value is still there and save the value a
488  if (strpos($this->email, ',') !== false) {
489  $emailList = ‪GeneralUtility::trimExplode(',', $this->email, true);
490  $this->email = implode(LF, $emailList);
491  $this->‪save();
492  } else {
493  $emailList = ‪GeneralUtility::trimExplode(LF, $this->email, true);
494  }
495 
496  foreach ($emailList as $emailAdd) {
497  if (!GeneralUtility::validEmail($emailAdd)) {
498  throw new \Exception(
499  $lang->sL($this->languageFile . ':tasks.error.invalidToEmail'),
500  1295476821
501  );
502  }
503  $validEmailList[] = $emailAdd;
504  }
505  }
506  if (is_array($validEmailList) && !empty($validEmailList)) {
507  $mail
508  ->to(...$validEmailList)
509  ->html($content)
510  ->send();
511  return true;
512  }
513  return false;
514  }
515 
525  protected function ‪buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
526  {
527  $pageSectionHtml = $this->templateService->getSubpart($this->templateMail, '###PAGE_SECTION###');
528  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'] ?? [] as $userFunc) {
529  $params = [
530  'curPage' => $curPage,
531  'pageList' => $pageList,
532  'markerArray' => $markerArray,
533  'oldBrokenLink' => $oldBrokenLink,
534  'pObj' => &$this
535  ];
536  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
537  if (is_array($newMarkers)) {
538  $markerArray = $newMarkers + $markerArray;
539  }
540  unset($params);
541  }
542  foreach ($markerArray as $markerKey => $markerValue) {
543  if (empty($oldBrokenLink[$markerKey])) {
544  $oldBrokenLink[$markerKey] = 0;
545  }
546  if ($markerValue != $oldBrokenLink[$markerKey]) {
547  $this->isDifferentToLastRun = true;
548  }
549  $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
550  }
551  $markerArray['title'] = ‪BackendUtility::getRecordTitle(
552  'pages',
553  ‪BackendUtility::getRecord('pages', $curPage) ?? []
554  );
555  $content = '';
556  if ($markerArray['total'] > 0) {
557  $content = $this->templateService->substituteMarkerArray(
558  $pageSectionHtml,
559  $markerArray,
560  '###|###',
561  true,
562  true
563  );
564  }
565  return $content;
566  }
567 
574  public function ‪getAdditionalInformation()
575  {
576  $additionalInformation = [];
577 
578  ‪$page = (int)$this->‪getPage();
579  $pageLabel = ‪$page;
580  if (‪$page !== 0) {
581  $pageData = ‪BackendUtility::getRecord('pages', ‪$page);
582  if (!empty($pageData)) {
583  $pageTitle = ‪BackendUtility::getRecordTitle('pages', $pageData);
584  $pageLabel = $pageTitle . ' (' . ‪$page . ')';
585  }
586  }
587  $lang = $this->‪getLanguageService();
588  ‪$depth = (int)$this->‪getDepth();
589  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.page') . ': ' . $pageLabel;
590  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.depth') . ': '
591  . $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_' . (‪$depth === 999 ? 'infi' : ‪$depth));
592  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.email') . ': '
593  . $this->‪getEmail();
594 
595  return implode(', ', $additionalInformation);
596  }
597 
601  protected function ‪setCliArguments()
602  {
603  $_SERVER['argv'] = [
604  $_SERVER['argv'][0],
605  'tx_link_scheduler_link',
606  '0',
607  '-ss',
608  '--sleepTime',
610  '--sleepAfterFinish',
612  '--countInARun',
614  ];
615  }
616 }
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setPage
‪setPage($page)
Definition: ValidatorTask.php:175
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setEmailOnBrokenLinkOnly
‪setEmailOnBrokenLinkOnly($emailOnBrokenLinkOnly)
Definition: ValidatorTask.php:155
‪TYPO3\CMS\Scheduler\Task\AbstractTask\save
‪bool save()
Definition: AbstractTask.php:558
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$emailOnBrokenLinkOnly
‪bool $emailOnBrokenLinkOnly
Definition: ValidatorTask.php:106
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$page
‪int $page
Definition: ValidatorTask.php:94
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
Definition: TypoScriptParser.php:37
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$templateMail
‪string $templateMail
Definition: ValidatorTask.php:64
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\buildMail
‪string buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
Definition: ValidatorTask.php:509
‪TYPO3\CMS\Core\Utility\MailUtility\getSystemFromAddress
‪static string getSystemFromAddress()
Definition: MailUtility.php:73
‪TYPO3\CMS\Core\Mail\MailMessage
Definition: MailMessage.php:28
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setEmail
‪setEmail($email)
Definition: ValidatorTask.php:135
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setCliArguments
‪setCliArguments()
Definition: ValidatorTask.php:585
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getConfiguration
‪array getConfiguration()
Definition: ValidatorTask.php:225
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\checkPageLinks
‪string checkPageLinks($page)
Definition: ValidatorTask.php:301
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\execute
‪bool execute()
Definition: ValidatorTask.php:246
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$sleepAfterFinish
‪int $sleepAfterFinish
Definition: ValidatorTask.php:42
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$totalBrokenLink
‪int $totalBrokenLink
Definition: ValidatorTask.php:52
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$isDifferentToLastRun
‪bool $isDifferentToLastRun
Definition: ValidatorTask.php:76
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$languageFile
‪string $languageFile
Definition: ValidatorTask.php:116
‪TYPO3\CMS\Scheduler\Task\AbstractTask\getLanguageService
‪LanguageService null getLanguageService()
Definition: AbstractTask.php:605
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\loadModTsConfig
‪array loadModTsConfig($page)
Definition: ValidatorTask.php:355
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getAdditionalInformation
‪string getAdditionalInformation()
Definition: ValidatorTask.php:558
‪TYPO3\CMS\Scheduler\Task\AbstractTask
Definition: AbstractTask.php:35
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getEmailOnBrokenLinkOnly
‪bool getEmailOnBrokenLinkOnly()
Definition: ValidatorTask.php:145
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$oldTotalBrokenLink
‪int $oldTotalBrokenLink
Definition: ValidatorTask.php:58
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$depth
‪int $depth
Definition: ValidatorTask.php:88
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setDepth
‪setDepth($depth)
Definition: ValidatorTask.php:195
‪TYPO3\CMS\Backend\Utility\BackendUtility\getPagesTSconfig
‪static array getPagesTSconfig($id)
Definition: BackendUtility.php:698
‪TYPO3\CMS\Backend\Utility\BackendUtility\getRecordTitle
‪static string getRecordTitle($table, $row, $prep=false, $forceResult=true)
Definition: BackendUtility.php:1541
‪TYPO3\CMS\Core\Utility\MailUtility
Definition: MailUtility.php:24
‪TYPO3\CMS\Linkvalidator\Task
Definition: ValidatorTask.php:16
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$emailTemplateFile
‪string $emailTemplateFile
Definition: ValidatorTask.php:82
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$templateService
‪MarkerBasedTemplateService $templateService
Definition: ValidatorTask.php:110
‪TYPO3\CMS\Backend\Utility\BackendUtility\getRecord
‪static array null getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
Definition: BackendUtility.php:95
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$brokenLinkRepository
‪BrokenLinkRepository $brokenLinkRepository
Definition: ValidatorTask.php:118
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setConfiguration
‪setConfiguration($configuration)
Definition: ValidatorTask.php:235
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\setEmailTemplateFile
‪setEmailTemplateFile($emailTemplateFile)
Definition: ValidatorTask.php:215
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$countInARun
‪int $countInARun
Definition: ValidatorTask.php:46
‪TYPO3\CMS\Core\Utility\MailUtility\getSystemFromName
‪static string getSystemFromName()
Definition: MailUtility.php:52
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getPage
‪int getPage()
Definition: ValidatorTask.php:165
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getDepth
‪int getDepth()
Definition: ValidatorTask.php:185
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getLinkTypes
‪array getLinkTypes(array $modTS)
Definition: ValidatorTask.php:402
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService
Definition: MarkerBasedTemplateService.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getSearchField
‪array getSearchField(array $modTS)
Definition: ValidatorTask.php:383
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$email
‪string $email
Definition: ValidatorTask.php:100
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\reportEmail
‪bool reportEmail($pageSections, array $modTsConfig)
Definition: ValidatorTask.php:422
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$configuration
‪array $configuration
Definition: ValidatorTask.php:70
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getEmail
‪string getEmail()
Definition: ValidatorTask.php:125
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\getEmailTemplateFile
‪string getEmailTemplateFile()
Definition: ValidatorTask.php:205
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask
Definition: ValidatorTask.php:35
‪TYPO3\CMS\Linkvalidator\Task\ValidatorTask\$sleepTime
‪int $sleepTime
Definition: ValidatorTask.php:38