See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
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->url); 396 unset($user->currentlogin); 397 unset($user->description); 398 unset($user->descriptionformat); 399 } 400 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); 412 } 413 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); 435 436 global $USER, $CFG; 437 438 require_once($CFG->dirroot.'/rating/lib.php'); 439 440 $ownpost = (isloggedin() && $USER->id == $post->userid); 441 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 } 448 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(); 453 454 $posters = array(); 455 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 } 462 463 $forumtracked = forum_tp_is_tracked($forum); 464 $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); 465 $post = $posts[$post->id]; 466 467 foreach ($posts as $pid=>$p) { 468 $posters[$p->userid] = $p->userid; 469 } 470 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 } 481 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; 499 500 $rm = new rating_manager(); 501 $posts = $rm->get_ratings($ratingoptions); 502 } 503 504 505 $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post 506 $post->forumtype = $forum->type; 507 508 $post->subject = format_string($post->subject); 509 510 $postread = !empty($post->postread); 511 512 forum_print_post_start($post); 513 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false, 514 '', '', $postread, true, $forumtracked); 515 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; 522 523 case FORUM_MODE_THREADED : 524 forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts); 525 break; 526 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); 532 } 533 534 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; 546 } 547 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 } 566 } 567 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; 589 } 590 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 = ''; 603 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; 617 } 618 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 = ''; 631 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; 640 } 641 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; 683 684 require_once($CFG->libdir . '/filelib.php'); 685 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; 694 695 $modcontext = context_module::instance($cm->id); 696 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 } 708 709 // caching 710 if (!isset($cm->cache)) { 711 $cm->cache = new stdClass; 712 } 713 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 } 726 727 if (!isset($cm->uservisible)) { 728 $cm->uservisible = \core_availability\info_module::is_user_visible($cm, 0, false); 729 } 730 731 if ($istracked && is_null($postisread)) { 732 $postisread = forum_tp_is_post_read($USER->id, $post); 733 } 734 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 } 745 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', ' ', 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 767 768 if ($return) { 769 return $output; 770 } 771 echo $output; 772 return; 773 } 774 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 ]); 790 791 $output .= html_writer::start_tag('header', array('class' => 'row header')); 792 $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); 793 794 $classes = ['topic']; 795 if (!empty($post->parent)) { 796 $classes[] = 'starter'; 797 } 798 $output .= html_writer::start_tag('div', ['class' => implode(' ', $classes)]); 799 800 // Subject. 801 $output .= html_writer::tag('div', get_string('forumsubjectdeleted', 'forum'), [ 802 'class' => 'subject', 803 'role' => 'header', 804 'id' => ('headp' . $post->id) 805 ]); 806 807 // Author. 808 $output .= html_writer::tag('address', '', ['class' => 'author']); 809 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', ' ', ['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. 817 818 if ($return) { 819 return $output; 820 } 821 echo $output; 822 return; 823 } 824 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 } 837 838 $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion)); 839 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)); 847 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 } 859 860 // Prepare the attachements for the post, files then images 861 list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages'); 862 863 // Determine if we need to shorten this post 864 $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost)); 865 866 // Prepare an array of commands 867 $commands = array(); 868 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']); 873 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 } 890 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 } 901 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 } 907 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 } 916 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 } 920 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 } 926 927 if ($reply) { 928 $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply); 929 } 930 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 } 941 942 $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK); 943 if (!empty($porfoliohtml)) { 944 $commands[] = $porfoliohtml; 945 } 946 } 947 // Finished building commands 948 949 950 // Begin output 951 952 $output = ''; 953 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 } 969 970 $topicclass = ''; 971 if (empty($post->parent)) { 972 $topicclass = ' firstpost starter'; 973 } 974 975 if (!empty($post->lastpost)) { 976 $forumpostclass .= ' lastpost'; 977 } 978 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']); 990 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 } 997 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)]); 1005 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(); 1020 1021 // End header row. 1022 $output .= html_writer::end_tag('header'); 1023 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 = ' '; 1035 } 1036 $output .= html_writer::div($groupoutput, 'grouppictures'); 1037 $output .= html_writer::end_div(); // Left side. 1038 } 1039 1040 $output .= html_writer::start_tag('div', array('class'=>'no-overflow')); 1041 $output .= html_writer::start_tag('div', array('class'=>'content')); 1042 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 } 1068 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 } 1072 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 1078 1079 $output .= html_writer::start_tag('nav', array('class' => 'row side')); 1080 $output .= html_writer::tag('div',' ', array('class'=>'left')); 1081 $output .= html_writer::start_tag('div', array('class'=>'options clearfix')); 1082 1083 if (!empty($attachments)) { 1084 $output .= html_writer::tag('div', $attachments, array('class' => 'attachments')); 1085 } 1086 1087 // Output ratings 1088 if (!empty($post->rating)) { 1089 $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating')); 1090 } 1091 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')); 1106 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 } 1129 1130 $output .= html_writer::start_tag('div', array('class'=>'link')); 1131 $output .= html_writer::link($discussionlink, get_string($langstring, 'forum')); 1132 $output .= ' ('.$replystring.')'; 1133 $output .= html_writer::end_tag('div'); // link 1134 } 1135 1136 // Output footer if required 1137 if ($footer) { 1138 $output .= html_writer::tag('div', $footer, array('class'=>'footer')); 1139 } 1140 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 1145 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 } 1150 1151 if ($return) { 1152 return $output; 1153 } 1154 echo $output; 1155 return; 1156 } 1157 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; 1178 1179 $link = false; 1180 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); 1187 1188 $postread = !empty($post->postread); 1189 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 } 1195 } 1196 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; 1210 1211 $link = false; 1212 1213 if (!empty($posts[$parent->id]->children)) { 1214 $posts = $posts[$parent->id]->children; 1215 1216 $modcontext = context_module::instance($cm->id); 1217 $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext); 1218 1219 foreach ($posts as $post) { 1220 1221 echo '<div class="indent">'; 1222 if ($depth > 0) { 1223 $ownpost = ($USER->id == $post->userid); 1224 $post->subject = format_string($post->subject); 1225 1226 $postread = !empty($post->postread); 1227 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 } 1250 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 } 1260 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 } 1270 1271 forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts); 1272 echo "</div>\n"; 1273 } 1274 } 1275 } 1276 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; 1288 1289 $link = false; 1290 1291 if (!empty($posts[$parent->id]->children)) { 1292 $posts = $posts[$parent->id]->children; 1293 1294 foreach ($posts as $post) { 1295 1296 echo '<div class="indent">'; 1297 if (!isloggedin()) { 1298 $ownpost = false; 1299 } else { 1300 $ownpost = ($USER->id == $post->userid); 1301 } 1302 1303 $post->subject = format_string($post->subject); 1304 $postread = !empty($post->postread); 1305 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 } 1314 } 1315 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; 1335 1336 require_once($CFG->dirroot . '/course/lib.php'); 1337 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); 1344 1345 if (empty($sort)) { 1346 $sort = forum_get_default_sort_order(); 1347 } 1348 1349 $olddiscussionlink = false; 1350 1351 // Sort out some defaults. 1352 if ($perpage <= 0) { 1353 $perpage = 0; 1354 $page = -1; 1355 } 1356 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 } 1364 1365 } else if ($maxdiscussions > 0) { 1366 $page = -1; 1367 $perpage = $maxdiscussions; 1368 } 1369 1370 $fullpost = false; 1371 if ($displayformat == 'plain') { 1372 $fullpost = true; 1373 } 1374 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 } 1381 1382 // Cache. 1383 $groups = array(); 1384 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. 1388 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 } 1401 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); 1419 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 } 1437 1438 // Get all the recent discussions we're allowed to see. 1439 1440 $getuserlastmodified = ($displayformat == 'header'); 1441 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 } 1455 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); 1461 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 } 1470 1471 } else { 1472 $replies = forum_count_discussion_replies($forum->id, "", -1, -1, 0, $canseeprivatereplies); 1473 1474 if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) { 1475 $olddiscussionlink = true; 1476 } 1477 } 1478 1479 $canviewparticipants = course_can_view_participants($context); 1480 $canviewhiddentimedposts = has_capability('mod/forum:viewhiddentimedposts', $context); 1481 1482 $strdatestring = get_string('strftimerecentfull'); 1483 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 } 1490 1491 if ($forumtracked) { 1492 $unreads = forum_get_discussions_unread($cm); 1493 } else { 1494 $unreads = array(); 1495 } 1496 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.'&mark=read&return=/mod/forum/view.php&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 } 1532 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 } 1538 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 } 1545 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 } 1559 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; 1567 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; 1584 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 } 1591 1592 $discussion->forum = $forum->id; 1593 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 } 1601 1602 if ($displayformat == "header") { 1603 echo '</tbody>'; 1604 echo '</table>'; 1605 } 1606 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.'&showall=1">'; 1615 echo $strolder.'</a> ...</div>'; 1616 } 1617 1618 if ($page != -1) { 1619 // Show the paging bar. 1620 echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id"); 1621 } 1622 } 1623 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 4.1 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); 1636 1637 if (!$children) { 1638 return $DB->count_records('forum_posts', array('parent' => $post->id)); 1639 } 1640 1641 $entityfactory = mod_forum\local\container::get_entity_factory(); 1642 $postentity = $entityfactory->get_post_from_stdclass($post); 1643 1644 $vaultfactory = mod_forum\local\container::get_vault_factory(); 1645 $postvault = $vaultfactory->get_post_vault(); 1646 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 ); 1653 } 1654 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'); 1661 } 1662 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; 1673 1674 require_once($CFG->dirroot.'/rating/lib.php'); 1675 1676 $ratingoptions = (object) [ 1677 'component' => 'mod_forum', 1678 'ratingarea' => 'post', 1679 'contextid' => $contextid, 1680 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 ]; 1689 1690 $rm = new rating_manager(); 1691 return $rm->get_user_grades($ratingoptions); 1692 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body