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 ///////////////////////////////////////////////////////////
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) {
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)) {
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);
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);
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);
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)) {
212 if ($discussions = get_records('forum_discussions', 'forum', $forum->id
)) {
213 foreach ($discussions as $discussion) {
214 if (!forum_delete_discussion($discussion, true)) {
220 if (!delete_records('forum_subscriptions', 'forum', $forum->id
)) {
224 forum_tp_delete_read_records(-1, -1, -1, $forum->id
);
226 if (!delete_records('forum', 'id', $forum->id
)) {
230 forum_grade_item_delete($forum);
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() {
244 $CFG->enablerecordcache
= true; // We want all the caching we can get
246 $cronuser = clone($USER);
249 // all users that are subscribed to any post that needs sending
253 $mailcount = array();
254 $errorcount = array();
257 $discussions = 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
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;
291 mtrace('Could not find discussion '.$discussionid);
296 $forumid = $discussions[$discussionid]->forum
;
297 if (!isset($forums[$forumid])) {
298 if ($forum = get_record('forum', 'id', $forumid)) {
299 $forums[$forumid] = $forum;
301 mtrace('Could not find forum '.$forumid);
306 $courseid = $forums[$forumid]->course
;
307 if (!isset($courses[$courseid])) {
308 if ($course = get_record('course', 'id', $courseid)) {
309 $courses[$courseid] = $course;
311 mtrace('Could not find course '.$courseid);
316 if (!isset($coursemodules[$forumid])) {
317 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
318 $coursemodules[$forumid] = $cm;
320 mtrace('Could not course module for forum '.$forumid);
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
);
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
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
);
376 // Set up the environment for the post, discussion, forum, course
377 $discussion = $discussions[$post->discussion
];
378 $forum = $forums[$discussion->forum
];
379 $course = $courses[$forum->course
];
380 $cm = $coursemodules[$forum->id
];
382 // Do some checks to see if we can bail out now
383 if (empty($subscribedusers[$forum->id
]) ||
!in_array($userto->id
, $subscribedusers[$forum->id
])) {
384 continue; // user does not subscribe to this forum
387 // Get the context (from cache)
388 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
); // Cached already
390 // setup global $COURSE properly - needed for roles and languages
391 course_setup($course); // More environment
393 // Make sure groups allow this user to see this email
394 if ($discussion->groupid
> 0 and $groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
395 if (! groups_group_exists($discussion->groupid
)) { // Can't find group
396 continue; // Be safe and don't send it to anyone
399 if (!groups_is_member($discussion->groupid
) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
400 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
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
);
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.");
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
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
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
);
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);
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
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;
524 $postuser = $users[$digestpost->userid
];
525 if ($postuser->emailstop
) {
526 add_to_log(SITEID
, 'forum', 'mail blocked', '', '', 0, $postuser->id
);
530 if (!isset($posts[$digestpost->postid
])) {
531 if ($post = get_record('forum_posts', 'id', $digestpost->postid
)) {
532 $posts[$digestpost->postid
] = $post;
537 $discussionid = $digestpost->discussionid
;
538 if (!isset($discussions[$discussionid])) {
539 if ($discussion = get_record('forum_discussions', 'id', $discussionid)) {
540 $discussions[$discussionid] = $discussion;
545 $forumid = $discussions[$discussionid]->forum
;
546 if (!isset($forums[$forumid])) {
547 if ($forum = get_record('forum', 'id', $forumid)) {
548 $forums[$forumid] = $forum;
554 $courseid = $forums[$forumid]->course
;
555 if (!isset($courses[$courseid])) {
556 if ($course = get_record('course', 'id', $courseid)) {
557 $courses[$courseid] = $course;
563 if (!isset($coursemodules[$forumid])) {
564 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
565 $coursemodules[$forumid] = $cm;
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.
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.'&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
];
616 course_setup($course);
618 $strforums = get_string('forums', 'forum');
619 $canunsubscribe = ! forum_is_forcesubscribed($forum);
620 $canreply = forum_user_can_post($forum, $userto);
623 $posttext .= "\n \n";
624 $posttext .= '=====================================================================';
625 $posttext .= "\n \n";
626 $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name
,true);
627 if ($discussion->name
!= $forum->name
) {
628 $posttext .= " -> ".format_string($discussion->name
,true);
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>";
639 $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name
,true)."</a></font></p>";
643 $postsarray = $discussionposts[$discussionid];
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.)
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
);
660 $userfrom->customheaders
= array ("Precedence: Bulk");
662 if ($userto->maildigest
== 2) {
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&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>';
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>";
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
701 if (!$mailresult = email_to_user($userto, $site->shortname
, $postsubject, $posttext, $posthtml,
702 '', '', $CFG->forum_replytouser
)) {
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
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));
733 course_setup(SITEID
); // reset cron user language, theme and timezone settings
735 if (!empty($CFG->forum_lastreadclean
)) {
737 if ($CFG->forum_lastreadclean +
(24*3600) < $timenow) {
738 set_config('forum_lastreadclean', $timenow);
739 forum_tp_clean_read_records();
742 set_config('forum_lastreadclean', time());
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) {
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
);
771 $by->name
= fullname($userfrom, $viewfullnames);
772 $by->date
= userdate($post->modified
, "", $userto->timezone
);
774 $strbynameondate = get_string('bynameondate', 'forum', $by);
776 $strforums = get_string('forums', 'forum');
778 $canunsubscribe = ! forum_is_forcesubscribed($forum);
779 $canreply = forum_user_can_post($forum, $userto);
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);
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
);
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";
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) {
833 if ($userto->mailformat
!= 1) { // Needs to be HTML
837 $strforums = get_string('forums', 'forum');
838 $canreply = forum_user_can_post($forum, $userto);
839 $canunsubscribe = ! forum_is_forcesubscribed($forum);
841 $posthtml = '<head>';
842 foreach ($CFG->stylesheets
as $stylesheet) {
843 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
845 $posthtml .= '</head>';
846 $posthtml .= "\n<body id=\"email\">\n\n";
848 $posthtml .= '<div class="navbar">'.
849 '<a target="_blank" href="'.$CFG->wwwroot
.'/course/view.php?id='.$course->id
.'">'.$course->shortname
.'</a> » '.
850 '<a target="_blank" href="'.$CFG->wwwroot
.'/mod/forum/index.php?id='.$course->id
.'">'.$strforums.'</a> » '.
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>';
855 $posthtml .= ' » <a target="_blank" href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$discussion->id
.'">'.
856 format_string($discussion->name
,true).'</a></div>';
858 $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
860 if ($canunsubscribe) {
861 $posthtml .= '<hr /><div align="center" class="unsubscribelink"><a href="'.$CFG->wwwroot
.'/mod/forum/subscribe.php?id='.$forum->id
.'">'.
862 get_string('unsubscribe', 'forum').'</a></div>';
865 $posthtml .= '</body>';
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
;
895 function forum_user_complete($course, $user, $mod, $forum) {
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);
906 echo "<p>".get_string("noposts", "forum")."</p>";
913 function forum_print_overview($courses,&$htmlarray) {
917 if (empty($courses) ||
!is_array($courses) ||
count($courses) == 0) {
921 if (!$forums = get_all_instances_in_courses('forum',$courses)) {
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 "
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)) {
969 if (empty($unread) and empty($new)) {
973 $strforum = get_string('modulename','forum');
974 $strnumunread = get_string('overviewnumunread','forum');
975 $strnumpostssince = get_string('overviewnumpostssince','forum');
977 foreach ($forums as $forum) {
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
;
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>';
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) {
1019 $LIKE = sql_ilike();
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')){
1031 $strftimerecent = get_string('strftimerecent');
1033 $mygroupid = mygroupid($course->id
);
1034 $groupmode = array(); // To cache group modes
1037 foreach ($logs as $log) {
1038 //Get post info, I'll need it later
1039 if ($post = forum_get_post_from_log($log)) {
1040 //Create a temp valid module structure (course,id)
1041 $tempmod = new object;
1042 $tempmod->course
= $log->course
;
1043 $tempmod->id
= $post->forum
;
1044 //Obtain the visible property from the instance
1045 $coursecontext = get_context_instance(CONTEXT_COURSE
, $tempmod->course
);
1046 $modvisible = instance_is_visible('forum', $tempmod)
1047 ||
has_capability('moodle/course:viewhiddenactivities', $coursecontext);
1050 //Only if the post exists and mod is visible
1051 if ($post && $modvisible) {
1053 if (!isset($cm[$post->forum
])) {
1054 $cm[$post->forum
] = get_coursemodule_from_instance('forum', $post->forum
, $course->id
);
1056 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm[$post->forum
]->id
);
1058 // Check whether this is belongs to a discussion in a group that
1059 // should NOT be accessible to the current user
1060 if (!has_capability('moodle/site:accessallgroups', $modcontext)
1061 && $post->groupid
!= -1) { // Open discussions have groupid -1
1063 $groupmode[$post->forum
] = groups_get_activity_groupmode($cm[$post->forum
]);
1065 if ($groupmode[$post->forum
]) {
1066 //hope i didn't break anything
1067 if (!@in_array
($mygroupid, $post->groupid
))/*$mygroupid != $post->groupid*/{
1074 print_headline(get_string('newforumposts', 'forum').':', 3);
1078 $date = userdate($post->modified
, $strftimerecent);
1080 $subjectclass = ($log->action
== 'add discussion') ?
' bold' : '';
1082 //Accessibility: markup as a list.
1084 echo "\n<ul class='unlist'>\n";
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>'.
1091 echo '<div class="info'.$subjectclass.'">';
1092 echo '"<a href="'.$CFG->wwwroot
.'/mod/forum/'.str_replace('&', '&', $log->url
).'">';
1093 $post->subject
= break_up_long_words(format_string($post->subject
,true));
1094 echo $post->subject
;
1095 echo "</a>\"</div></li>\n";
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) {
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
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) {
1134 if ($forum != null) {
1135 require_once($CFG->libdir
.'/gradelib.php');
1136 if ($grades = forum_get_user_grades($forum, $userid)) {
1137 grade_update('mod/forum', $forum->course
, 'mod', 'forum', $forum->id
, 0, $grades);
1139 } else if ($userid and $nullifnone) {
1140 $grade = new object();
1141 $grade->userid
= $userid;
1142 $grade->rawgrade
= NULL;
1143 grade_update('mod/forum', $data->course
, 'mod', 'forum', $forum->id
, 0, $grade);
1147 $sql = "SELECT f.*, cm.idnumber as cmidnumber
1148 FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1149 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1150 if ($rs = get_recordset_sql($sql)) {
1151 if ($rs->RecordCount() > 0) {
1152 while ($forum = rs_fetch_next_record($rs)) {
1153 forum_grade_item_update($forum);
1154 if ($forum->assessed
) {
1155 forum_update_grades($forum, 0, false);
1165 * Create/update grade item for given forum
1167 * @param object $forum object with extra cmidnumber
1168 * @return int 0 if ok
1170 function forum_grade_item_update($forum) {
1172 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1173 require_once($CFG->libdir
.'/gradelib.php');
1176 $params = array('itemname'=>$forum->name
, 'idnumber'=>$forum->cmidnumber
);
1178 if (!$forum->assessed
or $forum->scale
== 0) {
1179 $params['gradetype'] = GRADE_TYPE_NONE
;
1181 } else if ($forum->scale
> 0) {
1182 $params['gradetype'] = GRADE_TYPE_VALUE
;
1183 $params['grademax'] = $forum->scale
;
1184 $params['grademin'] = 0;
1186 } else if ($forum->scale
< 0) {
1187 $params['gradetype'] = GRADE_TYPE_SCALE
;
1188 $params['scaleid'] = -$forum->scale
;
1191 return grade_update('mod/forum', $forum->course
, 'mod', 'forum', $forum->id
, 0, NULL, $params);
1195 * Delete grade item for given forum
1197 * @param object $forum object
1198 * @return object grade_item
1200 function forum_grade_item_delete($forum) {
1202 require_once($CFG->libdir
.'/gradelib.php');
1204 return grade_update('mod/forum', $forum->course
, 'mod', 'forum', $forum->id
, 0, NULL, array('deleted'=>1));
1209 * Returns the users with data in one forum
1210 * (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
1212 function forum_get_participants($forumid) {
1216 //Get students from forum_subscriptions
1217 $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
1218 FROM {$CFG->prefix}user u,
1219 {$CFG->prefix}forum_subscriptions s
1220 WHERE s.forum = '$forumid' and
1222 //Get students from forum_posts
1223 $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
1224 FROM {$CFG->prefix}user u,
1225 {$CFG->prefix}forum_discussions d,
1226 {$CFG->prefix}forum_posts p
1227 WHERE d.forum = '$forumid' and
1228 p.discussion = d.id and
1231 //Get students from forum_ratings
1232 $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
1233 FROM {$CFG->prefix}user u,
1234 {$CFG->prefix}forum_discussions d,
1235 {$CFG->prefix}forum_posts p,
1236 {$CFG->prefix}forum_ratings r
1237 WHERE d.forum = '$forumid' and
1238 p.discussion = d.id and
1242 //Add st_posts to st_subscriptions
1244 foreach ($st_posts as $st_post) {
1245 $st_subscriptions[$st_post->id
] = $st_post;
1248 //Add st_ratings to st_subscriptions
1250 foreach ($st_ratings as $st_rating) {
1251 $st_subscriptions[$st_rating->id
] = $st_rating;
1254 //Return st_subscriptions array (it contains an array of unique users)
1255 return ($st_subscriptions);
1261 function forum_scale_used ($forumid,$scaleid) {
1262 //This function returns if a scale is being used by one forum
1266 $rec = get_record("forum","id","$forumid","scale","-$scaleid");
1268 if (!empty($rec) && !empty($scaleid)) {
1276 * Checks if scale is being used by any instance of forum
1278 * This is used to find out if scale used anywhere
1279 * @param $scaleid int
1280 * @return boolean True if the scale is used by any forum
1282 function forum_scale_used_anywhere($scaleid) {
1283 if ($scaleid and record_exists('forum', 'scale', -$scaleid)) {
1290 // SQL FUNCTIONS ///////////////////////////////////////////////////////////
1293 * Gets a post with all info ready for forum_print_post
1294 * Most of these joins are just to get the forum id
1296 function forum_get_post_full($postid) {
1299 return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1300 FROM {$CFG->prefix}forum_posts p
1301 LEFT JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
1302 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1303 WHERE p.id = '$postid'");
1307 * Gets posts with all info ready for forum_print_post
1308 * We pass forumid in because we always know it so no need to make a
1309 * complicated join to find it out.
1311 function forum_get_discussion_posts($discussion, $sort, $forumid) {
1314 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1315 FROM {$CFG->prefix}forum_posts p
1316 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1317 WHERE p.discussion = $discussion
1318 AND p.parent > 0 $sort");
1322 * Gets posts with all info ready for forum_print_post
1323 * We pass forumid in because we always know it so no need to make a
1324 * complicated join to find it out.
1326 function forum_get_child_posts($parent, $forumid) {
1329 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1330 FROM {$CFG->prefix}forum_posts p
1331 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1332 WHERE p.parent = '$parent'
1333 ORDER BY p.created ASC");
1337 * An array of forum objects that the user is allowed to read/search through.
1339 * @param $courseid - if 0, we look for forums throughout the whole site.
1340 * @return array of forum objects, or false if no matches
1341 * Forum objects have the following attributes:
1342 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1343 * viewhiddentimedposts
1345 function forum_get_readable_forums($userid, $courseid=0) {
1349 if (!$forummod = get_record('modules', 'name', 'forum')) {
1350 error('The forum module is not installed');
1354 $courses = get_records('course', 'id', $courseid);
1356 // If no course is specified, then the user can see SITE + his courses.
1357 // And admins can see all courses, so pass the $doanything flag enabled
1358 $courses1 = get_records('course', 'id', SITEID
);
1359 $courses2 = get_my_courses($userid, null, null, true);
1360 $courses = array_merge($courses1, $courses2);
1366 $readableforums = array();
1368 foreach ($courses as $course) {
1370 $coursecontext = get_context_instance(CONTEXT_COURSE
, $course->id
);
1372 if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
1373 $selecthidden = ' AND cm.visible = 1';
1378 $selectforums = "SELECT f.id AS id,
1383 cm.visible AS cmvisible,
1384 cm.groupmode AS cmgroupmode,
1385 cm.groupingid AS cmgroupingid,
1386 cm.groupmembersonly AS cmgroupmembersonly
1387 FROM {$CFG->prefix}course_modules cm,
1388 {$CFG->prefix}forum f
1389 WHERE cm.instance = f.id
1390 AND cm.course = {$course->id}
1391 AND cm.module = {$forummod->id}
1393 ORDER BY f.name ASC";
1395 if ($forums = get_records_sql($selectforums)) {
1397 $group = groups_get_all_groups($course->id
, $userid);
1399 foreach ($forums as $forum) {
1400 $forumcontext = get_context_instance(CONTEXT_MODULE
, $forum->cmid
);
1402 if (has_capability('mod/forum:viewdiscussion', $forumcontext)) {
1404 // Evaluate groupmode.
1406 $cm->id
= $forum->cmid
;
1407 $cm->groupmode
= $forum->cmgroupmode
;
1408 $cm->groupingid
= $forum->cmgroupingid
;
1409 $cm->groupmembersonly
= $forum->cmgroupmembersonly
;
1410 $cm->course
= $forum->course
;
1411 $forum->cmgroupmode
= groups_get_activity_groupmode($cm);
1412 if (!groups_course_module_visible($cm)) {
1415 if ($forum->cmgroupmode
== SEPARATEGROUPS
1416 && !has_capability('moodle/site:accessallgroups', $forumcontext)) {
1417 $forum->accessallgroups
= false;
1418 $forum->accessgroup
= $group->id
; // The user can only access
1419 // discussions for this group.
1421 $forum->accessallgroups
= true;
1424 $forum->viewhiddentimedposts
1425 = has_capability('mod/forum:viewhiddentimedposts', $forumcontext);
1427 if ($forum->type
== 'qanda'
1428 && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) {
1430 // We need to check whether the user has posted in the qanda forum.
1431 $forum->onlydiscussions
= array(); // Holds discussion ids for the discussions
1432 // the user is allowed to see in this forum.
1434 if ($discussionspostedin =
1435 forum_discussions_user_has_posted_in($forum->id
, $USER->id
)) {
1436 foreach ($discussionspostedin as $d) {
1437 array_push($forum->onlydiscussions
, $d->id
);
1441 array_push($readableforums, $forum);
1445 } // End foreach $courses
1447 //print_object($courses);
1448 //print_object($readableforums);
1450 return $readableforums;
1454 * Returns a list of posts found using an array of search terms.
1455 * @param $searchterms - array of search terms, e.g. word +word -word
1456 * @param $courseid - if 0, we search through the whole site
1458 * @param $recordsperpage=50
1459 * @param &$totalcount
1461 * @return array of posts found
1463 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
1464 &$totalcount, $extrasql='') {
1466 require_once($CFG->libdir
.'/searchlib.php');
1468 $forums = forum_get_readable_forums($USER->id
, $courseid);
1470 if (count($forums) == 0) {
1474 for ($i=0; $i<count($forums); $i++
) {
1476 $selectdiscussion = " ((d.forum = {$forums[$i]->id}";
1478 $selectdiscussion .= " OR (d.forum = {$forums[$i]->id}";
1480 if (!empty($CFG->forum_enabletimedposts
) && !$forums[$i]->viewhiddentimedposts
) {
1482 $selectdiscussion .= " AND ( d.userid = {$USER->id}
1483 OR ((d.timestart = 0 OR d.timestart <= $now)
1484 AND (d.timeend = 0 OR d.timeend > $now)) )";
1486 if ($forums[$i]->type
== 'qanda' && isset($forums[$i]->onlydiscussions
)) {
1487 // This is a qanda forum.
1488 if (is_array($forums[$i]->onlydiscussions
)) {
1489 // Show question posts as well as posts from discussions in
1490 // which the user has posted a reply.
1491 $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions
);
1492 $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)";
1494 // Show only the question posts.
1495 $selectdiscussion .= ' AND (p.parent = 0)';
1498 if (!$forums[$i]->accessallgroups
) {
1499 if (!empty($forums[$i]->accessgroup
)) {
1500 $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}";
1501 $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups.
1503 // User isn't in any group. Only search discussions that are
1504 // open to all groups.
1505 $selectdiscussion .= ' AND d.groupid = -1';
1508 $selectdiscussion .= ")\n";
1510 $selectdiscussion .= ")";
1513 // Some differences SQL
1514 $LIKE = sql_ilike();
1515 $NOTLIKE = 'NOT ' . $LIKE;
1516 if ($CFG->dbfamily
== 'postgres') {
1521 $NOTREGEXP = 'NOT REGEXP';
1524 $messagesearch = '';
1527 // Need to concat these back together for parser to work.
1528 foreach($searchterms as $searchterm){
1529 if ($searchstring != '') {
1530 $searchstring .= ' ';
1532 $searchstring .= $searchterm;
1535 // We need to allow quoted strings for the search. The quotes *should* be stripped
1536 // by the parser, but this should be examined carefully for security implications.
1537 $searchstring = str_replace("\\\"","\"",$searchstring);
1538 $parser = new search_parser();
1539 $lexer = new search_lexer($parser);
1541 if ($lexer->parse($searchstring)) {
1542 $parsearray = $parser->get_parsed_array();
1543 // Experimental feature under 1.8! MDL-8830
1544 // Use alternative text searches if defined
1545 // This feature only works under mysql until properly implemented for other DBs
1546 // Requires manual creation of text index for forum_posts before enabling it:
1547 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
1548 // Experimental feature under 1.8! MDL-8830
1549 if (!empty($CFG->forum_usetextsearches
)) {
1550 $messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
1551 'p.userid', 'u.id', 'u.firstname',
1552 'u.lastname', 'p.modified', 'd.forum');
1554 $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
1555 'p.userid', 'u.id', 'u.firstname',
1556 'u.lastname', 'p.modified', 'd.forum');
1560 $fromsql = "{$CFG->prefix}forum_posts p,
1561 {$CFG->prefix}forum_discussions d,
1562 {$CFG->prefix}user u";
1564 $selectsql = " $messagesearch
1565 AND p.discussion = d.id
1567 AND $selectdiscussion
1570 $countsql = "SELECT COUNT(*)
1574 $searchsql = "SELECT p.*,
1582 ORDER BY p.modified DESC";
1584 $totalcount = count_records_sql($countsql);
1586 return get_records_sql($searchsql, $limitfrom, $limitnum);
1590 * Returns a list of ratings for a particular post - sorted.
1592 function forum_get_ratings($postid, $sort="u.firstname ASC") {
1594 return get_records_sql("SELECT u.*, r.rating, r.time
1595 FROM {$CFG->prefix}forum_ratings r,
1596 {$CFG->prefix}user u
1597 WHERE r.post = '$postid'
1603 * Returns a list of all new posts that have not been mailed yet
1605 function forum_get_unmailed_posts($starttime, $endtime) {
1608 return get_records_sql("SELECT p.*, d.course
1609 FROM {$CFG->prefix}forum_posts p,
1610 {$CFG->prefix}forum_discussions d
1612 AND (p.created >= '$starttime' OR d.timestart > 0)
1613 AND (p.created < '$endtime' OR p.mailnow = 1)
1614 AND p.discussion = d.id
1615 AND ((d.timestart = 0 OR d.timestart <= '$now')
1616 AND (d.timeend = 0 OR d.timeend > '$now'))
1617 ORDER BY p.modified ASC");
1621 * Marks posts before a certain time as being mailed already
1623 function forum_mark_old_posts_as_mailed($endtime) {
1625 // Find out posts those are not showing immediately so we can exclude them
1627 $delayed_posts = get_records_sql("SELECT p.id, p.discussion
1628 FROM {$CFG->prefix}forum_posts p,
1629 {$CFG->prefix}forum_discussions d
1631 AND p.discussion = d.id
1632 AND d.timestart > '$now'");
1633 $delayed_ids = array();
1634 if ($delayed_posts) {
1635 foreach ($delayed_posts as $post) {
1636 $delayed_ids[] = $post->id
;
1641 return execute_sql("UPDATE {$CFG->prefix}forum_posts
1643 WHERE id NOT IN (".implode(',',$delayed_ids).")
1644 AND (created < '$endtime' OR mailnow = 1)
1645 AND mailed ='0'", false);
1649 * Get all the posts for a user in a forum suitable for forum_print_post
1651 function forum_get_user_posts($forumid, $userid) {
1654 return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1655 FROM {$CFG->prefix}forum f,
1656 {$CFG->prefix}forum_discussions d,
1657 {$CFG->prefix}forum_posts p,
1658 {$CFG->prefix}user u
1659 WHERE f.id = '$forumid'
1661 AND p.discussion = d.id
1662 AND p.userid = '$userid'
1664 ORDER BY p.modified ASC");
1668 * Given a log entry, return the forum post details for it.
1670 function forum_get_post_from_log($log) {
1673 if ($log->action
== "add post") {
1675 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1676 u.firstname, u.lastname, u.email, u.picture
1677 FROM {$CFG->prefix}forum_discussions d,
1678 {$CFG->prefix}forum_posts p,
1679 {$CFG->prefix}forum f,
1680 {$CFG->prefix}user u
1681 WHERE p.id = '$log->info'
1682 AND d.id = p.discussion
1684 AND u.deleted <> '1'
1685 AND f.id = d.forum");
1688 } else if ($log->action
== "add discussion") {
1690 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1691 u.firstname, u.lastname, u.email, u.picture
1692 FROM {$CFG->prefix}forum_discussions d,
1693 {$CFG->prefix}forum_posts p,
1694 {$CFG->prefix}forum f,
1695 {$CFG->prefix}user u
1696 WHERE d.id = '$log->info'
1697 AND d.firstpost = p.id
1699 AND u.deleted <> '1'
1700 AND f.id = d.forum");
1706 * Given a discussion id, return the first post from the discussion
1708 function forum_get_firstpost_from_discussion($discussionid) {
1711 return get_record_sql("SELECT p.*
1712 FROM {$CFG->prefix}forum_discussions d,
1713 {$CFG->prefix}forum_posts p
1714 WHERE d.id = '$discussionid'
1715 AND d.firstpost = p.id ");
1719 * Returns an array of counts of replies to each discussion (optionally in one forum or course and/or user)
1721 function forum_count_discussion_replies($forum='0', $course='0', $user='0') {
1724 $forumselect = $courseselect = $userselect = '';
1727 $forumselect = " AND d.forum = '$forum'";
1730 $courseselect = " AND d.course = '$course'";
1733 $userselect = " AND d.userid = '$user'";
1735 return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid
1736 FROM {$CFG->prefix}forum_posts p,
1737 {$CFG->prefix}forum_discussions d
1738 WHERE p.parent > 0 $forumselect $courseselect $userselect
1739 AND p.discussion = d.id
1740 GROUP BY p.discussion");
1744 * How many unrated posts are in the given discussion for a given user?
1746 function forum_count_unrated_posts($discussionid, $userid) {
1748 if ($posts = get_record_sql("SELECT count(*) as num
1749 FROM {$CFG->prefix}forum_posts
1751 AND discussion = '$discussionid'
1752 AND userid <> '$userid' ")) {
1754 if ($rated = get_record_sql("SELECT count(*) as num
1755 FROM {$CFG->prefix}forum_posts p,
1756 {$CFG->prefix}forum_ratings r
1757 WHERE p.discussion = '$discussionid'
1759 AND r.userid = '$userid'")) {
1760 $difference = $posts->num
- $rated->num
;
1761 if ($difference > 0) {
1764 return 0; // Just in case there was a counting error
1775 * Get all discussions in a forum
1777 function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC",
1778 $user=0, $fullpost=true, $currentgroup=-1, $limit=0, $userlastmodified=false) {
1783 if (!$cm = get_coursemodule_from_instance('forum', $forum)) {
1784 error('Course Module ID was incorrect');
1787 if (!empty($CFG->forum_enabletimedposts
)) {
1789 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
1791 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
1793 $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
1794 if (!empty($USER->id
)) {
1795 $timelimit .= " OR d.userid = '$USER->id'";
1802 $userselect = " AND u.id = '$user' ";
1813 if ($currentgroup == -1) {
1814 $currentgroup = get_current_group($cm->course
);
1817 if ($currentgroup) {
1818 $groupselect = " AND (d.groupid = '$currentgroup' OR d.groupid = -1) ";
1823 if (empty($forumsort)) {
1824 $forumsort = "d.timemodified DESC";
1826 if (empty($fullpost)) {
1827 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
1832 if (empty($userlastmodified)) { // We don't need to know this
1836 $umfields = ', um.firstname AS umfirstname, um.lastname AS umlastname';
1837 $umtable = ' LEFT JOIN '.$CFG->prefix
.'user um on (d.usermodified = um.id)';
1840 //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.
1841 if ($CFG->dbfamily
== 'postgres' ||
$CFG->dbfamily
== 'mssql' ||
$CFG->dbfamily
== 'oracle') {
1842 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1843 u.firstname, u.lastname, u.email, u.picture $umfields
1844 FROM {$CFG->prefix}forum_discussions d
1845 JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
1846 JOIN {$CFG->prefix}user u ON p.userid = u.id
1848 WHERE d.forum = '$forum'
1850 $timelimit $groupselect $userselect
1851 ORDER BY $forumsort", $limitfrom, $limitnum);
1852 } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query)
1853 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1854 u.firstname, u.lastname, u.email, u.picture $umfields
1855 FROM ({$CFG->prefix}forum_posts p,
1856 {$CFG->prefix}user u,
1857 {$CFG->prefix}forum_discussions d)
1859 WHERE d.forum = '$forum'
1860 AND p.discussion = d.id
1862 AND p.userid = u.id $timelimit $groupselect $userselect
1863 ORDER BY $forumsort", $limitfrom, $limitnum);
1870 * Get all discussions started by a particular user in a course (or group)
1871 * This function no longer used ...
1873 function forum_get_user_discussions($courseid, $userid, $groupid=0) {
1877 $groupselect = " AND d.groupid = '$groupid' ";
1882 return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture,
1883 f.type as forumtype, f.name as forumname, f.id as forumid
1884 FROM {$CFG->prefix}forum_discussions d,
1885 {$CFG->prefix}forum_posts p,
1886 {$CFG->prefix}user u,
1887 {$CFG->prefix}forum f
1888 WHERE d.course = '$courseid'
1889 AND p.discussion = d.id
1892 AND u.id = '$userid'
1893 AND d.forum = f.id $groupselect
1894 ORDER BY p.created DESC");
1898 * Returns list of user objects that are subscribed to this forum
1900 function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) {
1904 static $resultscache = array();
1906 if ($cache && isset($resultscache[$forum->id
][$groupid])) {
1907 return $resultscache[$forum->id
][$groupid];
1911 $grouptables = ", {$CFG->prefix}groups_members gm ";
1912 $groupselect = "AND gm.groupid = '$groupid' AND u.id = gm.userid";
1919 if (forum_is_forcesubscribed($forum)) {
1920 $results = get_course_users($course->id
); // Otherwise get everyone in the course
1922 $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop,
1923 u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums
1924 FROM {$CFG->prefix}user u,
1925 {$CFG->prefix}forum_subscriptions s $grouptables
1926 WHERE s.forum = '$forum->id'
1928 AND u.deleted <> 1 $groupselect
1929 ORDER BY u.email ASC");
1931 // Guest user should never be subscribed to a forum.
1932 if ($guest = guest_user()) {
1933 unset($results[$guest->id
]);
1937 $resultscache[$forum->id
][$groupid] = $results;
1945 // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
1948 function forum_get_course_forum($courseid, $type) {
1949 // How to set up special 1-per-course forums
1952 if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
1953 // There should always only be ONE, but with the right combination of
1954 // errors there might be more. In this case, just return the oldest one (lowest ID).
1955 foreach ($forums as $forum) {
1956 return $forum; // ie the first one
1960 // Doesn't exist, so create one now.
1961 $forum->course
= $courseid;
1962 $forum->type
= "$type";
1963 switch ($forum->type
) {
1965 $forum->name
= addslashes(get_string("namenews", "forum"));
1966 $forum->intro
= addslashes(get_string("intronews", "forum"));
1967 $forum->forcesubscribe
= FORUM_FORCESUBSCRIBE
;
1968 $forum->assessed
= 0;
1969 if ($courseid == SITEID
) {
1970 $forum->name
= get_string("sitenews");
1971 $forum->forcesubscribe
= 0;
1975 $forum->name
= addslashes(get_string("namesocial", "forum"));
1976 $forum->intro
= addslashes(get_string("introsocial", "forum"));
1977 $forum->assessed
= 0;
1978 $forum->forcesubscribe
= 0;
1981 notify("That forum type doesn't exist!");
1986 $forum->timemodified
= time();
1987 $forum->id
= insert_record("forum", $forum);
1989 if (! $module = get_record("modules", "name", "forum")) {
1990 notify("Could not find forum module!!");
1993 $mod->course
= $courseid;
1994 $mod->module
= $module->id
;
1995 $mod->instance
= $forum->id
;
1997 if (! $mod->coursemodule
= add_course_module($mod) ) { // assumes course/lib.php is loaded
1998 notify("Could not add a new course module to the course '" . format_string($course->fullname
) . "'");
2001 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
2002 notify("Could not add the new course module to that section");
2005 if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule
)) {
2006 notify("Could not update the course module with the correct section");
2009 include_once("$CFG->dirroot/course/lib.php");
2010 rebuild_course_cache($courseid);
2012 return get_record("forum", "id", "$forum->id");
2017 * Given the data about a posting, builds up the HTML to display it and
2018 * returns the HTML in a string. This is designed for sending via HTML email.
2020 function forum_make_mail_post(&$post, $user, $touser, $course,
2021 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2026 // the old caching was removed for now, because it did not work due to recent changes in cron
2028 $post->forum
= get_field('forum_discussions', 'forum', 'id', $post->discussion
);
2030 if (!$cm = get_coursemodule_from_instance('forum', $post->forum
)) {
2031 mtrace('Course Module ID was incorrect');
2033 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
2035 // format the post body
2036 $options = new object();
2037 $options->para
= true;
2038 $formattedtext = format_text(trusttext_strip($post->message
), $post->format
, $options, $course->id
);
2040 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
2042 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
2043 $output .= print_user_picture($user->id
, $course->id
, $user->picture
, false, true);
2046 if ($post->parent
) {
2047 $output .= '<td class="topic">';
2049 $output .= '<td class="topic starter">';
2051 $output .= '<div class="subject">'.format_string($post->subject
).'</div>';
2053 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext));
2055 $by->name
= '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.$user->id
.'&course='.$course->id
.'">'.$fullname.'</a>';
2056 $by->date
= userdate($post->modified
, '', $touser->timezone
);
2057 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
2059 $output .= '</td></tr>';
2061 $output .= '<tr><td class="left side" valign="top">';
2062 if ($group = groups_get_all_groups($course->id
, $user->id
)) {
2063 $output .= print_group_picture($group, $course->id
, false, true, true);
2065 $output .= ' ';
2068 $output .= '</td><td class="content">';
2070 if ($post->attachment
) {
2071 $post->course
= $course->id
;
2072 $output .= '<div class="attachments">';
2073 $output .= forum_print_attachments($post, 'html');
2074 $output .= "</div>";
2077 $output .= $formattedtext;
2080 $commands = array();
2082 if ($post->parent
) {
2083 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.
2084 $post->discussion
.'&parent='.$post->parent
.'">'.get_string('parent', 'forum').'</a>';
2088 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot
.'/mod/forum/post.php?reply='.$post->id
.'">'.
2089 get_string('reply', 'forum').'</a>';
2092 $output .= '<div class="commands">';
2093 $output .= implode(' | ', $commands);
2094 $output .= '</div>';
2096 // Context link to post if required
2098 $output .= '<div class="link">';
2099 $output .= '<a target="_blank" href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'#p'.$post->id
.'">'.
2100 get_string('postincontext', 'forum').'</a>';
2101 $output .= '</div>';
2105 $output .= '<div class="footer">'.$footer.'</div>';
2107 $output .= '</td></tr></table>'."\n\n";
2113 * Print a forum post
2115 * @param object $post The post to print.
2116 * @param integer $courseid The course this post belongs to.
2117 * @param boolean $ownpost Whether this post belongs to the current user.
2118 * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
2119 * @param boolean $link Just print a shortened version of the post as a link to the full post.
2120 * @param object $ratings -- I don't really know --
2121 * @param string $footer Extra stuff to print after the message.
2122 * @param string $highlight Space-separated list of terms to highlight.
2123 * @param int $post_read true, false or -99. If we already know whether this user
2124 * has read this post, pass that in, otherwise, pass in -99, and this
2125 * function will work it out.
2126 * @param boolean $dummyifcantsee When forum_user_can_see_post says that
2127 * the current user can't see this post, if this argument is true
2128 * (the default) then print a dummy 'you can't see this post' post.
2129 * If false, don't output anything at all.
2131 function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false,
2132 $ratings=NULL, $footer="", $highlight="", $post_read=-99, $dummyifcantsee=true) {
2136 static $stredit, $strdelete, $strreply, $strparent, $strprune;
2137 static $strpruneheading, $displaymode;
2138 static $strmarkread, $strmarkunread, $istracked;
2141 $discussion = get_record('forum_discussions', 'id', $post->discussion
);
2142 if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum
)) {
2143 error('Course Module ID was incorrect');
2145 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
2148 if (!forum_user_can_see_post($post->forum
,$post->discussion
,$post)) {
2149 if (!$dummyifcantsee) {
2152 echo '<a id="p'.$post->id
.'"></a>';
2153 echo '<table cellspacing="0" class="forumpost">';
2154 echo '<tr class="header"><td class="picture left">';
2155 // print_user_picture($post->userid, $courseid, $post->picture);
2157 if ($post->parent
) {
2158 echo '<td class="topic">';
2160 echo '<td class="topic starter">';
2162 echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
2163 echo '<div class="author">';
2164 print_string('forumauthorhidden','forum');
2165 echo '</div></td></tr>';
2167 echo '<tr><td class="left side">';
2172 echo '</td><td class="content">'."\n";
2173 echo get_string('forumbodyhidden','forum');
2174 echo '</td></tr></table>';
2178 if (empty($stredit)) {
2179 $stredit = get_string('edit', 'forum');
2180 $strdelete = get_string('delete', 'forum');
2181 $strreply = get_string('reply', 'forum');
2182 $strparent = get_string('parent', 'forum');
2183 $strpruneheading = get_string('pruneheading', 'forum');
2184 $strprune = get_string('prune', 'forum');
2185 $displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode
);
2186 $strmarkread = get_string('markread', 'forum');
2187 $strmarkunread = get_string('markunread', 'forum');
2189 if (!empty($post->forum
)) {
2190 $istracked = (forum_tp_can_track_forums($post->forum
) &&
2191 forum_tp_is_tracked($post->forum
));
2198 if ($post_read == -99) { // If we don't know yet...
2199 // The front page can display a news item post to non-logged in users. This should
2200 // always appear as 'read'.
2201 $post_read = empty($USER) ||
forum_tp_is_post_read($USER->id
, $post);
2204 $read_style = ' read';
2206 $read_style = ' unread';
2207 echo '<a name="unread"></a>';
2213 echo '<a id="p'.$post->id
.'"></a>';
2214 echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
2216 echo '<tr class="header"><td class="picture left">';
2217 print_user_picture($post->userid
, $courseid, $post->picture
);
2220 if ($post->parent
) {
2221 echo '<td class="topic">';
2223 echo '<td class="topic starter">';
2226 if (!empty($post->subjectnoformat
)) {
2227 echo '<div class="subject">'.$post->subject
.'</div>';
2229 echo '<div class="subject">'.format_string($post->subject
).'</div>';
2232 echo '<div class="author">';
2233 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2234 $by->name
= '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.
2235 $post->userid
.'&course='.$courseid.'">'.$fullname.'</a>';
2236 $by->date
= userdate($post->modified
);
2237 print_string('bynameondate', 'forum', $by);
2238 echo '</div></td></tr>';
2240 echo '<tr><td class="left side">';
2241 if ($group = groups_get_all_groups($courseid, $post->userid
)) {
2242 print_group_picture($group, $courseid, false, false, true);
2249 echo '</td><td class="content">'."\n";
2251 if ($post->attachment
) {
2252 $post->course
= $courseid;
2253 $post->forum
= get_field('forum_discussions', 'forum', 'id', $post->discussion
);
2254 echo '<div class="attachments">';
2255 $attachedimages = forum_print_attachments($post);
2258 $attachedimages = '';
2262 $options = new Object;
2263 $options->para
= false;
2264 $options->trusttext
= true;
2265 if ($link and (strlen(strip_tags($post->message
)) > $CFG->forum_longpost
)) {
2266 // Print shortened version
2267 echo format_text(forum_shorten_post($post->message
), $post->format
, $options, $courseid);
2268 $numwords = count_words(strip_tags($post->message
));
2269 echo '<p><a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'">';
2270 echo get_string('readtherest', 'forum');
2271 echo '</a> ('.get_string('numwords', '', $numwords).')...</p>';
2273 // Print whole message
2275 echo highlight($highlight, format_text($post->message
, $post->format
, $options, $courseid));
2277 echo format_text($post->message
, $post->format
, $options, $courseid);
2279 echo $attachedimages;
2285 $commands = array();
2288 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
2289 // Don't display the mark read / unread controls in this case.
2290 if ($CFG->forum_usermarksread
&& !empty($USER)) {
2292 $mcmd = '&mark=unread&postid='.$post->id
;
2293 $mtxt = $strmarkunread;
2295 $mcmd = '&mark=read&postid='.$post->id
;
2296 $mtxt = $strmarkread;
2298 if ($displaymode == FORUM_MODE_THREADED
) {
2299 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.
2300 $post->discussion
.'&parent='.$post->id
.$mcmd.'">'.$mtxt.'</a>';
2302 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.
2303 $post->discussion
.$mcmd.'#p'.$post->id
.'">'.$mtxt.'</a>';
2308 if ($post->parent
) { // Zoom in to the parent specifically
2309 if ($displaymode == FORUM_MODE_THREADED
) {
2310 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.
2311 $post->discussion
.'&parent='.$post->parent
.'">'.$strparent.'</a>';
2313 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.
2314 $post->discussion
.'#p'.$post->parent
.'">'.$strparent.'</a>';
2318 $forumtype = get_field('forum', 'type', 'id', $post->forum
);
2320 $age = time() - $post->created
;
2321 // Hack for allow to edit news posts those are not displayed yet until they are displayed
2323 && $forumtype == 'news'
2324 && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) {
2327 $editanypost = has_capability('mod/forum:editanypost', $modcontext);
2331 if ($ownpost or $editanypost) {
2332 if (($age < $CFG->maxeditingtime
) or $editanypost) {
2333 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/post.php?edit='.$post->id
.'">'.$stredit.'</a>';
2337 if (has_capability('mod/forum:splitdiscussions', $modcontext)
2338 && $post->parent
&& $forumtype != 'single') {
2340 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/post.php?prune='.$post->id
.
2341 '" title="'.$strpruneheading.'">'.$strprune.'</a>';
2344 if (($ownpost and $age < $CFG->maxeditingtime
2345 and has_capability('mod/forum:deleteownpost', $modcontext))
2346 or has_capability('mod/forum:deleteanypost', $modcontext)) {
2347 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/post.php?delete='.$post->id
.'">'.$strdelete.'</a>';
2351 $commands[] = '<a href="'.$CFG->wwwroot
.'/mod/forum/post.php?reply='.$post->id
.'">'.$strreply.'</a>';
2354 echo '<div class="commands">';
2355 echo implode(' | ', $commands);
2361 $ratingsmenuused = false;
2362 if (!empty($ratings) and !empty($USER->id
)) {
2363 echo '<div class="ratings">';
2365 if ($ratings->assesstimestart
and $ratings->assesstimefinish
) {
2366 if ($post->created
< $ratings->assesstimestart
or $post->created
> $ratings->assesstimefinish
) {
2367 $useratings = false;
2371 $mypost = ($USER->id
== $post->userid
);
2373 $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext);
2375 if ($canviewallratings and !$mypost) {
2376 forum_print_ratings_mean($post->id
, $ratings->scale
, $canviewallratings);
2377 if (!empty($ratings->allow
)) {
2379 forum_print_rating_menu($post->id
, $USER->id
, $ratings->scale
);
2380 $ratingsmenuused = true;
2383 } else if ($mypost) {
2384 forum_print_ratings_mean($post->id
, $ratings->scale
, true);
2386 } else if (!empty($ratings->allow
) ) {
2387 forum_print_rating_menu($post->id
, $USER->id
, $ratings->scale
);
2388 $ratingsmenuused = true;
2394 // Link to post if required
2397 echo '<div class="link">';
2398 if ($post->replies
== 1) {
2399 $replystring = get_string('repliesone', 'forum', $post->replies
);
2401 $replystring = get_string('repliesmany', 'forum', $post->replies
);
2403 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'">'.
2404 get_string('discussthistopic', 'forum').'</a> ('.$replystring.')';
2409 echo '<div class="footer">'.$footer.'</div>';
2411 echo '</td></tr></table>'."\n\n";
2413 if ($istracked && !$CFG->forum_usermarksread
&& !empty($post->forum
)) {
2414 forum_tp_mark_post_read($USER->id
, $post, $post->forum
);
2417 return $ratingsmenuused;
2422 * This function prints the overview of a discussion in the forum listing.
2423 * It needs some discussion information and some post information, these
2424 * happen to be combined for efficiency in the $post parameter by the function
2425 * that calls this one: forum_print_latest_discussions()
2427 * @param object $post The post object (passed by reference for speed).
2428 * @param object $forum The forum object.
2429 * @param int $group Current group.
2430 * @param string $datestring Format to use for the dates.
2431 * @param boolean $cantrack Is tracking enabled for this forum.
2432 * @param boolean $forumtracked Is the user tracking this forum.
2433 * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
2435 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
2436 $cantrack=true, $forumtracked=true, $canviewparticipants=true) {
2441 static $strmarkalldread;
2444 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
2445 error('Course Module ID was incorrect');
2447 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
2450 if (!isset($rowcount)) {
2452 $strmarkalldread = get_string('markalldread', 'forum');
2454 $rowcount = ($rowcount +
1) %
2;
2457 $post->subject
= format_string($post->subject
,true);
2460 echo '<tr class="discussion r'.$rowcount.'">';
2463 echo '<td class="topic starter">';
2464 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'">'.$post->subject
.'</a>';
2468 echo '<td class="picture">';
2469 print_user_picture($post->userid
, $forum->course
, $post->picture
);
2473 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2474 echo '<td class="author">';
2475 echo '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.$post->userid
.'&course='.$forum->course
.'">'.$fullname.'</a>';
2479 if ($group !== -1) { // Groups are active - group is a group data object or NULL
2480 echo '<td class="picture group">';
2481 if (!empty($group->picture
) and empty($group->hidepicture
)) {
2482 print_group_picture($group, $forum->course
, false, false, true);
2483 } else if (isset($group->id
)) {
2484 if($canviewparticipants) {
2485 echo '<a href="'.$CFG->wwwroot
.'/user/index.php?id='.$forum->course
.'&group='.$group->id
.'">'.$group->name
.'</a>';
2493 if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies
2494 echo '<td class="replies">';
2495 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'">';
2496 echo $post->replies
.'</a>';
2500 echo '<td class="replies">';
2501 if ($forumtracked) {
2502 if ($post->unread
> 0) {
2503 echo '<span class="unread">';
2504 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.'#unread">';
2507 echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot
.'/mod/forum/markposts.php?f='.
2508 $forum->id
.'&d='.$post->discussion
.'&mark=read&returnpage=view.php">' .
2509 '<img src="'.$CFG->pixpath
.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
2512 echo '<span class="read">';
2517 echo '<span class="read">';
2525 echo '<td class="lastpost">';
2526 $usedate = (empty($post->timemodified
)) ?
$post->modified
: $post->timemodified
; // Just in case
2527 $parenturl = (empty($post->lastpostid
)) ?
'' : '&parent='.$post->lastpostid
;
2528 $usermodified->id
= $post->usermodified
;
2529 $usermodified->firstname
= $post->umfirstname
;
2530 $usermodified->lastname
= $post->umlastname
;
2531 echo '<a href="'.$CFG->wwwroot
.'/user/view.php?id='.$post->usermodified
.'&course='.$forum->course
.'">'.
2532 fullname($usermodified).'</a><br />';
2533 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/discuss.php?d='.$post->discussion
.$parenturl.'">'.
2534 userdate($usedate, $datestring).'</a>';
2543 * Given a post object that we already know has a long message
2544 * this function truncates the message nicely to the first
2545 * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
2547 function forum_shorten_post($message) {
2553 $length = strlen($message);
2558 for ($i=0; $i<$length; $i++
) {
2559 $char = $message[$i];
2581 if ($count > $CFG->forum_shortpost
) {
2591 return substr($message, 0, $truncate);
2596 * Print the multiple ratings on a post given to the current user by others.
2597 * Scale is an array of ratings
2599 function forum_print_ratings_mean($postid, $scale, $link=true) {
2603 $mean = forum_get_ratings_mean($postid, $scale);
2607 if (empty($strratings)) {
2608 $strratings = get_string("ratings", "forum");
2611 echo "$strratings: ";
2613 link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600);
2622 * Return the mean rating of a post given to the current user by others.
2623 * Scale is an array of possible ratings in the scale
2624 * Ratings is an optional simple array of actual ratings (just integers)
2626 function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
2630 if ($rates = get_records("forum_ratings", "post", $postid)) {
2631 foreach ($rates as $rate) {
2632 $ratings[] = $rate->rating
;
2637 $count = count($ratings);
2642 } else if ($count == 1) {
2643 return $scale[$ratings[0]];
2647 foreach ($ratings as $rating) {
2650 $mean = round( ((float)$total/(float)$count) +
0.001); // Little fudge factor so that 0.5 goes UP
2652 if (isset($scale[$mean])) {
2653 return $scale[$mean]." ($count)";
2655 return "$mean ($count)"; // Should never happen, hopefully
2661 * Return a summary of post ratings given to the current user by others.
2662 * Scale is an array of possible ratings in the scale
2663 * Ratings is an optional simple array of actual ratings (just integers)
2665 function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
2669 if ($rates = get_records("forum_ratings", "post", $postid)) {
2670 foreach ($rates as $rate) {
2671 $rating[] = $rate->rating
;
2677 if (!$count = count($ratings)) {
2682 foreach ($scale as $key => $scaleitem) {
2683 $sumrating[$key] = 0;
2686 foreach ($ratings as $rating) {
2687 $sumrating[$rating]++
;
2691 foreach ($scale as $key => $scaleitem) {
2692 $summary = $sumrating[$key].$summary;
2694 $summary = "/$summary";
2701 * Print the menu of ratings as part of a larger form.
2702 * If the post has already been - set that value.
2703 * Scale is an array of ratings
2705 function forum_print_rating_menu($postid, $userid, $scale) {
2709 if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
2710 $rating->rating
= FORUM_UNSET_POST_RATING
;
2713 if (empty($strrate)) {
2714 $strrate = get_string("rate", "forum");
2716 $scale = array(FORUM_UNSET_POST_RATING
=> $strrate.'...') +
$scale;
2717 choose_from_menu($scale, $postid, $rating->rating
, '');
2721 * Print the drop down that allows the user to select how they want to have
2722 * the discussion displayed.
2723 * @param $id - forum id if $forumtype is 'single',
2724 * discussion id for any other forum type
2725 * @param $mode - forum layout mode
2726 * @param $forumtype - optional
2728 function forum_print_mode_form($id, $mode, $forumtype='') {
2729 global $FORUM_LAYOUT_MODES;
2731 if ($forumtype == 'single') {
2732 popup_form("view.php?f=$id&mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2734 popup_form("discuss.php?d=$id&mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2741 function forum_search_form($course, $search='') {
2744 $output = '<div class="forumsearch">';
2745 $output .= '<form action="'.$CFG->wwwroot
.'/mod/forum/search.php" style="display:inline">';
2746 $output .= '<fieldset class="invisiblefieldset">';
2747 $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
2748 $output .= '<input name="search" type="text" size="18" value="'.$search.'" alt="search" />';
2749 $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
2750 $output .= '<input name="id" type="hidden" value="'.$course->id
.'" />';
2751 $output .= '</fieldset>';
2752 $output .= '</form>';
2753 $output .= '</div>';
2762 function forum_set_return() {
2763 global $CFG, $SESSION;
2765 if (! isset($SESSION->fromdiscussion
)) {
2766 if (!empty($_SERVER['HTTP_REFERER'])) {
2767 $referer = $_SERVER['HTTP_REFERER'];
2771 // If the referer is NOT a login screen then save it.
2772 if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
2773 $SESSION->fromdiscussion
= $_SERVER["HTTP_REFERER"];
2782 function forum_go_back_to($default) {
2785 if (!empty($SESSION->fromdiscussion
)) {
2786 $returnto = $SESSION->fromdiscussion
;
2787 unset($SESSION->fromdiscussion
);
2795 * Creates a directory file name, suitable for make_upload_directory()
2797 function forum_file_area_name($post) {
2800 return "$post->course/$CFG->moddata/forum/$post->forum/$post->id";
2806 function forum_file_area($post) {
2807 return make_upload_directory( forum_file_area_name($post) );
2813 function forum_delete_old_attachments($post, $exception="") {
2816 * Deletes all the user files in the attachments area for a post
2817 * EXCEPT for any file named $exception
2819 if ($basedir = forum_file_area($post)) {
2820 if ($files = get_directory_list($basedir)) {
2821 foreach ($files as $file) {
2822 if ($file != $exception) {
2823 unlink("$basedir/$file");
2824 notify("Existing file '$file' has been deleted!");
2828 if (!$exception) { // Delete directory as well, if empty
2835 * Given a discussion object that is being moved to forumid,
2836 * this function checks all posts in that discussion
2837 * for attachments, and if any are found, these are
2838 * moved to the new forum directory.
2840 function forum_move_attachments($discussion, $forumid) {
2844 require_once($CFG->dirroot
.'/lib/uploadlib.php');
2848 if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
2849 foreach ($posts as $oldpost) {
2850 $oldpost->course
= $discussion->course
;
2851 $oldpost->forum
= $discussion->forum
;
2852 $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
2853 if (is_dir($oldpostdir)) {
2854 $newpost = $oldpost;
2855 $newpost->forum
= $forumid;
2856 $newpostdir = forum_file_area_name($newpost);
2857 // take off the last directory because otherwise we're renaming to a directory that already exists
2858 // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
2859 make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
2860 $newpostdir = $CFG->dataroot
.'/'.forum_file_area_name($newpost);
2861 $files = get_directory_list($oldpostdir); // get it before we rename it.
2862 if (! @rename
($oldpostdir, $newpostdir)) {
2865 foreach ($files as $file) {
2866 clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
2875 * if return=html, then return a html string.
2876 * if return=text, then return a text-only string.
2877 * otherwise, print HTML for non-images, and return image HTML
2879 function forum_print_attachments($post, $return=NULL) {
2883 $filearea = forum_file_area_name($post);
2888 if ($basedir = forum_file_area($post)) {
2889 if ($files = get_directory_list($basedir)) {
2890 $strattachment = get_string("attachment", "forum");
2891 foreach ($files as $file) {
2892 $icon = mimeinfo("icon", $file);
2893 $type = mimeinfo("type", $file);
2894 if ($CFG->slasharguments
) {
2895 $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
2897 $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
2899 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
2901 if ($return == "html") {
2902 $output .= "<a href=\"$ffurl\">$image</a> ";
2903 $output .= "<a href=\"$ffurl\">$file</a><br />";
2905 } else if ($return == "text") {
2906 $output .= "$strattachment $file:\n$ffurl\n";
2909 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
2910 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
2912 echo "<a href=\"$ffurl\">$image</a> ";
2913 echo filter_text("<a href=\"$ffurl\">$file</a><br />");
2924 return $imagereturn;
2927 * If successful, this function returns the name of the file
2928 * @param $post is a full post record, including course and forum
2929 * @param $newfile is a full upload array from $_FILES
2930 * @param $message is a string to hold the messages.
2936 function forum_add_attachment($post, $inputname,&$message) {
2940 if (!$forum = get_record("forum", "id", $post->forum
)) {
2944 if (!$course = get_record("course", "id", $forum->course
)) {
2948 require_once($CFG->dirroot
.'/lib/uploadlib.php');
2949 $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes
,true,true);
2950 $dir = forum_file_area_name($post);
2951 if ($um->process_file_uploads($dir)) {
2952 $message .= $um->get_errors();
2953 return $um->get_new_filename();
2955 $message .= $um->get_errors();
2962 function forum_add_new_post($post,&$message) {
2966 $post->created
= $post->modified
= time();
2967 $post->mailed
= "0";
2968 $post->userid
= $USER->id
;
2969 $post->attachment
= "";
2971 if (! $post->id
= insert_record("forum_posts", $post)) {
2975 if ($post->attachment
= forum_add_attachment($post, 'attachment',$message)) {
2976 set_field("forum_posts", "attachment", $post->attachment
, "id", $post->id
);
2979 // Update discussion modified date
2980 set_field("forum_discussions", "timemodified", $post->modified
, "id", $post->discussion
);
2981 set_field("forum_discussions", "usermodified", $post->userid
, "id", $post->discussion
);
2983 if (forum_tp_can_track_forums($post->forum
) && forum_tp_is_tracked($post->forum
)) {
2984 forum_tp_mark_post_read($post->userid
, $post, $post->forum
);
2993 function forum_update_post($post,&$message) {
2997 $post->modified
= time();
2999 if (!$post->parent
) { // Post is a discussion starter - update discussion title too
3000 set_field("forum_discussions", "name", $post->subject
, "id", $post->discussion
);
3003 if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
3004 $post->attachment
= $newfilename;
3006 unset($post->attachment
);
3009 // Update discussion modified date
3010 set_field("forum_discussions", "timemodified", $post->modified
, "id", $post->discussion
);
3011 set_field("forum_discussions", "usermodified", $post->userid
, "id", $post->discussion
);
3013 if (forum_tp_can_track_forums($post->forum
) && forum_tp_is_tracked($post->forum
)) {
3014 forum_tp_mark_post_read($post->userid
, $post, $post->forum
);
3017 return update_record("forum_posts", $post);
3021 * Given an object containing all the necessary data,
3022 * create a new discussion and return the id
3024 function forum_add_discussion($discussion,&$message) {
3030 // The first post is stored as a real post, and linked
3031 // to from the discuss entry.
3033 $post->discussion
= 0;
3035 $post->userid
= $USER->id
;
3036 $post->created
= $timenow;
3037 $post->modified
= $timenow;
3039 $post->subject
= $discussion->name
;
3040 $post->message
= $discussion->intro
;
3041 $post->attachment
= "";
3042 $post->forum
= $discussion->forum
;
3043 $post->course
= $discussion->course
;
3044 $post->format
= $discussion->format
;
3045 $post->mailnow
= $discussion->mailnow
;
3047 if (! $post->id
= insert_record("forum_posts", $post) ) {
3051 if ($post->attachment
= forum_add_attachment($post, 'attachment',$message)) {
3052 set_field("forum_posts", "attachment", $post->attachment
, "id", $post->id
); //ignore errors
3055 // Now do the main entry for the discussion,
3056 // linking to this first post
3058 $discussion->firstpost
= $post->id
;
3059 $discussion->timemodified
= $timenow;
3060 $discussion->usermodified
= $post->userid
;
3061 $discussion->userid
= $USER->id
;
3063 if (! $post->discussion
= insert_record("forum_discussions", $discussion) ) {
3064 delete_records("forum_posts", "id", $post->id
);
3068 // Finally, set the pointer on the post.
3069 if (! set_field("forum_posts", "discussion", $post->discussion
, "id", $post->id
)) {
3070 delete_records("forum_posts", "id", $post->id
);
3071 delete_records("forum_discussions", "id", $post->discussion
);
3075 if (forum_tp_can_track_forums($post->forum
) && forum_tp_is_tracked($post->forum
)) {
3076 forum_tp_mark_post_read($post->userid
, $post, $post->forum
);
3079 return $post->discussion
;
3086 function forum_delete_discussion($discussion, $fulldelete=false) {
3087 // $discussion is a discussion record object
3091 if ($posts = get_records("forum_posts", "discussion", $discussion->id
)) {
3092 foreach ($posts as $post) {
3093 $post->course
= $discussion->course
;
3094 $post->forum
= $discussion->forum
;
3095 if (! delete_records("forum_ratings", "post", "$post->id")) {
3098 if (! forum_delete_post($post, $fulldelete)) {
3104 forum_tp_delete_read_records(-1, -1, $discussion->id
);
3106 if (! delete_records("forum_discussions", "id", "$discussion->id")) {
3117 function forum_delete_post($post, $children=false) {
3118 if ($childposts = get_records('forum_posts', 'parent', $post->id
)) {
3120 foreach ($childposts as $childpost) {
3121 forum_delete_post($childpost, true);
3127 if (delete_records("forum_posts", "id", $post->id
)) {
3128 delete_records("forum_ratings", "post", $post->id
); // Just in case
3130 forum_tp_delete_read_records(-1, $post->id
);
3132 if ($post->attachment
) {
3133 $discussion = get_record("forum_discussions", "id", $post->discussion
);
3134 $post->course
= $discussion->course
;
3135 $post->forum
= $discussion->forum
;
3136 forum_delete_old_attachments($post);
3139 // Just in case we are deleting the last post
3140 forum_discussion_update_last_post($post->discussion
);
3150 function forum_count_replies($post, $children=true) {
3154 if ($childposts = get_records('forum_posts', 'parent', $post->id
)) {
3155 foreach ($childposts as $childpost) {
3156 $count ++
; // For this child
3157 $count +
= forum_count_replies($childpost, true);
3161 $count +
= count_records('forum_posts', 'parent', $post->id
);
3171 function forum_forcesubscribe($forumid, $value=1) {
3172 return set_field("forum", "forcesubscribe", $value, "id", $forumid);
3178 function forum_is_forcesubscribed($forum) {
3179 if (isset($forum->forcesubscribe
)) { // then we use that
3180 return ($forum->forcesubscribe
== FORUM_FORCESUBSCRIBE
);
3181 } else { // Check the database
3182 return (get_field('forum', 'forcesubscribe', 'id', $forum) == FORUM_FORCESUBSCRIBE
);
3189 function forum_is_subscribed($userid, $forumid) {
3190 if (forum_is_forcesubscribed($forumid)) {
3193 return record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid);
3197 * Adds user to the subscriber list
3199 function forum_subscribe($userid, $forumid) {
3201 if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) {
3205 $sub->userid
= $userid;
3206 $sub->forum
= $forumid;
3208 return insert_record("forum_subscriptions", $sub);
3212 * Removes user from the subscriber list
3214 function forum_unsubscribe($userid, $forumid) {
3215 return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid);
3219 * Given a new post, subscribes or unsubscribes as appropriate.
3220 * Returns some text which describes what happened.
3222 function forum_post_subscription($post) {
3226 $subscribed=forum_is_subscribed($USER->id
, $post->forum
);
3227 if ((isset($post->subscribe
) && $post->subscribe
&& $subscribed)
3228 ||
(!$post->subscribe
&& !$subscribed)) {
3232 if (!$forum = get_record("forum", "id", $post->forum
)) {
3236 $info->name
= fullname($USER);
3237 $info->forum
= $forum->name
;
3239 if (!empty($post->subscribe
)) {
3240 forum_subscribe($USER->id
, $post->forum
);
3241 return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
3244 forum_unsubscribe($USER->id
, $post->forum
);
3245 return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
3249 * Generate and return the subscribe or unsubscribe link for a forum.
3250 * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
3251 * @param object $context the context object for this forum.
3252 * @param array $messages text used for the link in its various states
3253 * (subscribed, unsubscribed, forcesubscribed or cantsubscribe).
3254 * Any strings not passed in are taken from the $defaultmessages array
3255 * at the top of the function.
3258 function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false, $fakelink=true) {
3260 $defaultmessages = array(
3261 'subscribed' => get_string('unsubscribe', 'forum'),
3262 'unsubscribed' => get_string('subscribe', 'forum'),
3263 'cantaccessgroup' => get_string('no'),
3264 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
3265 'cantsubscribe' => get_string('disallowsubscribe','forum')
3267 $messages = $messages +
$defaultmessages;
3269 if (forum_is_forcesubscribed($forum->id
)) {
3270 return $messages['forcesubscribed'];
3271 } else if ($forum->forcesubscribe
== FORUM_DISALLOWSUBSCRIBE
&& !has_capability('mod/forum:managesubscriptions', $context)) {
3272 return $messages['cantsubscribe'];
3273 } else if ($cantaccessagroup) {
3274 return $messages['cantaccessgroup'];
3276 if (forum_is_subscribed($USER->id
, $forum->id
)) {
3277 $linktext = $messages['subscribed'];
3278 $linktitle = get_string('subscribestop', 'forum');
3280 $linktext = $messages['unsubscribed'];
3281 $linktitle = get_string('subscribestart', 'forum');
3286 $link .= '<script type="text/javascript">';
3287 $link .= '//<![CDATA['."\n";
3288 $link .= 'document.getElementById("subscriptionlink").innerHTML = "<a title=\"' . $linktitle . '\" href=\"' . $CFG->wwwroot
.
3289 '/mod/forum/subscribe.php?id=' . $forum->id
. '\">' . $linktext . '<\/a>";';
3291 $link .= '</script>';
3292 // use <noscript> to print button in case javascript is not enabled
3293 $link .= '<noscript>';
3295 $link .= print_single_button($CFG->wwwroot
. '/mod/forum/subscribe.php?id=' . $forum->id
,
3296 '', $linktext, 'post', '_self', true, $linktitle);
3298 $link .= '</noscript>';
3307 * Generate and return the track or no track link for a forum.
3308 * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
3310 function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) {
3313 static $strnotrackforum, $strtrackforum;
3315 if (isset($messages['trackforum'])) {
3316 $strtrackforum = $messages['trackforum'];
3318 if (isset($messages['notrackforum'])) {
3319 $strnotrackforum = $messages['notrackforum'];
3321 if (empty($strtrackforum)) {
3322 $strtrackforum = get_string('trackforum', 'forum');
3324 if (empty($strnotrackforum)) {
3325 $strnotrackforum = get_string('notrackforum', 'forum');
3328 if (forum_tp_is_tracked($forum, $USER->id
)) {
3329 $linktitle = $strnotrackforum;
3330 $linktext = $strtrackforum;
3332 $linktitle = $strtrackforum;
3333 $linktext = $strnotrackforum;
3338 $link .= '<script type="text/javascript">';
3339 $link .= '//<![CDATA['."\n";
3340 $link .= 'document.getElementById("trackinglink").innerHTML = "<a title=\"' . $linktitle . '\" href=\"' . $CFG->wwwroot
.
3341 '/mod/forum/settracking.php?id=' . $forum->id
. '\">' . $linktext . '<\/a>";'."\n";
3342 $link .= '//]]>'."\n";
3343 $link .= '</script>';
3344 // use <noscript> to print button in case javascript is not enabled
3345 $link .= '<noscript>';
3347 $link .= print_single_button($CFG->wwwroot
. '/mod/forum/settracking.php?id=' . $forum->id
,
3348 '', $linktext, 'post', '_self', true, $linktitle);
3350 $link .= '</noscript>';
3361 function forum_user_has_posted_discussion($forumid, $userid) {
3362 if ($discussions = forum_get_discussions($forumid, '', $userid)) {
3372 function forum_discussions_user_has_posted_in($forumid, $userid) {
3375 $haspostedsql = "SELECT d.id AS id,
3377 FROM {$CFG->prefix}forum_posts p,
3378 {$CFG->prefix}forum_discussions d
3379 WHERE p.discussion = d.id
3380 AND d.forum = $forumid
3381 AND p.userid = $userid";
3383 return get_records_sql($haspostedsql);
3389 function forum_user_has_posted($forumid, $did, $userid) {
3390 return record_exists('forum_posts','discussion',$did,'userid',$userid);
3396 function forum_user_can_post_discussion($forum, $currentgroup=-1, $groupmode=-1, $cm=NULL, $context=NULL) {
3397 // $forum is an object
3398 global $USER, $SESSION;
3401 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
3402 error('Course Module ID was incorrect');
3406 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3409 if ($currentgroup == -1) {
3410 $currentgroup = get_current_group($cm->course
);
3413 if ($groupmode == -1) {
3414 if (!$course = get_record('course', 'id', $cm->course
)) {
3415 error('Can not find course');
3417 $groupmode = groups_get_activity_groupmode($cm);
3420 if ($forum->type
== 'news') {
3421 $capname = 'mod/forum:addnews';
3423 $capname = 'mod/forum:startdiscussion';
3426 if (!has_capability($capname, $context)) {
3430 if ($forum->type
== 'eachuser') {
3431 if (forum_user_has_posted_discussion($forum->id
, $USER->id
)) {
3436 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
3440 if ($currentgroup) {
3441 return groups_is_member($currentgroup);
3443 //else it might be group 0 in visible mode
3444 if ($groupmode == VISIBLEGROUPS
){
3453 * This function checks whether the user can reply to posts in a forum
3454 * discussion. Use forum_user_can_post_discussion() to check whether the user
3455 * can start dicussions.
3456 * @param $forum - forum object
3457 * @param $user - user object
3459 function forum_user_can_post($forum, $user=NULL, $cm=NULL, $context=NULL) {
3462 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
3463 error('Course Module ID was incorrect');
3467 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3470 if ($forum->type
== 'news') {
3471 $capname = 'mod/forum:replynews';
3473 $capname = 'mod/forum:replypost';
3476 if (!empty($user)) {
3477 $canreply = has_capability($capname, $context, $user->id
, false)
3478 && !has_capability('moodle/legacy:guest', $context, $user->id
, false);
3480 $canreply = has_capability($capname, $context, NULL, false)
3481 && !has_capability('moodle/legacy:guest', $context, NULL, false);
3488 //checks to see if a user can view a particular post
3489 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
3497 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3498 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
3502 // If it's a grouped discussion, make sure the user is a member
3503 if ($discussion->groupid
> 0) {
3504 $groupmode = groups_get_activity_groupmode($cm);
3505 if ($groupmode == SEPARATEGROUPS
) {
3506 return groups_is_member($discussion->groupid
) ||
has_capability('moodle/site:accessallgroups', $modcontext);
3516 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
3519 if (empty($user) ||
empty($user->id
)) {
3523 // retrieve objects (yuk)
3524 if (is_numeric($forum)) {
3525 if (!$forum = get_record('forum','id',$forum)) {
3529 if (is_numeric($discussion)) {
3530 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3535 if (!has_capability('mod/forum:viewdiscussion', $context)) {
3539 if ($forum->type
== 'qanda' &&
3540 !forum_user_has_posted($forum->id
, $discussion->id
, $user->id
) &&
3541 !has_capability('mod/forum:viewqandawithoutposting', $context)) {
3551 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL) {
3554 // retrieve objects (yuk)
3555 if (is_numeric($forum)) {
3556 if (!$forum = get_record('forum','id',$forum)) {
3561 if (is_numeric($discussion)) {
3562 if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3566 if (is_numeric($post)) {
3567 if (!$post = get_record('forum_posts','id',$post)) {
3571 if (!isset($post->id
) && isset($post->parent
)) {
3572 $post->id
= $post->parent
;
3575 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
3576 error('Course Module ID was incorrect');
3578 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3580 if (empty($user) ||
empty($user->id
)) {
3584 if (!has_capability('mod/forum:viewdiscussion', $context, $user->id
)) {
3588 if (!groups_course_module_visible($cm, $user->id
)) {
3592 if ($forum->type
== 'qanda') {
3593 $firstpost = forum_get_firstpost_from_discussion($discussion->id
);
3595 return (forum_user_has_posted($forum->id
,$discussion->id
,$user->id
) ||
3596 $firstpost->id
== $post->id ||
3597 has_capability('mod/forum:viewqandawithoutposting', $context, false, $user->id
));
3604 * Prints the discussion view screen for a forum.
3606 * @param object $course The current course object.
3607 * @param object $forum Forum to be printed.
3608 * @param int $maxdiscussions The maximum number of discussions per page(optional).
3609 * @param string $displayformat The display format to use (optional).
3610 * @param string $sort Sort arguments for database query (optional).
3611 * @param int $currentgroup Group to display discussions for (optional).
3612 * @param int $groupmode Group mode of the forum (optional).
3613 * @param int $page Page mode, page to display (optional).
3616 function forum_print_latest_discussions($course, $forum, $maxdiscussions=5, $displayformat='plain', $sort='',
3617 $currentgroup=-1, $groupmode=-1, $page=-1) {
3620 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
3621 error('Course Module ID was incorrect');
3623 $context = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3626 // Sort out some defaults
3628 if ((!$maxdiscussions) && ($displayformat == 'plain')) {
3629 $displayformat = 'header'; // Abbreviate display by default
3633 if ($displayformat == 'plain') {
3638 // Decide if current user is allowed to see ALL the current discussions or not
3640 // First check the group stuff
3641 $groupmode = groups_get_activity_groupmode($cm);
3642 $currentgroup = groups_get_activity_group($cm);
3644 // If the user can post discussions, then this is a good place to put the
3645 // button for it. We do not show the button if we are showing site news
3646 // and the current user is a guest.
3648 if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context) ||
3649 ($forum->type
!= 'news' && has_capability('moodle/legacy:guest', $context, NULL, false)) ) {
3651 echo '<div class="singlebutton forumaddnew">';
3652 echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
3654 echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
3655 echo '<input type="submit" value="';
3656 echo ($forum->type
== 'news') ?
get_string('addanewtopic', 'forum')
3657 : (($forum->type
== 'qanda')
3658 ?
get_string('addanewquestion','forum')
3659 : get_string('addanewdiscussion', 'forum'));
3664 } else if (!isguestuser() and isloggedin() and $forum->type
!= 'news' and $groupmode == SEPARATEGROUPS
and !groups_is_member($currentgroup)) {
3665 notify(get_string('cannotadddiscussion', 'forum'));
3669 // Get all the recent discussions we're allowed to see
3671 $getuserlastmodified = ($displayformat == 'header');
3673 if (! $discussions = forum_get_discussions($forum->id
, $sort, 0, $fullpost, $currentgroup,0,$getuserlastmodified) ) {
3674 echo '<div class="forumnodiscuss">';
3675 if ($forum->type
== 'news') {
3676 echo '('.get_string('nonews', 'forum').')';
3677 } else if ($forum->type
== 'qanda') {
3678 echo '('.get_string('noquestions','forum').')';
3680 echo '('.get_string('nodiscussions', 'forum').')';
3686 // If no discussions then don't use paging (to avoid some divide by 0 errors)
3688 if ($maxdiscussions <= 0) {
3690 $maxdiscussions = 0;
3693 // If we want paging
3696 ///Get the number of discussions found
3697 $numdiscussions = count($discussions);
3699 ///Show the paging bar
3700 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&");
3702 //Calculate the page "window"
3703 $pagestart = ($page * $maxdiscussions) +
1;
3704 $pageend = $pagestart +
$maxdiscussions - 1;
3708 $replies = forum_count_discussion_replies($forum->id
);
3710 $canreply = forum_user_can_post($forum);
3711 $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
3713 $discussioncount = 0;
3714 $olddiscussionlink = false;
3715 $strdatestring = get_string('strftimerecentfull');
3717 // Check if the forum is tracked.
3718 if ($cantrack = forum_tp_can_track_forums($forum)) {
3719 $forumtracked = forum_tp_is_tracked($forum);
3721 $forumtracked = false;
3724 if ($displayformat == 'header') {
3725 echo '<table cellspacing="0" class="forumheaderlist">';
3728 echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
3729 echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
3730 if ($groupmode > 0) {
3731 echo '<th class="header group" scope="col">'.get_string('group').'</th>';
3733 if (has_capability('mod/forum:viewdiscussion', $context)) {
3734 echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
3735 // If the forum can be tracked, display the unread column.
3737 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
3738 if ($forumtracked) {
3739 echo ' <a title="'.get_string('markallread', 'forum').
3740 '" href="'.$CFG->wwwroot
.'/mod/forum/markposts.php?f='.
3741 $forum->id
.'&mark=read&returnpage=view.php">'.
3742 '<img src="'.$CFG->pixpath
.'/t/clear.gif" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
3747 echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
3753 foreach ($discussions as $discussion) {
3756 if ($page != -1) { // We are using paging
3757 if ($discussioncount < $pagestart) { // Not there yet
3760 if ($discussioncount > $pageend) { // All done, finish the loop
3763 //Without paging, old approach
3764 } else if ($maxdiscussions && ($discussioncount > $maxdiscussions)) {
3765 $olddiscussionlink = true;
3769 if (!empty($replies[$discussion->discussion
])) {
3770 $discussion->replies
= $replies[$discussion->discussion
]->replies
;
3771 $discussion->lastpostid
= $replies[$discussion->discussion
]->lastpostid
;
3773 $discussion->replies
= 0;
3776 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
3777 // All posts are read in this case.
3778 if (!$forumtracked) {
3779 $discussion->unread
= '-';
3780 } else if (empty($USER)) {
3781 $discussion->unread
= 0;
3783 $discussion->unread
= forum_tp_count_discussion_unread_posts($USER->id
, $discussion->discussion
);
3786 if (!empty($USER->id
)) {
3787 $ownpost = ($discussion->userid
== $USER->id
);
3791 // Use discussion name instead of subject of first post
3792 $discussion->subject
= $discussion->name
;
3794 switch ($displayformat) {
3796 if ($groupmode > 0) {
3797 if (isset($groups[$discussion->groupid
])) {
3798 $group = $groups[$discussion->groupid
];
3800 $group = $groups[$discussion->groupid
] = groups_get_group($discussion->groupid
); //TODO:
3805 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
3806 $canviewparticipants);
3809 if ($canreply or $discussion->replies
) {
3815 $discussion->forum
= $forum->id
;
3817 forum_print_post($discussion, $course->id
, $ownpost, $reply=0, $link, $assessed=false);
3822 if ($displayformat == "header") {
3827 if ($olddiscussionlink) {
3828 echo '<div class="forumolddiscuss">';
3829 echo '<a href="'.$CFG->wwwroot
.'/mod/forum/view.php?f='.$forum->id
.'&showall=1">';
3830 echo get_string('olderdiscussions', 'forum').'</a> ...</div>';
3833 if ($page != -1) { ///Show the paging bar
3834 print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&");
3842 function forum_print_discussion($course, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
3846 if (!empty($USER->id
)) {
3847 $ownpost = ($USER->id
== $post->userid
);
3851 if ($canreply === NULL) {
3852 $reply = forum_user_can_post($forum);
3858 $ratingsmenuused = false;
3859 $ratingsformused = false;
3860 if ($forum->assessed
and !empty($USER->id
)) {
3861 if ($ratings->scale
= make_grades_menu($forum->scale
)) {
3862 $ratings->assesstimestart
= $forum->assesstimestart
;
3863 $ratings->assesstimefinish
= $forum->assesstimefinish
;
3864 $ratings->allow
= $canrate;
3866 if ($ratings->allow
) {
3867 echo '<form id="form" method="post" action="rate.php">';
3868 echo '<div class="ratingform">';
3869 echo '<input type="hidden" name="forumid" value="'.$forum->id
.'" />';
3870 $ratingsformused = true;
3875 $post->forum
= $forum->id
; // Add the forum id to the post object, later used by forum_print_post
3876 $post->forumtype
= $forum->type
;
3878 $post->subject
= format_string($post->subject
);
3880 if (forum_tp_can_track_forums($forum)) {
3881 if ($forumtracked = forum_tp_is_tracked($forum)) {
3882 $user_read_array = forum_tp_get_discussion_read_records($USER->id
, $post->discussion
);
3884 $user_read_array = array();
3887 $forumtracked = false;
3888 $user_read_array = array();
3891 if (forum_print_post($post, $course->id
, $ownpost, $reply, $link=false, $ratings,
3892 '', '', (!$forumtracked ||
isset($user_read_array[$post->id
]) ||
forum_tp_is_post_old($post)))) {
3893 $ratingsmenuused = true;
3897 case FORUM_MODE_FLATOLDEST
:
3898 case FORUM_MODE_FLATNEWEST
:
3900 if (forum_print_posts_flat($post->discussion
, $course->id
, $mode, $ratings, $reply,
3901 $user_read_array, $post->forum
)) {
3902 $ratingsmenuused = true;
3906 case FORUM_MODE_THREADED
:
3907 if (forum_print_posts_threaded($post->id
, $course->id
, 0, $ratings, $reply,
3908 $user_read_array, $post->forum
)) {
3909 $ratingsmenuused = true;
3913 case FORUM_MODE_NESTED
:
3914 if (forum_print_posts_nested($post->id
, $course->id
, $ratings, $reply,
3915 $user_read_array, $post->forum
)) {
3916 $ratingsmenuused = true;
3921 if ($ratingsformused) {
3922 if ($ratingsmenuused) {
3923 echo '<div class="ratingsubmit">';
3924 echo '<input type="submit" value="'.get_string('sendinratings', 'forum').'" />';
3925 if ($forum->scale
< 0) {
3926 if ($scale = get_record("scale", "id", abs($forum->scale
))) {
3927 print_scale_menu_helpbutton($course->id
, $scale );
3942 function forum_print_posts_flat($discussion, $courseid, $direction, $ratings, $reply, &$user_read_array, $forumid=0) {
3946 $ratingsmenuused = false;
3948 if ($direction < 0) {
3949 $sort = "ORDER BY created DESC";
3951 $sort = "ORDER BY created ASC";
3954 if ($posts = forum_get_discussion_posts($discussion, $sort, $forumid)) {
3955 foreach ($posts as $post) {
3957 $post->subject
= format_string($post->subject
);
3959 $ownpost = ($USER->id
== $post->userid
);
3960 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3961 '', '', (isset($user_read_array[$post->id
]) ||
forum_tp_is_post_old($post)))) {
3962 $ratingsmenuused = true;
3967 return $ratingsmenuused;
3974 function forum_print_posts_threaded($parent, $courseid, $depth, $ratings, $reply, &$user_read_array, $forumid=0) {
3978 $ratingsmenuused = false;
3980 $istracking = forum_tp_can_track_forums($forumid) && forum_tp_is_tracked($forumid);
3982 if ($posts = forum_get_child_posts($parent, $forumid)) {
3984 if (!$cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
3985 error('Course Module ID was incorrect');
3987 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
3988 $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
3990 foreach ($posts as $post) {
3992 echo '<div class="indent">';
3994 $ownpost = ($USER->id
== $post->userid
);
3996 $post->subject
= format_string($post->subject
);
3998 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3999 '', '', (isset($user_read_array[$post->id
]) ||
forum_tp_is_post_old($post)))) {
4000 $ratingsmenuused = true;
4003 if (!forum_user_can_see_post($post->forum
,$post->discussion
,$post)) {
4006 $by->name
= fullname($post, $canviewfullnames);
4007 $by->date
= userdate($post->modified
);
4010 if (isset($user_read_array[$post->id
]) ||
forum_tp_is_post_old($post)) {
4011 $style = '<span class="forumthread read">';
4013 $style = '<span class="forumthread unread">';
4016 $style = '<span class="forumthread">';
4018 echo $style."<a name=\"$post->id\"></a>".
4019 "<a href=\"discuss.php?d=$post->discussion&parent=$post->id\">".format_string($post->subject
,true)."</a> ";
4020 print_string("bynameondate", "forum", $by);
4024 if (forum_print_posts_threaded($post->id
, $courseid, $depth-1, $ratings, $reply,
4025 $user_read_array, $forumid)) {
4026 $ratingsmenuused = true;
4031 return $ratingsmenuused;
4037 function forum_print_posts_nested($parent, $courseid, $ratings, $reply, &$user_read_array, $forumid=0) {
4041 $ratingsmenuused = false;
4043 if ($posts = forum_get_child_posts($parent, $forumid)) {
4044 foreach ($posts as $post) {
4046 echo '<div class="indent">';
4047 if (empty($USER->id
)) {
4050 $ownpost = ($USER->id
== $post->userid
);
4053 $post->subject
= format_string($post->subject
);
4055 if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
4056 '', '', (isset($user_read_array[$post->id
]) ||
forum_tp_is_post_old($post)))) {
4057 $ratingsmenuused = true;
4059 if (forum_print_posts_nested($post->id
, $courseid, $ratings, $reply, $user_read_array, $forumid)) {
4060 $ratingsmenuused = true;
4065 return $ratingsmenuused;
4071 function forum_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $cmid="0", $user="", $groupid="") {
4072 // Returns all forum posts since a given time. If forum is specified then
4073 // this restricts the results
4078 $forumselect = " AND cm.id = '$cmid'";
4084 $userselect = " AND u.id = '$user'";
4089 $posts = get_records_sql("SELECT p.*, d.name, u.firstname, u.lastname,
4090 u.picture, d.groupid, cm.instance, f.name,
4091 cm.section, cm.id AS cmid
4092 FROM {$CFG->prefix}forum_posts p,
4093 {$CFG->prefix}forum_discussions d,
4094 {$CFG->prefix}user u,
4095 {$CFG->prefix}course_modules cm,
4096 {$CFG->prefix}forum f
4097 WHERE p.modified > '$sincetime' $forumselect
4098 AND p.userid = u.id $userselect
4099 AND d.course = '$courseid'
4100 AND p.discussion = d.id
4101 AND cm.instance = f.id
4102 AND cm.course = d.course
4103 AND cm.course = f.course
4105 ORDER BY p.discussion ASC,p.created ASC");
4107 if (empty($posts)) {
4111 foreach ($posts as $post) {
4113 $modcontext = get_context_instance(CONTEXT_MODULE
, $post->cmid
);
4114 $canviewallgroups = has_capability('moodle/site:accessallgroups', $modcontext);
4116 if ($groupid and ($post->groupid
!= -1 and $groupid != $post->groupid
and !$canviewallgroups)) {
4119 if (!groups_course_module_visible($post->cmid
)) {
4123 $tmpactivity = new Object;
4125 $tmpactivity->type
= "forum";
4126 $tmpactivity->defaultindex
= $index;
4127 $tmpactivity->instance
= $post->instance
;
4128 $tmpactivity->name
= $post->name
;
4129 $tmpactivity->section
= $post->section
;
4131 $tmpactivity->content
->id
= $post->id
;
4132 $tmpactivity->content
->discussion
= $post->discussion
;
4133 $tmpactivity->content
->subject
= $post->subject
;
4134 $tmpactivity->content
->parent
= $post->parent
;
4136 $tmpactivity->user
->userid
= $post->userid
;
4137 $tmpactivity->user
->fullname
= fullname($post);
4138 $tmpactivity->user
->picture
= $post->picture
;
4140 $tmpactivity->timestamp
= $post->modified
;
4141 $activities[] = $tmpactivity;
4152 function forum_print_recent_mod_activity($activity, $course, $detail=false) {
4156 echo '<table border="0" cellpadding="3" cellspacing="0">';
4158 if ($activity->content
->parent
) {
4159 $openformat = "<font size=\"2\"><i>";
4160 $closeformat = "</i></font>";
4162 $openformat = "<b>";
4163 $closeformat = "</b>";
4166 echo "<tr><td class=\"forumpostpicture\" width=\"35\" valign=\"top\">";
4167 print_user_picture($activity->user
->userid
, $course, $activity->user
->picture
);
4168 echo "</td><td>$openformat";
4171 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
4172 "class=\"icon\" alt=\"".strip_tags(format_string($activity->name
,true))."\" /> ";
4174 echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d=" . $activity->content
->discussion
4175 . "#p" . $activity->content
->id
. "\">";
4177 echo format_string($activity->content
->subject
,true);
4178 echo "</a>$closeformat";
4180 echo "<br /><font size=\"2\">";
4181 echo "<a href=\"$CFG->wwwroot/user/view.php?id=" . $activity->user
->userid
. "&course=" . "$course\">"
4182 . $activity->user
->fullname
. "</a>";
4183 echo " - " . userdate($activity->timestamp
) . "</font></td></tr>";
4190 * recursively sets the discussion field to $discussionid on $postid and all its children
4191 * used when pruning a post
4193 function forum_change_discussionid($postid, $discussionid) {
4194 set_field('forum_posts', 'discussion', $discussionid, 'id', $postid);
4195 if ($posts = get_records('forum_posts', 'parent', $postid)) {
4196 foreach ($posts as $post) {
4197 forum_change_discussionid($post->id
, $discussionid);
4204 * Prints the editing button on subscribers page
4206 function forum_update_subscriptions_button($courseid, $forumid) {
4209 if (!empty($USER->subscriptionsediting
)) {
4210 $string = get_string('turneditingoff');
4213 $string = get_string('turneditingon');
4217 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
4218 "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
4219 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4220 "<input type=\"submit\" value=\"$string\" /></form>";
4224 * This function gets run whenever a role is assigned to a user in a context
4226 * @param integer $userid
4227 * @param object $context
4230 function forum_role_assign($userid, $context, $roleid) {
4231 // check to see if this role comes with mod/forum:initialsubscriptions
4232 $cap = role_context_capabilities($roleid, $context, 'mod/forum:initialsubscriptions');
4233 $cap1 = role_context_capabilities($roleid, $context, 'moodle/course:view');
4234 // we are checking the role because has_capability() will pull this capability out
4235 // from other roles this user might have and resolve them, which is no good
4236 // the role needs course view to
4237 if (isset($cap['mod/forum:initialsubscriptions']) && $cap['mod/forum:initialsubscriptions'] == CAP_ALLOW
&&
4238 isset($cap1['moodle/course:view']) && $cap1['moodle/course:view'] == CAP_ALLOW
) {
4239 return forum_add_user_default_subscriptions($userid, $context);
4241 // MDL-8981, do not subscribe to forum
4248 * This function gets run whenever a role is assigned to a user in a context
4250 * @param integer $userid
4251 * @param object $context
4254 function forum_role_unassign($userid, $context) {
4255 return forum_remove_user_subscriptions($userid, $context);
4260 * Add subscriptions for new users
4262 function forum_add_user_default_subscriptions($userid, $context) {
4264 if (empty($context->contextlevel
)) {
4268 switch ($context->contextlevel
) {
4270 case CONTEXT_SYSTEM
: // For the whole site
4271 if ($courses = get_records('course')) {
4272 foreach ($courses as $course) {
4273 $subcontext = get_context_instance(CONTEXT_COURSE
, $course->id
);
4274 forum_add_user_default_subscriptions($userid, $subcontext);
4279 case CONTEXT_COURSECAT
: // For a whole category
4280 if ($courses = get_records('course', 'category', $context->instanceid
)) {
4281 foreach ($courses as $course) {
4282 $subcontext = get_context_instance(CONTEXT_COURSE
, $course->id
);
4283 forum_add_user_default_subscriptions($userid, $subcontext);
4286 if ($categories = get_records('course_categories', 'parent', $context->instanceid
)) {
4287 foreach ($categories as $category) {
4288 $subcontext = get_context_instance(CONTEXT_COURSECAT
, $category->id
);
4289 forum_add_user_default_subscriptions($userid, $subcontext);
4295 case CONTEXT_COURSE
: // For a whole course
4296 if ($course = get_record('course', 'id', $context->instanceid
)) {
4297 if ($forums = get_all_instances_in_course('forum', $course, $userid, false)) {
4298 foreach ($forums as $forum) {
4299 if ($forum->forcesubscribe
!= FORUM_INITIALSUBSCRIBE
) {
4302 if ($modcontext = get_context_instance(CONTEXT_MODULE
, $forum->coursemodule
)) {
4303 if (has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4304 forum_subscribe($userid, $forum->id
);
4312 case CONTEXT_MODULE
: // Just one forum
4313 if ($cm = get_coursemodule_from_id('forum', $context->instanceid
)) {
4314 if ($forum = get_record('forum', 'id', $cm->instance
)) {
4315 if ($forum->forcesubscribe
!= FORUM_INITIALSUBSCRIBE
) {
4318 if (has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4319 forum_subscribe($userid, $forum->id
);
4331 * Remove subscriptions for a user in a context
4333 function forum_remove_user_subscriptions($userid, $context) {
4337 if (empty($context->contextlevel
)) {
4341 switch ($context->contextlevel
) {
4343 case CONTEXT_SYSTEM
: // For the whole site
4344 //if ($courses = get_my_courses($userid)) {
4345 // find all courses in which this user has a forum subscription
4346 if ($courses = get_records_sql("SELECT c.*
4347 FROM {$CFG->prefix}course c,
4348 {$CFG->prefix}forum_subscriptions fs,
4349 {$CFG->prefix}forum f
4350 WHERE c.id = f.course
4352 AND fs.userid = $userid")) {
4354 foreach ($courses as $course) {
4355 $subcontext = get_context_instance(CONTEXT_COURSE
, $course->id
);
4356 forum_remove_user_subscriptions($userid, $subcontext);
4361 case CONTEXT_COURSECAT
: // For a whole category
4362 if ($courses = get_records('course', 'category', $context->instanceid
)) {
4363 foreach ($courses as $course) {
4364 $subcontext = get_context_instance(CONTEXT_COURSE
, $course->id
);
4365 forum_remove_user_subscriptions($userid, $subcontext);
4368 if ($categories = get_records('course_categories', 'parent', $context->instanceid
)) {
4369 foreach ($categories as $category) {
4370 $subcontext = get_context_instance(CONTEXT_COURSECAT
, $category->id
);
4371 forum_remove_user_subscriptions($userid, $subcontext);
4376 case CONTEXT_COURSE
: // For a whole course
4377 if ($course = get_record('course', 'id', $context->instanceid
)) {
4378 // find all forums in which this user has a subscription, and its coursemodule id
4379 if ($forums = get_records_sql("SELECT f.id, cm.id as coursemodule
4380 FROM {$CFG->prefix}forum f,
4381 {$CFG->prefix}modules m,
4382 {$CFG->prefix}course_modules cm,
4383 {$CFG->prefix}forum_subscriptions fs
4384 WHERE fs.userid = $userid
4386 AND f.course = $context->instanceid
4387 AND cm.instance = f.id
4388 AND cm.module = m.id
4389 AND m.name = 'forum'")) {
4391 //if ($forums = get_all_instances_in_course('forum', $course, $userid, true)) {
4392 foreach ($forums as $forum) {
4393 if ($modcontext = get_context_instance(CONTEXT_MODULE
, $forum->coursemodule
)) {
4394 if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
4395 forum_unsubscribe($userid, $forum->id
);
4403 case CONTEXT_MODULE
: // Just one forum
4404 if ($cm = get_coursemodule_from_id('forum', $context->instanceid
)) {
4405 if ($forum = get_record('forum', 'id', $cm->instance
)) {
4406 if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
4407 forum_unsubscribe($userid, $forum->id
);
4417 // Functions to do with read tracking.
4421 function forum_tp_add_read_record($userid, $postid, $discussionid=-1, $forumid=-1) {
4422 if (($readrecord = forum_tp_get_read_records($userid, $postid)) === false) {
4425 $readrecord->userid
= $userid;
4426 $readrecord->postid
= $postid;
4427 $readrecord->discussionid
= $discussionid;
4428 $readrecord->forumid
= $forumid;
4429 $readrecord->firstread
= time();
4430 $readrecord->lastread
= $readrecord->firstread
;
4431 return insert_record('forum_read', $readrecord, true);
4434 // Update read record
4435 $readrecord = reset($readrecord);
4436 $readrecord->lastread
= time();
4439 $update->id
= $readrecord->id
;
4440 $update->lastread
= $readrecord->lastread
;
4442 // This shouldn't happen, but just in case...
4443 if (!$readrecord->firstread
) {
4444 // Update the 'firstread' field.
4445 $update->firstread
= $readrecord->lastread
;
4447 if ($discussionid > -1) {
4448 // Update the 'discussionid' field.
4449 $update->discussionid
= $discussionid;
4451 if ($forumid > -1) {
4452 // Update the 'forumid' field.
4453 $update->forumid
= $forumid;
4456 return update_record('forum_read', $update);
4461 * Returns all records in the 'forum_read' table matching the passed keys, indexed
4464 function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4467 if ($select != '') $select .= ' AND ';
4468 $select .= 'userid = \''.$userid.'\'';
4471 if ($select != '') $select .= ' AND ';
4472 $select .= 'postid = \''.$postid.'\'';
4474 if ($discussionid > -1) {
4475 if ($select != '') $select .= ' AND ';
4476 $select .= 'discussionid = \''.$discussionid.'\'';
4478 if ($forumid > -1) {
4479 if ($select != '') $select .= ' AND ';
4480 $select .= 'forumid = \''.$forumid.'\'';
4483 return get_records_select('forum_read', $select);
4487 * Returns all read records for the provided user and discussion, indexed by postid.
4489 function forum_tp_get_discussion_read_records($userid, $discussionid) {
4490 $select = 'userid = \''.$userid.'\' AND discussionid = \''.$discussionid.'\'';
4491 $fields = 'postid, firstread, lastread';
4492 return get_records_select('forum_read', $select, '', $fields);
4496 * If its an old post, do nothing. If the record exists, the maintenance will clear it up later.
4498 function forum_tp_mark_post_read($userid, &$post, $forumid) {
4499 if (!forum_tp_is_post_old($post)) {
4500 return forum_tp_add_read_record($userid, $post->id
, $post->discussion
, $forumid);
4507 * Marks a whole forum as read, for a given user
4509 function forum_tp_mark_forum_read($userid, $forumid, $groupid=false) {
4512 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4515 if ($groupid !== false) {
4516 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4519 $sql = 'SELECT p.id as postid, d.id as discussionid, d.forum as forumid '.
4520 'FROM '.$CFG->prefix
.'forum_posts p '.
4521 'LEFT JOIN '.$CFG->prefix
.'forum_discussions d ON p.discussion = d.id '.
4522 'LEFT JOIN '.$CFG->prefix
.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4523 'WHERE d.forum = '.$forumid.$groupsel.
4524 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4526 if ($posts = get_records_sql($sql)) {
4527 foreach ($posts as $post) {
4528 forum_tp_add_read_record($userid, $post->postid
, $post->discussionid
, $post->forumid
);
4535 * Marks a whole discussion as read, for a given user
4537 function forum_tp_mark_discussion_read($userid, $discussionid, $forumid) {
4540 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4542 $sql = 'SELECT p.id as postid, p.discussion as discussionid '.
4543 'FROM '.$CFG->prefix
.'forum_posts p '.
4544 'LEFT JOIN '.$CFG->prefix
.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4545 'WHERE p.discussion = '.$discussionid.' '.
4546 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4548 if ($posts = get_records_sql($sql)) {
4549 foreach ($posts as $post) {
4550 forum_tp_add_read_record($userid, $post->postid
, $post->discussionid
, $forumid);
4559 function forum_tp_is_post_read($userid, &$post) {
4560 return (forum_tp_is_post_old($post) ||
4561 (get_record('forum_read', 'userid', $userid, 'postid', $post->id
) !== false));
4567 function forum_tp_is_post_old(&$post, $time=null) {
4570 if (is_null($time)) $time = time();
4571 return ($post->modified
< ($time - ($CFG->forum_oldpostdays
* 24 * 3600)));
4575 * Returns the count of records for the provided user and discussion.
4577 function forum_tp_count_discussion_read_records($userid, $discussionid) {
4580 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4582 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4583 'FROM '.$CFG->prefix
.'forum_discussions d '.
4584 'LEFT JOIN '.$CFG->prefix
.'forum_read r ON d.id = r.discussionid AND r.userid = '.$userid.' '.
4585 'LEFT JOIN '.$CFG->prefix
.'forum_posts p ON p.discussion = d.id '.
4586 'AND (p.modified < '.$cutoffdate.' OR p.id = r.postid) '.
4587 'WHERE d.id = '.$discussionid;
4589 return (count_records_sql($sql));
4593 * Returns the count of records for the provided user and discussion.
4595 function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
4598 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4600 $sql = 'SELECT COUNT(p.id) '.
4601 'FROM '.$CFG->prefix
.'forum_posts p '.
4602 'LEFT JOIN '.$CFG->prefix
.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4603 'WHERE p.discussion = '.$discussionid.' '.
4604 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4606 return (count_records_sql($sql));
4610 * Returns the count of posts for the provided forum and [optionally] group.
4612 function forum_tp_count_forum_posts($forumid, $groupid=false) {
4615 $sql = 'SELECT COUNT(*) '.
4616 'FROM '.$CFG->prefix
.'forum_posts fp,'.$CFG->prefix
.'forum_discussions fd '.
4617 'WHERE fd.forum = '.$forumid.' AND fp.discussion = fd.id';
4618 if ($groupid !== false) {
4619 $sql .= ' AND (fd.groupid = '.$groupid.' OR fd.groupid = -1)';
4621 $count = count_records_sql($sql);
4628 * Returns the count of records for the provided user and forum and [optionally] group.
4630 function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
4633 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4636 if ($groupid !== false) {
4637 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4640 if ($CFG->dbfamily
=== 'postgres' ||
$CFG->dbfamily
=== 'mssql' ||
$CFG->dbfamily
=== 'oracle') {
4641 // this query takes 20ms, vs several minutes for the one below
4642 $sql = " SELECT COUNT (DISTINCT u.id ) "
4645 . " FROM {$CFG->prefix}forum_posts p "
4646 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4647 . " JOIN {$CFG->prefix}forum_read r ON p.id = r.postid"
4648 . " WHERE d.forum = $forumid $groupsel "
4649 . " AND r.userid= $userid"
4652 . " FROM {$CFG->prefix}forum_posts p "
4653 . " JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id "
4654 . " WHERE d.forum = $forumid $groupsel "
4655 . " AND p.modified < $cutoffdate"
4657 } else { // This is for MySQL. TODO: Check if the above works for MySQL 4.1
4658 $sql = 'SELECT COUNT(DISTINCT p.id) '.
4659 'FROM '.$CFG->prefix
.'forum_posts p,'.$CFG->prefix
.'forum_read r,'.$CFG->prefix
.'forum_discussions d '.
4660 'WHERE d.forum = '.$forumid.$groupsel.' AND p.discussion = d.id AND '.
4661 '((p.id = r.postid AND r.userid = '.$userid.') OR p.modified < '.$cutoffdate.' ) ';
4663 return (count_records_sql($sql));
4667 * Returns the count of records for the provided user and forum and [optionally] group.
4669 function forum_tp_count_forum_unread_posts($userid, $forumid, $groupid=false) {
4672 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4675 if ($groupid !== false) {
4676 $groupsel = ' AND (d.groupid = '.$groupid.' OR d.groupid = -1)';
4679 $sql = 'SELECT COUNT(p.id) '.
4680 'FROM '.$CFG->prefix
.'forum_posts p '.
4681 'LEFT JOIN '.$CFG->prefix
.'forum_discussions d ON p.discussion = d.id '.
4682 'LEFT JOIN '.$CFG->prefix
.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
4683 'WHERE d.forum = '.$forumid.$groupsel.
4684 ' AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
4686 return (count_records_sql($sql));
4690 * Deletes read records for the specified index. At least one parameter must be specified.
4692 function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
4695 if ($select != '') $select .= ' AND ';
4696 $select .= 'userid = \''.$userid.'\'';
4699 if ($select != '') $select .= ' AND ';
4700 $select .= 'postid = \''.$postid.'\'';
4702 if ($discussionid > -1) {
4703 if ($select != '') $select .= ' AND ';
4704 $select .= 'discussionid = \''.$discussionid.'\'';
4706 if ($forumid > -1) {
4707 if ($select != '') $select .= ' AND ';
4708 $select .= 'forumid = \''.$forumid.'\'';
4710 if ($select == '') {
4714 return delete_records_select('forum_read', $select);
4718 * Get a list of forums not tracked by the user.
4720 * @param int $userid The id of the user to use.
4721 * @param int $courseid The id of the course being checked (optional).
4722 * @return mixed An array indexed by forum id, or false.
4724 function forum_tp_get_untracked_forums($userid, $courseid=false) {
4727 // If a course is specified, get the forums with tracking turned off.
4728 if ($courseid !== false) {
4729 $select = 'course = '.$courseid.' AND trackingtype = '.FORUM_TRACKING_OFF
;
4730 $forced = get_records_select('forum', $select, '', 'id,course');
4735 // Get the forums that the user has turned off.
4736 $sql = 'SELECT ft.forumid, ft.userid '.
4737 'FROM '.$CFG->prefix
.'forum_track_prefs ft, '.$CFG->prefix
.'forum f '.
4738 'WHERE ft.userid = '.$userid.' AND f.id = ft.forumid ' .
4739 'AND f.trackingtype != '.FORUM_TRACKING_ON
;
4740 $useroff = get_records_sql($sql);
4743 } else if (!$useroff) {
4746 return ($useroff +
$forced);
4751 * Determine if a user can track forums and optionally a particular forum.
4752 * Checks the site settings, the user settings and the forum settings (if
4755 * @param mixed $forum The forum object to test, or the int id (optional).
4756 * @param mixed $userid The user object to check for (optional).
4759 function forum_tp_can_track_forums($forum=false, $user=false) {
4762 // if possible, avoid expensive
4764 if (empty($CFG->forum_trackreadposts
)) {
4768 if ($user === false) {
4769 // Must be logged in and not a guest.
4770 $isauser = isloggedin() && !isguest();
4776 if ($forum === false) {
4777 $forumallows = true;
4778 $forumforced = false;
4780 // Work toward always passing an object...
4781 if (is_numeric($forum)) {
4782 $forum = get_record('forum', 'id', $forum, '','','','', 'id,trackingtype');
4785 $forumallows = ($forum->trackingtype
== FORUM_TRACKING_OPTIONAL
);
4786 $forumforced = ($forum->trackingtype
== FORUM_TRACKING_ON
);
4789 return ($isauser && ($forumforced ||
($forumallows && !empty($user->trackforums
))));
4793 * Tells whether a specific forum is tracked by the user. A user can optionally
4794 * be specified. If not specified, the current user is assumed.
4796 * @param mixed $forum If int, the id of the forum being checked; if object, the forum object
4797 * @param int $userid The id of the user being checked (optional).
4800 function forum_tp_is_tracked($forum, $userid=false) {
4803 if ($userid === false) {
4804 if (empty($USER->id
)) {
4807 $userid = $USER->id
;
4810 // Work toward always passing an object...
4811 if (is_numeric($forum)) {
4812 $forum = get_record('forum', 'id', $forum);
4815 return (($forum->trackingtype
== FORUM_TRACKING_ON
) ||
4816 ($forum->trackingtype
== FORUM_TRACKING_OPTIONAL
&&
4817 get_record('forum_track_prefs', 'userid', $userid, 'forumid', $forum->id
) === false));
4823 function forum_tp_start_tracking($forumid, $userid=false) {
4826 if ($userid === false) {
4827 $userid = $USER->id
;
4830 return delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forumid);
4836 function forum_tp_stop_tracking($forumid, $userid=false) {
4839 if ($userid === false) {
4840 $userid = $USER->id
;
4843 $track_prefs = new stdClass
;
4844 $track_prefs->userid
= $userid;
4845 $track_prefs->forumid
= $forumid;
4846 if (insert_record('forum_track_prefs', $track_prefs)) {
4847 return forum_tp_delete_read_records($userid, -1, -1, $forumid);
4855 * Clean old records from the forum_read table.
4857 function forum_tp_clean_read_records() {
4860 // Look for records older than the cutoffdate that are still in the forum_read table.
4861 $cutoffdate = isset($CFG->forum_oldpostdays
) ?
(time() - ($CFG->forum_oldpostdays
*24*60*60)) : 0;
4862 $sql = 'SELECT fr.id, fr.userid, fr.postid '.
4863 'FROM '.$CFG->prefix
.'forum_posts fp, '.$CFG->prefix
.'forum_read fr '.
4864 'WHERE fp.modified < '.$cutoffdate.' AND fp.id = fr.postid';
4865 if (($oldreadposts = get_records_sql($sql))) {
4866 foreach($oldreadposts as $oldreadpost) {
4867 delete_records('forum_read', 'id', $oldreadpost->id
);
4873 * Sets the last post for a given discussion
4875 function forum_discussion_update_last_post($discussionid) {
4878 // Check the given discussion exists
4879 if (!record_exists('forum_discussions', 'id', $discussionid)) {
4883 // Use SQL to find the last post for this discussion
4884 $sql = 'SELECT id, userid, modified '.
4885 'FROM '.$CFG->prefix
.'forum_posts '.
4886 'WHERE discussion='.$discussionid.' '.
4887 'ORDER BY modified DESC ';
4889 // Lets go find the last post
4890 if (($lastpost = get_record_sql($sql, true))) {
4891 $discussionobject = new Object;
4892 $discussionobject->id
= $discussionid;
4893 $discussionobject->usermodified
= $lastpost->userid
;
4894 $discussionobject->timemodified
= $lastpost->modified
;
4895 if (update_record('forum_discussions', $discussionobject)) {
4896 return $lastpost->id
;
4900 // To get here either we couldn't find a post for the discussion (weird)
4901 // or we couldn't update the discussion record (weird x2)
4909 function forum_get_view_actions() {
4910 return array('view discussion','search','forum','forums','subscribers');
4916 function forum_get_post_actions() {
4917 return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
4921 * this function returns all the separate forum ids, given a courseid
4922 * @param int $courseid
4925 function forum_get_separate_modules($courseid) {
4928 $forummodule = get_record("modules", "name", "forum");
4930 $sql = 'SELECT f.id, f.id FROM '.$CFG->prefix
.'forum f, '.$CFG->prefix
.'course_modules cm WHERE
4931 f.id = cm.instance AND cm.module ='.$forummodule->id
.' AND cm.visible = 1 AND cm.course = '.$courseid.'
4932 AND cm.groupmode ='.SEPARATEGROUPS
;
4934 return get_records_sql($sql);
4941 function forum_check_throttling($forum) {
4944 if (is_numeric($forum)) {
4945 $forum = get_record('forum','id',$forum);
4947 if (!is_object($forum)) {
4948 return false; // this is broken.
4951 if (empty($forum->blockafter
)) {
4955 if (empty($forum->blockperiod
)) {
4959 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
, $forum->course
)) {
4960 error('Course Module ID was incorrect');
4962 $modcontext = get_context_instance(CONTEXT_MODULE
, $cm->id
);
4963 if(!has_capability('mod/forum:throttlingapplies', $modcontext)) {
4967 // get the number of posts in the last period we care about
4969 $timeafter = $timenow - $forum->blockperiod
;
4971 $numposts = count_records_sql('SELECT COUNT(p.id) FROM '.$CFG->prefix
.'forum_posts p'
4972 .' JOIN '.$CFG->prefix
.'forum_discussions d'
4973 .' ON p.discussion = d.id WHERE d.forum = '.$forum->id
4974 .' AND p.userid = '.$USER->id
.' AND p.created > '.$timeafter);
4976 $a->blockafter
= $forum->blockafter
;
4977 $a->numposts
= $numposts;
4978 $a->blockperiod
= get_string('secondstotime'.$forum->blockperiod
);
4980 if ($forum->blockafter
<= $numposts) {
4981 error(get_string('forumblockingtoomanyposts','error',$a),$CFG->wwwroot
.'/mod/forum/view.php?f='.$forum->id
);
4983 if ($forum->warnafter
<= $numposts) {
4984 notify(get_string('forumblockingalmosttoomanyposts','forum',$a));
4992 * This function is used by the remove_course_userdata function in moodlelib.
4993 * If this function exists, remove_course_userdata will execute it.
4994 * This function will remove all posts from the specified forum.
4996 function forum_delete_userdata($data, $showfeedback=true) {
4999 $sql = "DELETE FROM {$CFG->prefix}forum_posts
5000 WHERE discussion IN (
5001 SELECT fd.id FROM {$CFG->prefix}forum_discussions fd, {$CFG->prefix}forum f
5002 WHERE f.course={$data->courseid} AND f.id=fd.forum "; // closing ) added bellow
5004 $strreset = get_string('reset');
5006 if (!empty($data->reset_forum_news
)) {
5007 $select = "$sql AND f.type = 'news' )";
5008 if (execute_sql($select, false) and $showfeedback) {
5009 notify($strreset.': '.get_string('namenews','forum'), 'notifysuccess');
5012 if (!empty($data->reset_forum_single
)) {
5013 $select = "$sql AND f.type = 'single' ) AND parent <> 0";
5014 if (execute_sql($select, false) and $showfeedback) {
5015 notify($strreset.': '.get_string('singleforum','forum'), 'notifysuccess');
5018 if (!empty($data->reset_forum_eachuser
)) {
5019 $select = "$sql AND f.type = 'eachuser' )";
5020 if (execute_sql($select, false) and $showfeedback) {
5021 notify($strreset.': '.get_string('eachuserforum','forum'), 'notifysuccess');
5024 if (!empty($data->reset_forum_general
)) {
5025 $select = "$sql AND f.type = 'general' )";
5026 if (execute_sql($select, false) and $showfeedback) {
5027 notify($strreset.': '.get_string('generalforum','forum'), 'notifysuccess');
5030 if (!empty($data->reset_forum_subscriptions
)) {
5031 $subscripsql = "DELETE FROM {$CFG->prefix}forum_subscriptions
5033 SELECT id FROM {$CFG->prefix}forum
5034 WHERE course = {$data->courseid} )";
5036 if (execute_sql($subscripsql, false) and $showfeedback) {
5037 notify($strreset.': '.get_string('resetsubscriptions','forum'), 'notifysuccess');
5044 * Called by course/reset.php
5046 function forum_reset_course_form($course) {
5047 echo get_string('resetforums', 'forum'); echo ':<br />';
5048 print_checkbox('reset_forum_news', 1, true, get_string('namenews','forum'), '', ''); echo '<br />';
5049 print_checkbox('reset_forum_single', 1, true, get_string('singleforum','forum'), '', ''); echo '<br />';
5050 print_checkbox('reset_forum_eachuser', 1, true, get_string('eachuserforum','forum'), '', ''); echo '<br />';
5051 print_checkbox('reset_forum_general', 1, true, get_string('generalforum','forum'), '', ''); echo '<br />';
5053 print_checkbox('reset_forum_subscriptions', 1, true, get_string('resetsubscriptions','forum'), '', '');
5059 * Converts a forum to use the Roles System
5060 * @param $forum - a forum object with the same attributes as a record
5061 * from the forum database table
5062 * @param $forummodid - the id of the forum module, from the modules table
5063 * @param $teacherroles - array of roles that have moodle/legacy:teacher
5064 * @param $studentroles - array of roles that have moodle/legacy:student
5065 * @param $guestroles - array of roles that have moodle/legacy:guest
5066 * @param $cmid - the course_module id for this forum instance
5067 * @return boolean - forum was converted or not
5069 function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
5070 $studentroles=array(), $guestroles=array(), $cmid=NULL) {
5074 if (!isset($forum->open
) && !isset($forum->assesspublic
)) {
5075 // We assume that this forum has already been converted to use the
5076 // Roles System. Columns forum.open and forum.assesspublic get dropped
5077 // once the forum module has been upgraded to use Roles.
5081 if ($forum->type
== 'teacher') {
5083 // Teacher forums should be converted to normal forums that
5084 // use the Roles System to implement the old behavior.
5086 // Seems that teacher forums were never backed up in 1.6 since they
5087 // didn't have an entry in the course_modules table.
5088 require_once($CFG->dirroot
.'/course/lib.php');
5090 if (count_records('forum_discussions', 'forum', $forum->id
) == 0) {
5091 // Delete empty teacher forums.
5092 delete_records('forum', 'id', $forum->id
);
5094 // Create a course module for the forum and assign it to
5095 // section 0 in the course.
5097 $mod->course
= $forum->course
;
5098 $mod->module
= $forummodid;
5099 $mod->instance
= $forum->id
;
5101 $mod->visible
= 0; // Hide the forum
5102 $mod->visibleold
= 0; // Hide the forum
5103 $mod->groupmode
= 0;
5105 if (!$cmid = add_course_module($mod)) {
5106 error('Could not create new course module instance for the teacher forum');
5108 $mod->coursemodule
= $cmid;
5109 if (!$sectionid = add_mod_to_section($mod)) {
5110 error('Could not add converted teacher forum instance to section 0 in the course');
5112 if (!set_field('course_modules', 'section', $sectionid, 'id', $cmid)) {
5113 error('Could not update course module with section id');
5118 // Change the forum type to general.
5119 $forum->type
= 'general';
5120 if (!update_record('forum', $forum)) {
5121 error('Could not change forum from type teacher to type general');
5124 $context = get_context_instance(CONTEXT_MODULE
, $cmid);
5126 // Create overrides for default student and guest roles (prevent).
5127 foreach ($studentroles as $studentrole) {
5128 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT
, $studentrole->id
, $context->id
);
5129 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT
, $studentrole->id
, $context->id
);
5130 assign_capability('mod/forum:startdiscussion', CAP_PREVENT
, $studentrole->id
, $context->id
);
5131 assign_capability('mod/forum:replypost', CAP_PREVENT
, $studentrole->id
, $context->id
);
5132 assign_capability('mod/forum:viewrating', CAP_PREVENT
, $studentrole->id
, $context->id
);
5133 assign_capability('mod/forum:viewanyrating', CAP_PREVENT
, $studentrole->id
, $context->id
);
5134 assign_capability('mod/forum:rate', CAP_PREVENT
, $studentrole->id
, $context->id
);
5135 assign_capability('mod/forum:createattachment', CAP_PREVENT
, $studentrole->id
, $context->id
);
5136 assign_capability('mod/forum:deleteownpost', CAP_PREVENT
, $studentrole->id
, $context->id
);
5137 assign_capability('mod/forum:deleteanypost', CAP_PREVENT
, $studentrole->id
, $context->id
);
5138 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT
, $studentrole->id
, $context->id
);
5139 assign_capability('mod/forum:movediscussions', CAP_PREVENT
, $studentrole->id
, $context->id
);
5140 assign_capability('mod/forum:editanypost', CAP_PREVENT
, $studentrole->id
, $context->id
);
5141 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT
, $studentrole->id
, $context->id
);
5142 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT
, $studentrole->id
, $context->id
);
5143 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT
, $studentrole->id
, $context->id
);
5144 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT
, $studentrole->id
, $context->id
);
5146 foreach ($guestroles as $guestrole) {
5147 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT
, $guestrole->id
, $context->id
);
5148 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT
, $guestrole->id
, $context->id
);
5149 assign_capability('mod/forum:startdiscussion', CAP_PREVENT
, $guestrole->id
, $context->id
);
5150 assign_capability('mod/forum:replypost', CAP_PREVENT
, $guestrole->id
, $context->id
);
5151 assign_capability('mod/forum:viewrating', CAP_PREVENT
, $guestrole->id
, $context->id
);
5152 assign_capability('mod/forum:viewanyrating', CAP_PREVENT
, $guestrole->id
, $context->id
);
5153 assign_capability('mod/forum:rate', CAP_PREVENT
, $guestrole->id
, $context->id
);
5154 assign_capability('mod/forum:createattachment', CAP_PREVENT
, $guestrole->id
, $context->id
);
5155 assign_capability('mod/forum:deleteownpost', CAP_PREVENT
, $guestrole->id
, $context->id
);
5156 assign_capability('mod/forum:deleteanypost', CAP_PREVENT
, $guestrole->id
, $context->id
);
5157 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT
, $guestrole->id
, $context->id
);
5158 assign_capability('mod/forum:movediscussions', CAP_PREVENT
, $guestrole->id
, $context->id
);
5159 assign_capability('mod/forum:editanypost', CAP_PREVENT
, $guestrole->id
, $context->id
);
5160 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT
, $guestrole->id
, $context->id
);
5161 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT
, $guestrole->id
, $context->id
);
5162 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT
, $guestrole->id
, $context->id
);
5163 assign_capability('mod/forum:throttlingapplies', CAP_PREVENT
, $guestrole->id
, $context->id
);
5167 // Non-teacher forum.
5170 // We were not given the course_module id. Try to find it.
5171 if (!$cm = get_coursemodule_from_instance('forum', $forum->id
)) {
5172 notify('Could not get the course module for the forum');
5178 $context = get_context_instance(CONTEXT_MODULE
, $cmid);
5180 // $forum->open defines what students can do:
5181 // 0 = No discussions, no replies
5182 // 1 = No discussions, but replies are allowed
5183 // 2 = Discussions and replies are allowed
5184 switch ($forum->open
) {
5186 foreach ($studentroles as $studentrole) {
5187 assign_capability('mod/forum:startdiscussion', CAP_PREVENT
, $studentrole->id
, $context->id
);
5188 assign_capability('mod/forum:replypost', CAP_PREVENT
, $studentrole->id
, $context->id
);
5192 foreach ($studentroles as $studentrole) {
5193 assign_capability('mod/forum:startdiscussion', CAP_PREVENT
, $studentrole->id
, $context->id
);
5194 assign_capability('mod/forum:replypost', CAP_ALLOW
, $studentrole->id
, $context->id
);
5198 foreach ($studentroles as $studentrole) {
5199 assign_capability('mod/forum:startdiscussion', CAP_ALLOW
, $studentrole->id
, $context->id
);
5200 assign_capability('mod/forum:replypost', CAP_ALLOW
, $studentrole->id
, $context->id
);
5205 // $forum->assessed defines whether forum rating is turned
5206 // on (1 or 2) and who can rate posts:
5207 // 1 = Everyone can rate posts
5208 // 2 = Only teachers can rate posts
5209 switch ($forum->assessed
) {
5211 foreach ($studentroles as $studentrole) {
5212 assign_capability('mod/forum:rate', CAP_ALLOW
, $studentrole->id
, $context->id
);
5214 foreach ($teacherroles as $teacherrole) {
5215 assign_capability('mod/forum:rate', CAP_ALLOW
, $teacherrole->id
, $context->id
);
5219 foreach ($studentroles as $studentrole) {
5220 assign_capability('mod/forum:rate', CAP_PREVENT
, $studentrole->id
, $context->id
);
5222 foreach ($teacherroles as $teacherrole) {
5223 assign_capability('mod/forum:rate', CAP_ALLOW
, $teacherrole->id
, $context->id
);
5228 // $forum->assesspublic defines whether students can see
5229 // everybody's ratings:
5230 // 0 = Students can only see their own ratings
5231 // 1 = Students can see everyone's ratings
5232 switch ($forum->assesspublic
) {
5234 foreach ($studentroles as $studentrole) {
5235 assign_capability('mod/forum:viewanyrating', CAP_PREVENT
, $studentrole->id
, $context->id
);
5237 foreach ($teacherroles as $teacherrole) {
5238 assign_capability('mod/forum:viewanyrating', CAP_ALLOW
, $teacherrole->id
, $context->id
);
5242 foreach ($studentroles as $studentrole) {
5243 assign_capability('mod/forum:viewanyrating', CAP_ALLOW
, $studentrole->id
, $context->id
);
5245 foreach ($teacherroles as $teacherrole) {
5246 assign_capability('mod/forum:viewanyrating', CAP_ALLOW
, $teacherrole->id
, $context->id
);
5252 $cm = get_record('course_modules', 'id', $cmid);
5257 // 1 - Separate groups
5258 // 2 - Visible groups
5259 switch ($cm->groupmode
) {
5263 foreach ($studentroles as $studentrole) {
5264 assign_capability('moodle/site:accessallgroups', CAP_PREVENT
, $studentrole->id
, $context->id
);
5266 foreach ($teacherroles as $teacherrole) {
5267 assign_capability('moodle/site:accessallgroups', CAP_ALLOW
, $teacherrole->id
, $context->id
);
5271 foreach ($studentroles as $studentrole) {
5272 assign_capability('moodle/site:accessallgroups', CAP_ALLOW
, $studentrole->id
, $context->id
);
5274 foreach ($teacherroles as $teacherrole) {
5275 assign_capability('moodle/site:accessallgroups', CAP_ALLOW
, $teacherrole->id
, $context->id
);