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