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