PageRenderTime 31ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/mod/forum/search.php

https://bitbucket.org/moodle/moodle
PHP | 400 lines | 292 code | 58 blank | 50 comment | 37 complexity | ca7585bbb04562f6424da715912f9196 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * @package mod_forum
  18. * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
  19. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  20. */
  21. require_once('../../config.php');
  22. require_once('lib.php');
  23. $id = required_param('id', PARAM_INT); // course id
  24. $search = trim(optional_param('search', '', PARAM_NOTAGS)); // search string
  25. $page = optional_param('page', 0, PARAM_INT); // which page to show
  26. $perpage = optional_param('perpage', 10, PARAM_INT); // how many per page
  27. $showform = optional_param('showform', 0, PARAM_INT); // Just show the form
  28. $user = trim(optional_param('user', '', PARAM_NOTAGS)); // Names to search for
  29. $userid = trim(optional_param('userid', 0, PARAM_INT)); // UserID to search for
  30. $forumid = trim(optional_param('forumid', 0, PARAM_INT)); // ForumID to search for
  31. $subject = trim(optional_param('subject', '', PARAM_NOTAGS)); // Subject
  32. $phrase = trim(optional_param('phrase', '', PARAM_NOTAGS)); // Phrase
  33. $words = trim(optional_param('words', '', PARAM_NOTAGS)); // Words
  34. $fullwords = trim(optional_param('fullwords', '', PARAM_NOTAGS)); // Whole words
  35. $notwords = trim(optional_param('notwords', '', PARAM_NOTAGS)); // Words we don't want
  36. $tags = optional_param_array('tags', [], PARAM_TEXT);
  37. $timefromrestrict = optional_param('timefromrestrict', 0, PARAM_INT); // Use starting date
  38. $fromday = optional_param('fromday', 0, PARAM_INT); // Starting date
  39. $frommonth = optional_param('frommonth', 0, PARAM_INT); // Starting date
  40. $fromyear = optional_param('fromyear', 0, PARAM_INT); // Starting date
  41. $fromhour = optional_param('fromhour', 0, PARAM_INT); // Starting date
  42. $fromminute = optional_param('fromminute', 0, PARAM_INT); // Starting date
  43. if ($timefromrestrict) {
  44. $calendartype = \core_calendar\type_factory::get_calendar_instance();
  45. $gregorianfrom = $calendartype->convert_to_gregorian($fromyear, $frommonth, $fromday);
  46. $datefrom = make_timestamp($gregorianfrom['year'], $gregorianfrom['month'], $gregorianfrom['day'], $fromhour, $fromminute);
  47. } else {
  48. $datefrom = optional_param('datefrom', 0, PARAM_INT); // Starting date
  49. }
  50. $timetorestrict = optional_param('timetorestrict', 0, PARAM_INT); // Use ending date
  51. $today = optional_param('today', 0, PARAM_INT); // Ending date
  52. $tomonth = optional_param('tomonth', 0, PARAM_INT); // Ending date
  53. $toyear = optional_param('toyear', 0, PARAM_INT); // Ending date
  54. $tohour = optional_param('tohour', 0, PARAM_INT); // Ending date
  55. $tominute = optional_param('tominute', 0, PARAM_INT); // Ending date
  56. if ($timetorestrict) {
  57. $calendartype = \core_calendar\type_factory::get_calendar_instance();
  58. $gregorianto = $calendartype->convert_to_gregorian($toyear, $tomonth, $today);
  59. $dateto = make_timestamp($gregorianto['year'], $gregorianto['month'], $gregorianto['day'], $tohour, $tominute);
  60. } else {
  61. $dateto = optional_param('dateto', 0, PARAM_INT); // Ending date
  62. }
  63. $starredonly = optional_param('starredonly', false, PARAM_BOOL); // Include only favourites.
  64. $PAGE->set_pagelayout('standard');
  65. $PAGE->set_url($FULLME); //TODO: this is very sloppy --skodak
  66. $PAGE->set_secondary_active_tab("coursehome");
  67. if (empty($search)) { // Check the other parameters instead
  68. if (!empty($words)) {
  69. $search .= ' '.$words;
  70. }
  71. if (!empty($userid)) {
  72. $search .= ' userid:'.$userid;
  73. }
  74. if (!empty($forumid)) {
  75. $search .= ' forumid:'.$forumid;
  76. }
  77. if (!empty($user)) {
  78. $search .= ' '.forum_clean_search_terms($user, 'user:');
  79. }
  80. if (!empty($subject)) {
  81. $search .= ' '.forum_clean_search_terms($subject, 'subject:');
  82. }
  83. if (!empty($fullwords)) {
  84. $search .= ' '.forum_clean_search_terms($fullwords, '+');
  85. }
  86. if (!empty($notwords)) {
  87. $search .= ' '.forum_clean_search_terms($notwords, '-');
  88. }
  89. if (!empty($phrase)) {
  90. $search .= ' "'.$phrase.'"';
  91. }
  92. if (!empty($datefrom)) {
  93. $search .= ' datefrom:'.$datefrom;
  94. }
  95. if (!empty($dateto)) {
  96. $search .= ' dateto:'.$dateto;
  97. }
  98. if (!empty($tags)) {
  99. $search .= ' tags:' . implode(',', $tags);
  100. }
  101. if (!empty($starredonly)) {
  102. $search .= ' starredonly:on';
  103. }
  104. $individualparams = true;
  105. } else {
  106. $individualparams = false;
  107. }
  108. if ($search) {
  109. $search = forum_clean_search_terms($search);
  110. }
  111. if (!$course = $DB->get_record('course', array('id'=>$id))) {
  112. print_error('invalidcourseid');
  113. }
  114. require_course_login($course);
  115. $params = array(
  116. 'context' => $PAGE->context,
  117. 'other' => array('searchterm' => $search)
  118. );
  119. $event = \mod_forum\event\course_searched::create($params);
  120. $event->trigger();
  121. $strforums = get_string("modulenameplural", "forum");
  122. $strsearch = get_string("search", "forum");
  123. $strsearchresults = get_string("searchresults", "forum");
  124. $strpage = get_string("page");
  125. if (!$search || $showform) {
  126. $url = new moodle_url('/mod/forum/index.php', array('id' => $course->id));
  127. $PAGE->navbar->add($strforums, $url);
  128. $url = new moodle_url('/mod/forum/search.php', array('id' => $course->id));
  129. $PAGE->navbar->add(get_string('advancedsearch', 'forum'), $url);
  130. $PAGE->set_title($strsearch);
  131. $PAGE->set_heading($course->fullname);
  132. echo $OUTPUT->header();
  133. forum_print_big_search_form($course);
  134. echo $OUTPUT->footer();
  135. exit;
  136. }
  137. /// We need to do a search now and print results
  138. $searchterms = str_replace('forumid:', 'instance:', $search);
  139. $searchterms = explode(' ', $searchterms);
  140. $searchform = forum_search_form($course, $search);
  141. $PAGE->navbar->add($strsearch, new moodle_url('/mod/forum/search.php', array('id'=>$course->id)));
  142. $PAGE->navbar->add($strsearchresults);
  143. if (!$posts = forum_search_posts($searchterms, $course->id, $page*$perpage, $perpage, $totalcount)) {
  144. $PAGE->set_title($strsearchresults);
  145. $PAGE->set_heading($course->fullname);
  146. echo $OUTPUT->header();
  147. if (!$PAGE->has_secondary_navigation()) {
  148. echo $OUTPUT->heading($strforums, 2);
  149. }
  150. echo $OUTPUT->heading($strsearchresults, 3);
  151. echo $OUTPUT->heading(get_string("noposts", "forum"), 4);
  152. if (!$individualparams) {
  153. $words = $search;
  154. }
  155. forum_print_big_search_form($course);
  156. echo $OUTPUT->footer();
  157. exit;
  158. }
  159. //including this here to prevent it being included if there are no search results
  160. require_once($CFG->dirroot.'/rating/lib.php');
  161. //set up the ratings information that will be the same for all posts
  162. $ratingoptions = new stdClass();
  163. $ratingoptions->component = 'mod_forum';
  164. $ratingoptions->ratingarea = 'post';
  165. $ratingoptions->userid = $USER->id;
  166. $ratingoptions->returnurl = $PAGE->url->out(false);
  167. $rm = new rating_manager();
  168. $PAGE->set_title($strsearchresults);
  169. $PAGE->set_heading($course->fullname);
  170. $PAGE->add_header_action($searchform);
  171. echo $OUTPUT->header();
  172. echo '<div class="reportlink">';
  173. $params = [
  174. 'id' => $course->id,
  175. 'user' => $user,
  176. 'userid' => $userid,
  177. 'forumid' => $forumid,
  178. 'subject' => $subject,
  179. 'phrase' => $phrase,
  180. 'words' => $words,
  181. 'fullwords' => $fullwords,
  182. 'notwords' => $notwords,
  183. 'dateto' => $dateto,
  184. 'datefrom' => $datefrom,
  185. 'showform' => 1,
  186. 'starredonly' => $starredonly
  187. ];
  188. $url = new moodle_url("/mod/forum/search.php", $params);
  189. foreach ($tags as $tag) {
  190. $url .= "&tags[]=$tag";
  191. }
  192. echo html_writer::link($url, get_string('advancedsearch', 'forum').'...');
  193. echo '</div>';
  194. echo $OUTPUT->heading($strforums, 2);
  195. echo $OUTPUT->heading("$strsearchresults: $totalcount", 3);
  196. $url = new moodle_url('search.php', array('search' => $search, 'id' => $course->id, 'perpage' => $perpage));
  197. echo $OUTPUT->paging_bar($totalcount, $page, $perpage, $url);
  198. //added to implement highlighting of search terms found only in HTML markup
  199. //fiedorow - 9/2/2005
  200. $strippedsearch = str_replace('user:','',$search);
  201. $strippedsearch = str_replace('subject:','',$strippedsearch);
  202. $strippedsearch = str_replace('&quot;','',$strippedsearch);
  203. $searchterms = explode(' ', $strippedsearch); // Search for words independently
  204. foreach ($searchterms as $key => $searchterm) {
  205. if (preg_match('/^\-/',$searchterm)) {
  206. unset($searchterms[$key]);
  207. } else {
  208. $searchterms[$key] = preg_replace('/^\+/','',$searchterm);
  209. }
  210. }
  211. $strippedsearch = implode(' ', $searchterms); // Rebuild the string
  212. $entityfactory = mod_forum\local\container::get_entity_factory();
  213. $vaultfactory = mod_forum\local\container::get_vault_factory();
  214. $rendererfactory = mod_forum\local\container::get_renderer_factory();
  215. $managerfactory = mod_forum\local\container::get_manager_factory();
  216. $legacydatamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory();
  217. $forumdatamapper = $legacydatamapperfactory->get_forum_data_mapper();
  218. $discussionvault = $vaultfactory->get_discussion_vault();
  219. $discussionids = array_keys(array_reduce($posts, function($carry, $post) {
  220. $carry[$post->discussion] = true;
  221. return $carry;
  222. }, []));
  223. $discussions = $discussionvault->get_from_ids($discussionids);
  224. $discussionsbyid = array_reduce($discussions, function($carry, $discussion) {
  225. $carry[$discussion->get_id()] = $discussion;
  226. return $carry;
  227. }, []);
  228. $forumvault = $vaultfactory->get_forum_vault();
  229. $forumids = array_keys(array_reduce($discussions, function($carry, $discussion) {
  230. $carry[$discussion->get_forum_id()] = true;
  231. return $carry;
  232. }, []));
  233. $forums = $forumvault->get_from_ids($forumids);
  234. $forumsbyid = array_reduce($forums, function($carry, $forum) {
  235. $carry[$forum->get_id()] = $forum;
  236. return $carry;
  237. }, []);
  238. $postids = array_map(function($post) {
  239. return $post->id;
  240. }, $posts);
  241. $poststorender = [];
  242. foreach ($posts as $post) {
  243. // Replace the simple subject with the three items forum name -> thread name -> subject
  244. // (if all three are appropriate) each as a link.
  245. if (!isset($discussionsbyid[$post->discussion])) {
  246. print_error('invaliddiscussionid', 'forum');
  247. }
  248. $discussion = $discussionsbyid[$post->discussion];
  249. if (!isset($forumsbyid[$discussion->get_forum_id()])) {
  250. print_error('invalidforumid', 'forum');
  251. }
  252. $forum = $forumsbyid[$discussion->get_forum_id()];
  253. $capabilitymanager = $managerfactory->get_capability_manager($forum);
  254. $postentity = $entityfactory->get_post_from_stdclass($post);
  255. if (!$capabilitymanager->can_view_post($USER, $discussion, $postentity)) {
  256. // Don't render posts that the user can't view.
  257. continue;
  258. }
  259. if ($postentity->is_deleted()) {
  260. // Don't render deleted posts.
  261. continue;
  262. }
  263. $poststorender[] = $postentity;
  264. }
  265. $renderer = $rendererfactory->get_posts_search_results_renderer($searchterms);
  266. echo $renderer->render(
  267. $USER,
  268. $forumsbyid,
  269. $discussionsbyid,
  270. $poststorender
  271. );
  272. echo $OUTPUT->paging_bar($totalcount, $page, $perpage, $url);
  273. echo $OUTPUT->footer();
  274. /**
  275. * Print a full-sized search form for the specified course.
  276. *
  277. * @param stdClass $course The Course that will be searched.
  278. * @return void The function prints the form.
  279. */
  280. function forum_print_big_search_form($course) {
  281. global $PAGE, $words, $subject, $phrase, $user, $fullwords, $notwords, $datefrom,
  282. $dateto, $forumid, $tags, $starredonly;
  283. $renderable = new \mod_forum\output\big_search_form($course, $user);
  284. $renderable->set_words($words);
  285. $renderable->set_phrase($phrase);
  286. $renderable->set_notwords($notwords);
  287. $renderable->set_fullwords($fullwords);
  288. $renderable->set_datefrom($datefrom);
  289. $renderable->set_dateto($dateto);
  290. $renderable->set_subject($subject);
  291. $renderable->set_user($user);
  292. $renderable->set_forumid($forumid);
  293. $renderable->set_tags($tags);
  294. $renderable->set_starredonly($starredonly);
  295. $output = $PAGE->get_renderer('mod_forum');
  296. echo $output->render($renderable);
  297. }
  298. /**
  299. * This function takes each word out of the search string, makes sure they are at least
  300. * two characters long and returns an string of the space-separated search
  301. * terms.
  302. *
  303. * @param string $words String containing space-separated strings to search for.
  304. * @param string $prefix String to prepend to the each token taken out of $words.
  305. * @return string The filtered search terms, separated by spaces.
  306. * @todo Take the hardcoded limit out of this function and put it into a user-specified parameter.
  307. */
  308. function forum_clean_search_terms($words, $prefix='') {
  309. $searchterms = explode(' ', $words);
  310. foreach ($searchterms as $key => $searchterm) {
  311. if (strlen($searchterm) < 2) {
  312. unset($searchterms[$key]);
  313. } else if ($prefix) {
  314. $searchterms[$key] = $prefix.$searchterm;
  315. }
  316. }
  317. return trim(implode(' ', $searchterms));
  318. }
  319. /**
  320. * Retrieve a list of the forums that this user can view.
  321. *
  322. * @param stdClass $course The Course to use.
  323. * @return array A set of formatted forum names stored against the forum id.
  324. */
  325. function forum_menu_list($course) {
  326. $menu = array();
  327. $modinfo = get_fast_modinfo($course);
  328. if (empty($modinfo->instances['forum'])) {
  329. return $menu;
  330. }
  331. foreach ($modinfo->instances['forum'] as $cm) {
  332. if (!$cm->uservisible) {
  333. continue;
  334. }
  335. $context = context_module::instance($cm->id);
  336. if (!has_capability('mod/forum:viewdiscussion', $context)) {
  337. continue;
  338. }
  339. $menu[$cm->instance] = format_string($cm->name);
  340. }
  341. return $menu;
  342. }