From 2e19ca183b0fd9580c3138b21e21748c2cb4ecfb Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Fri, 8 Mar 2019 15:03:28 +0800 Subject: [PATCH] MDL-64820 forum: deprecate forum print functions --- mod/forum/deprecatedlib.php | 892 +++++++++++++++++++++++++++++++++++ mod/forum/lib.php | 903 +----------------------------------- 2 files changed, 911 insertions(+), 884 deletions(-) diff --git a/mod/forum/deprecatedlib.php b/mod/forum/deprecatedlib.php index 2ca2ee7a56a..ad61a8a2e0a 100644 --- a/mod/forum/deprecatedlib.php +++ b/mod/forum/deprecatedlib.php @@ -410,3 +410,895 @@ function forum_cron() { debugging("forum_cron() has been deprecated and replaced with new tasks. Please uses these instead.", DEBUG_DEVELOPER); } + +/** + * Prints a forum discussion + * + * @uses CONTEXT_MODULE + * @uses FORUM_MODE_FLATNEWEST + * @uses FORUM_MODE_FLATOLDEST + * @uses FORUM_MODE_THREADED + * @uses FORUM_MODE_NESTED + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $forum + * @param stdClass $discussion + * @param stdClass $post + * @param int $mode + * @param mixed $canreply + * @param bool $canrate + * @deprecated since Moodle 3.7 + */ +function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) { + debugging('forum_print_discussion() has been deprecated, please use \mod_forum\local\renderers\discussion instead.', DEBUG_DEVELOPER); + + global $USER, $CFG; + + require_once($CFG->dirroot.'/rating/lib.php'); + + $ownpost = (isloggedin() && $USER->id == $post->userid); + + $modcontext = context_module::instance($cm->id); + if ($canreply === NULL) { + $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); + } else { + $reply = $canreply; + } + + // $cm holds general cache for forum functions + $cm->cache = new stdClass; + $cm->cache->groups = groups_get_all_groups($course->id, 0, $cm->groupingid); + $cm->cache->usersgroups = array(); + + $posters = array(); + + // preload all posts - TODO: improve... + if ($mode == FORUM_MODE_FLATNEWEST) { + $sort = "p.created DESC"; + } else { + $sort = "p.created ASC"; + } + + $forumtracked = forum_tp_is_tracked($forum); + $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); + $post = $posts[$post->id]; + + foreach ($posts as $pid=>$p) { + $posters[$p->userid] = $p->userid; + } + + // preload all groups of ppl that posted in this discussion + if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) { + foreach($postersgroups as $pg) { + if (!isset($cm->cache->usersgroups[$pg->userid])) { + $cm->cache->usersgroups[$pg->userid] = array(); + } + $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid; + } + unset($postersgroups); + } + + //load ratings + if ($forum->assessed != RATING_AGGREGATE_NONE) { + $ratingoptions = new stdClass; + $ratingoptions->context = $modcontext; + $ratingoptions->component = 'mod_forum'; + $ratingoptions->ratingarea = 'post'; + $ratingoptions->items = $posts; + $ratingoptions->aggregate = $forum->assessed;//the aggregation method + $ratingoptions->scaleid = $forum->scale; + $ratingoptions->userid = $USER->id; + if ($forum->type == 'single' or !$discussion->id) { + $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id"; + } else { + $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id"; + } + $ratingoptions->assesstimestart = $forum->assesstimestart; + $ratingoptions->assesstimefinish = $forum->assesstimefinish; + + $rm = new rating_manager(); + $posts = $rm->get_ratings($ratingoptions); + } + + + $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post + $post->forumtype = $forum->type; + + $post->subject = format_string($post->subject); + + $postread = !empty($post->postread); + + forum_print_post_start($post); + forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false, + '', '', $postread, true, $forumtracked); + + switch ($mode) { + case FORUM_MODE_FLATOLDEST : + case FORUM_MODE_FLATNEWEST : + default: + forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts); + break; + + case FORUM_MODE_THREADED : + forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts); + break; + + case FORUM_MODE_NESTED : + forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); + break; + } + forum_print_post_end($post); +} + + +/** + * Return a static array of posts that are open. + * + * @return array + * @deprecated since Moodle 3.7 + */ +function forum_post_nesting_cache() { + debugging('forum_post_nesting_cache() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + static $nesting = array(); + return $nesting; +} + +/** + * Return true for the first time this post was started + * + * @param int $id The id of the post to start + * @return bool + * @deprecated since Moodle 3.7 + */ +function forum_should_start_post_nesting($id) { + debugging('forum_should_start_post_nesting() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + $cache = forum_post_nesting_cache(); + if (!array_key_exists($id, $cache)) { + $cache[$id] = 1; + return true; + } else { + $cache[$id]++; + return false; + } +} + +/** + * Return true when all the opens are nested with a close. + * + * @param int $id The id of the post to end + * @return bool + * @deprecated since Moodle 3.7 + */ +function forum_should_end_post_nesting($id) { + debugging('forum_should_end_post_nesting() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + $cache = forum_post_nesting_cache(); + if (!array_key_exists($id, $cache)) { + return true; + } else { + $cache[$id]--; + if ($cache[$id] == 0) { + unset($cache[$id]); + return true; + } + } + return false; +} + +/** + * Start a forum post container + * + * @param object $post The post to print. + * @param bool $return Return the string or print it + * @return string + * @deprecated since Moodle 3.7 + */ +function forum_print_post_start($post, $return = false) { + debugging('forum_print_post_start() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + $output = ''; + + if (forum_should_start_post_nesting($post->id)) { + $attributes = [ + 'id' => 'p'.$post->id, + 'tabindex' => -1, + 'class' => 'relativelink' + ]; + $output .= html_writer::start_tag('article', $attributes); + } + if ($return) { + return $output; + } + echo $output; + return; +} + +/** + * End a forum post container + * + * @param object $post The post to print. + * @param bool $return Return the string or print it + * @return string + * @deprecated since Moodle 3.7 + */ +function forum_print_post_end($post, $return = false) { + debugging('forum_print_post_end() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + $output = ''; + + if (forum_should_end_post_nesting($post->id)) { + $output .= html_writer::end_tag('article'); + } + if ($return) { + return $output; + } + echo $output; + return; +} + +/** + * Print a forum post + * This function should always be surrounded with calls to forum_print_post_start + * and forum_print_post_end to create the surrounding container for the post. + * Replies can be nested before forum_print_post_end and should reflect the structure of + * thread. + * + * @global object + * @global object + * @uses FORUM_MODE_THREADED + * @uses PORTFOLIO_FORMAT_PLAINHTML + * @uses PORTFOLIO_FORMAT_FILE + * @uses PORTFOLIO_FORMAT_RICHHTML + * @uses PORTFOLIO_ADD_TEXT_LINK + * @uses CONTEXT_MODULE + * @param object $post The post to print. + * @param object $discussion + * @param object $forum + * @param object $cm + * @param object $course + * @param boolean $ownpost Whether this post belongs to the current user. + * @param boolean $reply Whether to print a 'reply' link at the bottom of the message. + * @param boolean $link Just print a shortened version of the post as a link to the full post. + * @param string $footer Extra stuff to print after the message. + * @param string $highlight Space-separated list of terms to highlight. + * @param int $post_read true, false or -99. If we already know whether this user + * has read this post, pass that in, otherwise, pass in -99, and this + * function will work it out. + * @param boolean $dummyifcantsee When forum_user_can_see_post says that + * the current user can't see this post, if this argument is true + * (the default) then print a dummy 'you can't see this post' post. + * If false, don't output anything at all. + * @param bool|null $istracked + * @return void + * @deprecated since Moodle 3.7 + */ +function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false, + $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) { + debugging('forum_print_post() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + global $USER, $CFG, $OUTPUT; + + require_once($CFG->libdir . '/filelib.php'); + + // String cache + static $str; + // This is an extremely hacky way to ensure we only print the 'unread' anchor + // the first time we encounter an unread post on a page. Ideally this would + // be moved into the caller somehow, and be better testable. But at the time + // of dealing with this bug, this static workaround was the most surgical and + // it fits together with only printing th unread anchor id once on a given page. + static $firstunreadanchorprinted = false; + + $modcontext = context_module::instance($cm->id); + + $post->course = $course->id; + $post->forum = $forum->id; + $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id); + if (!empty($CFG->enableplagiarism)) { + require_once($CFG->libdir.'/plagiarismlib.php'); + $post->message .= plagiarism_get_links(array('userid' => $post->userid, + 'content' => $post->message, + 'cmid' => $cm->id, + 'course' => $post->course, + 'forum' => $post->forum)); + } + + // caching + if (!isset($cm->cache)) { + $cm->cache = new stdClass; + } + + if (!isset($cm->cache->caps)) { + $cm->cache->caps = array(); + $cm->cache->caps['mod/forum:viewdiscussion'] = has_capability('mod/forum:viewdiscussion', $modcontext); + $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modcontext); + $cm->cache->caps['mod/forum:editanypost'] = has_capability('mod/forum:editanypost', $modcontext); + $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext); + $cm->cache->caps['mod/forum:deleteownpost'] = has_capability('mod/forum:deleteownpost', $modcontext); + $cm->cache->caps['mod/forum:deleteanypost'] = has_capability('mod/forum:deleteanypost', $modcontext); + $cm->cache->caps['mod/forum:viewanyrating'] = has_capability('mod/forum:viewanyrating', $modcontext); + $cm->cache->caps['mod/forum:exportpost'] = has_capability('mod/forum:exportpost', $modcontext); + $cm->cache->caps['mod/forum:exportownpost'] = has_capability('mod/forum:exportownpost', $modcontext); + } + + if (!isset($cm->uservisible)) { + $cm->uservisible = \core_availability\info_module::is_user_visible($cm, 0, false); + } + + if ($istracked && is_null($postisread)) { + $postisread = forum_tp_is_post_read($USER->id, $post); + } + + if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) { + // Do _not_ check the deleted flag - we need to display a different UI. + $output = ''; + if (!$dummyifcantsee) { + if ($return) { + return $output; + } + echo $output; + return; + } + + $output .= html_writer::start_tag('div', array('class' => 'forumpost clearfix', + 'aria-label' => get_string('hiddenforumpost', 'forum'))); + $output .= html_writer::start_tag('header', array('class' => 'row header')); + $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); // Picture. + if ($post->parent) { + $output .= html_writer::start_tag('div', array('class' => 'topic')); + } else { + $output .= html_writer::start_tag('div', array('class' => 'topic starter')); + } + $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class' => 'subject', + 'role' => 'header', + 'id' => ('headp' . $post->id))); // Subject. + $authorclasses = array('class' => 'author'); + $output .= html_writer::tag('address', get_string('forumauthorhidden', 'forum'), $authorclasses); // Author. + $output .= html_writer::end_tag('div'); + $output .= html_writer::end_tag('header'); // Header. + $output .= html_writer::start_tag('div', array('class'=>'row')); + $output .= html_writer::tag('div', ' ', array('class'=>'left side')); // Groups + $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content + $output .= html_writer::end_tag('div'); // row + $output .= html_writer::end_tag('div'); // forumpost + + if ($return) { + return $output; + } + echo $output; + return; + } + + if (!empty($post->deleted)) { + // Note: Posts marked as deleted are still returned by the above forum_user_can_post because it is required for + // nesting of posts. + $output = ''; + if (!$dummyifcantsee) { + if ($return) { + return $output; + } + echo $output; + return; + } + $output .= html_writer::start_tag('div', [ + 'class' => 'forumpost clearfix', + 'aria-label' => get_string('forumbodydeleted', 'forum'), + ]); + + $output .= html_writer::start_tag('header', array('class' => 'row header')); + $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); + + $classes = ['topic']; + if (!empty($post->parent)) { + $classes[] = 'starter'; + } + $output .= html_writer::start_tag('div', ['class' => implode(' ', $classes)]); + + // Subject. + $output .= html_writer::tag('div', get_string('forumsubjectdeleted', 'forum'), [ + 'class' => 'subject', + 'role' => 'header', + 'id' => ('headp' . $post->id) + ]); + + // Author. + $output .= html_writer::tag('address', '', ['class' => 'author']); + + $output .= html_writer::end_tag('div'); + $output .= html_writer::end_tag('header'); // End header. + $output .= html_writer::start_tag('div', ['class' => 'row']); + $output .= html_writer::tag('div', ' ', ['class' => 'left side']); // Groups. + $output .= html_writer::tag('div', get_string('forumbodydeleted', 'forum'), ['class' => 'content']); // Content. + $output .= html_writer::end_tag('div'); // End row. + $output .= html_writer::end_tag('div'); // End forumpost. + + if ($return) { + return $output; + } + echo $output; + return; + } + + if (empty($str)) { + $str = new stdClass; + $str->edit = get_string('edit', 'forum'); + $str->delete = get_string('delete', 'forum'); + $str->reply = get_string('reply', 'forum'); + $str->parent = get_string('parent', 'forum'); + $str->pruneheading = get_string('pruneheading', 'forum'); + $str->prune = get_string('prune', 'forum'); + $str->displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode); + $str->markread = get_string('markread', 'forum'); + $str->markunread = get_string('markunread', 'forum'); + } + + $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion)); + + // Build an object that represents the posting user + $postuser = new stdClass; + $postuserfields = explode(',', user_picture::fields()); + $postuser = username_load_fields_from_object($postuser, $post, null, $postuserfields); + $postuser->id = $post->userid; + $postuser->fullname = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']); + $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id)); + + // Prepare the groups the posting user belongs to + if (isset($cm->cache->usersgroups)) { + $groups = array(); + if (isset($cm->cache->usersgroups[$post->userid])) { + foreach ($cm->cache->usersgroups[$post->userid] as $gid) { + $groups[$gid] = $cm->cache->groups[$gid]; + } + } + } else { + $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid); + } + + // Prepare the attachements for the post, files then images + list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages'); + + // Determine if we need to shorten this post + $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost)); + + // Prepare an array of commands + $commands = array(); + + // Add a permalink. + $permalink = new moodle_url($discussionlink); + $permalink->set_anchor('p' . $post->id); + $commands[] = array('url' => $permalink, 'text' => get_string('permalink', 'forum'), 'attributes' => ['rel' => 'bookmark']); + + // SPECIAL CASE: The front page can display a news item post to non-logged in users. + // Don't display the mark read / unread controls in this case. + if ($istracked && $CFG->forum_usermarksread && isloggedin()) { + $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread')); + $text = $str->markunread; + if (!$postisread) { + $url->param('mark', 'read'); + $text = $str->markread; + } + if ($str->displaymode == FORUM_MODE_THREADED) { + $url->param('parent', $post->parent); + } else { + $url->set_anchor('p'.$post->id); + } + $commands[] = array('url'=>$url, 'text'=>$text, 'attributes' => ['rel' => 'bookmark']); + } + + // Zoom in to the parent specifically + if ($post->parent) { + $url = new moodle_url($discussionlink); + if ($str->displaymode == FORUM_MODE_THREADED) { + $url->param('parent', $post->parent); + } else { + $url->set_anchor('p'.$post->parent); + } + $commands[] = array('url'=>$url, 'text'=>$str->parent, 'attributes' => ['rel' => 'bookmark']); + } + + // Hack for allow to edit news posts those are not displayed yet until they are displayed + $age = time() - $post->created; + if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) { + $age = 0; + } + + if ($forum->type == 'single' and $discussion->firstpost == $post->id) { + if (has_capability('moodle/course:manageactivities', $modcontext)) { + // The first post in single simple is the forum description. + $commands[] = array('url'=>new moodle_url('/course/modedit.php', array('update'=>$cm->id, 'sesskey'=>sesskey(), 'return'=>1)), 'text'=>$str->edit); + } + } else if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) { + $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit); + } + + if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') { + $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading); + } + + if ($forum->type == 'single' and $discussion->firstpost == $post->id) { + // Do not allow deleting of first post in single simple type. + } else if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) { + $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete); + } + + if ($reply) { + $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply); + } + + if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) { + $p = array('postid' => $post->id); + require_once($CFG->libdir.'/portfoliolib.php'); + $button = new portfolio_add_button(); + $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), 'mod_forum'); + if (empty($attachments)) { + $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); + } else { + $button->set_formats(PORTFOLIO_FORMAT_RICHHTML); + } + + $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK); + if (!empty($porfoliohtml)) { + $commands[] = $porfoliohtml; + } + } + // Finished building commands + + + // Begin output + + $output = ''; + + if ($istracked) { + if ($postisread) { + $forumpostclass = ' read'; + } else { + $forumpostclass = ' unread'; + // If this is the first unread post printed then give it an anchor and id of unread. + if (!$firstunreadanchorprinted) { + $output .= html_writer::tag('a', '', array('id' => 'unread')); + $firstunreadanchorprinted = true; + } + } + } else { + // ignore trackign status if not tracked or tracked param missing + $forumpostclass = ''; + } + + $topicclass = ''; + if (empty($post->parent)) { + $topicclass = ' firstpost starter'; + } + + if (!empty($post->lastpost)) { + $forumpostclass .= ' lastpost'; + } + + // Flag to indicate whether we should hide the author or not. + $authorhidden = forum_is_author_hidden($post, $forum); + $postbyuser = new stdClass; + $postbyuser->post = $post->subject; + $postbyuser->user = $postuser->fullname; + $discussionbyuser = get_string('postbyuser', 'forum', $postbyuser); + // Begin forum post. + $output .= html_writer::start_div('forumpost clearfix' . $forumpostclass . $topicclass, + ['aria-label' => $discussionbyuser]); + // Begin header row. + $output .= html_writer::start_tag('header', ['class' => 'row header clearfix']); + + // User picture. + if (!$authorhidden) { + $picture = $OUTPUT->user_picture($postuser, ['courseid' => $course->id]); + $output .= html_writer::div($picture, 'left picture', ['role' => 'presentation']); + $topicclass = 'topic' . $topicclass; + } + + // Begin topic column. + $output .= html_writer::start_div($topicclass); + $postsubject = $post->subject; + if (empty($post->subjectnoformat)) { + $postsubject = format_string($postsubject); + } + $output .= html_writer::div($postsubject, 'subject', ['role' => 'heading', 'aria-level' => '1', 'id' => ('headp' . $post->id)]); + + if ($authorhidden) { + $bytext = userdate_htmltime($post->created); + } else { + $by = new stdClass(); + $by->date = userdate_htmltime($post->created); + $by->name = html_writer::link($postuser->profilelink, $postuser->fullname); + $bytext = get_string('bynameondate', 'forum', $by); + } + $bytextoptions = [ + 'class' => 'author' + ]; + $output .= html_writer::tag('address', $bytext, $bytextoptions); + // End topic column. + $output .= html_writer::end_div(); + + // End header row. + $output .= html_writer::end_tag('header'); + + // Row with the forum post content. + $output .= html_writer::start_div('row maincontent clearfix'); + // Show if author is not hidden or we have groups. + if (!$authorhidden || $groups) { + $output .= html_writer::start_div('left'); + $groupoutput = ''; + if ($groups) { + $groupoutput = print_group_picture($groups, $course->id, false, true, true); + } + if (empty($groupoutput)) { + $groupoutput = ' '; + } + $output .= html_writer::div($groupoutput, 'grouppictures'); + $output .= html_writer::end_div(); // Left side. + } + + $output .= html_writer::start_tag('div', array('class'=>'no-overflow')); + $output .= html_writer::start_tag('div', array('class'=>'content')); + + $options = new stdClass; + $options->para = false; + $options->trusted = $post->messagetrust; + $options->context = $modcontext; + if ($shortenpost) { + // Prepare shortened version by filtering the text then shortening it. + $postclass = 'shortenedpost'; + $postcontent = format_text($post->message, $post->messageformat, $options); + $postcontent = shorten_text($postcontent, $CFG->forum_shortpost); + $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum')); + $postcontent .= html_writer::tag('div', '('.get_string('numwords', 'moodle', count_words($post->message)).')', + array('class'=>'post-word-count')); + } else { + // Prepare whole post + $postclass = 'fullpost'; + $postcontent = format_text($post->message, $post->messageformat, $options, $course->id); + if (!empty($highlight)) { + $postcontent = highlight($highlight, $postcontent); + } + if (!empty($forum->displaywordcount)) { + $postcontent .= html_writer::tag('div', get_string('numwords', 'moodle', count_words($postcontent)), + array('class'=>'post-word-count')); + } + $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages')); + } + + if (\core_tag_tag::is_enabled('mod_forum', 'forum_posts')) { + $postcontent .= $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id), null, 'forum-tags'); + } + + // Output the post content + $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass)); + $output .= html_writer::end_tag('div'); // Content + $output .= html_writer::end_tag('div'); // Content mask + $output .= html_writer::end_tag('div'); // Row + + $output .= html_writer::start_tag('nav', array('class' => 'row side')); + $output .= html_writer::tag('div',' ', array('class'=>'left')); + $output .= html_writer::start_tag('div', array('class'=>'options clearfix')); + + if (!empty($attachments)) { + $output .= html_writer::tag('div', $attachments, array('class' => 'attachments')); + } + + // Output ratings + if (!empty($post->rating)) { + $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating')); + } + + // Output the commands + $commandhtml = array(); + foreach ($commands as $command) { + if (is_array($command)) { + $attributes = ['class' => 'nav-item nav-link']; + if (isset($command['attributes'])) { + $attributes = array_merge($attributes, $command['attributes']); + } + $commandhtml[] = html_writer::link($command['url'], $command['text'], $attributes); + } else { + $commandhtml[] = $command; + } + } + $output .= html_writer::tag('div', implode(' ', $commandhtml), array('class' => 'commands nav')); + + // Output link to post if required + if ($link) { + if (forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext)) { + $langstring = 'discussthistopic'; + } else { + $langstring = 'viewthediscussion'; + } + if ($post->replies == 1) { + $replystring = get_string('repliesone', 'forum', $post->replies); + } else { + $replystring = get_string('repliesmany', 'forum', $post->replies); + } + if (!empty($discussion->unread) && $discussion->unread !== '-') { + $replystring .= ' / '; + $unreadlink = new moodle_url($discussionlink, null, 'unread'); + if ($discussion->unread == 1) { + $replystring .= html_writer::link($unreadlink, get_string('unreadpostsone', 'forum')); + } else { + $replystring .= html_writer::link($unreadlink, get_string('unreadpostsnumber', 'forum', $discussion->unread)); + } + $replystring .= ''; + } + + $output .= html_writer::start_tag('div', array('class'=>'link')); + $output .= html_writer::link($discussionlink, get_string($langstring, 'forum')); + $output .= ' ('.$replystring.')'; + $output .= html_writer::end_tag('div'); // link + } + + // Output footer if required + if ($footer) { + $output .= html_writer::tag('div', $footer, array('class'=>'footer')); + } + + // Close remaining open divs + $output .= html_writer::end_tag('div'); // content + $output .= html_writer::end_tag('nav'); // row + $output .= html_writer::end_tag('div'); // forumpost + + // Mark the forum post as read if required + if ($istracked && !$CFG->forum_usermarksread && !$postisread) { + forum_tp_mark_post_read($USER->id, $post); + } + + if ($return) { + return $output; + } + echo $output; + return; +} + +/** + * @global object + * @global object + * @uses FORUM_MODE_FLATNEWEST + * @param object $course + * @param object $cm + * @param object $forum + * @param object $discussion + * @param object $post + * @param object $mode + * @param bool $reply + * @param bool $forumtracked + * @param array $posts + * @return void + * @deprecated since Moodle 3.7 + */ +function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) { + debugging('forum_print_posts_flat() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + global $USER, $CFG; + + $link = false; + + foreach ($posts as $post) { + if (!$post->parent) { + continue; + } + $post->subject = format_string($post->subject); + $ownpost = ($USER->id == $post->userid); + + $postread = !empty($post->postread); + + forum_print_post_start($post); + forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, + '', '', $postread, true, $forumtracked); + forum_print_post_end($post); + } +} + +/** + * @todo Document this function + * + * @global object + * @global object + * @uses CONTEXT_MODULE + * @return void + * @deprecated since Moodle 3.7 + */ +function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) { + debugging('forum_print_posts_threaded() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + global $USER, $CFG; + + $link = false; + + if (!empty($posts[$parent->id]->children)) { + $posts = $posts[$parent->id]->children; + + $modcontext = context_module::instance($cm->id); + $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext); + + foreach ($posts as $post) { + + echo '
'; + if ($depth > 0) { + $ownpost = ($USER->id == $post->userid); + $post->subject = format_string($post->subject); + + $postread = !empty($post->postread); + + forum_print_post_start($post); + forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, + '', '', $postread, true, $forumtracked); + forum_print_post_end($post); + } else { + if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, true)) { + if (forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) { + // This post has been deleted but still exists and may have children. + $subject = get_string('privacy:request:delete:post:subject', 'mod_forum'); + $byline = ''; + } else { + // The user can't see this post at all. + echo "
\n"; + continue; + } + } else { + $by = new stdClass(); + $by->name = fullname($post, $canviewfullnames); + $by->date = userdate_htmltime($post->modified); + $byline = ' ' . get_string("bynameondate", "forum", $by); + $subject = format_string($post->subject, true); + } + + if ($forumtracked) { + if (!empty($post->postread)) { + $style = ''; + } else { + $style = ''; + } + } else { + $style = ''; + } + + echo $style; + echo ""; + echo html_writer::link(new moodle_url('/mod/forum/discuss.php', [ + 'd' => $post->discussion, + 'parent' => $post->id, + ]), $subject); + echo $byline; + echo ""; + } + + forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts); + echo "\n"; + } + } +} + +/** + * @todo Document this function + * @global object + * @global object + * @return void + * @deprecated since Moodle 3.7 + */ +function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) { + debugging('forum_print_posts_nested() has been deprecated, please use \mod_forum\local\renderers\posts instead.', DEBUG_DEVELOPER); + global $USER, $CFG; + + $link = false; + + if (!empty($posts[$parent->id]->children)) { + $posts = $posts[$parent->id]->children; + + foreach ($posts as $post) { + + echo '
'; + if (!isloggedin()) { + $ownpost = false; + } else { + $ownpost = ($USER->id == $post->userid); + } + + $post->subject = format_string($post->subject); + $postread = !empty($post->postread); + + forum_print_post_start($post); + forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, + '', '', $postread, true, $forumtracked); + forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); + forum_print_post_end($post); + echo "
\n"; + } + } +} diff --git a/mod/forum/lib.php b/mod/forum/lib.php index ad536535cb7..9281187dcec 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -509,18 +509,25 @@ function forum_user_complete($course, $user, $mod, $forum) { if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) { print_error('invalidcoursemodule'); } + $context = context_module::instance($cm->id); $discussions = forum_get_user_involved_discussions($forum->id, $user->id); + $posts = array_filter($posts, function($post) use ($discussions) { + return isset($discussions[$post->discussion]); + }); + $entityfactory = mod_forum\local\container::get_entity_factory(); + $rendererfactory = mod_forum\local\container::get_renderer_factory(); + $postrenderer = $rendererfactory->get_posts_renderer(); - foreach ($posts as $post) { - if (!isset($discussions[$post->discussion])) { - continue; - } - $discussion = $discussions[$post->discussion]; - - forum_print_post_start($post); - forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false); - forum_print_post_end($post); - } + echo $postrenderer->render( + $USER, + [$forum->id => $entityfactory->get_forum_from_stdclass($forum, $context, $cm, $course)], + array_map(function($discussion) use ($entityfactory) { + return $entityfactory->get_discussion_from_stdclass($discussion); + }, $discussions), + array_map(function($post) use ($entityfactory) { + return $entityfactory->get_post_from_stdclass($post); + }, $posts) + ); } else { echo "

".get_string("noposts", "forum")."

"; } @@ -2219,611 +2226,6 @@ function forum_get_course_forum($courseid, $type) { return $DB->get_record("forum", array("id" => "$forum->id")); } -/** - * Return a static array of posts that are open. - * - * @return array - */ -function forum_post_nesting_cache() { - static $nesting = array(); - return $nesting; -} - -/** - * Return true for the first time this post was started - * - * @param int $id The id of the post to start - * @return bool - */ -function forum_should_start_post_nesting($id) { - $cache = forum_post_nesting_cache(); - if (!array_key_exists($id, $cache)) { - $cache[$id] = 1; - return true; - } else { - $cache[$id]++; - return false; - } -} - -/** - * Return true when all the opens are nested with a close. - * - * @param int $id The id of the post to end - * @return bool - */ -function forum_should_end_post_nesting($id) { - $cache = forum_post_nesting_cache(); - if (!array_key_exists($id, $cache)) { - return true; - } else { - $cache[$id]--; - if ($cache[$id] == 0) { - unset($cache[$id]); - return true; - } - } - return false; -} - -/** - * Start a forum post container - * - * @param object $post The post to print. - * @param bool $return Return the string or print it - * @return string - */ -function forum_print_post_start($post, $return = false) { - $output = ''; - - if (forum_should_start_post_nesting($post->id)) { - $attributes = [ - 'id' => 'p'.$post->id, - 'tabindex' => -1, - 'class' => 'relativelink' - ]; - $output .= html_writer::start_tag('article', $attributes); - } - if ($return) { - return $output; - } - echo $output; - return; -} - -/** - * End a forum post container - * - * @param object $post The post to print. - * @param bool $return Return the string or print it - * @return string - */ -function forum_print_post_end($post, $return = false) { - $output = ''; - - if (forum_should_end_post_nesting($post->id)) { - $output .= html_writer::end_tag('article'); - } - if ($return) { - return $output; - } - echo $output; - return; -} - -/** - * Print a forum post - * This function should always be surrounded with calls to forum_print_post_start - * and forum_print_post_end to create the surrounding container for the post. - * Replies can be nested before forum_print_post_end and should reflect the structure of - * thread. - * - * @global object - * @global object - * @uses FORUM_MODE_THREADED - * @uses PORTFOLIO_FORMAT_PLAINHTML - * @uses PORTFOLIO_FORMAT_FILE - * @uses PORTFOLIO_FORMAT_RICHHTML - * @uses PORTFOLIO_ADD_TEXT_LINK - * @uses CONTEXT_MODULE - * @param object $post The post to print. - * @param object $discussion - * @param object $forum - * @param object $cm - * @param object $course - * @param boolean $ownpost Whether this post belongs to the current user. - * @param boolean $reply Whether to print a 'reply' link at the bottom of the message. - * @param boolean $link Just print a shortened version of the post as a link to the full post. - * @param string $footer Extra stuff to print after the message. - * @param string $highlight Space-separated list of terms to highlight. - * @param int $post_read true, false or -99. If we already know whether this user - * has read this post, pass that in, otherwise, pass in -99, and this - * function will work it out. - * @param boolean $dummyifcantsee When forum_user_can_see_post says that - * the current user can't see this post, if this argument is true - * (the default) then print a dummy 'you can't see this post' post. - * If false, don't output anything at all. - * @param bool|null $istracked - * @return void - */ -function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false, - $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) { - global $USER, $CFG, $OUTPUT; - - require_once($CFG->libdir . '/filelib.php'); - - // String cache - static $str; - // This is an extremely hacky way to ensure we only print the 'unread' anchor - // the first time we encounter an unread post on a page. Ideally this would - // be moved into the caller somehow, and be better testable. But at the time - // of dealing with this bug, this static workaround was the most surgical and - // it fits together with only printing th unread anchor id once on a given page. - static $firstunreadanchorprinted = false; - - $modcontext = context_module::instance($cm->id); - - $post->course = $course->id; - $post->forum = $forum->id; - $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id); - if (!empty($CFG->enableplagiarism)) { - require_once($CFG->libdir.'/plagiarismlib.php'); - $post->message .= plagiarism_get_links(array('userid' => $post->userid, - 'content' => $post->message, - 'cmid' => $cm->id, - 'course' => $post->course, - 'forum' => $post->forum)); - } - - // caching - if (!isset($cm->cache)) { - $cm->cache = new stdClass; - } - - if (!isset($cm->cache->caps)) { - $cm->cache->caps = array(); - $cm->cache->caps['mod/forum:viewdiscussion'] = has_capability('mod/forum:viewdiscussion', $modcontext); - $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modcontext); - $cm->cache->caps['mod/forum:editanypost'] = has_capability('mod/forum:editanypost', $modcontext); - $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext); - $cm->cache->caps['mod/forum:deleteownpost'] = has_capability('mod/forum:deleteownpost', $modcontext); - $cm->cache->caps['mod/forum:deleteanypost'] = has_capability('mod/forum:deleteanypost', $modcontext); - $cm->cache->caps['mod/forum:viewanyrating'] = has_capability('mod/forum:viewanyrating', $modcontext); - $cm->cache->caps['mod/forum:exportpost'] = has_capability('mod/forum:exportpost', $modcontext); - $cm->cache->caps['mod/forum:exportownpost'] = has_capability('mod/forum:exportownpost', $modcontext); - } - - if (!isset($cm->uservisible)) { - $cm->uservisible = \core_availability\info_module::is_user_visible($cm, 0, false); - } - - if ($istracked && is_null($postisread)) { - $postisread = forum_tp_is_post_read($USER->id, $post); - } - - if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) { - // Do _not_ check the deleted flag - we need to display a different UI. - $output = ''; - if (!$dummyifcantsee) { - if ($return) { - return $output; - } - echo $output; - return; - } - - $output .= html_writer::start_tag('div', array('class' => 'forumpost clearfix', - 'aria-label' => get_string('hiddenforumpost', 'forum'))); - $output .= html_writer::start_tag('header', array('class' => 'row header')); - $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); // Picture. - if ($post->parent) { - $output .= html_writer::start_tag('div', array('class' => 'topic')); - } else { - $output .= html_writer::start_tag('div', array('class' => 'topic starter')); - } - $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class' => 'subject', - 'role' => 'header', - 'id' => ('headp' . $post->id))); // Subject. - $authorclasses = array('class' => 'author'); - $output .= html_writer::tag('address', get_string('forumauthorhidden', 'forum'), $authorclasses); // Author. - $output .= html_writer::end_tag('div'); - $output .= html_writer::end_tag('header'); // Header. - $output .= html_writer::start_tag('div', array('class'=>'row')); - $output .= html_writer::tag('div', ' ', array('class'=>'left side')); // Groups - $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content - $output .= html_writer::end_tag('div'); // row - $output .= html_writer::end_tag('div'); // forumpost - - if ($return) { - return $output; - } - echo $output; - return; - } - - if (!empty($post->deleted)) { - // Note: Posts marked as deleted are still returned by the above forum_user_can_post because it is required for - // nesting of posts. - $output = ''; - if (!$dummyifcantsee) { - if ($return) { - return $output; - } - echo $output; - return; - } - $output .= html_writer::start_tag('div', [ - 'class' => 'forumpost clearfix', - 'aria-label' => get_string('forumbodydeleted', 'forum'), - ]); - - $output .= html_writer::start_tag('header', array('class' => 'row header')); - $output .= html_writer::tag('div', '', array('class' => 'left picture', 'role' => 'presentation')); - - $classes = ['topic']; - if (!empty($post->parent)) { - $classes[] = 'starter'; - } - $output .= html_writer::start_tag('div', ['class' => implode(' ', $classes)]); - - // Subject. - $output .= html_writer::tag('div', get_string('forumsubjectdeleted', 'forum'), [ - 'class' => 'subject', - 'role' => 'header', - 'id' => ('headp' . $post->id) - ]); - - // Author. - $output .= html_writer::tag('address', '', ['class' => 'author']); - - $output .= html_writer::end_tag('div'); - $output .= html_writer::end_tag('header'); // End header. - $output .= html_writer::start_tag('div', ['class' => 'row']); - $output .= html_writer::tag('div', ' ', ['class' => 'left side']); // Groups. - $output .= html_writer::tag('div', get_string('forumbodydeleted', 'forum'), ['class' => 'content']); // Content. - $output .= html_writer::end_tag('div'); // End row. - $output .= html_writer::end_tag('div'); // End forumpost. - - if ($return) { - return $output; - } - echo $output; - return; - } - - if (empty($str)) { - $str = new stdClass; - $str->edit = get_string('edit', 'forum'); - $str->delete = get_string('delete', 'forum'); - $str->reply = get_string('reply', 'forum'); - $str->parent = get_string('parent', 'forum'); - $str->pruneheading = get_string('pruneheading', 'forum'); - $str->prune = get_string('prune', 'forum'); - $str->displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode); - $str->markread = get_string('markread', 'forum'); - $str->markunread = get_string('markunread', 'forum'); - } - - $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion)); - - // Build an object that represents the posting user - $postuser = new stdClass; - $postuserfields = explode(',', user_picture::fields()); - $postuser = username_load_fields_from_object($postuser, $post, null, $postuserfields); - $postuser->id = $post->userid; - $postuser->fullname = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']); - $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id)); - - // Prepare the groups the posting user belongs to - if (isset($cm->cache->usersgroups)) { - $groups = array(); - if (isset($cm->cache->usersgroups[$post->userid])) { - foreach ($cm->cache->usersgroups[$post->userid] as $gid) { - $groups[$gid] = $cm->cache->groups[$gid]; - } - } - } else { - $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid); - } - - // Prepare the attachements for the post, files then images - list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages'); - - // Determine if we need to shorten this post - $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost)); - - // Prepare an array of commands - $commands = array(); - - // Add a permalink. - $permalink = new moodle_url($discussionlink); - $permalink->set_anchor('p' . $post->id); - $commands[] = array('url' => $permalink, 'text' => get_string('permalink', 'forum'), 'attributes' => ['rel' => 'bookmark']); - - // SPECIAL CASE: The front page can display a news item post to non-logged in users. - // Don't display the mark read / unread controls in this case. - if ($istracked && $CFG->forum_usermarksread && isloggedin()) { - $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread')); - $text = $str->markunread; - if (!$postisread) { - $url->param('mark', 'read'); - $text = $str->markread; - } - if ($str->displaymode == FORUM_MODE_THREADED) { - $url->param('parent', $post->parent); - } else { - $url->set_anchor('p'.$post->id); - } - $commands[] = array('url'=>$url, 'text'=>$text, 'attributes' => ['rel' => 'bookmark']); - } - - // Zoom in to the parent specifically - if ($post->parent) { - $url = new moodle_url($discussionlink); - if ($str->displaymode == FORUM_MODE_THREADED) { - $url->param('parent', $post->parent); - } else { - $url->set_anchor('p'.$post->parent); - } - $commands[] = array('url'=>$url, 'text'=>$str->parent, 'attributes' => ['rel' => 'bookmark']); - } - - // Hack for allow to edit news posts those are not displayed yet until they are displayed - $age = time() - $post->created; - if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) { - $age = 0; - } - - if ($forum->type == 'single' and $discussion->firstpost == $post->id) { - if (has_capability('moodle/course:manageactivities', $modcontext)) { - // The first post in single simple is the forum description. - $commands[] = array('url'=>new moodle_url('/course/modedit.php', array('update'=>$cm->id, 'sesskey'=>sesskey(), 'return'=>1)), 'text'=>$str->edit); - } - } else if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) { - $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit); - } - - if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') { - $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading); - } - - if ($forum->type == 'single' and $discussion->firstpost == $post->id) { - // Do not allow deleting of first post in single simple type. - } else if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) { - $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete); - } - - if ($reply) { - $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply); - } - - if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) { - $p = array('postid' => $post->id); - require_once($CFG->libdir.'/portfoliolib.php'); - $button = new portfolio_add_button(); - $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), 'mod_forum'); - if (empty($attachments)) { - $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); - } else { - $button->set_formats(PORTFOLIO_FORMAT_RICHHTML); - } - - $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK); - if (!empty($porfoliohtml)) { - $commands[] = $porfoliohtml; - } - } - // Finished building commands - - - // Begin output - - $output = ''; - - if ($istracked) { - if ($postisread) { - $forumpostclass = ' read'; - } else { - $forumpostclass = ' unread'; - // If this is the first unread post printed then give it an anchor and id of unread. - if (!$firstunreadanchorprinted) { - $output .= html_writer::tag('a', '', array('id' => 'unread')); - $firstunreadanchorprinted = true; - } - } - } else { - // ignore trackign status if not tracked or tracked param missing - $forumpostclass = ''; - } - - $topicclass = ''; - if (empty($post->parent)) { - $topicclass = ' firstpost starter'; - } - - if (!empty($post->lastpost)) { - $forumpostclass .= ' lastpost'; - } - - // Flag to indicate whether we should hide the author or not. - $authorhidden = forum_is_author_hidden($post, $forum); - $postbyuser = new stdClass; - $postbyuser->post = $post->subject; - $postbyuser->user = $postuser->fullname; - $discussionbyuser = get_string('postbyuser', 'forum', $postbyuser); - // Begin forum post. - $output .= html_writer::start_div('forumpost clearfix' . $forumpostclass . $topicclass, - ['aria-label' => $discussionbyuser]); - // Begin header row. - $output .= html_writer::start_tag('header', ['class' => 'row header clearfix']); - - // User picture. - if (!$authorhidden) { - $picture = $OUTPUT->user_picture($postuser, ['courseid' => $course->id]); - $output .= html_writer::div($picture, 'left picture', ['role' => 'presentation']); - $topicclass = 'topic' . $topicclass; - } - - // Begin topic column. - $output .= html_writer::start_div($topicclass); - $postsubject = $post->subject; - if (empty($post->subjectnoformat)) { - $postsubject = format_string($postsubject); - } - $output .= html_writer::div($postsubject, 'subject', ['role' => 'heading', 'aria-level' => '1', 'id' => ('headp' . $post->id)]); - - if ($authorhidden) { - $bytext = userdate_htmltime($post->created); - } else { - $by = new stdClass(); - $by->date = userdate_htmltime($post->created); - $by->name = html_writer::link($postuser->profilelink, $postuser->fullname); - $bytext = get_string('bynameondate', 'forum', $by); - } - $bytextoptions = [ - 'class' => 'author' - ]; - $output .= html_writer::tag('address', $bytext, $bytextoptions); - // End topic column. - $output .= html_writer::end_div(); - - // End header row. - $output .= html_writer::end_tag('header'); - - // Row with the forum post content. - $output .= html_writer::start_div('row maincontent clearfix'); - // Show if author is not hidden or we have groups. - if (!$authorhidden || $groups) { - $output .= html_writer::start_div('left'); - $groupoutput = ''; - if ($groups) { - $groupoutput = print_group_picture($groups, $course->id, false, true, true); - } - if (empty($groupoutput)) { - $groupoutput = ' '; - } - $output .= html_writer::div($groupoutput, 'grouppictures'); - $output .= html_writer::end_div(); // Left side. - } - - $output .= html_writer::start_tag('div', array('class'=>'no-overflow')); - $output .= html_writer::start_tag('div', array('class'=>'content')); - - $options = new stdClass; - $options->para = false; - $options->trusted = $post->messagetrust; - $options->context = $modcontext; - if ($shortenpost) { - // Prepare shortened version by filtering the text then shortening it. - $postclass = 'shortenedpost'; - $postcontent = format_text($post->message, $post->messageformat, $options); - $postcontent = shorten_text($postcontent, $CFG->forum_shortpost); - $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum')); - $postcontent .= html_writer::tag('div', '('.get_string('numwords', 'moodle', count_words($post->message)).')', - array('class'=>'post-word-count')); - } else { - // Prepare whole post - $postclass = 'fullpost'; - $postcontent = format_text($post->message, $post->messageformat, $options, $course->id); - if (!empty($highlight)) { - $postcontent = highlight($highlight, $postcontent); - } - if (!empty($forum->displaywordcount)) { - $postcontent .= html_writer::tag('div', get_string('numwords', 'moodle', count_words($postcontent)), - array('class'=>'post-word-count')); - } - $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages')); - } - - if (\core_tag_tag::is_enabled('mod_forum', 'forum_posts')) { - $postcontent .= $OUTPUT->tag_list(core_tag_tag::get_item_tags('mod_forum', 'forum_posts', $post->id), null, 'forum-tags'); - } - - // Output the post content - $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass)); - $output .= html_writer::end_tag('div'); // Content - $output .= html_writer::end_tag('div'); // Content mask - $output .= html_writer::end_tag('div'); // Row - - $output .= html_writer::start_tag('nav', array('class' => 'row side')); - $output .= html_writer::tag('div',' ', array('class'=>'left')); - $output .= html_writer::start_tag('div', array('class'=>'options clearfix')); - - if (!empty($attachments)) { - $output .= html_writer::tag('div', $attachments, array('class' => 'attachments')); - } - - // Output ratings - if (!empty($post->rating)) { - $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating')); - } - - // Output the commands - $commandhtml = array(); - foreach ($commands as $command) { - if (is_array($command)) { - $attributes = ['class' => 'nav-item nav-link']; - if (isset($command['attributes'])) { - $attributes = array_merge($attributes, $command['attributes']); - } - $commandhtml[] = html_writer::link($command['url'], $command['text'], $attributes); - } else { - $commandhtml[] = $command; - } - } - $output .= html_writer::tag('div', implode(' ', $commandhtml), array('class' => 'commands nav')); - - // Output link to post if required - if ($link) { - if (forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext)) { - $langstring = 'discussthistopic'; - } else { - $langstring = 'viewthediscussion'; - } - if ($post->replies == 1) { - $replystring = get_string('repliesone', 'forum', $post->replies); - } else { - $replystring = get_string('repliesmany', 'forum', $post->replies); - } - if (!empty($discussion->unread) && $discussion->unread !== '-') { - $replystring .= ' / '; - $unreadlink = new moodle_url($discussionlink, null, 'unread'); - if ($discussion->unread == 1) { - $replystring .= html_writer::link($unreadlink, get_string('unreadpostsone', 'forum')); - } else { - $replystring .= html_writer::link($unreadlink, get_string('unreadpostsnumber', 'forum', $discussion->unread)); - } - $replystring .= ''; - } - - $output .= html_writer::start_tag('div', array('class'=>'link')); - $output .= html_writer::link($discussionlink, get_string($langstring, 'forum')); - $output .= ' ('.$replystring.')'; - $output .= html_writer::end_tag('div'); // link - } - - // Output footer if required - if ($footer) { - $output .= html_writer::tag('div', $footer, array('class'=>'footer')); - } - - // Close remaining open divs - $output .= html_writer::end_tag('div'); // content - $output .= html_writer::end_tag('nav'); // row - $output .= html_writer::end_tag('div'); // forumpost - - // Mark the forum post as read if required - if ($istracked && !$CFG->forum_usermarksread && !$postisread) { - forum_tp_mark_post_read($USER->id, $post); - } - - if ($return) { - return $output; - } - echo $output; - return; -} - /** * Return rating related permissions * @@ -3111,8 +2513,8 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring echo $post->unread; echo ''; echo '' . - $OUTPUT->pix_icon('t/markasread', $strmarkalldread) . ''; + $forum->id.'&d='.$post->discussion.'&mark=read&return=/mod/forum/view.php&sesskey=' . + sesskey() . '">' . $OUTPUT->pix_icon('t/markasread', $strmarkalldread) . ''; echo '
'; } else { echo ''; @@ -4928,273 +4330,6 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions = -1, $ } } - -/** - * Prints a forum discussion - * - * @uses CONTEXT_MODULE - * @uses FORUM_MODE_FLATNEWEST - * @uses FORUM_MODE_FLATOLDEST - * @uses FORUM_MODE_THREADED - * @uses FORUM_MODE_NESTED - * @param stdClass $course - * @param stdClass $cm - * @param stdClass $forum - * @param stdClass $discussion - * @param stdClass $post - * @param int $mode - * @param mixed $canreply - * @param bool $canrate - */ -function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) { - global $USER, $CFG; - - require_once($CFG->dirroot.'/rating/lib.php'); - - $ownpost = (isloggedin() && $USER->id == $post->userid); - - $modcontext = context_module::instance($cm->id); - if ($canreply === NULL) { - $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); - } else { - $reply = $canreply; - } - - // $cm holds general cache for forum functions - $cm->cache = new stdClass; - $cm->cache->groups = groups_get_all_groups($course->id, 0, $cm->groupingid); - $cm->cache->usersgroups = array(); - - $posters = array(); - - // preload all posts - TODO: improve... - if ($mode == FORUM_MODE_FLATNEWEST) { - $sort = "p.created DESC"; - } else { - $sort = "p.created ASC"; - } - - $forumtracked = forum_tp_is_tracked($forum); - $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); - $post = $posts[$post->id]; - - foreach ($posts as $pid=>$p) { - $posters[$p->userid] = $p->userid; - } - - // preload all groups of ppl that posted in this discussion - if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) { - foreach($postersgroups as $pg) { - if (!isset($cm->cache->usersgroups[$pg->userid])) { - $cm->cache->usersgroups[$pg->userid] = array(); - } - $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid; - } - unset($postersgroups); - } - - //load ratings - if ($forum->assessed != RATING_AGGREGATE_NONE) { - $ratingoptions = new stdClass; - $ratingoptions->context = $modcontext; - $ratingoptions->component = 'mod_forum'; - $ratingoptions->ratingarea = 'post'; - $ratingoptions->items = $posts; - $ratingoptions->aggregate = $forum->assessed;//the aggregation method - $ratingoptions->scaleid = $forum->scale; - $ratingoptions->userid = $USER->id; - if ($forum->type == 'single' or !$discussion->id) { - $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id"; - } else { - $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id"; - } - $ratingoptions->assesstimestart = $forum->assesstimestart; - $ratingoptions->assesstimefinish = $forum->assesstimefinish; - - $rm = new rating_manager(); - $posts = $rm->get_ratings($ratingoptions); - } - - - $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post - $post->forumtype = $forum->type; - - $post->subject = format_string($post->subject); - - $postread = !empty($post->postread); - - forum_print_post_start($post); - forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false, - '', '', $postread, true, $forumtracked); - - switch ($mode) { - case FORUM_MODE_FLATOLDEST : - case FORUM_MODE_FLATNEWEST : - default: - forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts); - break; - - case FORUM_MODE_THREADED : - forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts); - break; - - case FORUM_MODE_NESTED : - forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); - break; - } - forum_print_post_end($post); -} - - -/** - * @global object - * @global object - * @uses FORUM_MODE_FLATNEWEST - * @param object $course - * @param object $cm - * @param object $forum - * @param object $discussion - * @param object $post - * @param object $mode - * @param bool $reply - * @param bool $forumtracked - * @param array $posts - * @return void - */ -function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) { - global $USER, $CFG; - - $link = false; - - foreach ($posts as $post) { - if (!$post->parent) { - continue; - } - $post->subject = format_string($post->subject); - $ownpost = ($USER->id == $post->userid); - - $postread = !empty($post->postread); - - forum_print_post_start($post); - forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, - '', '', $postread, true, $forumtracked); - forum_print_post_end($post); - } -} - -/** - * @todo Document this function - * - * @global object - * @global object - * @uses CONTEXT_MODULE - * @return void - */ -function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) { - global $USER, $CFG; - - $link = false; - - if (!empty($posts[$parent->id]->children)) { - $posts = $posts[$parent->id]->children; - - $modcontext = context_module::instance($cm->id); - $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext); - - foreach ($posts as $post) { - - echo '
'; - if ($depth > 0) { - $ownpost = ($USER->id == $post->userid); - $post->subject = format_string($post->subject); - - $postread = !empty($post->postread); - - forum_print_post_start($post); - forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, - '', '', $postread, true, $forumtracked); - forum_print_post_end($post); - } else { - if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm, true)) { - if (forum_user_can_see_post($forum, $discussion, $post, null, $cm, false)) { - // This post has been deleted but still exists and may have children. - $subject = get_string('privacy:request:delete:post:subject', 'mod_forum'); - $byline = ''; - } else { - // The user can't see this post at all. - echo "
\n"; - continue; - } - } else { - $by = new stdClass(); - $by->name = fullname($post, $canviewfullnames); - $by->date = userdate_htmltime($post->modified); - $byline = ' ' . get_string("bynameondate", "forum", $by); - $subject = format_string($post->subject, true); - } - - if ($forumtracked) { - if (!empty($post->postread)) { - $style = ''; - } else { - $style = ''; - } - } else { - $style = ''; - } - - echo $style; - echo ""; - echo html_writer::link(new moodle_url('/mod/forum/discuss.php', [ - 'd' => $post->discussion, - 'parent' => $post->id, - ]), $subject); - echo $byline; - echo ""; - } - - forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts); - echo "\n"; - } - } -} - -/** - * @todo Document this function - * @global object - * @global object - * @return void - */ -function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) { - global $USER, $CFG; - - $link = false; - - if (!empty($posts[$parent->id]->children)) { - $posts = $posts[$parent->id]->children; - - foreach ($posts as $post) { - - echo '
'; - if (!isloggedin()) { - $ownpost = false; - } else { - $ownpost = ($USER->id == $post->userid); - } - - $post->subject = format_string($post->subject); - $postread = !empty($post->postread); - - forum_print_post_start($post); - forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, - '', '', $postread, true, $forumtracked); - forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); - forum_print_post_end($post); - echo "
\n"; - } - } -} - /** * Returns all forum posts since a given time in specified forum. *