TYPO3 CMS  TYPO3_7-6
ValidatorTask.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
26 
31 {
35  protected $sleepTime;
36 
40  protected $sleepAfterFinish;
41 
45  protected $countInARun;
46 
52  protected $totalBrokenLink = 0;
53 
59  protected $oldTotalBrokenLink = 0;
60 
66  protected $templateMail;
67 
73  protected $configuration = [];
74 
81 
87  protected $emailTemplateFile;
88 
94  protected $depth;
95 
101  protected $page;
102 
108  protected $email;
109 
116 
120  protected $templateService;
121 
127  public function getEmail()
128  {
129  return $this->email;
130  }
131 
138  public function setEmail($email)
139  {
140  $this->email = $email;
141  }
142 
148  public function getEmailOnBrokenLinkOnly()
149  {
151  }
152 
160  {
161  $this->emailOnBrokenLinkOnly = $emailOnBrokenLinkOnly;
162  }
163 
169  public function getPage()
170  {
171  return $this->page;
172  }
173 
180  public function setPage($page)
181  {
182  $this->page = $page;
183  }
184 
190  public function getDepth()
191  {
192  return $this->depth;
193  }
194 
201  public function setDepth($depth)
202  {
203  $this->depth = $depth;
204  }
205 
211  public function getEmailTemplateFile()
212  {
214  }
215 
223  {
224  $this->emailTemplateFile = $emailTemplateFile;
225  }
226 
232  public function getConfiguration()
233  {
234  return $this->configuration;
235  }
236 
244  {
245  $this->configuration = $configuration;
246  }
247 
254  public function execute()
255  {
256  $this->setCliArguments();
257  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
258  $successfullyExecuted = true;
259  if (
260  !file_exists(($file = GeneralUtility::getFileAbsFileName($this->emailTemplateFile)))
261  && !empty($this->email)
262  ) {
263  if ($this->emailTemplateFile === 'EXT:linkvalidator/res/mailtemplate.html') {
264  // Update the default email template file path
265  $this->emailTemplateFile = 'EXT:linkvalidator/Resources/Private/Templates/mailtemplate.html';
266  $this->save();
267  } else {
268  throw new \InvalidArgumentException(
269  $this->getLanguageService()->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.error.invalidEmailTemplateFile'),
270  '1295476972'
271  );
272  }
273  }
274  $htmlFile = GeneralUtility::getURL($file);
275  $this->templateMail = $this->templateService->getSubpart($htmlFile, '###REPORT_TEMPLATE###');
276  // The array to put the content into
277  $pageSections = '';
278  $this->isDifferentToLastRun = false;
279  $pageList = GeneralUtility::trimExplode(',', $this->page, true);
280  $modTs = $this->loadModTsConfig($this->page);
281  if (is_array($pageList)) {
282  foreach ($pageList as $page) {
283  $pageSections .= $this->checkPageLinks($page);
284  }
285  }
286  if ($this->totalBrokenLink != $this->oldTotalBrokenLink) {
287  $this->isDifferentToLastRun = true;
288  }
289  if ($this->totalBrokenLink > 0 && (!$this->emailOnBrokenLinkOnly || $this->isDifferentToLastRun) && !empty($this->email)) {
290  $successfullyExecuted = $this->reportEmail($pageSections, $modTs);
291  }
292  return $successfullyExecuted;
293  }
294 
301  protected function checkPageLinks($page)
302  {
303  $page = (int)$page;
304  $pageSections = '';
305  $pageIds = '';
306  $oldLinkCounts = [];
307  $modTs = $this->loadModTsConfig($page);
308  $searchFields = $this->getSearchField($modTs);
309  $linkTypes = $this->getLinkTypes($modTs);
311  $processor = GeneralUtility::makeInstance(LinkAnalyzer::class);
312  if ($page === 0) {
313  $rootLineHidden = false;
314  } else {
315  $pageRow = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . $page);
316  $rootLineHidden = $processor->getRootLineIsHidden($pageRow);
317  }
318  if (!$rootLineHidden || $modTs['checkhidden'] == 1) {
319  $pageIds = $processor->extGetTreeList($page, $this->depth, 0, '1=1', $modTs['checkhidden']);
320  if (isset($pageRow) && $pageRow['hidden'] == 0 || $modTs['checkhidden'] == 1) {
321  // \TYPO3\CMS\Linkvalidator\LinkAnalyzer->extGetTreeList() always adds trailing comma
322  $pageIds .= $page;
323  }
324  }
325  if (!empty($pageIds)) {
326  $processor->init($searchFields, $pageIds, $modTs);
327  if (!empty($this->email)) {
328  $oldLinkCounts = $processor->getLinkCounts($page);
329  $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount'];
330  }
331  $processor->getLinkStatistics($linkTypes, $modTs['checkhidden']);
332  if (!empty($this->email)) {
333  $linkCounts = $processor->getLinkCounts($page);
334  $this->totalBrokenLink += $linkCounts['brokenlinkCount'];
335  $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
336  }
337  }
338  return $pageSections;
339  }
340 
348  protected function loadModTsConfig($page)
349  {
350  $modTs = BackendUtility::getModTSconfig($page, 'mod.linkvalidator');
351  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
352  $parseObj->parse($this->configuration);
353  if (!empty($parseObj->errors)) {
354  $parseErrorMessage = $this->getLanguageService()->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.error.invalidTSconfig') . '<br />';
355  foreach ($parseObj->errors as $errorInfo) {
356  $parseErrorMessage .= $errorInfo[0] . '<br />';
357  }
358  throw new \Exception($parseErrorMessage, '1295476989');
359  }
360  $tsConfig = $parseObj->setup;
361  $modTs = $modTs['properties'];
362  $overrideTs = $tsConfig['mod.']['linkvalidator.'];
363  if (is_array($overrideTs)) {
364  ArrayUtility::mergeRecursiveWithOverrule($modTs, $overrideTs);
365  } else {
366  $deprecatedOverrideTs = $tsConfig['mod.']['tx_linkvalidator.'];
367  if (is_array($deprecatedOverrideTs)) {
368  GeneralUtility::deprecationLog('Using mod.tx_linkvalidator in the scheduler TSConfig setting is deprecated since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8. Please use mod.linkvalidator instead.');
369  ArrayUtility::mergeRecursiveWithOverrule($modTs, $deprecatedOverrideTs);
370  }
371  }
372  return $modTs;
373  }
374 
381  protected function getSearchField(array $modTS)
382  {
383  // Get the searchFields from TypoScript
384  foreach ($modTS['searchFields.'] as $table => $fieldList) {
385  $fields = GeneralUtility::trimExplode(',', $fieldList);
386  foreach ($fields as $field) {
387  $searchFields[$table][] = $field;
388  }
389  }
390  return isset($searchFields) ? $searchFields : [];
391  }
392 
399  protected function getLinkTypes(array $modTS)
400  {
401  $linkTypes = [];
402  $typesTmp = GeneralUtility::trimExplode(',', $modTS['linktypes'], true);
403  if (is_array($typesTmp)) {
404  if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks']) && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
405  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
406  if (in_array($type, $typesTmp)) {
407  $linkTypes[$type] = 1;
408  }
409  }
410  }
411  }
412  return $linkTypes;
413  }
414 
423  protected function reportEmail($pageSections, array $modTsConfig)
424  {
425  $content = $this->templateService->substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
427  $markerArray = [];
429  $validEmailList = [];
431  $sendEmail = true;
432  $markerArray['totalBrokenLink'] = $this->totalBrokenLink;
433  $markerArray['totalBrokenLink_old'] = $this->oldTotalBrokenLink;
434  // Hook
435  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'])) {
436  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'] as $userFunc) {
437  $params = [
438  'pObj' => &$this,
439  'markerArray' => $markerArray
440  ];
441  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
442  if (is_array($newMarkers)) {
443  $markerArray = $newMarkers + $markerArray;
444  }
445  unset($params);
446  }
447  }
448  $content = $this->templateService->substituteMarkerArray($content, $markerArray, '###|###', true, true);
450  $mail = GeneralUtility::makeInstance(MailMessage::class);
451  if (empty($modTsConfig['mail.']['fromemail'])) {
452  $modTsConfig['mail.']['fromemail'] = MailUtility::getSystemFromAddress();
453  }
454  if (empty($modTsConfig['mail.']['fromname'])) {
455  $modTsConfig['mail.']['fromname'] = MailUtility::getSystemFromName();
456  }
457  if (GeneralUtility::validEmail($modTsConfig['mail.']['fromemail'])) {
458  $mail->setFrom([$modTsConfig['mail.']['fromemail'] => $modTsConfig['mail.']['fromname']]);
459  } else {
460  throw new \Exception($this->getLanguageService()->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.error.invalidFromEmail'), '1295476760');
461  }
462  if (GeneralUtility::validEmail($modTsConfig['mail.']['replytoemail'])) {
463  $mail->setReplyTo([$modTsConfig['mail.']['replytoemail'] => $modTsConfig['mail.']['replytoname']]);
464  }
465  if (!empty($modTsConfig['mail.']['subject'])) {
466  $mail->setSubject($modTsConfig['mail.']['subject']);
467  } else {
468  throw new \Exception($this->getLanguageService()->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.error.noSubject'), '1295476808');
469  }
470  if (!empty($this->email)) {
471  // Check if old input field value is still there and save the value a
472  if (strpos($this->email, ',') !== false) {
473  $emailList = GeneralUtility::trimExplode(',', $this->email, true);
474  $this->email = implode(LF, $emailList);
475  $this->save();
476  } else {
477  $emailList = GeneralUtility::trimExplode(LF, $this->email, true);
478  }
479 
480  foreach ($emailList as $emailAdd) {
481  if (!GeneralUtility::validEmail($emailAdd)) {
482  throw new \Exception($this->getLanguageService()->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.error.invalidToEmail'), '1295476821');
483  } else {
484  $validEmailList[] = $emailAdd;
485  }
486  }
487  }
488  if (is_array($validEmailList) && !empty($validEmailList)) {
489  $mail->setTo($validEmailList);
490  } else {
491  $sendEmail = false;
492  }
493  if ($sendEmail) {
494  $mail->setBody($content, 'text/html');
495  $mail->send();
496  }
497  return $sendEmail;
498  }
499 
509  protected function buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
510  {
511  $pageSectionHtml = $this->templateService->getSubpart($this->templateMail, '###PAGE_SECTION###');
512  // Hook
513  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'])) {
514  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'] as $userFunc) {
515  $params = [
516  'curPage' => $curPage,
517  'pageList' => $pageList,
518  'markerArray' => $markerArray,
519  'oldBrokenLink' => $oldBrokenLink,
520  'pObj' => &$this
521  ];
522  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
523  if (is_array($newMarkers)) {
524  $markerArray = $newMarkers + $markerArray;
525  }
526  unset($params);
527  }
528  }
529  if (is_array($markerArray)) {
530  foreach ($markerArray as $markerKey => $markerValue) {
531  if (empty($oldBrokenLink[$markerKey])) {
532  $oldBrokenLink[$markerKey] = 0;
533  }
534  if ($markerValue != $oldBrokenLink[$markerKey]) {
535  $this->isDifferentToLastRun = true;
536  }
537  $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
538  }
539  }
540  $markerArray['title'] = BackendUtility::getRecordTitle(
541  'pages',
542  BackendUtility::getRecord('pages', $curPage)
543  );
544  $content = '';
545  if ($markerArray['brokenlinkCount'] > 0) {
546  $content = $this->templateService->substituteMarkerArray($pageSectionHtml, $markerArray, '###|###', true, true);
547  }
548  return $content;
549  }
550 
557  public function getAdditionalInformation()
558  {
559  $additionalInformation = [];
560 
561  $page = (int)$this->getPage();
562  $pageLabel = $page;
563  if ($page !== 0) {
564  $pageData = BackendUtility::getRecord('pages', $page);
565  if (!empty($pageData)) {
566  $pageTitle = BackendUtility::getRecordTitle('pages', $pageData);
567  $pageLabel = $pageTitle . ' (' . $page . ')';
568  }
569  }
570  $lang = $this->getLanguageService();
571  $additionalInformation[] = $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.validate.page') . ': ' . $pageLabel;
572 
573  $depth = (int)$this->getDepth();
574  $additionalInformation[] = $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.validate.depth') . ': ' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.depth_' . ($depth === 999 ? 'infi' : $depth));
575 
576  $additionalInformation[] = $lang->sL('LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf:tasks.validate.email') . ': ' . $this->getEmail();
577 
578  return implode(', ', $additionalInformation);
579  }
580 
586  protected function setCliArguments()
587  {
588  $_SERVER['argv'] = [
589  $_SERVER['argv'][0],
590  'tx_link_scheduler_link',
591  '0',
592  '-ss',
593  '--sleepTime',
595  '--sleepAfterFinish',
597  '--countInARun',
599  ];
600  }
601 
605  protected function getLanguageService()
606  {
607  return $GLOBALS['LANG'];
608  }
609 
613  protected function getDatabaseConnection()
614  {
615  return $GLOBALS['TYPO3_DB'];
616  }
617 }
setEmailOnBrokenLinkOnly($emailOnBrokenLinkOnly)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
static getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']