5f9ec9e654cb67d0b39439c2ba87c045d7eea5ca
[moodle.git] / search / querylib.php
1 <?php
2   require_once("$CFG->dirroot/search/Zend/Search/Lucene.php");
4   class SearchResult {
5     public  $url,
6             $title,            
7             $doctype,
8             $author,
9             $score,
10             $number;
11   } //SearchResult
14   //split this into Cache class and extend to SearchCache?
15   class SearchCache {
16     private $mode,
17             $valid;
18                 
19     public function __construct($mode='session') {
20       $accepted_modes = array('session');
21       
22       if (in_array($mode, $accepted_modes)) {
23         $this->mode = $mode;
24       } else {
25         $this->mode = 'session';
26       } //else
27       
28       $this->valid = true;
29     } //constructor
30     
31     public function can_cache() {
32       return $this->valid;
33     } //can_cache
34     
35     public function cache($id=false, $object=false) {
36       //see if there was a previous query
37       $last_term = $this->fetch('search_last_term');
38       
39       //if this query is different from the last, clear out the last one
40       if ($id != false and $last_term != $id) {
41         $this->clear($last_term);
42       } //if
43       
44       //store the new query if id and object are passed in
45       if ($object and $id) {
46         $this->store('search_last_term', $id);
47         $this->store($id, $object);
48         return true;
49       //otherwise return the stored results
50       } else if ($id and $this->exists($id)) {        
51         return $this->fetch($id);
52       } //else
53     } //cache
54     
55     private function exists($id) {
56       switch ($this->mode) {
57         case 'session' :
58           return isset($_SESSION[$id]);
59       } //switch
60     } //exists
61         
62     private function clear($id) {
63       switch ($this->mode) {
64         case 'session' :
65           unset($_SESSION[$id]);
66           session_unregister($id);
67           return;
68       } //switch
69     } //clear    
70         
71     private function fetch($id) {
72       switch ($this->mode) {
73         case 'session' :
74           return ($this->exists($id)) ? unserialize($_SESSION[$id]) : false;
75       } //switch
76     } //fetch
77     
78     private function store($id, $object) {
79       switch ($this->mode) {
80         case 'session' :
81           $_SESSION[$id] = serialize($object);
82           return;
83       } //switch
84     } //store        
85   } //SearchCache
86   
87   
88   class SearchQuery {
89     private $index,
90             $term,
91             $pagenumber,            
92             $cache,
93             $validquery,
94             $validindex,            
95             $results,
96             $results_per_page;
97     
98     public function __construct($term='', $page=1, $results_per_page=10, $cache=false) {
99       global $CFG;
100       
101       $this->term       = $term;
102       $this->pagenumber = $page;
103       $this->cache      = $cache;
104       $this->validquery = true;
105       $this->validindex = true;      
106       $this->results_per_page = $results_per_page;      
107       
108       $index_path = SEARCH_INDEX_PATH;
109       
110       try {
111         $this->index = new Zend_Search_Lucene($index_path, false);
112       } catch(Exception $e) {    
113         $this->validindex = false;        
114         return;
115       } //catch
116             
117       if (empty($this->term)) {
118         $this->validquery = false;
119       } else {
120         $this->set_query($this->term);
121       } //else      
122     } //constructor
123     
124     public function set_query($term='') {            
125       if (!empty($term)) {
126         $this->term = $term;
127       } //if
128       
129       if (empty($this->term)) {
130         $this->validquery = false;
131       } else {
132         $this->validquery = true;
133       } //else
134       
135       if ($this->validquery and $this->validindex) {
136         $this->results = $this->get_results();
137       } else {
138         $this->results = array();
139       } //else      
140     } //set_query
141     
142     public function results() {
143       if ($this->validquery and $this->validindex) {
144         return $this->get_subset_results();
145       } else {
146         return array();
147       } //else
148     } //results
149     
150     private function get_subset_results() {
151       if ($this->count() < $this->results_per_page) {
152         $this->pagenumber = 1;
153       } else if ($this->pagenumber > $this->total_pages()) {
154         $this->pagenumber = $this->total_pages();
155       } //if
156     
157       $start  = ($this->pagenumber - 1) * $this->results_per_page;
158                      
159       return array_slice($this->results, $start, $this->results_per_page);    
160     } //get_results
161     
162     private function get_all_results() {
163       global $USER;
164       
165       $resultdoc  = new SearchResult();
166       $resultdocs = array();
167       $i = 0;
168       
169       $hits = $this->index->find(strtolower($this->term));
170       
171       foreach ($hits as $hit) {            
172         //check permissions on each result
173         if ($this->can_display($USER, $hit->course_id, $hit->group_id)) {
174           $resultdoc->number  = $i;
175           $resultdoc->url     = $hit->url;
176           $resultdoc->title   = $hit->title;
177           $resultdoc->score   = $hit->score;
178           $resultdoc->doctype = $hit->doctype;
179           $resultdoc->author  = $hit->author;
180         
181           //and store it
182           $resultdocs[] = clone($resultdoc);
183           
184           $i++;
185         } //if
186       } //foreach
187       
188       return $resultdocs;
189     } //get_all_results
190               
191     private function get_results() {
192       $cache = new SearchCache();
193       
194       if ($this->cache and $cache->can_cache()) {        
195         if (!($resultdocs = $cache->cache($this->term))) {
196           $resultdocs = $this->get_all_results();
197           //cache the results so we don't have to compute this on every page-load
198           $cache->cache($this->term, $resultdocs);          
199           //print "Using new results.";
200         } else {            
201           //There was something in the cache, so we're using that to save time
202           //print "Using cached results.";
203         } //else                
204       } else {
205         //no caching :(
206         //print "Caching disabled!";
207         $resultdocs = $this->get_all_results();
208       } //else
210       return $resultdocs;
211     } //get_results  
212     
213     public function page_numbers() {
214       $pages  = $this->total_pages();
215       $query  = $this->term;
216       $page   = $this->pagenumber;
217       $next   = "Next";
218       $back   = "Back";
219       
220       $ret = "<div align='center' id='search_page_links'>";    
221       
222       //Back is disabled if we're on page 1
223       if ($page > 1) {
224         $ret .= "<a href='query.php?query_string=$query&page=".($page-1)."'>< $back</a>&nbsp;";
225       } else {
226         $ret .= "< $back&nbsp;";
227       } //else
228       
229       //don't <a href> the current page
230       for ($i = 1; $i <= $pages; $i++) {
231         if ($page == $i) {
232           $ret .= "[$i]&nbsp;";
233         } else {
234           $ret .= "<a href='query.php?query_string=$query&page=$i'>$i</a>&nbsp;";
235         } //else
236       } //for
237       
238       //Next disabled if we're on the last page
239       if ($page < $pages) {      
240         $ret .= "<a href='query.php?query_string=$query&page=".($page+1)."'>$next ></a>&nbsp;";
241       } else {
242         $ret .= "$next >&nbsp;";
243       } //else
244       
245       $ret .= "</div>";    
246       
247       //shorten really long page lists, to stop table distorting width-ways
248       if (strlen($ret) > 70) {
249         $start = 4;
250         $end = $page - 5;     
251         $ret = preg_replace("/<a\D+\d+\D+>$start<\/a>.*?<a\D+\d+\D+>$end<\/a>/", '...', $ret);
252   
253         $start = $page + 5;
254         $end = $pages - 3;      
255         $ret = preg_replace("/<a\D+\d+\D+>$start<\/a>.*?<a\D+\d+\D+>$end<\/a>/", '...', $ret);
256       } //if
257       
258       return $ret;
259     } //page_numbers    
261     //can the user see this result?          
262     private function can_display($user, $course_id, $group_id) {
263       //this function should return true/false depending on
264       //whether or not a user can see this resource
265       //..
266       //if one of you nice moodlers see this, feel free to
267       //implement it for me .. :-P      
268       return true;
269     } //can_display
270     
271     public function count() {
272       return count($this->results);
273     } //count
274     
275     public function index_count() {
276       return $this->index->count();
277     } //index_count    
278     
279     public function is_valid() {
280       return ($this->validquery and $this->validindex);
281     } //is_valid
282     
283     public function is_valid_query() {
284       return $this->validquery;
285     } //is_valid_query
287     public function is_valid_index() {
288       return $this->validindex;
289     } //is_valid_index
290     
291     public function total_pages() {
292       return ceil($this->count()/$this->results_per_page);
293     } //pages    
294     
295     public function get_pagenumber() {
296       return $this->pagenumber;
297     } //get_pagenumber
298     
299     public function get_results_per_page() {
300       return $this->results_per_page;
301     } //get_results_per_page        
302   } //SearchQuery
304 ?>