MDL-10092:
[moodle-linuxchix.git] / mod / forum / lib.php
blob123fdb246290544c9468a31e1db83c8e775090f3
1 <?php // $Id$
3 require_once($CFG->libdir.'/filelib.php');
5 /// CONSTANTS ///////////////////////////////////////////////////////////
7 define('FORUM_MODE_FLATOLDEST', 1);
8 define('FORUM_MODE_FLATNEWEST', -1);
9 define('FORUM_MODE_THREADED', 2);
10 define('FORUM_MODE_NESTED', 3);
12 define('FORUM_FORCESUBSCRIBE', 1);
13 define('FORUM_INITIALSUBSCRIBE', 2);
14 define('FORUM_DISALLOWSUBSCRIBE',3);
16 define('FORUM_TRACKING_OFF', 0);
17 define('FORUM_TRACKING_OPTIONAL', 1);
18 define('FORUM_TRACKING_ON', 2);
20 define('FORUM_UNSET_POST_RATING', -999);
22 $FORUM_LAYOUT_MODES = array ( FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
23 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
24 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'),
25 FORUM_MODE_NESTED => get_string('modenested', 'forum') );
27 // These are course content forums that can be added to the course manually
28 $FORUM_TYPES = array ('general' => get_string('generalforum', 'forum'),
29 'eachuser' => get_string('eachuserforum', 'forum'),
30 'single' => get_string('singleforum', 'forum'),
31 'qanda' => get_string('qandaforum', 'forum')
34 $FORUM_OPEN_MODES = array ('2' => get_string('openmode2', 'forum'),
35 '1' => get_string('openmode1', 'forum'),
36 '0' => get_string('openmode0', 'forum') );
38 if (!isset($CFG->forum_displaymode)) {
39 set_config('forum_displaymode', FORUM_MODE_NESTED);
42 if (!isset($CFG->forum_shortpost)) {
43 set_config('forum_shortpost', 300); // Less non-HTML characters than this is short
46 if (!isset($CFG->forum_longpost)) {
47 set_config('forum_longpost', 600); // More non-HTML characters than this is long
50 if (!isset($CFG->forum_manydiscussions)) {
51 set_config('forum_manydiscussions', 100); // Number of discussions on a page
54 if (!isset($CFG->forum_maxbytes)) {
55 set_config('forum_maxbytes', 512000); // Default maximum size for all forums
58 if (!isset($CFG->forum_trackreadposts)) {
59 set_config('forum_trackreadposts', true); // Default whether user needs to mark a post as read
62 if (!isset($CFG->forum_oldpostdays)) {
63 set_config('forum_oldpostdays', 14); // Default number of days that a post is considered old
66 if (!isset($CFG->forum_usermarksread)) {
67 set_config('forum_usermarksread', false); // Default whether user needs to mark a post as read
70 if (!isset($CFG->forum_cleanreadtime)) {
71 set_config('forum_cleanreadtime', 2); // Default time (hour) to execute 'clean_read_records' cron
74 if (!isset($CFG->forum_replytouser)) {
75 set_config('forum_replytouser', true); // Default maximum size for all forums
78 if (empty($USER->id) or isguest()) {
79 $CFG->forum_trackreadposts = false; // This feature never works when a user isn't logged in
82 if (!isset($CFG->forum_enabletimedposts)) { // Newish feature that is not quite ready for production in 1.6
83 $CFG->forum_enabletimedposts = false;
87 /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
89 /**
90 * Given an object containing all the necessary data,
91 * (defined by the form in mod.html) this function
92 * will create a new instance and return the id number
93 * of the new instance.
95 function forum_add_instance($forum) {
96 global $CFG;
98 $forum->timemodified = time();
100 if (empty($forum->assessed)) {
101 $forum->assessed = 0;
104 if (empty($forum->ratingtime) or empty($forum->assessed)) {
105 $forum->assesstimestart = 0;
106 $forum->assesstimefinish = 0;
109 if (!$forum->id = insert_record('forum', $forum)) {
110 return false;
113 if ($forum->type == 'single') { // Create related discussion.
114 $discussion = new object();
115 $discussion->course = $forum->course;
116 $discussion->forum = $forum->id;
117 $discussion->name = $forum->name;
118 $discussion->intro = $forum->intro;
119 $discussion->assessed = $forum->assessed;
120 $discussion->format = $forum->type;
121 $discussion->mailnow = false;
123 if (! forum_add_discussion($discussion, $discussion->intro)) {
124 error('Could not add the discussion for this forum');
128 if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) { // all users should be subscribed initially
129 $users = get_course_users($forum->course);
130 foreach ($users as $user) {
131 forum_subscribe($user->id, $forum->id);
135 $forum = stripslashes_recursive($forum);
136 forum_grade_item_update($forum);
138 return $forum->id;
142 /**
143 * Given an object containing all the necessary data,
144 * (defined by the form in mod.html) this function
145 * will update an existing instance with new data.
147 function forum_update_instance($forum) {
148 $forum->timemodified = time();
149 $forum->id = $forum->instance;
151 if (empty($forum->assessed)) {
152 $forum->assessed = 0;
155 if (empty($forum->ratingtime) or empty($forum->assessed)) {
156 $forum->assesstimestart = 0;
157 $forum->assesstimefinish = 0;
160 if ($forum->type == 'single') { // Update related discussion and post.
161 if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
162 if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
163 notify('Warning! There is more than one discussion in this forum - using the most recent');
164 $discussion = array_pop($discussions);
165 } else {
166 error('Could not find the discussion in this forum');
169 if (! $post = get_record('forum_posts', 'id', $discussion->firstpost)) {
170 error('Could not find the first post in this forum discussion');
173 $post->subject = $forum->name;
174 $post->message = $forum->intro;
175 $post->modified = $forum->timemodified;
177 if (! update_record('forum_posts', ($post))) {
178 error('Could not update the first post');
181 $discussion->name = $forum->name;
183 if (! update_record('forum_discussions', ($discussion))) {
184 error('Could not update the discussion');
188 if (!update_record('forum', $forum)) {
189 error('Can not update forum');
192 $forum = stripslashes_recursive($forum);
193 forum_grade_item_update($forum);
195 return true;
200 * Given an ID of an instance of this module,
201 * this function will permanently delete the instance
202 * and any data that depends on it.
204 function forum_delete_instance($id) {
206 if (!$forum = get_record('forum', 'id', $id)) {
207 return false;
210 $result = true;
212 if ($discussions = get_records('forum_discussions', 'forum', $forum->id)) {
213 foreach ($discussions as $discussion) {
214 if (!forum_delete_discussion($discussion, true)) {
215 $result = false;
220 if (!delete_records('forum_subscriptions', 'forum', $forum->id)) {
221 $result = false;
224 forum_tp_delete_read_records(-1, -1, -1, $forum->id);
226 if (!delete_records('forum', 'id', $forum->id)) {
227 $result = false;
230 forum_grade_item_delete($forum);
232 return $result;
237 * Function to be run periodically according to the moodle cron
238 * Finds all posts that have yet to be mailed out, and mails them
239 * out to all subscribers
241 function forum_cron() {
242 global $CFG, $USER;
244 $CFG->enablerecordcache = true; // We want all the caching we can get
246 $cronuser = clone($USER);
247 $site = get_site();
249 // all users that are subscribed to any post that needs sending
250 $users = array();
252 // status arrays
253 $mailcount = array();
254 $errorcount = array();
256 // caches
257 $discussions = array();
258 $forums = array();
259 $courses = array();
260 $coursemodules = array();
261 $postinfos = array();
262 $subscribedusers = array();
265 // Posts older than 2 days will not be mailed. This is to avoid the problem where
266 // cron has not been running for a long time, and then suddenly people are flooded
267 // with mail from the past few weeks or months
268 $timenow = time();
269 $endtime = $timenow - $CFG->maxeditingtime;
270 $starttime = $endtime - 48 * 3600; // Two days earlier
272 if ($posts = forum_get_unmailed_posts($starttime, $endtime)) {
273 // Mark them all now as being mailed. It's unlikely but possible there
274 // might be an error later so that a post is NOT actually mailed out,
275 // but since mail isn't crucial, we can accept this risk. Doing it now
276 // prevents the risk of duplicated mails, which is a worse problem.
278 if (!forum_mark_old_posts_as_mailed($endtime)) {
279 mtrace('Errors occurred while trying to mark some posts as being mailed.');
280 return false; // Don't continue trying to mail them, in case we are in a cron loop
283 // checking post validity, and adding users to loop through later
284 foreach ($posts as $pid => $post) {
286 $discussionid = $post->discussion;
287 if (!isset($discussions[$discussionid])) {
288 if ($discussion = get_record('forum_discussions', 'id', $post->discussion)) {
289 $discussions[$discussionid] = $discussion;
290 } else {
291 mtrace('Could not find discussion '.$discussionid);
292 unset($posts[$pid]);
293 continue;
296 $forumid = $discussions[$discussionid]->forum;
297 if (!isset($forums[$forumid])) {
298 if ($forum = get_record('forum', 'id', $forumid)) {
299 $forums[$forumid] = $forum;
300 } else {
301 mtrace('Could not find forum '.$forumid);
302 unset($posts[$pid]);
303 continue;
306 $courseid = $forums[$forumid]->course;
307 if (!isset($courses[$courseid])) {
308 if ($course = get_record('course', 'id', $courseid)) {
309 $courses[$courseid] = $course;
310 } else {
311 mtrace('Could not find course '.$courseid);
312 unset($posts[$pid]);
313 continue;
316 if (!isset($coursemodules[$forumid])) {
317 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
318 $coursemodules[$forumid] = $cm;
319 } else {
320 mtrace('Could not course module for forum '.$forumid);
321 unset($posts[$pid]);
322 continue;
327 // caching subscribed users of each forum
328 if (!isset($subscribedusers[$forumid])) {
329 if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, true)) {
330 foreach ($subusers as $postuser) {
331 // do not try to mail users with stopped email
332 if ($postuser->emailstop) {
333 add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
334 continue;
336 // this user is subscribed to this forum
337 $subscribedusers[$forumid][] = $postuser->id;
338 // this user is a user we have to process later
339 $users[$postuser->id] = $postuser;
344 $mailcount[$pid] = 0;
345 $errorcount[$pid] = 0;
349 if ($users && $posts) {
351 $urlinfo = parse_url($CFG->wwwroot);
352 $hostname = $urlinfo['host'];
354 foreach ($users as $userto) {
356 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
358 // set this so that the capabilities are cached, and environment matches receiving user
359 $USER = $userto;
361 mtrace('Processing user '.$userto->id);
363 // we might want to add another layer - forums here (by checking array_keys($subscribedusers))
364 // so that we can skip many posts
366 foreach ($posts as $pid => $post) {
368 // Get info about the sending user
369 if (array_key_exists($post->userid, $users)) { // we might know him/her already
370 $userfrom = $users[$post->userid];
371 } else if (!$userfrom = get_record('user', 'id', $post->userid)) {
372 mtrace('Could not find user '.$post->userid);
373 continue;
376 // Set up the environment for the post, discussion, forum, course
377 $discussion = $discussions[$post->discussion];
378 $forum = $forums[$discussion->forum];
379 $course = $courses[$forum->course];
380 $cm = $coursemodules[$forum->id];
382 // Do some checks to see if we can bail out now
383 if (empty($subscribedusers[$forum->id]) || !in_array($userto->id, $subscribedusers[$forum->id])) {
384 continue; // user does not subscribe to this forum
387 // Get the context (from cache)
388 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); // Cached already
390 // setup global $COURSE properly - needed for roles and languages
391 course_setup($course); // More environment
393 // Make sure groups allow this user to see this email
394 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
395 if (! groups_group_exists($discussion->groupid)) { // Can't find group
396 continue; // Be safe and don't send it to anyone
399 if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
400 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
401 continue;
405 // Make sure we're allowed to see it...
406 if (!forum_user_can_see_post($forum, $discussion, $post)) {
407 mtrace('user '.$userto->id. ' can not see '.$post->id);
408 continue;
411 // OK so we need to send the email.
413 // Does the user want this post in a digest? If so postpone it for now.
414 if ($userto->maildigest > 0) {
415 // This user wants the mails to be in digest form
416 $queue = new object();
417 $queue->userid = $userto->id;
418 $queue->discussionid = $discussion->id;
419 $queue->postid = $post->id;
420 if (!insert_record('forum_queue', $queue)) {
421 mtrace("Error: mod/forum/cron.php: Could not queue for digest mail for id $post->id to user $userto->id ($userto->email) .. not trying again.");
423 continue;
427 // Prepare to actually send the post now, and build up the content
429 $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
431 $userfrom->customheaders = array ( // Headers to make emails easier to track
432 'Precedence: Bulk',
433 'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
434 'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
435 'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>',
436 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>',
437 'References: <moodlepost'.$post->parent.'@'.$hostname.'>',
438 'X-Course-Id: '.$course->id,
439 'X-Course-Name: '.format_string($course->fullname, true)
443 $postsubject = "$course->shortname: ".format_string($post->subject,true);
444 $posttext = forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto);
445 $posthtml = forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto);
447 // Send the post now!
449 mtrace('Sending ', '');
451 if (!$mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext,
452 $posthtml, '', '', $CFG->forum_replytouser)) {
453 mtrace("Error: mod/forum/cron.php: Could not send out mail for id $post->id to user $userto->id".
454 " ($userto->email) .. not trying again.");
455 add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
456 substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
457 $errorcount[$post->id]++;
458 } else if ($mailresult === 'emailstop') {
459 // should not be reached anymore - see check above
460 } else {
461 $mailcount[$post->id]++;
463 // Mark post as read if forum_usermarksread is set off
464 if (!$CFG->forum_usermarksread && forum_tp_can_track_forums($forum, $userto) &&
465 forum_tp_is_tracked($forum, $userto->id)) {
466 if (!forum_tp_mark_post_read($userto->id, $post, $forum->id)) {
467 mtrace("Error: mod/forum/cron.php: Could not mark post $post->id read for user $userto->id".
468 " while sending email.");
473 mtrace('post '.$post->id. ': '.$post->subject);
478 if ($posts) {
479 foreach ($posts as $post) {
480 mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
481 if ($errorcount[$post->id]) {
482 set_field("forum_posts", "mailed", "2", "id", "$post->id");
487 $USER = clone($cronuser);
488 course_setup(SITEID);
490 $sitetimezone = $CFG->timezone;
492 // Now see if there are any digest mails waiting to be sent, and if we should send them
494 if (!isset($CFG->digestmailtimelast)) { // To catch the first time
495 set_config('digestmailtimelast', 0);
498 $timenow = time();
499 $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
501 if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
503 set_config('digestmailtimelast', $timenow);
505 mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
507 if ($digestposts = get_records('forum_queue')) {
509 // We have work to do
510 $usermailcount = 0;
512 //caches - reuse the those filled before too
513 $discussionposts = array();
514 $userdiscussions = array();
516 foreach ($digestposts as $digestpost) {
517 if (!isset($users[$digestpost->userid])) {
518 if ($user = get_record('user', 'id', $digestpost->userid)) {
519 $users[$digestpost->userid] = $user;
520 } else {
521 continue;
524 $postuser = $users[$digestpost->userid];
525 if ($postuser->emailstop) {
526 add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
527 continue;
530 if (!isset($posts[$digestpost->postid])) {
531 if ($post = get_record('forum_posts', 'id', $digestpost->postid)) {
532 $posts[$digestpost->postid] = $post;
533 } else {
534 continue;
537 $discussionid = $digestpost->discussionid;
538 if (!isset($discussions[$discussionid])) {
539 if ($discussion = get_record('forum_discussions', 'id', $discussionid)) {
540 $discussions[$discussionid] = $discussion;
541 } else {
542 continue;
545 $forumid = $discussions[$discussionid]->forum;
546 if (!isset($forums[$forumid])) {
547 if ($forum = get_record('forum', 'id', $forumid)) {
548 $forums[$forumid] = $forum;
549 } else {
550 continue;
554 $courseid = $forums[$forumid]->course;
555 if (!isset($courses[$courseid])) {
556 if ($course = get_record('course', 'id', $courseid)) {
557 $courses[$courseid] = $course;
558 } else {
559 continue;
563 if (!isset($coursemodules[$forumid])) {
564 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
565 $coursemodules[$forumid] = $cm;
566 } else {
567 continue;
570 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
571 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
574 // Data collected, start sending out emails to each user
575 foreach ($userdiscussions as $userid => $thesediscussions) {
577 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
579 mtrace(get_string('processingdigest', 'forum', $userid), '... ');
581 // First of all delete all the queue entries for this user
582 delete_records('forum_queue', 'userid', $userid);
583 $userto = $users[$userid];
585 // Override the language and timezone of the "current" user, so that
586 // mail is customised for the receiver.
587 $USER = $userto;
588 course_setup(SITEID);
590 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
592 $headerdata = new object();
593 $headerdata->sitename = format_string($site->fullname, true);
594 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
596 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
597 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
599 $posthtml = "<head>";
600 foreach ($CFG->stylesheets as $stylesheet) {
601 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
603 $posthtml .= "</head>\n<body>\n";
604 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
606 foreach ($thesediscussions as $discussionid) {
608 @set_time_limit(120); // to be reset for each post
610 $discussion = $discussions[$discussionid];
611 $forum = $forums[$discussion->forum];
612 $course = $courses[$forum->course];
613 $cm = $coursemodules[$forum->id];
615 //override language
616 course_setup($course);
618 $strforums = get_string('forums', 'forum');
619 $canunsubscribe = ! forum_is_forcesubscribed($forum);
620 $canreply = forum_user_can_post($forum, $userto);
623 $posttext .= "\n \n";
624 $posttext .= '=====================================================================';
625 $posttext .= "\n \n";
626 $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
627 if ($discussion->name != $forum->name) {
628 $posttext .= " -> ".format_string($discussion->name,true);
630 $posttext .= "\n";
632 $posthtml .= "<p><font face=\"sans-serif\">".
633 "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
634 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
635 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
636 if ($discussion->name == $forum->name) {
637 $posthtml .= "</font></p>";
638 } else {
639 $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
641 $posthtml .= '<p>';
643 $postsarray = $discussionposts[$discussionid];
644 sort($postsarray);
646 // Create an empty array to use for marking read posts.
647 // (I'm sure there's already a structure I can use here, but I can't be sure.)
648 $markread = array();
650 foreach ($postsarray as $postid) {
651 $post = $posts[$postid];
653 if (array_key_exists($post->userid, $users)) { // we might know him/her already
654 $userfrom = $users[$post->userid];
655 } else if (!$userfrom = get_record('user', 'id', $post->userid)) {
656 mtrace('Could not find user '.$post->userid);
657 continue;
660 $userfrom->customheaders = array ("Precedence: Bulk");
662 if ($userto->maildigest == 2) {
663 // Subjects only
664 $by = new object();
665 $by->name = fullname($userfrom);
666 $by->date = userdate($post->modified);
667 $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
668 $posttext .= "\n---------------------------------------------------------------------";
670 $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
671 $posthtml .= '<div><a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'#p'.$post->id.'">'.format_string($post->subject,true).'</a> '.get_string("bynameondate", "forum", $by).'</div>';
673 } else {
674 // The full treatment
675 $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true);
676 $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
678 // Create an array of postid's for this user to mark as read.
679 if (!$CFG->forum_usermarksread &&
680 forum_tp_can_track_forums($forum, $userto) &&
681 forum_tp_is_tracked($forum, $userto->id)) {
682 $markread[$post->id]->post = $post;
683 $markread[$post->id]->forumid = $forum->id;
687 if ($canunsubscribe) {
688 $posthtml .= "\n<div align=\"right\"><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>";
689 } else {
690 $posthtml .= "\n<div align=\"right\"><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
692 $posthtml .= '<hr size="1" noshade="noshade" /></p>';
694 $posthtml .= '</body>';
696 if ($userto->mailformat != 1) {
697 // This user DOESN'T want to receive HTML
698 $posthtml = '';
701 if (!$mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml,
702 '', '', $CFG->forum_replytouser)) {
703 mtrace("ERROR!");
704 echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
705 add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
706 } else if ($mailresult === 'emailstop') {
707 // should not happen anymore - see check above
708 } else {
709 mtrace("success.");
710 $usermailcount++;
712 // Mark post as read if forum_usermarksread is set off
713 if (!$CFG->forum_usermarksread &&
714 forum_tp_can_track_forums($forum->id, $userto) &&
715 forum_tp_is_tracked($forum->id, $userto->id)) {
716 foreach ($markread as $postinfo) {
717 if (!forum_tp_mark_post_read($userto->id, $postinfo->post, $postinfo->forumid)) {
718 mtrace("Error: mod/forum/cron.php: Could not mark post $postid read for user $userto->id".
719 " while sending digest email.");
728 if (!empty($usermailcount)) {
729 mtrace(get_string('digestsentusers', 'forum', $usermailcount));
732 $USER = $cronuser;
733 course_setup(SITEID); // reset cron user language, theme and timezone settings
735 if (!empty($CFG->forum_lastreadclean)) {
736 $timenow = time();
737 if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
738 set_config('forum_lastreadclean', $timenow);
739 forum_tp_clean_read_records();
741 } else {
742 set_config('forum_lastreadclean', time());
746 return true;
750 * Builds and returns the body of the email notification in plain text.
752 * @param object $course
753 * @param object $forum
754 * @param object $discussion
755 * @param object $post
756 * @param object $userfrom
757 * @param object $userto
758 * @param boolean $bare
759 * @return string The email body in plain text format.
761 function forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
762 global $CFG, $USER;
764 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
765 error('Course Module ID was incorrect');
767 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
768 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
770 $by = New stdClass;
771 $by->name = fullname($userfrom, $viewfullnames);
772 $by->date = userdate($post->modified, "", $userto->timezone);
774 $strbynameondate = get_string('bynameondate', 'forum', $by);
776 $strforums = get_string('forums', 'forum');
778 $canunsubscribe = ! forum_is_forcesubscribed($forum);
779 $canreply = forum_user_can_post($forum, $userto);
781 $posttext = '';
783 if (!$bare) {
784 $posttext = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
786 if ($discussion->name != $forum->name) {
787 $posttext .= " -> ".format_string($discussion->name,true);
791 $posttext .= "\n---------------------------------------------------------------------\n";
792 $posttext .= format_string($post->subject,true);
793 if ($bare) {
794 $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
796 $posttext .= "\n".$strbynameondate."\n";
797 $posttext .= "---------------------------------------------------------------------\n";
798 $posttext .= format_text_email(trusttext_strip($post->message), $post->format);
799 $posttext .= "\n\n";
800 if ($post->attachment) {
801 $post->course = $course->id;
802 $post->forum = $forum->id;
803 $posttext .= forum_print_attachments($post, "text");
805 if (!$bare && $canreply) {
806 $posttext .= "---------------------------------------------------------------------\n";
807 $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
808 $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
810 if (!$bare && $canunsubscribe) {
811 $posttext .= "\n---------------------------------------------------------------------\n";
812 $posttext .= get_string("unsubscribe", "forum");
813 $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
816 return $posttext;
820 * Builds and returns the body of the email notification in html format.
822 * @param object $course
823 * @param object $forum
824 * @param object $discussion
825 * @param object $post
826 * @param object $userfrom
827 * @param object $userto
828 * @return string The email text in HTML format
830 function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto) {
831 global $CFG;
833 if ($userto->mailformat != 1) { // Needs to be HTML
834 return '';
837 $strforums = get_string('forums', 'forum');
838 $canreply = forum_user_can_post($forum, $userto);
839 $canunsubscribe = ! forum_is_forcesubscribed($forum);
841 $posthtml = '<head>';
842 foreach ($CFG->stylesheets as $stylesheet) {
843 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
845 $posthtml .= '</head>';
846 $posthtml .= "\n<body id=\"email\">\n\n";
848 $posthtml .= '<div class="navbar">'.
849 '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
850 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
851 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
852 if ($discussion->name == $forum->name) {
853 $posthtml .= '</div>';
854 } else {
855 $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
856 format_string($discussion->name,true).'</a></div>';
858 $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
860 if ($canunsubscribe) {
861 $posthtml .= '<hr /><div align="center" class="unsubscribelink"><a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.
862 get_string('unsubscribe', 'forum').'</a></div>';
865 $posthtml .= '</body>';
867 return $posthtml;
873 * @param object $course
874 * @param object $user
875 * @param object $mod TODO this is not used in this function, refactor
876 * @param object $forum
877 * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
879 function forum_user_outline($course, $user, $mod, $forum) {
881 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
882 $result->info = get_string("numposts", "forum", count($posts));
884 $lastpost = array_pop($posts);
885 $result->time = $lastpost->modified;
886 return $result;
888 return NULL;
895 function forum_user_complete($course, $user, $mod, $forum) {
896 global $CFG;
898 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
899 foreach ($posts as $post) {
901 $post->forum = $forum->id;
902 forum_print_post($post, $course->id, $ownpost=false, $reply=false, $link=false, $rate=false);
905 } else {
906 echo "<p>".get_string("noposts", "forum")."</p>";
913 function forum_print_overview($courses,&$htmlarray) {
914 global $USER, $CFG;
915 $LIKE = sql_ilike();
917 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
918 return array();
921 if (!$forums = get_all_instances_in_courses('forum',$courses)) {
922 return;
926 // get all forum logs in ONE query (much better!)
927 $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {$CFG->prefix}log l "
928 ." JOIN {$CFG->prefix}course_modules cm ON cm.id = cmid "
929 ." WHERE (";
930 foreach ($courses as $course) {
931 $sql .= '(l.course = '.$course->id.' AND l.time > '.$course->lastaccess.') OR ';
933 $sql = substr($sql,0,-3); // take off the last OR
935 $sql .= ") AND l.module = 'forum' AND action $LIKE 'add post%' "
936 ." AND userid != ".$USER->id." GROUP BY cmid,l.course,instance";
938 if (!$new = get_records_sql($sql)) {
939 $new = array(); // avoid warnings
942 // also get all forum tracking stuff ONCE.
943 $trackingforums = array();
944 foreach ($forums as $forum) {
945 if (forum_tp_can_track_forums($forum)) {
946 $trackingforums[$forum->id] = $forum;
950 if (count($trackingforums) > 0) {
951 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
952 $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
953 ' FROM '.$CFG->prefix.'forum_posts p '.
954 ' JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
955 ' LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$USER->id.' WHERE (';
956 foreach ($trackingforums as $track) {
957 $sql .= '(d.forum = '.$track->id.' AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = '.get_current_group($track->course).')) OR ';
959 $sql = substr($sql,0,-3); // take off the last OR
960 $sql .= ') AND p.modified >= '.$cutoffdate.' AND r.id is NULL GROUP BY d.forum,d.course';
962 if (!$unread = get_records_sql($sql)) {
963 $unread = array();
965 } else {
966 $unread = array();
969 if (empty($unread) and empty($new)) {
970 return;
973 $strforum = get_string('modulename','forum');
974 $strnumunread = get_string('overviewnumunread','forum');
975 $strnumpostssince = get_string('overviewnumpostssince','forum');
977 foreach ($forums as $forum) {
978 $str = '';
979 $count = 0;
980 $thisunread = 0;
981 $showunread = false;
982 // either we have something from logs, or trackposts, or nothing.
983 if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
984 $count = $new[$forum->id]->count;
986 if (array_key_exists($forum->id,$unread)) {
987 $thisunread = $unread[$forum->id]->count;
988 $showunread = true;
990 if ($count > 0 || $thisunread > 0) {
991 $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
992 $forum->name.'</a></div>';
993 $str .= '<div class="info">';
994 $str .= $count.' '.$strnumpostssince;
995 if (!empty($showunread)) {
996 $str .= '<br />'.$thisunread .' '.$strnumunread;
998 $str .= '</div></div>';
1000 if (!empty($str)) {
1001 if (!array_key_exists($forum->course,$htmlarray)) {
1002 $htmlarray[$forum->course] = array();
1004 if (!array_key_exists('forum',$htmlarray[$forum->course])) {
1005 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
1007 $htmlarray[$forum->course]['forum'] .= $str;
1013 * Given a course and a date, prints a summary of all the new
1014 * messages posted in the course since that date
1016 function forum_print_recent_activity($course, $isteacher, $timestart) {
1018 global $CFG;
1019 $LIKE = sql_ilike();
1021 $heading = false;
1022 $content = false;
1024 if (!$logs = get_records_select('log', 'time > \''.$timestart.'\' AND '.
1025 'course = \''.$course->id.'\' AND '.
1026 'module = \'forum\' AND '.
1027 'action '.$LIKE.' \'add %\' ', 'time ASC')){
1028 return false;
1031 $strftimerecent = get_string('strftimerecent');
1033 $mygroupid = mygroupid($course->id);
1034 $groupmode = array(); // To cache group modes
1036 $count = 0;
1037 foreach ($logs as $log) {
1038 //Get post info, I'll need it later
1039 if ($post = forum_get_post_from_log($log)) {
1040 //Create a temp valid module structure (course,id)
1041 $tempmod = new object;
1042 $tempmod->course = $log->course;
1043 $tempmod->id = $post->forum;
1044 //Obtain the visible property from the instance
1045 $coursecontext = get_context_instance(CONTEXT_COURSE, $tempmod->course);
1046 $modvisible = instance_is_visible('forum', $tempmod)
1047 || has_capability('moodle/course:viewhiddenactivities', $coursecontext);
1050 //Only if the post exists and mod is visible
1051 if ($post && $modvisible) {
1053 if (!isset($cm[$post->forum])) {
1054 $cm[$post->forum] = get_coursemodule_from_instance('forum', $post->forum, $course->id);
1056 $modcontext = get_context_instance(CONTEXT_MODULE, $cm[$post->forum]->id);
1058 // Check whether this is belongs to a discussion in a group that
1059 // should NOT be accessible to the current user
1060 if (!has_capability('moodle/site:accessallgroups', $modcontext)
1061 && $post->groupid != -1) { // Open discussions have groupid -1
1063 $groupmode[$post->forum] = groups_get_activity_groupmode($cm[$post->forum]);
1065 if ($groupmode[$post->forum]) {
1066 //hope i didn't break anything
1067 if (!@in_array($mygroupid, $post->groupid))/*$mygroupid != $post->groupid*/{
1068 continue;
1073 if (! $heading) {
1074 print_headline(get_string('newforumposts', 'forum').':', 3);
1075 $heading = true;
1076 $content = true;
1078 $date = userdate($post->modified, $strftimerecent);
1080 $subjectclass = ($log->action == 'add discussion') ? ' bold' : '';
1082 //Accessibility: markup as a list.
1083 if ($count < 1) {
1084 echo "\n<ul class='unlist'>\n";
1086 $count++;
1087 echo '<li><div class="head">'.
1088 '<div class="date">'.$date.'</div>'.
1089 '<div class="name">'.fullname($post, has_capability('moodle/site:viewfullnames', $coursecontext)).'</div>'.
1090 '</div>';
1091 echo '<div class="info'.$subjectclass.'">';
1092 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/'.str_replace('&', '&amp;', $log->url).'">';
1093 $post->subject = break_up_long_words(format_string($post->subject,true));
1094 echo $post->subject;
1095 echo "</a>\"</div></li>\n";
1098 echo "</ul>\n";
1099 return $content;
1103 * Return grade for given user or all users.
1105 * @param int $forumid id of forum
1106 * @param int $userid optional user id, 0 means all users
1107 * @return array array of grades, false if none
1109 function forum_get_user_grades($forum, $userid=0) {
1110 global $CFG;
1112 $user = $userid ? "AND u.id = $userid" : "";
1114 $sql = "SELECT u.id, u.id AS userid, avg(fr.rating) AS rawgrade
1115 FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
1116 {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
1117 WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
1118 AND fr.userid != u.id AND fd.forum = $forum->id
1119 $user
1120 GROUP BY u.id";
1122 return get_records_sql($sql);
1126 * Update grades by firing grade_updated event
1128 * @param object $forum null means all forums
1129 * @param int $userid specific user only, 0 mean all
1131 function forum_update_grades($forum=null, $userid=0, $nullifnone=true) {
1132 global $CFG;
1134 if ($forum != null) {
1135 require_once($CFG->libdir.'/gradelib.php');
1136 if ($grades = forum_get_user_grades($forum, $userid)) {
1137 grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades);
1139 } else if ($userid and $nullifnone) {
1140 $grade = new object();
1141 $grade->userid = $userid;
1142 $grade->rawgrade = NULL;
1143 grade_update('mod/forum', $data->course, 'mod', 'forum', $forum->id, 0, $grade);
1146 } else {
1147 $sql = "SELECT f.*, cm.idnumber as cmidnumber
1148 FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1149 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1150 if ($rs = get_recordset_sql($sql)) {
1151 if ($rs->RecordCount() > 0) {
1152 while ($forum = rs_fetch_next_record($rs)) {
1153 forum_grade_item_update($forum);
1154 if ($forum->assessed) {
1155 forum_update_grades($forum, 0, false);
1159 rs_close($rs);
1165 * Create/update grade item for given forum
1167 * @param object $forum object with extra cmidnumber
1168 * @return int 0 if ok
1170 function forum_grade_item_update($forum) {
1171 global $CFG;
1172 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1173 require_once($CFG->libdir.'/gradelib.php');
1176 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
1178 if (!$forum->assessed or $forum->scale == 0) {
1179 $params['gradetype'] = GRADE_TYPE_NONE;
1181 } else if ($forum->scale > 0) {
1182 $params['gradetype'] = GRADE_TYPE_VALUE;
1183 $params['grademax'] = $forum->scale;
1184 $params['grademin'] = 0;
1186 } else if ($forum->scale < 0) {
1187 $params['gradetype'] = GRADE_TYPE_SCALE;
1188 $params['scaleid'] = -$forum->scale;
1191 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, $params);
1195 * Delete grade item for given forum
1197 * @param object $forum object
1198 * @return object grade_item
1200 function forum_grade_item_delete($forum) {
1201 global $CFG;
1202 require_once($CFG->libdir.'/gradelib.php');
1204 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
1209 * Returns the users with data in one forum
1210 * (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
1212 function forum_get_participants($forumid) {
1214 global $CFG;
1216 //Get students from forum_subscriptions
1217 $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
1218 FROM {$CFG->prefix}user u,
1219 {$CFG->prefix}forum_subscriptions s
1220 WHERE s.forum = '$forumid' and
1221 u.id = s.userid");
1222 //Get students from forum_posts
1223 $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
1224 FROM {$CFG->prefix}user u,
1225 {$CFG->prefix}forum_discussions d,
1226 {$CFG->prefix}forum_posts p
1227 WHERE d.forum = '$forumid' and
1228 p.discussion = d.id and
1229 u.id = p.userid");
1231 //Get students from forum_ratings
1232 $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
1233 FROM {$CFG->prefix}user u,
1234 {$CFG->prefix}forum_discussions d,
1235 {$CFG->prefix}forum_posts p,
1236 {$CFG->prefix}forum_ratings r
1237 WHERE d.forum = '$forumid' and
1238 p.discussion = d.id and
1239 r.post = p.id and
1240 u.id = r.userid");
1242 //Add st_posts to st_subscriptions
1243 if ($st_posts) {
1244 foreach ($st_posts as $st_post) {
1245 $st_subscriptions[$st_post->id] = $st_post;
1248 //Add st_ratings to st_subscriptions
1249 if ($st_ratings) {
1250 foreach ($st_ratings as $st_rating) {
1251 $st_subscriptions[$st_rating->id] = $st_rating;
1254 //Return st_subscriptions array (it contains an array of unique users)
1255 return ($st_subscriptions);
1261 function forum_scale_used ($forumid,$scaleid) {
1262 //This function returns if a scale is being used by one forum
1264 $return = false;
1266 $rec = get_record("forum","id","$forumid","scale","-$scaleid");
1268 if (!empty($rec) && !empty($scaleid)) {
1269 $return = true;
1272 return $return;
1275 // SQL FUNCTIONS ///////////////////////////////////////////////////////////
1278 * Gets a post with all info ready for forum_print_post
1279 * Most of these joins are just to get the forum id
1281 function forum_get_post_full($postid) {
1282 global $CFG;
1284 return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1285 FROM {$CFG->prefix}forum_posts p
1286 LEFT JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
1287 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1288 WHERE p.id = '$postid'");
1292 * Gets posts with all info ready for forum_print_post
1293 * We pass forumid in because we always know it so no need to make a
1294 * complicated join to find it out.
1296 function forum_get_discussion_posts($discussion, $sort, $forumid) {
1297 global $CFG;
1299 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1300 FROM {$CFG->prefix}forum_posts p
1301 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1302 WHERE p.discussion = $discussion
1303 AND p.parent > 0 $sort");
1307 * Gets posts with all info ready for forum_print_post
1308 * We pass forumid in because we always know it so no need to make a
1309 * complicated join to find it out.
1311 function forum_get_child_posts($parent, $forumid) {
1312 global $CFG;
1314 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1315 FROM {$CFG->prefix}forum_posts p
1316 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1317 WHERE p.parent = '$parent'
1318 ORDER BY p.created ASC");
1322 * An array of forum objects that the user is allowed to read/search through.
1323 * @param $userid
1324 * @param $courseid - if 0, we look for forums throughout the whole site.
1325 * @return array of forum objects, or false if no matches
1326 * Forum objects have the following attributes:
1327 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1328 * viewhiddentimedposts
1330 function forum_get_readable_forums($userid, $courseid=0) {
1332 global $CFG, $USER;
1334 if (!$forummod = get_record('modules', 'name', 'forum')) {
1335 error('The forum module is not installed');
1338 if ($courseid) {
1339 $courses = get_records('course', 'id', $courseid);
1340 } else {
1341 // If no course is specified, then the user can see SITE + his courses.
1342 // And admins can see all courses, so pass the $doanything flag enabled
1343 $courses1 = get_records('course', 'id', SITEID);
1344 $courses2 = get_my_courses($userid, null, null, true);
1345 $courses = array_merge($courses1, $courses2);
1347 if (!$courses) {
1348 return false;
1351 $readableforums = array();
1353 foreach ($courses as $course) {
1355 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
1357 if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
1358 $selecthidden = ' AND cm.visible = 1';
1359 } else {
1360 $selecthidden = '';
1363 $selectforums = "SELECT f.id AS id,
1364 f.name AS name,
1365 f.type AS type,
1366 f.course AS course,
1367 cm.id AS cmid,
1368 cm.visible AS cmvisible,
1369 cm.groupmode AS cmgroupmode,
1370 cm.groupingid AS cmgroupingid,
1371 cm.groupmembersonly AS cmgroupmembersonly
1372 FROM {$CFG->prefix}course_modules cm,
1373 {$CFG->prefix}forum f
1374 WHERE cm.instance = f.id
1375 AND cm.course = {$course->id}
1376 AND cm.module = {$forummod->id}
1377 $selecthidden
1378 ORDER BY f.name ASC";
1380 if ($forums = get_records_sql($selectforums)) {
1382 $group = groups_get_all_groups($course->id, $userid);
1384 foreach ($forums as $forum) {
1385 $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid);
1387 if (has_capability('mod/forum:viewdiscussion', $forumcontext)) {
1389 // Evaluate groupmode.
1390 $cm = new object;
1391 $cm->id = $forum->cmid;
1392 $cm->groupmode = $forum->cmgroupmode;
1393 $cm->groupingid = $forum->cmgroupingid;
1394 $cm->groupmembersonly = $forum->cmgroupmembersonly;
1395 $cm->course = $forum->course;
1396 $forum->cmgroupmode = groups_get_activity_groupmode($cm);
1397 if (!groups_course_module_visible($cm)) {
1398 continue;
1400 if ($forum->cmgroupmode == SEPARATEGROUPS
1401 && !has_capability('moodle/site:accessallgroups', $forumcontext)) {
1402 $forum->accessallgroups = false;
1403 $forum->accessgroup = $group->id; // The user can only access
1404 // discussions for this group.
1405 } else {
1406 $forum->accessallgroups = true;
1409 $forum->viewhiddentimedposts
1410 = has_capability('mod/forum:viewhiddentimedposts', $forumcontext);
1412 if ($forum->type == 'qanda'
1413 && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) {
1415 // We need to check whether the user has posted in the qanda forum.
1416 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
1417 // the user is allowed to see in this forum.
1419 if ($discussionspostedin =
1420 forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1421 foreach ($discussionspostedin as $d) {
1422 array_push($forum->onlydiscussions, $d->id);
1426 array_push($readableforums, $forum);
1430 } // End foreach $courses
1432 //print_object($courses);
1433 //print_object($readableforums);
1435 return $readableforums;
1439 * Returns a list of posts found using an array of search terms.
1440 * @param $searchterms - array of search terms, e.g. word +word -word
1441 * @param $courseid - if 0, we search through the whole site
1442 * @param $page
1443 * @param $recordsperpage=50
1444 * @param &$totalcount
1445 * @param $extrasql
1446 * @return array of posts found
1448 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
1449 &$totalcount, $extrasql='') {
1450 global $CFG, $USER;
1451 require_once($CFG->libdir.'/searchlib.php');
1453 $forums = forum_get_readable_forums($USER->id, $courseid);
1455 if (count($forums) == 0) {
1456 return false;
1459 for ($i=0; $i<count($forums); $i++) {
1460 if ($i == 0) {
1461 $selectdiscussion = " ((d.forum = {$forums[$i]->id}";
1462 } else {
1463 $selectdiscussion .= " OR (d.forum = {$forums[$i]->id}";
1465 if (!empty($CFG->forum_enabletimedposts) && !$forums[$i]->viewhiddentimedposts) {
1466 $now = time();
1467 $selectdiscussion .= " AND ( d.userid = {$USER->id}
1468 OR ((d.timestart = 0 OR d.timestart <= $now)
1469 AND (d.timeend = 0 OR d.timeend > $now)) )";
1471 if ($forums[$i]->type == 'qanda' && isset($forums[$i]->onlydiscussions)) {
1472 // This is a qanda forum.
1473 if (is_array($forums[$i]->onlydiscussions)) {
1474 // Show question posts as well as posts from discussions in
1475 // which the user has posted a reply.
1476 $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions);
1477 $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)";
1478 } else {
1479 // Show only the question posts.
1480 $selectdiscussion .= ' AND (p.parent = 0)';
1483 if (!$forums[$i]->accessallgroups) {
1484 if (!empty($forums[$i]->accessgroup)) {
1485 $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}";
1486 $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups.
1487 } else {
1488 // User isn't in any group. Only search discussions that are
1489 // open to all groups.
1490 $selectdiscussion .= ' AND d.groupid = -1';
1493 $selectdiscussion .= ")\n";
1495 $selectdiscussion .= ")";
1498 // Some differences SQL
1499 $LIKE = sql_ilike();
1500 $NOTLIKE = 'NOT ' . $LIKE;
1501 if ($CFG->dbfamily == 'postgres') {
1502 $REGEXP = '~*';
1503 $NOTREGEXP = '!~*';
1504 } else {
1505 $REGEXP = 'REGEXP';
1506 $NOTREGEXP = 'NOT REGEXP';
1509 $messagesearch = '';
1510 $searchstring = '';
1512 // Need to concat these back together for parser to work.
1513 foreach($searchterms as $searchterm){
1514 if ($searchstring != '') {
1515 $searchstring .= ' ';
1517 $searchstring .= $searchterm;
1520 // We need to allow quoted strings for the search. The quotes *should* be stripped
1521 // by the parser, but this should be examined carefully for security implications.
1522 $searchstring = str_replace("\\\"","\"",$searchstring);
1523 $parser = new search_parser();
1524 $lexer = new search_lexer($parser);
1526 if ($lexer->parse($searchstring)) {
1527 $parsearray = $parser->get_parsed_array();
1528 // Experimental feature under 1.8! MDL-8830
1529 // Use alternative text searches if defined
1530 // This feature only works under mysql until properly implemented for other DBs
1531 // Requires manual creation of text index for forum_posts before enabling it:
1532 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
1533 // Experimental feature under 1.8! MDL-8830
1534 if (!empty($CFG->forum_usetextsearches)) {
1535 $messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
1536 'p.userid', 'u.id', 'u.firstname',
1537 'u.lastname', 'p.modified', 'd.forum');
1538 } else {
1539 $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
1540 'p.userid', 'u.id', 'u.firstname',
1541 'u.lastname', 'p.modified', 'd.forum');
1545 $fromsql = "{$CFG->prefix}forum_posts p,
1546 {$CFG->prefix}forum_discussions d,
1547 {$CFG->prefix}user u";
1549 $selectsql = " $messagesearch
1550 AND p.discussion = d.id
1551 AND p.userid = u.id
1552 AND $selectdiscussion
1553 $extrasql";
1555 $countsql = "SELECT COUNT(*)
1556 FROM $fromsql
1557 WHERE $selectsql";
1559 $searchsql = "SELECT p.*,
1560 d.forum,
1561 u.firstname,
1562 u.lastname,
1563 u.email,
1564 u.picture
1565 FROM $fromsql
1566 WHERE $selectsql
1567 ORDER BY p.modified DESC";
1569 $totalcount = count_records_sql($countsql);
1571 return get_records_sql($searchsql, $limitfrom, $limitnum);
1575 * Returns a list of ratings for a particular post - sorted.
1577 function forum_get_ratings($postid, $sort="u.firstname ASC") {
1578 global $CFG;
1579 return get_records_sql("SELECT u.*, r.rating, r.time
1580 FROM {$CFG->prefix}forum_ratings r,
1581 {$CFG->prefix}user u
1582 WHERE r.post = '$postid'
1583 AND r.userid = u.id
1584 ORDER BY $sort");
1588 * Returns a list of all new posts that have not been mailed yet
1590 function forum_get_unmailed_posts($starttime, $endtime) {
1591 global $CFG;
1592 $now = time();
1593 return get_records_sql("SELECT p.*, d.course
1594 FROM {$CFG->prefix}forum_posts p,
1595 {$CFG->prefix}forum_discussions d
1596 WHERE p.mailed = 0
1597 AND (p.created >= '$starttime' OR d.timestart > 0)
1598 AND (p.created < '$endtime' OR p.mailnow = 1)
1599 AND p.discussion = d.id
1600 AND ((d.timestart = 0 OR d.timestart <= '$now')
1601 AND (d.timeend = 0 OR d.timeend > '$now'))
1602 ORDER BY p.modified ASC");
1606 * Marks posts before a certain time as being mailed already
1608 function forum_mark_old_posts_as_mailed($endtime) {
1609 global $CFG;
1610 // Find out posts those are not showing immediately so we can exclude them
1611 $now = time();
1612 $delayed_posts = get_records_sql("SELECT p.id, p.discussion
1613 FROM {$CFG->prefix}forum_posts p,
1614 {$CFG->prefix}forum_discussions d
1615 WHERE p.mailed = 0
1616 AND p.discussion = d.id
1617 AND d.timestart > '$now'");
1618 $delayed_ids = array();
1619 if ($delayed_posts) {
1620 foreach ($delayed_posts as $post) {
1621 $delayed_ids[] = $post->id;
1623 } else {
1624 $delayed_ids[] = 0;
1626 return execute_sql("UPDATE {$CFG->prefix}forum_posts
1627 SET mailed = '1'
1628 WHERE id NOT IN (".implode(',',$delayed_ids).")
1629 AND (created < '$endtime' OR mailnow = 1)
1630 AND mailed ='0'", false);
1634 * Get all the posts for a user in a forum suitable for forum_print_post
1636 function forum_get_user_posts($forumid, $userid) {
1637 global $CFG;
1639 return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1640 FROM {$CFG->prefix}forum f,
1641 {$CFG->prefix}forum_discussions d,
1642 {$CFG->prefix}forum_posts p,
1643 {$CFG->prefix}user u
1644 WHERE f.id = '$forumid'
1645 AND d.forum = f.id
1646 AND p.discussion = d.id
1647 AND p.userid = '$userid'
1648 AND p.userid = u.id
1649 ORDER BY p.modified ASC");
1653 * Given a log entry, return the forum post details for it.
1655 function forum_get_post_from_log($log) {
1656 global $CFG;
1658 if ($log->action == "add post") {
1660 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1661 u.firstname, u.lastname, u.email, u.picture
1662 FROM {$CFG->prefix}forum_discussions d,
1663 {$CFG->prefix}forum_posts p,
1664 {$CFG->prefix}forum f,
1665 {$CFG->prefix}user u
1666 WHERE p.id = '$log->info'
1667 AND d.id = p.discussion
1668 AND p.userid = u.id
1669 AND u.deleted <> '1'
1670 AND f.id = d.forum");
1673 } else if ($log->action == "add discussion") {
1675 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1676 u.firstname, u.lastname, u.email, u.picture
1677 FROM {$CFG->prefix}forum_discussions d,
1678 {$CFG->prefix}forum_posts p,
1679 {$CFG->prefix}forum f,
1680 {$CFG->prefix}user u
1681 WHERE d.id = '$log->info'
1682 AND d.firstpost = p.id
1683 AND p.userid = u.id
1684 AND u.deleted <> '1'
1685 AND f.id = d.forum");
1687 return NULL;
1691 * Given a discussion id, return the first post from the discussion
1693 function forum_get_firstpost_from_discussion($discussionid) {
1694 global $CFG;
1696 return get_record_sql("SELECT p.*
1697 FROM {$CFG->prefix}forum_discussions d,
1698 {$CFG->prefix}forum_posts p
1699 WHERE d.id = '$discussionid'
1700 AND d.firstpost = p.id ");
1704 * Returns an array of counts of replies to each discussion (optionally in one forum or course and/or user)
1706 function forum_count_discussion_replies($forum='0', $course='0', $user='0') {
1707 global $CFG;
1709 $forumselect = $courseselect = $userselect = '';
1711 if ($forum) {
1712 $forumselect = " AND d.forum = '$forum'";
1714 if ($course) {
1715 $courseselect = " AND d.course = '$course'";
1717 if ($user) {
1718 $userselect = " AND d.userid = '$user'";
1720 return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid
1721 FROM {$CFG->prefix}forum_posts p,
1722 {$CFG->prefix}forum_discussions d
1723 WHERE p.parent > 0 $forumselect $courseselect $userselect
1724 AND p.discussion = d.id
1725 GROUP BY p.discussion");
1729 * How many unrated posts are in the given discussion for a given user?
1731 function forum_count_unrated_posts($discussionid, $userid) {
1732 global $CFG;
1733 if ($posts = get_record_sql("SELECT count(*) as num
1734 FROM {$CFG->prefix}forum_posts
1735 WHERE parent > 0
1736 AND discussion = '$discussionid'
1737 AND userid <> '$userid' ")) {
1739 if ($rated = get_record_sql("SELECT count(*) as num
1740 FROM {$CFG->prefix}forum_posts p,
1741 {$CFG->prefix}forum_ratings r
1742 WHERE p.discussion = '$discussionid'
1743 AND p.id = r.post
1744 AND r.userid = '$userid'")) {
1745 $difference = $posts->num - $rated->num;
1746 if ($difference > 0) {
1747 return $difference;
1748 } else {
1749 return 0; // Just in case there was a counting error
1751 } else {
1752 return $posts->num;
1754 } else {
1755 return 0;
1760 * Get all discussions in a forum
1762 function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC",
1763 $user=0, $fullpost=true, $currentgroup=-1, $limit=0, $userlastmodified=false) {
1764 global $CFG, $USER;
1766 $timelimit = '';
1768 if (!$cm = get_coursemodule_from_instance('forum', $forum)) {
1769 error('Course Module ID was incorrect');
1772 if (!empty($CFG->forum_enabletimedposts)) {
1774 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1776 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
1777 $now = time();
1778 $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
1779 if (!empty($USER->id)) {
1780 $timelimit .= " OR d.userid = '$USER->id'";
1782 $timelimit .= ')';
1786 if ($user) {
1787 $userselect = " AND u.id = '$user' ";
1788 } else {
1789 $userselect = "";
1792 $limitfrom = 0;
1793 $limitnum = 0;
1794 if ($limit) {
1795 $limitnum = $limit;
1798 if ($currentgroup == -1) {
1799 $currentgroup = get_current_group($cm->course);
1802 if ($currentgroup) {
1803 $groupselect = " AND (d.groupid = '$currentgroup' OR d.groupid = -1) ";
1804 } else {
1805 $groupselect = "";
1808 if (empty($forumsort)) {
1809 $forumsort = "d.timemodified DESC";
1811 if (empty($fullpost)) {
1812 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
1813 } else {
1814 $postdata = "p.*";
1817 if (empty($userlastmodified)) { // We don't need to know this
1818 $umfields = '';
1819 $umtable = '';
1820 } else {
1821 $umfields = ', um.firstname AS umfirstname, um.lastname AS umlastname';
1822 $umtable = ' LEFT JOIN '.$CFG->prefix.'user um on (d.usermodified = um.id)';
1825 //TODO: there must be a nice way to do this that keeps both postgres and mysql 3.2x happy but I can't find it right now.
1826 if ($CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'oracle') {
1827 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1828 u.firstname, u.lastname, u.email, u.picture $umfields
1829 FROM {$CFG->prefix}forum_discussions d
1830 JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
1831 JOIN {$CFG->prefix}user u ON p.userid = u.id
1832 $umtable
1833 WHERE d.forum = '$forum'
1834 AND p.parent = 0
1835 $timelimit $groupselect $userselect
1836 ORDER BY $forumsort", $limitfrom, $limitnum);
1837 } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query)
1838 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1839 u.firstname, u.lastname, u.email, u.picture $umfields
1840 FROM ({$CFG->prefix}forum_posts p,
1841 {$CFG->prefix}user u,
1842 {$CFG->prefix}forum_discussions d)
1843 $umtable
1844 WHERE d.forum = '$forum'
1845 AND p.discussion = d.id
1846 AND p.parent = 0
1847 AND p.userid = u.id $timelimit $groupselect $userselect
1848 ORDER BY $forumsort", $limitfrom, $limitnum);
1855 * Get all discussions started by a particular user in a course (or group)
1856 * This function no longer used ...
1858 function forum_get_user_discussions($courseid, $userid, $groupid=0) {
1859 global $CFG;
1861 if ($groupid) {
1862 $groupselect = " AND d.groupid = '$groupid' ";
1863 } else {
1864 $groupselect = "";
1867 return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture,
1868 f.type as forumtype, f.name as forumname, f.id as forumid
1869 FROM {$CFG->prefix}forum_discussions d,
1870 {$CFG->prefix}forum_posts p,
1871 {$CFG->prefix}user u,
1872 {$CFG->prefix}forum f
1873 WHERE d.course = '$courseid'
1874 AND p.discussion = d.id
1875 AND p.parent = 0
1876 AND p.userid = u.id
1877 AND u.id = '$userid'
1878 AND d.forum = f.id $groupselect
1879 ORDER BY p.created DESC");
1883 * Returns list of user objects that are subscribed to this forum
1885 function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) {
1887 global $CFG;
1889 static $resultscache = array();
1891 if ($cache && isset($resultscache[$forum->id][$groupid])) {
1892 return $resultscache[$forum->id][$groupid];
1895 if ($groupid) {
1896 $grouptables = ", {$CFG->prefix}groups_members gm ";
1897 $groupselect = "AND gm.groupid = '$groupid' AND u.id = gm.userid";
1899 } else {
1900 $grouptables = '';
1901 $groupselect = '';
1904 if (forum_is_forcesubscribed($forum)) {
1905 $results = get_course_users($course->id); // Otherwise get everyone in the course
1906 } else {
1907 $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop,
1908 u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums
1909 FROM {$CFG->prefix}user u,
1910 {$CFG->prefix}forum_subscriptions s $grouptables
1911 WHERE s.forum = '$forum->id'
1912 AND s.userid = u.id
1913 AND u.deleted <> 1 $groupselect
1914 ORDER BY u.email ASC");
1916 // Guest user should never be subscribed to a forum.
1917 if ($guest = guest_user()) {
1918 unset($results[$guest->id]);
1921 if ($cache) {
1922 $resultscache[$forum->id][$groupid] = $results;
1925 return $results;
1930 // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
1933 function forum_get_course_forum($courseid, $type) {
1934 // How to set up special 1-per-course forums
1935 global $CFG;
1937 if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
1938 // There should always only be ONE, but with the right combination of
1939 // errors there might be more. In this case, just return the oldest one (lowest ID).
1940 foreach ($forums as $forum) {
1941 return $forum; // ie the first one
1945 // Doesn't exist, so create one now.
1946 $forum->course = $courseid;
1947 $forum->type = "$type";
1948 switch ($forum->type) {
1949 case "news":
1950 $forum->name = addslashes(get_string("namenews", "forum"));
1951 $forum->intro = addslashes(get_string("intronews", "forum"));
1952 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
1953 $forum->assessed = 0;
1954 if ($courseid == SITEID) {
1955 $forum->name = get_string("sitenews");
1956 $forum->forcesubscribe = 0;
1958 break;
1959 case "social":
1960 $forum->name = addslashes(get_string("namesocial", "forum"));
1961 $forum->intro = addslashes(get_string("introsocial", "forum"));
1962 $forum->assessed = 0;
1963 $forum->forcesubscribe = 0;
1964 break;
1965 default:
1966 notify("That forum type doesn't exist!");
1967 return false;
1968 break;
1971 $forum->timemodified = time();
1972 $forum->id = insert_record("forum", $forum);
1974 if (! $module = get_record("modules", "name", "forum")) {
1975 notify("Could not find forum module!!");
1976 return false;
1978 $mod->course = $courseid;
1979 $mod->module = $module->id;
1980 $mod->instance = $forum->id;
1981 $mod->section = 0;
1982 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
1983 notify("Could not add a new course module to the course '" . format_string($course->fullname) . "'");
1984 return false;
1986 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
1987 notify("Could not add the new course module to that section");
1988 return false;
1990 if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
1991 notify("Could not update the course module with the correct section");
1992 return false;
1994 include_once("$CFG->dirroot/course/lib.php");
1995 rebuild_course_cache($courseid);
1997 return get_record("forum", "id", "$forum->id");
2002 * Given the data about a posting, builds up the HTML to display it and
2003 * returns the HTML in a string. This is designed for sending via HTML email.
2005 function forum_make_mail_post(&$post, $user, $touser, $course,
2006 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2009 global $CFG, $USER;
2011 // the old caching was removed for now, because it did not work due to recent changes in cron
2013 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2015 if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) {
2016 mtrace('Course Module ID was incorrect');
2018 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2020 // format the post body
2021 $options = new object();
2022 $options->para = true;
2023 $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);
2025 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
2027 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
2028 $output .= print_user_picture($user->id, $course->id, $user->picture, false, true);
2029 $output .= '</td>';
2031 if ($post->parent) {
2032 $output .= '<td class="topic">';
2033 } else {
2034 $output .= '<td class="topic starter">';
2036 $output .= '<div class="subject">'.format_string($post->subject).'</div>';
2038 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext));
2039 $by = new object();
2040 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
2041 $by->date = userdate($post->modified, '', $touser->timezone);
2042 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
2044 $output .= '</td></tr>';
2046 $output .= '<tr><td class="left side" valign="top">';
2047 if ($group = groups_get_all_groups($course->id, $user->id)) {
2048 $output .= print_group_picture($group, $course->id, false, true, true);
2049 } else {
2050 $output .= '&nbsp;';
2053 $output .= '</td><td class="content">';
2055 if ($post->attachment) {
2056 $post->course = $course->id;
2057 $output .= '<div class="attachments">';
2058 $output .= forum_print_attachments($post, 'html');
2059 $output .= "</div>";
2062 $output .= $formattedtext;
2064 // Commands
2065 $commands = array();
2067 if ($post->parent) {
2068 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2069 $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
2072 if ($reply) {
2073 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
2074 get_string('reply', 'forum').'</a>';
2077 $output .= '<div class="commands">';
2078 $output .= implode(' | ', $commands);
2079 $output .= '</div>';
2081 // Context link to post if required
2082 if ($link) {
2083 $output .= '<div class="link">';
2084 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
2085 get_string('postincontext', 'forum').'</a>';
2086 $output .= '</div>';
2089 if ($footer) {
2090 $output .= '<div class="footer">'.$footer.'</div>';
2092 $output .= '</td></tr></table>'."\n\n";
2094 return $output;
2098 * Print a forum post
2100 * @param object $post The post to print.
2101 * @param integer $courseid The course this post belongs to.
2102 * @param boolean $ownpost Whether this post belongs to the current user.
2103 * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
2104 * @param boolean $link Just print a shortened version of the post as a link to the full post.
2105 * @param object $ratings -- I don't really know --
2106 * @param string $footer Extra stuff to print after the message.
2107 * @param string $highlight Space-separated list of terms to highlight.
2108 * @param int $post_read true, false or -99. If we already know whether this user
2109 * has read this post, pass that in, otherwise, pass in -99, and this
2110 * function will work it out.
2111 * @param boolean $dummyifcantsee When forum_user_can_see_post says that
2112 * the current user can't see this post, if this argument is true
2113 * (the default) then print a dummy 'you can't see this post' post.
2114 * If false, don't output anything at all.
2116 function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false,
2117 $ratings=NULL, $footer="", $highlight="", $post_read=-99, $dummyifcantsee=true) {
2119 global $USER, $CFG;
2121 static $stredit, $strdelete, $strreply, $strparent, $strprune;
2122 static $strpruneheading, $displaymode;
2123 static $strmarkread, $strmarkunread, $istracked;
2126 $discussion = get_record('forum_discussions', 'id', $post->discussion);
2127 if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum)) {
2128 error('Course Module ID was incorrect');
2130 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2133 if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
2134 if (!$dummyifcantsee) {
2135 return;
2137 echo '<a id="p'.$post->id.'"></a>';
2138 echo '<table cellspacing="0" class="forumpost">';
2139 echo '<tr class="header"><td class="picture left">';
2140 // print_user_picture($post->userid, $courseid, $post->picture);
2141 echo '</td>';
2142 if ($post->parent) {
2143 echo '<td class="topic">';
2144 } else {
2145 echo '<td class="topic starter">';
2147 echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
2148 echo '<div class="author">';
2149 print_string('forumauthorhidden','forum');
2150 echo '</div></td></tr>';
2152 echo '<tr><td class="left side">';
2153 echo '&nbsp;';
2155 // Actual content
2157 echo '</td><td class="content">'."\n";
2158 echo get_string('forumbodyhidden','forum');
2159 echo '</td></tr></table>';
2160 return;
2163 if (empty($stredit)) {
2164 $stredit = get_string('edit', 'forum');
2165 $strdelete = get_string('delete', 'forum');
2166 $strreply = get_string('reply', 'forum');
2167 $strparent = get_string('parent', 'forum');
2168 $strpruneheading = get_string('pruneheading', 'forum');
2169 $strprune = get_string('prune', 'forum');
2170 $displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
2171 $strmarkread = get_string('markread', 'forum');
2172 $strmarkunread = get_string('markunread', 'forum');
2174 if (!empty($post->forum)) {
2175 $istracked = (forum_tp_can_track_forums($post->forum) &&
2176 forum_tp_is_tracked($post->forum));
2177 } else {
2178 $istracked = false;
2182 if ($istracked) {
2183 if ($post_read == -99) { // If we don't know yet...
2184 // The front page can display a news item post to non-logged in users. This should
2185 // always appear as 'read'.
2186 $post_read = empty($USER) || forum_tp_is_post_read($USER->id, $post);
2188 if ($post_read) {
2189 $read_style = ' read';
2190 } else {
2191 $read_style = ' unread';
2192 echo '<a name="unread"></a>';
2194 } else {
2195 $read_style = '';
2198 echo '<a id="p'.$post->id.'"></a>';
2199 echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
2201 echo '<tr class="header"><td class="picture left">';
2202 print_user_picture($post->userid, $courseid, $post->picture);
2203 echo '</td>';
2205 if ($post->parent) {
2206 echo '<td class="topic">';
2207 } else {
2208 echo '<td class="topic starter">';
2211 if (!empty($post->subjectnoformat)) {
2212 echo '<div class="subject">'.$post->subject.'</div>';
2213 } else {
2214 echo '<div class="subject">'.format_string($post->subject).'</div>';
2217 echo '<div class="author">';
2218 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2219 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
2220 $post->userid.'&amp;course='.$courseid.'">'.$fullname.'</a>';
2221 $by->date = userdate($post->modified);
2222 print_string('bynameondate', 'forum', $by);
2223 echo '</div></td></tr>';
2225 echo '<tr><td class="left side">';
2226 if ($group = groups_get_all_groups($courseid, $post->userid)) {
2227 print_group_picture($group, $courseid, false, false, true);
2228 } else {
2229 echo '&nbsp;';
2232 // Actual content
2234 echo '</td><td class="content">'."\n";
2236 if ($post->attachment) {
2237 $post->course = $courseid;
2238 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2239 echo '<div class="attachments">';
2240 $attachedimages = forum_print_attachments($post);
2241 echo '</div>';
2242 } else {
2243 $attachedimages = '';
2247 $options = new Object;
2248 $options->para = false;
2249 $options->trusttext = true;
2250 if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) {
2251 // Print shortened version
2252 echo format_text(forum_shorten_post($post->message), $post->format, $options, $courseid);
2253 $numwords = count_words(strip_tags($post->message));
2254 echo '<p><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2255 echo get_string('readtherest', 'forum');
2256 echo '</a> ('.get_string('numwords', '', $numwords).')...</p>';
2257 } else {
2258 // Print whole message
2259 if ($highlight) {
2260 echo highlight($highlight, format_text($post->message, $post->format, $options, $courseid));
2261 } else {
2262 echo format_text($post->message, $post->format, $options, $courseid);
2264 echo $attachedimages;
2268 // Commands
2270 $commands = array();
2272 if ($istracked) {
2273 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
2274 // Don't display the mark read / unread controls in this case.
2275 if ($CFG->forum_usermarksread && !empty($USER)) {
2276 if ($post_read) {
2277 $mcmd = '&amp;mark=unread&amp;postid='.$post->id;
2278 $mtxt = $strmarkunread;
2279 } else {
2280 $mcmd = '&amp;mark=read&amp;postid='.$post->id;
2281 $mtxt = $strmarkread;
2283 if ($displaymode == FORUM_MODE_THREADED) {
2284 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2285 $post->discussion.'&amp;parent='.$post->id.$mcmd.'">'.$mtxt.'</a>';
2286 } else {
2287 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2288 $post->discussion.$mcmd.'#p'.$post->id.'">'.$mtxt.'</a>';
2293 if ($post->parent) { // Zoom in to the parent specifically
2294 if ($displaymode == FORUM_MODE_THREADED) {
2295 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2296 $post->discussion.'&amp;parent='.$post->parent.'">'.$strparent.'</a>';
2297 } else {
2298 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2299 $post->discussion.'#p'.$post->parent.'">'.$strparent.'</a>';
2303 $forumtype = get_field('forum', 'type', 'id', $post->forum);
2305 $age = time() - $post->created;
2306 // Hack for allow to edit news posts those are not displayed yet until they are displayed
2307 if (!$post->parent
2308 && $forumtype == 'news'
2309 && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) {
2310 $age = 0;
2312 $editanypost = has_capability('mod/forum:editanypost', $modcontext);
2316 if ($ownpost or $editanypost) {
2317 if (($age < $CFG->maxeditingtime) or $editanypost) {
2318 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?edit='.$post->id.'">'.$stredit.'</a>';
2322 if (has_capability('mod/forum:splitdiscussions', $modcontext)
2323 && $post->parent && $forumtype != 'single') {
2325 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?prune='.$post->id.
2326 '" title="'.$strpruneheading.'">'.$strprune.'</a>';
2329 if (($ownpost and $age < $CFG->maxeditingtime
2330 and has_capability('mod/forum:deleteownpost', $modcontext))
2331 or has_capability('mod/forum:deleteanypost', $modcontext)) {
2332 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?delete='.$post->id.'">'.$strdelete.'</a>';
2335 if ($reply) {
2336 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.$strreply.'</a>';
2339 echo '<div class="commands">';
2340 echo implode(' | ', $commands);
2341 echo '</div>';
2344 // Ratings
2346 $ratingsmenuused = false;
2347 if (!empty($ratings) and !empty($USER->id)) {
2348 echo '<div class="ratings">';
2349 $useratings = true;
2350 if ($ratings->assesstimestart and $ratings->assesstimefinish) {
2351 if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) {
2352 $useratings = false;
2355 if ($useratings) {
2356 $mypost = ($USER->id == $post->userid);
2358 $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext);
2360 if ($canviewallratings and !$mypost) {
2361 forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings);
2362 if (!empty($ratings->allow)) {
2363 echo '&nbsp;';
2364 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2365 $ratingsmenuused = true;
2368 } else if ($mypost) {
2369 forum_print_ratings_mean($post->id, $ratings->scale, true);
2371 } else if (!empty($ratings->allow) ) {
2372 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2373 $ratingsmenuused = true;
2376 echo '</div>';
2379 // Link to post if required
2381 if ($link) {
2382 echo '<div class="link">';
2383 if ($post->replies == 1) {
2384 $replystring = get_string('repliesone', 'forum', $post->replies);
2385 } else {
2386 $replystring = get_string('repliesmany', 'forum', $post->replies);
2388 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.
2389 get_string('discussthistopic', 'forum').'</a>&nbsp;('.$replystring.')';
2390 echo '</div>';
2393 if ($footer) {
2394 echo '<div class="footer">'.$footer.'</div>';
2396 echo '</td></tr></table>'."\n\n";
2398 if ($istracked && !$CFG->forum_usermarksread && !empty($post->forum)) {
2399 forum_tp_mark_post_read($USER->id, $post, $post->forum);
2402 return $ratingsmenuused;
2407 * This function prints the overview of a discussion in the forum listing.
2408 * It needs some discussion information and some post information, these
2409 * happen to be combined for efficiency in the $post parameter by the function
2410 * that calls this one: forum_print_latest_discussions()
2412 * @param object $post The post object (passed by reference for speed).
2413 * @param object $forum The forum object.
2414 * @param int $group Current group.
2415 * @param string $datestring Format to use for the dates.
2416 * @param boolean $cantrack Is tracking enabled for this forum.
2417 * @param boolean $forumtracked Is the user tracking this forum.
2418 * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
2420 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
2421 $cantrack=true, $forumtracked=true, $canviewparticipants=true) {
2423 global $USER, $CFG;
2425 static $rowcount;
2426 static $strmarkalldread;
2429 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2430 error('Course Module ID was incorrect');
2432 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2435 if (!isset($rowcount)) {
2436 $rowcount = 0;
2437 $strmarkalldread = get_string('markalldread', 'forum');
2438 } else {
2439 $rowcount = ($rowcount + 1) % 2;
2442 $post->subject = format_string($post->subject,true);
2444 echo "\n\n";
2445 echo '<tr class="discussion r'.$rowcount.'">';
2447 // Topic
2448 echo '<td class="topic starter">';
2449 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
2450 echo "</td>\n";
2452 // Picture
2453 echo '<td class="picture">';
2454 print_user_picture($post->userid, $forum->course, $post->picture);
2455 echo "</td>\n";
2457 // User name
2458 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2459 echo '<td class="author">';
2460 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
2461 echo "</td>\n";
2463 // Group picture
2464 if ($group !== -1) { // Groups are active - group is a group data object or NULL
2465 echo '<td class="picture group">';
2466 if (!empty($group->picture) and empty($group->hidepicture)) {
2467 print_group_picture($group, $forum->course, false, false, true);
2468 } else if (isset($group->id)) {
2469 if($canviewparticipants) {
2470 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
2471 } else {
2472 echo $group->name;
2475 echo "</td>\n";
2478 if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies
2479 echo '<td class="replies">';
2480 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2481 echo $post->replies.'</a>';
2482 echo "</td>\n";
2484 if ($cantrack) {
2485 echo '<td class="replies">';
2486 if ($forumtracked) {
2487 if ($post->unread > 0) {
2488 echo '<span class="unread">';
2489 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
2490 echo $post->unread;
2491 echo '</a>';
2492 echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
2493 $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
2494 '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
2495 echo '</span>';
2496 } else {
2497 echo '<span class="read">';
2498 echo $post->unread;
2499 echo '</span>';
2501 } else {
2502 echo '<span class="read">';
2503 echo '-';
2504 echo '</span>';
2506 echo "</td>\n";
2510 echo '<td class="lastpost">';
2511 $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case
2512 $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
2513 $usermodified->id = $post->usermodified;
2514 $usermodified->firstname = $post->umfirstname;
2515 $usermodified->lastname = $post->umlastname;
2516 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
2517 fullname($usermodified).'</a><br />';
2518 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
2519 userdate($usedate, $datestring).'</a>';
2520 echo "</td>\n";
2522 echo "</tr>\n\n";
2528 * Given a post object that we already know has a long message
2529 * this function truncates the message nicely to the first
2530 * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
2532 function forum_shorten_post($message) {
2534 global $CFG;
2536 $i = 0;
2537 $tag = false;
2538 $length = strlen($message);
2539 $count = 0;
2540 $stopzone = false;
2541 $truncate = 0;
2543 for ($i=0; $i<$length; $i++) {
2544 $char = $message[$i];
2546 switch ($char) {
2547 case "<":
2548 $tag = true;
2549 break;
2550 case ">":
2551 $tag = false;
2552 break;
2553 default:
2554 if (!$tag) {
2555 if ($stopzone) {
2556 if ($char == ".") {
2557 $truncate = $i+1;
2558 break 2;
2561 $count++;
2563 break;
2565 if (!$stopzone) {
2566 if ($count > $CFG->forum_shortpost) {
2567 $stopzone = true;
2572 if (!$truncate) {
2573 $truncate = $i;
2576 return substr($message, 0, $truncate);
2581 * Print the multiple ratings on a post given to the current user by others.
2582 * Scale is an array of ratings
2584 function forum_print_ratings_mean($postid, $scale, $link=true) {
2586 static $strrate;
2588 $mean = forum_get_ratings_mean($postid, $scale);
2590 if ($mean !== "") {
2592 if (empty($strratings)) {
2593 $strratings = get_string("ratings", "forum");
2596 echo "$strratings: ";
2597 if ($link) {
2598 link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600);
2599 } else {
2600 echo "$mean ";
2607 * Return the mean rating of a post given to the current user by others.
2608 * Scale is an array of possible ratings in the scale
2609 * Ratings is an optional simple array of actual ratings (just integers)
2611 function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
2613 if (!$ratings) {
2614 $ratings = array();
2615 if ($rates = get_records("forum_ratings", "post", $postid)) {
2616 foreach ($rates as $rate) {
2617 $ratings[] = $rate->rating;
2622 $count = count($ratings);
2624 if ($count == 0) {
2625 return "";
2627 } else if ($count == 1) {
2628 return $scale[$ratings[0]];
2630 } else {
2631 $total = 0;
2632 foreach ($ratings as $rating) {
2633 $total += $rating;
2635 $mean = round( ((float)$total/(float)$count) + 0.001); // Little fudge factor so that 0.5 goes UP
2637 if (isset($scale[$mean])) {
2638 return $scale[$mean]." ($count)";
2639 } else {
2640 return "$mean ($count)"; // Should never happen, hopefully
2646 * Return a summary of post ratings given to the current user by others.
2647 * Scale is an array of possible ratings in the scale
2648 * Ratings is an optional simple array of actual ratings (just integers)
2650 function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
2652 if (!$ratings) {
2653 $ratings = array();
2654 if ($rates = get_records("forum_ratings", "post", $postid)) {
2655 foreach ($rates as $rate) {
2656 $rating[] = $rate->rating;
2662 if (!$count = count($ratings)) {
2663 return "";
2667 foreach ($scale as $key => $scaleitem) {
2668 $sumrating[$key] = 0;
2671 foreach ($ratings as $rating) {
2672 $sumrating[$rating]++;
2675 $summary = "";
2676 foreach ($scale as $key => $scaleitem) {
2677 $summary = $sumrating[$key].$summary;
2678 if ($key > 1) {
2679 $summary = "/$summary";
2682 return $summary;
2686 * Print the menu of ratings as part of a larger form.
2687 * If the post has already been - set that value.
2688 * Scale is an array of ratings
2690 function forum_print_rating_menu($postid, $userid, $scale) {
2692 static $strrate;
2694 if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
2695 $rating->rating = FORUM_UNSET_POST_RATING;
2698 if (empty($strrate)) {
2699 $strrate = get_string("rate", "forum");
2701 $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale;
2702 choose_from_menu($scale, $postid, $rating->rating, '');
2706 * Print the drop down that allows the user to select how they want to have
2707 * the discussion displayed.
2708 * @param $id - forum id if $forumtype is 'single',
2709 * discussion id for any other forum type
2710 * @param $mode - forum layout mode
2711 * @param $forumtype - optional
2713 function forum_print_mode_form($id, $mode, $forumtype='') {
2714 global $FORUM_LAYOUT_MODES;
2716 if ($forumtype == 'single') {
2717 popup_form("view.php?f=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2718 } else {
2719 popup_form("discuss.php?d=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2726 function forum_search_form($course, $search='') {
2727 global $CFG;
2729 $output = '<div class="forumsearch">';
2730 $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
2731 $output .= '<fieldset class="invisiblefieldset">';
2732 $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
2733 $output .= '<input name="search" type="text" size="18" value="'.$search.'" alt="search" />';
2734 $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
2735 $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
2736 $output .= '</fieldset>';
2737 $output .= '</form>';
2738 $output .= '</div>';
2740 return $output;
2747 function forum_set_return() {
2748 global $CFG, $SESSION;
2750 if (! isset($SESSION->fromdiscussion)) {
2751 if (!empty($_SERVER['HTTP_REFERER'])) {
2752 $referer = $_SERVER['HTTP_REFERER'];
2753 } else {
2754 $referer = "";
2756 // If the referer is NOT a login screen then save it.
2757 if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
2758 $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
2767 function forum_go_back_to($default) {
2768 global $SESSION;
2770 if (!empty($SESSION->fromdiscussion)) {
2771 $returnto = $SESSION->fromdiscussion;
2772 unset($SESSION->fromdiscussion);
2773 return $returnto;
2774 } else {
2775 return $default;
2780 * Creates a directory file name, suitable for make_upload_directory()
2782 function forum_file_area_name($post) {
2783 global $CFG;
2785 return "$post->course/$CFG->moddata/forum/$post->forum/$post->id";
2791 function forum_file_area($post) {
2792 return make_upload_directory( forum_file_area_name($post) );
2798 function forum_delete_old_attachments($post, $exception="") {
2801 * Deletes all the user files in the attachments area for a post
2802 * EXCEPT for any file named $exception
2804 if ($basedir = forum_file_area($post)) {
2805 if ($files = get_directory_list($basedir)) {
2806 foreach ($files as $file) {
2807 if ($file != $exception) {
2808 unlink("$basedir/$file");
2809 notify("Existing file '$file' has been deleted!");
2813 if (!$exception) { // Delete directory as well, if empty
2814 rmdir("$basedir");
2820 * Given a discussion object that is being moved to forumid,
2821 * this function checks all posts in that discussion
2822 * for attachments, and if any are found, these are
2823 * moved to the new forum directory.
2825 function forum_move_attachments($discussion, $forumid) {
2827 global $CFG;
2829 require_once($CFG->dirroot.'/lib/uploadlib.php');
2831 $return = true;
2833 if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
2834 foreach ($posts as $oldpost) {
2835 $oldpost->course = $discussion->course;
2836 $oldpost->forum = $discussion->forum;
2837 $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
2838 if (is_dir($oldpostdir)) {
2839 $newpost = $oldpost;
2840 $newpost->forum = $forumid;
2841 $newpostdir = forum_file_area_name($newpost);
2842 // take off the last directory because otherwise we're renaming to a directory that already exists
2843 // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
2844 make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
2845 $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost);
2846 $files = get_directory_list($oldpostdir); // get it before we rename it.
2847 if (! @rename($oldpostdir, $newpostdir)) {
2848 $return = false;
2850 foreach ($files as $file) {
2851 clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
2856 return $return;
2860 * if return=html, then return a html string.
2861 * if return=text, then return a text-only string.
2862 * otherwise, print HTML for non-images, and return image HTML
2864 function forum_print_attachments($post, $return=NULL) {
2866 global $CFG;
2868 $filearea = forum_file_area_name($post);
2870 $imagereturn = "";
2871 $output = "";
2873 if ($basedir = forum_file_area($post)) {
2874 if ($files = get_directory_list($basedir)) {
2875 $strattachment = get_string("attachment", "forum");
2876 foreach ($files as $file) {
2877 $icon = mimeinfo("icon", $file);
2878 $type = mimeinfo("type", $file);
2879 if ($CFG->slasharguments) {
2880 $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
2881 } else {
2882 $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
2884 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
2886 if ($return == "html") {
2887 $output .= "<a href=\"$ffurl\">$image</a> ";
2888 $output .= "<a href=\"$ffurl\">$file</a><br />";
2890 } else if ($return == "text") {
2891 $output .= "$strattachment $file:\n$ffurl\n";
2893 } else {
2894 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
2895 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
2896 } else {
2897 echo "<a href=\"$ffurl\">$image</a> ";
2898 echo filter_text("<a href=\"$ffurl\">$file</a><br />");
2905 if ($return) {
2906 return $output;
2909 return $imagereturn;
2912 * If successful, this function returns the name of the file
2913 * @param $post is a full post record, including course and forum
2914 * @param $newfile is a full upload array from $_FILES
2915 * @param $message is a string to hold the messages.
2921 function forum_add_attachment($post, $inputname,&$message) {
2923 global $CFG;
2925 if (!$forum = get_record("forum", "id", $post->forum)) {
2926 return "";
2929 if (!$course = get_record("course", "id", $forum->course)) {
2930 return "";
2933 require_once($CFG->dirroot.'/lib/uploadlib.php');
2934 $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true);
2935 $dir = forum_file_area_name($post);
2936 if ($um->process_file_uploads($dir)) {
2937 $message .= $um->get_errors();
2938 return $um->get_new_filename();
2940 $message .= $um->get_errors();
2941 return null;
2947 function forum_add_new_post($post,&$message) {
2949 global $USER, $CFG;
2951 $post->created = $post->modified = time();
2952 $post->mailed = "0";
2953 $post->userid = $USER->id;
2954 $post->attachment = "";
2956 if (! $post->id = insert_record("forum_posts", $post)) {
2957 return false;
2960 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
2961 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id);
2964 // Update discussion modified date
2965 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2966 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2968 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2969 forum_tp_mark_post_read($post->userid, $post, $post->forum);
2972 return $post->id;
2978 function forum_update_post($post,&$message) {
2980 global $USER, $CFG;
2982 $post->modified = time();
2984 if (!$post->parent) { // Post is a discussion starter - update discussion title too
2985 set_field("forum_discussions", "name", $post->subject, "id", $post->discussion);
2988 if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
2989 $post->attachment = $newfilename;
2990 } else {
2991 unset($post->attachment);
2994 // Update discussion modified date
2995 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2996 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2998 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2999 forum_tp_mark_post_read($post->userid, $post, $post->forum);
3002 return update_record("forum_posts", $post);
3006 * Given an object containing all the necessary data,
3007 * create a new discussion and return the id
3009 function forum_add_discussion($discussion,&$message) {
3011 GLOBAL $USER, $CFG;
3013 $timenow = time();
3015 // The first post is stored as a real post, and linked
3016 // to from the discuss entry.
3018 $post->discussion = 0;
3019 $post->parent = 0;
3020 $post->userid = $USER->id;
3021 $post->created = $timenow;
3022 $post->modified = $timenow;
3023 $post->mailed = 0;
3024 $post->subject = $discussion->name;
3025 $post->message = $discussion->intro;
3026 $post->attachment = "";
3027 $post->forum = $discussion->forum;
3028 $post->course = $discussion->course;
3029 $post->format = $discussion->format;
3030 $post->mailnow = $discussion->mailnow;
3032 if (! $post->id = insert_record("forum_posts", $post) ) {
3033 return 0;
3036 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
3037 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors
3040 // Now do the main entry for the discussion,
3041 // linking to this first post
3043 $discussion->firstpost = $post->id;
3044 $discussion->timemodified = $timenow;
3045 $discussion->usermodified = $post->userid;
3046 $discussion->userid = $USER->id;
3048 if (! $post->discussion = insert_record("forum_discussions", $discussion) ) {
3049 delete_records("forum_posts", "id", $post->id);
3050 return 0;
3053 // Finally, set the pointer on the post.
3054 if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) {
3055 delete_records("forum_posts", "id", $post->id);
3056 delete_records("forum_discussions", "id", $post->discussion);
3057 return 0;
3060 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
3061 forum_tp_mark_post_read($post->userid, $post, $post->forum);
3064 return $post->discussion;
3071 function forum_delete_discussion($discussion, $fulldelete=false) {
3072 // $discussion is a discussion record object
3074 $result = true;
3076 if ($posts = get_records("forum_posts", "discussion", $discussion->id)) {
3077 foreach ($posts as $post) {
3078 $post->course = $discussion->course;
3079 $post->forum = $discussion->forum;
3080 if (! delete_records("forum_ratings", "post", "$post->id")) {
3081 $result = false;
3083 if (! forum_delete_post($post, $fulldelete)) {
3084 $result = false;
3089 forum_tp_delete_read_records(-1, -1, $discussion->id);
3091 if (! delete_records("forum_discussions", "id", "$discussion->id")) {
3092 $result = false;
3095 return $result;
3102 function forum_delete_post($post, $children=false) {
3103 if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
3104 if ($children) {
3105 foreach ($childposts as $childpost) {
3106 forum_delete_post($childpost, true);
3108 } else {
3109 return false;
3112 if (delete_records("forum_posts", "id", $post->id)) {
3113 delete_records("forum_ratings", "post", $post->id); // Just in case
3115 forum_tp_delete_read_records(-1, $post->id);
3117 if ($post->attachment) {
3118 $discussion = get_record("forum_discussions", "id", $post->discussion);
3119 $post->course = $discussion->course;
3120 $post->forum = $discussion->forum;
3121 forum_delete_old_attachments($post);
3124 // Just in case we are deleting the last post
3125 forum_discussion_update_last_post($post->discussion);
3127 return true;
3129 return false;
3135 function forum_count_replies($post, $children=true) {
3136 $count = 0;
3138 if ($children) {
3139 if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
3140 foreach ($childposts as $childpost) {
3141 $count ++; // For this child
3142 $count += forum_count_replies($childpost, true);
3145 } else {
3146 $count += count_records('forum_posts', 'parent', $post->id);
3149 return $count;
3156 function forum_forcesubscribe($forumid, $value=1) {
3157 return set_field("forum", "forcesubscribe", $value, "id", $forumid);
3163 function forum_is_forcesubscribed($forum) {
3164 if (isset($forum->forcesubscribe)) { // then we use that
3165 return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE);
3166 } else { // Check the database
3167 return (get_field('forum', 'forcesubscribe', 'id', $forum) == FORUM_FORCESUBSCRIBE);
3174 function forum_is_subscribed($userid, $forumid) {
3175 if (forum_is_forcesubscribed($forumid)) {
3176 return true;
3178 return record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid);
3182 * Adds user to the subscriber list
3184 function forum_subscribe($userid, $forumid) {
3186 if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) {
3187 return true;
3190 $sub->userid = $userid;
3191 $sub->forum = $forumid;
3193 return insert_record("forum_subscriptions", $sub);
3197 * Removes user from the subscriber list
3199 function forum_unsubscribe($userid, $forumid) {
3200 return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid);
3204 * Given a new post, subscribes or unsubscribes as appropriate.
3205 * Returns some text which describes what happened.
3207 function forum_post_subscription($post) {
3209 global $USER;
3211 $subscribed=forum_is_subscribed($USER->id, $post->forum);
3212 if ((isset($post->subscribe) && $post->subscribe && $subscribed)
3213 || (!$post->subscribe && !$subscribed)) {
3214 return "";
3217 if (!$forum = get_record("forum", "id", $post->forum)) {
3218 return "";
3221 $info->name = fullname($USER);
3222 $info->forum = $forum->name;
3224 if (!empty($post->subscribe)) {
3225 forum_subscribe($USER->id, $post->forum);
3226 return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
3229 forum_unsubscribe($USER->id, $post->forum);
3230 return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
3234 * Generate and return the subscribe or unsubscribe link for a forum.
3235 * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
3236 * @param object $context the context object for this forum.
3237 * @param array $messages text used for the link in its various states
3238 * (subscribed, unsubscribed, forcesubscribed or cantsubscribe).
3239 * Any strings not passed in are taken from the $defaultmessages array
3240 * at the top of the function.
3241 * @param
3243 function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false) {
3244 global $CFG, $USER;
3245 $defaultmessages = array(
3246 'subscribed' => get_string('unsubscribe', 'forum'),
3247 'unsubscribed' => get_string('subscribe', 'forum'),
3248 'cantaccessgroup' => get_string('no'),
3249 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
3250 'cantsubscribe' => get_string('disallowsubscribe','forum')
3252 $messages = $messages + $defaultmessages;
3254 if (forum_is_forcesubscribed($forum->id)) {
3255 return $messages['forcesubscribed'];
3256 } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) {
3257 return $messages['cantsubscribe'];
3258 } else if ($cantaccessagroup) {
3259 return $messages['cantaccessgroup'];
3260 } else {
3261 if (forum_is_subscribed($USER->id, $forum->id)) {
3262 $linktext = $messages['subscribed'];
3263 $linktitle = get_string('subscribestop', 'forum');
3264 } else {
3265 $linktext = $messages['unsubscribed'];
3266 $linktitle = get_string('subscribestart', 'forum');
3269 $link = '<script type="text/javascript">';
3270 $link .= 'document.getElementById("subscriptionlink").innerHTML = "<a title=\"' . $linktitle . '\" href=\"' . $CFG->wwwroot .
3271 '/mod/forum/subscribe.php?id=' . $forum->id . '\">' . $linktext . '</a>";';
3272 $link .= '</script>';
3273 // use <noscript> to print button in case javascript is not enabled
3274 $link .= '<noscript>';
3275 $link .= print_single_button($CFG->wwwroot . '/mod/forum/subscribe.php?id=' . $forum->id,
3276 '', $linktext, 'post', '_self', true, $linktitle);
3277 $link .= '</noscript>';
3279 return $link;
3286 function forum_user_has_posted_discussion($forumid, $userid) {
3287 if ($discussions = forum_get_discussions($forumid, '', $userid)) {
3288 return true;
3289 } else {
3290 return false;
3297 function forum_discussions_user_has_posted_in($forumid, $userid) {
3298 global $CFG;
3300 $haspostedsql = "SELECT d.id AS id,
3302 FROM {$CFG->prefix}forum_posts p,
3303 {$CFG->prefix}forum_discussions d
3304 WHERE p.discussion = d.id
3305 AND d.forum = $forumid
3306 AND p.userid = $userid";
3308 return get_records_sql($haspostedsql);
3314 function forum_user_has_posted($forumid, $did, $userid) {
3315 return record_exists('forum_posts','discussion',$did,'userid',$userid);
3321 function forum_user_can_post_discussion($forum, $currentgroup=-1, $groupmode=-1, $cm=NULL, $context=NULL) {
3322 // $forum is an object
3323 global $USER, $SESSION;
3325 if (!$cm) {
3326 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3327 error('Course Module ID was incorrect');
3330 if (!$context) {
3331 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3334 if ($currentgroup == -1) {
3335 $currentgroup = get_current_group($cm->course);
3338 if ($groupmode == -1) {
3339 if (!$course = get_record('course', 'id', $cm->course)) {
3340 error('Can not find course');
3342 $groupmode = groups_get_activity_groupmode($cm);
3345 if ($forum->type == 'news') {
3346 $capname = 'mod/forum:addnews';
3347 } else {
3348 $capname = 'mod/forum:startdiscussion';
3351 if (!has_capability($capname, $context)) {
3352 return false;
3355 if ($forum->type == 'eachuser') {
3356 if (forum_user_has_posted_discussion($forum->id, $USER->id)) {
3357 return false;
3361 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
3362 return true;
3365 if ($currentgroup) {
3366 return groups_is_member($currentgroup);
3367 } else {
3368 //else it might be group 0 in visible mode
3369 if ($groupmode == VISIBLEGROUPS){
3370 return true;
3371 } else {
3372 return false;
3378 * This function checks whether the user can reply to posts in a forum
3379 * discussion. Use forum_user_can_post_discussion() to check whether the user
3380 * can start dicussions.
3381 * @param $forum - forum object
3382 * @param $user - user object
3384 function forum_user_can_post($forum, $user=NULL, $cm=NULL, $context=NULL) {
3386 if (!$cm) {
3387 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3388 error('Course Module ID was incorrect');
3391 if (!$context) {
3392 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3395 if ($forum->type == 'news') {
3396 $capname = 'mod/forum:replynews';
3397 } else {
3398 $capname = 'mod/forum:replypost';
3401 if (!empty($user)) {
3402 $canreply = has_capability($capname, $context, $user->id, false)
3403 && !has_capability('moodle/legacy:guest', $context, $user->id, false);
3404 } else {
3405 $canreply = has_capability($capname, $context, NULL, false)
3406 && !has_capability('moodle/legacy:guest', $context, NULL, false);
3409 return $canreply;
3413 //checks to see if a user can view a particular post
3414 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
3416 global $CFG, $USER;
3418 if (!$user){
3419 $user = $USER;
3422 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3423 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
3424 return false;
3427 // If it's a grouped discussion, make sure the user is a member
3428 if ($discussion->groupid > 0) {
3429 $groupmode = groups_get_activity_groupmode($cm);
3430 if ($groupmode == SEPARATEGROUPS) {
3431 return groups_is_member($discussion->groupid) || has_capability('moodle/site:accessallgroups', $modcontext);
3434 return true;
3441 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
3442 global $USER;
3444 if (empty($user) || empty($user->id)) {
3445 $user = $USER;
3448 // retrieve objects (yuk)
3449 if (is_numeric($forum)) {
3450 if (!$forum = get_record('forum','id',$forum)) {
3451 return false;
3454 if (is_numeric($discussion)) {
3455 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3456 return false;
3460 if (!has_capability('mod/forum:viewdiscussion', $context)) {
3461 return false;
3464 if ($forum->type == 'qanda' &&
3465 !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
3466 !has_capability('mod/forum:viewqandawithoutposting', $context)) {
3467 return false;
3469 return true;
3476 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL) {
3477 global $USER;
3479 // retrieve objects (yuk)
3480 if (is_numeric($forum)) {
3481 if (!$forum = get_record('forum','id',$forum)) {
3482 return false;
3486 if (is_numeric($discussion)) {
3487 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3488 return false;
3491 if (is_numeric($post)) {
3492 if (!$post = get_record('forum_posts','id',$post)) {
3493 return false;
3496 if (!isset($post->id) && isset($post->parent)) {
3497 $post->id = $post->parent;
3500 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3501 error('Course Module ID was incorrect');
3503 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3505 if (empty($user) || empty($user->id)) {
3506 $user = $USER;
3509 if (!has_capability('mod/forum:viewdiscussion', $context, $user->id)) {
3510 return false;
3513 if (!groups_course_module_visible($cm, $user->id)) {
3514 return false;
3517 if ($forum->type == 'qanda') {
3518 $firstpost = forum_get_firstpost_from_discussion($discussion->id);
3520 return (forum_user_has_posted($forum->id,$discussion->id,$user->id) ||
3521 $firstpost->id == $post->id ||
3522 has_capability('mod/forum:viewqandawithoutposting', $context, false, $user->id));
3524 return true;
3529 * Prints the discussion view screen for a forum.
3531 * @param object $course The current course object.
3532 * @param object $forum Forum to be printed.
3533 * @param int $maxdiscussions The maximum number of discussions per page(optional).
3534 * @param string $displayformat The display format to use (optional).
3535 * @param string $sort Sort arguments for database query (optional).
3536 * @param int $currentgroup Group to display discussions for (optional).
3537 * @param int $groupmode Group mode of the forum (optional).
3538 * @param int $page Page mode, page to display (optional).
3541 function forum_print_latest_discussions($course, $forum, $maxdiscussions=5, $displayformat='plain', $sort='',
3542 $currentgroup=-1, $groupmode=-1, $page=-1) {
3543 global $CFG, $USER;
3545 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3546 error('Course Module ID was incorrect');
3548 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3551 // Sort out some defaults
3553 if ((!$maxdiscussions) && ($displayformat == 'plain')) {
3554 $displayformat = 'header'; // Abbreviate display by default
3557 $fullpost = false;
3558 if ($displayformat == 'plain') {
3559 $fullpost = true;
3563 // Decide if current user is allowed to see ALL the current discussions or not
3565 // First check the group stuff
3566 $groupmode = groups_get_activity_groupmode($cm);
3567 $currentgroup = groups_get_activity_group($cm);
3569 // If the user can post discussions, then this is a good place to put the
3570 // button for it. We do not show the button if we are showing site news
3571 // and the current user is a guest.
3573 if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context) ||
3574 ($forum->type != 'news' && has_capability('moodle/legacy:guest', $context, NULL, false)) ) {
3576 echo '<div class="singlebutton forumaddnew">';
3577 echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
3578 echo '<div>';
3579 echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
3580 echo '<input type="submit" value="';
3581 echo ($forum->type == 'news') ? get_string('addanewtopic', 'forum')
3582 : (($forum->type == 'qanda')
3583 ? get_string('addanewquestion','forum')
3584 : get_string('addanewdiscussion', 'forum'));
3585 echo '" />';
3586 echo '</div>';
3587 echo '</form>';
3588 echo "</div>\n";
3589 } else if (!isguestuser() and isloggedin() and $forum->type != 'news' and $groupmode == SEPARATEGROUPS and !groups_is_member($currentgroup)) {
3590 notify(get_string('cannotadddiscussion', 'forum'));
3594 // Get all the recent discussions we're allowed to see
3596 $getuserlastmodified = ($displayformat == 'header');
3598 if (! $discussions = forum_get_discussions($forum->id, $sort, 0, $fullpost, $currentgroup,0,$getuserlastmodified) ) {
3599 echo '<div class="forumnodiscuss">';
3600 if ($forum->type == 'news') {
3601 echo '('.get_string('nonews', 'forum').')';
3602 } else if ($forum->type == 'qanda') {
3603 echo '('.get_string('noquestions','forum').')';
3604 } else {
3605 echo '('.get_string('nodiscussions', 'forum').')';
3607 echo "</div>\n";
3608 return;
3611 // If no discussions then don't use paging (to avoid some divide by 0 errors)
3613 if ($maxdiscussions <= 0) {
3614 $page = -1;
3615 $maxdiscussions = 0;
3618 // If we want paging
3620 if ($page != -1) {
3621 ///Get the number of discussions found
3622 $numdiscussions = count($discussions);
3624 ///Show the paging bar
3625 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3627 //Calculate the page "window"
3628 $pagestart = ($page * $maxdiscussions) + 1;
3629 $pageend = $pagestart + $maxdiscussions - 1;
3633 $replies = forum_count_discussion_replies($forum->id);
3635 $canreply = forum_user_can_post($forum);
3636 $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
3638 $discussioncount = 0;
3639 $olddiscussionlink = false;
3640 $strdatestring = get_string('strftimerecentfull');
3642 // Check if the forum is tracked.
3643 if ($cantrack = forum_tp_can_track_forums($forum)) {
3644 $forumtracked = forum_tp_is_tracked($forum);
3645 } else {
3646 $forumtracked = false;
3649 if ($displayformat == 'header') {
3650 echo '<table cellspacing="0" class="forumheaderlist">';
3651 echo '<thead>';
3652 echo '<tr>';
3653 echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
3654 echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
3655 if ($groupmode > 0) {
3656 echo '<th class="header group" scope="col">'.get_string('group').'</th>';
3658 if (has_capability('mod/forum:viewdiscussion', $context)) {
3659 echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
3660 // If the forum can be tracked, display the unread column.
3661 if ($cantrack) {
3662 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
3663 if ($forumtracked) {
3664 echo '&nbsp;<a title="'.get_string('markallread', 'forum').
3665 '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
3666 $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
3667 '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
3669 echo '</th>';
3672 echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
3673 echo '</tr>';
3674 echo '</thead>';
3675 echo '<tbody>';
3678 foreach ($discussions as $discussion) {
3679 $discussioncount++;
3681 if ($page != -1) { // We are using paging
3682 if ($discussioncount < $pagestart) { // Not there yet
3683 continue;
3685 if ($discussioncount > $pageend) { // All done, finish the loop
3686 break;
3688 //Without paging, old approach
3689 } else if ($maxdiscussions && ($discussioncount > $maxdiscussions)) {
3690 $olddiscussionlink = true;
3691 break;
3694 if (!empty($replies[$discussion->discussion])) {
3695 $discussion->replies = $replies[$discussion->discussion]->replies;
3696 $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
3697 } else {
3698 $discussion->replies = 0;
3701 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
3702 // All posts are read in this case.
3703 if (!$forumtracked) {
3704 $discussion->unread = '-';
3705 } else if (empty($USER)) {
3706 $discussion->unread = 0;
3707 } else {
3708 $discussion->unread = forum_tp_count_discussion_unread_posts($USER->id, $discussion->discussion);
3711 if (!empty($USER->id)) {
3712 $ownpost = ($discussion->userid == $USER->id);
3713 } else {
3714 $ownpost=false;
3716 // Use discussion name instead of subject of first post
3717 $discussion->subject = $discussion->name;
3719 switch ($displayformat) {
3720 case 'header':
3721 if ($groupmode > 0) {
3722 if (isset($groups[$discussion->groupid])) {
3723 $group = $groups[$discussion->groupid];
3724 } else {
3725 $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid); //TODO:
3727 } else {
3728 $group = -1;
3730 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
3731 $canviewparticipants);
3732 break;
3733 default:
3734 if ($canreply or $discussion->replies) {
3735 $link = true;
3736 } else {
3737 $link = false;
3740 $discussion->forum = $forum->id;
3742 forum_print_post($discussion, $course->id, $ownpost, $reply=0, $link, $assessed=false);
3743 break;
3747 if ($displayformat == "header") {
3748 echo '</tbody>';
3749 echo '</table>';
3752 if ($olddiscussionlink) {
3753 echo '<div class="forumolddiscuss">';
3754 echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
3755 echo get_string('olderdiscussions', 'forum').'</a> ...</div>';
3758 if ($page != -1) { ///Show the paging bar
3759 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3767 function forum_print_discussion($course, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
3769 global $USER, $CFG;
3771 if (!empty($USER->id)) {
3772 $ownpost = ($USER->id == $post->userid);
3773 } else {
3774 $ownpost = false;
3776 if ($canreply === NULL) {
3777 $reply = forum_user_can_post($forum);
3778 } else {
3779 $reply = $canreply;
3782 $ratings = NULL;
3783 $ratingsmenuused = false;
3784 $ratingsformused = false;
3785 if ($forum->assessed and !empty($USER->id)) {
3786 if ($ratings->scale = make_grades_menu($forum->scale)) {
3787 $ratings->assesstimestart = $forum->assesstimestart;
3788 $ratings->assesstimefinish = $forum->assesstimefinish;
3789 $ratings->allow = $canrate;
3791 if ($ratings->allow) {
3792 echo '<form id="form" method="post" action="rate.php">';
3793 echo '<div class="ratingform">';
3794 echo '<input type="hidden" name="forumid" value="'.$forum->id.'" />';
3795 $ratingsformused = true;
3800 $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post
3801 $post->forumtype = $forum->type;
3803 $post->subject = format_string($post->subject);
3805 if (forum_tp_can_track_forums($forum)) {
3806 if ($forumtracked = forum_tp_is_tracked($forum)) {
3807 $user_read_array = forum_tp_get_discussion_read_records($USER->id, $post->discussion);
3808 } else {
3809 $user_read_array = array();
3811 } else {
3812 $forumtracked = false;
3813 $user_read_array = array();
3816 if (forum_print_post($post, $course->id, $ownpost, $reply, $link=false, $ratings,
3817 '', '', (!$forumtracked || isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3818 $ratingsmenuused = true;
3821 switch ($mode) {
3822 case FORUM_MODE_FLATOLDEST :
3823 case FORUM_MODE_FLATNEWEST :
3824 default:
3825 if (forum_print_posts_flat($post->discussion, $course->id, $mode, $ratings, $reply,
3826 $user_read_array, $post->forum)) {
3827 $ratingsmenuused = true;
3829 break;
3831 case FORUM_MODE_THREADED :
3832 if (forum_print_posts_threaded($post->id, $course->id, 0, $ratings, $reply,
3833 $user_read_array, $post->forum)) {
3834 $ratingsmenuused = true;
3836 break;
3838 case FORUM_MODE_NESTED :
3839 if (forum_print_posts_nested($post->id, $course->id, $ratings, $reply,
3840 $user_read_array, $post->forum)) {
3841 $ratingsmenuused = true;
3843 break;
3846 if ($ratingsformused) {
3847 if ($ratingsmenuused) {
3848 echo '<div class="ratingsubmit">';
3849 echo '<input type="submit" value="'.get_string('sendinratings', 'forum').'" />';
3850 if ($forum->scale < 0) {
3851 if ($scale = get_record("scale", "id", abs($forum->scale))) {
3852 print_scale_menu_helpbutton($course->id, $scale );
3855 echo '</div>';
3858 echo '</div>';
3859 echo '</form>';
3867 function forum_print_posts_flat($discussion, $courseid, $direction, $ratings, $reply, &$user_read_array, $forumid=0) {
3868 global $USER, $CFG;
3870 $link = false;
3871 $ratingsmenuused = false;
3873 if ($direction < 0) {
3874 $sort = "ORDER BY created DESC";
3875 } else {
3876 $sort = "ORDER BY created ASC";
3879 if ($posts = forum_get_discussion_posts($discussion, $sort, $forumid)) {
3880 foreach ($posts as $post) {
3882 $post->subject = format_string($post->subject);
3884 $ownpost = ($USER->id == $post->userid);
3885 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3886 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3887 $ratingsmenuused = true;
3892 return $ratingsmenuused;
3897 * TODO document
3899 function forum_print_posts_threaded($parent, $courseid, $depth, $ratings, $reply, &$user_read_array, $forumid=0) {
3900 global $USER, $CFG;
3902 $link = false;
3903 $ratingsmenuused = false;
3905 $istracking = forum_tp_can_track_forums($forumid) && forum_tp_is_tracked($forumid);
3907 if ($posts = forum_get_child_posts($parent, $forumid)) {
3909 if (!$cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
3910 error('Course Module ID was incorrect');
3912 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3913 $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
3915 foreach ($posts as $post) {
3917 echo '<div class="indent">';
3918 if ($depth > 0) {
3919 $ownpost = ($USER->id == $post->userid);
3921 $post->subject = format_string($post->subject);
3923 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3924 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3925 $ratingsmenuused = true;
3927 } else {
3928 if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
3929 continue;
3931 $by->name = fullname($post, $canviewfullnames);
3932 $by->date = userdate($post->modified);
3934 if ($istracking) {
3935 if (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)) {
3936 $style = '<span class="forumthread read">';
3937 } else {
3938 $style = '<span class="forumthread unread">';
3940 } else {
3941 $style = '<span class="forumthread">';
3943 echo $style."<a name=\"$post->id\"></a>".
3944 "<a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a> ";
3945 print_string("bynameondate", "forum", $by);
3946 echo "</span>";
3949 if (forum_print_posts_threaded($post->id, $courseid, $depth-1, $ratings, $reply,
3950 $user_read_array, $forumid)) {
3951 $ratingsmenuused = true;
3953 echo "</div>\n";
3956 return $ratingsmenuused;
3962 function forum_print_posts_nested($parent, $courseid, $ratings, $reply, &$user_read_array, $forumid=0) {
3963 global $USER, $CFG;
3965 $link = false;
3966 $ratingsmenuused = false;
3968 if ($posts = forum_get_child_posts($parent, $forumid)) {
3969 foreach ($posts as $post) {
3971 echo '<div class="indent">';
3972 if (empty($USER->id)) {
3973 $ownpost = false;
3974 } else {
3975 $ownpost = ($USER->id == $post->userid);
3978 $post->subject = format_string($post->subject);
3980 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3981 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3982 $ratingsmenuused = true;
3984 if (forum_print_posts_nested($post->id, $courseid, $ratings, $reply, $user_read_array, $forumid)) {
3985 $ratingsmenuused = true;
3987 echo "</div>\n";
3990 return $ratingsmenuused;
3994 * TODO document
3996 function forum_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="0", $user="", $groupid="") {
3997 // Returns all forum posts since a given time. If forum is specified then
3998 // this restricts the results
4000 global $CFG;
4002 if ($cmid) {
4003 $forumselect = " AND cm.id = '$cmid'";
4004 } else {
4005 $forumselect = "";
4008 if ($user) {
4009 $userselect = " AND u.id = '$user'";
4010 } else {
4011 $userselect = "";
4014 $posts = get_records_sql("SELECT p.*, d.name, u.firstname, u.lastname,
4015 u.picture, d.groupid, cm.instance, f.name,
4016 cm.section, cm.id AS cmid
4017 FROM {$CFG->prefix}forum_posts p,
4018 {$CFG->prefix}forum_discussions d,
4019 {$CFG->prefix}user u,
4020 {$CFG->prefix}course_modules cm,
4021 {$CFG->prefix}forum f
4022 WHERE p.modified > '$sincetime' $forumselect
4023 AND p.userid = u.id $userselect
4024 AND d.course = '$courseid'
4025 AND p.discussion = d.id
4026 AND cm.instance = f.id
4027 AND cm.course = d.course
4028 AND cm.course = f.course
4029 AND f.id = d.forum
4030 ORDER BY p.discussion ASC,p.created ASC");
4032 if (empty($posts)) {
4033 return;
4036 foreach ($posts as $post) {
4038 $modcontext = get_context_instance(CONTEXT_MODULE, $post->cmid);
4039 $canviewallgroups = has_capability('moodle/site:accessallgroups', $modcontext);
4041 if ($groupid and ($post->groupid != -1 and $groupid != $post->groupid and !$canviewallgroups)) {
4042 continue;
4044 if (!groups_course_module_visible($post->cmid)) {
4045 continue;
4048 $tmpactivity = new Object;
4050 $tmpactivity->type = "forum";
4051 $tmpactivity->defaultindex = $index;
4052 $tmpactivity->instance = $post->instance;
4053 $tmpactivity->name = $post->name;
4054 $tmpactivity->section = $post->section;
4056 $tmpactivity->content->id = $post->id;
4057 $tmpactivity->content->discussion = $post->discussion;
4058 $tmpactivity->content->subject = $post->subject;
4059 $tmpactivity->content->parent = $post->parent;
4061 $tmpactivity->user->userid = $post->userid;
4062 $tmpactivity->user->fullname = fullname($post);
4063 $tmpactivity->user->picture = $post->picture;
4065 $tmpactivity->timestamp = $post->modified;
4066 $activities[] = $tmpactivity;
4068 $index++;
4071 return;
4077 function forum_print_recent_mod_activity($activity, $course, $detail=false) {
4079 global $CFG;
4081 echo '<table border="0" cellpadding="3" cellspacing="0">';
4083 if ($activity->content->parent) {
4084 $openformat = "<font size=\"2\"><i>";
4085 $closeformat = "</i></font>";
4086 } else {
4087 $openformat = "<b>";
4088 $closeformat = "</b>";
4091 echo "<tr><td class=\"forumpostpicture\" width=\"35\" valign=\"top\">";
4092 print_user_picture($activity->user->userid, $course, $activity->user->picture);
4093 echo "</td><td>$openformat";
4095 if ($detail) {
4096 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
4097 "class=\"icon\" alt=\"".strip_tags(format_string($activity->name,true))."\" /> ";
4099 echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d=" . $activity->content->discussion
4100 . "#p" . $activity->content->id . "\">";
4102 echo format_string($activity->content->subject,true);
4103 echo "</a>$closeformat";
4105 echo "<br /><font size=\"2\">";
4106 echo "<a href=\"$CFG->wwwroot/user/view.php?id=" . $activity->user->userid . "&amp;course=" . "$course\">"
4107 . $activity->user->fullname . "</a>";
4108 echo " - " . userdate($activity->timestamp) . "</font></td></tr>";
4109 echo "</table>";
4111 return;
4115 * recursively sets the discussion field to $discussionid on $postid and all its children
4116 * used when pruning a post
4118 function forum_change_discussionid($postid, $discussionid) {
4119 set_field('forum_posts', 'discussion', $discussionid, 'id', $postid);
4120 if ($posts = get_records('forum_posts', 'parent', $postid)) {
4121 foreach ($posts as $post) {
4122 forum_change_discussionid($post->id, $discussionid);
4125 return true;
4129 * Prints the editing button on subscribers page
4131 function forum_update_subscriptions_button($courseid, $forumid) {
4132 global $CFG, $USER;
4134 if (!empty($USER->subscriptionsediting)) {
4135 $string = get_string('turneditingoff');
4136 $edit = "off";
4137 } else {
4138 $string = get_string('turneditingon');
4139 $edit = "on";
4142 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
4143 "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
4144 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4145 "<input type=\"submit\" value=\"$string\" /></form>";
4149 * This function gets run whenever a role is assigned to a user in a context
4151 * @param integer $userid
4152 * @param object $context
4153 * @return bool
4155 function forum_role_assign($userid, $context, $roleid) {
4156 // check to see if this role comes with mod/forum:initialsubscriptions
4157 $cap = role_context_capabilities($roleid, $context, 'mod/forum:initialsubscriptions');
4158 $cap1 = role_context_capabilities($roleid, $context, 'moodle/course:view');
4159 // we are checking the role because has_capability() will pull this capability out
4160 // from other roles this user might have and resolve them, which is no good
4161 // the role needs course view to
4162 if (isset($cap['mod/forum:initialsubscriptions']) && $cap['mod/forum:initialsubscriptions'] == CAP_ALLOW &&
4163 isset($cap1['moodle/course:view']) && $cap1['moodle/course:view'] == CAP_ALLOW) {
4164 return forum_add_user_default_subscriptions($userid, $context);
4165 } else {
4166 // MDL-8981, do not subscribe to forum
4167 return true;
4173 * This function gets run whenever a role is assigned to a user in a context
4175 * @param integer $userid
4176 * @param object $context
4177 * @return bool
4179 function forum_role_unassign($userid, $context) {
4180 return forum_remove_user_subscriptions($userid, $context);
4185 * Add subscriptions for new users
4187 function forum_add_user_default_subscriptions($userid, $context) {
4189 if (empty($context->contextlevel)) {
4190 return false;
4193 switch ($context->contextlevel) {
4195 case CONTEXT_SYSTEM: // For the whole site
4196 if ($courses = get_records('course')) {
4197 foreach ($courses as $course) {
4198 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4199 forum_add_user_default_subscriptions($userid, $subcontext);
4202 break;
4204 case CONTEXT_COURSECAT: // For a whole category
4205 if ($courses = get_records('course', 'category', $context->instanceid)) {
4206 foreach ($courses as $course) {
4207 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4208 forum_add_user_default_subscriptions($userid, $subcontext);
4211 if ($categories = get_records('course_categories', 'parent', $context->instanceid)) {
4212 foreach ($categories as $category) {
4213 $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
4214 forum_add_user_default_subscriptions($userid, $subcontext);
4217 break;
4220 case CONTEXT_COURSE: // For a whole course
4221 if ($course = get_record('course', 'id', $context->instanceid)) {
4222 if ($forums = get_all_instances_in_course('forum', $course, $userid, false)) {
4223 foreach ($forums as $forum) {
4224 if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
4225 continue;
4227 if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
4228 if (has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4229 forum_subscribe($userid, $forum->id);
4235 break;
4237 case CONTEXT_MODULE: // Just one forum
4238 if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
4239 if ($forum = get_record('forum', 'id', $cm->instance)) {
4240 if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
4241 continue;
4243 if (has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4244 forum_subscribe($userid, $forum->id);
4248 break;
4251 return true;
4256 * Remove subscriptions for a user in a context
4258 function forum_remove_user_subscriptions($userid, $context) {
4260 global $CFG;
4262 if (empty($context->contextlevel)) {
4263 return false;
4266 switch ($context->contextlevel) {
4268 case CONTEXT_SYSTEM: // For the whole site
4269 //if ($courses = get_my_courses($userid)) {
4270 // find all courses in which this user has a forum subscription
4271 if ($courses = get_records_sql("SELECT c.*
4272 FROM {$CFG->prefix}course c,
4273 {$CFG->prefix}forum_subscriptions fs,
4274 {$CFG->prefix}forum f
4275 WHERE c.id = f.course
4276 AND f.id = fs.forum
4277 AND fs.userid = $userid")) {
4279 foreach ($courses as $course) {
4280 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4281 forum_remove_user_subscriptions($userid, $subcontext);
4284 break;
4286 case CONTEXT_COURSECAT: // For a whole category
4287 if ($courses = get_records('course', 'category', $context->instanceid)) {
4288 foreach ($courses as $course) {
4289 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4290 forum_remove_user_subscriptions($userid, $subcontext);
4293 if ($categories = get_records('course_categories', 'parent', $context->instanceid)) {
4294 foreach ($categories as $category) {
4295 $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
4296 forum_remove_user_subscriptions($userid, $subcontext);
4299 break;
4301 case CONTEXT_COURSE: // For a whole course
4302 if ($course = get_record('course', 'id', $context->instanceid)) {
4303 // find all forums in which this user has a subscription, and its coursemodule id
4304 if ($forums = get_records_sql("SELECT f.id, cm.id as coursemodule
4305 FROM {$CFG->prefix}forum f,
4306 {$CFG->prefix}modules m,
4307 {$CFG->prefix}course_modules cm,
4308 {$CFG->prefix}forum_subscriptions fs
4309 WHERE fs.userid = $userid
4310 AND fs.forum = f.id
4311 AND f.course = $context->instanceid
4312 AND cm.instance = f.id
4313 AND cm.module = m.id
4314 AND m.name = 'forum'")) {
4316 //if ($forums = get_all_instances_in_course('forum', $course, $userid, true)) {
4317 foreach ($forums as $forum) {
4318 if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
4319 if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4320 forum_unsubscribe($userid, $forum->id);
4326 break;
4328 case CONTEXT_MODULE: // Just one forum
4329 if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
4330 if ($forum = get_record('forum', 'id', $cm->instance)) {
4331 if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4332 forum_unsubscribe($userid, $forum->id);
4336 break;
4339 return true;
4342 // Functions to do with read tracking.
4346 function forum_tp_add_read_record($userid, $postid, $discussionid=-1, $forumid=-1) {
4347 if (($readrecord = forum_tp_get_read_records($userid, $postid)) === false) {
4348 // New read record
4349 unset($readrecord);
4350 $readrecord->userid = $userid;
4351 $readrecord->postid = $postid;
4352 $readrecord->discussionid = $discussionid;
4353 $readrecord->forumid = $forumid;
4354 $readrecord->firstread = time();
4355 $readrecord->lastread = $readrecord->firstread;
4356 return insert_record('forum_read', $readrecord, true);
4358 } else {
4359 // Update read record
4360 $readrecord = reset($readrecord);
4361 $readrecord->lastread = time();
4363 $update = NULL;
4364 $update->id = $readrecord->id;
4365 $update->lastread = $readrecord->lastread;
4367 // This shouldn't happen, but just in case...
4368 if (!$readrecord->firstread) {
4369 // Update the 'firstread' field.
4370 $update->firstread = $readrecord->lastread;
4372 if ($discussionid > -1) {
4373 // Update the 'discussionid' field.
4374 $update->discussionid = $discussionid;
4376 if ($forumid > -1) {
4377 // Update the 'forumid' field.
4378 $update->forumid = $forumid;
4381 return update_record('forum_read', $update);
4386 * Returns all records in the 'forum_read' table matching the passed keys, indexed
4387 * by userid.
4389 function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4390 $select = '';
4391 if ($userid > -1) {
4392 if ($select != '') $select .= ' AND ';
4393 $select .= 'userid = \''.$userid.'\'';
4395 if ($postid > -1) {
4396 if ($select != '') $select .= ' AND ';
4397 $select .= 'postid = \''.$postid.'\'';
4399 if ($discussionid > -1) {
4400 if ($select != '') $select .= ' AND ';
4401 $select .= 'discussionid = \''.$discussionid.'\'';
4403 if ($forumid > -1) {
4404 if ($select != '') $select .= ' AND ';
4405 $select .= 'forumid = \''.$forumid.'\'';
4408 return get_records_select('forum_read', $select);
4412 * Returns all read records for the provided user and discussion, indexed by postid.
4414 function forum_tp_get_discussion_read_records($userid, $discussionid) {
4415 $select = 'userid = \''.$userid.'\' AND discussionid = \''.$discussionid.'\'';
4416 $fields = 'postid, firstread, lastread';
4417 return get_records_select('forum_read', $select, '', $fields);
4421 * If its an old post, do nothing. If the record exists, the maintenance will clear it up later.
4423 function forum_tp_mark_post_read($userid, &$post, $forumid) {
4424 if (!forum_tp_is_post_old($post)) {
4425 return forum_tp_add_read_record($userid, $post->id, $post->discussion, $forumid);
4426 } else {
4427 return true;
4432 * Marks a whole forum as read, for a given user
4434 function forum_tp_mark_forum_read($userid, $forumid, $groupid=false) {
4435 global $CFG;
4437 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4439 $groupsel = '';
4440 if ($groupid !== false) {
4441 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4444 $sql = 'SELECT p.id as postid, d.id as discussionid, d.forum as forumid '.
4445 'FROM '.$CFG->prefix.'forum_posts p '.
4446 'LEFT JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
4447 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4448 'WHERE d.forum = '.$forumid.$groupsel.
4449 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4451 if ($posts = get_records_sql($sql)) {
4452 foreach ($posts as $post) {
4453 forum_tp_add_read_record($userid, $post->postid, $post->discussionid, $post->forumid);
4455 return true;
4460 * Marks a whole discussion as read, for a given user
4462 function forum_tp_mark_discussion_read($userid, $discussionid, $forumid) {
4463 global $CFG;
4465 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4467 $sql = 'SELECT p.id as postid, p.discussion as discussionid '.
4468 'FROM '.$CFG->prefix.'forum_posts p '.
4469 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4470 'WHERE p.discussion = '.$discussionid.' '.
4471 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4473 if ($posts = get_records_sql($sql)) {
4474 foreach ($posts as $post) {
4475 forum_tp_add_read_record($userid, $post->postid, $post->discussionid, $forumid);
4477 return true;
4484 function forum_tp_is_post_read($userid, &$post) {
4485 return (forum_tp_is_post_old($post) ||
4486 (get_record('forum_read', 'userid', $userid, 'postid', $post->id) !== false));
4492 function forum_tp_is_post_old(&$post, $time=null) {
4493 global $CFG;
4495 if (is_null($time)) $time = time();
4496 return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600)));
4500 * Returns the count of records for the provided user and discussion.
4502 function forum_tp_count_discussion_read_records($userid, $discussionid) {
4503 global $CFG;
4505 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4507 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4508 'FROM '.$CFG->prefix.'forum_discussions d '.
4509 'LEFT JOIN '.$CFG->prefix.'forum_read r ON d.id = r.discussionid AND r.userid = '.$userid.' '.
4510 'LEFT JOIN '.$CFG->prefix.'forum_posts p ON p.discussion = d.id '.
4511 'AND (p.modified < '.$cutoffdate.' OR p.id = r.postid) '.
4512 'WHERE d.id = '.$discussionid;
4514 return (count_records_sql($sql));
4518 * Returns the count of records for the provided user and discussion.
4520 function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
4521 global $CFG;
4523 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4525 $sql = 'SELECT COUNT(p.id) '.
4526 'FROM '.$CFG->prefix.'forum_posts p '.
4527 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4528 'WHERE p.discussion = '.$discussionid.' '.
4529 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4531 return (count_records_sql($sql));
4535 * Returns the count of posts for the provided forum and [optionally] group.
4537 function forum_tp_count_forum_posts($forumid, $groupid=false) {
4538 global $CFG;
4540 $sql = 'SELECT COUNT(*) '.
4541 'FROM '.$CFG->prefix.'forum_posts fp,'.$CFG->prefix.'forum_discussions fd '.
4542 'WHERE fd.forum = '.$forumid.' AND fp.discussion = fd.id';
4543 if ($groupid !== false) {
4544 $sql .= ' AND (fd.groupid = '.$groupid.' OR fd.groupid = -1)';
4546 $count = count_records_sql($sql);
4549 return $count;
4553 * Returns the count of records for the provided user and forum and [optionally] group.
4555 function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
4556 global $CFG;
4558 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4560 $groupsel = '';
4561 if ($groupid !== false) {
4562 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4565 if ($CFG->dbfamily === 'postgres' || $CFG->dbfamily === 'mssql' || $CFG->dbfamily === 'oracle') {
4566 // this query takes 20ms, vs several minutes for the one below
4567 $sql = " SELECT COUNT (DISTINCT u.id ) "
4568 . " FROM ( "
4569 . " SELECT p.id "
4570 . " FROM {$CFG->prefix}forum_posts p "
4571 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4572 . " JOIN {$CFG->prefix}forum_read r ON p.id = r.postid"
4573 . " WHERE d.forum = $forumid $groupsel "
4574 . " AND r.userid= $userid"
4575 . " UNION"
4576 . " SELECT p.id"
4577 . " FROM {$CFG->prefix}forum_posts p "
4578 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4579 . " WHERE d.forum = $forumid $groupsel "
4580 . " AND p.modified < $cutoffdate"
4581 . ") u";
4582 } else { // This is for MySQL. TODO: Check if the above works for MySQL 4.1
4583 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4584 'FROM '.$CFG->prefix.'forum_posts p,'.$CFG->prefix.'forum_read r,'.$CFG->prefix.'forum_discussions d '.
4585 'WHERE d.forum = '.$forumid.$groupsel.' AND p.discussion = d.id AND '.
4586 '((p.id = r.postid AND r.userid = '.$userid.') OR p.modified < '.$cutoffdate.' ) ';
4588 return (count_records_sql($sql));
4592 * Returns the count of records for the provided user and forum and [optionally] group.
4594 function forum_tp_count_forum_unread_posts($userid, $forumid, $groupid=false) {
4595 global $CFG;
4597 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4599 $groupsel = '';
4600 if ($groupid !== false) {
4601 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4604 $sql = 'SELECT COUNT(p.id) '.
4605 'FROM '.$CFG->prefix.'forum_posts p '.
4606 'LEFT JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
4607 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4608 'WHERE d.forum = '.$forumid.$groupsel.
4609 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4611 return (count_records_sql($sql));
4615 * Deletes read records for the specified index. At least one parameter must be specified.
4617 function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4618 $select = '';
4619 if ($userid > -1) {
4620 if ($select != '') $select .= ' AND ';
4621 $select .= 'userid = \''.$userid.'\'';
4623 if ($postid > -1) {
4624 if ($select != '') $select .= ' AND ';
4625 $select .= 'postid = \''.$postid.'\'';
4627 if ($discussionid > -1) {
4628 if ($select != '') $select .= ' AND ';
4629 $select .= 'discussionid = \''.$discussionid.'\'';
4631 if ($forumid > -1) {
4632 if ($select != '') $select .= ' AND ';
4633 $select .= 'forumid = \''.$forumid.'\'';
4635 if ($select == '') {
4636 return false;
4638 else {
4639 return delete_records_select('forum_read', $select);
4643 * Get a list of forums not tracked by the user.
4645 * @param int $userid The id of the user to use.
4646 * @param int $courseid The id of the course being checked (optional).
4647 * @return mixed An array indexed by forum id, or false.
4649 function forum_tp_get_untracked_forums($userid, $courseid=false) {
4650 global $CFG;
4652 // If a course is specified, get the forums with tracking turned off.
4653 if ($courseid !== false) {
4654 $select = 'course = '.$courseid.' AND trackingtype = '.FORUM_TRACKING_OFF;
4655 $forced = get_records_select('forum', $select, '', 'id,course');
4656 } else {
4657 $forced = false;
4660 // Get the forums that the user has turned off.
4661 $sql = 'SELECT ft.forumid, ft.userid '.
4662 'FROM '.$CFG->prefix.'forum_track_prefs ft, '.$CFG->prefix.'forum f '.
4663 'WHERE ft.userid = '.$userid.' AND f.id = ft.forumid ' .
4664 'AND f.trackingtype != '.FORUM_TRACKING_ON;
4665 $useroff = get_records_sql($sql);
4666 if (!$forced) {
4667 return $useroff;
4668 } else if (!$useroff) {
4669 return $forced;
4670 } else {
4671 return ($useroff + $forced);
4676 * Determine if a user can track forums and optionally a particular forum.
4677 * Checks the site settings, the user settings and the forum settings (if
4678 * requested).
4680 * @param mixed $forum The forum object to test, or the int id (optional).
4681 * @param mixed $userid The user object to check for (optional).
4682 * @return boolean
4684 function forum_tp_can_track_forums($forum=false, $user=false) {
4685 global $USER, $CFG;
4687 // if possible, avoid expensive
4688 // queries
4689 if (empty($CFG->forum_trackreadposts)) {
4690 return false;
4693 if ($user === false) {
4694 // Must be logged in and not a guest.
4695 $isauser = isloggedin() && !isguest();
4696 $user = $USER;
4697 } else {
4698 $isauser = true;
4701 if ($forum === false) {
4702 $forumallows = true;
4703 $forumforced = false;
4704 } else {
4705 // Work toward always passing an object...
4706 if (is_numeric($forum)) {
4707 $forum = get_record('forum', 'id', $forum, '','','','', 'id,trackingtype');
4710 $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
4711 $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
4714 return ($isauser && ($forumforced || ($forumallows && !empty($user->trackforums))));
4718 * Tells whether a specific forum is tracked by the user. A user can optionally
4719 * be specified. If not specified, the current user is assumed.
4721 * @param mixed $forum If int, the id of the forum being checked; if object, the forum object
4722 * @param int $userid The id of the user being checked (optional).
4723 * @return boolean
4725 function forum_tp_is_tracked($forum, $userid=false) {
4726 global $USER, $CFG;
4728 if ($userid === false) {
4729 if (empty($USER->id)) {
4730 return false;
4732 $userid = $USER->id;
4735 // Work toward always passing an object...
4736 if (is_numeric($forum)) {
4737 $forum = get_record('forum', 'id', $forum);
4740 return (($forum->trackingtype == FORUM_TRACKING_ON) ||
4741 ($forum->trackingtype == FORUM_TRACKING_OPTIONAL &&
4742 get_record('forum_track_prefs', 'userid', $userid, 'forumid', $forum->id) === false));
4748 function forum_tp_start_tracking($forumid, $userid=false) {
4749 global $USER;
4751 if ($userid === false) {
4752 $userid = $USER->id;
4755 return delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forumid);
4761 function forum_tp_stop_tracking($forumid, $userid=false) {
4762 global $USER;
4764 if ($userid === false) {
4765 $userid = $USER->id;
4768 $track_prefs = new stdClass;
4769 $track_prefs->userid = $userid;
4770 $track_prefs->forumid = $forumid;
4771 if (insert_record('forum_track_prefs', $track_prefs)) {
4772 return forum_tp_delete_read_records($userid, -1, -1, $forumid);
4773 } else {
4774 return false;
4779 /**
4780 * Clean old records from the forum_read table.
4782 function forum_tp_clean_read_records() {
4783 global $CFG;
4785 // Look for records older than the cutoffdate that are still in the forum_read table.
4786 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4787 $sql = 'SELECT fr.id, fr.userid, fr.postid '.
4788 'FROM '.$CFG->prefix.'forum_posts fp, '.$CFG->prefix.'forum_read fr '.
4789 'WHERE fp.modified < '.$cutoffdate.' AND fp.id = fr.postid';
4790 if (($oldreadposts = get_records_sql($sql))) {
4791 foreach($oldreadposts as $oldreadpost) {
4792 delete_records('forum_read', 'id', $oldreadpost->id);
4798 * Sets the last post for a given discussion
4800 function forum_discussion_update_last_post($discussionid) {
4801 global $CFG, $db;
4803 // Check the given discussion exists
4804 if (!record_exists('forum_discussions', 'id', $discussionid)) {
4805 return false;
4808 // Use SQL to find the last post for this discussion
4809 $sql = 'SELECT id, userid, modified '.
4810 'FROM '.$CFG->prefix.'forum_posts '.
4811 'WHERE discussion='.$discussionid.' '.
4812 'ORDER BY modified DESC ';
4814 // Lets go find the last post
4815 if (($lastpost = get_record_sql($sql, true))) {
4816 $discussionobject = new Object;
4817 $discussionobject->id = $discussionid;
4818 $discussionobject->usermodified = $lastpost->userid;
4819 $discussionobject->timemodified = $lastpost->modified;
4820 if (update_record('forum_discussions', $discussionobject)) {
4821 return $lastpost->id;
4825 // To get here either we couldn't find a post for the discussion (weird)
4826 // or we couldn't update the discussion record (weird x2)
4827 return false;
4834 function forum_get_view_actions() {
4835 return array('view discussion','search','forum','forums','subscribers');
4841 function forum_get_post_actions() {
4842 return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
4846 * this function returns all the separate forum ids, given a courseid
4847 * @param int $courseid
4848 * @return array
4850 function forum_get_separate_modules($courseid) {
4852 global $CFG,$db;
4853 $forummodule = get_record("modules", "name", "forum");
4855 $sql = 'SELECT f.id, f.id FROM '.$CFG->prefix.'forum f, '.$CFG->prefix.'course_modules cm WHERE
4856 f.id = cm.instance AND cm.module ='.$forummodule->id.' AND cm.visible = 1 AND cm.course = '.$courseid.'
4857 AND cm.groupmode ='.SEPARATEGROUPS;
4859 return get_records_sql($sql);
4866 function forum_check_throttling($forum) {
4867 global $USER, $CFG;
4869 if (is_numeric($forum)) {
4870 $forum = get_record('forum','id',$forum);
4872 if (!is_object($forum)) {
4873 return false; // this is broken.
4876 if (empty($forum->blockafter)) {
4877 return true;
4880 if (empty($forum->blockperiod)) {
4881 return true;
4884 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
4885 error('Course Module ID was incorrect');
4887 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
4888 if(!has_capability('mod/forum:throttlingapplies', $modcontext)) {
4889 return true;
4892 // get the number of posts in the last period we care about
4893 $timenow = time();
4894 $timeafter = $timenow - $forum->blockperiod;
4896 $numposts = count_records_sql('SELECT COUNT(p.id) FROM '.$CFG->prefix.'forum_posts p'
4897 .' JOIN '.$CFG->prefix.'forum_discussions d'
4898 .' ON p.discussion = d.id WHERE d.forum = '.$forum->id
4899 .' AND p.userid = '.$USER->id.' AND p.created > '.$timeafter);
4901 $a->blockafter = $forum->blockafter;
4902 $a->numposts = $numposts;
4903 $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
4905 if ($forum->blockafter <= $numposts) {
4906 error(get_string('forumblockingtoomanyposts','error',$a),$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id);
4908 if ($forum->warnafter <= $numposts) {
4909 notify(get_string('forumblockingalmosttoomanyposts','forum',$a));
4917 * This function is used by the remove_course_userdata function in moodlelib.
4918 * If this function exists, remove_course_userdata will execute it.
4919 * This function will remove all posts from the specified forum.
4921 function forum_delete_userdata($data, $showfeedback=true) {
4922 global $CFG;
4924 $sql = "DELETE FROM {$CFG->prefix}forum_posts
4925 WHERE discussion IN (
4926 SELECT fd.id FROM {$CFG->prefix}forum_discussions fd, {$CFG->prefix}forum f
4927 WHERE f.course={$data->courseid} AND f.id=fd.forum "; // closing ) added bellow
4929 $strreset = get_string('reset');
4931 if (!empty($data->reset_forum_news)) {
4932 $select = "$sql AND f.type = 'news' )";
4933 if (execute_sql($select, false) and $showfeedback) {
4934 notify($strreset.': '.get_string('namenews','forum'), 'notifysuccess');
4937 if (!empty($data->reset_forum_single)) {
4938 $select = "$sql AND f.type = 'single' ) AND parent <> 0";
4939 if (execute_sql($select, false) and $showfeedback) {
4940 notify($strreset.': '.get_string('singleforum','forum'), 'notifysuccess');
4943 if (!empty($data->reset_forum_eachuser)) {
4944 $select = "$sql AND f.type = 'eachuser' )";
4945 if (execute_sql($select, false) and $showfeedback) {
4946 notify($strreset.': '.get_string('eachuserforum','forum'), 'notifysuccess');
4949 if (!empty($data->reset_forum_general)) {
4950 $select = "$sql AND f.type = 'general' )";
4951 if (execute_sql($select, false) and $showfeedback) {
4952 notify($strreset.': '.get_string('generalforum','forum'), 'notifysuccess');
4955 if (!empty($data->reset_forum_subscriptions)) {
4956 $subscripsql = "DELETE FROM {$CFG->prefix}forum_subscriptions
4957 WHERE forum IN (
4958 SELECT id FROM {$CFG->prefix}forum
4959 WHERE course = {$data->courseid} )";
4961 if (execute_sql($subscripsql, false) and $showfeedback) {
4962 notify($strreset.': '.get_string('resetsubscriptions','forum'), 'notifysuccess');
4969 * Called by course/reset.php
4971 function forum_reset_course_form($course) {
4972 echo get_string('resetforums', 'forum'); echo ':<br />';
4973 print_checkbox('reset_forum_news', 1, true, get_string('namenews','forum'), '', ''); echo '<br />';
4974 print_checkbox('reset_forum_single', 1, true, get_string('singleforum','forum'), '', ''); echo '<br />';
4975 print_checkbox('reset_forum_eachuser', 1, true, get_string('eachuserforum','forum'), '', ''); echo '<br />';
4976 print_checkbox('reset_forum_general', 1, true, get_string('generalforum','forum'), '', ''); echo '<br />';
4977 echo '<p>';
4978 print_checkbox('reset_forum_subscriptions', 1, true, get_string('resetsubscriptions','forum'), '', '');
4979 echo '</p>';
4984 * Converts a forum to use the Roles System
4985 * @param $forum - a forum object with the same attributes as a record
4986 * from the forum database table
4987 * @param $forummodid - the id of the forum module, from the modules table
4988 * @param $teacherroles - array of roles that have moodle/legacy:teacher
4989 * @param $studentroles - array of roles that have moodle/legacy:student
4990 * @param $guestroles - array of roles that have moodle/legacy:guest
4991 * @param $cmid - the course_module id for this forum instance
4992 * @return boolean - forum was converted or not
4994 function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
4995 $studentroles=array(), $guestroles=array(), $cmid=NULL) {
4997 global $CFG;
4999 if (!isset($forum->open) && !isset($forum->assesspublic)) {
5000 // We assume that this forum has already been converted to use the
5001 // Roles System. Columns forum.open and forum.assesspublic get dropped
5002 // once the forum module has been upgraded to use Roles.
5003 return false;
5006 if ($forum->type == 'teacher') {
5008 // Teacher forums should be converted to normal forums that
5009 // use the Roles System to implement the old behavior.
5010 // Note:
5011 // Seems that teacher forums were never backed up in 1.6 since they
5012 // didn't have an entry in the course_modules table.
5013 require_once($CFG->dirroot.'/course/lib.php');
5015 if (count_records('forum_discussions', 'forum', $forum->id) == 0) {
5016 // Delete empty teacher forums.
5017 delete_records('forum', 'id', $forum->id);
5018 } else {
5019 // Create a course module for the forum and assign it to
5020 // section 0 in the course.
5021 $mod = new object;
5022 $mod->course = $forum->course;
5023 $mod->module = $forummodid;
5024 $mod->instance = $forum->id;
5025 $mod->section = 0;
5026 $mod->visible = 0; // Hide the forum
5027 $mod->visibleold = 0; // Hide the forum
5028 $mod->groupmode = 0;
5030 if (!$cmid = add_course_module($mod)) {
5031 error('Could not create new course module instance for the teacher forum');
5032 } else {
5033 $mod->coursemodule = $cmid;
5034 if (!$sectionid = add_mod_to_section($mod)) {
5035 error('Could not add converted teacher forum instance to section 0 in the course');
5036 } else {
5037 if (!set_field('course_modules', 'section', $sectionid, 'id', $cmid)) {
5038 error('Could not update course module with section id');
5043 // Change the forum type to general.
5044 $forum->type = 'general';
5045 if (!update_record('forum', $forum)) {
5046 error('Could not change forum from type teacher to type general');
5049 $context = get_context_instance(CONTEXT_MODULE, $cmid);
5051 // Create overrides for default student and guest roles (prevent).
5052 foreach ($studentroles as $studentrole) {
5053 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5054 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id);
5055 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5056 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
5057 assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
5058 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
5059 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
5060 assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id);
5061 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id);
5062 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id);
5063 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id);
5064 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id);
5065 assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id);
5066 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id);
5067 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id);
5068 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id);
5069 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $studentrole->id, $context->id);
5071 foreach ($guestroles as $guestrole) {
5072 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
5073 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id);
5074 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
5075 assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id);
5076 assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id);
5077 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id);
5078 assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id);
5079 assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id);
5080 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id);
5081 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id);
5082 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id);
5083 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id);
5084 assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id);
5085 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id);
5086 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id);
5087 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id);
5088 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $guestrole->id, $context->id);
5091 } else {
5092 // Non-teacher forum.
5094 if (empty($cmid)) {
5095 // We were not given the course_module id. Try to find it.
5096 if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
5097 notify('Could not get the course module for the forum');
5098 return false;
5099 } else {
5100 $cmid = $cm->id;
5103 $context = get_context_instance(CONTEXT_MODULE, $cmid);
5105 // $forum->open defines what students can do:
5106 // 0 = No discussions, no replies
5107 // 1 = No discussions, but replies are allowed
5108 // 2 = Discussions and replies are allowed
5109 switch ($forum->open) {
5110 case 0:
5111 foreach ($studentroles as $studentrole) {
5112 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5113 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
5115 break;
5116 case 1:
5117 foreach ($studentroles as $studentrole) {
5118 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5119 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
5121 break;
5122 case 2:
5123 foreach ($studentroles as $studentrole) {
5124 assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id);
5125 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
5127 break;
5130 // $forum->assessed defines whether forum rating is turned
5131 // on (1 or 2) and who can rate posts:
5132 // 1 = Everyone can rate posts
5133 // 2 = Only teachers can rate posts
5134 switch ($forum->assessed) {
5135 case 1:
5136 foreach ($studentroles as $studentrole) {
5137 assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id);
5139 foreach ($teacherroles as $teacherrole) {
5140 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
5142 break;
5143 case 2:
5144 foreach ($studentroles as $studentrole) {
5145 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
5147 foreach ($teacherroles as $teacherrole) {
5148 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
5150 break;
5153 // $forum->assesspublic defines whether students can see
5154 // everybody's ratings:
5155 // 0 = Students can only see their own ratings
5156 // 1 = Students can see everyone's ratings
5157 switch ($forum->assesspublic) {
5158 case 0:
5159 foreach ($studentroles as $studentrole) {
5160 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
5162 foreach ($teacherroles as $teacherrole) {
5163 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
5165 break;
5166 case 1:
5167 foreach ($studentroles as $studentrole) {
5168 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id);
5170 foreach ($teacherroles as $teacherrole) {
5171 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
5173 break;
5176 if (empty($cm)) {
5177 $cm = get_record('course_modules', 'id', $cmid);
5180 // $cm->groupmode:
5181 // 0 - No groups
5182 // 1 - Separate groups
5183 // 2 - Visible groups
5184 switch ($cm->groupmode) {
5185 case 0:
5186 break;
5187 case 1:
5188 foreach ($studentroles as $studentrole) {
5189 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
5191 foreach ($teacherroles as $teacherrole) {
5192 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
5194 break;
5195 case 2:
5196 foreach ($studentroles as $studentrole) {
5197 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
5199 foreach ($teacherroles as $teacherrole) {
5200 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
5202 break;
5205 return true;