Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

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