MDL-69521 core: Move all comments in code from 4.1 to 3.11
[moodle.git] / mod / forum / deprecatedlib.php
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/>.
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  */
23 defined('MOODLE_INTERNAL') || die();
25 // Deprecated a very long time ago.
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 }
35 // Since Moodle 1.5.
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 }
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 }
52 // Since Moodle 1.6.
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 }
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 }
69 // Since Moodle 1.7.
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 }
79 // Since Moodle 1.9.
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 }
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 }
96 // Since Moodle 2.0.
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.');
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.');
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.');
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.');
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.');
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.');
140 // Deprecated in 2.3.
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.');
150 // Deprecated in 2.4.
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.');
160 // Deprecated in 2.6.
162 /**
163  * FORUM_TRACKING_ON - deprecated alias for FORUM_TRACKING_FORCED.
164  * @deprecated since 2.6
165  */
166 define('FORUM_TRACKING_ON', 2);
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.');
177 // Deprecated in 2.8.
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.');
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');
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');
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');
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');
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');
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');
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');
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');
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');
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         );
291     $modcontext = context_module::instance($cm->id);
292     $renderable->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
294     if ($bare) {
295         $renderer = $PAGE->get_renderer('mod_forum', 'emaildigestfull', 'textemail');
296     } else {
297         $renderer = $PAGE->get_renderer('mod_forum', 'email', 'textemail');
298     }
300     debugging("forum_make_mail_text() has been deprecated, please use the \mod_forum\output\forum_post_email renderable instead.",
301             DEBUG_DEVELOPER);
303     return $renderer->render($renderable);
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     );
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);
364     $modcontext = context_module::instance($cm->id);
365     $renderable->viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
367     // Assume that this is being used as a standard forum email.
368     $renderer = $PAGE->get_renderer('mod_forum', 'email', 'htmlemail');
370     debugging("forum_make_mail_post() has been deprecated, please use the \mod_forum\output\forum_post_email renderable instead.",
371             DEBUG_DEVELOPER);
373     return $renderer->render($renderable);
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);
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.
391     unset($user->institution);
392     unset($user->department);
393     unset($user->address);
394     unset($user->city);
395     unset($user->url);
396     unset($user->currentlogin);
397     unset($user->description);
398     unset($user->descriptionformat);
401 /**
402  * Function to be run periodically according to the scheduled task.
403  *
404  * Finds all posts that have yet to be mailed out, and mails them out to all subscribers as well as other maintance
405  * tasks.
406  *
407  * @deprecated since Moodle 3.7
408  */
409 function forum_cron() {
410     debugging("forum_cron() has been deprecated and replaced with new tasks. Please uses these instead.",
411             DEBUG_DEVELOPER);
414 /**
415  * Prints a forum discussion
416  *
417  * @uses CONTEXT_MODULE
418  * @uses FORUM_MODE_FLATNEWEST
419  * @uses FORUM_MODE_FLATOLDEST
420  * @uses FORUM_MODE_THREADED
421  * @uses FORUM_MODE_NESTED
422  * @param stdClass $course
423  * @param stdClass $cm
424  * @param stdClass $forum
425  * @param stdClass $discussion
426  * @param stdClass $post
427  * @param int $mode
428  * @param mixed $canreply
429  * @param bool $canrate
430  * @deprecated since Moodle 3.7
431  */
432 function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
433     debugging('forum_print_discussion() has been deprecated, ' .
434         'please use \mod_forum\local\renderers\discussion instead.', DEBUG_DEVELOPER);
436     global $USER, $CFG;
438     require_once($CFG->dirroot.'/rating/lib.php');
440     $ownpost = (isloggedin() && $USER->id == $post->userid);
442     $modcontext = context_module::instance($cm->id);
443     if ($canreply === NULL) {
444         $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
445     } else {
446         $reply = $canreply;
447     }
449     // $cm holds general cache for forum functions
450     $cm->cache = new stdClass;
451     $cm->cache->groups      = groups_get_all_groups($course->id, 0, $cm->groupingid);
452     $cm->cache->usersgroups = array();
454     $posters = array();
456     // preload all posts - TODO: improve...
457     if ($mode == FORUM_MODE_FLATNEWEST) {
458         $sort = "p.created DESC";
459     } else {
460         $sort = "p.created ASC";
461     }
463     $forumtracked = forum_tp_is_tracked($forum);
464     $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
465     $post = $posts[$post->id];
467     foreach ($posts as $pid=>$p) {
468         $posters[$p->userid] = $p->userid;
469     }
471     // preload all groups of ppl that posted in this discussion
472     if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) {
473         foreach($postersgroups as $pg) {
474             if (!isset($cm->cache->usersgroups[$pg->userid])) {
475                 $cm->cache->usersgroups[$pg->userid] = array();
476             }
477             $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid;
478         }
479         unset($postersgroups);
480     }
482     //load ratings
483     if ($forum->assessed != RATING_AGGREGATE_NONE) {
484         $ratingoptions = new stdClass;
485         $ratingoptions->context = $modcontext;
486         $ratingoptions->component = 'mod_forum';
487         $ratingoptions->ratingarea = 'post';
488         $ratingoptions->items = $posts;
489         $ratingoptions->aggregate = $forum->assessed;//the aggregation method
490         $ratingoptions->scaleid = $forum->scale;
491         $ratingoptions->userid = $USER->id;
492         if ($forum->type == 'single' or !$discussion->id) {
493             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id";
494         } else {
495             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
496         }
497         $ratingoptions->assesstimestart = $forum->assesstimestart;
498         $ratingoptions->assesstimefinish = $forum->assesstimefinish;
500         $rm = new rating_manager();
501         $posts = $rm->get_ratings($ratingoptions);
502     }
505     $post->forum = $forum->id;   // Add the forum id to the post object, later used by forum_print_post
506     $post->forumtype = $forum->type;
508     $post->subject = format_string($post->subject);
510     $postread = !empty($post->postread);
512     forum_print_post_start($post);
513     forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false,
514                          '', '', $postread, true, $forumtracked);
516     switch ($mode) {
517         case FORUM_MODE_FLATOLDEST :
518         case FORUM_MODE_FLATNEWEST :
519         default:
520             forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts);
521             break;
523         case FORUM_MODE_THREADED :
524             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts);
525             break;
527         case FORUM_MODE_NESTED :
528             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
529             break;
530     }
531     forum_print_post_end($post);
535 /**
536  * Return a static array of posts that are open.
537  *
538  * @return array
539  * @deprecated since Moodle 3.7
540  */
541 function forum_post_nesting_cache() {
542     debugging('forum_post_nesting_cache() has been deprecated, ' .
543         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
544     static $nesting = array();
545     return $nesting;
548 /**
549  * Return true for the first time this post was started
550  *
551  * @param int $id The id of the post to start
552  * @return bool
553  * @deprecated since Moodle 3.7
554  */
555 function forum_should_start_post_nesting($id) {
556     debugging('forum_should_start_post_nesting() has been deprecated, ' .
557         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
558     $cache = forum_post_nesting_cache();
559     if (!array_key_exists($id, $cache)) {
560         $cache[$id] = 1;
561         return true;
562     } else {
563         $cache[$id]++;
564         return false;
565     }
568 /**
569  * Return true when all the opens are nested with a close.
570  *
571  * @param int $id The id of the post to end
572  * @return bool
573  * @deprecated since Moodle 3.7
574  */
575 function forum_should_end_post_nesting($id) {
576     debugging('forum_should_end_post_nesting() has been deprecated, ' .
577         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
578     $cache = forum_post_nesting_cache();
579     if (!array_key_exists($id, $cache)) {
580         return true;
581     } else {
582         $cache[$id]--;
583         if ($cache[$id] == 0) {
584             unset($cache[$id]);
585             return true;
586         }
587     }
588     return false;
591 /**
592  * Start a forum post container
593  *
594  * @param object $post The post to print.
595  * @param bool $return Return the string or print it
596  * @return string
597  * @deprecated since Moodle 3.7
598  */
599 function forum_print_post_start($post, $return = false) {
600     debugging('forum_print_post_start() has been deprecated, ' .
601         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
602     $output = '';
604     if (forum_should_start_post_nesting($post->id)) {
605         $attributes = [
606             'id' => 'p'.$post->id,
607             'tabindex' => -1,
608             'class' => 'relativelink'
609         ];
610         $output .= html_writer::start_tag('article', $attributes);
611     }
612     if ($return) {
613         return $output;
614     }
615     echo $output;
616     return;
619 /**
620  * End a forum post container
621  *
622  * @param object $post The post to print.
623  * @param bool $return Return the string or print it
624  * @return string
625  * @deprecated since Moodle 3.7
626  */
627 function forum_print_post_end($post, $return = false) {
628     debugging('forum_print_post_end() has been deprecated, ' .
629         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
630     $output = '';
632     if (forum_should_end_post_nesting($post->id)) {
633         $output .= html_writer::end_tag('article');
634     }
635     if ($return) {
636         return $output;
637     }
638     echo $output;
639     return;
642 /**
643  * Print a forum post
644  * This function should always be surrounded with calls to forum_print_post_start
645  * and forum_print_post_end to create the surrounding container for the post.
646  * Replies can be nested before forum_print_post_end and should reflect the structure of
647  * thread.
648  *
649  * @global object
650  * @global object
651  * @uses FORUM_MODE_THREADED
652  * @uses PORTFOLIO_FORMAT_PLAINHTML
653  * @uses PORTFOLIO_FORMAT_FILE
654  * @uses PORTFOLIO_FORMAT_RICHHTML
655  * @uses PORTFOLIO_ADD_TEXT_LINK
656  * @uses CONTEXT_MODULE
657  * @param object $post The post to print.
658  * @param object $discussion
659  * @param object $forum
660  * @param object $cm
661  * @param object $course
662  * @param boolean $ownpost Whether this post belongs to the current user.
663  * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
664  * @param boolean $link Just print a shortened version of the post as a link to the full post.
665  * @param string $footer Extra stuff to print after the message.
666  * @param string $highlight Space-separated list of terms to highlight.
667  * @param int $post_read true, false or -99. If we already know whether this user
668  *          has read this post, pass that in, otherwise, pass in -99, and this
669  *          function will work it out.
670  * @param boolean $dummyifcantsee When forum_user_can_see_post says that
671  *          the current user can't see this post, if this argument is true
672  *          (the default) then print a dummy 'you can't see this post' post.
673  *          If false, don't output anything at all.
674  * @param bool|null $istracked
675  * @return void
676  * @deprecated since Moodle 3.7
677  */
678 function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
679                           $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) {
680     debugging('forum_print_post() has been deprecated, ' .
681         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
682     global $USER, $CFG, $OUTPUT;
684     require_once($CFG->libdir . '/filelib.php');
686     // String cache
687     static $str;
688     // This is an extremely hacky way to ensure we only print the 'unread' anchor
689     // the first time we encounter an unread post on a page. Ideally this would
690     // be moved into the caller somehow, and be better testable. But at the time
691     // of dealing with this bug, this static workaround was the most surgical and
692     // it fits together with only printing th unread anchor id once on a given page.
693     static $firstunreadanchorprinted = false;
695     $modcontext = context_module::instance($cm->id);
697     $post->course = $course->id;
698     $post->forum  = $forum->id;
699     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
700     if (!empty($CFG->enableplagiarism)) {
701         require_once($CFG->libdir.'/plagiarismlib.php');
702         $post->message .= plagiarism_get_links(array('userid' => $post->userid,
703             'content' => $post->message,
704             'cmid' => $cm->id,
705             'course' => $post->course,
706             'forum' => $post->forum));
707     }
709     // caching
710     if (!isset($cm->cache)) {
711         $cm->cache = new stdClass;
712     }
714     if (!isset($cm->cache->caps)) {
715         $cm->cache->caps = array();
716         $cm->cache->caps['mod/forum:viewdiscussion']   = has_capability('mod/forum:viewdiscussion', $modcontext);
717         $cm->cache->caps['moodle/site:viewfullnames']  = has_capability('moodle/site:viewfullnames', $modcontext);
718         $cm->cache->caps['mod/forum:editanypost']      = has_capability('mod/forum:editanypost', $modcontext);
719         $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext);
720         $cm->cache->caps['mod/forum:deleteownpost']    = has_capability('mod/forum:deleteownpost', $modcontext);
721         $cm->cache->caps['mod/forum:deleteanypost']    = has_capability('mod/forum:deleteanypost', $modcontext);
722         $cm->cache->caps['mod/forum:viewanyrating']    = has_capability('mod/forum:viewanyrating', $modcontext);
723         $cm->cache->caps['mod/forum:exportpost']       = has_capability('mod/forum:exportpost', $modcontext);
724         $cm->cache->caps['mod/forum:exportownpost']    = has_capability('mod/forum:exportownpost', $modcontext);
725     }
727     if (!isset($cm->uservisible)) {
728         $cm->uservisible = \core_availability\info_module::is_user_visible($cm, 0, false);
729     }
731     if ($istracked && is_null($postisread)) {
732         $postisread = forum_tp_is_post_read($USER->id, $post);
733     }
735     if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
736         // Do _not_ check the deleted flag - we need to display a different UI.
737         $output = '';
738         if (!$dummyifcantsee) {
739             if ($return) {
740                 return $output;
741             }
742             echo $output;
743             return;
744         }
746         $output .= html_writer::start_tag('div', array('class' => 'forumpost clearfix',
747                                                        'aria-label' => get_string('hiddenforumpost', 'forum')));
748         $output .= html_writer::start_tag('header', array('class' => 'row header'));
749         $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); // Picture.
750         if ($post->parent) {
751             $output .= html_writer::start_tag('div', array('class' => 'topic'));
752         } else {
753             $output .= html_writer::start_tag('div', array('class' => 'topic starter'));
754         }
755         $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class' => 'subject',
756                                                                                            'role' => 'header',
757                                                                                            'id' => ('headp' . $post->id))); // Subject.
758         $authorclasses = array('class' => 'author');
759         $output .= html_writer::tag('address', get_string('forumauthorhidden', 'forum'), $authorclasses); // Author.
760         $output .= html_writer::end_tag('div');
761         $output .= html_writer::end_tag('header'); // Header.
762         $output .= html_writer::start_tag('div', array('class'=>'row'));
763         $output .= html_writer::tag('div', '&nbsp;', array('class'=>'left side')); // Groups
764         $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content
765         $output .= html_writer::end_tag('div'); // row
766         $output .= html_writer::end_tag('div'); // forumpost
768         if ($return) {
769             return $output;
770         }
771         echo $output;
772         return;
773     }
775     if (!empty($post->deleted)) {
776         // Note: Posts marked as deleted are still returned by the above forum_user_can_post because it is required for
777         // nesting of posts.
778         $output = '';
779         if (!$dummyifcantsee) {
780             if ($return) {
781                 return $output;
782             }
783             echo $output;
784             return;
785         }
786         $output .= html_writer::start_tag('div', [
787                 'class' => 'forumpost clearfix',
788                 'aria-label' => get_string('forumbodydeleted', 'forum'),
789             ]);
791         $output .= html_writer::start_tag('header', array('class' => 'row header'));
792         $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation'));
794         $classes = ['topic'];
795         if (!empty($post->parent)) {
796             $classes[] = 'starter';
797         }
798         $output .= html_writer::start_tag('div', ['class' => implode(' ', $classes)]);
800         // Subject.
801         $output .= html_writer::tag('div', get_string('forumsubjectdeleted', 'forum'), [
802                 'class' => 'subject',
803                 'role' => 'header',
804                 'id' => ('headp' . $post->id)
805             ]);
807         // Author.
808         $output .= html_writer::tag('address', '', ['class' => 'author']);
810         $output .= html_writer::end_tag('div');
811         $output .= html_writer::end_tag('header'); // End header.
812         $output .= html_writer::start_tag('div', ['class' => 'row']);
813         $output .= html_writer::tag('div', '&nbsp;', ['class' => 'left side']); // Groups.
814         $output .= html_writer::tag('div', get_string('forumbodydeleted', 'forum'), ['class' => 'content']); // Content.
815         $output .= html_writer::end_tag('div'); // End row.
816         $output .= html_writer::end_tag('div'); // End forumpost.
818         if ($return) {
819             return $output;
820         }
821         echo $output;
822         return;
823     }
825     if (empty($str)) {
826         $str = new stdClass;
827         $str->edit         = get_string('edit', 'forum');
828         $str->delete       = get_string('delete', 'forum');
829         $str->reply        = get_string('reply', 'forum');
830         $str->parent       = get_string('parent', 'forum');
831         $str->pruneheading = get_string('pruneheading', 'forum');
832         $str->prune        = get_string('prune', 'forum');
833         $str->displaymode     = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
834         $str->markread     = get_string('markread', 'forum');
835         $str->markunread   = get_string('markunread', 'forum');
836     }
838     $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion));
840     // Build an object that represents the posting user
841     $postuser = new stdClass;
842     $postuserfields = explode(',', user_picture::fields());
843     $postuser = username_load_fields_from_object($postuser, $post, null, $postuserfields);
844     $postuser->id = $post->userid;
845     $postuser->fullname    = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']);
846     $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id));
848     // Prepare the groups the posting user belongs to
849     if (isset($cm->cache->usersgroups)) {
850         $groups = array();
851         if (isset($cm->cache->usersgroups[$post->userid])) {
852             foreach ($cm->cache->usersgroups[$post->userid] as $gid) {
853                 $groups[$gid] = $cm->cache->groups[$gid];
854             }
855         }
856     } else {
857         $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid);
858     }
860     // Prepare the attachements for the post, files then images
861     list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages');
863     // Determine if we need to shorten this post
864     $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost));
866     // Prepare an array of commands
867     $commands = array();
869     // Add a permalink.
870     $permalink = new moodle_url($discussionlink);
871     $permalink->set_anchor('p' . $post->id);
872     $commands[] = array('url' => $permalink, 'text' => get_string('permalink', 'forum'), 'attributes' => ['rel' => 'bookmark']);
874     // SPECIAL CASE: The front page can display a news item post to non-logged in users.
875     // Don't display the mark read / unread controls in this case.
876     if ($istracked && $CFG->forum_usermarksread && isloggedin()) {
877         $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread'));
878         $text = $str->markunread;
879         if (!$postisread) {
880             $url->param('mark', 'read');
881             $text = $str->markread;
882         }
883         if ($str->displaymode == FORUM_MODE_THREADED) {
884             $url->param('parent', $post->parent);
885         } else {
886             $url->set_anchor('p'.$post->id);
887         }
888         $commands[] = array('url'=>$url, 'text'=>$text, 'attributes' => ['rel' => 'bookmark']);
889     }
891     // Zoom in to the parent specifically
892     if ($post->parent) {
893         $url = new moodle_url($discussionlink);
894         if ($str->displaymode == FORUM_MODE_THREADED) {
895             $url->param('parent', $post->parent);
896         } else {
897             $url->set_anchor('p'.$post->parent);
898         }
899         $commands[] = array('url'=>$url, 'text'=>$str->parent, 'attributes' => ['rel' => 'bookmark']);
900     }
902     // Hack for allow to edit news posts those are not displayed yet until they are displayed
903     $age = time() - $post->created;
904     if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) {
905         $age = 0;
906     }
908     if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
909         if (has_capability('moodle/course:manageactivities', $modcontext)) {
910             // The first post in single simple is the forum description.
911             $commands[] = array('url'=>new moodle_url('/course/modedit.php', array('update'=>$cm->id, 'sesskey'=>sesskey(), 'return'=>1)), 'text'=>$str->edit);
912         }
913     } else if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) {
914         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit);
915     }
917     if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') {
918         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading);
919     }
921     if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
922         // Do not allow deleting of first post in single simple type.
923     } else if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) {
924         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete);
925     }
927     if ($reply) {
928         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply);
929     }
931     if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) {
932         $p = array('postid' => $post->id);
933         require_once($CFG->libdir.'/portfoliolib.php');
934         $button = new portfolio_add_button();
935         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), 'mod_forum');
936         if (empty($attachments)) {
937             $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
938         } else {
939             $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
940         }
942         $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
943         if (!empty($porfoliohtml)) {
944             $commands[] = $porfoliohtml;
945         }
946     }
947     // Finished building commands
950     // Begin output
952     $output  = '';
954     if ($istracked) {
955         if ($postisread) {
956             $forumpostclass = ' read';
957         } else {
958             $forumpostclass = ' unread';
959             // If this is the first unread post printed then give it an anchor and id of unread.
960             if (!$firstunreadanchorprinted) {
961                 $output .= html_writer::tag('a', '', array('id' => 'unread'));
962                 $firstunreadanchorprinted = true;
963             }
964         }
965     } else {
966         // ignore trackign status if not tracked or tracked param missing
967         $forumpostclass = '';
968     }
970     $topicclass = '';
971     if (empty($post->parent)) {
972         $topicclass = ' firstpost starter';
973     }
975     if (!empty($post->lastpost)) {
976         $forumpostclass .= ' lastpost';
977     }
979     // Flag to indicate whether we should hide the author or not.
980     $authorhidden = forum_is_author_hidden($post, $forum);
981     $postbyuser = new stdClass;
982     $postbyuser->post = $post->subject;
983     $postbyuser->user = $postuser->fullname;
984     $discussionbyuser = get_string('postbyuser', 'forum', $postbyuser);
985     // Begin forum post.
986     $output .= html_writer::start_div('forumpost clearfix' . $forumpostclass . $topicclass,
987         ['aria-label' => $discussionbyuser]);
988     // Begin header row.
989     $output .= html_writer::start_tag('header', ['class' => 'row header clearfix']);
991     // User picture.
992     if (!$authorhidden) {
993         $picture = $OUTPUT->user_picture($postuser, ['courseid' => $course->id]);
994         $output .= html_writer::div($picture, 'left picture', ['role' => 'presentation']);
995         $topicclass = 'topic' . $topicclass;
996     }
998     // Begin topic column.
999     $output .= html_writer::start_div($topicclass);
1000     $postsubject = $post->subject;
1001     if (empty($post->subjectnoformat)) {
1002         $postsubject = format_string($postsubject);
1003     }
1004     $output .= html_writer::div($postsubject, 'subject', ['role' => 'heading', 'aria-level' => '1', 'id' => ('headp' . $post->id)]);
1006     if ($authorhidden) {
1007         $bytext = userdate_htmltime($post->created);
1008     } else {
1009         $by = new stdClass();
1010         $by->date = userdate_htmltime($post->created);
1011         $by->name = html_writer::link($postuser->profilelink, $postuser->fullname);
1012         $bytext = get_string('bynameondate', 'forum', $by);
1013     }
1014     $bytextoptions = [
1015         'class' => 'author'
1016     ];
1017     $output .= html_writer::tag('address', $bytext, $bytextoptions);
1018     // End topic column.
1019     $output .= html_writer::end_div();
1021     // End header row.
1022     $output .= html_writer::end_tag('header');
1024     // Row with the forum post content.
1025     $output .= html_writer::start_div('row maincontent clearfix');
1026     // Show if author is not hidden or we have groups.
1027     if (!$authorhidden || $groups) {
1028         $output .= html_writer::start_div('left');
1029         $groupoutput = '';
1030         if ($groups) {
1031             $groupoutput = print_group_picture($groups, $course->id, false, true, true);
1032         }
1033         if (empty($groupoutput)) {
1034             $groupoutput = '&nbsp;';
1035         }
1036         $output .= html_writer::div($groupoutput, 'grouppictures');
1037         $output .= html_writer::end_div(); // Left side.
1038     }
1040     $output .= html_writer::start_tag('div', array('class'=>'no-overflow'));
1041     $output .= html_writer::start_tag('div', array('class'=>'content'));
1043     $options = new stdClass;
1044     $options->para    = false;
1045     $options->trusted = $post->messagetrust;
1046     $options->context = $modcontext;
1047     if ($shortenpost) {
1048         // Prepare shortened version by filtering the text then shortening it.
1049         $postclass    = 'shortenedpost';
1050         $postcontent  = format_text($post->message, $post->messageformat, $options);
1051         $postcontent  = shorten_text($postcontent, $CFG->forum_shortpost);
1052         $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum'));
1053         $postcontent .= html_writer::tag('div', '('.get_string('numwords', 'moodle', count_words($post->message)).')',
1054             array('class'=>'post-word-count'));
1055     } else {
1056         // Prepare whole post
1057         $postclass    = 'fullpost';
1058         $postcontent  = format_text($post->message, $post->messageformat, $options, $course->id);
1059         if (!empty($highlight)) {
1060             $postcontent = highlight($highlight, $postcontent);
1061         }
1062         if (!empty($forum->displaywordcount)) {
1063             $postcontent .= html_writer::tag('div', get_string('numwords', 'moodle', count_words($postcontent)),
1064                 array('class'=>'post-word-count'));
1065         }
1066         $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages'));
1067     }
1069     if (\core_tag_tag::is_enabled('mod_forum', 'forum_posts')) {
1070         $postcontent .= $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id), null, 'forum-tags');
1071     }
1073     // Output the post content
1074     $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass));
1075     $output .= html_writer::end_tag('div'); // Content
1076     $output .= html_writer::end_tag('div'); // Content mask
1077     $output .= html_writer::end_tag('div'); // Row
1079     $output .= html_writer::start_tag('nav', array('class' => 'row side'));
1080     $output .= html_writer::tag('div','&nbsp;', array('class'=>'left'));
1081     $output .= html_writer::start_tag('div', array('class'=>'options clearfix'));
1083     if (!empty($attachments)) {
1084         $output .= html_writer::tag('div', $attachments, array('class' => 'attachments'));
1085     }
1087     // Output ratings
1088     if (!empty($post->rating)) {
1089         $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating'));
1090     }
1092     // Output the commands
1093     $commandhtml = array();
1094     foreach ($commands as $command) {
1095         if (is_array($command)) {
1096             $attributes = ['class' => 'nav-item nav-link'];
1097             if (isset($command['attributes'])) {
1098                 $attributes = array_merge($attributes, $command['attributes']);
1099             }
1100             $commandhtml[] = html_writer::link($command['url'], $command['text'], $attributes);
1101         } else {
1102             $commandhtml[] = $command;
1103         }
1104     }
1105     $output .= html_writer::tag('div', implode(' ', $commandhtml), array('class' => 'commands nav'));
1107     // Output link to post if required
1108     if ($link) {
1109         if (forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext)) {
1110             $langstring = 'discussthistopic';
1111         } else {
1112             $langstring = 'viewthediscussion';
1113         }
1114         if ($post->replies == 1) {
1115             $replystring = get_string('repliesone', 'forum', $post->replies);
1116         } else {
1117             $replystring = get_string('repliesmany', 'forum', $post->replies);
1118         }
1119         if (!empty($discussion->unread) && $discussion->unread !== '-') {
1120             $replystring .= ' <span class="sep">/</span> <span class="unread">';
1121             $unreadlink = new moodle_url($discussionlink, null, 'unread');
1122             if ($discussion->unread == 1) {
1123                 $replystring .= html_writer::link($unreadlink, get_string('unreadpostsone', 'forum'));
1124             } else {
1125                 $replystring .= html_writer::link($unreadlink, get_string('unreadpostsnumber', 'forum', $discussion->unread));
1126             }
1127             $replystring .= '</span>';
1128         }
1130         $output .= html_writer::start_tag('div', array('class'=>'link'));
1131         $output .= html_writer::link($discussionlink, get_string($langstring, 'forum'));
1132         $output .= '&nbsp;('.$replystring.')';
1133         $output .= html_writer::end_tag('div'); // link
1134     }
1136     // Output footer if required
1137     if ($footer) {
1138         $output .= html_writer::tag('div', $footer, array('class'=>'footer'));
1139     }
1141     // Close remaining open divs
1142     $output .= html_writer::end_tag('div'); // content
1143     $output .= html_writer::end_tag('nav'); // row
1144     $output .= html_writer::end_tag('div'); // forumpost
1146     // Mark the forum post as read if required
1147     if ($istracked && !$CFG->forum_usermarksread && !$postisread) {
1148         forum_tp_mark_post_read($USER->id, $post);
1149     }
1151     if ($return) {
1152         return $output;
1153     }
1154     echo $output;
1155     return;
1158 /**
1159  * @global object
1160  * @global object
1161  * @uses FORUM_MODE_FLATNEWEST
1162  * @param object $course
1163  * @param object $cm
1164  * @param object $forum
1165  * @param object $discussion
1166  * @param object $post
1167  * @param object $mode
1168  * @param bool $reply
1169  * @param bool $forumtracked
1170  * @param array $posts
1171  * @return void
1172  * @deprecated since Moodle 3.7
1173  */
1174 function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) {
1175     debugging('forum_print_posts_flat() has been deprecated, ' .
1176         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
1177     global $USER, $CFG;
1179     $link  = false;
1181     foreach ($posts as $post) {
1182         if (!$post->parent) {
1183             continue;
1184         }
1185         $post->subject = format_string($post->subject);
1186         $ownpost = ($USER->id == $post->userid);
1188         $postread = !empty($post->postread);
1190         forum_print_post_start($post);
1191         forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
1192                              '', '', $postread, true, $forumtracked);
1193         forum_print_post_end($post);
1194     }
1197 /**
1198  * @todo Document this function
1199  *
1200  * @global object
1201  * @global object
1202  * @uses CONTEXT_MODULE
1203  * @return void
1204  * @deprecated since Moodle 3.7
1205  */
1206 function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) {
1207     debugging('forum_print_posts_threaded() has been deprecated, ' .
1208         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
1209     global $USER, $CFG;
1211     $link  = false;
1213     if (!empty($posts[$parent->id]->children)) {
1214         $posts = $posts[$parent->id]->children;
1216         $modcontext       = context_module::instance($cm->id);
1217         $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
1219         foreach ($posts as $post) {
1221             echo '<div class="indent">';
1222             if ($depth > 0) {
1223                 $ownpost = ($USER->id == $post->userid);
1224                 $post->subject = format_string($post->subject);
1226                 $postread = !empty($post->postread);
1228                 forum_print_post_start($post);
1229                 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
1230                                      '', '', $postread, true, $forumtracked);
1231                 forum_print_post_end($post);
1232             } else {
1233                 if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, true)) {
1234                     if (forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) {
1235                         // This post has been deleted but still exists and may have children.
1236                         $subject = get_string('privacy:request:delete:post:subject', 'mod_forum');
1237                         $byline = '';
1238                     } else {
1239                         // The user can't see this post at all.
1240                         echo "</div>\n";
1241                         continue;
1242                     }
1243                 } else {
1244                     $by = new stdClass();
1245                     $by->name = fullname($post, $canviewfullnames);
1246                     $by->date = userdate_htmltime($post->modified);
1247                     $byline = ' ' . get_string("bynameondate", "forum", $by);
1248                     $subject = format_string($post->subject, true);
1249                 }
1251                 if ($forumtracked) {
1252                     if (!empty($post->postread)) {
1253                         $style = '<span class="forumthread read">';
1254                     } else {
1255                         $style = '<span class="forumthread unread">';
1256                     }
1257                 } else {
1258                     $style = '<span class="forumthread">';
1259                 }
1261                 echo $style;
1262                 echo "<a name='{$post->id}'></a>";
1263                 echo html_writer::link(new moodle_url('/mod/forum/discuss.php', [
1264                         'd' => $post->discussion,
1265                         'parent' => $post->id,
1266                     ]), $subject);
1267                 echo $byline;
1268                 echo "</span>";
1269             }
1271             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts);
1272             echo "</div>\n";
1273         }
1274     }
1277 /**
1278  * @todo Document this function
1279  * @global object
1280  * @global object
1281  * @return void
1282  * @deprecated since Moodle 3.7
1283  */
1284 function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) {
1285     debugging('forum_print_posts_nested() has been deprecated, ' .
1286         'please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER);
1287     global $USER, $CFG;
1289     $link  = false;
1291     if (!empty($posts[$parent->id]->children)) {
1292         $posts = $posts[$parent->id]->children;
1294         foreach ($posts as $post) {
1296             echo '<div class="indent">';
1297             if (!isloggedin()) {
1298                 $ownpost = false;
1299             } else {
1300                 $ownpost = ($USER->id == $post->userid);
1301             }
1303             $post->subject = format_string($post->subject);
1304             $postread = !empty($post->postread);
1306             forum_print_post_start($post);
1307             forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
1308                                  '', '', $postread, true, $forumtracked);
1309             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
1310             forum_print_post_end($post);
1311             echo "</div>\n";
1312         }
1313     }
1316 /**
1317  * Prints the discussion view screen for a forum.
1318  *
1319  * @param object $course The current course object.
1320  * @param object $forum Forum to be printed.
1321  * @param int $maxdiscussions
1322  * @param string $displayformat The display format to use (optional).
1323  * @param string $sort Sort arguments for database query (optional).
1324  * @param int $currentgroup
1325  * @param int $groupmode Group mode of the forum (optional).
1326  * @param int $page Page mode, page to display (optional).
1327  * @param int $perpage The maximum number of discussions per page(optional)
1328  * @param stdClass $cm
1329  * @deprecated since Moodle 3.7
1330  */
1331 function forum_print_latest_discussions($course, $forum, $maxdiscussions = -1, $displayformat = 'plain', $sort = '',
1332                                         $currentgroup = -1, $groupmode = -1, $page = -1, $perpage = 100, $cm = null) {
1333     debugging('forum_print_latest_discussions has been deprecated.', DEBUG_DEVELOPER);
1334     global $CFG, $USER, $OUTPUT;
1336     require_once($CFG->dirroot . '/course/lib.php');
1338     if (!$cm) {
1339         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
1340             print_error('invalidcoursemodule');
1341         }
1342     }
1343     $context = context_module::instance($cm->id);
1345     if (empty($sort)) {
1346         $sort = forum_get_default_sort_order();
1347     }
1349     $olddiscussionlink = false;
1351     // Sort out some defaults.
1352     if ($perpage <= 0) {
1353         $perpage = 0;
1354         $page    = -1;
1355     }
1357     if ($maxdiscussions == 0) {
1358         // All discussions - backwards compatibility.
1359         $page    = -1;
1360         $perpage = 0;
1361         if ($displayformat == 'plain') {
1362             $displayformat = 'header';  // Abbreviate display by default.
1363         }
1365     } else if ($maxdiscussions > 0) {
1366         $page    = -1;
1367         $perpage = $maxdiscussions;
1368     }
1370     $fullpost = false;
1371     if ($displayformat == 'plain') {
1372         $fullpost = true;
1373     }
1375     // Decide if current user is allowed to see ALL the current discussions or not.
1376     // First check the group stuff.
1377     if ($currentgroup == -1 or $groupmode == -1) {
1378         $groupmode    = groups_get_activity_groupmode($cm, $course);
1379         $currentgroup = groups_get_activity_group($cm);
1380     }
1382     // Cache.
1383     $groups = array();
1385     // If the user can post discussions, then this is a good place to put the
1386     // button for it. We do not show the button if we are showing site news
1387     // and the current user is a guest.
1389     $canstart = forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context);
1390     if (!$canstart and $forum->type !== 'news') {
1391         if (isguestuser() or !isloggedin()) {
1392             $canstart = true;
1393         }
1394         if (!is_enrolled($context) and !is_viewing($context)) {
1395             // Allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link
1396             // normal users with temporary guest access see this button too, they are asked to enrol instead
1397             // do not show the button to users with suspended enrolments here.
1398             $canstart = enrol_selfenrol_available($course->id);
1399         }
1400     }
1402     if ($canstart) {
1403         switch ($forum->type) {
1404             case 'news':
1405             case 'blog':
1406                 $buttonadd = get_string('addanewtopic', 'forum');
1407                 break;
1408             case 'qanda':
1409                 $buttonadd = get_string('addanewquestion', 'forum');
1410                 break;
1411             default:
1412                 $buttonadd = get_string('addanewdiscussion', 'forum');
1413                 break;
1414         }
1415         $button = new single_button(new moodle_url('/mod/forum/post.php', ['forum' => $forum->id]), $buttonadd, 'get');
1416         $button->class = 'singlebutton forumaddnew';
1417         $button->formid = 'newdiscussionform';
1418         echo $OUTPUT->render($button);
1420     } else if (isguestuser() or !isloggedin() or $forum->type == 'news' or
1421         $forum->type == 'qanda' and !has_capability('mod/forum:addquestion', $context) or
1422         $forum->type != 'qanda' and !has_capability('mod/forum:startdiscussion', $context)) {
1423         // No button and no info.
1424         $ignore = true;
1425     } else if ($groupmode and !has_capability('moodle/site:accessallgroups', $context)) {
1426         // Inform users why they can not post new discussion.
1427         if (!$currentgroup) {
1428             if (!has_capability('mod/forum:canposttomygroups', $context)) {
1429                 echo $OUTPUT->notification(get_string('cannotadddiscussiongroup', 'forum'));
1430             } else {
1431                 echo $OUTPUT->notification(get_string('cannotadddiscussionall', 'forum'));
1432             }
1433         } else if (!groups_is_member($currentgroup)) {
1434             echo $OUTPUT->notification(get_string('cannotadddiscussion', 'forum'));
1435         }
1436     }
1438     // Get all the recent discussions we're allowed to see.
1440     $getuserlastmodified = ($displayformat == 'header');
1442     $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage);
1443     if (!$discussions) {
1444         echo '<div class="forumnodiscuss">';
1445         if ($forum->type == 'news') {
1446             echo '('.get_string('nonews', 'forum').')';
1447         } else if ($forum->type == 'qanda') {
1448             echo '('.get_string('noquestions', 'forum').')';
1449         } else {
1450             echo '('.get_string('nodiscussions', 'forum').')';
1451         }
1452         echo "</div>\n";
1453         return;
1454     }
1456     $canseeprivatereplies = has_capability('mod/forum:readprivatereplies', $context);
1457     // If we want paging.
1458     if ($page != -1) {
1459         // Get the number of discussions found.
1460         $numdiscussions = forum_get_discussions_count($cm);
1462         // Show the paging bar.
1463         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
1464         if ($numdiscussions > 1000) {
1465             // Saves some memory on sites with very large forums.
1466             $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage, $canseeprivatereplies);
1467         } else {
1468             $replies = forum_count_discussion_replies($forum->id, "", -1, -1, 0, $canseeprivatereplies);
1469         }
1471     } else {
1472         $replies = forum_count_discussion_replies($forum->id, "", -1, -1, 0, $canseeprivatereplies);
1474         if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) {
1475             $olddiscussionlink = true;
1476         }
1477     }
1479     $canviewparticipants = course_can_view_participants($context);
1480     $canviewhiddentimedposts = has_capability('mod/forum:viewhiddentimedposts', $context);
1482     $strdatestring = get_string('strftimerecentfull');
1484     // Check if the forum is tracked.
1485     if ($cantrack = forum_tp_can_track_forums($forum)) {
1486         $forumtracked = forum_tp_is_tracked($forum);
1487     } else {
1488         $forumtracked = false;
1489     }
1491     if ($forumtracked) {
1492         $unreads = forum_get_discussions_unread($cm);
1493     } else {
1494         $unreads = array();
1495     }
1497     if ($displayformat == 'header') {
1498         echo '<table cellspacing="0" class="forumheaderlist">';
1499         echo '<thead class="text-left">';
1500         echo '<tr>';
1501         echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
1502         echo '<th class="header author" scope="col">'.get_string('startedby', 'forum').'</th>';
1503         if ($groupmode > 0) {
1504             echo '<th class="header group" scope="col">'.get_string('group').'</th>';
1505         }
1506         if (has_capability('mod/forum:viewdiscussion', $context)) {
1507             echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
1508             // If the forum can be tracked, display the unread column.
1509             if ($cantrack) {
1510                 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
1511                 if ($forumtracked) {
1512                     echo '<a title="'.get_string('markallread', 'forum').
1513                          '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
1514                          $forum->id.'&amp;mark=read&amp;return=/mod/forum/view.php&amp;sesskey=' . sesskey() . '">'.
1515                          $OUTPUT->pix_icon('t/markasread', get_string('markallread', 'forum')) . '</a>';
1516                 }
1517                 echo '</th>';
1518             }
1519         }
1520         echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
1521         if ((!is_guest($context, $USER) && isloggedin()) && has_capability('mod/forum:viewdiscussion', $context)) {
1522             if (\mod_forum\subscriptions::is_subscribable($forum)) {
1523                 echo '<th class="header discussionsubscription" scope="col">';
1524                 echo forum_get_discussion_subscription_icon_preloaders();
1525                 echo '</th>';
1526             }
1527         }
1528         echo '</tr>';
1529         echo '</thead>';
1530         echo '<tbody>';
1531     }
1533     foreach ($discussions as $discussion) {
1534         if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $context) &&
1535             !forum_user_has_posted($forum->id, $discussion->discussion, $USER->id)) {
1536             $canviewparticipants = false;
1537         }
1539         if (!empty($replies[$discussion->discussion])) {
1540             $discussion->replies = $replies[$discussion->discussion]->replies;
1541             $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
1542         } else {
1543             $discussion->replies = 0;
1544         }
1546         // SPECIAL CASE: The front page can display a news item post to non-logged in users.
1547         // All posts are read in this case.
1548         if (!$forumtracked) {
1549             $discussion->unread = '-';
1550         } else if (empty($USER)) {
1551             $discussion->unread = 0;
1552         } else {
1553             if (empty($unreads[$discussion->discussion])) {
1554                 $discussion->unread = 0;
1555             } else {
1556                 $discussion->unread = $unreads[$discussion->discussion];
1557             }
1558         }
1560         if (isloggedin()) {
1561             $ownpost = ($discussion->userid == $USER->id);
1562         } else {
1563             $ownpost = false;
1564         }
1565         // Use discussion name instead of subject of first post.
1566         $discussion->subject = $discussion->name;
1568         switch ($displayformat) {
1569             case 'header':
1570                 if ($groupmode > 0) {
1571                     if (isset($groups[$discussion->groupid])) {
1572                         $group = $groups[$discussion->groupid];
1573                     } else {
1574                         $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid);
1575                     }
1576                 } else {
1577                     $group = -1;
1578                 }
1579                 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
1580                     $canviewparticipants, $context, $canviewhiddentimedposts);
1581             break;
1582             default:
1583                 $link = false;
1585                 if ($discussion->replies) {
1586                     $link = true;
1587                 } else {
1588                     $modcontext = context_module::instance($cm->id);
1589                     $link = forum_user_can_see_discussion($forum, $discussion, $modcontext, $USER);
1590                 }
1592                 $discussion->forum = $forum->id;
1594                 forum_print_post_start($discussion);
1595                 forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false,
1596                         '', null, true, $forumtracked);
1597                 forum_print_post_end($discussion);
1598             break;
1599         }
1600     }
1602     if ($displayformat == "header") {
1603         echo '</tbody>';
1604         echo '</table>';
1605     }
1607     if ($olddiscussionlink) {
1608         if ($forum->type == 'news') {
1609             $strolder = get_string('oldertopics', 'forum');
1610         } else {
1611             $strolder = get_string('olderdiscussions', 'forum');
1612         }
1613         echo '<div class="forumolddiscuss">';
1614         echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
1615         echo $strolder.'</a> ...</div>';
1616     }
1618     if ($page != -1) {
1619         // Show the paging bar.
1620         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
1621     }
1624 /**
1625  * Count the number of replies to the specified post.
1626  *
1627  * @param object $post
1628  * @param bool $children
1629  * @return int
1630  * @deprecated since Moodle 3.7
1631  * @todo MDL-65252 This will be removed in Moodle 3.11
1632  */
1633 function forum_count_replies($post, $children = true) {
1634     global $USER;
1635     debugging('forum_count_replies has been deprecated. Please use the Post vault instead.', DEBUG_DEVELOPER);
1637     if (!$children) {
1638         return $DB->count_records('forum_posts', array('parent' => $post->id));
1639     }
1641     $entityfactory = mod_forum\local\container::get_entity_factory();
1642     $postentity = $entityfactory->get_post_from_stdclass($post);
1644     $vaultfactory = mod_forum\local\container::get_vault_factory();
1645     $postvault = $vaultfactory->get_post_vault();
1647     return $postvault->get_reply_count_for_post_id_in_discussion_id(
1648             $USER,
1649             $postentity->get_id(),
1650             $postentity->get_discussion_id(),
1651             true
1652         );
1655 /**
1656  * @deprecated since Moodle 3.8
1657  */
1658 function forum_scale_used() {
1659     throw new coding_exception('forum_scale_used() can not be used anymore. Plugins can implement ' .
1660         '<modname>_scale_used_anywhere, all implementations of <modname>_scale_used are now ignored');
1663 /**
1664  * Return grade for given user or all users.
1665  *
1666  * @deprecated since Moodle 3.8
1667  * @param object $forum
1668  * @param int $userid optional user id, 0 means all users
1669  * @return array array of grades, false if none
1670  */
1671 function forum_get_user_grades($forum, $userid = 0) {
1672     global $CFG;
1674     require_once($CFG->dirroot.'/rating/lib.php');
1676     $ratingoptions = (object) [
1677         'component' => 'mod_forum',
1678         'ratingarea' => 'post',
1679         'contextid' => $contextid,
1681         'modulename' => 'forum',
1682         'moduleid  ' => $forum->id,
1683         'userid' => $userid,
1684         'aggregationmethod' => $forum->assessed,
1685         'scaleid' => $forum->scale,
1686         'itemtable' => 'forum_posts',
1687         'itemtableusercolumn' => 'userid',
1688     ];
1690     $rm = new rating_manager();
1691     return $rm->get_user_grades($ratingoptions);