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