| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Global Search Engine for Moodle 4 * 5 * @package search 6 * @category core 7 * @subpackage search_engine 8 * @author Michael Champanis (mchampan) [cynnical@gmail.com], Valery Fremaux [valery.fremaux@club-internet.fr] > 1.8 9 * @date 2008/03/31 10 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 11 */ 12 13 /** 14 * includes and requires 15 */ 16 require_once("{$CFG->dirroot}/search/Zend/Search/Lucene.php"); 17 18 define('DEFAULT_POPUP_SETTINGS', "\"menubar=0,location=0,scrollbars,resizable,width=600,height=450\""); 19 20 /** 21 * a class that represents a single result record of the search engine 22 */ 23 class SearchResult { 24 public $url, 25 $title, 26 $doctype, 27 $author, 28 $score, 29 $number; 30 } 31 32 33 /** 34 * split this into Cache class and extend to SearchCache? 35 */ 36 class SearchCache { 37 private $mode, 38 $valid; 39 40 // foresees other caching locations 41 public function __construct($mode = 'session') { 42 $accepted_modes = array('session'); 43 44 if (in_array($mode, $accepted_modes)) { 45 $this->mode = $mode; 46 } else { 47 $this->mode = 'session'; 48 } //else 49 50 $this->valid = true; 51 } 52 53 /** 54 * returns the search cache status 55 * @return boolean 56 */ 57 public function can_cache() { 58 return $this->valid; 59 } 60 61 /** 62 * 63 * 64 */ 65 public function cache($id = false, $object = false) { 66 //see if there was a previous query 67 $last_term = $this->fetch('search_last_term'); 68 69 //if this query is different from the last, clear out the last one 70 if ($id != false and $last_term != $id) { 71 $this->clear($last_term); 72 } 73 74 //store the new query if id and object are passed in 75 if ($object and $id) { 76 $this->store('search_last_term', $id); 77 $this->store($id, $object); 78 return true; 79 //otherwise return the stored results 80 } else if ($id and $this->exists($id)) { 81 return $this->fetch($id); 82 } 83 } 84 85 /** 86 * do key exist in cache ? 87 * @param id the object key 88 * @return boolean 89 */ 90 private function exists($id) { 91 switch ($this->mode) { 92 case 'session' : 93 return isset($_SESSION[$id]); 94 } 95 } 96 97 /** 98 * clears a cached object in cache 99 * @param the object key to clear 100 * @return void 101 */ 102 private function clear($id) { 103 switch ($this->mode) { 104 case 'session' : 105 unset($_SESSION[$id]); 106 session_unregister($id); 107 return; 108 } 109 } 110 111 /** 112 * fetches a cached object 113 * @param id the object identifier 114 * @return the object cached 115 */ 116 private function fetch($id) { 117 switch ($this->mode) { 118 case 'session' : 119 return ($this->exists($id)) ? unserialize($_SESSION[$id]) : false; 120 } 121 } 122 123 /** 124 * put an object in cache 125 * @param id the key for that object 126 * @param object the object to cache as a serialized value 127 * @return void 128 */ 129 private function store($id, $object) { 130 switch ($this->mode) { 131 case 'session' : 132 $_SESSION[$id] = serialize($object); 133 return; 134 } 135 } 136 } 137 138 /** 139 * Represents a single query with results 140 * 141 */ 142 class SearchQuery { 143 private $index, 144 $term, 145 $pagenumber, 146 $cache, 147 $validquery, 148 $validindex, 149 $results, 150 $results_per_page, 151 $total_results; 152 153 /** 154 * constructor records query parameters 155 * 156 */ 157 public function __construct($term = '', $page = 1, $results_per_page = 10, $cache = false) { 158 global $CFG; 159 160 $this->term = $term; 161 $this->pagenumber = $page; 162 $this->cache = $cache; 163 $this->validquery = true; 164 $this->validindex = true; 165 $this->results_per_page = $results_per_page; 166 167 $index_path = SEARCH_INDEX_PATH; 168 169 try { 170 $this->index = new Zend_Search_Lucene($index_path, false); 171 } catch(Exception $e) { 172 $this->validindex = false; 173 return; 174 } 175 176 if (empty($this->term)) { 177 $this->validquery = false; 178 } else { 179 $this->set_query($this->term); 180 } 181 } 182 183 /** 184 * determines state of query object depending on query entry and 185 * tries to lauch search if all is OK 186 * @return void (this is only a state changing trigger). 187 */ 188 public function set_query($term = '') { 189 if (!empty($term)) { 190 $this->term = $term; 191 } 192 193 if (empty($this->term)) { 194 $this->validquery = false; 195 } else { 196 $this->validquery = true; 197 } 198 199 if ($this->validquery and $this->validindex) { 200 $this->results = $this->get_results(); 201 } else { 202 $this->results = array(); 203 } 204 } 205 206 /** 207 * accessor to the result table. 208 * @return an array of result records 209 */ 210 public function results() { 211 return $this->results; 212 } 213 214 /** 215 * do the effective collection of results 216 * @param boolean $all 217 * @uses USER 218 */ 219 private function process_results($all=false) { 220 global $USER; 221 222 $term = mb_convert_case($this->term, MB_CASE_LOWER, 'UTF-8'); 223 224 //experimental - return more results 225 $strip_arr = array('author:', 'title:', '+', '-', 'doctype:'); 226 $stripped_term = str_replace($strip_arr, '', $term); 227 228 $hits = $this->index->find($term." title:".$stripped_term." author:".$stripped_term); 229 //-- 230 231 $hitcount = count($hits); 232 $this->total_results = $hitcount; 233 234 if ($hitcount == 0) return array(); 235 236 $totalpages = ceil($hitcount/$this->results_per_page); 237 238 if (!$all) { 239 if ($hitcount < $this->results_per_page) { 240 $this->pagenumber = 1; 241 } else if ($this->pagenumber > $totalpages) { 242 $this->pagenumber = $totalpages; 243 } 244 245 $start = ($this->pagenumber - 1) * $this->results_per_page; 246 $end = $start + $this->results_per_page; 247 248 if ($end > $hitcount) { 249 $end = $hitcount; 250 } 251 } else { 252 $start = 0; 253 $end = $hitcount; 254 } 255 256 $resultdoc = new SearchResult(); 257 $resultdocs = array(); 258 259 for ($i = $start; $i < $end; $i++) { 260 $hit = $hits[$i]; 261 262 //check permissions on each result 263 if ($this->can_display($USER, $hit->docid, $hit->doctype, $hit->course_id, $hit->group_id, $hit->path, $hit->itemtype, $hit->context_id )) { 264 $resultdoc->number = $i; 265 $resultdoc->url = $hit->url; 266 $resultdoc->title = $hit->title; 267 $resultdoc->score = $hit->score; 268 $resultdoc->doctype = $hit->doctype; 269 $resultdoc->author = $hit->author; 270 271 //and store it 272 $resultdocs[] = clone($resultdoc); 273 } else { 274 // lowers total_results one unit 275 $this->total_results--; 276 } 277 } 278 279 return $resultdocs; 280 } 281 282 /** 283 * get results of a search query using a caching strategy if available 284 * @return the result documents as an array of search objects 285 */ 286 private function get_results() { 287 $cache = new SearchCache(); 288 289 if ($this->cache and $cache->can_cache()) { 290 if (!($resultdocs = $cache->cache($this->term))) { 291 $resultdocs = $this->process_results(); 292 //cache the results so we don't have to compute this on every page-load 293 $cache->cache($this->term, $resultdocs); 294 //print "Using new results."; 295 } else { 296 //There was something in the cache, so we're using that to save time 297 //print "Using cached results."; 298 } 299 } else { 300 //no caching :( 301 //print "Caching disabled!"; 302 $resultdocs = $this->process_results(); 303 } 304 return $resultdocs; 305 } 306 307 /** 308 * constructs the results paging links on results. 309 * @return string the results paging links 310 */ 311 public function page_numbers() { 312 $pages = $this->total_pages(); 313 $query = htmlentities($this->term); 314 $page = $this->pagenumber; 315 $next = get_string('next', 'search'); 316 $back = get_string('back', 'search'); 317 318 $ret = "<div align='center' id='search_page_links'>"; 319 320 //Back is disabled if we're on page 1 321 if ($page > 1) { 322 $ret .= "<a href='query.php?query_string={$query}&page=".($page-1)."'>< {$back}</a> "; 323 } else { 324 $ret .= "< {$back} "; 325 } 326 327 //don't <a href> the current page 328 for ($i = 1; $i <= $pages; $i++) { 329 if ($page == $i) { 330 $ret .= "($i) "; 331 } else { 332 $ret .= "<a href='query.php?query_string={$query}&page={$i}'>{$i}</a> "; 333 } 334 } 335 336 //Next disabled if we're on the last page 337 if ($page < $pages) { 338 $ret .= "<a href='query.php?query_string={$query}&page=".($page+1)."'>{$next} ></a> "; 339 } else { 340 $ret .= "{$next} > "; 341 } 342 343 $ret .= "</div>"; 344 345 //shorten really long page lists, to stop table distorting width-ways 346 if (strlen($ret) > 70) { 347 $start = 4; 348 $end = $page - 5; 349 $ret = preg_replace("/<a\D+\d+\D+>$start<\/a>.*?<a\D+\d+\D+>$end<\/a>/", '...', $ret); 350 351 $start = $page + 5; 352 $end = $pages - 3; 353 $ret = preg_replace("/<a\D+\d+\D+>$start<\/a>.*?<a\D+\d+\D+>$end<\/a>/", '...', $ret); 354 } 355 356 return $ret; 357 } 358 359 /** 360 * can the user see this result ? 361 * @param user a reference upon the user to be checked for access 362 * @param this_id the item identifier 363 * @param doctype the search document type. MAtches the module or block or 364 * extra search source definition 365 * @param course_id the course reference of the searched result 366 * @param group_id the group identity attached to the found resource 367 * @param path the path that routes to the local lib.php of the searched 368 * surrounding object fot that document 369 * @param item_type a subclassing information for complex module data models 370 * @uses CFG 371 * // TODO reorder parameters more consistently 372 */ 373 private function can_display(&$user, $this_id, $doctype, $course_id, $group_id, $path, $item_type, $context_id) { 374 global $CFG; 375 376 /** 377 * course related checks 378 */ 379 // admins can see everything, anyway. 380 if (isadmin()){ 381 return true; 382 } 383 384 // first check course compatibility against user : enrolled users to that course can see. 385 $myCourses = get_my_courses($user->id); 386 $unenroled = !in_array($course_id, array_keys($myCourses)); 387 388 // if guests are allowed, logged guest can see 389 $isallowedguest = (isguest()) ? get_field('course', 'guest', 'id', $course_id) : false ; 390 391 if ($unenroled && !$isallowedguest){ 392 return false; 393 } 394 395 // if user is enrolled or is allowed user and course is hidden, can he see it ? 396 $visibility = get_field('course', 'visible', 'id', $course_id); 397 if ($visibility <= 0){ 398 if (!has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course_id))){ 399 return false; 400 } 401 } 402 403 /** 404 * prerecorded capabilities 405 */ 406 // get context caching information and tries to discard unwanted records here 407 408 409 /** 410 * final checks 411 */ 412 // then give back indexing data to the module for local check 413 include_once "{$CFG->dirroot}/search/documents/{$doctype}_document.php"; 414 $access_check_function = "{$doctype}_check_text_access"; 415 416 if (function_exists($access_check_function)){ 417 $modulecheck = $access_check_function($path, $item_type, $this_id, $user, $group_id, $context_id); 418 // echo "module said $modulecheck for item $doctype/$item_type/$this_id"; 419 return($modulecheck); 420 } 421 422 return true; 423 } 424 425 /** 426 * 427 */ 428 public function count() { 429 return $this->total_results; 430 } //count 431 432 /** 433 * 434 */ 435 public function is_valid() { 436 return ($this->validquery and $this->validindex); 437 } 438 439 /** 440 * 441 */ 442 public function is_valid_query() { 443 return $this->validquery; 444 } 445 446 /** 447 * 448 */ 449 public function is_valid_index() { 450 return $this->validindex; 451 } 452 453 /** 454 * 455 */ 456 public function total_pages() { 457 return ceil($this->count()/$this->results_per_page); 458 } 459 460 /** 461 * 462 */ 463 public function get_pagenumber() { 464 return $this->pagenumber; 465 } 466 467 /** 468 * 469 */ 470 public function get_results_per_page() { 471 return $this->results_per_page; 472 } 473 } 474 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |