ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 public $db = null;
15 public $log_messages = array();
16 public $invalid_links = array();
17
18 public $validate_all = true;
19 public $mail_status = false;
20 public $page_id = 0;
21
22
23 public function __construct($db, $a_validate_all = true)
24 {
25 global $DIC;
26
27 $ilDB = $DIC['ilDB'];
28
29 define('DEBUG', 1);
30 define('SOCKET_TIMEOUT', 5);
31 define('MAX_REDIRECTS', 5);
32
33 $this->db = $db;
34
35 // SET GLOBAL DB HANDLER FOR STATIC METHODS OTHER CLASSES
36 $ilDB = $db;
37
38 $this->validate_all = $a_validate_all;
39 }
40
41 public function setCheckPeriod($a_period)
42 {
43 $this->period = $a_period;
44 }
45 public function getCheckPeriod()
46 {
47 return $this->period;
48 }
49
50 public function setMailStatus($a_status)
51 {
52 $this->mail_status = (bool) $a_status;
53 }
54 public function getMailStatus()
55 {
56 return (bool) $this->mail_status;
57 }
58
59 public function __setType($a_type)
60 {
61 $this->type = $a_type;
62 }
63 public function __getType()
64 {
65 return $this->type;
66 }
67
68 public function setObjId($a_page_id)
69 {
70 return $this->page_id = $a_page_id;
71 }
72 public function getObjId()
73 {
74 return $this->page_id;
75 }
76
77 public function getValidateAll()
78 {
79 return $this->validate_all ? true : false;
80 }
81
82 public function getLogMessages()
83 {
84 return $this->log_messages ? $this->log_messages : array();
85 }
86
87 public function getInvalidLinks()
88 {
89 return $this->invalid_links ? $this->invalid_links : array();
90 }
91
92 public function getInvalidLinksFromDB()
93 {
94 global $DIC;
95
96 $ilDB = $DIC['ilDB'];
97
98 $query = "SELECT * FROM link_check " .
99 "WHERE obj_id = " . $ilDB->quote($this->getObjId(), 'integer') . " ";
100
101 $res = $this->db->query($query);
102 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
103 $invalid[] = array('page_id' => $row->page_id,
104 'url' => $row->url);
105 }
106
107 return $invalid ? $invalid : array();
108 }
109
110 public function getLastCheckTimestamp()
111 {
112 global $DIC;
113
114 $ilDB = $DIC['ilDB'];
115
116 if ($this->getValidateAll()) {
117 $query = "SELECT MAX(last_check) last_check FROM link_check ";
118 } else {
119 $query = "SELECT MAX(last_check) last_check FROM link_check " .
120 "WHERE obj_id = " . $ilDB->quote($this->getObjId(), 'integer') . " ";
121 }
122 $res = $ilDB->query($query);
123 $row = $ilDB->fetchObject($res);
124
125 return $row->last_check ? $row->last_check : 0;
126 }
127
128 public function checkWebResourceLinks()
129 {
130 $pages = array();
131
132 $this->__setType('webr');
133 $this->__clearLogMessages();
134 $this->__clearInvalidLinks();
135 $this->__appendLogMessage('LinkChecker: Start checkLinks()');
136
137 if (count($invalid = $this->__validateLinks($this->__getWebResourceLinks()))) {
138 foreach ($invalid as $invalid_item) {
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 public function checkLinks()
153 {
154 global $DIC;
155
156 $ilDB = $DIC['ilDB'];
157
158 $pages = array();
159
160 $this->__setType('lm');
161 $this->__clearLogMessages();
162 $this->__clearInvalidLinks();
163 $this->__appendLogMessage('LinkChecker: Start checkLinks()');
164
165 if (!$this->getValidateAll() and !$this->getObjId()) {
166 echo "ilLinkChecker::checkLinks() No Page id given";
167
168 return false;
169 } elseif (!$this->getValidateAll() and $this->getObjId()) {
170 $query = "SELECT * FROM page_object " .
171 "WHERE parent_id = " . $ilDB->quote($this->getObjId()) . " " .
172 "AND parent_type = 'lm'";
173
174 $res = $this->db->query($query);
175 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
176 $pages[] = array('page_id' => $row->page_id,
177 'content' => $row->content,
178 'type' => $row->parent_type);
179 }
180 } elseif ($this->getValidateAll()) {
181 $query = "SELECT * FROM page_object " .
182 "WHERE parent_type = 'lm'";
183
184 $res = $this->db->query($query);
185 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
186 $pages[] = array('page_id' => $row->page_id,
187 'content' => $row->content,
188 'type' => $row->parent_type);
189 }
190 }
191
192 // VALIDATE
193 foreach ($pages as $page) {
194 if (count($invalid = $this->__validateLinks($this->__getLinks($page)))) {
195 foreach ($invalid as $invalid_item) {
196 $this->__appendLogMessage('LinkChecker: found invalid link: ' . $invalid_item['complete']);
197 $this->__appendInvalidLink($invalid_item);
198 }
199 }
200 }
201
202 $this->__appendLogMessage('LinkChecker: End checkLinks()');
203 $this->__saveInDB();
204
205 $this->__sendMail();
206
207 return $this->getInvalidLinks();
208 }
209
210 // PRIVATE
211 public function __txt($language, $key, $module = 'common')
212 {
214 }
215
216 public function __fetchUserData($a_usr_id)
217 {
218 global $DIC;
219
220 $ilDB = $DIC['ilDB'];
221
222 $r = $this->db->query("SELECT email FROM usr_data WHERE usr_id = " . $ilDB->quote($a_usr_id));
223
225
226 $data['email'] = $row->email;
227
228 $set = $ilDB->query("SELECT * FROM usr_pref " .
229 "WHERE usr_id = " . $ilDB->quote($a_usr_id, "integer") . " " .
230 "AND keyword = " . $ilDB->quote('language', "text"));
231
232 $row = $ilDB->fetchObject($set);
233
234 $data['lang'] = $row->value;
235
236 return $data;
237 }
238
239 public function __getTitle($a_lm_obj_id)
240 {
241 global $DIC;
242
243 $ilDB = $DIC['ilDB'];
244
245 $r = $this->db->query("SELECT title FROM object_data " .
246 "WHERE obj_id = " . $ilDB->quote($a_lm_obj_id, 'integer') . " ");
247
249
250 return $row->title;
251 }
252
253 public function __sendMail()
254 {
255 global $DIC;
256
257 $ilUser = $DIC['ilUser'];
258
259
260 if (!count($notify = $this->__getNotifyLinks())) {
261 // Nothing to do
262 return true;
263 }
264 if (!$this->getMailStatus()) {
265 return true;
266 }
267
268
269 $body = "";
270 $obj_name = "";
271
272 foreach (ilLinkCheckNotify::_getAllNotifiers($this->db) as $usr_id => $obj_ids) {
273 if (!is_object($tmp_user = &ilObjectFactory::getInstanceByObjId($usr_id, false))) {
274 $this->__appendLogMessage('LinkChecker: Cannot find user with id: ' . $usr_id);
275 continue;
276 }
277
278 $counter = 0;
279 foreach ($obj_ids as $obj_id) {
280 if (!isset($notify[$obj_id])) {
281 continue;
282 }
283 ++$counter;
284
285 switch ($this->__getType()) {
286 case 'webr':
287 $obj_name = $this->__txt($tmp_user->getLanguage(), 'obj_webr');
288 break;
289 case 'lm':
290 default:
291 $obj_name = $this->__txt($tmp_user->getLanguage(), 'lo');
292 break;
293 }
294 $body .= $obj_name . ': ' . $this->__getTitle($obj_id) . "\r\n";
295 $body .= $this->__txt($tmp_user->getLanguage(), 'link_check_perma_link', "mail") . ": " .
296 $this->createPermanentLink($obj_id, $usr_id, $this->__getType()) . " \r\n";
297 $body .= $this->__txt($tmp_user->getLanguage(), "link_check_affected_links", "mail") . ":\r\n";
298
299 // Print all invalid
300 foreach ($notify[$obj_id] as $data) {
301 $body .= $data['url'] . "\r\n";
302 }
303 $body .= "\r\n";
304 }
305 if ($counter) {
306 $ntf = new ilSystemNotification();
307 $ntf->setLangModules(array("mail", "common"));
308 $ntf->setSubjectLangId("link_check_subject");
309 $ntf->setIntroductionLangId("link_check_introduction");
310 $ntf->setReasonLangId("link_check_reason");
311 $ntf->addAdditionalInfo("additional_info", $body, true);
312 $ntf->sendMail(array($tmp_user->getId()));
313
314 $this->__appendLogMessage('LinkChecker: Sent mail to ' . $tmp_user->getEmail());
315 }
316 $body = "";
317 }
318 }
319
327 protected function createPermanentLink($a_obj_id, $a_usr_id, $a_obj_type)
328 {
329 global $DIC;
330
331 $ilAccess = $DIC['ilAccess'];
332 $ref_ids = ilObject::_getAllReferences($a_obj_id);
333 $ref_id = null;
334
335 foreach ((array) $ref_ids as $id) {
336 if ($ilAccess->checkAccessOfUser($a_usr_id, "read", "", $id, $a_obj_type, $a_obj_id)) {
337 $ref_id = $id;
338 }
339 }
340
341 if ($ref_id === null) {
342 return false;
343 }
344
345
346 return ilLink::_getLink($ref_id, $a_obj_type);
347 }
348
349 public function __getNotifyLinks()
350 {
351 return $this->notify ? $this->notify : array();
352 }
353
354
355 public function __clearInvalidLinks()
356 {
357 $this->invalid_links = array();
358 }
359 public function __appendInvalidLink($a_link)
360 {
361 $this->invalid_links[] = $a_link;
362 }
363
364
365 public function __appendLogMessage($a_string)
366 {
367 $this->log_messages[] = $a_string;
368 }
369 public function __clearLogMessages()
370 {
371 return $this->log_messages = array();
372 }
373
374 public function __getLinks($a_page)
375 {
376 $matches = array();
377
378 $pattern_complete = '/<ExtLink Href="([^"]*)">/';
379 if (preg_match_all($pattern_complete, $a_page['content'], $matches)) {
380 for ($i = 0;$i < count($matches[0]); ++$i) {
381 $url_data = @parse_url($matches[1][$i]);
382 // continue if mailto link
383 if ($url_data['scheme'] == 'mailto') {
384 continue;
385 }
386
387 // PUH, HTTP_REQUEST needs a beginning http://
388 if (!$url_data['scheme']) {
389 $matches[1][$i] = 'http://' . $matches[1][$i];
390 }
391
392 $lm_id = $this->__getObjIdByPageId($a_page['page_id']);
393 $link[] = array('page_id' => $a_page['page_id'],
394 'obj_id' => $lm_id,
395 'type' => $a_page['type'],
396 'complete' => $matches[1][$i],
397 'scheme' => isset($url_data['scheme']) ? $url_data['scheme'] : 'http',
398 'host' => isset($url_data['host']) ? $url_data['host'] : $url_data['path']);
399 }
400 }
401
402 return $link ? $link : array();
403 }
404
405 public function __getWebResourceLinks()
406 {
407 global $DIC;
408
409 $objDefinition = $DIC['objDefinition'];
410
411
412
413 $link_res_obj = new ilLinkResourceItems($this->getObjId());
414
415 foreach ($check_links = $link_res_obj->getCheckItems($this->getCheckPeriod()) as $item_data) {
416 // #10091 - internal
417 if (strpos($item_data['target'], '|')) {
418 $parts = explode('|', $item_data['target']);
419 if (sizeof($parts) == 2 &&
420 is_numeric($parts[1]) &&
421 $objDefinition->isAllowedInRepository($parts[0])) {
422 $link[] = array('page_id' => $item_data['link_id'],
423 'obj_id' => $this->getObjId(),
424 'type' => 'webr',
425 'complete' => $item_data['target'],
426 'scheme' => 'internal',
427 'obj_type' => $parts[0],
428 'ref_id' => $parts[1]);
429 continue;
430 }
431 }
432
433 // external
434 $url_data = @parse_url($item_data['target']);
435
436 // PUH, HTTP_REQUEST needs a beginning http://
437 if (!$url_data['scheme']) {
438 $item_data['target'] = 'http://' . $item_data['target'];
439 }
440
441 $link[] = array('page_id' => $item_data['link_id'],
442 'obj_id' => $this->getObjId(),
443 'type' => 'webr',
444 'complete' => $item_data['target'],
445 'scheme' => isset($url_data['scheme']) ? $url_data['scheme'] : 'http',
446 'host' => isset($url_data['host']) ? $url_data['host'] : $url_data['path']);
447 }
448 return $link ? $link : array();
449 }
450
451
468 public function __validateLinks($a_links)
469 {
470 global $DIC;
471
472 $tree = $DIC['tree'];
474 $this->__appendLogMessage('LinkChecker: Pear HTTP_Request is not installed. Aborting');
475 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: Curl extension is not loeaded. Aborting');
476 return array();
477 }
478 $invalid = array();
479
480 foreach ($a_links as $link) {
481 // #10091 - internal
482 if ($link['scheme'] == 'internal') {
483 $obj_id = ilObject::_lookupObjId($link['ref_id']);
484 if (!$obj_id ||
485 ilObject::_lookupType($obj_id) != $link['obj_type'] ||
486 $tree->isDeleted($link['ref_id'])) {
487 $invalid[] = $link;
488 }
489 }
490 // external
491 else {
492 //ilLoggerFactory::getLogger('lchk')->debug('Check: '.$link['complete']);
493
494 if ($link['scheme'] !== 'http' and $link['scheme'] !== 'https') {
495 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: Unkown link sheme "' . $link['scheme'] . '". Continue check');
496 continue;
497 }
498
499 $curl = null;
500 $http_code = 0;
501 $c_error_no = 0;
502 try {
503 $curl = new ilCurlConnection($link['complete']);
504 $curl->init();
505
506 if (ilProxySettings::_getInstance()->isActive()) {
507 $curl->setOpt(CURLOPT_HTTPPROXYTUNNEL, true);
508 $curl->setOpt(CURLOPT_PROXY, ilProxySettings::_getInstance()->getHost());
509 $curl->setOpt(CURLOPT_PROXYPORT, ilProxySettings::_getInstance()->getPort());
510 }
511
512 $curl->setOpt(CURLOPT_HEADER, 1);
513 $curl->setOpt(CURLOPT_RETURNTRANSFER, 1);
514 $curl->setOpt(CURLOPT_CONNECTTIMEOUT, SOCKET_TIMEOUT);
515 $curl->setOpt(CURLOPT_FOLLOWLOCATION, 1);
516 $curl->setOpt(CURLOPT_MAXREDIRS, MAX_REDIRECTS);
517 $curl->exec();
518 $headers = $curl->getInfo();
519 $http_code = $headers['http_code'];
520 } catch (ilCurlConnectionException $e) {
521 $c_error_no = $e->getCode();
522 ilLoggerFactory::getLogger('lchk')->error('LinkChecker: No valid http code received. Curl error (' . $e->getCode() . '): ' . $e->getMessage());
523 } finally {
524 if ($curl != null) {
525 $curl->close();
526 }
527 }
528
529 switch ($http_code) {
530 // EVERYTHING OK
531 case '200':
532 // In the moment 301 will be handled as ok
533 case '301':
534 case '302':
535 break;
536 default:
537 $link['http_status_code'] = $http_code;
538 if ($http_code == 0 && $c_error_no != 0) {
539 $link['curl_errno'] = $c_error_no;
540 }
541 $invalid[] = $link;
542 break;
543 }
544 }
545 }
546 return $invalid;
547 }
548
549 public function __getObjIdByPageId($a_page_id)
550 {
551 $res = $this->db->query("SELECT lm_id FROM lm_data " .
552 "WHERE obj_id = '" . $a_page_id . "'");
553
555
556 return $row->lm_id ? $row->lm_id : 0;
557 }
558
559 public function __isInvalid($a_page_id, $a_url)
560 {
561 foreach ($this->getInvalidLinks() as $link) {
562 if ($link['page_id'] == $a_page_id and
563 substr($link['complete'], 0, 255) == $a_url) {
564 return true;
565 }
566 }
567 return false;
568 }
569
570 public function __saveInDB()
571 {
572 global $DIC;
573
574 $ilDB = $DIC['ilDB'];
575
576 if ($this->getMailStatus()) {
577 $this->__checkNotify();
578 }
579 $this->__clearDBData();
580
581
582 foreach ($this->getInvalidLinks() as $link) {
583 $id = $ilDB->nextId('link_check');
584
585 $query = "INSERT INTO link_check (id, obj_id,page_id,url,parent_type,http_status_code,last_check) " .
586 "VALUES ( " .
587 $ilDB->quote($id, "integer") . "," .
588 $ilDB->quote($link['obj_id'], 'integer') . ", " .
589 $ilDB->quote($link['page_id'], 'integer') . ", " .
590 $ilDB->quote(substr($link['complete'], 0, 255), 'text') . ", " .
591 $ilDB->quote($link['type'], 'text') . ", " .
592 $ilDB->quote($link['http_status_code'] ? $link['http_status_code'] : 0, 'integer') . ", " .
593 $ilDB->quote(time(), 'integer') . " " .
594 ")";
595 $res = $ilDB->manipulate($query);
596 }
597 }
598
599 public function __checkNotify()
600 {
601 global $DIC;
602
603 $ilDB = $DIC['ilDB'];
604
605 foreach ($this->getInvalidLinks() as $link) {
606 $query = "SELECT * FROM link_check " .
607 "WHERE page_id = " . $ilDB->quote($link['page_id'], 'integer') . " " .
608 "AND url = " . $ilDB->quote(substr($link['complete'], 0, 255), 'text') . " ";
609 $res = $ilDB->query($query);
610
611 if (!$res->numRows()) {
612 $this->notify[$link["obj_id"]][] = array('page_id' => $link['page_id'],
613 'url' => $link['complete']);
614 }
615 }
616 }
617
618
619 public function __clearDBData()
620 {
621 global $DIC;
622
623 $ilDB = $DIC['ilDB'];
624
625 if ($this->getValidateAll()) {
626 $query = "DELETE FROM link_check";
627 } else {
628 $query = "DELETE FROM link_check " .
629 "WHERE obj_id = " . $ilDB->quote($this->getObjId(), 'integer');
630 }
631 $res = $ilDB->manipulate($query);
632
633 return true;
634 }
635}
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.
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
$r
Definition: example_031.php:79
if(!array_key_exists('StateId', $_REQUEST)) $id
$row
if($modEnd===false) $module
Definition: module.php:59
$query
$type
global $DIC
Definition: saml.php:7
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18
$data
Definition: bench.php:6
$a_type
Definition: workflow.php:92