Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 9 May 2022 (12 months).
  • Bug fixes for security issues in 3.11.x will end 14 November 2022 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • Differences Between: [Versions 310 and 311] [Versions 35 and 311] [Versions 36 and 311] [Versions 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

       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  /**
      18   * @package   mod_forum
      19   * @copyright 2014 Andrew Robert Nicols <andrew@nicols.co.uk>
      20   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      21   */
      22  
      23  defined('MOODLE_INTERNAL') || die();
      24  
      25  // Deprecated a very long time ago.
      26  
      27  /**
      28   * @deprecated since Moodle 1.1 - please do not use this function any more.
      29   */
      30  function forum_count_unrated_posts() {
      31      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      32  }
      33  
      34  
      35  // Since Moodle 1.5.
      36  
      37  /**
      38   * @deprecated since Moodle 1.5 - please do not use this function any more.
      39   */
      40  function forum_tp_count_discussion_read_records() {
      41      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      42  }
      43  
      44  /**
      45   * @deprecated since Moodle 1.5 - please do not use this function any more.
      46   */
      47  function forum_get_user_discussions() {
      48      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      49  }
      50  
      51  
      52  // Since Moodle 1.6.
      53  
      54  /**
      55   * @deprecated since Moodle 1.6 - please do not use this function any more.
      56   */
      57  function forum_tp_count_forum_posts() {
      58      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      59  }
      60  
      61  /**
      62   * @deprecated since Moodle 1.6 - please do not use this function any more.
      63   */
      64  function forum_tp_count_forum_read_records() {
      65      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      66  }
      67  
      68  
      69  // Since Moodle 1.7.
      70  
      71  /**
      72   * @deprecated since Moodle 1.7 - please do not use this function any more.
      73   */
      74  function forum_get_open_modes() {
      75      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      76  }
      77  
      78  
      79  // Since Moodle 1.9.
      80  
      81  /**
      82   * @deprecated since Moodle 1.9 MDL-13303 - please do not use this function any more.
      83   */
      84  function forum_get_child_posts() {
      85      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      86  }
      87  
      88  /**
      89   * @deprecated since Moodle 1.9 MDL-13303 - please do not use this function any more.
      90   */
      91  function forum_get_discussion_posts() {
      92      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
      93  }
      94  
      95  
      96  // Since Moodle 2.0.
      97  
      98  /**
      99   * @deprecated since Moodle 2.0 MDL-21657 - please do not use this function any more.
     100   */
     101  function forum_get_ratings() {
     102      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     103  }
     104  
     105  /**
     106   * @deprecated since Moodle 2.0 MDL-14632 - please do not use this function any more.
     107   */
     108  function forum_get_tracking_link() {
     109      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     110  }
     111  
     112  /**
     113   * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
     114   */
     115  function forum_tp_count_discussion_unread_posts() {
     116      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     117  }
     118  
     119  /**
     120   * @deprecated since Moodle 2.0 MDL-23479 - please do not use this function any more.
     121   */
     122  function forum_convert_to_roles() {
     123      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     124  }
     125  
     126  /**
     127   * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
     128   */
     129  function forum_tp_get_read_records() {
     130      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     131  }
     132  
     133  /**
     134   * @deprecated since Moodle 2.0 MDL-14113 - please do not use this function any more.
     135   */
     136  function forum_tp_get_discussion_read_records() {
     137      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     138  }
     139  
     140  // Deprecated in 2.3.
     141  
     142  /**
     143   * @deprecated since Moodle 2.3 MDL-33166 - please do not use this function any more.
     144   */
     145  function forum_user_enrolled() {
     146      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     147  }
     148  
     149  
     150  // Deprecated in 2.4.
     151  
     152  /**
     153   * @deprecated since Moodle 2.4 use forum_user_can_see_post() instead
     154   */
     155  function forum_user_can_view_post() {
     156      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     157  }
     158  
     159  
     160  // Deprecated in 2.6.
     161  
     162  /**
     163   * FORUM_TRACKING_ON - deprecated alias for FORUM_TRACKING_FORCED.
     164   * @deprecated since 2.6
     165   */
     166  define('FORUM_TRACKING_ON', 2);
     167  
     168  /**
     169   * @deprecated since Moodle 2.6
     170   * @see shorten_text()
     171   */
     172  function forum_shorten_post($message) {
     173      throw new coding_exception(__FUNCTION__ . '() can not be used any more. '
     174          . 'Please use shorten_text($message, $CFG->forum_shortpost) instead.');
     175  }
     176  
     177  // Deprecated in 2.8.
     178  
     179  /**
     180   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_subscribed() instead
     181   */
     182  function forum_is_subscribed() {
     183      throw new coding_exception(__FUNCTION__ . '() can not be used any more.');
     184  }
     185  
     186  /**
     187   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::subscribe_user() instead
     188   */
     189  function forum_subscribe() {
     190      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     191          . \mod_forum\subscriptions::class . '::subscribe_user() instead');
     192  }
     193  
     194  /**
     195   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::unsubscribe_user() instead
     196   */
     197  function forum_unsubscribe() {
     198      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     199          . \mod_forum\subscriptions::class . '::unsubscribe_user() instead');
     200  }
     201  
     202  /**
     203   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::fetch_subscribed_users() instead
     204    */
     205  function forum_subscribed_users() {
     206      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     207          . \mod_forum\subscriptions::class . '::fetch_subscribed_users() instead');
     208  }
     209  
     210  /**
     211   * Determine whether the forum is force subscribed.
     212   *
     213   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_forcesubscribed() instead
     214   */
     215  function forum_is_forcesubscribed($forum) {
     216      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     217          . \mod_forum\subscriptions::class . '::is_forcesubscribed() instead');
     218  }
     219  
     220  /**
     221   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::set_subscription_mode() instead
     222   */
     223  function forum_forcesubscribe($forumid, $value = 1) {
     224      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     225          . \mod_forum\subscriptions::class . '::set_subscription_mode() instead');
     226  }
     227  
     228  /**
     229   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_subscription_mode() instead
     230   */
     231  function forum_get_forcesubscribed($forum) {
     232      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     233          . \mod_forum\subscriptions::class . '::set_subscription_mode() instead');
     234  }
     235  
     236  /**
     237   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::is_subscribed in combination wtih
     238   * \mod_forum\subscriptions::fill_subscription_cache_for_course instead.
     239   */
     240  function forum_get_subscribed_forums() {
     241      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     242          . \mod_forum\subscriptions::class . '::is_subscribed(), and '
     243          . \mod_forum\subscriptions::class . '::fill_subscription_cache_for_course() instead');
     244  }
     245  
     246  /**
     247   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_unsubscribable_forums() instead
     248   */
     249  function forum_get_optional_subscribed_forums() {
     250      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     251          . \mod_forum\subscriptions::class . '::get_unsubscribable_forums() instead');
     252  }
     253  
     254  /**
     255   * @deprecated since Moodle 2.8 use \mod_forum\subscriptions::get_potential_subscribers() instead
     256   */
     257  function forum_get_potential_subscribers() {
     258      throw new coding_exception(__FUNCTION__ . '() can not be used any more. Please use '
     259          . \mod_forum\subscriptions::class . '::get_potential_subscribers() instead');
     260  }
     261  
     262  /**
     263   * Builds and returns the body of the email notification in plain text.
     264   *
     265   * @uses CONTEXT_MODULE
     266   * @param object $course
     267   * @param object $cm
     268   * @param object $forum
     269   * @param object $discussion
     270   * @param object $post
     271   * @param object $userfrom
     272   * @param object $userto
     273   * @param boolean $bare
     274   * @param string $replyaddress The inbound address that a user can reply to the generated e-mail with. [Since 2.8].
     275   * @return string The email body in plain text format.
     276   * @deprecated since Moodle 3.0 use \mod_forum\output\forum_post_email instead
     277   */
     278  function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $bare = false, $replyaddress = null) {
     279      global $PAGE;
     280      $renderable = new \mod_forum\output\forum_post_email(
     281          $course,
     282          $cm,
     283          $forum,
     284          $discussion,
     285          $post,
     286          $userfrom,
     287          $userto,
     288          forum_user_can_post($forum, $discussion, $userto, $cm, $course)
     289          );
     290  
     291      $modcontext = context_module::instance($cm->id);
     292      $renderable->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
     293  
     294      if ($bare) {
     295          $renderer = $PAGE->get_renderer('mod_forum', 'emaildigestfull', 'textemail');
     296      } else {
     297          $renderer = $PAGE->get_renderer('mod_forum', 'email', 'textemail');
     298      }
     299  
     300      debugging("forum_make_mail_text() has been deprecated, please use the \mod_forum\output\forum_post_email renderable instead.",
     301              DEBUG_DEVELOPER);
     302  
     303      return $renderer->render($renderable);
     304  }
     305  
     306  /**
     307   * Builds and returns the body of the email notification in html format.
     308   *
     309   * @param object $course
     310   * @param object $cm
     311   * @param object $forum
     312   * @param object $discussion
     313   * @param object $post
     314   * @param object $userfrom
     315   * @param object $userto
     316   * @param string $replyaddress The inbound address that a user can reply to the generated e-mail with. [Since 2.8].
     317   * @return string The email text in HTML format
     318   * @deprecated since Moodle 3.0 use \mod_forum\output\forum_post_email instead
     319   */
     320  function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $replyaddress = null) {
     321      return forum_make_mail_post($course,
     322          $cm,
     323          $forum,
     324          $discussion,
     325          $post,
     326          $userfrom,
     327          $userto,
     328          forum_user_can_post($forum, $discussion, $userto, $cm, $course)
     329      );
     330  }
     331  
     332  /**
     333   * Given the data about a posting, builds up the HTML to display it and
     334   * returns the HTML in a string.  This is designed for sending via HTML email.
     335   *
     336   * @param object $course
     337   * @param object $cm
     338   * @param object $forum
     339   * @param object $discussion
     340   * @param object $post
     341   * @param object $userfrom
     342   * @param object $userto
     343   * @param bool $ownpost
     344   * @param bool $reply
     345   * @param bool $link
     346   * @param bool $rate
     347   * @param string $footer
     348   * @return string
     349   * @deprecated since Moodle 3.0 use \mod_forum\output\forum_post_email instead
     350   */
     351  function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto,
     352                                $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
     353      global $PAGE;
     354      $renderable = new \mod_forum\output\forum_post_email(
     355          $course,
     356          $cm,
     357          $forum,
     358          $discussion,
     359          $post,
     360          $userfrom,
     361          $userto,
     362          $reply);
     363  
     364      $modcontext = context_module::instance($cm->id);
     365      $renderable->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
     366  
     367      // Assume that this is being used as a standard forum email.
     368      $renderer = $PAGE->get_renderer('mod_forum', 'email', 'htmlemail');
     369  
     370      debugging("forum_make_mail_post() has been deprecated, please use the \mod_forum\output\forum_post_email renderable instead.",
     371              DEBUG_DEVELOPER);
     372  
     373      return $renderer->render($renderable);
     374  }
     375  
     376  /**
     377   * Removes properties from user record that are not necessary for sending post notifications.
     378   *
     379   * @param stdClass $user
     380   * @return void, $user parameter is modified
     381   * @deprecated since Moodle 3.7
     382   */
     383  function forum_cron_minimise_user_record(stdClass $user) {
     384      debugging("forum_cron_minimise_user_record() has been deprecated and has not been replaced.",
     385              DEBUG_DEVELOPER);
     386  
     387      // We store large amount of users in one huge array,
     388      // make sure we do not store info there we do not actually need
     389      // in mail generation code or messaging.
     390  
     391      unset($user->institution);
     392      unset($user->department);
     393      unset($user->address);
     394      unset($user->city);
     395      unset($user->currentlogin);
     396      unset($user->description);
     397      unset($user->descriptionformat);
     398  }
     399  
     400  /**
     401   * Function to be run periodically according to the scheduled task.
     402   *
     403   * Finds all posts that have yet to be mailed out, and mails them out to all subscribers as well as other maintance
     404   * tasks.
     405   *
     406   * @deprecated since Moodle 3.7
     407   */
     408  function forum_cron() {
     409      debugging("forum_cron() has been deprecated and replaced with new tasks. Please uses these instead.",
     410              DEBUG_DEVELOPER);
     411  }
     412  
     413  /**
     414   * Prints a forum discussion
     415   *
     416   * @uses CONTEXT_MODULE
     417   * @uses FORUM_MODE_FLATNEWEST
     418   * @uses FORUM_MODE_FLATOLDEST
     419   * @uses FORUM_MODE_THREADED
     420   * @uses FORUM_MODE_NESTED
     421   * @param stdClass $course
     422   * @param stdClass $cm
     423   * @param stdClass $forum
     424   * @param stdClass $discussion
     425   * @param stdClass $post
     426   * @param int $mode
     427   * @param mixed $canreply
     428   * @param bool $canrate
     429   * @deprecated since Moodle 3.7
     430   */
     431  function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
     432      debugging('forum_print_discussion() has been deprecated, ' .
     433          'please use \mod_forum\local\renderers\discussion instead.', DEBUG_DEVELOPER);
     434  
     435      global $USER, $CFG;
     436  
     437      require_once($CFG->dirroot.'/rating/lib.php');
     438  
     439      $ownpost = (isloggedin() && $USER->id == $post->userid);
     440  
     441      $modcontext = context_module::instance($cm->id);
     442      if ($canreply === NULL) {
     443          $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
     444      } else {
     445          $reply = $canreply;
     446      }
     447  
     448      // $cm holds general cache for forum functions
     449      $cm->cache = new stdClass;
     450      $cm->cache->groups      = groups_get_all_groups($course->id, 0, $cm->groupingid);
     451      $cm->cache->usersgroups = array();
     452  
     453      $posters = array();
     454  
     455      // preload all posts - TODO: improve...
     456      if ($mode == FORUM_MODE_FLATNEWEST) {
     457          $sort = "p.created DESC";
     458      } else {
     459          $sort = "p.created ASC";
     460      }
     461  
     462      $forumtracked = forum_tp_is_tracked($forum);
     463      $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
     464      $post = $posts[$post->id];
     465  
     466      foreach ($posts as $pid=>$p) {
     467          $posters[$p->userid] = $p->userid;
     468      }
     469  
     470      // preload all groups of ppl that posted in this discussion
     471      if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) {
     472          foreach($postersgroups as $pg) {
     473              if (!isset($cm->cache->usersgroups[$pg->userid])) {
     474                  $cm->cache->usersgroups[$pg->userid] = array();
     475              }
     476              $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid;
     477          }
     478          unset($postersgroups);
     479      }
     480  
     481      //load ratings
     482      if ($forum->assessed != RATING_AGGREGATE_NONE) {
     483          $ratingoptions = new stdClass;
     484          $ratingoptions->context = $modcontext;
     485          $ratingoptions->component = 'mod_forum';
     486          $ratingoptions->ratingarea = 'post';
     487          $ratingoptions->items = $posts;
     488          $ratingoptions->aggregate = $forum->assessed;//the aggregation method
     489          $ratingoptions->scaleid = $forum->scale;
     490          $ratingoptions->userid = $USER->id;
     491          if ($forum->type == 'single' or !$discussion->id) {
     492              $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id";
     493          } else {
     494              $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
     495          }
     496          $ratingoptions->assesstimestart = $forum->assesstimestart;
     497          $ratingoptions->assesstimefinish = $forum->assesstimefinish;
     498  
     499          $rm = new rating_manager();
     500          $posts = $rm->get_ratings($ratingoptions);
     501      }
     502  
     503  
     504      $post->forum = $forum->id;   // Add the forum id to the post object, later used by forum_print_post
     505      $post->forumtype = $forum->type;
     506  
     507      $post->subject = format_string($post->subject);
     508  
     509      $postread = !empty($post->postread);
     510  
     511      forum_print_post_start($post);
     512      forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false,
     513                           '', '', $postread, true, $forumtracked);
     514  
     515      switch ($mode) {
     516          case FORUM_MODE_FLATOLDEST :
     517          case FORUM_MODE_FLATNEWEST :
     518          default:
     519              forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts);
     520              break;
     521  
     522          case FORUM_MODE_THREADED :
     523              forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts);
     524              break;
     525  
     526          case FORUM_MODE_NESTED :
     527              forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
     528              break;
     529      }
     530      forum_print_post_end($post);
     531  }
     532  
     533  
     534  /**
     535   * Return a static array of posts that are open.
     536   *
     537   * @return array
     538   * @deprecated since Moodle 3.7
     539   */
     540  function forum_post_nesting_cache() {
     541      debugging('forum_post_nesting_cache() has been deprecated, ' .
     542          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     543      static $nesting = array();
     544      return $nesting;
     545  }
     546  
     547  /**
     548   * Return true for the first time this post was started
     549   *
     550   * @param int $id The id of the post to start
     551   * @return bool
     552   * @deprecated since Moodle 3.7
     553   */
     554  function forum_should_start_post_nesting($id) {
     555      debugging('forum_should_start_post_nesting() has been deprecated, ' .
     556          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     557      $cache = forum_post_nesting_cache();
     558      if (!array_key_exists($id, $cache)) {
     559          $cache[$id] = 1;
     560          return true;
     561      } else {
     562          $cache[$id]++;
     563          return false;
     564      }
     565  }
     566  
     567  /**
     568   * Return true when all the opens are nested with a close.
     569   *
     570   * @param int $id The id of the post to end
     571   * @return bool
     572   * @deprecated since Moodle 3.7
     573   */
     574  function forum_should_end_post_nesting($id) {
     575      debugging('forum_should_end_post_nesting() has been deprecated, ' .
     576          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     577      $cache = forum_post_nesting_cache();
     578      if (!array_key_exists($id, $cache)) {
     579          return true;
     580      } else {
     581          $cache[$id]--;
     582          if ($cache[$id] == 0) {
     583              unset($cache[$id]);
     584              return true;
     585          }
     586      }
     587      return false;
     588  }
     589  
     590  /**
     591   * Start a forum post container
     592   *
     593   * @param object $post The post to print.
     594   * @param bool $return Return the string or print it
     595   * @return string
     596   * @deprecated since Moodle 3.7
     597   */
     598  function forum_print_post_start($post, $return = false) {
     599      debugging('forum_print_post_start() has been deprecated, ' .
     600          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     601      $output = '';
     602  
     603      if (forum_should_start_post_nesting($post->id)) {
     604          $attributes = [
     605              'id' => 'p'.$post->id,
     606              'tabindex' => -1,
     607              'class' => 'relativelink'
     608          ];
     609          $output .= html_writer::start_tag('article', $attributes);
     610      }
     611      if ($return) {
     612          return $output;
     613      }
     614      echo $output;
     615      return;
     616  }
     617  
     618  /**
     619   * End a forum post container
     620   *
     621   * @param object $post The post to print.
     622   * @param bool $return Return the string or print it
     623   * @return string
     624   * @deprecated since Moodle 3.7
     625   */
     626  function forum_print_post_end($post, $return = false) {
     627      debugging('forum_print_post_end() has been deprecated, ' .
     628          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     629      $output = '';
     630  
     631      if (forum_should_end_post_nesting($post->id)) {
     632          $output .= html_writer::end_tag('article');
     633      }
     634      if ($return) {
     635          return $output;
     636      }
     637      echo $output;
     638      return;
     639  }
     640  
     641  /**
     642   * Print a forum post
     643   * This function should always be surrounded with calls to forum_print_post_start
     644   * and forum_print_post_end to create the surrounding container for the post.
     645   * Replies can be nested before forum_print_post_end and should reflect the structure of
     646   * thread.
     647   *
     648   * @global object
     649   * @global object
     650   * @uses FORUM_MODE_THREADED
     651   * @uses PORTFOLIO_FORMAT_PLAINHTML
     652   * @uses PORTFOLIO_FORMAT_FILE
     653   * @uses PORTFOLIO_FORMAT_RICHHTML
     654   * @uses PORTFOLIO_ADD_TEXT_LINK
     655   * @uses CONTEXT_MODULE
     656   * @param object $post The post to print.
     657   * @param object $discussion
     658   * @param object $forum
     659   * @param object $cm
     660   * @param object $course
     661   * @param boolean $ownpost Whether this post belongs to the current user.
     662   * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
     663   * @param boolean $link Just print a shortened version of the post as a link to the full post.
     664   * @param string $footer Extra stuff to print after the message.
     665   * @param string $highlight Space-separated list of terms to highlight.
     666   * @param int $post_read true, false or -99. If we already know whether this user
     667   *          has read this post, pass that in, otherwise, pass in -99, and this
     668   *          function will work it out.
     669   * @param boolean $dummyifcantsee When forum_user_can_see_post says that
     670   *          the current user can't see this post, if this argument is true
     671   *          (the default) then print a dummy 'you can't see this post' post.
     672   *          If false, don't output anything at all.
     673   * @param bool|null $istracked
     674   * @return void
     675   * @deprecated since Moodle 3.7
     676   */
     677  function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
     678                            $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) {
     679      debugging('forum_print_post() has been deprecated, ' .
     680          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
     681      global $USER, $CFG, $OUTPUT;
     682  
     683      require_once($CFG->libdir . '/filelib.php');
     684  
     685      // String cache
     686      static $str;
     687      // This is an extremely hacky way to ensure we only print the 'unread' anchor
     688      // the first time we encounter an unread post on a page. Ideally this would
     689      // be moved into the caller somehow, and be better testable. But at the time
     690      // of dealing with this bug, this static workaround was the most surgical and
     691      // it fits together with only printing th unread anchor id once on a given page.
     692      static $firstunreadanchorprinted = false;
     693  
     694      $modcontext = context_module::instance($cm->id);
     695  
     696      $post->course = $course->id;
     697      $post->forum  = $forum->id;
     698      $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
     699      if (!empty($CFG->enableplagiarism)) {
     700          require_once($CFG->libdir.'/plagiarismlib.php');
     701          $post->message .= plagiarism_get_links(array('userid' => $post->userid,
     702              'content' => $post->message,
     703              'cmid' => $cm->id,
     704              'course' => $post->course,
     705              'forum' => $post->forum));
     706      }
     707  
     708      // caching
     709      if (!isset($cm->cache)) {
     710          $cm->cache = new stdClass;
     711      }
     712  
     713      if (!isset($cm->cache->caps)) {
     714          $cm->cache->caps = array();
     715          $cm->cache->caps['mod/forum:viewdiscussion']   = has_capability('mod/forum:viewdiscussion', $modcontext);
     716          $cm->cache->caps['moodle/site:viewfullnames']  = has_capability('moodle/site:viewfullnames', $modcontext);
     717          $cm->cache->caps['mod/forum:editanypost']      = has_capability('mod/forum:editanypost', $modcontext);
     718          $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext);
     719          $cm->cache->caps['mod/forum:deleteownpost']    = has_capability('mod/forum:deleteownpost', $modcontext);
     720          $cm->cache->caps['mod/forum:deleteanypost']    = has_capability('mod/forum:deleteanypost', $modcontext);
     721          $cm->cache->caps['mod/forum:viewanyrating']    = has_capability('mod/forum:viewanyrating', $modcontext);
     722          $cm->cache->caps['mod/forum:exportpost']       = has_capability('mod/forum:exportpost', $modcontext);
     723          $cm->cache->caps['mod/forum:exportownpost']    = has_capability('mod/forum:exportownpost', $modcontext);
     724      }
     725  
     726      if (!isset($cm->uservisible)) {
     727          $cm->uservisible = \core_availability\info_module::is_user_visible($cm, 0, false);
     728      }
     729  
     730      if ($istracked && is_null($postisread)) {
     731          $postisread = forum_tp_is_post_read($USER->id, $post);
     732      }
     733  
     734      if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
     735          // Do _not_ check the deleted flag - we need to display a different UI.
     736          $output = '';
     737          if (!$dummyifcantsee) {
     738              if ($return) {
     739                  return $output;
     740              }
     741              echo $output;
     742              return;
     743          }
     744  
     745          $output .= html_writer::start_tag('div', array('class' => 'forumpost clearfix',
     746                                                         'aria-label' => get_string('hiddenforumpost', 'forum')));
     747          $output .= html_writer::start_tag('header', array('class' => 'row header'));
     748          $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); // Picture.
     749          if ($post->parent) {
     750              $output .= html_writer::start_tag('div', array('class' => 'topic'));
     751          } else {
     752              $output .= html_writer::start_tag('div', array('class' => 'topic starter'));
     753          }
     754          $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class' => 'subject',
     755                                                                                             'role' => 'header',
     756                                                                                             'id' => ('headp' . $post->id))); // Subject.
     757          $authorclasses = array('class' => 'author');
     758          $output .= html_writer::tag('address', get_string('forumauthorhidden', 'forum'), $authorclasses); // Author.
     759          $output .= html_writer::end_tag('div');
     760          $output .= html_writer::end_tag('header'); // Header.
     761          $output .= html_writer::start_tag('div', array('class'=>'row'));
     762          $output .= html_writer::tag('div', '&nbsp;', array('class'=>'left side')); // Groups
     763          $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content
     764          $output .= html_writer::end_tag('div'); // row
     765          $output .= html_writer::end_tag('div'); // forumpost
     766  
     767          if ($return) {
     768              return $output;
     769          }
     770          echo $output;
     771          return;
     772      }
     773  
     774      if (!empty($post->deleted)) {
     775          // Note: Posts marked as deleted are still returned by the above forum_user_can_post because it is required for
     776          // nesting of posts.
     777          $output = '';
     778          if (!$dummyifcantsee) {
     779              if ($return) {
     780                  return $output;
     781              }
     782              echo $output;
     783              return;
     784          }
     785          $output .= html_writer::start_tag('div', [
     786                  'class' => 'forumpost clearfix',
     787                  'aria-label' => get_string('forumbodydeleted', 'forum'),
     788              ]);
     789  
     790          $output .= html_writer::start_tag('header', array('class' => 'row header'));
     791          $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation'));
     792  
     793          $classes = ['topic'];
     794          if (!empty($post->parent)) {
     795              $classes[] = 'starter';
     796          }
     797          $output .= html_writer::start_tag('div', ['class' => implode(' ', $classes)]);
     798  
     799          // Subject.
     800          $output .= html_writer::tag('div', get_string('forumsubjectdeleted', 'forum'), [
     801                  'class' => 'subject',
     802                  'role' => 'header',
     803                  'id' => ('headp' . $post->id)
     804              ]);
     805  
     806          // Author.
     807          $output .= html_writer::tag('address', '', ['class' => 'author']);
     808  
     809          $output .= html_writer::end_tag('div');
     810          $output .= html_writer::end_tag('header'); // End header.
     811          $output .= html_writer::start_tag('div', ['class' => 'row']);
     812          $output .= html_writer::tag('div', '&nbsp;', ['class' => 'left side']); // Groups.
     813          $output .= html_writer::tag('div', get_string('forumbodydeleted', 'forum'), ['class' => 'content']); // Content.
     814          $output .= html_writer::end_tag('div'); // End row.
     815          $output .= html_writer::end_tag('div'); // End forumpost.
     816  
     817          if ($return) {
     818              return $output;
     819          }
     820          echo $output;
     821          return;
     822      }
     823  
     824      if (empty($str)) {
     825          $str = new stdClass;
     826          $str->edit         = get_string('edit', 'forum');
     827          $str->delete       = get_string('delete', 'forum');
     828          $str->reply        = get_string('reply', 'forum');
     829          $str->parent       = get_string('parent', 'forum');
     830          $str->pruneheading = get_string('pruneheading', 'forum');
     831          $str->prune        = get_string('prune', 'forum');
     832          $str->displaymode     = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
     833          $str->markread     = get_string('markread', 'forum');
     834          $str->markunread   = get_string('markunread', 'forum');
     835      }
     836  
     837      $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion));
     838  
     839      // Build an object that represents the posting user
     840      $postuser = new stdClass;
     841      $postuserfields = explode(',', implode(',', \core_user\fields::get_picture_fields()));
     842      $postuser = username_load_fields_from_object($postuser, $post, null, $postuserfields);
     843      $postuser->id = $post->userid;
     844      $postuser->fullname    = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']);
     845      $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id));
     846  
     847      // Prepare the groups the posting user belongs to
     848      if (isset($cm->cache->usersgroups)) {
     849          $groups = array();
     850          if (isset($cm->cache->usersgroups[$post->userid])) {
     851              foreach ($cm->cache->usersgroups[$post->userid] as $gid) {
     852                  $groups[$gid] = $cm->cache->groups[$gid];
     853              }
     854          }
     855      } else {
     856          $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid);
     857      }
     858  
     859      // Prepare the attachements for the post, files then images
     860      list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages');
     861  
     862      // Determine if we need to shorten this post
     863      $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost));
     864  
     865      // Prepare an array of commands
     866      $commands = array();
     867  
     868      // Add a permalink.
     869      $permalink = new moodle_url($discussionlink);
     870      $permalink->set_anchor('p' . $post->id);
     871      $commands[] = array('url' => $permalink, 'text' => get_string('permalink', 'forum'), 'attributes' => ['rel' => 'bookmark']);
     872  
     873      // SPECIAL CASE: The front page can display a news item post to non-logged in users.
     874      // Don't display the mark read / unread controls in this case.
     875      if ($istracked && $CFG->forum_usermarksread && isloggedin()) {
     876          $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread'));
     877          $text = $str->markunread;
     878          if (!$postisread) {
     879              $url->param('mark', 'read');
     880              $text = $str->markread;
     881          }
     882          if ($str->displaymode == FORUM_MODE_THREADED) {
     883              $url->param('parent', $post->parent);
     884          } else {
     885              $url->set_anchor('p'.$post->id);
     886          }
     887          $commands[] = array('url'=>$url, 'text'=>$text, 'attributes' => ['rel' => 'bookmark']);
     888      }
     889  
     890      // Zoom in to the parent specifically
     891      if ($post->parent) {
     892          $url = new moodle_url($discussionlink);
     893          if ($str->displaymode == FORUM_MODE_THREADED) {
     894              $url->param('parent', $post->parent);
     895          } else {
     896              $url->set_anchor('p'.$post->parent);
     897          }
     898          $commands[] = array('url'=>$url, 'text'=>$str->parent, 'attributes' => ['rel' => 'bookmark']);
     899      }
     900  
     901      // Hack for allow to edit news posts those are not displayed yet until they are displayed
     902      $age = time() - $post->created;
     903      if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) {
     904          $age = 0;
     905      }
     906  
     907      if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
     908          if (has_capability('moodle/course:manageactivities', $modcontext)) {
     909              // The first post in single simple is the forum description.
     910              $commands[] = array('url'=>new moodle_url('/course/modedit.php', array('update'=>$cm->id, 'sesskey'=>sesskey(), 'return'=>1)), 'text'=>$str->edit);
     911          }
     912      } else if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) {
     913          $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit);
     914      }
     915  
     916      if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') {
     917          $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading);
     918      }
     919  
     920      if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
     921          // Do not allow deleting of first post in single simple type.
     922      } else if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) {
     923          $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete);
     924      }
     925  
     926      if ($reply) {
     927          $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply);
     928      }
     929  
     930      if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) {
     931          $p = array('postid' => $post->id);
     932          require_once($CFG->libdir.'/portfoliolib.php');
     933          $button = new portfolio_add_button();
     934          $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), 'mod_forum');
     935          if (empty($attachments)) {
     936              $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
     937          } else {
     938              $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
     939          }
     940  
     941          $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
     942          if (!empty($porfoliohtml)) {
     943              $commands[] = $porfoliohtml;
     944          }
     945      }
     946      // Finished building commands
     947  
     948  
     949      // Begin output
     950  
     951      $output  = '';
     952  
     953      if ($istracked) {
     954          if ($postisread) {
     955              $forumpostclass = ' read';
     956          } else {
     957              $forumpostclass = ' unread';
     958              // If this is the first unread post printed then give it an anchor and id of unread.
     959              if (!$firstunreadanchorprinted) {
     960                  $output .= html_writer::tag('a', '', array('id' => 'unread'));
     961                  $firstunreadanchorprinted = true;
     962              }
     963          }
     964      } else {
     965          // ignore trackign status if not tracked or tracked param missing
     966          $forumpostclass = '';
     967      }
     968  
     969      $topicclass = '';
     970      if (empty($post->parent)) {
     971          $topicclass = ' firstpost starter';
     972      }
     973  
     974      if (!empty($post->lastpost)) {
     975          $forumpostclass .= ' lastpost';
     976      }
     977  
     978      // Flag to indicate whether we should hide the author or not.
     979      $authorhidden = forum_is_author_hidden($post, $forum);
     980      $postbyuser = new stdClass;
     981      $postbyuser->post = $post->subject;
     982      $postbyuser->user = $postuser->fullname;
     983      $discussionbyuser = get_string('postbyuser', 'forum', $postbyuser);
     984      // Begin forum post.
     985      $output .= html_writer::start_div('forumpost clearfix' . $forumpostclass . $topicclass,
     986          ['aria-label' => $discussionbyuser]);
     987      // Begin header row.
     988      $output .= html_writer::start_tag('header', ['class' => 'row header clearfix']);
     989  
     990      // User picture.
     991      if (!$authorhidden) {
     992          $picture = $OUTPUT->user_picture($postuser, ['courseid' => $course->id]);
     993          $output .= html_writer::div($picture, 'left picture', ['role' => 'presentation']);
     994          $topicclass = 'topic' . $topicclass;
     995      }
     996  
     997      // Begin topic column.
     998      $output .= html_writer::start_div($topicclass);
     999      $postsubject = $post->subject;
    1000      if (empty($post->subjectnoformat)) {
    1001          $postsubject = format_string($postsubject);
    1002      }
    1003      $output .= html_writer::div($postsubject, 'subject', ['role' => 'heading', 'aria-level' => '1', 'id' => ('headp' . $post->id)]);
    1004  
    1005      if ($authorhidden) {
    1006          $bytext = userdate_htmltime($post->created);
    1007      } else {
    1008          $by = new stdClass();
    1009          $by->date = userdate_htmltime($post->created);
    1010          $by->name = html_writer::link($postuser->profilelink, $postuser->fullname);
    1011          $bytext = get_string('bynameondate', 'forum', $by);
    1012      }
    1013      $bytextoptions = [
    1014          'class' => 'author'
    1015      ];
    1016      $output .= html_writer::tag('address', $bytext, $bytextoptions);
    1017      // End topic column.
    1018      $output .= html_writer::end_div();
    1019  
    1020      // End header row.
    1021      $output .= html_writer::end_tag('header');
    1022  
    1023      // Row with the forum post content.
    1024      $output .= html_writer::start_div('row maincontent clearfix');
    1025      // Show if author is not hidden or we have groups.
    1026      if (!$authorhidden || $groups) {
    1027          $output .= html_writer::start_div('left');
    1028          $groupoutput = '';
    1029          if ($groups) {
    1030              $groupoutput = print_group_picture($groups, $course->id, false, true, true);
    1031          }
    1032          if (empty($groupoutput)) {
    1033              $groupoutput = '&nbsp;';
    1034          }
    1035          $output .= html_writer::div($groupoutput, 'grouppictures');
    1036          $output .= html_writer::end_div(); // Left side.
    1037      }
    1038  
    1039      $output .= html_writer::start_tag('div', array('class'=>'no-overflow'));
    1040      $output .= html_writer::start_tag('div', array('class'=>'content'));
    1041  
    1042      $options = new stdClass;
    1043      $options->para    = false;
    1044      $options->trusted = $post->messagetrust;
    1045      $options->context = $modcontext;
    1046      if ($shortenpost) {
    1047          // Prepare shortened version by filtering the text then shortening it.
    1048          $postclass    = 'shortenedpost';
    1049          $postcontent  = format_text($post->message, $post->messageformat, $options);
    1050          $postcontent  = shorten_text($postcontent, $CFG->forum_shortpost);
    1051          $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum'));
    1052          $postcontent .= html_writer::tag('div', '('.get_string('numwords', 'moodle', count_words($post->message)).')',
    1053              array('class'=>'post-word-count'));
    1054      } else {
    1055          // Prepare whole post
    1056          $postclass    = 'fullpost';
    1057          $postcontent  = format_text($post->message, $post->messageformat, $options, $course->id);
    1058          if (!empty($highlight)) {
    1059              $postcontent = highlight($highlight, $postcontent);
    1060          }
    1061          if (!empty($forum->displaywordcount)) {
    1062              $postcontent .= html_writer::tag('div', get_string('numwords', 'moodle', count_words($postcontent)),
    1063                  array('class'=>'post-word-count'));
    1064          }
    1065          $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages'));
    1066      }
    1067  
    1068      if (\core_tag_tag::is_enabled('mod_forum', 'forum_posts')) {
    1069          $postcontent .= $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id), null, 'forum-tags');
    1070      }
    1071  
    1072      // Output the post content
    1073      $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass));
    1074      $output .= html_writer::end_tag('div'); // Content
    1075      $output .= html_writer::end_tag('div'); // Content mask
    1076      $output .= html_writer::end_tag('div'); // Row
    1077  
    1078      $output .= html_writer::start_tag('nav', array('class' => 'row side'));
    1079      $output .= html_writer::tag('div','&nbsp;', array('class'=>'left'));
    1080      $output .= html_writer::start_tag('div', array('class'=>'options clearfix'));
    1081  
    1082      if (!empty($attachments)) {
    1083          $output .= html_writer::tag('div', $attachments, array('class' => 'attachments'));
    1084      }
    1085  
    1086      // Output ratings
    1087      if (!empty($post->rating)) {
    1088          $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating'));
    1089      }
    1090  
    1091      // Output the commands
    1092      $commandhtml = array();
    1093      foreach ($commands as $command) {
    1094          if (is_array($command)) {
    1095              $attributes = ['class' => 'nav-item nav-link'];
    1096              if (isset($command['attributes'])) {
    1097                  $attributes = array_merge($attributes, $command['attributes']);
    1098              }
    1099              $commandhtml[] = html_writer::link($command['url'], $command['text'], $attributes);
    1100          } else {
    1101              $commandhtml[] = $command;
    1102          }
    1103      }
    1104      $output .= html_writer::tag('div', implode(' ', $commandhtml), array('class' => 'commands nav'));
    1105  
    1106      // Output link to post if required
    1107      if ($link) {
    1108          if (forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext)) {
    1109              $langstring = 'discussthistopic';
    1110          } else {
    1111              $langstring = 'viewthediscussion';
    1112          }
    1113          if ($post->replies == 1) {
    1114              $replystring = get_string('repliesone', 'forum', $post->replies);
    1115          } else {
    1116              $replystring = get_string('repliesmany', 'forum', $post->replies);
    1117          }
    1118          if (!empty($discussion->unread) && $discussion->unread !== '-') {
    1119              $replystring .= ' <span class="sep">/</span> <span class="unread">';
    1120              $unreadlink = new moodle_url($discussionlink, null, 'unread');
    1121              if ($discussion->unread == 1) {
    1122                  $replystring .= html_writer::link($unreadlink, get_string('unreadpostsone', 'forum'));
    1123              } else {
    1124                  $replystring .= html_writer::link($unreadlink, get_string('unreadpostsnumber', 'forum', $discussion->unread));
    1125              }
    1126              $replystring .= '</span>';
    1127          }
    1128  
    1129          $output .= html_writer::start_tag('div', array('class'=>'link'));
    1130          $output .= html_writer::link($discussionlink, get_string($langstring, 'forum'));
    1131          $output .= '&nbsp;('.$replystring.')';
    1132          $output .= html_writer::end_tag('div'); // link
    1133      }
    1134  
    1135      // Output footer if required
    1136      if ($footer) {
    1137          $output .= html_writer::tag('div', $footer, array('class'=>'footer'));
    1138      }
    1139  
    1140      // Close remaining open divs
    1141      $output .= html_writer::end_tag('div'); // content
    1142      $output .= html_writer::end_tag('nav'); // row
    1143      $output .= html_writer::end_tag('div'); // forumpost
    1144  
    1145      // Mark the forum post as read if required
    1146      if ($istracked && !$CFG->forum_usermarksread && !$postisread) {
    1147          forum_tp_mark_post_read($USER->id, $post);
    1148      }
    1149  
    1150      if ($return) {
    1151          return $output;
    1152      }
    1153      echo $output;
    1154      return;
    1155  }
    1156  
    1157  /**
    1158   * @global object
    1159   * @global object
    1160   * @uses FORUM_MODE_FLATNEWEST
    1161   * @param object $course
    1162   * @param object $cm
    1163   * @param object $forum
    1164   * @param object $discussion
    1165   * @param object $post
    1166   * @param object $mode
    1167   * @param bool $reply
    1168   * @param bool $forumtracked
    1169   * @param array $posts
    1170   * @return void
    1171   * @deprecated since Moodle 3.7
    1172   */
    1173  function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) {
    1174      debugging('forum_print_posts_flat() has been deprecated, ' .
    1175          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
    1176      global $USER, $CFG;
    1177  
    1178      $link  = false;
    1179  
    1180      foreach ($posts as $post) {
    1181          if (!$post->parent) {
    1182              continue;
    1183          }
    1184          $post->subject = format_string($post->subject);
    1185          $ownpost = ($USER->id == $post->userid);
    1186  
    1187          $postread = !empty($post->postread);
    1188  
    1189          forum_print_post_start($post);
    1190          forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
    1191                               '', '', $postread, true, $forumtracked);
    1192          forum_print_post_end($post);
    1193      }
    1194  }
    1195  
    1196  /**
    1197   * @todo Document this function
    1198   *
    1199   * @global object
    1200   * @global object
    1201   * @uses CONTEXT_MODULE
    1202   * @return void
    1203   * @deprecated since Moodle 3.7
    1204   */
    1205  function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) {
    1206      debugging('forum_print_posts_threaded() has been deprecated, ' .
    1207          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
    1208      global $USER, $CFG;
    1209  
    1210      $link  = false;
    1211  
    1212      if (!empty($posts[$parent->id]->children)) {
    1213          $posts = $posts[$parent->id]->children;
    1214  
    1215          $modcontext       = context_module::instance($cm->id);
    1216          $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
    1217  
    1218          foreach ($posts as $post) {
    1219  
    1220              echo '<div class="indent">';
    1221              if ($depth > 0) {
    1222                  $ownpost = ($USER->id == $post->userid);
    1223                  $post->subject = format_string($post->subject);
    1224  
    1225                  $postread = !empty($post->postread);
    1226  
    1227                  forum_print_post_start($post);
    1228                  forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
    1229                                       '', '', $postread, true, $forumtracked);
    1230                  forum_print_post_end($post);
    1231              } else {
    1232                  if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, true)) {
    1233                      if (forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
    1234                          // This post has been deleted but still exists and may have children.
    1235                          $subject = get_string('privacy:request:delete:post:subject', 'mod_forum');
    1236                          $byline = '';
    1237                      } else {
    1238                          // The user can't see this post at all.
    1239                          echo "</div>\n";
    1240                          continue;
    1241                      }
    1242                  } else {
    1243                      $by = new stdClass();
    1244                      $by->name = fullname($post, $canviewfullnames);
    1245                      $by->date = userdate_htmltime($post->modified);
    1246                      $byline = ' ' . get_string("bynameondate", "forum", $by);
    1247                      $subject = format_string($post->subject, true);
    1248                  }
    1249  
    1250                  if ($forumtracked) {
    1251                      if (!empty($post->postread)) {
    1252                          $style = '<span class="forumthread read">';
    1253                      } else {
    1254                          $style = '<span class="forumthread unread">';
    1255                      }
    1256                  } else {
    1257                      $style = '<span class="forumthread">';
    1258                  }
    1259  
    1260                  echo $style;
    1261                  echo "<a name='{$post->id}'></a>";
    1262                  echo html_writer::link(new moodle_url('/mod/forum/discuss.php', [
    1263                          'd' => $post->discussion,
    1264                          'parent' => $post->id,
    1265                      ]), $subject);
    1266                  echo $byline;
    1267                  echo "</span>";
    1268              }
    1269  
    1270              forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts);
    1271              echo "</div>\n";
    1272          }
    1273      }
    1274  }
    1275  
    1276  /**
    1277   * @todo Document this function
    1278   * @global object
    1279   * @global object
    1280   * @return void
    1281   * @deprecated since Moodle 3.7
    1282   */
    1283  function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) {
    1284      debugging('forum_print_posts_nested() has been deprecated, ' .
    1285          'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
    1286      global $USER, $CFG;
    1287  
    1288      $link  = false;
    1289  
    1290      if (!empty($posts[$parent->id]->children)) {
    1291          $posts = $posts[$parent->id]->children;
    1292  
    1293          foreach ($posts as $post) {
    1294  
    1295              echo '<div class="indent">';
    1296              if (!isloggedin()) {
    1297                  $ownpost = false;
    1298              } else {
    1299                  $ownpost = ($USER->id == $post->userid);
    1300              }
    1301  
    1302              $post->subject = format_string($post->subject);
    1303              $postread = !empty($post->postread);
    1304  
    1305              forum_print_post_start($post);
    1306              forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
    1307                                   '', '', $postread, true, $forumtracked);
    1308              forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
    1309              forum_print_post_end($post);
    1310              echo "</div>\n";
    1311          }
    1312      }
    1313  }
    1314  
    1315  /**
    1316   * Prints the discussion view screen for a forum.
    1317   *
    1318   * @param object $course The current course object.
    1319   * @param object $forum Forum to be printed.
    1320   * @param int $maxdiscussions
    1321   * @param string $displayformat The display format to use (optional).
    1322   * @param string $sort Sort arguments for database query (optional).
    1323   * @param int $currentgroup
    1324   * @param int $groupmode Group mode of the forum (optional).
    1325   * @param int $page Page mode, page to display (optional).
    1326   * @param int $perpage The maximum number of discussions per page(optional)
    1327   * @param stdClass $cm
    1328   * @deprecated since Moodle 3.7
    1329   */
    1330  function forum_print_latest_discussions($course, $forum, $maxdiscussions = -1, $displayformat = 'plain', $sort = '',
    1331                                          $currentgroup = -1, $groupmode = -1, $page = -1, $perpage = 100, $cm = null) {
    1332      debugging('forum_print_latest_discussions has been deprecated.', DEBUG_DEVELOPER);
    1333      global $CFG, $USER, $OUTPUT;
    1334  
    1335      require_once($CFG->dirroot . '/course/lib.php');
    1336  
    1337      if (!$cm) {
    1338          if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
    1339              print_error('invalidcoursemodule');
    1340          }
    1341      }
    1342      $context = context_module::instance($cm->id);
    1343  
    1344      if (empty($sort)) {
    1345          $sort = forum_get_default_sort_order();
    1346      }
    1347  
    1348      $olddiscussionlink = false;
    1349  
    1350      // Sort out some defaults.
    1351      if ($perpage <= 0) {
    1352          $perpage = 0;
    1353          $page    = -1;
    1354      }
    1355  
    1356      if ($maxdiscussions == 0) {
    1357          // All discussions - backwards compatibility.
    1358          $page    = -1;
    1359          $perpage = 0;
    1360          if ($displayformat == 'plain') {
    1361              $displayformat = 'header';  // Abbreviate display by default.
    1362          }
    1363  
    1364      } else if ($maxdiscussions > 0) {
    1365          $page    = -1;
    1366          $perpage = $maxdiscussions;
    1367      }
    1368  
    1369      $fullpost = false;
    1370      if ($displayformat == 'plain') {
    1371          $fullpost = true;
    1372      }
    1373  
    1374      // Decide if current user is allowed to see ALL the current discussions or not.
    1375      // First check the group stuff.
    1376      if ($currentgroup == -1 or $groupmode == -1) {
    1377          $groupmode    = groups_get_activity_groupmode($cm, $course);
    1378          $currentgroup = groups_get_activity_group($cm);
    1379      }
    1380  
    1381      // Cache.
    1382      $groups = array();
    1383  
    1384      // If the user can post discussions, then this is a good place to put the
    1385      // button for it. We do not show the button if we are showing site news
    1386      // and the current user is a guest.
    1387  
    1388      $canstart = forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context);
    1389      if (!$canstart and $forum->type !== 'news') {
    1390          if (isguestuser() or !isloggedin()) {
    1391              $canstart = true;
    1392          }
    1393          if (!is_enrolled($context) and !is_viewing($context)) {
    1394              // Allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link
    1395              // normal users with temporary guest access see this button too, they are asked to enrol instead
    1396              // do not show the button to users with suspended enrolments here.
    1397              $canstart = enrol_selfenrol_available($course->id);
    1398          }
    1399      }
    1400  
    1401      if ($canstart) {
    1402          switch ($forum->type) {
    1403              case 'news':
    1404              case 'blog':
    1405                  $buttonadd = get_string('addanewtopic', 'forum');
    1406                  break;
    1407              case 'qanda':
    1408                  $buttonadd = get_string('addanewquestion', 'forum');
    1409                  break;
    1410              default:
    1411                  $buttonadd = get_string('addanewdiscussion', 'forum');
    1412                  break;
    1413          }
    1414          $button = new single_button(new moodle_url('/mod/forum/post.php', ['forum' => $forum->id]), $buttonadd, 'get');
    1415          $button->class = 'singlebutton forumaddnew';
    1416          $button->formid = 'newdiscussionform';
    1417          echo $OUTPUT->render($button);
    1418  
    1419      } else if (isguestuser() or !isloggedin() or $forum->type == 'news' or
    1420          $forum->type == 'qanda' and !has_capability('mod/forum:addquestion', $context) or
    1421          $forum->type != 'qanda' and !has_capability('mod/forum:startdiscussion', $context)) {
    1422          // No button and no info.
    1423          $ignore = true;
    1424      } else if ($groupmode and !has_capability('moodle/site:accessallgroups', $context)) {
    1425          // Inform users why they can not post new discussion.
    1426          if (!$currentgroup) {
    1427              if (!has_capability('mod/forum:canposttomygroups', $context)) {
    1428                  echo $OUTPUT->notification(get_string('cannotadddiscussiongroup', 'forum'));
    1429              } else {
    1430                  echo $OUTPUT->notification(get_string('cannotadddiscussionall', 'forum'));
    1431              }
    1432          } else if (!groups_is_member($currentgroup)) {
    1433              echo $OUTPUT->notification(get_string('cannotadddiscussion', 'forum'));
    1434          }
    1435      }
    1436  
    1437      // Get all the recent discussions we're allowed to see.
    1438  
    1439      $getuserlastmodified = ($displayformat == 'header');
    1440  
    1441      $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage);
    1442      if (!$discussions) {
    1443          echo '<div class="forumnodiscuss">';
    1444          if ($forum->type == 'news') {
    1445              echo '('.get_string('nonews', 'forum').')';
    1446          } else if ($forum->type == 'qanda') {
    1447              echo '('.get_string('noquestions', 'forum').')';
    1448          } else {
    1449              echo '('.get_string('nodiscussions', 'forum').')';
    1450          }
    1451          echo "</div>\n";
    1452          return;
    1453      }
    1454  
    1455      $canseeprivatereplies = has_capability('mod/forum:readprivatereplies', $context);
    1456      // If we want paging.
    1457      if ($page != -1) {
    1458          // Get the number of discussions found.
    1459          $numdiscussions = forum_get_discussions_count($cm);
    1460  
    1461          // Show the paging bar.
    1462          echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
    1463          if ($numdiscussions > 1000) {
    1464              // Saves some memory on sites with very large forums.
    1465              $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage, $canseeprivatereplies);
    1466          } else {
    1467              $replies = forum_count_discussion_replies($forum->id, "", -1, -1, 0, $canseeprivatereplies);
    1468          }
    1469  
    1470      } else {
    1471          $replies = forum_count_discussion_replies($forum->id, "", -1, -1, 0, $canseeprivatereplies);
    1472  
    1473          if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) {
    1474              $olddiscussionlink = true;
    1475          }
    1476      }
    1477  
    1478      $canviewparticipants = course_can_view_participants($context);
    1479      $canviewhiddentimedposts = has_capability('mod/forum:viewhiddentimedposts', $context);
    1480  
    1481      $strdatestring = get_string('strftimerecentfull');
    1482  
    1483      // Check if the forum is tracked.
    1484      if ($cantrack = forum_tp_can_track_forums($forum)) {
    1485          $forumtracked = forum_tp_is_tracked($forum);
    1486      } else {
    1487          $forumtracked = false;
    1488      }
    1489  
    1490      if ($forumtracked) {
    1491          $unreads = forum_get_discussions_unread($cm);
    1492      } else {
    1493          $unreads = array();
    1494      }
    1495  
    1496      if ($displayformat == 'header') {
    1497          echo '<table cellspacing="0" class="forumheaderlist">';
    1498          echo '<thead class="text-left">';
    1499          echo '<tr>';
    1500          echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
    1501          echo '<th class="header author" scope="col">'.get_string('startedby', 'forum').'</th>';
    1502          if ($groupmode > 0) {
    1503              echo '<th class="header group" scope="col">'.get_string('group').'</th>';
    1504          }
    1505          if (has_capability('mod/forum:viewdiscussion', $context)) {
    1506              echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
    1507              // If the forum can be tracked, display the unread column.
    1508              if ($cantrack) {
    1509                  echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
    1510                  if ($forumtracked) {
    1511                      echo '<a title="'.get_string('markallread', 'forum').
    1512                           '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
    1513                           $forum->id.'&amp;mark=read&amp;return=/mod/forum/view.php&amp;sesskey=' . sesskey() . '">'.
    1514                           $OUTPUT->pix_icon('t/markasread', get_string('markallread', 'forum')) . '</a>';
    1515                  }
    1516                  echo '</th>';
    1517              }
    1518          }
    1519          echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
    1520          if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/forum:viewdiscussion', $context)) {
    1521              if (\mod_forum\subscriptions::is_subscribable($forum)) {
    1522                  echo '<th class="header discussionsubscription" scope="col">';
    1523                  echo forum_get_discussion_subscription_icon_preloaders();
    1524                  echo '</th>';
    1525              }
    1526          }
    1527          echo '</tr>';
    1528          echo '</thead>';
    1529          echo '<tbody>';
    1530      }
    1531  
    1532      foreach ($discussions as $discussion) {
    1533          if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $context) &&
    1534              !forum_user_has_posted($forum->id, $discussion->discussion, $USER->id)) {
    1535              $canviewparticipants = false;
    1536          }
    1537  
    1538          if (!empty($replies[$discussion->discussion])) {
    1539              $discussion->replies = $replies[$discussion->discussion]->replies;
    1540              $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
    1541          } else {
    1542              $discussion->replies = 0;
    1543          }
    1544  
    1545          // SPECIAL CASE: The front page can display a news item post to non-logged in users.
    1546          // All posts are read in this case.
    1547          if (!$forumtracked) {
    1548              $discussion->unread = '-';
    1549          } else if (empty($USER)) {
    1550              $discussion->unread = 0;
    1551          } else {
    1552              if (empty($unreads[$discussion->discussion])) {
    1553                  $discussion->unread = 0;
    1554              } else {
    1555                  $discussion->unread = $unreads[$discussion->discussion];
    1556              }
    1557          }
    1558  
    1559          if (isloggedin()) {
    1560              $ownpost = ($discussion->userid == $USER->id);
    1561          } else {
    1562              $ownpost = false;
    1563          }
    1564          // Use discussion name instead of subject of first post.
    1565          $discussion->subject = $discussion->name;
    1566  
    1567          switch ($displayformat) {
    1568              case 'header':
    1569                  if ($groupmode > 0) {
    1570                      if (isset($groups[$discussion->groupid])) {
    1571                          $group = $groups[$discussion->groupid];
    1572                      } else {
    1573                          $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid);
    1574                      }
    1575                  } else {
    1576                      $group = -1;
    1577                  }
    1578                  forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
    1579                      $canviewparticipants, $context, $canviewhiddentimedposts);
    1580              break;
    1581              default:
    1582                  $link = false;
    1583  
    1584                  if ($discussion->replies) {
    1585                      $link = true;
    1586                  } else {
    1587                      $modcontext = context_module::instance($cm->id);
    1588                      $link = forum_user_can_see_discussion($forum, $discussion, $modcontext, $USER);
    1589                  }
    1590  
    1591                  $discussion->forum = $forum->id;
    1592  
    1593                  forum_print_post_start($discussion);
    1594                  forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false,
    1595                          '', null, true, $forumtracked);
    1596                  forum_print_post_end($discussion);
    1597              break;
    1598          }
    1599      }
    1600  
    1601      if ($displayformat == "header") {
    1602          echo '</tbody>';
    1603          echo '</table>';
    1604      }
    1605  
    1606      if ($olddiscussionlink) {
    1607          if ($forum->type == 'news') {
    1608              $strolder = get_string('oldertopics', 'forum');
    1609          } else {
    1610              $strolder = get_string('olderdiscussions', 'forum');
    1611          }
    1612          echo '<div class="forumolddiscuss">';
    1613          echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
    1614          echo $strolder.'</a> ...</div>';
    1615      }
    1616  
    1617      if ($page != -1) {
    1618          // Show the paging bar.
    1619          echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
    1620      }
    1621  }
    1622  
    1623  /**
    1624   * Count the number of replies to the specified post.
    1625   *
    1626   * @param object $post
    1627   * @param bool $children
    1628   * @return int
    1629   * @deprecated since Moodle 3.7
    1630   * @todo MDL-65252 This will be removed in Moodle 3.11
    1631   */
    1632  function forum_count_replies($post, $children = true) {
    1633      global $USER;
    1634      debugging('forum_count_replies has been deprecated. Please use the Post vault instead.', DEBUG_DEVELOPER);
    1635  
    1636      if (!$children) {
    1637          return $DB->count_records('forum_posts', array('parent' => $post->id));
    1638      }
    1639  
    1640      $entityfactory = mod_forum\local\container::get_entity_factory();
    1641      $postentity = $entityfactory->get_post_from_stdclass($post);
    1642  
    1643      $vaultfactory = mod_forum\local\container::get_vault_factory();
    1644      $postvault = $vaultfactory->get_post_vault();
    1645  
    1646      return $postvault->get_reply_count_for_post_id_in_discussion_id(
    1647              $USER,
    1648              $postentity->get_id(),
    1649              $postentity->get_discussion_id(),
    1650              true
    1651          );
    1652  }
    1653  
    1654  /**
    1655   * @deprecated since Moodle 3.8
    1656   */
    1657  function forum_scale_used() {
    1658      throw new coding_exception('forum_scale_used() can not be used anymore. Plugins can implement ' .
    1659          '<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored');
    1660  }
    1661  
    1662  /**
    1663   * Return grade for given user or all users.
    1664   *
    1665   * @deprecated since Moodle 3.8
    1666   * @param object $forum
    1667   * @param int $userid optional user id, 0 means all users
    1668   * @return array array of grades, false if none
    1669   */
    1670  function forum_get_user_grades($forum, $userid = 0) {
    1671      global $CFG;
    1672  
    1673      require_once($CFG->dirroot.'/rating/lib.php');
    1674  
    1675      $ratingoptions = (object) [
    1676          'component' => 'mod_forum',
    1677          'ratingarea' => 'post',
    1678          'contextid' => $contextid,
    1679  
    1680          'modulename' => 'forum',
    1681          'moduleid  ' => $forum->id,
    1682          'userid' => $userid,
    1683          'aggregationmethod' => $forum->assessed,
    1684          'scaleid' => $forum->scale,
    1685          'itemtable' => 'forum_posts',
    1686          'itemtableusercolumn' => 'userid',
    1687      ];
    1688  
    1689      $rm = new rating_manager();
    1690      return $rm->get_user_grades($ratingoptions);
    1691  }
    1692  
    1693  /**
    1694   * Obtains the automatic completion state for this forum based on any conditions
    1695   * in forum settings.
    1696   *
    1697   * @deprecated since Moodle 3.11
    1698   * @todo MDL-71196 Final deprecation in Moodle 4.3
    1699   * @see \mod_forum\completion\custom_completion
    1700   * @global object
    1701   * @global object
    1702   * @param object $course Course
    1703   * @param object $cm Course-module
    1704   * @param int $userid User ID
    1705   * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
    1706   * @return bool True if completed, false if not. (If no conditions, then return
    1707   *   value depends on comparison type)
    1708   */
    1709  function forum_get_completion_state($course, $cm, $userid, $type) {
    1710      global $DB;
    1711  
    1712      // No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
    1713  
    1714      // Get forum details.
    1715      if (!($forum = $DB->get_record('forum', array('id' => $cm->instance)))) {
    1716          throw new Exception("Can't find forum {$cm->instance}");
    1717      }
    1718  
    1719      $result = $type; // Default return value.
    1720  
    1721      $postcountparams = array('userid' => $userid, 'forumid' => $forum->id);
    1722      $postcountsql = "
    1723  SELECT
    1724      COUNT(1)
    1725  FROM
    1726      {forum_posts} fp
    1727      INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id
    1728  WHERE
    1729      fp.userid=:userid AND fd.forum=:forumid";
    1730  
    1731      if ($forum->completiondiscussions) {
    1732          $value = $forum->completiondiscussions <=
    1733              $DB->count_records('forum_discussions', array('forum' => $forum->id, 'userid' => $userid));
    1734          if ($type == COMPLETION_AND) {
    1735              $result = $result && $value;
    1736          } else {
    1737              $result = $result || $value;
    1738          }
    1739      }
    1740      if ($forum->completionreplies) {
    1741          $value = $forum->completionreplies <=
    1742              $DB->get_field_sql($postcountsql . ' AND fp.parent<>0', $postcountparams);
    1743          if ($type == COMPLETION_AND) {
    1744              $result = $result && $value;
    1745          } else {
    1746              $result = $result || $value;
    1747          }
    1748      }
    1749      if ($forum->completionposts) {
    1750          $value = $forum->completionposts <= $DB->get_field_sql($postcountsql, $postcountparams);
    1751          if ($type == COMPLETION_AND) {
    1752              $result = $result && $value;
    1753          } else {
    1754              $result = $result || $value;
    1755          }
    1756      }
    1757  
    1758      return $result;
    1759  }