Commit | Line | Data |
---|---|---|
682d4032 | 1 | <?php |
3319ef85 | 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 | * The query page - accepts a user-entered query string and returns results. | |
13 | * | |
14 | * Queries are boolean-aware, e.g.: | |
15 | * | |
16 | * '+' term required | |
17 | * '-' term must not be present | |
18 | * '' (no modifier) term's presence increases rank, but isn't required | |
19 | * 'field:' search this field | |
20 | * | |
21 | * Examples: | |
22 | * | |
23 | * 'earthquake +author:michael' | |
24 | * Searches for documents written by 'michael' that contain 'earthquake' | |
25 | * | |
26 | * 'earthquake +doctype:wiki' | |
27 | * Search all wiki pages for 'earthquake' | |
28 | * | |
29 | * '+author:helen +author:foster' | |
30 | * All articles written by Helen Foster | |
31 | * | |
32 | */ | |
70f52c70 | 33 | |
3319ef85 | 34 | /** |
35 | * includes and requires | |
36 | */ | |
37 | require_once('../config.php'); | |
964a5e92 | 38 | require_once($CFG->dirroot.'/search/lib.php'); |
eef868d1 | 39 | |
3319ef85 | 40 | if ($CFG->forcelogin) { |
41 | require_login(); | |
42 | } | |
70f52c70 | 43 | |
3319ef85 | 44 | if (empty($CFG->enableglobalsearch)) { |
32487831 | 45 | print_error('globalsearchdisabled', 'search'); |
3319ef85 | 46 | } |
70f52c70 | 47 | |
a1abd74d | 48 | $adv = new stdClass(); |
70f52c70 | 49 | |
3319ef85 | 50 | /// check for php5, but don't die yet (see line 52) |
eef868d1 | 51 | |
964a5e92 | 52 | require_once($CFG->dirroot.'/search/querylib.php'); |
32487831 | 53 | |
964a5e92 | 54 | $page_number = optional_param('page', -1, PARAM_INT); |
55 | $pages = ($page_number == -1) ? false : true; | |
56 | $advanced = (optional_param('a', '0', PARAM_INT) == '1') ? true : false; | |
00521032 | 57 | $query_string = optional_param('query_string', '', PARAM_CLEAN); |
32487831 | 58 | |
a6855934 | 59 | $url = new moodle_url('/search/query.php'); |
c86bdd5d | 60 | if ($page_number !== -1) { |
61 | $url->param('page', $page_number); | |
62 | } | |
63 | if ($advanced) { | |
64 | $url->param('a', '1'); | |
65 | } | |
66 | $PAGE->set_url($url); | |
67 | ||
70f52c70 | 68 | /// discard harmfull searches |
32487831 | 69 | |
964a5e92 | 70 | if (!isset($CFG->block_search_utf8dir)){ |
71 | set_config('block_search_utf8dir', 1); | |
72 | } | |
70f52c70 | 73 | |
74 | /// discard harmfull searches | |
32487831 | 75 | |
964a5e92 | 76 | if (preg_match("/^[\*\?]+$/", $query_string)){ |
77 | $query_string = ''; | |
78 | $error = get_string('fullwildcardquery','search'); | |
79 | } | |
70f52c70 | 80 | |
964a5e92 | 81 | |
82 | if ($pages && isset($_SESSION['search_advanced_query'])) { | |
83 | // if both are set, then we are busy browsing through the result pages of an advanced query | |
84 | $adv = unserialize($_SESSION['search_advanced_query']); | |
85 | } elseif ($advanced) { | |
86 | // otherwise we are dealing with a new advanced query | |
87 | unset($_SESSION['search_advanced_query']); | |
88 | session_unregister('search_advanced_query'); | |
70f52c70 | 89 | |
964a5e92 | 90 | // chars to strip from strings (whitespace) |
91 | $chars = " \t\n\r\0\x0B,-+"; | |
70f52c70 | 92 | |
964a5e92 | 93 | // retrieve advanced query variables |
94 | $adv->mustappear = trim(optional_param('mustappear', '', PARAM_CLEAN), $chars); | |
95 | $adv->notappear = trim(optional_param('notappear', '', PARAM_CLEAN), $chars); | |
96 | $adv->canappear = trim(optional_param('canappear', '', PARAM_CLEAN), $chars); | |
97 | $adv->module = optional_param('module', '', PARAM_CLEAN); | |
98 | $adv->title = trim(optional_param('title', '', PARAM_CLEAN), $chars); | |
99 | $adv->author = trim(optional_param('author', '', PARAM_CLEAN), $chars); | |
70f52c70 | 100 | } |
964a5e92 | 101 | |
102 | if ($advanced) { | |
103 | //parse the advanced variables into a query string | |
104 | //TODO: move out to external query class (QueryParse?) | |
70f52c70 | 105 | |
964a5e92 | 106 | $query_string = ''; |
70f52c70 | 107 | |
964a5e92 | 108 | // get all available module types adding third party modules |
109 | $module_types = array_merge(array('all'), array_values(search_get_document_types())); | |
110 | $module_types = array_merge($module_types, array_values(search_get_document_types('X_SEARCH_TYPE'))); | |
111 | $adv->module = in_array($adv->module, $module_types) ? $adv->module : 'all'; | |
70f52c70 | 112 | |
964a5e92 | 113 | // convert '1 2' into '+1 +2' for required words field |
114 | if (strlen(trim($adv->mustappear)) > 0) { | |
115 | $query_string = ' +'.implode(' +', preg_split("/[\s,;]+/", $adv->mustappear)); | |
70f52c70 | 116 | } |
117 | ||
964a5e92 | 118 | // convert '1 2' into '-1 -2' for not wanted words field |
119 | if (strlen(trim($adv->notappear)) > 0) { | |
120 | $query_string .= ' -'.implode(' -', preg_split("/[\s,;]+/", $adv->notappear)); | |
70f52c70 | 121 | } |
122 | ||
964a5e92 | 123 | // this field is left untouched, apart from whitespace being stripped |
124 | if (strlen(trim($adv->canappear)) > 0) { | |
125 | $query_string .= ' '.implode(' ', preg_split("/[\s,;]+/", $adv->canappear)); | |
70f52c70 | 126 | } |
127 | ||
964a5e92 | 128 | // add module restriction |
4021d539 | 129 | $doctypestr = 'doctype'; |
130 | $titlestr = 'title'; | |
131 | $authorstr = 'author'; | |
964a5e92 | 132 | if ($adv->module != 'all') { |
133 | $query_string .= " +{$doctypestr}:".$adv->module; | |
70f52c70 | 134 | } |
135 | ||
964a5e92 | 136 | // create title search string |
137 | if (strlen(trim($adv->title)) > 0) { | |
138 | $query_string .= " +{$titlestr}:".implode(" +{$titlestr}:", preg_split("/[\s,;]+/", $adv->title)); | |
70f52c70 | 139 | } |
140 | ||
964a5e92 | 141 | // create author search string |
142 | if (strlen(trim($adv->author)) > 0) { | |
143 | $query_string .= " +{$authorstr}:".implode(" +{$authorstr}:", preg_split("/[\s,;]+/", $adv->author)); | |
70f52c70 | 144 | } |
145 | ||
964a5e92 | 146 | // save our options if the query is valid |
147 | if (!empty($query_string)) { | |
148 | $_SESSION['search_advanced_query'] = serialize($adv); | |
70f52c70 | 149 | } |
150 | } | |
964a5e92 | 151 | |
152 | // normalise page number | |
153 | if ($page_number < 1) { | |
154 | $page_number = 1; | |
70f52c70 | 155 | } |
964a5e92 | 156 | |
157 | //run the query against the index ensuring internal coding works in UTF-8 | |
158 | Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive()); | |
159 | $sq = new SearchQuery($query_string, $page_number, 10, false); | |
70f52c70 | 160 | |
2a250a0b | 161 | $site = get_site(); |
70f52c70 | 162 | |
3319ef85 | 163 | $strsearch = get_string('search', 'search'); |
164 | $strquery = get_string('enteryoursearchquery', 'search'); | |
70f52c70 | 165 | |
964a5e92 | 166 | // print the header |
964a5e92 | 167 | $site = get_site(); |
4c4d78fb | 168 | $PAGE->set_context(get_context_instance(CONTEXT_SYSTEM)); |
a6855934 PS |
169 | $PAGE->navbar->add($strsearch, new moodle_url('/search/index.php')); |
170 | $PAGE->navbar->add($strquery, new moodle_url('/search/stats.php')); | |
766ccfbd | 171 | $PAGE->set_title($strsearch); |
172 | $PAGE->set_heading($site->fullname); | |
173 | echo $OUTPUT->header(); | |
70f52c70 | 174 | |
e3c7f155 | 175 | if (!empty($error)){ |
176 | notice ($error); | |
177 | } | |
70f52c70 | 178 | |
179 | echo $OUTPUT->box_start(); | |
f7a1496e | 180 | echo $OUTPUT->heading($strquery); |
70f52c70 | 181 | |
182 | echo $OUTPUT->box_start(); | |
183 | ||
3319ef85 | 184 | $vars = get_object_vars($adv); |
70f52c70 | 185 | |
3319ef85 | 186 | if (isset($vars)) { |
187 | foreach ($vars as $key => $value) { | |
964a5e92 | 188 | // htmlentities breaks non-ascii chars ?? |
00521032 PS |
189 | $adv->key = $value; |
190 | //$adv->$key = htmlentities($value); | |
70f52c70 | 191 | } |
3319ef85 | 192 | } |
193 | ?> | |
3319ef85 | 194 | <form id="query" method="get" action="query.php"> |
70f52c70 | 195 | <?php |
196 | if (!$advanced) { | |
3319ef85 | 197 | ?> |
964a5e92 | 198 | <input type="text" name="query_string" length="50" value="<?php p($query_string) ?>" /> |
199 | <input type="submit" value="<?php print_string('search', 'search') ?>" /> | |
3319ef85 | 200 | <a href="query.php?a=1"><?php print_string('advancedsearch', 'search') ?></a> | |
201 | <a href="stats.php"><?php print_string('statistics', 'search') ?></a> | |
70f52c70 | 202 | <?php |
203 | } | |
e3c7f155 | 204 | else { |
70f52c70 | 205 | echo $OUTPUT->box_start(); |
3319ef85 | 206 | ?> |
9a439947 | 207 | <input type="hidden" name="a" value="<?php p($advanced); ?>"/> |
70f52c70 | 208 | |
3319ef85 | 209 | <table border="0" cellpadding="3" cellspacing="3"> |
70f52c70 | 210 | |
3319ef85 | 211 | <tr> |
212 | <td width="240"><?php print_string('thesewordsmustappear', 'search') ?>:</td> | |
9a439947 | 213 | <td><input type="text" name="mustappear" length="50" value="<?php p($adv->mustappear); ?>" /></td> |
3319ef85 | 214 | </tr> |
70f52c70 | 215 | |
3319ef85 | 216 | <tr> |
217 | <td><?php print_string('thesewordsmustnotappear', 'search') ?>:</td> | |
9a439947 | 218 | <td><input type="text" name="notappear" length="50" value="<?php p($adv->notappear); ?>" /></td> |
3319ef85 | 219 | </tr> |
70f52c70 | 220 | |
3319ef85 | 221 | <tr> |
222 | <td><?php print_string('thesewordshelpimproverank', 'search') ?>:</td> | |
9a439947 | 223 | <td><input type="text" name="canappear" length="50" value="<?php p($adv->canappear); ?>" /></td> |
3319ef85 | 224 | </tr> |
70f52c70 | 225 | |
3319ef85 | 226 | <tr> |
227 | <td><?php print_string('whichmodulestosearch?', 'search') ?>:</td> | |
228 | <td> | |
229 | <select name="module"> | |
70f52c70 | 230 | <?php |
3319ef85 | 231 | foreach($module_types as $mod) { |
232 | if ($mod == $adv->module) { | |
233 | if ($mod != 'all'){ | |
234 | print "<option value='$mod' selected=\"selected\">".get_string('modulenameplural', $mod)."</option>\n"; | |
235 | } | |
236 | else{ | |
237 | print "<option value='$mod' selected=\"selected\">".get_string('all', 'search')."</option>\n"; | |
238 | } | |
70f52c70 | 239 | } |
3319ef85 | 240 | else { |
241 | if ($mod != 'all'){ | |
242 | print "<option value='$mod'>".get_string('modulenameplural', $mod)."</option>\n"; | |
243 | } | |
244 | else{ | |
245 | print "<option value='$mod'>".get_string('all', 'search')."</option>\n"; | |
246 | } | |
70f52c70 | 247 | } |
248 | } | |
3319ef85 | 249 | ?> |
250 | </select> | |
251 | </td> | |
252 | </tr> | |
70f52c70 | 253 | |
3319ef85 | 254 | <tr> |
255 | <td><?php print_string('wordsintitle', 'search') ?>:</td> | |
9a439947 | 256 | <td><input type="text" name="title" length="50" value="<?php p($adv->title); ?>" /></td> |
3319ef85 | 257 | </tr> |
70f52c70 | 258 | |
3319ef85 | 259 | <tr> |
260 | <td><?php print_string('authorname', 'search') ?>:</td> | |
9a439947 | 261 | <td><input type="text" name="author" length="50" value="<?php p($adv->author); ?>" /></td> |
3319ef85 | 262 | </tr> |
70f52c70 | 263 | |
3319ef85 | 264 | <tr> |
9a439947 | 265 | <td colspan="3" align="center"><br /><input type="submit" value="<?php p(get_string('search', 'search')) ?>" /></td> |
3319ef85 | 266 | </tr> |
70f52c70 | 267 | |
3319ef85 | 268 | <tr> |
269 | <td colspan="3" align="center"> | |
270 | <table border="0" cellpadding="0" cellspacing="0"> | |
271 | <tr> | |
272 | <td><a href="query.php"><?php print_string('normalsearch', 'search') ?></a> |</td> | |
273 | <td> <a href="stats.php"><?php print_string('statistics', 'search') ?></a></td> | |
274 | </tr> | |
275 | </table> | |
276 | </td> | |
277 | </tr> | |
6e780562 | 278 | </table> |
3319ef85 | 279 | <?php |
70f52c70 | 280 | echo $OUTPUT->box_end(); |
281 | } | |
3319ef85 | 282 | ?> |
283 | </form> | |
284 | <br/> | |
70f52c70 | 285 | |
4021d539 | 286 | <div align="center"> |
3319ef85 | 287 | <?php |
288 | print_string('searching', 'search') . ': '; | |
70f52c70 | 289 | |
3319ef85 | 290 | if ($sq->is_valid_index()) { |
291 | //use cached variable to show up-to-date index size (takes deletions into account) | |
292 | print $CFG->search_index_size; | |
70f52c70 | 293 | } |
e3c7f155 | 294 | else { |
3319ef85 | 295 | print "0"; |
70f52c70 | 296 | } |
297 | ||
3319ef85 | 298 | print ' '; |
299 | print_string('documents', 'search'); | |
300 | print '.'; | |
70f52c70 | 301 | |
4f0c2d00 | 302 | if (!$sq->is_valid_index() and has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) { |
3319ef85 | 303 | print '<p>' . get_string('noindexmessage', 'search') . '<a href="indexersplash.php">' . get_string('createanindex', 'search')."</a></p>\n"; |
70f52c70 | 304 | } |
305 | ||
3319ef85 | 306 | ?> |
307 | </div> | |
308 | <?php | |
70f52c70 | 309 | echo $OUTPUT->box_end(); |
310 | ||
3efa38a4 | 311 | /// prints all the results in a box |
312 | ||
3319ef85 | 313 | if ($sq->is_valid()) { |
70f52c70 | 314 | echo $OUTPUT->box_start(); |
315 | ||
3319ef85 | 316 | search_stopwatch(); |
317 | $hit_count = $sq->count(); | |
70f52c70 | 318 | |
3319ef85 | 319 | print "<br />"; |
70f52c70 | 320 | |
decf9db1 | 321 | print $hit_count.' '.get_string('resultsreturnedfor', 'search') . " '".s($query_string)."'."; |
3319ef85 | 322 | print "<br />"; |
70f52c70 | 323 | |
3319ef85 | 324 | if ($hit_count > 0) { |
325 | $page_links = $sq->page_numbers(); | |
326 | $hits = $sq->results(); | |
70f52c70 | 327 | |
3319ef85 | 328 | if ($advanced) { |
329 | // if in advanced mode, search options are saved in the session, so | |
330 | // we can remove the query string var from the page links, and replace | |
331 | // it with a=1 (Advanced = on) instead | |
332 | $page_links = preg_replace("/query_string=[^&]+/", 'a=1', $page_links); | |
70f52c70 | 333 | } |
334 | ||
3319ef85 | 335 | print "<ol>"; |
70f52c70 | 336 | |
3319ef85 | 337 | $typestr = get_string('type', 'search'); |
338 | $scorestr = get_string('score', 'search'); | |
339 | $authorstr = get_string('author', 'search'); | |
3efa38a4 | 340 | |
341 | $searchables = search_collect_searchables(false, false); | |
22820396 AB |
342 | |
343 | //build a list of distinct user objects needed for results listing. | |
344 | $hitusers = array(); | |
345 | foreach ($hits as $listing) { | |
346 | if ($listing->doctype == 'user' and !isset($hitusers[$listing->userid])) { | |
347 | $hitusers[$listing->userid] = $DB->get_record('user', array('id' => $listing->userid)); | |
348 | } | |
349 | } | |
350 | ||
70f52c70 | 351 | foreach ($hits as $listing) { |
352 | ||
22820396 AB |
353 | if ($listing->doctype == 'user') { // A special handle for users |
354 | $icon = $OUTPUT->user_picture($hitusers[$listing->userid]); | |
964a5e92 | 355 | } else { |
b5d0cafc | 356 | $iconpath = $OUTPUT->pix_url('icon', $listing->doctype); |
964a5e92 | 357 | $icon = "<img align=\"top\" src=\"".$iconpath."\" class=\"activityicon\" alt=\"\"/>"; |
358 | } | |
78946b9b | 359 | $coursename = $DB->get_field('course', 'fullname', array('id' => $listing->courseid)); |
3efa38a4 | 360 | $courseword = mb_convert_case(get_string('course', 'moodle'), MB_CASE_LOWER, 'UTF-8'); |
964a5e92 | 361 | $course = ($listing->doctype != 'user') ? '<strong> ('.$courseword.': \''.$coursename.'\')</strong>' : '' ; |
362 | ||
3319ef85 | 363 | $title_post_processing_function = $listing->doctype.'_link_post_processing'; |
3efa38a4 | 364 | $searchable_instance = $searchables[$listing->doctype]; |
365 | if ($searchable_instance->location == 'internal'){ | |
366 | require_once "{$CFG->dirroot}/search/documents/{$listing->doctype}_document.php"; | |
367 | } else { | |
368 | require_once "{$CFG->dirroot}/{$searchable_instance->location}/{$listing->doctype}/search_document.php"; | |
369 | } | |
3319ef85 | 370 | if (function_exists($title_post_processing_function)) { |
371 | $listing->title = $title_post_processing_function($listing->title); | |
372 | } | |
70f52c70 | 373 | |
964a5e92 | 374 | echo "<li value='".($listing->number + 1)."'><a href='" |
3efa38a4 | 375 | .str_replace('DEFAULT_POPUP_SETTINGS', DEFAULT_POPUP_SETTINGS ,$listing->url) |
964a5e92 | 376 | ."'>$icon $listing->title</a> $course<br />\n"; |
377 | echo "{$typestr}: " . $listing->doctype . ", {$scorestr}: " . round($listing->score, 3); | |
378 | if (!empty($listing->author) && !is_numeric($listing->author)){ | |
3efa38a4 | 379 | echo ", {$authorstr}: ".$listing->author."\n" |
b93b987d | 380 | ."</li>\n"; |
381 | } | |
70f52c70 | 382 | } |
3efa38a4 | 383 | echo "</ol>"; |
384 | echo $page_links; | |
70f52c70 | 385 | } |
386 | echo $OUTPUT->box_end(); | |
3319ef85 | 387 | ?> |
4021d539 | 388 | <div align="center"> |
70f52c70 | 389 | <?php |
3319ef85 | 390 | print_string('ittook', 'search'); |
70f52c70 | 391 | search_stopwatch(); |
3319ef85 | 392 | print_string('tofetchtheseresults', 'search'); |
393 | ?>. | |
394 | </div> | |
70f52c70 | 395 | |
3319ef85 | 396 | <?php |
397 | } | |
70f52c70 | 398 | echo $OUTPUT->box_end(); |
daa2cd33 | 399 | echo $OUTPUT->footer(); |
f7a1496e | 400 | ?> |