See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * Displays a post, and all the posts below it. 20 * If no post is given, displays all posts in a discussion 21 * 22 * @package mod_forum 23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 require_once('../../config.php'); 28 29 $d = required_param('d', PARAM_INT); // Discussion ID 30 $parent = optional_param('parent', 0, PARAM_INT); // If set, then display this post and all children. 31 $mode = optional_param('mode', 0, PARAM_INT); // If set, changes the layout of the thread 32 $move = optional_param('move', 0, PARAM_INT); // If set, moves this discussion to another forum 33 $mark = optional_param('mark', '', PARAM_ALPHA); // Used for tracking read posts if user initiated. 34 $postid = optional_param('postid', 0, PARAM_INT); // Used for tracking read posts if user initiated. 35 $pin = optional_param('pin', -1, PARAM_INT); // If set, pin or unpin this discussion. 36 37 $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$d)); 38 if ($parent !== 0) { 39 $url->param('parent', $parent); 40 } 41 $PAGE->set_url($url); 42 43 $vaultfactory = mod_forum\local\container::get_vault_factory(); 44 $discussionvault = $vaultfactory->get_discussion_vault(); 45 $discussion = $discussionvault->get_from_id($d); 46 47 if (!$discussion) { 48 throw new \moodle_exception('errordiscussionnotfound', 'mod_forum'); 49 } 50 51 $forumvault = $vaultfactory->get_forum_vault(); 52 $forum = $forumvault->get_from_id($discussion->get_forum_id()); 53 54 if (!$forum) { 55 throw new \moodle_exception('errorforumnotfound', 'mod_forum'); 56 } 57 58 $course = $forum->get_course_record(); 59 $cm = $forum->get_course_module_record(); 60 61 require_course_login($course, true, $cm); 62 63 $managerfactory = mod_forum\local\container::get_manager_factory(); 64 $capabilitymanager = $managerfactory->get_capability_manager($forum); 65 $urlfactory = mod_forum\local\container::get_url_factory(); 66 67 // Make sure we can render. 68 if (!$capabilitymanager->can_view_discussions($USER)) { 69 throw new moodle_exception('noviewdiscussionspermission', 'mod_forum'); 70 } 71 72 $datamapperfactory = mod_forum\local\container::get_legacy_data_mapper_factory(); 73 $forumdatamapper = $datamapperfactory->get_forum_data_mapper(); 74 $forumrecord = $forumdatamapper->to_legacy_object($forum); 75 $discussiondatamapper = $datamapperfactory->get_discussion_data_mapper(); 76 $discussionrecord = $discussiondatamapper->to_legacy_object($discussion); 77 $discussionviewurl = $urlfactory->get_discussion_view_url_from_discussion($discussion); 78 // Set the activity record, to avoid additional calls to the db if the page getter is called. 79 $PAGE->set_activity_record($forumrecord); 80 81 // move this down fix for MDL-6926 82 require_once($CFG->dirroot . '/mod/forum/lib.php'); 83 84 $modcontext = $forum->get_context(); 85 86 if ( 87 !empty($CFG->enablerssfeeds) && 88 !empty($CFG->forum_enablerssfeeds) && 89 $forum->get_rss_type() && 90 $forum->get_rss_articles() 91 ) { 92 require_once("$CFG->libdir/rsslib.php"); 93 94 $rsstitle = format_string( 95 $course->shortname, 96 true, 97 ['context' => context_course::instance($course->id)] 98 ); 99 $rsstitle .= ': ' . format_string($forum->get_name()); 100 rss_add_http_header($modcontext, 'mod_forum', $forumrecord, $rsstitle); 101 } 102 103 // Move discussion if requested. 104 if ($move > 0 && confirm_sesskey()) { 105 $forumid = $forum->get_id(); 106 $discussionid = $discussion->get_id(); 107 $return = $discussionviewurl->out(false); 108 109 if (!$forumto = $DB->get_record('forum', ['id' => $move])) { 110 throw new \moodle_exception('cannotmovetonotexist', 'forum', $return); 111 } 112 113 if (!$capabilitymanager->can_move_discussions($USER)) { 114 if ($forum->get_type() == 'single') { 115 throw new \moodle_exception('cannotmovefromsingleforum', 'forum', $return); 116 } else { 117 throw new \moodle_exception('nopermissions', 'error', $return, get_capability_string('mod/forum:movediscussions')); 118 } 119 } 120 121 if ($forumto->type == 'single') { 122 throw new \moodle_exception('cannotmovetosingleforum', 'forum', $return); 123 } 124 125 // Get target forum cm and check it is visible to current user. 126 $modinfo = get_fast_modinfo($course); 127 $forums = $modinfo->get_instances_of('forum'); 128 if (!array_key_exists($forumto->id, $forums)) { 129 throw new \moodle_exception('cannotmovetonotfound', 'forum', $return); 130 } 131 132 $cmto = $forums[$forumto->id]; 133 if (!$cmto->uservisible) { 134 throw new \moodle_exception('cannotmovenotvisible', 'forum', $return); 135 } 136 137 $destinationctx = context_module::instance($cmto->id); 138 require_capability('mod/forum:startdiscussion', $destinationctx); 139 140 if (!forum_move_attachments($discussionrecord, $forumid, $forumto->id)) { 141 echo $OUTPUT->notification("Errors occurred while moving attachment directories - check your file permissions"); 142 } 143 // For each subscribed user in this forum and discussion, copy over per-discussion subscriptions if required. 144 $discussiongroup = $discussion->get_group_id() == -1 ? 0 : $discussion->get_group_id(); 145 $potentialsubscribers = \mod_forum\subscriptions::fetch_subscribed_users( 146 $forumrecord, 147 $discussiongroup, 148 $modcontext, 149 'u.id', 150 true 151 ); 152 153 // Pre-seed the subscribed_discussion caches. 154 // Firstly for the forum being moved to. 155 \mod_forum\subscriptions::fill_subscription_cache($forumto->id); 156 // And also for the discussion being moved. 157 \mod_forum\subscriptions::fill_subscription_cache($forumid); 158 $subscriptionchanges = []; 159 $subscriptiontime = time(); 160 foreach ($potentialsubscribers as $subuser) { 161 $userid = $subuser->id; 162 $targetsubscription = \mod_forum\subscriptions::is_subscribed($userid, $forumto, null, $cmto); 163 $discussionsubscribed = \mod_forum\subscriptions::is_subscribed($userid, $forumrecord, $discussionid); 164 $forumsubscribed = \mod_forum\subscriptions::is_subscribed($userid, $forumrecord); 165 166 if ($forumsubscribed && !$discussionsubscribed && $targetsubscription) { 167 // The user has opted out of this discussion and the move would cause them to receive notifications again. 168 // Ensure they are unsubscribed from the discussion still. 169 $subscriptionchanges[$userid] = \mod_forum\subscriptions::FORUM_DISCUSSION_UNSUBSCRIBED; 170 } else if (!$forumsubscribed && $discussionsubscribed && !$targetsubscription) { 171 // The user has opted into this discussion and would otherwise not receive the subscription after the move. 172 // Ensure they are subscribed to the discussion still. 173 $subscriptionchanges[$userid] = $subscriptiontime; 174 } 175 } 176 177 $DB->set_field('forum_discussions', 'forum', $forumto->id, ['id' => $discussionid]); 178 $DB->set_field('forum_read', 'forumid', $forumto->id, ['discussionid' => $discussionid]); 179 180 // Delete the existing per-discussion subscriptions and replace them with the newly calculated ones. 181 $DB->delete_records('forum_discussion_subs', ['discussion' => $discussionid]); 182 $newdiscussion = clone $discussionrecord; 183 $newdiscussion->forum = $forumto->id; 184 foreach ($subscriptionchanges as $userid => $preference) { 185 if ($preference != \mod_forum\subscriptions::FORUM_DISCUSSION_UNSUBSCRIBED) { 186 // Users must have viewdiscussion to a discussion. 187 if (has_capability('mod/forum:viewdiscussion', $destinationctx, $userid)) { 188 \mod_forum\subscriptions::subscribe_user_to_discussion($userid, $newdiscussion, $destinationctx); 189 } 190 } else { 191 \mod_forum\subscriptions::unsubscribe_user_from_discussion($userid, $newdiscussion, $destinationctx); 192 } 193 } 194 195 $params = [ 196 'context' => $destinationctx, 197 'objectid' => $discussionid, 198 'other' => [ 199 'fromforumid' => $forumid, 200 'toforumid' => $forumto->id, 201 ] 202 ]; 203 $event = \mod_forum\event\discussion_moved::create($params); 204 $event->add_record_snapshot('forum_discussions', $discussionrecord); 205 $event->add_record_snapshot('forum', $forumrecord); 206 $event->add_record_snapshot('forum', $forumto); 207 $event->trigger(); 208 209 // Delete the RSS files for the 2 forums to force regeneration of the feeds 210 require_once($CFG->dirroot . '/mod/forum/rsslib.php'); 211 forum_rss_delete_file($forumrecord); 212 forum_rss_delete_file($forumto); 213 214 redirect($return . '&move=-1&sesskey=' . sesskey()); 215 } 216 // Pin or unpin discussion if requested. 217 if ($pin !== -1 && confirm_sesskey()) { 218 if (!$capabilitymanager->can_pin_discussions($USER)) { 219 throw new \moodle_exception('nopermissions', 'error', $return, get_capability_string('mod/forum:pindiscussions')); 220 } 221 222 $params = ['context' => $modcontext, 'objectid' => $discussion->get_id(), 'other' => ['forumid' => $forum->get_id()]]; 223 224 switch ($pin) { 225 case FORUM_DISCUSSION_PINNED: 226 // Pin the discussion and trigger discussion pinned event. 227 forum_discussion_pin($modcontext, $forumrecord, $discussionrecord); 228 break; 229 case FORUM_DISCUSSION_UNPINNED: 230 // Unpin the discussion and trigger discussion unpinned event. 231 forum_discussion_unpin($modcontext, $forumrecord, $discussionrecord); 232 break; 233 default: 234 echo $OUTPUT->notification("Invalid value when attempting to pin/unpin discussion"); 235 break; 236 } 237 238 redirect($discussionviewurl->out(false)); 239 } 240 241 // Trigger discussion viewed event. 242 forum_discussion_view($modcontext, $forumrecord, $discussionrecord); 243 244 unset($SESSION->fromdiscussion); 245 246 $saveddisplaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode); 247 248 if ($mode) { 249 $displaymode = $mode; 250 } else { 251 $displaymode = $saveddisplaymode; 252 } 253 254 if (get_user_preferences('forum_useexperimentalui', false)) { 255 if ($displaymode == FORUM_MODE_NESTED) { 256 $displaymode = FORUM_MODE_NESTED_V2; 257 } 258 } else { 259 if ($displaymode == FORUM_MODE_NESTED_V2) { 260 $displaymode = FORUM_MODE_NESTED; 261 } 262 } 263 264 if ($displaymode != $saveddisplaymode) { 265 set_user_preference('forum_displaymode', $displaymode); 266 } 267 268 if ($parent) { 269 // If flat AND parent, then force nested display this time 270 if ($displaymode == FORUM_MODE_FLATOLDEST or $displaymode == FORUM_MODE_FLATNEWEST) { 271 $displaymode = FORUM_MODE_NESTED; 272 } 273 } else { 274 $parent = $discussion->get_first_post_id(); 275 } 276 277 $postvault = $vaultfactory->get_post_vault(); 278 if (!$post = $postvault->get_from_id($parent)) { 279 throw new \moodle_exception("notexists", 'forum', "$CFG->wwwroot/mod/forum/view.php?f={$forum->get_id()}"); 280 } 281 282 if (!$capabilitymanager->can_view_post($USER, $discussion, $post)) { 283 throw new \moodle_exception('noviewdiscussionspermission', 'forum', "$CFG->wwwroot/mod/forum/view.php?id={$forum->get_id()}"); 284 } 285 286 $istracked = forum_tp_is_tracked($forumrecord, $USER); 287 if ($mark == 'read'|| $mark == 'unread') { 288 if ($CFG->forum_usermarksread && forum_tp_can_track_forums($forumrecord) && $istracked) { 289 if ($mark == 'read') { 290 forum_tp_add_read_record($USER->id, $postid); 291 } else { 292 // unread 293 forum_tp_delete_read_records($USER->id, $postid); 294 } 295 } 296 } 297 298 $searchform = forum_search_form($course); 299 300 $forumnode = $PAGE->navigation->find($cm->id, navigation_node::TYPE_ACTIVITY); 301 if (empty($forumnode)) { 302 $forumnode = $PAGE->navbar; 303 } else { 304 $forumnode->make_active(); 305 } 306 $node = $forumnode->add(format_string($discussion->get_name()), $discussionviewurl); 307 $node->display = false; 308 if ($node && $post->get_id() != $discussion->get_first_post_id()) { 309 $node->add(format_string($post->get_subject()), $PAGE->url); 310 } 311 312 $isnestedv2displaymode = $displaymode == FORUM_MODE_NESTED_V2; 313 $PAGE->set_title("$course->shortname: " . format_string($discussion->get_name())); 314 $PAGE->set_heading($course->fullname); 315 $PAGE->set_secondary_active_tab('modulepage'); 316 $PAGE->activityheader->disable(); 317 if ($isnestedv2displaymode) { 318 $PAGE->add_body_class('nested-v2-display-mode reset-style'); 319 $settingstrigger = $OUTPUT->render_from_template('mod_forum/settings_drawer_trigger', null); 320 $PAGE->add_header_action($settingstrigger); 321 } else { 322 $PAGE->add_header_action(forum_search_form($course)); 323 } 324 325 echo $OUTPUT->header(); 326 if (!$isnestedv2displaymode) { 327 if (!$PAGE->has_secondary_navigation()) { 328 echo $OUTPUT->heading(format_string($forum->get_name()), 2); 329 } 330 echo $OUTPUT->heading(format_string($discussion->get_name()), 3, 'discussionname'); 331 } 332 333 $rendererfactory = mod_forum\local\container::get_renderer_factory(); 334 $discussionrenderer = $rendererfactory->get_discussion_renderer($forum, $discussion, $displaymode); 335 $orderpostsby = $displaymode == FORUM_MODE_FLATNEWEST ? 'created DESC' : 'created ASC'; 336 $replies = $postvault->get_replies_to_post($USER, $post, $capabilitymanager->can_view_any_private_reply($USER), $orderpostsby); 337 338 if ($move == -1 and confirm_sesskey()) { 339 $forumname = format_string($forum->get_name(), true); 340 echo $OUTPUT->notification(get_string('discussionmoved', 'forum', $forumname), 'notifysuccess'); 341 } 342 343 echo $discussionrenderer->render($USER, $post, $replies); 344 echo $OUTPUT->footer(); 345 346 if ($istracked && !$CFG->forum_usermarksread) { 347 if ($displaymode == FORUM_MODE_THREADED) { 348 forum_tp_add_read_record($USER->id, $post->get_id()); 349 } else { 350 $postids = array_map(function($post) { 351 return $post->get_id(); 352 }, array_merge([$post], array_values($replies))); 353 forum_tp_mark_posts_read($USER, $postids); 354 } 355 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body