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 }
setCheckPeriod($a_period)
$type
__getTitle($a_lm_obj_id)
global $DIC
Definition: saml.php:7
if(!array_key_exists('StateId', $_REQUEST)) $id
class for checking external links in page objects Normally used in Cron jobs, but should be extensibl...
setMailStatus($a_status)
static _lookupEntry($a_lang_key, $a_mod, $a_id)
createPermanentLink($a_obj_id, $a_usr_id, $a_obj_type)
creates a permanent link
static _getAllReferences($a_id)
get all reference ids of object
if($modEnd===false) $module
Definition: module.php:59
$a_type
Definition: workflow.php:92
__appendLogMessage($a_string)
$r
Definition: example_031.php:79
foreach($_POST as $key=> $value) $res
__isInvalid($a_page_id, $a_url)
__txt($language, $key, $module='common')
static _isCurlExtensionLoaded()
Check if curl extension is loaded.
static _lookupObjId($a_id)
__construct($db, $a_validate_all=true)
$ilUser
Definition: imgupload.php:18
$query
static getInstanceByObjId($a_obj_id, $stop_on_error=true)
get an instance of an Ilias object by object id
static _lookupType($a_id, $a_reference=false)
lookup object type
$row
__getObjIdByPageId($a_page_id)
setObjId($a_page_id)
global $ilDB
Class ilObjLinkResourceGUI.
__appendInvalidLink($a_link)
$i
Definition: disco.tpl.php:19
static getLogger($a_component_id)
Get component logger.
__validateLinks($a_links)
$a_links Format: Array ( [1] => Array ( [&#39;scheme&#39;] => intern/http/https, [&#39;ref_id&#39;] => ILIAS ref ID...
static _getInstance()
Getter for unique instance.
$key
Definition: croninfo.php:18
Wrapper classes for system notifications.
__fetchUserData($a_usr_id)
$data
Definition: bench.php:6