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 }
setCheckPeriod($a_period)
$type
__getTitle($a_lm_obj_id)
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
$counter
$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
Create styles array
The data for the language used.
static _lookupType($a_id, $a_reference=false)
lookup object type
__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.
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
__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)