TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
ValidatorTask.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Linkvalidator\Task;
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 
146  public function setEmail($email)
147  {
148  $this->email = $email;
149  }
150 
156  public function getEmailOnBrokenLinkOnly()
157  {
159  }
160 
168  {
169  $this->emailOnBrokenLinkOnly = $emailOnBrokenLinkOnly;
170  }
171 
177  public function getPage()
178  {
179  return $this->page;
180  }
181 
188  public function setPage($page)
189  {
190  $this->page = $page;
191  }
192 
198  public function getDepth()
199  {
200  return $this->depth;
201  }
202 
209  public function setDepth($depth)
210  {
211  $this->depth = $depth;
212  }
213 
219  public function getEmailTemplateFile()
220  {
222  }
223 
231  {
232  $this->emailTemplateFile = $emailTemplateFile;
233  }
234 
240  public function getConfiguration()
241  {
242  return $this->configuration;
243  }
244 
252  {
253  $this->configuration = $configuration;
254  }
255 
262  public function execute()
263  {
264  $this->setCliArguments();
265  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
266  $successfullyExecuted = true;
267  if (!file_exists(($file = GeneralUtility::getFileAbsFileName($this->emailTemplateFile)))
268  && !empty($this->email)
269  ) {
270  if ($this->emailTemplateFile === 'EXT:linkvalidator/res/mailtemplate.html') {
271  // Update the default email template file path
272  $this->emailTemplateFile = 'EXT:linkvalidator/Resources/Private/Templates/mailtemplate.html';
273  $this->save();
274  } else {
275  $lang = $this->getLanguageService();
276  throw new \InvalidArgumentException(
277  $lang->sL($this->languageFile . ':tasks.error.invalidEmailTemplateFile'),
278  '1295476972'
279  );
280  }
281  }
282  $htmlFile = file_get_contents($file);
283  $this->templateMail = $this->templateService->getSubpart($htmlFile, '###REPORT_TEMPLATE###');
284  // The array to put the content into
285  $pageSections = '';
286  $this->isDifferentToLastRun = false;
287  $pageList = GeneralUtility::trimExplode(',', $this->page, true);
288  $modTs = $this->loadModTsConfig($this->page);
289  if (is_array($pageList)) {
290  foreach ($pageList as $page) {
291  $pageSections .= $this->checkPageLinks($page);
292  }
293  }
294  if ($this->totalBrokenLink != $this->oldTotalBrokenLink) {
295  $this->isDifferentToLastRun = true;
296  }
297  if ($this->totalBrokenLink > 0
298  && (!$this->emailOnBrokenLinkOnly || $this->isDifferentToLastRun)
299  && !empty($this->email)
300  ) {
301  $successfullyExecuted = $this->reportEmail($pageSections, $modTs);
302  }
303  return $successfullyExecuted;
304  }
305 
312  protected function checkPageLinks($page)
313  {
314  $page = (int)$page;
315  $pageSections = '';
316  $pageIds = '';
317  $oldLinkCounts = [];
318  $modTs = $this->loadModTsConfig($page);
319  $searchFields = $this->getSearchField($modTs);
320  $linkTypes = $this->getLinkTypes($modTs);
322  $processor = GeneralUtility::makeInstance(LinkAnalyzer::class);
323  if ($page === 0) {
324  $rootLineHidden = false;
325  } else {
326  $pageRow = BackendUtility::getRecord('pages', $page, '*', '', false);
327  $rootLineHidden = $processor->getRootLineIsHidden($pageRow);
328  }
329  if (!$rootLineHidden || $modTs['checkhidden'] == 1) {
330  $pageIds = $processor->extGetTreeList($page, $this->depth, 0, '1=1', $modTs['checkhidden']);
331  if (isset($pageRow) && $pageRow['hidden'] == 0 || $modTs['checkhidden'] == 1) {
332  // \TYPO3\CMS\Linkvalidator\LinkAnalyzer->extGetTreeList() always adds trailing comma
333  $pageIds .= $page;
334  }
335  }
336  if (!empty($pageIds)) {
337  $processor->init($searchFields, $pageIds, $modTs);
338  if (!empty($this->email)) {
339  $oldLinkCounts = $processor->getLinkCounts($page);
340  $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount'];
341  }
342  $processor->getLinkStatistics($linkTypes, $modTs['checkhidden']);
343  if (!empty($this->email)) {
344  $linkCounts = $processor->getLinkCounts($page);
345  $this->totalBrokenLink += $linkCounts['brokenlinkCount'];
346  $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
347  }
348  }
349  return $pageSections;
350  }
351 
359  protected function loadModTsConfig($page)
360  {
361  $modTs = BackendUtility::getModTSconfig($page, 'mod.linkvalidator');
362  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
363  $parseObj->parse($this->configuration);
364  if (!empty($parseObj->errors)) {
365  $languageService = $this->getLanguageService();
366  $parseErrorMessage = $languageService->sL($this->languageFile . ':tasks.error.invalidTSconfig')
367  . '<br />';
368  foreach ($parseObj->errors as $errorInfo) {
369  $parseErrorMessage .= $errorInfo[0] . '<br />';
370  }
371  throw new \Exception($parseErrorMessage, '1295476989');
372  }
373  $tsConfig = $parseObj->setup;
374  $modTs = $modTs['properties'];
375  $overrideTs = $tsConfig['mod.']['linkvalidator.'];
376  if (is_array($overrideTs)) {
377  ArrayUtility::mergeRecursiveWithOverrule($modTs, $overrideTs);
378  }
379  return $modTs;
380  }
381 
388  protected function getSearchField(array $modTS)
389  {
390  // Get the searchFields from TypoScript
391  foreach ($modTS['searchFields.'] as $table => $fieldList) {
392  $fields = GeneralUtility::trimExplode(',', $fieldList);
393  foreach ($fields as $field) {
394  $searchFields[$table][] = $field;
395  }
396  }
397  return isset($searchFields) ? $searchFields : [];
398  }
399 
406  protected function getLinkTypes(array $modTS)
407  {
408  $linkTypes = [];
409  $typesTmp = GeneralUtility::trimExplode(',', $modTS['linktypes'], true);
410  if (is_array($typesTmp)) {
411  if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
412  && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
413  ) {
414  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
415  if (in_array($type, $typesTmp)) {
416  $linkTypes[$type] = 1;
417  }
418  }
419  }
420  }
421  return $linkTypes;
422  }
423 
432  protected function reportEmail($pageSections, array $modTsConfig)
433  {
434  $lang = $this->getLanguageService();
435  $content = $this->templateService->substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
437  $markerArray = [];
439  $validEmailList = [];
441  $sendEmail = true;
442  $markerArray['totalBrokenLink'] = $this->totalBrokenLink;
443  $markerArray['totalBrokenLink_old'] = $this->oldTotalBrokenLink;
444 
445  // Hook
446  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'])) {
447  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'] as $userFunc) {
448  $params = [
449  'pObj' => &$this,
450  'markerArray' => $markerArray
451  ];
452  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
453  if (is_array($newMarkers)) {
454  $markerArray = $newMarkers + $markerArray;
455  }
456  unset($params);
457  }
458  }
459  $content = $this->templateService->substituteMarkerArray($content, $markerArray, '###|###', true, true);
461  $mail = GeneralUtility::makeInstance(MailMessage::class);
462  if (empty($modTsConfig['mail.']['fromemail'])) {
463  $modTsConfig['mail.']['fromemail'] = MailUtility::getSystemFromAddress();
464  }
465  if (empty($modTsConfig['mail.']['fromname'])) {
466  $modTsConfig['mail.']['fromname'] = MailUtility::getSystemFromName();
467  }
468  if (GeneralUtility::validEmail($modTsConfig['mail.']['fromemail'])) {
469  $mail->setFrom([$modTsConfig['mail.']['fromemail'] => $modTsConfig['mail.']['fromname']]);
470  } else {
471  throw new \Exception(
472  $lang->sL($this->languageFile . ':tasks.error.invalidFromEmail'),
473  '1295476760'
474  );
475  }
476  if (GeneralUtility::validEmail($modTsConfig['mail.']['replytoemail'])) {
477  $mail->setReplyTo([$modTsConfig['mail.']['replytoemail'] => $modTsConfig['mail.']['replytoname']]);
478  }
479  if (!empty($modTsConfig['mail.']['subject'])) {
480  $mail->setSubject($modTsConfig['mail.']['subject']);
481  } else {
482  throw new \Exception(
483  $lang->sL($this->languageFile . ':tasks.error.noSubject'),
484  '1295476808'
485  );
486  }
487  if (!empty($this->email)) {
488  // Check if old input field value is still there and save the value a
489  if (strpos($this->email, ',') !== false) {
490  $emailList = GeneralUtility::trimExplode(',', $this->email, true);
491  $this->email = implode(LF, $emailList);
492  $this->save();
493  } else {
494  $emailList = GeneralUtility::trimExplode(LF, $this->email, true);
495  }
496 
497  foreach ($emailList as $emailAdd) {
498  if (!GeneralUtility::validEmail($emailAdd)) {
499  throw new \Exception(
500  $lang->sL($this->languageFile . ':tasks.error.invalidToEmail'),
501  '1295476821'
502  );
503  } else {
504  $validEmailList[] = $emailAdd;
505  }
506  }
507  }
508  if (is_array($validEmailList) && !empty($validEmailList)) {
509  $mail->setTo($validEmailList);
510  } else {
511  $sendEmail = false;
512  }
513  if ($sendEmail) {
514  $mail->setBody($content, 'text/html');
515  $mail->send();
516  }
517  return $sendEmail;
518  }
519 
529  protected function buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
530  {
531  $pageSectionHtml = $this->templateService->getSubpart($this->templateMail, '###PAGE_SECTION###');
532  // Hook
533  if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'])) {
534  foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'] as $userFunc) {
535  $params = [
536  'curPage' => $curPage,
537  'pageList' => $pageList,
538  'markerArray' => $markerArray,
539  'oldBrokenLink' => $oldBrokenLink,
540  'pObj' => &$this
541  ];
542  $newMarkers = GeneralUtility::callUserFunction($userFunc, $params, $this);
543  if (is_array($newMarkers)) {
544  $markerArray = $newMarkers + $markerArray;
545  }
546  unset($params);
547  }
548  }
549  if (is_array($markerArray)) {
550  foreach ($markerArray as $markerKey => $markerValue) {
551  if (empty($oldBrokenLink[$markerKey])) {
552  $oldBrokenLink[$markerKey] = 0;
553  }
554  if ($markerValue != $oldBrokenLink[$markerKey]) {
555  $this->isDifferentToLastRun = true;
556  }
557  $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
558  }
559  }
560  $markerArray['title'] = BackendUtility::getRecordTitle(
561  'pages',
562  BackendUtility::getRecord('pages', $curPage)
563  );
564  $content = '';
565  if ($markerArray['brokenlinkCount'] > 0) {
566  $content = $this->templateService->substituteMarkerArray(
567  $pageSectionHtml,
568  $markerArray,
569  '###|###',
570  true,
571  true
572  );
573  }
574  return $content;
575  }
576 
583  public function getAdditionalInformation()
584  {
585  $additionalInformation = [];
586 
587  $page = (int)$this->getPage();
588  $pageLabel = $page;
589  if ($page !== 0) {
590  $pageData = BackendUtility::getRecord('pages', $page);
591  if (!empty($pageData)) {
592  $pageTitle = BackendUtility::getRecordTitle('pages', $pageData);
593  $pageLabel = $pageTitle . ' (' . $page . ')';
594  }
595  }
596  $lang = $this->getLanguageService();
597  $depth = (int)$this->getDepth();
598  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.page') . ': ' . $pageLabel;
599  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.depth') . ': '
600  . $lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.depth_' . ($depth === 999 ? 'infi' : $depth));
601  $additionalInformation[] = $lang->sL($this->languageFile . ':tasks.validate.email') . ': '
602  . $this->getEmail();
603 
604  return implode(', ', $additionalInformation);
605  }
606 
612  protected function setCliArguments()
613  {
614  $_SERVER['argv'] = [
615  $_SERVER['argv'][0],
616  'tx_link_scheduler_link',
617  '0',
618  '-ss',
619  '--sleepTime',
621  '--sleepAfterFinish',
623  '--countInARun',
625  ];
626  }
627 
631  protected function getLanguageService()
632  {
633  return $GLOBALS['LANG'];
634  }
635 }
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink)
static getRecordTitle($table, $row, $prep=false, $forceResult=true)
static getRecord($table, $uid, $fields= '*', $where= '', $useDeleteClause=true)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
setEmailOnBrokenLinkOnly($emailOnBrokenLinkOnly)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static getFileAbsFileName($filename, $_=null, $_2=null)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)