PageRenderTime 27ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/search/query.php

https://github.com/kpike/moodle
PHP | 400 lines | 272 code | 66 blank | 62 comment | 49 complexity | b857d325cb3983f8d03b45172bc6bd98 MD5 | raw file
  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. * 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. */
  33. /**
  34. * includes and requires
  35. */
  36. require_once('../config.php');
  37. require_once($CFG->dirroot.'/search/lib.php');
  38. if ($CFG->forcelogin) {
  39. require_login();
  40. }
  41. if (empty($CFG->enableglobalsearch)) {
  42. print_error('globalsearchdisabled', 'search');
  43. }
  44. $adv = new stdClass();
  45. /// check for php5, but don't die yet (see line 52)
  46. require_once($CFG->dirroot.'/search/querylib.php');
  47. $page_number = optional_param('page', -1, PARAM_INT);
  48. $pages = ($page_number == -1) ? false : true;
  49. $advanced = (optional_param('a', '0', PARAM_INT) == '1') ? true : false;
  50. $query_string = optional_param('query_string', '', PARAM_CLEAN);
  51. $url = new moodle_url('/search/query.php');
  52. if ($page_number !== -1) {
  53. $url->param('page', $page_number);
  54. }
  55. if ($advanced) {
  56. $url->param('a', '1');
  57. }
  58. $PAGE->set_url($url);
  59. /// discard harmfull searches
  60. if (!isset($CFG->block_search_utf8dir)){
  61. set_config('block_search_utf8dir', 1);
  62. }
  63. /// discard harmfull searches
  64. if (preg_match("/^[\*\?]+$/", $query_string)){
  65. $query_string = '';
  66. $error = get_string('fullwildcardquery','search');
  67. }
  68. if ($pages && isset($_SESSION['search_advanced_query'])) {
  69. // if both are set, then we are busy browsing through the result pages of an advanced query
  70. $adv = unserialize($_SESSION['search_advanced_query']);
  71. } elseif ($advanced) {
  72. // otherwise we are dealing with a new advanced query
  73. unset($_SESSION['search_advanced_query']);
  74. session_unregister('search_advanced_query');
  75. // chars to strip from strings (whitespace)
  76. $chars = " \t\n\r\0\x0B,-+";
  77. // retrieve advanced query variables
  78. $adv->mustappear = trim(optional_param('mustappear', '', PARAM_CLEAN), $chars);
  79. $adv->notappear = trim(optional_param('notappear', '', PARAM_CLEAN), $chars);
  80. $adv->canappear = trim(optional_param('canappear', '', PARAM_CLEAN), $chars);
  81. $adv->module = optional_param('module', '', PARAM_CLEAN);
  82. $adv->title = trim(optional_param('title', '', PARAM_CLEAN), $chars);
  83. $adv->author = trim(optional_param('author', '', PARAM_CLEAN), $chars);
  84. }
  85. if ($advanced) {
  86. //parse the advanced variables into a query string
  87. //TODO: move out to external query class (QueryParse?)
  88. $query_string = '';
  89. // get all available module types adding third party modules
  90. $module_types = array_merge(array('all'), array_values(search_get_document_types()));
  91. $module_types = array_merge($module_types, array_values(search_get_document_types('X_SEARCH_TYPE')));
  92. $adv->module = in_array($adv->module, $module_types) ? $adv->module : 'all';
  93. // convert '1 2' into '+1 +2' for required words field
  94. if (strlen(trim($adv->mustappear)) > 0) {
  95. $query_string = ' +'.implode(' +', preg_split("/[\s,;]+/", $adv->mustappear));
  96. }
  97. // convert '1 2' into '-1 -2' for not wanted words field
  98. if (strlen(trim($adv->notappear)) > 0) {
  99. $query_string .= ' -'.implode(' -', preg_split("/[\s,;]+/", $adv->notappear));
  100. }
  101. // this field is left untouched, apart from whitespace being stripped
  102. if (strlen(trim($adv->canappear)) > 0) {
  103. $query_string .= ' '.implode(' ', preg_split("/[\s,;]+/", $adv->canappear));
  104. }
  105. // add module restriction
  106. $doctypestr = 'doctype';
  107. $titlestr = 'title';
  108. $authorstr = 'author';
  109. if ($adv->module != 'all') {
  110. $query_string .= " +{$doctypestr}:".$adv->module;
  111. }
  112. // create title search string
  113. if (strlen(trim($adv->title)) > 0) {
  114. $query_string .= " +{$titlestr}:".implode(" +{$titlestr}:", preg_split("/[\s,;]+/", $adv->title));
  115. }
  116. // create author search string
  117. if (strlen(trim($adv->author)) > 0) {
  118. $query_string .= " +{$authorstr}:".implode(" +{$authorstr}:", preg_split("/[\s,;]+/", $adv->author));
  119. }
  120. // save our options if the query is valid
  121. if (!empty($query_string)) {
  122. $_SESSION['search_advanced_query'] = serialize($adv);
  123. }
  124. }
  125. // normalise page number
  126. if ($page_number < 1) {
  127. $page_number = 1;
  128. }
  129. //run the query against the index ensuring internal coding works in UTF-8
  130. Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8_CaseInsensitive());
  131. $sq = new SearchQuery($query_string, $page_number, 10, false);
  132. $site = get_site();
  133. $strsearch = get_string('search', 'search');
  134. $strquery = get_string('enteryoursearchquery', 'search');
  135. // print the header
  136. $site = get_site();
  137. $PAGE->set_context(get_context_instance(CONTEXT_SYSTEM));
  138. $PAGE->navbar->add($strsearch, new moodle_url('/search/index.php'));
  139. $PAGE->navbar->add($strquery, new moodle_url('/search/stats.php'));
  140. $PAGE->set_title($strsearch);
  141. $PAGE->set_heading($site->fullname);
  142. echo $OUTPUT->header();
  143. if (!empty($error)){
  144. notice ($error);
  145. }
  146. echo $OUTPUT->box_start();
  147. echo $OUTPUT->heading($strquery);
  148. echo $OUTPUT->box_start();
  149. $vars = get_object_vars($adv);
  150. if (isset($vars)) {
  151. foreach ($vars as $key => $value) {
  152. // htmlentities breaks non-ascii chars ??
  153. $adv->key = $value;
  154. //$adv->$key = htmlentities($value);
  155. }
  156. }
  157. ?>
  158. <form id="query" method="get" action="query.php">
  159. <?php
  160. if (!$advanced) {
  161. ?>
  162. <input type="text" name="query_string" length="50" value="<?php p($query_string) ?>" />&nbsp;
  163. <input type="submit" value="<?php print_string('search', 'search') ?>" /> &nbsp;
  164. <a href="query.php?a=1"><?php print_string('advancedsearch', 'search') ?></a> |
  165. <a href="stats.php"><?php print_string('statistics', 'search') ?></a>
  166. <?php
  167. }
  168. else {
  169. echo $OUTPUT->box_start();
  170. ?>
  171. <input type="hidden" name="a" value="<?php p($advanced); ?>"/>
  172. <table border="0" cellpadding="3" cellspacing="3">
  173. <tr>
  174. <td width="240"><?php print_string('thesewordsmustappear', 'search') ?>:</td>
  175. <td><input type="text" name="mustappear" length="50" value="<?php p($adv->mustappear); ?>" /></td>
  176. </tr>
  177. <tr>
  178. <td><?php print_string('thesewordsmustnotappear', 'search') ?>:</td>
  179. <td><input type="text" name="notappear" length="50" value="<?php p($adv->notappear); ?>" /></td>
  180. </tr>
  181. <tr>
  182. <td><?php print_string('thesewordshelpimproverank', 'search') ?>:</td>
  183. <td><input type="text" name="canappear" length="50" value="<?php p($adv->canappear); ?>" /></td>
  184. </tr>
  185. <tr>
  186. <td><?php print_string('whichmodulestosearch?', 'search') ?>:</td>
  187. <td>
  188. <select name="module">
  189. <?php
  190. foreach($module_types as $mod) {
  191. if ($mod == $adv->module) {
  192. if ($mod != 'all'){
  193. print "<option value='$mod' selected=\"selected\">".get_string('modulenameplural', $mod)."</option>\n";
  194. }
  195. else{
  196. print "<option value='$mod' selected=\"selected\">".get_string('all', 'search')."</option>\n";
  197. }
  198. }
  199. else {
  200. if ($mod != 'all'){
  201. print "<option value='$mod'>".get_string('modulenameplural', $mod)."</option>\n";
  202. }
  203. else{
  204. print "<option value='$mod'>".get_string('all', 'search')."</option>\n";
  205. }
  206. }
  207. }
  208. ?>
  209. </select>
  210. </td>
  211. </tr>
  212. <tr>
  213. <td><?php print_string('wordsintitle', 'search') ?>:</td>
  214. <td><input type="text" name="title" length="50" value="<?php p($adv->title); ?>" /></td>
  215. </tr>
  216. <tr>
  217. <td><?php print_string('authorname', 'search') ?>:</td>
  218. <td><input type="text" name="author" length="50" value="<?php p($adv->author); ?>" /></td>
  219. </tr>
  220. <tr>
  221. <td colspan="3" align="center"><br /><input type="submit" value="<?php p(get_string('search', 'search')) ?>" /></td>
  222. </tr>
  223. <tr>
  224. <td colspan="3" align="center">
  225. <table border="0" cellpadding="0" cellspacing="0">
  226. <tr>
  227. <td><a href="query.php"><?php print_string('normalsearch', 'search') ?></a> |</td>
  228. <td>&nbsp;<a href="stats.php"><?php print_string('statistics', 'search') ?></a></td>
  229. </tr>
  230. </table>
  231. </td>
  232. </tr>
  233. </table>
  234. <?php
  235. echo $OUTPUT->box_end();
  236. }
  237. ?>
  238. </form>
  239. <br/>
  240. <div align="center">
  241. <?php
  242. print_string('searching', 'search') . ': ';
  243. if ($sq->is_valid_index()) {
  244. //use cached variable to show up-to-date index size (takes deletions into account)
  245. print $CFG->search_index_size;
  246. }
  247. else {
  248. print "0";
  249. }
  250. print ' ';
  251. print_string('documents', 'search');
  252. print '.';
  253. if (!$sq->is_valid_index() and has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
  254. print '<p>' . get_string('noindexmessage', 'search') . '<a href="indexersplash.php">' . get_string('createanindex', 'search')."</a></p>\n";
  255. }
  256. ?>
  257. </div>
  258. <?php
  259. echo $OUTPUT->box_end();
  260. /// prints all the results in a box
  261. if ($sq->is_valid()) {
  262. echo $OUTPUT->box_start();
  263. search_stopwatch();
  264. $hit_count = $sq->count();
  265. print "<br />";
  266. print $hit_count.' '.get_string('resultsreturnedfor', 'search') . " '".s($query_string)."'.";
  267. print "<br />";
  268. if ($hit_count > 0) {
  269. $page_links = $sq->page_numbers();
  270. $hits = $sq->results();
  271. if ($advanced) {
  272. // if in advanced mode, search options are saved in the session, so
  273. // we can remove the query string var from the page links, and replace
  274. // it with a=1 (Advanced = on) instead
  275. $page_links = preg_replace("/query_string=[^&]+/", 'a=1', $page_links);
  276. }
  277. print "<ol>";
  278. $typestr = get_string('type', 'search');
  279. $scorestr = get_string('score', 'search');
  280. $authorstr = get_string('author', 'search');
  281. $searchables = search_collect_searchables(false, false);
  282. //build a list of distinct user objects needed for results listing.
  283. $hitusers = array();
  284. foreach ($hits as $listing) {
  285. if ($listing->doctype == 'user' and !isset($hitusers[$listing->userid])) {
  286. $hitusers[$listing->userid] = $DB->get_record('user', array('id' => $listing->userid));
  287. }
  288. }
  289. foreach ($hits as $listing) {
  290. if ($listing->doctype == 'user') { // A special handle for users
  291. $icon = $OUTPUT->user_picture($hitusers[$listing->userid]);
  292. } else {
  293. $iconpath = $OUTPUT->pix_url('icon', $listing->doctype);
  294. $icon = "<img align=\"top\" src=\"".$iconpath."\" class=\"activityicon\" alt=\"\"/>";
  295. }
  296. $coursename = $DB->get_field('course', 'fullname', array('id' => $listing->courseid));
  297. $courseword = mb_convert_case(get_string('course', 'moodle'), MB_CASE_LOWER, 'UTF-8');
  298. $course = ($listing->doctype != 'user') ? '<strong> ('.$courseword.': \''.$coursename.'\')</strong>' : '' ;
  299. $title_post_processing_function = $listing->doctype.'_link_post_processing';
  300. $searchable_instance = $searchables[$listing->doctype];
  301. if ($searchable_instance->location == 'internal'){
  302. require_once "{$CFG->dirroot}/search/documents/{$listing->doctype}_document.php";
  303. } else {
  304. require_once "{$CFG->dirroot}/{$searchable_instance->location}/{$listing->doctype}/search_document.php";
  305. }
  306. if (function_exists($title_post_processing_function)) {
  307. $listing->title = $title_post_processing_function($listing->title);
  308. }
  309. echo "<li value='".($listing->number + 1)."'><a href='"
  310. .str_replace('DEFAULT_POPUP_SETTINGS', DEFAULT_POPUP_SETTINGS ,$listing->url)
  311. ."'>$icon $listing->title</a> $course<br />\n";
  312. echo "{$typestr}: " . $listing->doctype . ", {$scorestr}: " . round($listing->score, 3);
  313. if (!empty($listing->author) && !is_numeric($listing->author)){
  314. echo ", {$authorstr}: ".$listing->author."\n"
  315. ."</li>\n";
  316. }
  317. }
  318. echo "</ol>";
  319. echo $page_links;
  320. }
  321. echo $OUTPUT->box_end();
  322. ?>
  323. <div align="center">
  324. <?php
  325. print_string('ittook', 'search');
  326. search_stopwatch();
  327. print_string('tofetchtheseresults', 'search');
  328. ?>.
  329. </div>
  330. <?php
  331. }
  332. echo $OUTPUT->box_end();
  333. echo $OUTPUT->footer();
  334. ?>