a621a7b47d9801c3edca1ec060800d4784e6390a
[Packages/TYPO3.CMS.git] / typo3 / sysext / linkvalidator / Classes / Task / ValidatorTask.php
1 <?php
2 namespace TYPO3\CMS\Linkvalidator\Task;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010 - 2013 Michael Miousse (michael.miousse@infoglobe.ca)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 /**
27 * This class provides Scheduler plugin implementation
28 *
29 * @author Michael Miousse <michael.miousse@infoglobe.ca>
30 */
31 class ValidatorTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
32
33 /**
34 * @var integer
35 */
36 protected $sleepTime;
37
38 /**
39 * @var integer
40 */
41 protected $sleepAfterFinish;
42
43 /**
44 * @var integer
45 */
46 protected $countInARun;
47
48 /**
49 * Total number of broken links
50 *
51 * @var integer
52 */
53 protected $totalBrokenLink = 0;
54
55 /**
56 * Total number of broken links from the last run
57 *
58 * @var integer
59 */
60 protected $oldTotalBrokenLink = 0;
61
62 /**
63 * Mail template fetched from the given template file
64 *
65 * @var string
66 */
67 protected $templateMail;
68
69 /**
70 * specific TSconfig for this task.
71 *
72 * @var array
73 */
74 protected $configuration = array();
75
76 /**
77 * Shows if number of result was different from the result of the last check
78 *
79 * @var boolean
80 */
81 protected $isDifferentToLastRun;
82
83 /**
84 * Template to be used for the email
85 *
86 * @var string
87 */
88 protected $emailTemplateFile;
89
90 /**
91 * Level of pages the task should check
92 *
93 * @var integer
94 */
95 protected $depth;
96
97 /**
98 * UID of the start page for this task
99 *
100 * @var integer
101 */
102 protected $page;
103
104 /**
105 * Email address to which an email report is sent
106 *
107 * @var string
108 */
109 protected $email;
110
111 /**
112 * Only send an email, if new broken links were found
113 *
114 * @var boolean
115 */
116 protected $emailOnBrokenLinkOnly;
117
118 /**
119 * Get the value of the protected property email
120 *
121 * @return string Email address to which an email report is sent
122 */
123 public function getEmail() {
124 return $this->email;
125 }
126
127 /**
128 * Set the value of the private property email.
129 *
130 * @param string $email Email address to which an email report is sent
131 * @return void
132 */
133 public function setEmail($email) {
134 $this->email = $email;
135 }
136
137 /**
138 * Get the value of the protected property emailOnBrokenLinkOnly
139 *
140 * @return boolean Whether to send an email, if new broken links were found
141 */
142 public function getEmailOnBrokenLinkOnly() {
143 return $this->emailOnBrokenLinkOnly;
144 }
145
146 /**
147 * Set the value of the private property emailOnBrokenLinkOnly
148 *
149 * @param boolean $emailOnBrokenLinkOnly Only send an email, if new broken links were found
150 * @return void
151 */
152 public function setEmailOnBrokenLinkOnly($emailOnBrokenLinkOnly) {
153 $this->emailOnBrokenLinkOnly = $emailOnBrokenLinkOnly;
154 }
155
156 /**
157 * Get the value of the protected property page
158 *
159 * @return integer UID of the start page for this task
160 */
161 public function getPage() {
162 return $this->page;
163 }
164
165 /**
166 * Set the value of the private property page
167 *
168 * @param integer $page UID of the start page for this task.
169 * @return void
170 */
171 public function setPage($page) {
172 $this->page = $page;
173 }
174
175 /**
176 * Get the value of the protected property depth
177 *
178 * @return integer Level of pages the task should check
179 */
180 public function getDepth() {
181 return $this->depth;
182 }
183
184 /**
185 * Set the value of the private property depth
186 *
187 * @param integer $depth Level of pages the task should check
188 * @return void
189 */
190 public function setDepth($depth) {
191 $this->depth = $depth;
192 }
193
194 /**
195 * Get the value of the protected property emailTemplateFile
196 *
197 * @return string Template to be used for the email
198 */
199 public function getEmailTemplateFile() {
200 return $this->emailTemplateFile;
201 }
202
203 /**
204 * Set the value of the private property emailTemplateFile
205 *
206 * @param string $emailTemplateFile Template to be used for the email
207 * @return void
208 */
209 public function setEmailTemplateFile($emailTemplateFile) {
210 $this->emailTemplateFile = $emailTemplateFile;
211 }
212
213 /**
214 * Get the value of the protected property configuration
215 *
216 * @return array specific TSconfig for this task
217 */
218 public function getConfiguration() {
219 return $this->configuration;
220 }
221
222 /**
223 * Set the value of the private property configuration
224 *
225 * @param array $configuration specific TSconfig for this task
226 * @return void
227 */
228 public function setConfiguration($configuration) {
229 $this->configuration = $configuration;
230 }
231
232 /**
233 * Function execute from the Scheduler
234 *
235 * @throws \Exception if the email templale file can not be read
236 * @return boolean TRUE on successful execution, FALSE on error
237 */
238 public function execute() {
239 $this->setCliArguments();
240 $successfullyExecuted = TRUE;
241 if (!file_exists(($file = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($this->emailTemplateFile)))
242 && !empty($this->email)) {
243 throw new \Exception($GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidEmailTemplateFile'), '1295476972');
244 }
245 $htmlFile = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($file);
246 $this->templateMail = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($htmlFile, '###REPORT_TEMPLATE###');
247 // The array to put the content into
248 $html = array();
249 $pageSections = '';
250 $this->isDifferentToLastRun = FALSE;
251 $pageList = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->page, 1);
252 $modTS = $this->loadModTsConfig($this->page);
253 if (is_array($pageList)) {
254 foreach ($pageList as $page) {
255 $pageSections .= $this->checkPageLinks($page);
256 }
257 }
258 if ($this->totalBrokenLink != $this->oldTotalBrokenLink) {
259 $this->isDifferentToLastRun = TRUE;
260 }
261 if ($this->totalBrokenLink > 0 && (!$this->emailOnBrokenLinkOnly || $this->isDifferentToLastRun) && !empty($this->email)) {
262 $successfullyExecuted = $this->reportEmail($pageSections, $modTS);
263 }
264 return $successfullyExecuted;
265 }
266
267 /**
268 * Validate all links for a page based on the task configuration
269 *
270 * @param integer $page Uid of the page to parse
271 * @return string $pageSections Content of page section
272 */
273 protected function checkPageLinks($page) {
274 $page = intval($page);
275 $pageSections = '';
276 $pageIds = '';
277 $oldLinkCounts = array();
278 $modTS = $this->loadModTsConfig($page);
279 $searchFields = $this->getSearchField($modTS);
280 $linkTypes = $this->getLinkTypes($modTS);
281 /** @var \TYPO3\CMS\Linkvalidator\LinkAnalyzer $processor */
282 $processor = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Linkvalidator\\LinkAnalyzer');
283 if ($page === 0) {
284 $rootLineHidden = FALSE;
285 } else {
286 $pageRow = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . $page);
287 $rootLineHidden = $processor->getRootLineIsHidden($pageRow);
288 }
289 if (!$rootLineHidden || $modTS['checkhidden'] == 1) {
290 $pageIds = $processor->extGetTreeList($page, $this->depth, 0, '1=1', $modTS['checkhidden']);
291 if (isset($pageRow) && $pageRow['hidden'] == 0 || $modTS['checkhidden'] == 1) {
292 // \TYPO3\CMS\Linkvalidator\LinkAnalyzer->extGetTreeList() always adds trailing comma
293 $pageIds .= $page;
294 }
295 }
296 if (!empty($pageIds)) {
297 $processor->init($searchFields, $pageIds);
298 if (!empty($this->email)) {
299 $oldLinkCounts = $processor->getLinkCounts($page);
300 $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount'];
301 }
302 $processor->getLinkStatistics($linkTypes, $modTS['checkhidden']);
303 if (!empty($this->email)) {
304 $linkCounts = $processor->getLinkCounts($page);
305 $this->totalBrokenLink += $linkCounts['brokenlinkCount'];
306 $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
307 }
308 }
309 return $pageSections;
310 }
311
312 /**
313 * Get the linkvalidator modTSconfig for a page
314 *
315 * @param integer $page Uid of the page
316 * @throws \Exception
317 * @return array $modTsConfig mod.linkvalidator TSconfig array
318 */
319 protected function loadModTsConfig($page) {
320 $modTs = \TYPO3\CMS\Backend\Utility\BackendUtility::getModTSconfig($page, 'mod.linkvalidator');
321 $parseObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
322 $parseObj->parse($this->configuration);
323 if (count($parseObj->errors) > 0) {
324 $parseErrorMessage = $GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidTSconfig') . '<br />';
325 foreach ($parseObj->errors as $errorInfo) {
326 $parseErrorMessage .= $errorInfo[0] . '<br />';
327 }
328 throw new \Exception($parseErrorMessage, '1295476989');
329 }
330 $tsConfig = $parseObj->setup;
331 $modTs = $modTs['properties'];
332 $overrideTs = $tsConfig['mod.']['tx_linkvalidator.'];
333 if (is_array($overrideTs)) {
334 $modTs = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge_recursive_overrule($modTs, $overrideTs);
335 }
336 return $modTs;
337 }
338
339 /**
340 * Get the list of fields to parse in modTSconfig
341 *
342 * @param array $modTS mod.linkvalidator TSconfig array
343 * @return array $searchFields List of fields
344 */
345 protected function getSearchField(array $modTS) {
346 // Get the searchFields from TypoScript
347 foreach ($modTS['searchFields.'] as $table => $fieldList) {
348 $fields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $fieldList);
349 foreach ($fields as $field) {
350 $searchFields[$table][] = $field;
351 }
352 }
353 return isset($searchFields) ? $searchFields : array();
354 }
355
356 /**
357 * Get the list of linkTypes to parse in modTSconfig
358 *
359 * @param array $modTS mod.linkvalidator TSconfig array
360 * @return array $linkTypes list of link types
361 */
362 protected function getLinkTypes(array $modTS) {
363 $linkTypes = array();
364 $typesTmp = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $modTS['linktypes'], 1);
365 if (is_array($typesTmp)) {
366 if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks']) && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
367 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
368 if (in_array($type, $typesTmp)) {
369 $linkTypes[$type] = 1;
370 }
371 }
372 }
373 }
374 return $linkTypes;
375 }
376
377 /**
378 * Build and send warning email when new broken links were found
379 *
380 * @param string $pageSections Content of page section
381 * @param array $modTsConfig TSconfig array
382 * @throws \Exception if required modTsConfig settings are missing
383 * @return boolean TRUE if mail was sent, FALSE if or not
384 */
385 protected function reportEmail($pageSections, array $modTsConfig) {
386 $content = \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
387 /** @var array $markerArray */
388 $markerArray = array();
389 /** @var array $validEmailList */
390 $validEmailList = array();
391 /** @var boolean $sendEmail */
392 $sendEmail = TRUE;
393 $markerArray['totalBrokenLink'] = $this->totalBrokenLink;
394 $markerArray['totalBrokenLink_old'] = $this->oldTotalBrokenLink;
395 // Hook
396 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'])) {
397 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['reportEmailMarkers'] as $userFunc) {
398 $params = array(
399 'pObj' => &$this,
400 'markerArray' => $markerArray
401 );
402 $newMarkers = \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($userFunc, $params, $this);
403 if (is_array($newMarkers)) {
404 $markerArray = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($markerArray, $newMarkers);
405 }
406 unset($params);
407 }
408 }
409 $content = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($content, $markerArray, '###|###', TRUE, TRUE);
410 /** @var \TYPO3\CMS\Core\Mail\MailMessage $mail */
411 $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
412 if (empty($modTsConfig['mail.']['fromemail'])) {
413 $modTsConfig['mail.']['fromemail'] = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFromAddress();
414 }
415 if (empty($modTsConfig['mail.']['fromname'])) {
416 $modTsConfig['mail.']['fromname'] = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFromName();
417 }
418 if (\TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($modTsConfig['mail.']['fromemail'])) {
419 $mail->setFrom(array($modTsConfig['mail.']['fromemail'] => $modTsConfig['mail.']['fromname']));
420 } else {
421 throw new \Exception($GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidFromEmail'), '1295476760');
422 }
423 if (\TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($modTsConfig['mail.']['replytoemail'])) {
424 $mail->setReplyTo(array($modTsConfig['mail.']['replytoemail'] => $modTsConfig['mail.']['replytoname']));
425 }
426 if (!empty($modTsConfig['mail.']['subject'])) {
427 $mail->setSubject($modTsConfig['mail.']['subject']);
428 } else {
429 throw new \Exception($GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.noSubject'), '1295476808');
430 }
431 if (!empty($this->email)) {
432 $emailList = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->email);
433 foreach ($emailList as $emailAdd) {
434 if (!\TYPO3\CMS\Core\Utility\GeneralUtility::validEmail($emailAdd)) {
435 throw new \Exception($GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidToEmail'), '1295476821');
436 } else {
437 $validEmailList[] = $emailAdd;
438 }
439 }
440 }
441 if (is_array($validEmailList) && !empty($validEmailList)) {
442 $mail->setTo($this->email);
443 } else {
444 $sendEmail = FALSE;
445 }
446 if ($sendEmail) {
447 $mail->setBody($content, 'text/html');
448 $mail->send();
449 }
450 return $sendEmail;
451 }
452
453 /**
454 * Build the mail content
455 *
456 * @param int $curPage Id of the current page
457 * @param string $pageList List of pages id
458 * @param array $markerArray Array of markers
459 * @param array $oldBrokenLink Marker array with the number of link found
460 * @return string Content of the mail
461 */
462 protected function buildMail($curPage, $pageList, array $markerArray, array $oldBrokenLink) {
463 $pageSectionHtml = \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($this->templateMail, '###PAGE_SECTION###');
464 // Hook
465 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'])) {
466 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['buildMailMarkers'] as $userFunc) {
467 $params = array(
468 'curPage' => $curPage,
469 'pageList' => $pageList,
470 'markerArray' => $markerArray,
471 'oldBrokenLink' => $oldBrokenLink,
472 'pObj' => &$this
473 );
474 $newMarkers = \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($userFunc, $params, $this);
475 if (is_array($newMarkers)) {
476 $markerArray = \TYPO3\CMS\Core\Utility\GeneralUtility::array_merge($markerArray, $newMarkers);
477 }
478 unset($params);
479 }
480 }
481 if (is_array($markerArray)) {
482 foreach ($markerArray as $markerKey => $markerValue) {
483 if (empty($oldBrokenLink[$markerKey])) {
484 $oldBrokenLink[$markerKey] = 0;
485 }
486 if ($markerValue != $oldBrokenLink[$markerKey]) {
487 $this->isDifferentToLastRun = TRUE;
488 }
489 $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
490 }
491 }
492 $markerArray['title'] = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordTitle('pages', \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $curPage));
493 $content = '';
494 if ($markerArray['brokenlinkCount'] > 0) {
495 $content = \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($pageSectionHtml, $markerArray, '###|###', TRUE, TRUE);
496 }
497 return $content;
498 }
499
500 /**
501 * Simulate cli call with setting the required options to the $_SERVER['argv']
502 *
503 * @return void
504 */
505 protected function setCliArguments() {
506 $_SERVER['argv'] = array(
507 $_SERVER['argv'][0],
508 'tx_link_scheduler_link',
509 '0',
510 '-ss',
511 '--sleepTime',
512 $this->sleepTime,
513 '--sleepAfterFinish',
514 $this->sleepAfterFinish,
515 '--countInARun',
516 $this->countInARun
517 );
518 }
519 }
520 ?>