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;
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 
57  function __setType($a_type)
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 
365  function __getNotifyLinks()
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  }
549  catch(ilCurlConnectionException $e)
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 ?>
setCheckPeriod($a_period)
__getTitle($a_lm_obj_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
$counter
$a_type
Definition: workflow.php:93
__appendLogMessage($a_string)
$r
Definition: example_031.php:79
__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
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
$ref_id
Definition: sahs_server.php:39
__getObjIdByPageId($a_page_id)
setObjId($a_page_id)
global $ilDB
Class ilObjLinkResourceGUI.
__appendInvalidLink($a_link)
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.
Wrapper classes for system notifications.
__fetchUserData($a_usr_id)