adding some strings
[moodle-linuxchix.git] / mod / forum / lib.php
blobfafd10dc32d5d0601fdf07cbe98b6955ecad2678
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 = groupmode($course, $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 (!ismember($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->id);
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->id);
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->forcesubscribe;
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 .= '<br /><div 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] = groupmode($course, $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->itemid = $forum->id;
1142 $grade->userid = $userid;
1143 $grade->rawgrade = NULL;
1144 grade_update('mod/forum', $data->course, 'mod', 'forum', $forum->id, 0, $grade);
1147 } else {
1148 $sql = "SELECT f.*, cm.idnumber as cmidnumber
1149 FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1150 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1151 if ($rs = get_recordset_sql($sql)) {
1152 if ($rs->RecordCount() > 0) {
1153 while ($forum = rs_fetch_next_record($rs)) {
1154 forum_grade_item_update($forum);
1155 if ($forum->assessed) {
1156 forum_update_grades($forum, 0, false);
1160 rs_close($rs);
1166 * Create/update grade item for given forum
1168 * @param object $forum object with extra cmidnumber
1169 * @return int 0 if ok
1171 function forum_grade_item_update($forum) {
1172 global $CFG;
1173 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1174 require_once($CFG->libdir.'/gradelib.php');
1177 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
1179 if (!$forum->assessed or $forum->scale == 0) {
1180 $params['gradetype'] = GRADE_TYPE_NONE;
1182 } else if ($forum->scale > 0) {
1183 $params['gradetype'] = GRADE_TYPE_VALUE;
1184 $params['grademax'] = $forum->scale;
1185 $params['grademin'] = 0;
1187 } else if ($forum->scale < 0) {
1188 $params['gradetype'] = GRADE_TYPE_SCALE;
1189 $params['scaleid'] = -$forum->scale;
1192 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, $params);
1196 * Delete grade item for given forum
1198 * @param object $forum object
1199 * @return object grade_item
1201 function forum_grade_item_delete($forum) {
1202 global $CFG;
1203 require_once($CFG->libdir.'/gradelib.php');
1205 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
1210 * Returns the users with data in one forum
1211 * (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
1213 function forum_get_participants($forumid) {
1215 global $CFG;
1217 //Get students from forum_subscriptions
1218 $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
1219 FROM {$CFG->prefix}user u,
1220 {$CFG->prefix}forum_subscriptions s
1221 WHERE s.forum = '$forumid' and
1222 u.id = s.userid");
1223 //Get students from forum_posts
1224 $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
1225 FROM {$CFG->prefix}user u,
1226 {$CFG->prefix}forum_discussions d,
1227 {$CFG->prefix}forum_posts p
1228 WHERE d.forum = '$forumid' and
1229 p.discussion = d.id and
1230 u.id = p.userid");
1232 //Get students from forum_ratings
1233 $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
1234 FROM {$CFG->prefix}user u,
1235 {$CFG->prefix}forum_discussions d,
1236 {$CFG->prefix}forum_posts p,
1237 {$CFG->prefix}forum_ratings r
1238 WHERE d.forum = '$forumid' and
1239 p.discussion = d.id and
1240 r.post = p.id and
1241 u.id = r.userid");
1243 //Add st_posts to st_subscriptions
1244 if ($st_posts) {
1245 foreach ($st_posts as $st_post) {
1246 $st_subscriptions[$st_post->id] = $st_post;
1249 //Add st_ratings to st_subscriptions
1250 if ($st_ratings) {
1251 foreach ($st_ratings as $st_rating) {
1252 $st_subscriptions[$st_rating->id] = $st_rating;
1255 //Return st_subscriptions array (it contains an array of unique users)
1256 return ($st_subscriptions);
1262 function forum_scale_used ($forumid,$scaleid) {
1263 //This function returns if a scale is being used by one forum
1265 $return = false;
1267 $rec = get_record("forum","id","$forumid","scale","-$scaleid");
1269 if (!empty($rec) && !empty($scaleid)) {
1270 $return = true;
1273 return $return;
1276 // SQL FUNCTIONS ///////////////////////////////////////////////////////////
1279 * Gets a post with all info ready for forum_print_post
1280 * Most of these joins are just to get the forum id
1282 function forum_get_post_full($postid) {
1283 global $CFG;
1285 return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1286 FROM {$CFG->prefix}forum_posts p
1287 LEFT JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
1288 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1289 WHERE p.id = '$postid'");
1293 * Gets posts with all info ready for forum_print_post
1294 * We pass forumid in because we always know it so no need to make a
1295 * complicated join to find it out.
1297 function forum_get_discussion_posts($discussion, $sort, $forumid) {
1298 global $CFG;
1300 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1301 FROM {$CFG->prefix}forum_posts p
1302 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1303 WHERE p.discussion = $discussion
1304 AND p.parent > 0 $sort");
1308 * Gets posts with all info ready for forum_print_post
1309 * We pass forumid in because we always know it so no need to make a
1310 * complicated join to find it out.
1312 function forum_get_child_posts($parent, $forumid) {
1313 global $CFG;
1315 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1316 FROM {$CFG->prefix}forum_posts p
1317 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1318 WHERE p.parent = '$parent'
1319 ORDER BY p.created ASC");
1323 * An array of forum objects that the user is allowed to read/search through.
1324 * @param $userid
1325 * @param $courseid - if 0, we look for forums throughout the whole site.
1326 * @return array of forum objects, or false if no matches
1327 * Forum objects have the following attributes:
1328 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1329 * viewhiddentimedposts
1331 function forum_get_readable_forums($userid, $courseid=0) {
1333 global $CFG, $USER;
1335 if (!$forummod = get_record('modules', 'name', 'forum')) {
1336 error('The forum module is not installed');
1339 if ($courseid) {
1340 $courses = get_records('course', 'id', $courseid);
1341 } else {
1342 // If no course is specified, then the user can see SITE + his courses.
1343 // And admins can see all courses, so pass the $doanything flag enabled
1344 $courses1 = get_records('course', 'id', SITEID);
1345 $courses2 = get_my_courses($userid, null, null, true);
1346 $courses = array_merge($courses1, $courses2);
1348 if (!$courses) {
1349 return false;
1352 $readableforums = array();
1354 foreach ($courses as $course) {
1356 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
1358 if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
1359 $selecthidden = ' AND cm.visible = 1';
1360 } else {
1361 $selecthidden = '';
1364 $selectforums = "SELECT f.id AS id,
1365 f.name AS name,
1366 f.type AS type,
1367 f.course AS course,
1368 cm.id AS cmid,
1369 cm.visible AS cmvisible,
1370 cm.groupmode AS cmgroupmode
1371 FROM {$CFG->prefix}course_modules cm,
1372 {$CFG->prefix}forum f
1373 WHERE cm.instance = f.id
1374 AND cm.course = {$course->id}
1375 AND cm.module = {$forummod->id}
1376 $selecthidden
1377 ORDER BY f.name ASC";
1379 if ($forums = get_records_sql($selectforums)) {
1381 $group = user_group($course->id, $userid);
1383 foreach ($forums as $forum) {
1384 $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid);
1386 if (has_capability('mod/forum:viewdiscussion', $forumcontext)) {
1388 // Evaluate groupmode.
1389 $cm = new object;
1390 $cm->id = $forum->cmid;
1391 $cm->groupmode = $forum->cmgroupmode;
1392 $forum->cmgroupmode = groupmode($course, $cm);
1394 if ($forum->cmgroupmode == SEPARATEGROUPS
1395 && !has_capability('moodle/site:accessallgroups', $forumcontext)) {
1396 $forum->accessallgroups = false;
1397 $forum->accessgroup = $group->id; // The user can only access
1398 // discussions for this group.
1399 } else {
1400 $forum->accessallgroups = true;
1403 $forum->viewhiddentimedposts
1404 = has_capability('mod/forum:viewhiddentimedposts', $forumcontext);
1406 if ($forum->type == 'qanda'
1407 && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) {
1409 // We need to check whether the user has posted in the qanda forum.
1410 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
1411 // the user is allowed to see in this forum.
1413 if ($discussionspostedin =
1414 forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1415 foreach ($discussionspostedin as $d) {
1416 array_push($forum->onlydiscussions, $d->id);
1420 array_push($readableforums, $forum);
1424 } // End foreach $courses
1426 //print_object($courses);
1427 //print_object($readableforums);
1429 return $readableforums;
1433 * Returns a list of posts found using an array of search terms.
1434 * @param $searchterms - array of search terms, e.g. word +word -word
1435 * @param $courseid - if 0, we search through the whole site
1436 * @param $page
1437 * @param $recordsperpage=50
1438 * @param &$totalcount
1439 * @param $extrasql
1440 * @return array of posts found
1442 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
1443 &$totalcount, $extrasql='') {
1444 global $CFG, $USER;
1445 require_once($CFG->libdir.'/searchlib.php');
1447 $forums = forum_get_readable_forums($USER->id, $courseid);
1449 if (count($forums) == 0) {
1450 return false;
1453 for ($i=0; $i<count($forums); $i++) {
1454 if ($i == 0) {
1455 $selectdiscussion = " ((d.forum = {$forums[$i]->id}";
1456 } else {
1457 $selectdiscussion .= " OR (d.forum = {$forums[$i]->id}";
1459 if (!empty($CFG->forum_enabletimedposts) && !$forums[$i]->viewhiddentimedposts) {
1460 $now = time();
1461 $selectdiscussion .= " AND ( d.userid = {$USER->id}
1462 OR ((d.timestart = 0 OR d.timestart <= $now)
1463 AND (d.timeend = 0 OR d.timeend > $now)) )";
1465 if ($forums[$i]->type == 'qanda' && isset($forums[$i]->onlydiscussions)) {
1466 // This is a qanda forum.
1467 if (is_array($forums[$i]->onlydiscussions)) {
1468 // Show question posts as well as posts from discussions in
1469 // which the user has posted a reply.
1470 $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions);
1471 $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)";
1472 } else {
1473 // Show only the question posts.
1474 $selectdiscussion .= ' AND (p.parent = 0)';
1477 if (!$forums[$i]->accessallgroups) {
1478 if (!empty($forums[$i]->accessgroup)) {
1479 $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}";
1480 $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups.
1481 } else {
1482 // User isn't in any group. Only search discussions that are
1483 // open to all groups.
1484 $selectdiscussion .= ' AND d.groupid = -1';
1487 $selectdiscussion .= ")\n";
1489 $selectdiscussion .= ")";
1492 // Some differences SQL
1493 $LIKE = sql_ilike();
1494 $NOTLIKE = 'NOT ' . $LIKE;
1495 if ($CFG->dbfamily == 'postgres') {
1496 $REGEXP = '~*';
1497 $NOTREGEXP = '!~*';
1498 } else {
1499 $REGEXP = 'REGEXP';
1500 $NOTREGEXP = 'NOT REGEXP';
1503 $messagesearch = '';
1504 $searchstring = '';
1506 // Need to concat these back together for parser to work.
1507 foreach($searchterms as $searchterm){
1508 if ($searchstring != '') {
1509 $searchstring .= ' ';
1511 $searchstring .= $searchterm;
1514 // We need to allow quoted strings for the search. The quotes *should* be stripped
1515 // by the parser, but this should be examined carefully for security implications.
1516 $searchstring = str_replace("\\\"","\"",$searchstring);
1517 $parser = new search_parser();
1518 $lexer = new search_lexer($parser);
1520 if ($lexer->parse($searchstring)) {
1521 $parsearray = $parser->get_parsed_array();
1522 // Experimental feature under 1.8! MDL-8830
1523 // Use alternative text searches if defined
1524 // This feature only works under mysql until properly implemented for other DBs
1525 // Requires manual creation of text index for forum_posts before enabling it:
1526 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
1527 // Experimental feature under 1.8! MDL-8830
1528 if (!empty($CFG->forum_usetextsearches)) {
1529 $messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
1530 'p.userid', 'u.id', 'u.firstname',
1531 'u.lastname', 'p.modified', 'd.forum');
1532 } else {
1533 $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
1534 'p.userid', 'u.id', 'u.firstname',
1535 'u.lastname', 'p.modified', 'd.forum');
1539 $fromsql = "{$CFG->prefix}forum_posts p,
1540 {$CFG->prefix}forum_discussions d,
1541 {$CFG->prefix}user u";
1543 $selectsql = " $messagesearch
1544 AND p.discussion = d.id
1545 AND p.userid = u.id
1546 AND $selectdiscussion
1547 $extrasql";
1549 $countsql = "SELECT COUNT(*)
1550 FROM $fromsql
1551 WHERE $selectsql";
1553 $searchsql = "SELECT p.*,
1554 d.forum,
1555 u.firstname,
1556 u.lastname,
1557 u.email,
1558 u.picture
1559 FROM $fromsql
1560 WHERE $selectsql
1561 ORDER BY p.modified DESC";
1563 $totalcount = count_records_sql($countsql);
1565 return get_records_sql($searchsql, $limitfrom, $limitnum);
1569 * Returns a list of ratings for a particular post - sorted.
1571 function forum_get_ratings($postid, $sort="u.firstname ASC") {
1572 global $CFG;
1573 return get_records_sql("SELECT u.*, r.rating, r.time
1574 FROM {$CFG->prefix}forum_ratings r,
1575 {$CFG->prefix}user u
1576 WHERE r.post = '$postid'
1577 AND r.userid = u.id
1578 ORDER BY $sort");
1582 * Returns a list of all new posts that have not been mailed yet
1584 function forum_get_unmailed_posts($starttime, $endtime) {
1585 global $CFG;
1586 $now = time();
1587 return get_records_sql("SELECT p.*, d.course
1588 FROM {$CFG->prefix}forum_posts p,
1589 {$CFG->prefix}forum_discussions d
1590 WHERE p.mailed = 0
1591 AND (p.created >= '$starttime' OR d.timestart > 0)
1592 AND (p.created < '$endtime' OR p.mailnow = 1)
1593 AND p.discussion = d.id
1594 AND ((d.timestart = 0 OR d.timestart <= '$now')
1595 AND (d.timeend = 0 OR d.timeend > '$now'))
1596 ORDER BY p.modified ASC");
1600 * Marks posts before a certain time as being mailed already
1602 function forum_mark_old_posts_as_mailed($endtime) {
1603 global $CFG;
1604 // Find out posts those are not showing immediately so we can exclude them
1605 $now = time();
1606 $delayed_posts = get_records_sql("SELECT p.id, p.discussion
1607 FROM {$CFG->prefix}forum_posts p,
1608 {$CFG->prefix}forum_discussions d
1609 WHERE p.mailed = 0
1610 AND p.discussion = d.id
1611 AND d.timestart > '$now'");
1612 $delayed_ids = array();
1613 if ($delayed_posts) {
1614 foreach ($delayed_posts as $post) {
1615 $delayed_ids[] = $post->id;
1617 } else {
1618 $delayed_ids[] = 0;
1620 return execute_sql("UPDATE {$CFG->prefix}forum_posts
1621 SET mailed = '1'
1622 WHERE id NOT IN (".implode(',',$delayed_ids).")
1623 AND (created < '$endtime' OR mailnow = 1)
1624 AND mailed ='0'", false);
1628 * Get all the posts for a user in a forum suitable for forum_print_post
1630 function forum_get_user_posts($forumid, $userid) {
1631 global $CFG;
1633 return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1634 FROM {$CFG->prefix}forum f,
1635 {$CFG->prefix}forum_discussions d,
1636 {$CFG->prefix}forum_posts p,
1637 {$CFG->prefix}user u
1638 WHERE f.id = '$forumid'
1639 AND d.forum = f.id
1640 AND p.discussion = d.id
1641 AND p.userid = '$userid'
1642 AND p.userid = u.id
1643 ORDER BY p.modified ASC");
1647 * Given a log entry, return the forum post details for it.
1649 function forum_get_post_from_log($log) {
1650 global $CFG;
1652 if ($log->action == "add post") {
1654 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1655 u.firstname, u.lastname, u.email, u.picture
1656 FROM {$CFG->prefix}forum_discussions d,
1657 {$CFG->prefix}forum_posts p,
1658 {$CFG->prefix}forum f,
1659 {$CFG->prefix}user u
1660 WHERE p.id = '$log->info'
1661 AND d.id = p.discussion
1662 AND p.userid = u.id
1663 AND u.deleted <> '1'
1664 AND f.id = d.forum");
1667 } else if ($log->action == "add discussion") {
1669 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1670 u.firstname, u.lastname, u.email, u.picture
1671 FROM {$CFG->prefix}forum_discussions d,
1672 {$CFG->prefix}forum_posts p,
1673 {$CFG->prefix}forum f,
1674 {$CFG->prefix}user u
1675 WHERE d.id = '$log->info'
1676 AND d.firstpost = p.id
1677 AND p.userid = u.id
1678 AND u.deleted <> '1'
1679 AND f.id = d.forum");
1681 return NULL;
1685 * Given a discussion id, return the first post from the discussion
1687 function forum_get_firstpost_from_discussion($discussionid) {
1688 global $CFG;
1690 return get_record_sql("SELECT p.*
1691 FROM {$CFG->prefix}forum_discussions d,
1692 {$CFG->prefix}forum_posts p
1693 WHERE d.id = '$discussionid'
1694 AND d.firstpost = p.id ");
1698 * Returns an array of counts of replies to each discussion (optionally in one forum or course and/or user)
1700 function forum_count_discussion_replies($forum='0', $course='0', $user='0') {
1701 global $CFG;
1703 $forumselect = $courseselect = $userselect = '';
1705 if ($forum) {
1706 $forumselect = " AND d.forum = '$forum'";
1708 if ($course) {
1709 $courseselect = " AND d.course = '$course'";
1711 if ($user) {
1712 $userselect = " AND d.userid = '$user'";
1714 return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid
1715 FROM {$CFG->prefix}forum_posts p,
1716 {$CFG->prefix}forum_discussions d
1717 WHERE p.parent > 0 $forumselect $courseselect $userselect
1718 AND p.discussion = d.id
1719 GROUP BY p.discussion");
1723 * How many unrated posts are in the given discussion for a given user?
1725 function forum_count_unrated_posts($discussionid, $userid) {
1726 global $CFG;
1727 if ($posts = get_record_sql("SELECT count(*) as num
1728 FROM {$CFG->prefix}forum_posts
1729 WHERE parent > 0
1730 AND discussion = '$discussionid'
1731 AND userid <> '$userid' ")) {
1733 if ($rated = get_record_sql("SELECT count(*) as num
1734 FROM {$CFG->prefix}forum_posts p,
1735 {$CFG->prefix}forum_ratings r
1736 WHERE p.discussion = '$discussionid'
1737 AND p.id = r.post
1738 AND r.userid = '$userid'")) {
1739 $difference = $posts->num - $rated->num;
1740 if ($difference > 0) {
1741 return $difference;
1742 } else {
1743 return 0; // Just in case there was a counting error
1745 } else {
1746 return $posts->num;
1748 } else {
1749 return 0;
1754 * Get all discussions in a forum
1756 function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC",
1757 $user=0, $fullpost=true, $currentgroup=-1, $limit=0, $userlastmodified=false) {
1758 global $CFG, $USER;
1760 $timelimit = '';
1762 if (!empty($CFG->forum_enabletimedposts)) {
1764 if (!$cm = get_coursemodule_from_instance('forum', $forum)) {
1765 error('Course Module ID was incorrect');
1767 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1769 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
1770 $now = time();
1771 $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
1772 if (!empty($USER->id)) {
1773 $timelimit .= " OR d.userid = '$USER->id'";
1775 $timelimit .= ')';
1779 if ($user) {
1780 $userselect = " AND u.id = '$user' ";
1781 } else {
1782 $userselect = "";
1785 $limitfrom = 0;
1786 $limitnum = 0;
1787 if ($limit) {
1788 $limitnum = $limit;
1791 if ($currentgroup == -1) {
1792 $currentgroup = get_current_group($cm->course);
1795 if ($currentgroup) {
1796 $groupselect = " AND (d.groupid = '$currentgroup' OR d.groupid = -1) ";
1797 } else {
1798 $groupselect = "";
1801 if (empty($forumsort)) {
1802 $forumsort = "d.timemodified DESC";
1804 if (empty($fullpost)) {
1805 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
1806 } else {
1807 $postdata = "p.*";
1810 if (empty($userlastmodified)) { // We don't need to know this
1811 $umfields = '';
1812 $umtable = '';
1813 } else {
1814 $umfields = ', um.firstname AS umfirstname, um.lastname AS umlastname';
1815 $umtable = ' LEFT JOIN '.$CFG->prefix.'user um on (d.usermodified = um.id)';
1818 //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.
1819 if ($CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'oracle') {
1820 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1821 u.firstname, u.lastname, u.email, u.picture $umfields
1822 FROM {$CFG->prefix}forum_discussions d
1823 JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
1824 JOIN {$CFG->prefix}user u ON p.userid = u.id
1825 $umtable
1826 WHERE d.forum = '$forum'
1827 AND p.parent = 0
1828 $timelimit $groupselect $userselect
1829 ORDER BY $forumsort", $limitfrom, $limitnum);
1830 } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query)
1831 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1832 u.firstname, u.lastname, u.email, u.picture $umfields
1833 FROM ({$CFG->prefix}forum_posts p,
1834 {$CFG->prefix}user u,
1835 {$CFG->prefix}forum_discussions d)
1836 $umtable
1837 WHERE d.forum = '$forum'
1838 AND p.discussion = d.id
1839 AND p.parent = 0
1840 AND p.userid = u.id $timelimit $groupselect $userselect
1841 ORDER BY $forumsort", $limitfrom, $limitnum);
1848 * Get all discussions started by a particular user in a course (or group)
1849 * This function no longer used ...
1851 function forum_get_user_discussions($courseid, $userid, $groupid=0) {
1852 global $CFG;
1854 if ($groupid) {
1855 $groupselect = " AND d.groupid = '$groupid' ";
1856 } else {
1857 $groupselect = "";
1860 return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture,
1861 f.type as forumtype, f.name as forumname, f.id as forumid
1862 FROM {$CFG->prefix}forum_discussions d,
1863 {$CFG->prefix}forum_posts p,
1864 {$CFG->prefix}user u,
1865 {$CFG->prefix}forum f
1866 WHERE d.course = '$courseid'
1867 AND p.discussion = d.id
1868 AND p.parent = 0
1869 AND p.userid = u.id
1870 AND u.id = '$userid'
1871 AND d.forum = f.id $groupselect
1872 ORDER BY p.created DESC");
1876 * Returns list of user objects that are subscribed to this forum
1878 function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) {
1880 global $CFG;
1882 static $resultscache = array();
1884 if ($cache && isset($resultscache[$forum->id][$groupid])) {
1885 return $resultscache[$forum->id][$groupid];
1888 if ($groupid) {
1889 $grouptables = ', '. groups_members_from_sql();
1890 $groupselect = 'AND'.groups_members_where_sql($groupid, 'u.id');
1891 } else {
1892 $grouptables = '';
1893 $groupselect = '';
1896 if (forum_is_forcesubscribed($forum->id)) {
1897 $results = get_course_users($course->id); // Otherwise get everyone in the course
1898 } else {
1899 $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop,
1900 u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums
1901 FROM {$CFG->prefix}user u,
1902 {$CFG->prefix}forum_subscriptions s $grouptables
1903 WHERE s.forum = '$forum->id'
1904 AND s.userid = u.id
1905 AND u.deleted <> 1 $groupselect
1906 ORDER BY u.email ASC");
1908 // Guest user should never be subscribed to a forum.
1909 if ($guest = guest_user()) {
1910 unset($results[$guest->id]);
1913 if ($cache) {
1914 $resultscache[$forum->id][$groupid] = $results;
1917 return $results;
1922 // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
1925 function forum_get_course_forum($courseid, $type) {
1926 // How to set up special 1-per-course forums
1927 global $CFG;
1929 if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
1930 // There should always only be ONE, but with the right combination of
1931 // errors there might be more. In this case, just return the oldest one (lowest ID).
1932 foreach ($forums as $forum) {
1933 return $forum; // ie the first one
1937 // Doesn't exist, so create one now.
1938 $forum->course = $courseid;
1939 $forum->type = "$type";
1940 switch ($forum->type) {
1941 case "news":
1942 $forum->name = addslashes(get_string("namenews", "forum"));
1943 $forum->intro = addslashes(get_string("intronews", "forum"));
1944 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
1945 $forum->assessed = 0;
1946 if ($courseid == SITEID) {
1947 $forum->name = get_string("sitenews");
1948 $forum->forcesubscribe = 0;
1950 break;
1951 case "social":
1952 $forum->name = addslashes(get_string("namesocial", "forum"));
1953 $forum->intro = addslashes(get_string("introsocial", "forum"));
1954 $forum->assessed = 0;
1955 $forum->forcesubscribe = 0;
1956 break;
1957 default:
1958 notify("That forum type doesn't exist!");
1959 return false;
1960 break;
1963 $forum->timemodified = time();
1964 $forum->id = insert_record("forum", $forum);
1966 if (! $module = get_record("modules", "name", "forum")) {
1967 notify("Could not find forum module!!");
1968 return false;
1970 $mod->course = $courseid;
1971 $mod->module = $module->id;
1972 $mod->instance = $forum->id;
1973 $mod->section = 0;
1974 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
1975 notify("Could not add a new course module to the course '" . format_string($course->fullname) . "'");
1976 return false;
1978 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
1979 notify("Could not add the new course module to that section");
1980 return false;
1982 if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
1983 notify("Could not update the course module with the correct section");
1984 return false;
1986 include_once("$CFG->dirroot/course/lib.php");
1987 rebuild_course_cache($courseid);
1989 return get_record("forum", "id", "$forum->id");
1994 * Given the data about a posting, builds up the HTML to display it and
1995 * returns the HTML in a string. This is designed for sending via HTML email.
1997 function forum_make_mail_post(&$post, $user, $touser, $course,
1998 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2001 global $CFG, $USER;
2003 // the old caching was removed for now, because it did not work due to recent changes in cron
2005 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2007 if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) {
2008 mtrace('Course Module ID was incorrect');
2010 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2012 // format the post body
2013 $options = new object();
2014 $options->para = true;
2015 $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);
2017 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
2019 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
2020 $output .= print_user_picture($user->id, $course->id, $user->picture, false, true);
2021 $output .= '</td>';
2023 if ($post->parent) {
2024 $output .= '<td class="topic">';
2025 } else {
2026 $output .= '<td class="topic starter">';
2028 $output .= '<div class="subject">'.format_string($post->subject).'</div>';
2030 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext));
2031 $by = new object();
2032 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
2033 $by->date = userdate($post->modified, '', $touser->timezone);
2034 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
2036 $output .= '</td></tr>';
2038 $output .= '<tr><td class="left side" valign="top">';
2039 if ($group = user_group($course->id, $user->id)) {
2040 $output .= print_group_picture($group, $course->id, false, true, true);
2041 } else {
2042 $output .= '&nbsp;';
2045 $output .= '</td><td class="content">';
2047 if ($post->attachment) {
2048 $post->course = $course->id;
2049 $output .= '<div class="attachments">';
2050 $output .= forum_print_attachments($post, 'html');
2051 $output .= "</div>";
2054 $output .= $formattedtext;
2056 // Commands
2057 $commands = array();
2059 if ($post->parent) {
2060 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2061 $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
2064 if ($reply) {
2065 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
2066 get_string('reply', 'forum').'</a>';
2069 $output .= '<div class="commands">';
2070 $output .= implode(' | ', $commands);
2071 $output .= '</div>';
2073 // Context link to post if required
2074 if ($link) {
2075 $output .= '<div class="link">';
2076 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
2077 get_string('postincontext', 'forum').'</a>';
2078 $output .= '</div>';
2081 if ($footer) {
2082 $output .= '<div class="footer">'.$footer.'</div>';
2084 $output .= '</td></tr></table>'."\n\n";
2086 return $output;
2090 * Print a forum post
2092 * @param object $post The post to print.
2093 * @param integer $courseid The course this post belongs to.
2094 * @param boolean $ownpost Whether this post belongs to the current user.
2095 * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
2096 * @param boolean $link Just print a shortened version of the post as a link to the full post.
2097 * @param object $ratings -- I don't really know --
2098 * @param string $footer Extra stuff to print after the message.
2099 * @param string $highlight Space-separated list of terms to highlight.
2100 * @param int $post_read true, false or -99. If we already know whether this user
2101 * has read this post, pass that in, otherwise, pass in -99, and this
2102 * function will work it out.
2103 * @param boolean $dummyifcantsee When forum_user_can_see_post says that
2104 * the current user can't see this post, if this argument is true
2105 * (the default) then print a dummy 'you can't see this post' post.
2106 * If false, don't output anything at all.
2108 function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false,
2109 $ratings=NULL, $footer="", $highlight="", $post_read=-99, $dummyifcantsee=true) {
2111 global $USER, $CFG;
2113 static $stredit, $strdelete, $strreply, $strparent, $strprune;
2114 static $strpruneheading, $threadedmode;
2115 static $strmarkread, $strmarkunread, $istracked;
2118 $discussion = get_record('forum_discussions', 'id', $post->discussion);
2119 if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum)) {
2120 error('Course Module ID was incorrect');
2122 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2125 if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
2126 if (!$dummyifcantsee) {
2127 return;
2129 echo '<a id="p'.$post->id.'"></a>';
2130 echo '<table cellspacing="0" class="forumpost">';
2131 echo '<tr class="header"><td class="picture left">';
2132 // print_user_picture($post->userid, $courseid, $post->picture);
2133 echo '</td>';
2134 if ($post->parent) {
2135 echo '<td class="topic">';
2136 } else {
2137 echo '<td class="topic starter">';
2139 echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
2140 echo '<div class="author">';
2141 print_string('forumauthorhidden','forum');
2142 echo '</div></td></tr>';
2144 echo '<tr><td class="left side">';
2145 echo '&nbsp;';
2147 // Actual content
2149 echo '</td><td class="content">'."\n";
2150 echo get_string('forumbodyhidden','forum');
2151 echo '</td></tr></table>';
2152 return;
2155 if (empty($stredit)) {
2156 $stredit = get_string('edit', 'forum');
2157 $strdelete = get_string('delete', 'forum');
2158 $strreply = get_string('reply', 'forum');
2159 $strparent = get_string('parent', 'forum');
2160 $strpruneheading = get_string('pruneheading', 'forum');
2161 $strprune = get_string('prune', 'forum');
2162 $threadedmode = (!empty($USER->mode) and ($USER->mode == FORUM_MODE_THREADED));
2163 $strmarkread = get_string('markread', 'forum');
2164 $strmarkunread = get_string('markunread', 'forum');
2166 if (!empty($post->forum)) {
2167 $istracked = (forum_tp_can_track_forums($post->forum) &&
2168 forum_tp_is_tracked($post->forum));
2169 } else {
2170 $istracked = false;
2174 if ($istracked) {
2175 if ($post_read == -99) { // If we don't know yet...
2176 // The front page can display a news item post to non-logged in users. This should
2177 // always appear as 'read'.
2178 $post_read = empty($USER) || forum_tp_is_post_read($USER->id, $post);
2180 if ($post_read) {
2181 $read_style = ' read';
2182 } else {
2183 $read_style = ' unread';
2184 echo '<a name="unread"></a>';
2186 } else {
2187 $read_style = '';
2190 echo '<a id="p'.$post->id.'"></a>';
2191 echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
2193 echo '<tr class="header"><td class="picture left">';
2194 print_user_picture($post->userid, $courseid, $post->picture);
2195 echo '</td>';
2197 if ($post->parent) {
2198 echo '<td class="topic">';
2199 } else {
2200 echo '<td class="topic starter">';
2203 echo '<div class="subject">'.format_string($post->subject).'</div>';
2205 echo '<div class="author">';
2206 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2207 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
2208 $post->userid.'&amp;course='.$courseid.'">'.$fullname.'</a>';
2209 $by->date = userdate($post->modified);
2210 print_string('bynameondate', 'forum', $by);
2211 echo '</div></td></tr>';
2213 echo '<tr><td class="left side">';
2214 if ($group = user_group($courseid, $post->userid)) {
2215 print_group_picture($group, $courseid, false, false, true);
2216 } else {
2217 echo '&nbsp;';
2220 // Actual content
2222 echo '</td><td class="content">'."\n";
2224 if ($post->attachment) {
2225 $post->course = $courseid;
2226 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2227 echo '<div class="attachments">';
2228 $attachedimages = forum_print_attachments($post);
2229 echo '</div>';
2230 } else {
2231 $attachedimages = '';
2235 $options = new Object;
2236 $options->para = false;
2237 $options->trusttext = true;
2238 if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) {
2239 // Print shortened version
2240 echo format_text(forum_shorten_post($post->message), $post->format, $options, $courseid);
2241 $numwords = count_words(strip_tags($post->message));
2242 echo '<p><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2243 echo get_string('readtherest', 'forum');
2244 echo '</a> ('.get_string('numwords', '', $numwords).')...</p>';
2245 } else {
2246 // Print whole message
2247 if ($highlight) {
2248 echo highlight($highlight, format_text($post->message, $post->format, $options, $courseid));
2249 } else {
2250 echo format_text($post->message, $post->format, $options, $courseid);
2252 echo $attachedimages;
2256 // Commands
2258 $commands = array();
2260 if ($istracked) {
2261 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
2262 // Don't display the mark read / unread controls in this case.
2263 if ($CFG->forum_usermarksread && !empty($USER)) {
2264 if ($post_read) {
2265 $mcmd = '&amp;mark=unread&amp;postid='.$post->id;
2266 $mtxt = $strmarkunread;
2267 } else {
2268 $mcmd = '&amp;mark=read&amp;postid='.$post->id;
2269 $mtxt = $strmarkread;
2271 if ($threadedmode) {
2272 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2273 $post->discussion.'&amp;parent='.$post->id.$mcmd.'">'.$mtxt.'</a>';
2274 } else {
2275 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2276 $post->discussion.$mcmd.'#p'.$post->id.'">'.$mtxt.'</a>';
2281 if ($post->parent) {
2282 if ($threadedmode) {
2283 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2284 $post->discussion.'&amp;parent='.$post->parent.'">'.$strparent.'</a>';
2285 } else {
2286 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2287 $post->discussion.'#p'.$post->parent.'">'.$strparent.'</a>';
2291 $forumtype = get_field('forum', 'type', 'id', $post->forum);
2293 $age = time() - $post->created;
2294 // Hack for allow to edit news posts those are not displayed yet until they are displayed
2295 if (!$post->parent
2296 && $forumtype == 'news'
2297 && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) {
2298 $age = 0;
2300 $editanypost = has_capability('mod/forum:editanypost', $modcontext);
2304 if ($ownpost or $editanypost) {
2305 if (($age < $CFG->maxeditingtime) or $editanypost) {
2306 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?edit='.$post->id.'">'.$stredit.'</a>';
2310 if (has_capability('mod/forum:splitdiscussions', $modcontext)
2311 && $post->parent && $forumtype != 'single') {
2313 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?prune='.$post->id.
2314 '" title="'.$strpruneheading.'">'.$strprune.'</a>';
2317 if (($ownpost and $age < $CFG->maxeditingtime
2318 and has_capability('mod/forum:deleteownpost', $modcontext))
2319 or has_capability('mod/forum:deleteanypost', $modcontext)) {
2320 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?delete='.$post->id.'">'.$strdelete.'</a>';
2323 if ($reply) {
2324 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.$strreply.'</a>';
2327 echo '<div class="commands">';
2328 echo implode(' | ', $commands);
2329 echo '</div>';
2332 // Ratings
2334 $ratingsmenuused = false;
2335 if (!empty($ratings) and !empty($USER->id)) {
2336 echo '<div class="ratings">';
2337 $useratings = true;
2338 if ($ratings->assesstimestart and $ratings->assesstimefinish) {
2339 if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) {
2340 $useratings = false;
2343 if ($useratings) {
2344 $mypost = ($USER->id == $post->userid);
2346 $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext);
2348 if ($canviewallratings and !$mypost) {
2349 forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings);
2350 if (!empty($ratings->allow)) {
2351 echo '&nbsp;';
2352 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2353 $ratingsmenuused = true;
2356 } else if ($mypost) {
2357 forum_print_ratings_mean($post->id, $ratings->scale, true);
2359 } else if (!empty($ratings->allow) ) {
2360 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2361 $ratingsmenuused = true;
2364 echo '</div>';
2367 // Link to post if required
2369 if ($link) {
2370 echo '<div class="link">';
2371 if ($post->replies == 1) {
2372 $replystring = get_string('repliesone', 'forum', $post->replies);
2373 } else {
2374 $replystring = get_string('repliesmany', 'forum', $post->replies);
2376 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.
2377 get_string('discussthistopic', 'forum').'</a>&nbsp;('.$replystring.')';
2378 echo '</div>';
2381 if ($footer) {
2382 echo '<div class="footer">'.$footer.'</div>';
2384 echo '</td></tr></table>'."\n\n";
2386 if ($istracked && !$CFG->forum_usermarksread && !empty($post->forum)) {
2387 forum_tp_mark_post_read($USER->id, $post, $post->forum);
2390 return $ratingsmenuused;
2395 * This function prints the overview of a discussion in the forum listing.
2396 * It needs some discussion information and some post information, these
2397 * happen to be combined for efficiency in the $post parameter by the function
2398 * that calls this one: forum_print_latest_discussions()
2400 * @param object $post The post object (passed by reference for speed).
2401 * @param object $forum The forum object.
2402 * @param int $group Current group.
2403 * @param string $datestring Format to use for the dates.
2404 * @param boolean $cantrack Is tracking enabled for this forum.
2405 * @param boolean $forumtracked Is the user tracking this forum.
2406 * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
2408 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
2409 $cantrack=true, $forumtracked=true, $canviewparticipants=true) {
2411 global $USER, $CFG;
2413 static $rowcount;
2414 static $strmarkalldread;
2417 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2418 error('Course Module ID was incorrect');
2420 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2423 if (!isset($rowcount)) {
2424 $rowcount = 0;
2425 $strmarkalldread = get_string('markalldread', 'forum');
2426 } else {
2427 $rowcount = ($rowcount + 1) % 2;
2430 $post->subject = format_string($post->subject,true);
2432 echo "\n\n";
2433 echo '<tr class="discussion r'.$rowcount.'">';
2435 // Topic
2436 echo '<td class="topic starter">';
2437 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
2438 echo "</td>\n";
2440 // Picture
2441 echo '<td class="picture">';
2442 print_user_picture($post->userid, $forum->course, $post->picture);
2443 echo "</td>\n";
2445 // User name
2446 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2447 echo '<td class="author">';
2448 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
2449 echo "</td>\n";
2451 // Group picture
2452 if ($group !== -1) { // Groups are active - group is a group data object or NULL
2453 echo '<td class="picture group">';
2454 if (!empty($group->picture) and empty($group->hidepicture)) {
2455 print_group_picture($group, $forum->course, false, false, true);
2456 } else if (isset($group->id)) {
2457 if($canviewparticipants) {
2458 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
2459 } else {
2460 echo $group->name;
2463 echo "</td>\n";
2466 if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies
2467 echo '<td class="replies">';
2468 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2469 echo $post->replies.'</a>';
2470 echo "</td>\n";
2472 if ($cantrack) {
2473 echo '<td class="replies">';
2474 if ($forumtracked) {
2475 if ($post->unread > 0) {
2476 echo '<span class="unread">';
2477 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
2478 echo $post->unread;
2479 echo '</a>';
2480 echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
2481 $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
2482 '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
2483 echo '</span>';
2484 } else {
2485 echo '<span class="read">';
2486 echo $post->unread;
2487 echo '</span>';
2489 } else {
2490 echo '<span class="read">';
2491 echo '-';
2492 echo '</span>';
2494 echo "</td>\n";
2498 echo '<td class="lastpost">';
2499 $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case
2500 $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
2501 $usermodified->id = $post->usermodified;
2502 $usermodified->firstname = $post->umfirstname;
2503 $usermodified->lastname = $post->umlastname;
2504 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
2505 fullname($usermodified).'</a><br />';
2506 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
2507 userdate($usedate, $datestring).'</a>';
2508 echo "</td>\n";
2510 echo "</tr>\n\n";
2516 * Given a post object that we already know has a long message
2517 * this function truncates the message nicely to the first
2518 * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
2520 function forum_shorten_post($message) {
2522 global $CFG;
2524 $i = 0;
2525 $tag = false;
2526 $length = strlen($message);
2527 $count = 0;
2528 $stopzone = false;
2529 $truncate = 0;
2531 for ($i=0; $i<$length; $i++) {
2532 $char = $message[$i];
2534 switch ($char) {
2535 case "<":
2536 $tag = true;
2537 break;
2538 case ">":
2539 $tag = false;
2540 break;
2541 default:
2542 if (!$tag) {
2543 if ($stopzone) {
2544 if ($char == ".") {
2545 $truncate = $i+1;
2546 break 2;
2549 $count++;
2551 break;
2553 if (!$stopzone) {
2554 if ($count > $CFG->forum_shortpost) {
2555 $stopzone = true;
2560 if (!$truncate) {
2561 $truncate = $i;
2564 return substr($message, 0, $truncate);
2569 * Print the multiple ratings on a post given to the current user by others.
2570 * Scale is an array of ratings
2572 function forum_print_ratings_mean($postid, $scale, $link=true) {
2574 static $strrate;
2576 $mean = forum_get_ratings_mean($postid, $scale);
2578 if ($mean !== "") {
2580 if (empty($strratings)) {
2581 $strratings = get_string("ratings", "forum");
2584 echo "$strratings: ";
2585 if ($link) {
2586 link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600);
2587 } else {
2588 echo "$mean ";
2595 * Return the mean rating of a post given to the current user by others.
2596 * Scale is an array of possible ratings in the scale
2597 * Ratings is an optional simple array of actual ratings (just integers)
2599 function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
2601 if (!$ratings) {
2602 $ratings = array();
2603 if ($rates = get_records("forum_ratings", "post", $postid)) {
2604 foreach ($rates as $rate) {
2605 $ratings[] = $rate->rating;
2610 $count = count($ratings);
2612 if ($count == 0) {
2613 return "";
2615 } else if ($count == 1) {
2616 return $scale[$ratings[0]];
2618 } else {
2619 $total = 0;
2620 foreach ($ratings as $rating) {
2621 $total += $rating;
2623 $mean = round( ((float)$total/(float)$count) + 0.001); // Little fudge factor so that 0.5 goes UP
2625 if (isset($scale[$mean])) {
2626 return $scale[$mean]." ($count)";
2627 } else {
2628 return "$mean ($count)"; // Should never happen, hopefully
2634 * Return a summary of post ratings given to the current user by others.
2635 * Scale is an array of possible ratings in the scale
2636 * Ratings is an optional simple array of actual ratings (just integers)
2638 function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
2640 if (!$ratings) {
2641 $ratings = array();
2642 if ($rates = get_records("forum_ratings", "post", $postid)) {
2643 foreach ($rates as $rate) {
2644 $rating[] = $rate->rating;
2650 if (!$count = count($ratings)) {
2651 return "";
2655 foreach ($scale as $key => $scaleitem) {
2656 $sumrating[$key] = 0;
2659 foreach ($ratings as $rating) {
2660 $sumrating[$rating]++;
2663 $summary = "";
2664 foreach ($scale as $key => $scaleitem) {
2665 $summary = $sumrating[$key].$summary;
2666 if ($key > 1) {
2667 $summary = "/$summary";
2670 return $summary;
2674 * Print the menu of ratings as part of a larger form.
2675 * If the post has already been - set that value.
2676 * Scale is an array of ratings
2678 function forum_print_rating_menu($postid, $userid, $scale) {
2680 static $strrate;
2682 if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
2683 $rating->rating = FORUM_UNSET_POST_RATING;
2686 if (empty($strrate)) {
2687 $strrate = get_string("rate", "forum");
2689 $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale;
2690 choose_from_menu($scale, $postid, $rating->rating, '');
2694 * Print the drop down that allows the user to select how they want to have
2695 * the discussion displayed.
2696 * @param $id - forum id if $forumtype is 'single',
2697 * discussion id for any other forum type
2698 * @param $mode - forum layout mode
2699 * @param $forumtype - optional
2701 function forum_print_mode_form($id, $mode, $forumtype='') {
2702 global $FORUM_LAYOUT_MODES;
2704 if ($forumtype == 'single') {
2705 popup_form("view.php?f=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2706 } else {
2707 popup_form("discuss.php?d=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2714 function forum_search_form($course, $search='') {
2715 global $CFG;
2717 $output = '<div class="forumsearch">';
2718 $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
2719 $output .= '<fieldset class="invisiblefieldset">';
2720 $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
2721 $output .= '<input name="search" type="text" size="18" value="'.$search.'" alt="search" />';
2722 $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
2723 $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
2724 $output .= '</fieldset>';
2725 $output .= '</form>';
2726 $output .= '</div>';
2728 return $output;
2735 function forum_set_return() {
2736 global $CFG, $SESSION;
2738 if (! isset($SESSION->fromdiscussion)) {
2739 if (!empty($_SERVER['HTTP_REFERER'])) {
2740 $referer = $_SERVER['HTTP_REFERER'];
2741 } else {
2742 $referer = "";
2744 // If the referer is NOT a login screen then save it.
2745 if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
2746 $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
2755 function forum_go_back_to($default) {
2756 global $SESSION;
2758 if (!empty($SESSION->fromdiscussion)) {
2759 $returnto = $SESSION->fromdiscussion;
2760 unset($SESSION->fromdiscussion);
2761 return $returnto;
2762 } else {
2763 return $default;
2768 * Creates a directory file name, suitable for make_upload_directory()
2770 function forum_file_area_name($post) {
2771 global $CFG;
2773 return "$post->course/$CFG->moddata/forum/$post->forum/$post->id";
2779 function forum_file_area($post) {
2780 return make_upload_directory( forum_file_area_name($post) );
2786 function forum_delete_old_attachments($post, $exception="") {
2789 * Deletes all the user files in the attachments area for a post
2790 * EXCEPT for any file named $exception
2792 if ($basedir = forum_file_area($post)) {
2793 if ($files = get_directory_list($basedir)) {
2794 foreach ($files as $file) {
2795 if ($file != $exception) {
2796 unlink("$basedir/$file");
2797 notify("Existing file '$file' has been deleted!");
2801 if (!$exception) { // Delete directory as well, if empty
2802 rmdir("$basedir");
2808 * Given a discussion object that is being moved to forumid,
2809 * this function checks all posts in that discussion
2810 * for attachments, and if any are found, these are
2811 * moved to the new forum directory.
2813 function forum_move_attachments($discussion, $forumid) {
2815 global $CFG;
2817 require_once($CFG->dirroot.'/lib/uploadlib.php');
2819 $return = true;
2821 if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
2822 foreach ($posts as $oldpost) {
2823 $oldpost->course = $discussion->course;
2824 $oldpost->forum = $discussion->forum;
2825 $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
2826 if (is_dir($oldpostdir)) {
2827 $newpost = $oldpost;
2828 $newpost->forum = $forumid;
2829 $newpostdir = forum_file_area_name($newpost);
2830 // take off the last directory because otherwise we're renaming to a directory that already exists
2831 // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
2832 make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
2833 $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost);
2834 $files = get_directory_list($oldpostdir); // get it before we rename it.
2835 if (! @rename($oldpostdir, $newpostdir)) {
2836 $return = false;
2838 foreach ($files as $file) {
2839 clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
2844 return $return;
2848 * if return=html, then return a html string.
2849 * if return=text, then return a text-only string.
2850 * otherwise, print HTML for non-images, and return image HTML
2852 function forum_print_attachments($post, $return=NULL) {
2854 global $CFG;
2856 $filearea = forum_file_area_name($post);
2858 $imagereturn = "";
2859 $output = "";
2861 if ($basedir = forum_file_area($post)) {
2862 if ($files = get_directory_list($basedir)) {
2863 $strattachment = get_string("attachment", "forum");
2864 foreach ($files as $file) {
2865 $icon = mimeinfo("icon", $file);
2866 $type = mimeinfo("type", $file);
2867 if ($CFG->slasharguments) {
2868 $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
2869 } else {
2870 $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
2872 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
2874 if ($return == "html") {
2875 $output .= "<a href=\"$ffurl\">$image</a> ";
2876 $output .= "<a href=\"$ffurl\">$file</a><br />";
2878 } else if ($return == "text") {
2879 $output .= "$strattachment $file:\n$ffurl\n";
2881 } else {
2882 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
2883 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
2884 } else {
2885 echo "<a href=\"$ffurl\">$image</a> ";
2886 echo filter_text("<a href=\"$ffurl\">$file</a><br />");
2893 if ($return) {
2894 return $output;
2897 return $imagereturn;
2900 * If successful, this function returns the name of the file
2901 * @param $post is a full post record, including course and forum
2902 * @param $newfile is a full upload array from $_FILES
2903 * @param $message is a string to hold the messages.
2909 function forum_add_attachment($post, $inputname,&$message) {
2911 global $CFG;
2913 if (!$forum = get_record("forum", "id", $post->forum)) {
2914 return "";
2917 if (!$course = get_record("course", "id", $forum->course)) {
2918 return "";
2921 require_once($CFG->dirroot.'/lib/uploadlib.php');
2922 $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true);
2923 $dir = forum_file_area_name($post);
2924 if ($um->process_file_uploads($dir)) {
2925 $message .= $um->get_errors();
2926 return $um->get_new_filename();
2928 $message .= $um->get_errors();
2929 return null;
2935 function forum_add_new_post($post,&$message) {
2937 global $USER, $CFG;
2939 $post->created = $post->modified = time();
2940 $post->mailed = "0";
2941 $post->userid = $USER->id;
2942 $post->attachment = "";
2944 if (! $post->id = insert_record("forum_posts", $post)) {
2945 return false;
2948 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
2949 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id);
2952 // Update discussion modified date
2953 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2954 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2956 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2957 forum_tp_mark_post_read($post->userid, $post, $post->forum);
2960 return $post->id;
2966 function forum_update_post($post,&$message) {
2968 global $USER, $CFG;
2970 $post->modified = time();
2972 if (!$post->parent) { // Post is a discussion starter - update discussion title too
2973 set_field("forum_discussions", "name", $post->subject, "id", $post->discussion);
2976 if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
2977 $post->attachment = $newfilename;
2978 } else {
2979 unset($post->attachment);
2982 // Update discussion modified date
2983 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2984 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2986 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2987 forum_tp_mark_post_read($post->userid, $post, $post->forum);
2990 return update_record("forum_posts", $post);
2994 * Given an object containing all the necessary data,
2995 * create a new discussion and return the id
2997 function forum_add_discussion($discussion,&$message) {
2999 GLOBAL $USER, $CFG;
3001 $timenow = time();
3003 // The first post is stored as a real post, and linked
3004 // to from the discuss entry.
3006 $post->discussion = 0;
3007 $post->parent = 0;
3008 $post->userid = $USER->id;
3009 $post->created = $timenow;
3010 $post->modified = $timenow;
3011 $post->mailed = 0;
3012 $post->subject = $discussion->name;
3013 $post->message = $discussion->intro;
3014 $post->attachment = "";
3015 $post->forum = $discussion->forum;
3016 $post->course = $discussion->course;
3017 $post->format = $discussion->format;
3018 $post->mailnow = $discussion->mailnow;
3020 if (! $post->id = insert_record("forum_posts", $post) ) {
3021 return 0;
3024 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
3025 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors
3028 // Now do the main entry for the discussion,
3029 // linking to this first post
3031 $discussion->firstpost = $post->id;
3032 $discussion->timemodified = $timenow;
3033 $discussion->usermodified = $post->userid;
3034 $discussion->userid = $USER->id;
3036 if (! $post->discussion = insert_record("forum_discussions", $discussion) ) {
3037 delete_records("forum_posts", "id", $post->id);
3038 return 0;
3041 // Finally, set the pointer on the post.
3042 if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) {
3043 delete_records("forum_posts", "id", $post->id);
3044 delete_records("forum_discussions", "id", $post->discussion);
3045 return 0;
3048 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
3049 forum_tp_mark_post_read($post->userid, $post, $post->forum);
3052 return $post->discussion;
3059 function forum_delete_discussion($discussion, $fulldelete=false) {
3060 // $discussion is a discussion record object
3062 $result = true;
3064 if ($posts = get_records("forum_posts", "discussion", $discussion->id)) {
3065 foreach ($posts as $post) {
3066 $post->course = $discussion->course;
3067 $post->forum = $discussion->forum;
3068 if (! delete_records("forum_ratings", "post", "$post->id")) {
3069 $result = false;
3071 if (! forum_delete_post($post, $fulldelete)) {
3072 $result = false;
3077 forum_tp_delete_read_records(-1, -1, $discussion->id);
3079 if (! delete_records("forum_discussions", "id", "$discussion->id")) {
3080 $result = false;
3083 return $result;
3090 function forum_delete_post($post, $children=false) {
3091 if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
3092 if ($children) {
3093 foreach ($childposts as $childpost) {
3094 forum_delete_post($childpost, true);
3096 } else {
3097 return false;
3100 if (delete_records("forum_posts", "id", $post->id)) {
3101 delete_records("forum_ratings", "post", $post->id); // Just in case
3103 forum_tp_delete_read_records(-1, $post->id);
3105 if ($post->attachment) {
3106 $discussion = get_record("forum_discussions", "id", $post->discussion);
3107 $post->course = $discussion->course;
3108 $post->forum = $discussion->forum;
3109 forum_delete_old_attachments($post);
3112 // Just in case we are deleting the last post
3113 forum_discussion_update_last_post($post->discussion);
3115 return true;
3117 return false;
3123 function forum_count_replies($post, $children=true) {
3124 $count = 0;
3126 if ($children) {
3127 if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
3128 foreach ($childposts as $childpost) {
3129 $count ++; // For this child
3130 $count += forum_count_replies($childpost, true);
3133 } else {
3134 $count += count_records('forum_posts', 'parent', $post->id);
3137 return $count;
3144 function forum_forcesubscribe($forumid, $value=1) {
3145 return set_field("forum", "forcesubscribe", $value, "id", $forumid);
3151 function forum_is_forcesubscribed($forumid) {
3152 return (get_field("forum", "forcesubscribe", "id", $forumid) == 1);
3158 function forum_is_subscribed($userid, $forumid) {
3159 if (forum_is_forcesubscribed($forumid)) {
3160 return true;
3162 return record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid);
3166 * Adds user to the subscriber list
3168 function forum_subscribe($userid, $forumid) {
3170 if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) {
3171 return true;
3174 $sub->userid = $userid;
3175 $sub->forum = $forumid;
3177 return insert_record("forum_subscriptions", $sub);
3181 * Removes user from the subscriber list
3183 function forum_unsubscribe($userid, $forumid) {
3184 return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid);
3188 * Given a new post, subscribes or unsubscribes as appropriate.
3189 * Returns some text which describes what happened.
3191 function forum_post_subscription($post) {
3193 global $USER;
3195 $subscribed=forum_is_subscribed($USER->id, $post->forum);
3196 if ((isset($post->subscribe) && $post->subscribe && $subscribed)
3197 || (!$post->subscribe && !$subscribed)) {
3198 return "";
3201 if (!$forum = get_record("forum", "id", $post->forum)) {
3202 return "";
3205 $info->name = fullname($USER);
3206 $info->forum = $forum->name;
3208 if (!empty($post->subscribe)) {
3209 forum_subscribe($USER->id, $post->forum);
3210 return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
3213 forum_unsubscribe($USER->id, $post->forum);
3214 return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
3218 * Generate and return the subscribe or unsubscribe link for a forum.
3219 * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
3220 * @param object $context the context object for this forum.
3221 * @param array $messages text used for the link in its various states
3222 * (subscribed, unsubscribed, forcesubscribed or cantsubscribe).
3223 * Any strings not passed in are taken from the $defaultmessages array
3224 * at the top of the function.
3225 * @param
3227 function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false) {
3228 global $CFG, $USER;
3229 $defaultmessages = array(
3230 'subscribed' => get_string('unsubscribe', 'forum'),
3231 'unsubscribed' => get_string('subscribe', 'forum'),
3232 'cantaccessgroup' => get_string('no'),
3233 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
3234 'cantsubscribe' => get_string('disallowsubscribe','forum')
3236 $messages = $messages + $defaultmessages;
3238 if (forum_is_forcesubscribed($forum->id)) {
3239 return $messages['forcesubscribed'];
3240 } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) {
3241 return $messages['cantsubscribe'];
3242 } else if ($cantaccessagroup) {
3243 return $messages['cantaccessgroup'];
3244 } else {
3245 if (forum_is_subscribed($USER->id, $forum->id)) {
3246 $linktext = $messages['subscribed'];
3247 $linktitle = get_string('subscribestopt', 'forum');
3248 } else {
3249 $linktext = $messages['unsubscribed'];
3250 $linktitle = get_string('subscribestart', 'forum');
3252 return print_single_button($CFG->wwwroot . '/mod/forum/subscribe.php?id=' . $forum->id,
3253 '', $linktext, 'post', '_self', true, $linktitle);
3260 function forum_user_has_posted_discussion($forumid, $userid) {
3261 if ($discussions = forum_get_discussions($forumid, '', $userid)) {
3262 return true;
3263 } else {
3264 return false;
3271 function forum_discussions_user_has_posted_in($forumid, $userid) {
3272 global $CFG;
3274 $haspostedsql = "SELECT d.id AS id,
3276 FROM {$CFG->prefix}forum_posts p,
3277 {$CFG->prefix}forum_discussions d
3278 WHERE p.discussion = d.id
3279 AND d.forum = $forumid
3280 AND p.userid = $userid";
3282 return get_records_sql($haspostedsql);
3288 function forum_user_has_posted($forumid, $did, $userid) {
3289 return record_exists('forum_posts','discussion',$did,'userid',$userid);
3295 function forum_user_can_post_discussion($forum, $currentgroup=-1, $groupmode=-1, $cm=NULL, $context=NULL) {
3296 // $forum is an object
3297 global $USER, $SESSION;
3299 if (!$cm) {
3300 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3301 error('Course Module ID was incorrect');
3304 if (!$context) {
3305 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3308 if ($currentgroup == -1) {
3309 $currentgroup = get_current_group($cm->course);
3312 if ($groupmode == -1) {
3313 if (!$course = get_record('course', 'id', $cm->course)) {
3314 error('Can not find course');
3316 $groupmode = groupmode($course, $cm);
3319 if ($forum->type == 'news') {
3320 $capname = 'mod/forum:addnews';
3321 } else {
3322 $capname = 'mod/forum:startdiscussion';
3325 if (!has_capability($capname, $context)) {
3326 return false;
3329 if ($forum->type == 'eachuser') {
3330 if (forum_user_has_posted_discussion($forum->id, $USER->id)) {
3331 return false;
3335 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
3336 return true;
3339 if ($currentgroup) {
3340 return ismember($currentgroup);
3341 } else {
3342 //else it might be group 0 in visible mode
3343 if ($groupmode == VISIBLEGROUPS){
3344 return true;
3345 } else {
3346 return false;
3352 * This function checks whether the user can reply to posts in a forum
3353 * discussion. Use forum_user_can_post_discussion() to check whether the user
3354 * can start dicussions.
3355 * @param $forum - forum object
3356 * @param $user - user object
3358 function forum_user_can_post($forum, $user=NULL, $cm=NULL, $context=NULL) {
3360 if (!$cm) {
3361 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3362 error('Course Module ID was incorrect');
3365 if (!$context) {
3366 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3369 if ($forum->type == 'news') {
3370 $capname = 'mod/forum:replynews';
3371 } else {
3372 $capname = 'mod/forum:replypost';
3375 if (!empty($user)) {
3376 $canreply = has_capability($capname, $context, $user->id, false)
3377 && !has_capability('moodle/legacy:guest', $context, $user->id, false);
3378 } else {
3379 $canreply = has_capability($capname, $context, NULL, false)
3380 && !has_capability('moodle/legacy:guest', $context, NULL, false);
3383 return $canreply;
3387 //checks to see if a user can view a particular post
3388 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
3390 global $CFG, $USER;
3392 if (!$user){
3393 $user = $USER;
3396 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3397 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
3398 return false;
3401 // If it's a grouped discussion, make sure the user is a member
3402 if ($discussion->groupid > 0) {
3403 $groupmode = groupmode($course, $cm);
3404 if ($groupmode == SEPARATEGROUPS) {
3405 return ismember($discussion->groupid) || has_capability('moodle/site:accessallgroups', $modcontext);
3408 return true;
3415 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
3416 global $USER;
3418 if (empty($user) || empty($user->id)) {
3419 $user = $USER;
3422 // retrieve objects (yuk)
3423 if (is_numeric($forum)) {
3424 if (!$forum = get_record('forum','id',$forum)) {
3425 return false;
3428 if (is_numeric($discussion)) {
3429 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3430 return false;
3434 if (!has_capability('mod/forum:viewdiscussion', $context)) {
3435 return false;
3438 if ($forum->type == 'qanda' &&
3439 !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
3440 !has_capability('mod/forum:viewqandawithoutposting', $context)) {
3441 return false;
3443 return true;
3450 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL) {
3451 global $USER;
3453 // retrieve objects (yuk)
3454 if (is_numeric($forum)) {
3455 if (!$forum = get_record('forum','id',$forum)) {
3456 return false;
3460 if (is_numeric($discussion)) {
3461 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3462 return false;
3465 if (is_numeric($post)) {
3466 if (!$post = get_record('forum_posts','id',$post)) {
3467 return false;
3470 if (!isset($post->id) && isset($post->parent)) {
3471 $post->id = $post->parent;
3474 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3475 error('Course Module ID was incorrect');
3477 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3479 if (empty($user) || empty($user->id)) {
3480 $user = $USER;
3483 if (!has_capability('mod/forum:viewdiscussion', $context, $user->id)) {
3484 return false;
3487 if ($forum->type == 'qanda') {
3488 $firstpost = forum_get_firstpost_from_discussion($discussion->id);
3490 return (forum_user_has_posted($forum->id,$discussion->id,$user->id) ||
3491 $firstpost->id == $post->id ||
3492 has_capability('mod/forum:viewqandawithoutposting', $context, false, $user->id));
3494 return true;
3499 * Prints the discussion view screen for a forum.
3501 * @param object $course The current course object.
3502 * @param object $forum Forum to be printed.
3503 * @param int $maxdiscussions The maximum number of discussions per page(optional).
3504 * @param string $displayformat The display format to use (optional).
3505 * @param string $sort Sort arguments for database query (optional).
3506 * @param int $currentgroup Group to display discussions for (optional).
3507 * @param int $groupmode Group mode of the forum (optional).
3508 * @param int $page Page mode, page to display (optional).
3511 function forum_print_latest_discussions($course, $forum, $maxdiscussions=5, $displayformat='plain', $sort='',
3512 $currentgroup=-1, $groupmode=-1, $page=-1) {
3513 global $CFG, $USER;
3515 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3516 error('Course Module ID was incorrect');
3518 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3521 // Sort out some defaults
3523 if ((!$maxdiscussions) && ($displayformat == 'plain')) {
3524 $displayformat = 'header'; // Abbreviate display by default
3527 $fullpost = false;
3528 if ($displayformat == 'plain') {
3529 $fullpost = true;
3533 // Decide if current user is allowed to see ALL the current discussions or not
3535 // First check the group stuff
3536 $groupmode = groupmode($course, $cm);
3537 $currentgroup = get_and_set_current_group($course, $groupmode);
3539 // If the user can post discussions, then this is a good place to put the
3540 // button for it. We do not show the button if we are showing site news
3541 // and the current user is a guest.
3543 if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context) ||
3544 ($forum->type != 'news' && has_capability('moodle/legacy:guest', $context, NULL, false)) ) {
3546 echo '<div class="singlebutton forumaddnew">';
3547 echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
3548 echo '<div>';
3549 echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
3550 echo '<input type="submit" value="';
3551 echo ($forum->type == 'news') ? get_string('addanewtopic', 'forum')
3552 : (($forum->type == 'qanda')
3553 ? get_string('addanewquestion','forum')
3554 : get_string('addanewdiscussion', 'forum'));
3555 echo '" />';
3556 echo '</div>';
3557 echo '</form>';
3558 echo "</div>\n";
3559 } else if (!isguestuser() and isloggedin() and $forum->type != 'news' and $groupmode == SEPARATEGROUPS and !ismember($currentgroup)) {
3560 notify(get_string('cannotadddiscussion', 'forum'));
3564 // Get all the recent discussions we're allowed to see
3566 $getuserlastmodified = ($displayformat == 'header');
3568 if (! $discussions = forum_get_discussions($forum->id, $sort, 0, $fullpost, $currentgroup,0,$getuserlastmodified) ) {
3569 echo '<div class="forumnodiscuss">';
3570 if ($forum->type == 'news') {
3571 echo '('.get_string('nonews', 'forum').')';
3572 } else if ($forum->type == 'qanda') {
3573 echo '('.get_string('noquestions','forum').')';
3574 } else {
3575 echo '('.get_string('nodiscussions', 'forum').')';
3577 echo "</div>\n";
3578 return;
3581 // If no discussions then don't use paging (to avoid some divide by 0 errors)
3583 if ($maxdiscussions <= 0) {
3584 $page = -1;
3585 $maxdiscussions = 0;
3588 // If we want paging
3590 if ($page != -1) {
3591 ///Get the number of discussions found
3592 $numdiscussions = count($discussions);
3594 ///Show the paging bar
3595 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3597 //Calculate the page "window"
3598 $pagestart = ($page * $maxdiscussions) + 1;
3599 $pageend = $pagestart + $maxdiscussions - 1;
3603 $replies = forum_count_discussion_replies($forum->id);
3605 $canreply = forum_user_can_post($forum);
3606 $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
3608 $discussioncount = 0;
3609 $olddiscussionlink = false;
3610 $strdatestring = get_string('strftimerecentfull');
3612 // Check if the forum is tracked.
3613 if ($cantrack = forum_tp_can_track_forums($forum)) {
3614 $forumtracked = forum_tp_is_tracked($forum);
3615 } else {
3616 $forumtracked = false;
3619 if ($displayformat == 'header') {
3620 echo '<table cellspacing="0" class="forumheaderlist">';
3621 echo '<thead>';
3622 echo '<tr>';
3623 echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
3624 echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
3625 if ($groupmode > 0) {
3626 echo '<th class="header group" scope="col">'.get_string('group').'</th>';
3628 if (has_capability('mod/forum:viewdiscussion', $context)) {
3629 echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
3630 // If the forum can be tracked, display the unread column.
3631 if ($cantrack) {
3632 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
3633 if ($forumtracked) {
3634 echo '&nbsp;<a title="'.get_string('markallread', 'forum').
3635 '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
3636 $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
3637 '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
3639 echo '</th>';
3642 echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
3643 echo '</tr>';
3644 echo '</thead>';
3645 echo '<tbody>';
3648 foreach ($discussions as $discussion) {
3649 $discussioncount++;
3651 if ($page != -1) { // We are using paging
3652 if ($discussioncount < $pagestart) { // Not there yet
3653 continue;
3655 if ($discussioncount > $pageend) { // All done, finish the loop
3656 break;
3658 //Without paging, old approach
3659 } else if ($maxdiscussions && ($discussioncount > $maxdiscussions)) {
3660 $olddiscussionlink = true;
3661 break;
3664 if (!empty($replies[$discussion->discussion])) {
3665 $discussion->replies = $replies[$discussion->discussion]->replies;
3666 $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
3667 } else {
3668 $discussion->replies = 0;
3671 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
3672 // All posts are read in this case.
3673 if (!$forumtracked) {
3674 $discussion->unread = '-';
3675 } else if (empty($USER)) {
3676 $discussion->unread = 0;
3677 } else {
3678 $discussion->unread = forum_tp_count_discussion_unread_posts($USER->id, $discussion->discussion);
3681 if (!empty($USER->id)) {
3682 $ownpost = ($discussion->userid == $USER->id);
3683 } else {
3684 $ownpost=false;
3686 // Use discussion name instead of subject of first post
3687 $discussion->subject = $discussion->name;
3689 switch ($displayformat) {
3690 case 'header':
3691 if ($groupmode > 0) {
3692 if (isset($groups[$discussion->groupid])) {
3693 $group = $groups[$discussion->groupid];
3694 } else {
3695 $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid); //TODO:
3697 } else {
3698 $group = -1;
3700 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
3701 $canviewparticipants);
3702 break;
3703 default:
3704 if ($canreply or $discussion->replies) {
3705 $link = true;
3706 } else {
3707 $link = false;
3710 $discussion->forum = $forum->id;
3712 forum_print_post($discussion, $course->id, $ownpost, $reply=0, $link, $assessed=false);
3713 break;
3717 if ($displayformat == "header") {
3718 echo '</tbody>';
3719 echo '</table>';
3722 if ($olddiscussionlink) {
3723 echo '<div class="forumolddiscuss">';
3724 echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
3725 echo get_string('olderdiscussions', 'forum').'</a> ...</div>';
3728 if ($page != -1) { ///Show the paging bar
3729 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3737 function forum_print_discussion($course, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
3739 global $USER, $CFG;
3741 if (!empty($USER->id)) {
3742 $ownpost = ($USER->id == $post->userid);
3743 } else {
3744 $ownpost = false;
3746 if ($canreply === NULL) {
3747 $reply = forum_user_can_post($forum);
3748 } else {
3749 $reply = $canreply;
3752 $ratings = NULL;
3753 $ratingsmenuused = false;
3754 $ratingsformused = false;
3755 if ($forum->assessed and !empty($USER->id)) {
3756 if ($ratings->scale = make_grades_menu($forum->scale)) {
3757 $ratings->assesstimestart = $forum->assesstimestart;
3758 $ratings->assesstimefinish = $forum->assesstimefinish;
3759 $ratings->allow = $canrate;
3761 if ($ratings->allow) {
3762 echo '<form id="form" method="post" action="rate.php">';
3763 echo '<div class="ratingform">';
3764 echo '<input type="hidden" name="forumid" value="'.$forum->id.'" />';
3765 $ratingsformused = true;
3770 $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post
3771 $post->forumtype = $forum->type;
3773 $post->subject = format_string($post->subject);
3775 if (forum_tp_can_track_forums($forum)) {
3776 if ($forumtracked = forum_tp_is_tracked($forum)) {
3777 $user_read_array = forum_tp_get_discussion_read_records($USER->id, $post->discussion);
3778 } else {
3779 $user_read_array = array();
3781 } else {
3782 $forumtracked = false;
3783 $user_read_array = array();
3786 if (forum_print_post($post, $course->id, $ownpost, $reply, $link=false, $ratings,
3787 '', '', (!$forumtracked || isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3788 $ratingsmenuused = true;
3791 switch ($mode) {
3792 case FORUM_MODE_FLATOLDEST :
3793 case FORUM_MODE_FLATNEWEST :
3794 default:
3795 if (forum_print_posts_flat($post->discussion, $course->id, $mode, $ratings, $reply,
3796 $user_read_array, $post->forum)) {
3797 $ratingsmenuused = true;
3799 break;
3801 case FORUM_MODE_THREADED :
3802 if (forum_print_posts_threaded($post->id, $course->id, 0, $ratings, $reply,
3803 $user_read_array, $post->forum)) {
3804 $ratingsmenuused = true;
3806 break;
3808 case FORUM_MODE_NESTED :
3809 if (forum_print_posts_nested($post->id, $course->id, $ratings, $reply,
3810 $user_read_array, $post->forum)) {
3811 $ratingsmenuused = true;
3813 break;
3816 if ($ratingsformused) {
3817 if ($ratingsmenuused) {
3818 echo '<div class="ratingsubmit">';
3819 echo '<input type="submit" value="'.get_string('sendinratings', 'forum').'" />';
3820 if ($forum->scale < 0) {
3821 if ($scale = get_record("scale", "id", abs($forum->scale))) {
3822 print_scale_menu_helpbutton($course->id, $scale );
3825 echo '</div>';
3828 echo '</div>';
3829 echo '</form>';
3837 function forum_print_posts_flat($discussion, $courseid, $direction, $ratings, $reply, &$user_read_array, $forumid=0) {
3838 global $USER, $CFG;
3840 $link = false;
3841 $ratingsmenuused = false;
3843 if ($direction < 0) {
3844 $sort = "ORDER BY created DESC";
3845 } else {
3846 $sort = "ORDER BY created ASC";
3849 if ($posts = forum_get_discussion_posts($discussion, $sort, $forumid)) {
3850 foreach ($posts as $post) {
3852 $post->subject = format_string($post->subject);
3854 $ownpost = ($USER->id == $post->userid);
3855 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3856 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3857 $ratingsmenuused = true;
3862 return $ratingsmenuused;
3867 * TODO document
3869 function forum_print_posts_threaded($parent, $courseid, $depth, $ratings, $reply, &$user_read_array, $forumid=0) {
3870 global $USER, $CFG;
3872 $link = false;
3873 $ratingsmenuused = false;
3875 $istracking = forum_tp_can_track_forums($forumid) && forum_tp_is_tracked($forumid);
3877 if ($posts = forum_get_child_posts($parent, $forumid)) {
3879 if (!$cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
3880 error('Course Module ID was incorrect');
3882 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3883 $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
3885 foreach ($posts as $post) {
3887 echo '<div class="indent">';
3888 if ($depth > 0) {
3889 $ownpost = ($USER->id == $post->userid);
3891 $post->subject = format_string($post->subject);
3893 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3894 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3895 $ratingsmenuused = true;
3897 } else {
3898 if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
3899 continue;
3901 $by->name = fullname($post, $canviewfullnames);
3902 $by->date = userdate($post->modified);
3904 if ($istracking) {
3905 if (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)) {
3906 $style = '<span class="forumthread read">';
3907 } else {
3908 $style = '<span class="forumthread unread">';
3910 } else {
3911 $style = '<span class="forumthread">';
3913 echo $style."<a name=\"$post->id\"></a>".
3914 "<a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a> ";
3915 print_string("bynameondate", "forum", $by);
3916 echo "</span>";
3919 if (forum_print_posts_threaded($post->id, $courseid, $depth-1, $ratings, $reply,
3920 $user_read_array, $forumid)) {
3921 $ratingsmenuused = true;
3923 echo "</div>\n";
3926 return $ratingsmenuused;
3932 function forum_print_posts_nested($parent, $courseid, $ratings, $reply, &$user_read_array, $forumid=0) {
3933 global $USER, $CFG;
3935 $link = false;
3936 $ratingsmenuused = false;
3938 if ($posts = forum_get_child_posts($parent, $forumid)) {
3939 foreach ($posts as $post) {
3941 echo '<div class="indent">';
3942 if (empty($USER->id)) {
3943 $ownpost = false;
3944 } else {
3945 $ownpost = ($USER->id == $post->userid);
3948 $post->subject = format_string($post->subject);
3950 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3951 '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3952 $ratingsmenuused = true;
3954 if (forum_print_posts_nested($post->id, $courseid, $ratings, $reply, $user_read_array, $forumid)) {
3955 $ratingsmenuused = true;
3957 echo "</div>\n";
3960 return $ratingsmenuused;
3964 * TODO document
3966 function forum_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="0", $user="", $groupid="") {
3967 // Returns all forum posts since a given time. If forum is specified then
3968 // this restricts the results
3970 global $CFG;
3972 if ($cmid) {
3973 $forumselect = " AND cm.id = '$cmid'";
3974 } else {
3975 $forumselect = "";
3978 if ($user) {
3979 $userselect = " AND u.id = '$user'";
3980 } else {
3981 $userselect = "";
3984 $posts = get_records_sql("SELECT p.*, d.name, u.firstname, u.lastname,
3985 u.picture, d.groupid, cm.instance, f.name,
3986 cm.section, cm.id AS cmid
3987 FROM {$CFG->prefix}forum_posts p,
3988 {$CFG->prefix}forum_discussions d,
3989 {$CFG->prefix}user u,
3990 {$CFG->prefix}course_modules cm,
3991 {$CFG->prefix}forum f
3992 WHERE p.modified > '$sincetime' $forumselect
3993 AND p.userid = u.id $userselect
3994 AND d.course = '$courseid'
3995 AND p.discussion = d.id
3996 AND cm.instance = f.id
3997 AND cm.course = d.course
3998 AND cm.course = f.course
3999 AND f.id = d.forum
4000 ORDER BY p.discussion ASC,p.created ASC");
4002 if (empty($posts)) {
4003 return;
4006 foreach ($posts as $post) {
4008 $modcontext = get_context_instance(CONTEXT_MODULE, $post->cmid);
4009 $canviewallgroups = has_capability('moodle/site:accessallgroups', $modcontext);
4011 if ($groupid and ($post->groupid != -1 and $groupid != $post->groupid and !$canviewallgroups)) {
4012 continue;
4015 $tmpactivity = new Object;
4017 $tmpactivity->type = "forum";
4018 $tmpactivity->defaultindex = $index;
4019 $tmpactivity->instance = $post->instance;
4020 $tmpactivity->name = $post->name;
4021 $tmpactivity->section = $post->section;
4023 $tmpactivity->content->id = $post->id;
4024 $tmpactivity->content->discussion = $post->discussion;
4025 $tmpactivity->content->subject = $post->subject;
4026 $tmpactivity->content->parent = $post->parent;
4028 $tmpactivity->user->userid = $post->userid;
4029 $tmpactivity->user->fullname = fullname($post);
4030 $tmpactivity->user->picture = $post->picture;
4032 $tmpactivity->timestamp = $post->modified;
4033 $activities[] = $tmpactivity;
4035 $index++;
4038 return;
4044 function forum_print_recent_mod_activity($activity, $course, $detail=false) {
4046 global $CFG;
4048 echo '<table border="0" cellpadding="3" cellspacing="0">';
4050 if ($activity->content->parent) {
4051 $openformat = "<font size=\"2\"><i>";
4052 $closeformat = "</i></font>";
4053 } else {
4054 $openformat = "<b>";
4055 $closeformat = "</b>";
4058 echo "<tr><td class=\"forumpostpicture\" width=\"35\" valign=\"top\">";
4059 print_user_picture($activity->user->userid, $course, $activity->user->picture);
4060 echo "</td><td>$openformat";
4062 if ($detail) {
4063 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
4064 "class=\"icon\" alt=\"".strip_tags(format_string($activity->name,true))."\" /> ";
4066 echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d=" . $activity->content->discussion
4067 . "#p" . $activity->content->id . "\">";
4069 echo format_string($activity->content->subject,true);
4070 echo "</a>$closeformat";
4072 echo "<br /><font size=\"2\">";
4073 echo "<a href=\"$CFG->wwwroot/user/view.php?id=" . $activity->user->userid . "&amp;course=" . "$course\">"
4074 . $activity->user->fullname . "</a>";
4075 echo " - " . userdate($activity->timestamp) . "</font></td></tr>";
4076 echo "</table>";
4078 return;
4082 * recursively sets the discussion field to $discussionid on $postid and all its children
4083 * used when pruning a post
4085 function forum_change_discussionid($postid, $discussionid) {
4086 set_field('forum_posts', 'discussion', $discussionid, 'id', $postid);
4087 if ($posts = get_records('forum_posts', 'parent', $postid)) {
4088 foreach ($posts as $post) {
4089 forum_change_discussionid($post->id, $discussionid);
4092 return true;
4096 * Prints the editing button on subscribers page
4098 function forum_update_subscriptions_button($courseid, $forumid) {
4099 global $CFG, $USER;
4101 if (!empty($USER->subscriptionsediting)) {
4102 $string = get_string('turneditingoff');
4103 $edit = "off";
4104 } else {
4105 $string = get_string('turneditingon');
4106 $edit = "on";
4109 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
4110 "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
4111 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4112 "<input type=\"submit\" value=\"$string\" /></form>";
4116 * This function gets run whenever a role is assigned to a user in a context
4118 * @param integer $userid
4119 * @param object $context
4120 * @return bool
4122 function forum_role_assign($userid, $context, $roleid) {
4123 // check to see if this role comes with mod/forum:initialsubscriptions
4124 $cap = role_context_capabilities($roleid, $context, 'mod/forum:initialsubscriptions');
4126 // we are checking the role because has_capability() will pull this capability out
4127 // from other roles this user might have and resolve them, which is no good
4128 if (isset($cap['mod/forum:initialsubscriptions']) && $cap['mod/forum:initialsubscriptions'] == CAP_ALLOW) {
4129 return forum_add_user_default_subscriptions($userid, $context);
4130 } else {
4131 // MDL-8981, do not subscribe to forum
4132 return true;
4138 * This function gets run whenever a role is assigned to a user in a context
4140 * @param integer $userid
4141 * @param object $context
4142 * @return bool
4144 function forum_role_unassign($userid, $context) {
4145 return forum_remove_user_subscriptions($userid, $context);
4150 * Add subscriptions for new users
4152 function forum_add_user_default_subscriptions($userid, $context) {
4154 if (empty($context->contextlevel)) {
4155 return false;
4158 switch ($context->contextlevel) {
4160 case CONTEXT_SYSTEM: // For the whole site
4161 if ($courses = get_records('course')) {
4162 foreach ($courses as $course) {
4163 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4164 forum_add_user_default_subscriptions($userid, $subcontext);
4167 break;
4169 case CONTEXT_COURSECAT: // For a whole category
4170 if ($courses = get_records('course', 'category', $context->instanceid)) {
4171 foreach ($courses as $course) {
4172 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4173 forum_add_user_default_subscriptions($userid, $subcontext);
4176 if ($categories = get_records('course_categories', 'parent', $context->instanceid)) {
4177 foreach ($categories as $category) {
4178 $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
4179 forum_add_user_default_subscriptions($userid, $subcontext);
4182 break;
4185 case CONTEXT_COURSE: // For a whole course
4186 if ($course = get_record('course', 'id', $context->instanceid)) {
4187 if ($forums = get_all_instances_in_course('forum', $course, $userid, false)) {
4188 foreach ($forums as $forum) {
4189 if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
4190 continue;
4192 if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
4193 if (has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4194 forum_subscribe($userid, $forum->id);
4200 break;
4202 case CONTEXT_MODULE: // Just one forum
4203 if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
4204 if ($forum = get_record('forum', 'id', $cm->instance)) {
4205 if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
4206 continue;
4208 if (has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4209 forum_subscribe($userid, $forum->id);
4213 break;
4216 return true;
4221 * Remove subscriptions for a user in a context
4223 function forum_remove_user_subscriptions($userid, $context) {
4225 if (empty($context->contextlevel)) {
4226 return false;
4229 switch ($context->contextlevel) {
4231 case CONTEXT_SYSTEM: // For the whole site
4232 if ($courses = get_records('course')) {
4233 foreach ($courses as $course) {
4234 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4235 forum_remove_user_subscriptions($userid, $subcontext);
4238 break;
4240 case CONTEXT_COURSECAT: // For a whole category
4241 if ($courses = get_records('course', 'category', $context->instanceid)) {
4242 foreach ($courses as $course) {
4243 $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
4244 forum_remove_user_subscriptions($userid, $subcontext);
4247 if ($categories = get_records('course_categories', 'parent', $context->instanceid)) {
4248 foreach ($categories as $category) {
4249 $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
4250 forum_remove_user_subscriptions($userid, $subcontext);
4253 break;
4255 case CONTEXT_COURSE: // For a whole course
4256 if ($course = get_record('course', 'id', $context->instanceid)) {
4257 if ($forums = get_all_instances_in_course('forum', $course, $userid, true)) {
4258 foreach ($forums as $forum) {
4259 if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
4260 if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4261 forum_unsubscribe($userid, $forum->id);
4267 break;
4269 case CONTEXT_MODULE: // Just one forum
4270 if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
4271 if ($forum = get_record('forum', 'id', $cm->instance)) {
4272 if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4273 forum_unsubscribe($userid, $forum->id);
4277 break;
4280 return true;
4283 // Functions to do with read tracking.
4287 function forum_tp_add_read_record($userid, $postid, $discussionid=-1, $forumid=-1) {
4288 if (($readrecord = forum_tp_get_read_records($userid, $postid)) === false) {
4289 // New read record
4290 unset($readrecord);
4291 $readrecord->userid = $userid;
4292 $readrecord->postid = $postid;
4293 $readrecord->discussionid = $discussionid;
4294 $readrecord->forumid = $forumid;
4295 $readrecord->firstread = time();
4296 $readrecord->lastread = $readrecord->firstread;
4297 return insert_record('forum_read', $readrecord, true);
4299 } else {
4300 // Update read record
4301 $readrecord = reset($readrecord);
4302 $readrecord->lastread = time();
4304 $update = NULL;
4305 $update->id = $readrecord->id;
4306 $update->lastread = $readrecord->lastread;
4308 // This shouldn't happen, but just in case...
4309 if (!$readrecord->firstread) {
4310 // Update the 'firstread' field.
4311 $update->firstread = $readrecord->lastread;
4313 if ($discussionid > -1) {
4314 // Update the 'discussionid' field.
4315 $update->discussionid = $discussionid;
4317 if ($forumid > -1) {
4318 // Update the 'forumid' field.
4319 $update->forumid = $forumid;
4322 return update_record('forum_read', $update);
4327 * Returns all records in the 'forum_read' table matching the passed keys, indexed
4328 * by userid.
4330 function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4331 $select = '';
4332 if ($userid > -1) {
4333 if ($select != '') $select .= ' AND ';
4334 $select .= 'userid = \''.$userid.'\'';
4336 if ($postid > -1) {
4337 if ($select != '') $select .= ' AND ';
4338 $select .= 'postid = \''.$postid.'\'';
4340 if ($discussionid > -1) {
4341 if ($select != '') $select .= ' AND ';
4342 $select .= 'discussionid = \''.$discussionid.'\'';
4344 if ($forumid > -1) {
4345 if ($select != '') $select .= ' AND ';
4346 $select .= 'forumid = \''.$forumid.'\'';
4349 return get_records_select('forum_read', $select);
4353 * Returns all read records for the provided user and discussion, indexed by postid.
4355 function forum_tp_get_discussion_read_records($userid, $discussionid) {
4356 $select = 'userid = \''.$userid.'\' AND discussionid = \''.$discussionid.'\'';
4357 $fields = 'postid, firstread, lastread';
4358 return get_records_select('forum_read', $select, '', $fields);
4362 * If its an old post, do nothing. If the record exists, the maintenance will clear it up later.
4364 function forum_tp_mark_post_read($userid, &$post, $forumid) {
4365 if (!forum_tp_is_post_old($post)) {
4366 return forum_tp_add_read_record($userid, $post->id, $post->discussion, $forumid);
4367 } else {
4368 return true;
4373 * Marks a whole forum as read, for a given user
4375 function forum_tp_mark_forum_read($userid, $forumid, $groupid=false) {
4376 global $CFG;
4378 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4380 $groupsel = '';
4381 if ($groupid !== false) {
4382 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4385 $sql = 'SELECT p.id as postid, d.id as discussionid, d.forum as forumid '.
4386 'FROM '.$CFG->prefix.'forum_posts p '.
4387 'LEFT JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
4388 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4389 'WHERE d.forum = '.$forumid.$groupsel.
4390 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4392 if ($posts = get_records_sql($sql)) {
4393 foreach ($posts as $post) {
4394 forum_tp_add_read_record($userid, $post->postid, $post->discussionid, $post->forumid);
4396 return true;
4401 * Marks a whole discussion as read, for a given user
4403 function forum_tp_mark_discussion_read($userid, $discussionid, $forumid) {
4404 global $CFG;
4406 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4408 $sql = 'SELECT p.id as postid, p.discussion as discussionid '.
4409 'FROM '.$CFG->prefix.'forum_posts p '.
4410 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4411 'WHERE p.discussion = '.$discussionid.' '.
4412 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4414 if ($posts = get_records_sql($sql)) {
4415 foreach ($posts as $post) {
4416 forum_tp_add_read_record($userid, $post->postid, $post->discussionid, $forumid);
4418 return true;
4425 function forum_tp_is_post_read($userid, &$post) {
4426 return (forum_tp_is_post_old($post) ||
4427 (get_record('forum_read', 'userid', $userid, 'postid', $post->id) !== false));
4433 function forum_tp_is_post_old(&$post, $time=null) {
4434 global $CFG;
4436 if (is_null($time)) $time = time();
4437 return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600)));
4441 * Returns the count of records for the provided user and discussion.
4443 function forum_tp_count_discussion_read_records($userid, $discussionid) {
4444 global $CFG;
4446 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4448 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4449 'FROM '.$CFG->prefix.'forum_discussions d '.
4450 'LEFT JOIN '.$CFG->prefix.'forum_read r ON d.id = r.discussionid AND r.userid = '.$userid.' '.
4451 'LEFT JOIN '.$CFG->prefix.'forum_posts p ON p.discussion = d.id '.
4452 'AND (p.modified < '.$cutoffdate.' OR p.id = r.postid) '.
4453 'WHERE d.id = '.$discussionid;
4455 return (count_records_sql($sql));
4459 * Returns the count of records for the provided user and discussion.
4461 function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
4462 global $CFG;
4464 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4466 $sql = 'SELECT COUNT(p.id) '.
4467 'FROM '.$CFG->prefix.'forum_posts p '.
4468 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4469 'WHERE p.discussion = '.$discussionid.' '.
4470 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4472 return (count_records_sql($sql));
4476 * Returns the count of posts for the provided forum and [optionally] group.
4478 function forum_tp_count_forum_posts($forumid, $groupid=false) {
4479 global $CFG;
4481 $sql = 'SELECT COUNT(*) '.
4482 'FROM '.$CFG->prefix.'forum_posts fp,'.$CFG->prefix.'forum_discussions fd '.
4483 'WHERE fd.forum = '.$forumid.' AND fp.discussion = fd.id';
4484 if ($groupid !== false) {
4485 $sql .= ' AND (fd.groupid = '.$groupid.' OR fd.groupid = -1)';
4487 $count = count_records_sql($sql);
4490 return $count;
4494 * Returns the count of records for the provided user and forum and [optionally] group.
4496 function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
4497 global $CFG;
4499 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4501 $groupsel = '';
4502 if ($groupid !== false) {
4503 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4506 if ($CFG->dbfamily === 'postgres' || $CFG->dbfamily === 'mssql' || $CFG->dbfamily === 'oracle') {
4507 // this query takes 20ms, vs several minutes for the one below
4508 $sql = " SELECT COUNT (DISTINCT u.id ) "
4509 . " FROM ( "
4510 . " SELECT p.id "
4511 . " FROM {$CFG->prefix}forum_posts p "
4512 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4513 . " JOIN {$CFG->prefix}forum_read r ON p.id = r.postid"
4514 . " WHERE d.forum = $forumid $groupsel "
4515 . " AND r.userid= $userid"
4516 . " UNION"
4517 . " SELECT p.id"
4518 . " FROM {$CFG->prefix}forum_posts p "
4519 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4520 . " WHERE d.forum = $forumid $groupsel "
4521 . " AND p.modified < $cutoffdate"
4522 . ") u";
4523 } else { // This is for MySQL. TODO: Check if the above works for MySQL 4.1
4524 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4525 'FROM '.$CFG->prefix.'forum_posts p,'.$CFG->prefix.'forum_read r,'.$CFG->prefix.'forum_discussions d '.
4526 'WHERE d.forum = '.$forumid.$groupsel.' AND p.discussion = d.id AND '.
4527 '((p.id = r.postid AND r.userid = '.$userid.') OR p.modified < '.$cutoffdate.' ) ';
4529 return (count_records_sql($sql));
4533 * Returns the count of records for the provided user and forum and [optionally] group.
4535 function forum_tp_count_forum_unread_posts($userid, $forumid, $groupid=false) {
4536 global $CFG;
4538 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4540 $groupsel = '';
4541 if ($groupid !== false) {
4542 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4545 $sql = 'SELECT COUNT(p.id) '.
4546 'FROM '.$CFG->prefix.'forum_posts p '.
4547 'LEFT JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
4548 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4549 'WHERE d.forum = '.$forumid.$groupsel.
4550 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4552 return (count_records_sql($sql));
4556 * Deletes read records for the specified index. At least one parameter must be specified.
4558 function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4559 $select = '';
4560 if ($userid > -1) {
4561 if ($select != '') $select .= ' AND ';
4562 $select .= 'userid = \''.$userid.'\'';
4564 if ($postid > -1) {
4565 if ($select != '') $select .= ' AND ';
4566 $select .= 'postid = \''.$postid.'\'';
4568 if ($discussionid > -1) {
4569 if ($select != '') $select .= ' AND ';
4570 $select .= 'discussionid = \''.$discussionid.'\'';
4572 if ($forumid > -1) {
4573 if ($select != '') $select .= ' AND ';
4574 $select .= 'forumid = \''.$forumid.'\'';
4576 if ($select == '') {
4577 return false;
4579 else {
4580 return delete_records_select('forum_read', $select);
4584 * Get a list of forums not tracked by the user.
4586 * @param int $userid The id of the user to use.
4587 * @param int $courseid The id of the course being checked (optional).
4588 * @return mixed An array indexed by forum id, or false.
4590 function forum_tp_get_untracked_forums($userid, $courseid=false) {
4591 global $CFG;
4593 // If a course is specified, get the forums with tracking turned off.
4594 if ($courseid !== false) {
4595 $select = 'course = '.$courseid.' AND trackingtype = '.FORUM_TRACKING_OFF;
4596 $forced = get_records_select('forum', $select, '', 'id,course');
4597 } else {
4598 $forced = false;
4601 // Get the forums that the user has turned off.
4602 $sql = 'SELECT ft.forumid, ft.userid '.
4603 'FROM '.$CFG->prefix.'forum_track_prefs ft, '.$CFG->prefix.'forum f '.
4604 'WHERE ft.userid = '.$userid.' AND f.id = ft.forumid ' .
4605 'AND f.trackingtype != '.FORUM_TRACKING_ON;
4606 $useroff = get_records_sql($sql);
4607 if (!$forced) {
4608 return $useroff;
4609 } else if (!$useroff) {
4610 return $forced;
4611 } else {
4612 return ($useroff + $forced);
4617 * Determine if a user can track forums and optionally a particular forum.
4618 * Checks the site settings, the user settings and the forum settings (if
4619 * requested).
4621 * @param mixed $forum The forum object to test, or the int id (optional).
4622 * @param mixed $userid The user object to check for (optional).
4623 * @return boolean
4625 function forum_tp_can_track_forums($forum=false, $user=false) {
4626 global $USER, $CFG;
4628 // if possible, avoid expensive
4629 // queries
4630 if (empty($CFG->forum_trackreadposts)) {
4631 return false;
4634 if ($user === false) {
4635 // Must be logged in and not a guest.
4636 $isauser = isloggedin() && !isguest();
4637 $user = $USER;
4638 } else {
4639 $isauser = true;
4642 if ($forum === false) {
4643 $forumallows = true;
4644 $forumforced = false;
4645 } else {
4646 // Work toward always passing an object...
4647 if (is_numeric($forum)) {
4648 $forum = get_record('forum', 'id', $forum, '','','','', 'id,trackingtype');
4651 $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
4652 $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
4655 return ($isauser && ($forumforced || ($forumallows && !empty($user->trackforums))));
4659 * Tells whether a specific forum is tracked by the user. A user can optionally
4660 * be specified. If not specified, the current user is assumed.
4662 * @param mixed $forum If int, the id of the forum being checked; if object, the forum object
4663 * @param int $userid The id of the user being checked (optional).
4664 * @return boolean
4666 function forum_tp_is_tracked($forum, $userid=false) {
4667 global $USER, $CFG;
4669 if ($userid === false) {
4670 if (empty($USER->id)) {
4671 return false;
4673 $userid = $USER->id;
4676 // Work toward always passing an object...
4677 if (is_numeric($forum)) {
4678 $forum = get_record('forum', 'id', $forum);
4681 return (($forum->trackingtype == FORUM_TRACKING_ON) ||
4682 ($forum->trackingtype == FORUM_TRACKING_OPTIONAL &&
4683 get_record('forum_track_prefs', 'userid', $userid, 'forumid', $forum->id) === false));
4689 function forum_tp_start_tracking($forumid, $userid=false) {
4690 global $USER;
4692 if ($userid === false) {
4693 $userid = $USER->id;
4696 return delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forumid);
4702 function forum_tp_stop_tracking($forumid, $userid=false) {
4703 global $USER;
4705 if ($userid === false) {
4706 $userid = $USER->id;
4709 $track_prefs = new stdClass;
4710 $track_prefs->userid = $userid;
4711 $track_prefs->forumid = $forumid;
4712 if (insert_record('forum_track_prefs', $track_prefs)) {
4713 return forum_tp_delete_read_records($userid, -1, -1, $forumid);
4714 } else {
4715 return false;
4720 /**
4721 * Clean old records from the forum_read table.
4723 function forum_tp_clean_read_records() {
4724 global $CFG;
4726 // Look for records older than the cutoffdate that are still in the forum_read table.
4727 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
4728 $sql = 'SELECT fr.id, fr.userid, fr.postid '.
4729 'FROM '.$CFG->prefix.'forum_posts fp, '.$CFG->prefix.'forum_read fr '.
4730 'WHERE fp.modified < '.$cutoffdate.' AND fp.id = fr.postid';
4731 if (($oldreadposts = get_records_sql($sql))) {
4732 foreach($oldreadposts as $oldreadpost) {
4733 delete_records('forum_read', 'id', $oldreadpost->id);
4739 * Sets the last post for a given discussion
4741 function forum_discussion_update_last_post($discussionid) {
4742 global $CFG, $db;
4744 // Check the given discussion exists
4745 if (!record_exists('forum_discussions', 'id', $discussionid)) {
4746 return false;
4749 // Use SQL to find the last post for this discussion
4750 $sql = 'SELECT id, userid, modified '.
4751 'FROM '.$CFG->prefix.'forum_posts '.
4752 'WHERE discussion='.$discussionid.' '.
4753 'ORDER BY modified DESC ';
4755 // Lets go find the last post
4756 if (($lastpost = get_record_sql($sql, true))) {
4757 $discussionobject = new Object;
4758 $discussionobject->id = $discussionid;
4759 $discussionobject->usermodified = $lastpost->userid;
4760 $discussionobject->timemodified = $lastpost->modified;
4761 if (update_record('forum_discussions', $discussionobject)) {
4762 return $lastpost->id;
4766 // To get here either we couldn't find a post for the discussion (weird)
4767 // or we couldn't update the discussion record (weird x2)
4768 return false;
4775 function forum_get_view_actions() {
4776 return array('view discussion','search','forum','forums','subscribers');
4782 function forum_get_post_actions() {
4783 return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
4787 * this function returns all the separate forum ids, given a courseid
4788 * @param int $courseid
4789 * @return array
4791 function forum_get_separate_modules($courseid) {
4793 global $CFG,$db;
4794 $forummodule = get_record("modules", "name", "forum");
4796 $sql = 'SELECT f.id, f.id FROM '.$CFG->prefix.'forum f, '.$CFG->prefix.'course_modules cm WHERE
4797 f.id = cm.instance AND cm.module ='.$forummodule->id.' AND cm.visible = 1 AND cm.course = '.$courseid.'
4798 AND cm.groupmode ='.SEPARATEGROUPS;
4800 return get_records_sql($sql);
4807 function forum_check_throttling($forum) {
4808 global $USER, $CFG;
4810 if (is_numeric($forum)) {
4811 $forum = get_record('forum','id',$forum);
4813 if (!is_object($forum)) {
4814 return false; // this is broken.
4817 if (empty($forum->blockafter)) {
4818 return true;
4821 if (empty($forum->blockperiod)) {
4822 return true;
4825 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
4826 error('Course Module ID was incorrect');
4828 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
4829 if(!has_capability('mod/forum:throttlingapplies', $modcontext)) {
4830 return true;
4833 // get the number of posts in the last period we care about
4834 $timenow = time();
4835 $timeafter = $timenow - $forum->blockperiod;
4837 $numposts = count_records_sql('SELECT COUNT(p.id) FROM '.$CFG->prefix.'forum_posts p'
4838 .' JOIN '.$CFG->prefix.'forum_discussions d'
4839 .' ON p.discussion = d.id WHERE d.forum = '.$forum->id
4840 .' AND p.userid = '.$USER->id.' AND p.created > '.$timeafter);
4842 $a->blockafter = $forum->blockafter;
4843 $a->numposts = $numposts;
4844 $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
4846 if ($forum->blockafter <= $numposts) {
4847 error(get_string('forumblockingtoomanyposts','error',$a),$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id);
4849 if ($forum->warnafter <= $numposts) {
4850 notify(get_string('forumblockingalmosttoomanyposts','forum',$a));
4858 * This function is used by the remove_course_userdata function in moodlelib.
4859 * If this function exists, remove_course_userdata will execute it.
4860 * This function will remove all posts from the specified forum.
4862 function forum_delete_userdata($data, $showfeedback=true) {
4863 global $CFG;
4865 $sql = "DELETE FROM {$CFG->prefix}forum_posts
4866 WHERE discussion IN (
4867 SELECT fd.id FROM {$CFG->prefix}forum_discussions fd, {$CFG->prefix}forum f
4868 WHERE f.course={$data->courseid} AND f.id=fd.forum "; // closing ) added bellow
4870 $strreset = get_string('reset');
4872 if (!empty($data->reset_forum_news)) {
4873 $select = "$sql AND f.type = 'news' )";
4874 if (execute_sql($select, false) and $showfeedback) {
4875 notify($strreset.': '.get_string('namenews','forum'), 'notifysuccess');
4878 if (!empty($data->reset_forum_single)) {
4879 $select = "$sql AND f.type = 'single' ) AND parent <> 0";
4880 if (execute_sql($select, false) and $showfeedback) {
4881 notify($strreset.': '.get_string('singleforum','forum'), 'notifysuccess');
4884 if (!empty($data->reset_forum_eachuser)) {
4885 $select = "$sql AND f.type = 'eachuser' )";
4886 if (execute_sql($select, false) and $showfeedback) {
4887 notify($strreset.': '.get_string('eachuserforum','forum'), 'notifysuccess');
4890 if (!empty($data->reset_forum_general)) {
4891 $select = "$sql AND f.type = 'general' )";
4892 if (execute_sql($select, false) and $showfeedback) {
4893 notify($strreset.': '.get_string('generalforum','forum'), 'notifysuccess');
4896 if (!empty($data->reset_forum_subscriptions)) {
4897 $subscripsql = "DELETE FROM {$CFG->prefix}forum_subscriptions
4898 WHERE forum IN (
4899 SELECT id FROM {$CFG->prefix}forum
4900 WHERE course = {$data->courseid} )";
4902 if (execute_sql($subscripsql, false) and $showfeedback) {
4903 notify($strreset.': '.get_string('resetsubscriptions','forum'), 'notifysuccess');
4910 * Called by course/reset.php
4912 function forum_reset_course_form($course) {
4913 echo get_string('resetforums', 'forum'); echo ':<br />';
4914 print_checkbox('reset_forum_news', 1, true, get_string('namenews','forum'), '', ''); echo '<br />';
4915 print_checkbox('reset_forum_single', 1, true, get_string('singleforum','forum'), '', ''); echo '<br />';
4916 print_checkbox('reset_forum_eachuser', 1, true, get_string('eachuserforum','forum'), '', ''); echo '<br />';
4917 print_checkbox('reset_forum_general', 1, true, get_string('generalforum','forum'), '', ''); echo '<br />';
4918 echo '<p>';
4919 print_checkbox('reset_forum_subscriptions', 1, true, get_string('resetsubscriptions','forum'), '', '');
4920 echo '</p>';
4925 * Converts a forum to use the Roles System
4926 * @param $forum - a forum object with the same attributes as a record
4927 * from the forum database table
4928 * @param $forummodid - the id of the forum module, from the modules table
4929 * @param $teacherroles - array of roles that have moodle/legacy:teacher
4930 * @param $studentroles - array of roles that have moodle/legacy:student
4931 * @param $guestroles - array of roles that have moodle/legacy:guest
4932 * @param $cmid - the course_module id for this forum instance
4933 * @return boolean - forum was converted or not
4935 function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
4936 $studentroles=array(), $guestroles=array(), $cmid=NULL) {
4938 global $CFG;
4940 if (!isset($forum->open) && !isset($forum->assesspublic)) {
4941 // We assume that this forum has already been converted to use the
4942 // Roles System. Columns forum.open and forum.assesspublic get dropped
4943 // once the forum module has been upgraded to use Roles.
4944 return false;
4947 if ($forum->type == 'teacher') {
4949 // Teacher forums should be converted to normal forums that
4950 // use the Roles System to implement the old behavior.
4951 // Note:
4952 // Seems that teacher forums were never backed up in 1.6 since they
4953 // didn't have an entry in the course_modules table.
4954 require_once($CFG->dirroot.'/course/lib.php');
4956 if (count_records('forum_discussions', 'forum', $forum->id) == 0) {
4957 // Delete empty teacher forums.
4958 delete_records('forum', 'id', $forum->id);
4959 } else {
4960 // Create a course module for the forum and assign it to
4961 // section 0 in the course.
4962 $mod = new object;
4963 $mod->course = $forum->course;
4964 $mod->module = $forummodid;
4965 $mod->instance = $forum->id;
4966 $mod->section = 0;
4967 $mod->visible = 0; // Hide the forum
4968 $mod->visibleold = 0; // Hide the forum
4969 $mod->groupmode = 0;
4971 if (!$cmid = add_course_module($mod)) {
4972 error('Could not create new course module instance for the teacher forum');
4973 } else {
4974 $mod->coursemodule = $cmid;
4975 if (!$sectionid = add_mod_to_section($mod)) {
4976 error('Could not add converted teacher forum instance to section 0 in the course');
4977 } else {
4978 if (!set_field('course_modules', 'section', $sectionid, 'id', $cmid)) {
4979 error('Could not update course module with section id');
4984 // Change the forum type to general.
4985 $forum->type = 'general';
4986 if (!update_record('forum', $forum)) {
4987 error('Could not change forum from type teacher to type general');
4990 $context = get_context_instance(CONTEXT_MODULE, $cmid);
4992 // Create overrides for default student and guest roles (prevent).
4993 foreach ($studentroles as $studentrole) {
4994 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
4995 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id);
4996 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
4997 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
4998 assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
4999 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
5000 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
5001 assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id);
5002 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id);
5003 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id);
5004 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id);
5005 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id);
5006 assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id);
5007 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id);
5008 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id);
5009 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id);
5010 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $studentrole->id, $context->id);
5012 foreach ($guestroles as $guestrole) {
5013 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
5014 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id);
5015 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
5016 assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id);
5017 assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id);
5018 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id);
5019 assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id);
5020 assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id);
5021 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id);
5022 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id);
5023 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id);
5024 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id);
5025 assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id);
5026 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id);
5027 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id);
5028 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id);
5029 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $guestrole->id, $context->id);
5032 } else {
5033 // Non-teacher forum.
5035 if (empty($cmid)) {
5036 // We were not given the course_module id. Try to find it.
5037 if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
5038 notify('Could not get the course module for the forum');
5039 return false;
5040 } else {
5041 $cmid = $cm->id;
5044 $context = get_context_instance(CONTEXT_MODULE, $cmid);
5046 // $forum->open defines what students can do:
5047 // 0 = No discussions, no replies
5048 // 1 = No discussions, but replies are allowed
5049 // 2 = Discussions and replies are allowed
5050 switch ($forum->open) {
5051 case 0:
5052 foreach ($studentroles as $studentrole) {
5053 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5054 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
5056 break;
5057 case 1:
5058 foreach ($studentroles as $studentrole) {
5059 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
5060 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
5062 break;
5063 case 2:
5064 foreach ($studentroles as $studentrole) {
5065 assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id);
5066 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
5068 break;
5071 // $forum->assessed defines whether forum rating is turned
5072 // on (1 or 2) and who can rate posts:
5073 // 1 = Everyone can rate posts
5074 // 2 = Only teachers can rate posts
5075 switch ($forum->assessed) {
5076 case 1:
5077 foreach ($studentroles as $studentrole) {
5078 assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id);
5080 foreach ($teacherroles as $teacherrole) {
5081 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
5083 break;
5084 case 2:
5085 foreach ($studentroles as $studentrole) {
5086 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
5088 foreach ($teacherroles as $teacherrole) {
5089 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
5091 break;
5094 // $forum->assesspublic defines whether students can see
5095 // everybody's ratings:
5096 // 0 = Students can only see their own ratings
5097 // 1 = Students can see everyone's ratings
5098 switch ($forum->assesspublic) {
5099 case 0:
5100 foreach ($studentroles as $studentrole) {
5101 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
5103 foreach ($teacherroles as $teacherrole) {
5104 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
5106 break;
5107 case 1:
5108 foreach ($studentroles as $studentrole) {
5109 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id);
5111 foreach ($teacherroles as $teacherrole) {
5112 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
5114 break;
5117 if (empty($cm)) {
5118 $cm = get_record('course_modules', 'id', $cmid);
5121 // $cm->groupmode:
5122 // 0 - No groups
5123 // 1 - Separate groups
5124 // 2 - Visible groups
5125 switch ($cm->groupmode) {
5126 case 0:
5127 break;
5128 case 1:
5129 foreach ($studentroles as $studentrole) {
5130 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
5132 foreach ($teacherroles as $teacherrole) {
5133 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
5135 break;
5136 case 2:
5137 foreach ($studentroles as $studentrole) {
5138 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
5140 foreach ($teacherroles as $teacherrole) {
5141 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
5143 break;
5146 return true;