ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilLinkChecker.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
13{
14 var $db = null;
15 var $log_messages = array();
16 var $invalid_links = array();
17
18 var $validate_all = true;
19 var $mail_status = false;
20 var $page_id = 0;
21
22
23 public function __construct($db,$a_validate_all = true)
24 {
25 global $ilDB;
26
27 define('DEBUG',1);
28 define('SOCKET_TIMEOUT',5);
29 define('MAX_REDIRECTS',5);
30
31 $this->db = $db;
32
33 // SET GLOBAL DB HANDLER FOR STATIC METHODS OTHER CLASSES
34 $ilDB = $db;
35
36 $this->validate_all = $a_validate_all;
37 }
38
39 function setCheckPeriod($a_period)
40 {
41 $this->period = $a_period;
42 }
43 function getCheckPeriod()
44 {
45 return $this->period;
46 }
47
48 function setMailStatus($a_status)
49 {
50 $this->mail_status = (bool) $a_status;
51 }
52 function getMailStatus()
53 {
54 return (bool) $this->mail_status;
55 }
56
58 {
59 $this->type = $a_type;
60 }
61 function __getType()
62 {
63 return $this->type;
64 }
65
66 function setObjId($a_page_id)
67 {
68 return $this->page_id = $a_page_id;
69 }
70 function getObjId()
71 {
72 return $this->page_id;
73 }
74
75 function getValidateAll()
76 {
77 return $this->validate_all ? true : false;
78 }
79
80 function getLogMessages()
81 {
82 return $this->log_messages ? $this->log_messages : array();
83 }
84
85 function getInvalidLinks()
86 {
87 return $this->invalid_links ? $this->invalid_links : array();
88 }
89
91 {
92 global $ilDB;
93
94 $query = "SELECT * FROM link_check ".
95 "WHERE obj_id = ".$ilDB->quote($this->getObjId(),'integer')." ";
96
97 $res = $this->db->query($query);
98 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
99 {
100 $invalid[] = array('page_id' => $row->page_id,
101 'url' => $row->url);
102 }
103
104 return $invalid ? $invalid : array();
105 }
106
108 {
109 global $ilDB;
110
111 if($this->getValidateAll())
112 {
113 $query = "SELECT MAX(last_check) last_check FROM link_check ";
114 }
115 else
116 {
117 $query = "SELECT MAX(last_check) last_check FROM link_check ".
118 "WHERE obj_id = ".$ilDB->quote($this->getObjId(),'integer')." ";
119 }
120 $res = $ilDB->query($query);
121 $row = $ilDB->fetchObject($res);
122
123 return $row->last_check ? $row->last_check : 0;
124 }
125
127 {
128 $pages = array();
129
130 $this->__setType('webr');
131 $this->__clearLogMessages();
132 $this->__clearInvalidLinks();
133 $this->__appendLogMessage('LinkChecker: Start checkLinks()');
134
135 if(count($invalid = $this->__validateLinks($this->__getWebResourceLinks())))
136 {
137 foreach($invalid as $invalid_item)
138 {
139 $this->__appendLogMessage('LinkChecker: found invalid link: '.$invalid_item['complete']);
140 $this->__appendInvalidLink($invalid_item);
141 }
142 }
143
144 $this->__appendLogMessage('LinkChecker: End checkLinks()');
145 $this->__saveInDB();
146
147 $this->__sendMail();
148
149 return $this->getInvalidLinks();
150 }
151
152 function checkLinks()
153 {
154 global $ilDB;
155
156 $pages = array();
157
158 $this->__setType('lm');
159 $this->__clearLogMessages();
160 $this->__clearInvalidLinks();
161 $this->__appendLogMessage('LinkChecker: Start checkLinks()');
162
163 if(!$this->getValidateAll() and !$this->getObjId())
164 {
165 echo "ilLinkChecker::checkLinks() No Page id given";
166
167 return false;
168 }
169 elseif(!$this->getValidateAll() and $this->getObjId())
170 {
171 $query = "SELECT * FROM page_object ".
172 "WHERE parent_id = ".$ilDB->quote($this->getObjId())." ".
173 "AND parent_type = 'lm'";
174
175 $res = $this->db->query($query);
176 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
177 {
178 $pages[] = array('page_id' => $row->page_id,
179 'content' => $row->content,
180 'type' => $row->parent_type);
181 }
182 }
183 elseif($this->getValidateAll())
184 {
185 $query = "SELECT * FROM page_object ".
186 "WHERE parent_type = 'lm'";
187
188 $res = $this->db->query($query);
189 while($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT))
190 {
191 $pages[] = array('page_id' => $row->page_id,
192 'content' => $row->content,
193 'type' => $row->parent_type);
194 }
195 }
196
197 // VALIDATE
198 foreach($pages as $page)
199 {
200 if(count($invalid = $this->__validateLinks($this->__getLinks($page))))
201 {
202 foreach($invalid as $invalid_item)
203 {
204 $this->__appendLogMessage('LinkChecker: found invalid link: '.$invalid_item['complete']);
205 $this->__appendInvalidLink($invalid_item);
206 }
207 }
208 }
209
210 $this->__appendLogMessage('LinkChecker: End checkLinks()');
211 $this->__saveInDB();
212
213 $this->__sendMail();
214
215 return $this->getInvalidLinks();
216 }
217
218 // PRIVATE
219 function __txt($language,$key,$module = 'common')
220 {
221 global $ilDB;
222
223 include_once './Services/Language/classes/class.ilLanguage.php';
224 return ilLanguage::_lookupEntry($language, $module, $key);
225 }
226
227 function __fetchUserData($a_usr_id)
228 {
229 global $ilDB;
230
231 $r = $this->db->query("SELECT email FROM usr_data WHERE usr_id = ".$ilDB->quote($a_usr_id));
232
234
235 $data['email'] = $row->email;
236
237 $set = $ilDB->query("SELECT * FROM usr_pref ".
238 "WHERE usr_id = ".$ilDB->quote($a_usr_id, "integer")." ".
239 "AND keyword = ".$ilDB->quote('language', "text"));
240
241 $row = $ilDB->fetchObject($set);
242
243 $data['lang'] = $row->value;
244
245 return $data;
246 }
247
248 function __getTitle($a_lm_obj_id)
249 {
250 global $ilDB;
251
252 $r = $this->db->query("SELECT title FROM object_data ".
253 "WHERE obj_id = ".$ilDB->quote($a_lm_obj_id ,'integer')." ");
254
256
257 return $row->title;
258 }
259
260 function __sendMail()
261 {
262 global $ilUser;
263
264
265 if(!count($notify = $this->__getNotifyLinks()))
266 {
267 // Nothing to do
268 return true;
269 }
270 if(!$this->getMailStatus())
271 {
272 return true;
273 }
274
275 include_once './Services/LinkChecker/classes/class.ilLinkCheckNotify.php';
276 $body = "";
277 $obj_name = "";
278
279 foreach(ilLinkCheckNotify::_getAllNotifiers($this->db) as $usr_id => $obj_ids)
280 {
281 if(!is_object($tmp_user =& ilObjectFactory::getInstanceByObjId($usr_id,false)))
282 {
283 $this->__appendLogMessage('LinkChecker: Cannot find user with id: '.$usr_id);
284 continue;
285 }
286
287 $counter = 0;
288 foreach($obj_ids as $obj_id)
289 {
290 if(!isset($notify[$obj_id]))
291 {
292 continue;
293 }
294 ++$counter;
295
296 switch($this->__getType())
297 {
298 case 'webr':
299 $obj_name = $this->__txt($tmp_user->getLanguage(),'obj_webr');
300 break;
301 case 'lm':
302 default:
303 $obj_name = $this->__txt($tmp_user->getLanguage(),'lo');
304 break;
305 }
306 $body .= $obj_name.': '.$this->__getTitle($obj_id)."\r\n";
307 $body .= $this->__txt($tmp_user->getLanguage(),'link_check_perma_link', "mail"). ": " .
308 $this->createPermanentLink($obj_id, $usr_id, $this->__getType())." \r\n";
309 $body .= $this->__txt($tmp_user->getLanguage(),"link_check_affected_links", "mail"). ":\r\n";
310
311 // Print all invalid
312 foreach($notify[$obj_id] as $data)
313 {
314 $body .= $data['url']."\r\n";
315 }
316 $body .= "\r\n";
317 }
318 if($counter)
319 {
320 include_once "./Services/Notification/classes/class.ilSystemNotification.php";
321 $ntf = new ilSystemNotification();
322 $ntf->setLangModules(array("mail", "common"));
323 $ntf->setSubjectLangId("link_check_subject");
324 $ntf->setIntroductionLangId("link_check_introduction");
325 $ntf->setReasonLangId("link_check_reason");
326 $ntf->addAdditionalInfo("additional_info", $body,true);
327 $ntf->sendMail(array($tmp_user->getId()));
328
329 $this->__appendLogMessage('LinkChecker: Sent mail to '.$tmp_user->getEmail());
330 }
331 $body = "";
332 }
333 }
334
342 protected function createPermanentLink($a_obj_id, $a_usr_id, $a_obj_type)
343 {
344 global $ilAccess;
345 $ref_ids = ilObject::_getAllReferences($a_obj_id);
346 $ref_id = null;
347
348 foreach((array) $ref_ids as $id)
349 {
350 if($ilAccess->checkAccessOfUser($a_usr_id, "read", "", $id, $a_obj_type, $a_obj_id))
351 {
352 $ref_id = $id;
353 }
354 }
355
356 if($ref_id === null)
357 {
358 return false;
359 }
360
361 include_once './Services/Link/classes/class.ilLink.php';
362 return ilLink::_getLink($ref_id, $a_obj_type);
363 }
364
366 {
367 return $this->notify ? $this->notify : array();
368 }
369
370
372 {
373 $this->invalid_links = array();
374 }
375 function __appendInvalidLink($a_link)
376 {
377 $this->invalid_links[] = $a_link;
378 }
379
380
381 function __appendLogMessage($a_string)
382 {
383 $this->log_messages[] = $a_string;
384 }
386 {
387 return $this->log_messages = array();
388 }
389
390 function __getLinks($a_page)
391 {
392 $matches = array();
393
394 $pattern_complete = '/<ExtLink Href="([^"]*)">/';
395 if(preg_match_all($pattern_complete,$a_page['content'],$matches))
396 {
397 for($i = 0;$i < count($matches[0]); ++$i)
398 {
399 $url_data = @parse_url($matches[1][$i]);
400 // continue if mailto link
401 if($url_data['scheme'] == 'mailto')
402 {
403 continue;
404 }
405
406 // PUH, HTTP_REQUEST needs a beginning http://
407 if(!$url_data['scheme'])
408 {
409 $matches[1][$i] = 'http://'.$matches[1][$i];
410 }
411
412 $lm_id = $this->__getObjIdByPageId($a_page['page_id']);
413 $link[] = array('page_id' => $a_page['page_id'],
414 'obj_id' => $lm_id,
415 'type' => $a_page['type'],
416 'complete' => $matches[1][$i],
417 'scheme' => isset($url_data['scheme']) ? $url_data['scheme'] : 'http',
418 'host' => isset($url_data['host']) ? $url_data['host'] : $url_data['path']);
419 }
420 }
421
422 return $link ? $link : array();
423 }
424
426 {
427 global $objDefinition;
428
429 include_once 'Modules/WebResource/classes/class.ilLinkResourceItems.php';
430
431 $link_res_obj = new ilLinkResourceItems($this->getObjId());
432
433 foreach($check_links = $link_res_obj->getCheckItems($this->getCheckPeriod()) as $item_data)
434 {
435 // #10091 - internal
436 if(strpos($item_data['target'], '|'))
437 {
438 $parts = explode('|', $item_data['target']);
439 if(sizeof($parts) == 2 &&
440 is_numeric($parts[1]) &&
441 $objDefinition->isAllowedInRepository($parts[0]))
442 {
443 $link[] = array('page_id' => $item_data['link_id'],
444 'obj_id' => $this->getObjId(),
445 'type' => 'webr',
446 'complete' => $item_data['target'],
447 'scheme' => 'internal',
448 'obj_type' => $parts[0],
449 'ref_id' => $parts[1]);
450 continue;
451 }
452 }
453
454 // external
455 $url_data = @parse_url($item_data['target']);
456
457 // PUH, HTTP_REQUEST needs a beginning http://
458 if(!$url_data['scheme'])
459 {
460 $item_data['target'] = 'http://'.$item_data['target'];
461 }
462
463 $link[] = array('page_id' => $item_data['link_id'],
464 'obj_id' => $this->getObjId(),
465 'type' => 'webr',
466 'complete' => $item_data['target'],
467 'scheme' => isset($url_data['scheme']) ? $url_data['scheme'] : 'http',
468 'host' => isset($url_data['host']) ? $url_data['host'] : $url_data['path']);
469 }
470 return $link ? $link : array();
471 }
472
473
490 function __validateLinks($a_links)
491 {
492 global $tree;
493 include_once('./Services/Logging/classes/public/class.ilLoggerFactory.php');
494 include_once('./Services/WebServices/Curl/classes/class.ilCurlConnection.php');
496 {
497 $this->__appendLogMessage('LinkChecker: Pear HTTP_Request is not installed. Aborting');
498 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: Curl extension is not loeaded. Aborting');
499 return array();
500 }
501 $invalid = array();
502
503 foreach($a_links as $link)
504 {
505 // #10091 - internal
506 if($link['scheme'] == 'internal')
507 {
508 $obj_id = ilObject::_lookupObjId($link['ref_id']);
509 if(!$obj_id ||
510 ilObject::_lookupType($obj_id) != $link['obj_type'] ||
511 $tree->isDeleted($link['ref_id']))
512 {
513 $invalid[] = $link;
514 }
515 }
516 // external
517 else
518 {
519 if($link['scheme'] !== 'http' and $link['scheme'] !== 'https')
520 {
521 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: Unkown link sheme "' . $link['scheme'] . '". Continue check');
522 continue;
523 }
524
525 require_once './Services/Http/classes/class.ilProxySettings.php';
526 $http_code = 0;
527 $c_error_no = 0;
528 try
529 {
530 $curl = new ilCurlConnection($link['complete']);
531 $curl->init();
532
533 if(ilProxySettings::_getInstance()->isActive())
534 {
535 $curl->setOpt(CURLOPT_HTTPPROXYTUNNEL,true );
536 $curl->setOpt(CURLOPT_PROXY, ilProxySettings::_getInstance()->getHost());
537 $curl->setOpt(CURLOPT_PROXYPORT, ilProxySettings::_getInstance()->getPort());
538 }
539
540 $curl->setOpt( CURLOPT_HEADER, 1);
541 $curl->setOpt(CURLOPT_RETURNTRANSFER, 1);
542 $curl->setOpt(CURLOPT_CONNECTTIMEOUT ,SOCKET_TIMEOUT);
543 $curl->setOpt(CURLOPT_FOLLOWLOCATION, 1);
544 $curl->setOpt(CURLOPT_MAXREDIRS ,MAX_REDIRECTS);
545 $curl->exec();
546 $headers = $curl->getInfo();
547 $http_code = $headers['http_code'];
548 }
550 {
551 $c_error_no = $e->getCode();
552 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: No valid http code received. Curl error ('.$e->getCode().'): ' . $e->getMessage());
553 }
554
555 switch($http_code)
556 {
557 // EVERYTHING OK
558 case '200':
559 // In the moment 301 will be handled as ok
560 case '301':
561 case '302':
562 break;
563 default:
564 $link['http_status_code'] = $http_code;
565 if($http_code == 0 && $c_error_no != 0)
566 {
567 $link['curl_errno'] = $c_error_no;
568 }
569 $invalid[] = $link;
570 break;
571 }
572 }
573 }
574 return $invalid;
575 }
576
577 function __getObjIdByPageId($a_page_id)
578 {
579 $res = $this->db->query( "SELECT lm_id FROM lm_data ".
580 "WHERE obj_id = '".$a_page_id."'");
581
583
584 return $row->lm_id ? $row->lm_id : 0;
585 }
586
587 function __isInvalid($a_page_id, $a_url)
588 {
589 foreach($this->getInvalidLinks() as $link)
590 {
591 if($link['page_id'] == $a_page_id and
592 substr($link['complete'],0,255) == $a_url)
593 {
594 return true;
595 }
596 }
597 return false;
598 }
599
600 function __saveInDB()
601 {
602 global $ilDB;
603
604 if($this->getMailStatus())
605 {
606 $this->__checkNotify();
607 }
608 $this->__clearDBData();
609
610
611 foreach($this->getInvalidLinks() as $link)
612 {
613 $id = $ilDB->nextId('link_check');
614
615 $query = "INSERT INTO link_check (id, obj_id,page_id,url,parent_type,http_status_code,last_check) ".
616 "VALUES ( ".
617 $ilDB->quote($id, "integer").",".
618 $ilDB->quote($link['obj_id'],'integer').", ".
619 $ilDB->quote($link['page_id'],'integer').", ".
620 $ilDB->quote(substr($link['complete'],0,255),'text').", ".
621 $ilDB->quote($link['type'],'text').", ".
622 $ilDB->quote($link['http_status_code'] ? $link['http_status_code'] : 0,'integer').", ".
623 $ilDB->quote(time(),'integer')." ".
624 ")";
625 $res = $ilDB->manipulate($query);
626 }
627 }
628
629 function __checkNotify()
630 {
631 global $ilDB;
632
633 foreach($this->getInvalidLinks() as $link)
634 {
635 $query = "SELECT * FROM link_check ".
636 "WHERE page_id = ".$ilDB->quote($link['page_id'],'integer')." ".
637 "AND url = ".$ilDB->quote(substr($link['complete'],0,255),'text')." ";
638 $res = $ilDB->query($query);
639
640 if(!$res->numRows())
641 {
642 $this->notify[$link["obj_id"]][] = array('page_id' => $link['page_id'],
643 'url' => $link['complete']);
644 }
645 }
646 }
647
648
649 function __clearDBData()
650 {
651 global $ilDB;
652
653 if($this->getValidateAll())
654 {
655 $query = "DELETE FROM link_check";
656 }
657 else
658 {
659 $query = "DELETE FROM link_check ".
660 "WHERE obj_id = ".$ilDB->quote($this->getObjId(),'integer');
661 }
662 $res = $ilDB->manipulate($query);
663
664 return true;
665 }
666}
667?>
An exception for terminatinating execution or to throw for unit testing.
static _isCurlExtensionLoaded()
Check if curl extension is loaded.
static _lookupEntry($a_lang_key, $a_mod, $a_id)
class for checking external links in page objects Normally used in Cron jobs, but should be extensibl...
__fetchUserData($a_usr_id)
setCheckPeriod($a_period)
setObjId($a_page_id)
__txt($language, $key, $module='common')
__getObjIdByPageId($a_page_id)
__isInvalid($a_page_id, $a_url)
createPermanentLink($a_obj_id, $a_usr_id, $a_obj_type)
creates a permanent link
__getTitle($a_lm_obj_id)
__construct($db, $a_validate_all=true)
__validateLinks($a_links)
$a_links Format: Array ( [1] => Array ( ['scheme'] => intern/http/https, ['ref_id'] => ILIAS ref ID,...
setMailStatus($a_status)
__appendInvalidLink($a_link)
__appendLogMessage($a_string)
Class ilObjLinkResourceGUI.
static getLogger($a_component_id)
Get component logger.
static getInstanceByObjId($a_obj_id, $stop_on_error=true)
get an instance of an Ilias object by object id
static _lookupObjId($a_id)
static _getAllReferences($a_id)
get all reference ids of object
static _lookupType($a_id, $a_reference=false)
lookup object type
static _getInstance()
Getter for unique instance.
Wrapper classes for system notifications.
$counter
$r
Definition: example_031.php:79
$ref_id
Definition: sahs_server.php:39
global $ilDB
$ilUser
Definition: imgupload.php:18
$a_type
Definition: workflow.php:93