[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/search/ -> querylib.php (source)

   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)."'>&lt; {$back}</a>&nbsp;";
 323        } else {
 324          $ret .= "&lt; {$back}&nbsp;";
 325        } 
 326  
 327        //don't <a href> the current page
 328        for ($i = 1; $i <= $pages; $i++) {
 329          if ($page == $i) {
 330            $ret .= "($i)&nbsp;";
 331          } else {
 332            $ret .= "<a href='query.php?query_string={$query}&page={$i}'>{$i}</a>&nbsp;";
 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} &gt;</a>&nbsp;";
 339        } else {
 340          $ret .= "{$next} &gt;&nbsp;";
 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  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7