TYPO3 CMS  TYPO3_8-7
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 
27 
32 {
36  protected $sleepTime;
37 
41  protected $sleepAfterFinish;
42 
46  protected $countInARun;
47 
53  protected $totalBrokenLink = 0;
54 
60  protected $oldTotalBrokenLink = 0;
61 
67  protected $templateMail;
68 
74  protected $configuration = [];
75 
82 
88  protected $emailTemplateFile;
89 
95  protected $depth;
96 
102  protected $page;
103 
109  protected $email;
110 
117 
121  protected $templateService;
122 
128  protected $languageFile = 'LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf';
129 
135  public function getEmail()
136  {
137  return $this->email;
138  }
139 
145  public function setEmail($email)
146  {
147  $this->email = $email;
148  }
149 
155  public function getEmailOnBrokenLinkOnly()
156  {
158  }
159 
166  {
167  $this->emailOnBrokenLinkOnly = $emailOnBrokenLinkOnly;
168  }
169 
175  public function getPage()
176  {
177  return $this->page;
178  }
179 
185  public function setPage($page)
186  {
187  $this->page = $page;
188  }
189 
195  public function getDepth()
196  {
197  return $this->depth;
198  }
199 
205  public function setDepth($depth)
206  {
207  $this->depth = $depth;
208  }
209 
215  public function getEmailTemplateFile()
216  {
218  }
219 
226  {
227  $this->emailTemplateFile = $emailTemplateFile;
228  }
229 
235  public function getConfiguration()
236  {
237  return $this->configuration;
238  }
239 
246  {
247  $this->configuration = $configuration;
248  }
249 
256  public function execute()
257  {
258  $this->setCliArguments();
259  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
260  $successfullyExecuted = true;
261  if (!file_exists(($file = GeneralUtility::getFileAbsFileName($this->emailTemplateFile)))
262  && !empty($this->email)
263  ) {
264  if ($this->emailTemplateFile === 'EXT:linkvalidator/res/mailtemplate.html') {
265  // Update the default email template file path
266  $this->emailTemplateFile = 'EXT:linkvalidator/Resources/Private/Templates/mailtemplate.html';
267  $this->save();
268  } else {
269  $lang = $this->getLanguageService();
270  throw new \InvalidArgumentException(
271  $lang->sL($this->languageFile . ':tasks.error.invalidEmailTemplateFile'),
272  '1295476972'
273  );
274  }
275  }
276  $htmlFile = file_get_contents($file);
277  $this->templateMail = $this->templateService->getSubpart($htmlFile, '###REPORT_TEMPLATE###');
278  // The array to put the content into
279  $pageSections = '';
280  $this->isDifferentToLastRun = false;
281  $pageList = GeneralUtility::trimExplode(',', $this->page, true);
282  $modTs = $this->loadModTsConfig($this->page);
283  if (is_array($pageList)) {
284  // reset broken link counts as they were stored in the serialized object
285  $this->oldTotalBrokenLink = 0;
286  $this->totalBrokenLink = 0;
287  foreach ($pageList as $page) {
288  $pageSections .= $this->checkPageLinks($page);
289  }
290  }
291  if ($this->totalBrokenLink != $this->oldTotalBrokenLink) {
292  $this->isDifferentToLastRun = true;
293  }
294  if ($this->totalBrokenLink > 0
295  && (!$this->emailOnBrokenLinkOnly || $this->isDifferentToLastRun)
296  && !empty($this->email)
297  ) {
298  $successfullyExecuted = $this->reportEmail($pageSections, $modTs);
299  }
300  return $successfullyExecuted;
301  }
302 
310  protected function checkPageLinks($page)
311  {
312  $page = (int)$page;
313  $pageSections = '';
314  $pageIds = '';
315  $oldLinkCounts = [];
316  $modTs = $this->loadModTsConfig($page);
317  $searchFields = $this->getSearchField($modTs);
318  $linkTypes = $this->getLinkTypes($modTs);
320  $processor = GeneralUtility::makeInstance(LinkAnalyzer::class);
321  if ($page === 0) {
322  $rootLineHidden = false;
323  } else {
324  $pageRow = BackendUtility::getRecord('pages', $page, '*', '', false);
325  if ($pageRow === null) {
326  throw new \InvalidArgumentException(
327  sprintf($this->getLanguageService()->sL($this->languageFile . ':tasks.error.invalidPageUid'), $page),
328  1502800555
329  );
330  }
331  $rootLineHidden = $processor->getRootLineIsHidden($pageRow);
332  }
333  if (!$rootLineHidden || $modTs['checkhidden'] == 1) {
334  $pageIds = $processor->extGetTreeList($page, $this->depth, 0, '1=1', $modTs['checkhidden']);
335  if (isset($pageRow) && $pageRow['hidden'] == 0 || $modTs['checkhidden'] == 1) {
336  // \TYPO3\CMS\Linkvalidator\LinkAnalyzer->extGetTreeList() always adds trailing comma
337  $pageIds .= $page;
338  }
339  }
340  if (!empty($pageIds)) {
341  $processor->init($searchFields, $pageIds, $modTs);
342  if (!empty($this->email)) {
343  $oldLinkCounts = $processor->getLinkCounts($page);
344  $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount'];
345  }
346  $processor->getLinkStatistics($linkTypes, $modTs['checkhidden']);
347  if (!empty($this->email)) {
348  $linkCounts = $processor->getLinkCounts($page);
349  $this->totalBrokenLink += $linkCounts['brokenlinkCount'];
350  $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
351  }
352  }
353  return $pageSections;
354  }
355 
363  protected function loadModTsConfig($page)
364  {
365  $modTs = BackendUtility::getModTSconfig($page, 'mod.linkvalidator');
366  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
367  $parseObj->parse($this->configuration);
368  if (!empty($parseObj->errors)) {
369  $languageService = $this->getLanguageService();
370  $parseErrorMessage = $languageService->sL($this->languageFile . ':tasks.error.invalidTSconfig')
371  . '<br />';
372  foreach ($parseObj->errors as $errorInfo) {
373  $parseErrorMessage .= $errorInfo[0] . '<br />';
374  }
375  throw new \Exception($parseErrorMessage, '1295476989');
376  }
377  $tsConfig = $parseObj->setup;
378  $modTs = $modTs['properties'];
379  $overrideTs = $tsConfig['mod.']['linkvalidator.'];
380  if (is_array($overrideTs)) {
381  ArrayUtility::mergeRecursiveWithOverrule($modTs, $overrideTs);
382  }
383  return $modTs;
384  }
385 
392  protected function getSearchField(array $modTS)
393  {
394  // Get the searchFields from TypoScript
395  foreach ($modTS['searchFields.'] as $table => $fieldList) {
396  $fields = GeneralUtility::trimExplode(',', $fieldList);
397  foreach ($fields as $field) {
398  $searchFields[$table][] = $field;
399  }
400  }
401  return isset($searchFields) ? $searchFields : [];
402  }
403 
410  protected function getLinkTypes(array $modTS)
411  {
412  $linkTypes = [];
413  $typesTmp = GeneralUtility::trimExplode(',', $modTS['linktypes'], true);
414  if (is_array($typesTmp)) {
415  if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
416  && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
417  ) {
418  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
419  if (in_array($type, $typesTmp)) {
420  $linkTypes[$type] = 1;
421  }
422  }
423  }
424  }
425  return $linkTypes;
426  }
427 
436  protected function reportEmail($pageSections, array $modTsConfig)
437  {
438  $lang = $this->getLanguageService();
439  $content = $this->templateService->substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
441  $markerArray = [];
443  $validEmailList = [];
445  $sendEmail = true;
446  $markerArray['totalBrokenLink'] = $this->totalBrokenLink;
447  $markerArray['totalBrokenLink_old'] = $this->oldTotalBrokenLink;
448 
449  // Hook
450  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'])) {
451  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'] as $userFunc) {
452  $params = [
453  'pObj' => &$this,
454  'markerArray' => $markerArray
455  ];
456  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
457  if (is_array($newMarkers)) {
458  $markerArray = $newMarkers + $markerArray;
459  }
460  unset($params);
461  }
462  }
463  $content = $this->templateService->substituteMarkerArray($content, $markerArray, '###|###', true, true);
465  $mail = GeneralUtility::makeInstance(MailMessage::class);
466  if (empty($modTsConfig['mail.']['fromemail'])) {
467  $modTsConfig['mail.']['fromemail'] = MailUtility::getSystemFromAddress();
468  }
469  if (empty($modTsConfig['mail.']['fromname'])) {
470  $modTsConfig['mail.']['fromname'] = MailUtility::getSystemFromName();
471  }
472  if (GeneralUtility::validEmail($modTsConfig['mail.']['fromemail'])) {
473  $mail->setFrom([$modTsConfig['mail.']['fromemail'] => $modTsConfig['mail.']['fromname']]);
474  } else {
475  throw new \Exception(
476  $lang->sL($this->languageFile . ':tasks.error.invalidFromEmail'),
477  '1295476760'
478  );
479  }
480  if (GeneralUtility::validEmail($modTsConfig['mail.']['replytoemail'])) {
481  $mail->setReplyTo([$modTsConfig['mail.']['replytoemail'] => $modTsConfig['mail.']['replytoname']]);
482  }
483  if (!empty($modTsConfig['mail.']['subject'])) {
484  $mail->setSubject($modTsConfig['mail.']['subject']);
485  } else {
486  throw new \Exception(
487  $lang->sL($this->languageFile . ':tasks.error.noSubject'),
488  '1295476808'
489  );
490  }
491  if (!empty($this->email)) {
492  // Check if old input field value is still there and save the value a
493  if (strpos($this->email, ',') !== false) {
494  $emailList = GeneralUtility::trimExplode(',', $this->email, true);
495  $this->email = implode(LF, $emailList);
496  $this->save();
497  } else {
498  $emailList = GeneralUtility::trimExplode(LF, $this->email, true);
499  }
500 
501  foreach ($emailList as $emailAdd) {
502  if (!GeneralUtility::validEmail($emailAdd)) {
503  throw new \Exception(
504  $lang->sL($this->languageFile . ':tasks.error.invalidToEmail'),
505  '1295476821'
506  );
507  }
508  $validEmailList[] = $emailAdd;
509  }
510  }
511  if (is_array($validEmailList) && !empty($validEmailList)) {
512  $mail->setTo($validEmailList);
513  } else {
514  $sendEmail = false;
515  }
516  if ($sendEmail) {
517  $mail->setBody($content, 'text/html');
518  $mail->send();
519  }
520  return $sendEmail;
521  }
522 
532  protected function buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
533  {
534  $pageSectionHtml = $this->templateService->getSubpart($this->templateMail, '###PAGE_SECTION###');
535  // Hook
536  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'])) {
537  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'] as $userFunc) {
538  $params = [
539  'curPage' => $curPage,
540  'pageList' => $pageList,
541  'markerArray' => $markerArray,
542  'oldBrokenLink' => $oldBrokenLink,
543  'pObj' => &$this
544  ];
545  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
546  if (is_array($newMarkers)) {
547  $markerArray = $newMarkers + $markerArray;
548  }
549  unset($params);
550  }
551  }
552  if (is_array($markerArray)) {
553  foreach ($markerArray as $markerKey => $markerValue) {
554  if (empty($oldBrokenLink[$markerKey])) {
555  $oldBrokenLink[$markerKey] = 0;
556  }
557  if ($markerValue != $oldBrokenLink[$markerKey]) {
558  $this->isDifferentToLastRun = true;
559  }
560  $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
561  }
562  }
563  $markerArray['title'] = BackendUtility::getRecordTitle(
564  'pages',
565  BackendUtility::getRecord('pages', $curPage)
566  );
567  $content = '';
568  if ($markerArray['brokenlinkCount'] > 0) {
569  $content = $this->templateService->substituteMarkerArray(
570  $pageSectionHtml,
571  $markerArray,
572  '###|###',
573  true,
574  true
575  );
576  }
577  return $content;
578  }
579 
586  public function getAdditionalInformation()
587  {
588  $additionalInformation = [];
589 
590  $page = (int)$this->getPage();
591  $pageLabel = $page;
592  if ($page !== 0) {
593  $pageData = BackendUtility::getRecord('pages', $page);
594  if (!empty($pageData)) {
595  $pageTitle = BackendUtility::getRecordTitle('pages', $pageData);
596  $pageLabel = $pageTitle . ' (' . $page . ')';
597  }
598  }
599  $lang = $this->getLanguageService();
600  $depth = (int)$this->getDepth();
601  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.page') . ': ' . $pageLabel;
602  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.depth') . ': '
603  . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_' . ($depth === 999 ? 'infi' : $depth));
604  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.email') . ': '
605  . $this->getEmail();
606 
607  return implode(', ', $additionalInformation);
608  }
609 
613  protected function setCliArguments()
614  {
615  $_SERVER['argv'] = [
616  $_SERVER['argv'][0],
617  'tx_link_scheduler_link',
618  '0',
619  '-ss',
620  '--sleepTime',
622  '--sleepAfterFinish',
624  '--countInARun',
626  ];
627  }
628 
632  protected function getLanguageService()
633  {
634  return $GLOBALS['LANG'];
635  }
636 }
setEmailOnBrokenLinkOnly($emailOnBrokenLinkOnly)
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
static getFileAbsFileName($filename, $_=null, $_2=null)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static makeInstance($className,... $constructorArguments)
$fields
Definition: pages.php:4
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 getRecord($table, $uid, $fields=' *', $where='', $useDeleteClause=true)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']