* Fixed bug #12650: tx_linkvalidator_tasks_validator: exec() is too long
[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 diferent 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 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 Only 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 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 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 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 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 specific TSconfig for this task.
227 * @return void
228 */
229 public function setConfiguration($configuration) {
230 $this->configuration = $configuration;
231 }
232
233
234 /**
235 * Function executed from the Scheduler.
236 *
237 * @return void
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 $pageSections = '';
282 $modTS = $this->loadModTSconfig($page);
283 $searchFields = $this->getSearchField($modTS);
284 $linkTypes = $this->getLinkTypes($modTS);
285 $processor = t3lib_div::makeInstance('tx_linkvalidator_Processor');
286 $pageIds = $processor->extGetTreeList($page, $this->depth, 0, '1=1');
287 $pageIds .= $page;
288 $processor->init($searchFields, $pageIds);
289 if (!empty($this->email)) {
290 $oldLinkCounts = $processor->getLinkCounts($page);
291 $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount'];
292 }
293
294 $processor->getLinkStatistics($array, $modTS['checkhidden']);
295
296 if (!empty($this->email)) {
297 $linkCounts = $processor->getLinkCounts($page);
298 $this->totalBrokenLink += $linkCounts['brokenlinkCount'];
299 $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts);
300 }
301 return $pageSections;
302 }
303
304 /**
305 * Get the linkvalidator modTSconfig for a page.
306 *
307 * @param integer $page: uid of the page.
308 * @return array $modTS: mod.linkvalidator TSconfig array.
309 */
310 protected function loadModTSconfig($page) {
311 $modTS = t3lib_BEfunc::getModTSconfig($page, 'mod.linkvalidator');
312 $parseObj = t3lib_div::makeInstance('t3lib_TSparser');
313 $parseObj->parse($this->configuration);
314 if(count($parseObj->errors) > 0){
315 $parseErrorMessage = $GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidTSconfig') . '<br />';
316 foreach($parseObj->errors as $errorInfo){
317 $parseErrorMessage .= $errorInfo[0] . '<br />';
318 }
319 throw new Exception(
320 $parseErrorMessage,
321 '1295476989'
322 );
323 }
324 $TSconfig = $parseObj->setup;
325 $modTS = $modTS['properties'];
326 $overrideTs = $TSconfig['mod.']['tx_linkvalidator.'];
327 if (is_array($overrideTs)) {
328 $modTS = t3lib_div::array_merge_recursive_overrule($modTS, $overrideTs);
329 }
330 return $modTS;
331 }
332
333 /**
334 * Get the list of fields to parse in modTSconfig.
335 *
336 * @param array $modTS: mod.linkvalidator TSconfig array.
337 * @return array $searchFields: list of fields.
338 */
339 protected function getSearchField($modTS) {
340 // get the searchFields from TCA
341 foreach ($GLOBALS['TCA'] as $tableName => $table) {
342 if (!empty($table['columns'])) {
343 foreach ($table['columns'] as $columnName => $column) {
344 if ($column['config']['type'] == 'text' || $column['config']['type'] == 'input') {
345 if (!empty($column['config']['softref']) && (stripos($column['config']['softref'], "typolink")
346 !== FALSE || stripos($column['config']['softref'], "url") !== FALSE)) {
347
348 $searchFields[$tableName][] = $columnName;
349 }
350 }
351 }
352 }
353 }
354
355 // get the searchFields from TypoScript
356 foreach ($modTS['searchFields.'] as $table => $fieldList) {
357 $fields = t3lib_div::trimExplode(',', $fieldList);
358 foreach ($fields as $field) {
359 if (is_array($searchFields[$table])) {
360 if (array_search($field, $searchFields[$table]) === FALSE) {
361 $searchFields[$table][] = $field;
362 }
363 }
364 }
365 }
366 return $searchFields;
367 }
368
369 /**
370 * Get the list of linkTypes to parse in modTSconfig.
371 *
372 * @param array $modTS: mod.linkvalidator TSconfig array.
373 * @return array $linkTypes: list of link types.
374 */
375 protected function getLinkTypes($modTS) {
376 $types = t3lib_div::trimExplode(',', $modTS['linktypes'], 1);
377 if (is_array($linkTypes)) {
378 if (!empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])
379 && is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'])) {
380 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['linkvalidator']['checkLinks'] as $type => $value) {
381 if (in_array($type, $linkTypes)) {
382 $linkTypes[$type] = 1;
383 }
384 }
385 }
386 }
387 return $linkTypes;
388 }
389
390 /**
391 * Build and send warning email when new broken links were found.
392 *
393 * @param string $pageSections: Content of page section
394 * @param string $modTS: TSconfig array
395 * @return bool TRUE if mail was sent, FALSE if or not
396 */
397 protected function reportEmail($pageSections, $modTS) {
398 $content = t3lib_parsehtml::substituteSubpart($this->templateMail, '###PAGE_SECTION###', $pageSections);
399 /** @var array $markerArray */
400 $markerArray = array();
401 /** @var array $validEmailList */
402 $validEmailList = array();
403 /** @var boolean $sendEmail */
404 $sendEmail = TRUE;
405
406 $markerArray['totalBrokenLink'] = $this->totalBrokenLink;
407 $markerArray['totalBrokenLink_old'] = $this->oldTotalBrokenLink;
408 $content = t3lib_parsehtml::substituteMarkerArray($content, $markerArray, '###|###', TRUE, TRUE);
409
410 /** @var t3lib_mail_Message $mail */
411 $mail = t3lib_div::makeInstance('t3lib_mail_Message');
412 if (t3lib_div::validEmail($modTS['mail.']['fromemail'])) {
413 $mail->setFrom(array($modTS['mail.']['fromemail'] => $modTS['mail.']['fromname']));
414 } else {
415 throw new Exception(
416 $GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidFromEmail'),
417 '1295476760'
418 );
419 }
420 if(t3lib_div::validEmail($modTS['mail.']['replytoemail'])) {
421 $mail->setReplyTo(array($modTS['mail.']['replytoemail'] => $modTS['mail.']['replytoname']));
422 }
423
424 if(!empty($modTS['mail.']['subject'])) {
425 $mail->setSubject($modTS['mail.']['subject']);
426 } else {
427 throw new Exception(
428 $GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.noSubject'),
429 '1295476808'
430 );
431 }
432 if (!empty($this->email)) {
433 $emailList = t3lib_div::trimExplode(',', $this->email);
434 foreach ($emailList as $emailAdd) {
435 if (!t3lib_div::validEmail($emailAdd)) {
436 throw new Exception(
437 $GLOBALS['LANG']->sL('LLL:EXT:linkvalidator/locallang.xml:tasks.error.invalidToEmail'),
438 '1295476821'
439 );
440 } else {
441 $validEmailList[] = $emailAdd;
442 }
443 }
444 }
445 if (is_array($validEmailList) && !empty($validEmailList)) {
446 $mail->setTo($this->email);
447 } else {
448 $sendEmail = FALSE;
449 }
450
451 if($sendEmail) {
452 $mail->setBody($content,'text/html');
453 $mail->send();
454 }
455
456 return $sendEmail;
457 }
458
459
460 /**
461 * Build the mail content.
462 *
463 * @param int $curPage: id of the current page
464 * @param string $pageList: list of pages id
465 * @param array $markerArray: array of markers
466 * @param array $oldBrokenLink: markerarray with the number of link found
467 * @return string Content of the mail
468 */
469 protected function buildMail($curPage, $pageList, $markerArray, $oldBrokenLink) {
470 $pageSectionHTML = t3lib_parsehtml::getSubpart($this->templateMail, '###PAGE_SECTION###');
471
472 if (is_array($markerArray)) {
473 foreach ($markerArray as $markerKey => $markerValue) {
474 if (empty($oldBrokenLink[$markerKey])) {
475 $oldBrokenLink[$markerKey] = 0;
476 }
477 if ($markerValue != $oldBrokenLink[$markerKey]) {
478 $this->dif = TRUE;
479 }
480 $markerArray[$markerKey . '_old'] = $oldBrokenLink[$markerKey];
481 }
482 }
483 $markerArray['title'] = t3lib_BEfunc::getRecordTitle('pages', t3lib_BEfunc::getRecord('pages', $curPage));
484
485 $content = '';
486 if ($markerArray['brokenlinkCount'] > 0) {
487 $content = t3lib_parsehtml::substituteMarkerArray($pageSectionHTML, $markerArray, '###|###', TRUE, TRUE);
488 }
489 return $content;
490 }
491
492
493 /**
494 * Simulate cli call with setting the required options to the $_SERVER['argv']
495 *
496 * @return void
497 * @access protected
498 */
499 protected function setCliArguments() {
500 $_SERVER['argv'] = array(
501 $_SERVER['argv'][0],
502 'tx_link_scheduler_link',
503 '0',
504 '-ss',
505 '--sleepTime',
506 $this->sleepTime,
507 '--sleepAfterFinish',
508 $this->sleepAfterFinish,
509 '--countInARun',
510 $this->countInARun
511 );
512 }
513 }
514
515 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/linkvalidator/classes/tasks/class.tx_linkvalidator_tasks_validator.php'])) {
516 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/linkvalidator/classes/tasks/class.tx_linkvalidator_tasks_validator.php']);
517 }
518 ?>