MDL-8323 finished full conversion to proper $COURSE global - no more $CFG->coursethem...
[moodle.git] / mod / forum / lib.php
1 <?php  // $Id$
3 require_once($CFG->libdir.'/filelib.php');
5 /// CONSTANTS ///////////////////////////////////////////////////////////
7 define('FORUM_MODE_FLATOLDEST', 1);
8 define('FORUM_MODE_FLATNEWEST', -1);
9 define('FORUM_MODE_THREADED', 2);
10 define('FORUM_MODE_NESTED', 3);
12 define('FORUM_FORCESUBSCRIBE', 1);
13 define('FORUM_INITIALSUBSCRIBE', 2);
14 define('FORUM_DISALLOWSUBSCRIBE',3);
16 define('FORUM_TRACKING_OFF', 0);
17 define('FORUM_TRACKING_OPTIONAL', 1);
18 define('FORUM_TRACKING_ON', 2);
20 define('FORUM_UNSET_POST_RATING', -999);
22 $FORUM_LAYOUT_MODES = array ( FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
23                               FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
24                               FORUM_MODE_THREADED   => get_string('modethreaded', 'forum'),
25                               FORUM_MODE_NESTED     => get_string('modenested', 'forum') );
27 // These are course content forums that can be added to the course manually
28 $FORUM_TYPES   = array ('general'    => get_string('generalforum', 'forum'),
29                         'eachuser'   => get_string('eachuserforum', 'forum'),
30                         'single'     => get_string('singleforum', 'forum'),
31                         'qanda'      => get_string('qandaforum', 'forum')
32                         );
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);
40 }
42 if (!isset($CFG->forum_shortpost)) {
43     set_config('forum_shortpost', 300);  // Less non-HTML characters than this is short
44 }
46 if (!isset($CFG->forum_longpost)) {
47     set_config('forum_longpost', 600);  // More non-HTML characters than this is long
48 }
50 if (!isset($CFG->forum_manydiscussions)) {
51     set_config('forum_manydiscussions', 100);  // Number of discussions on a page
52 }
54 if (!isset($CFG->forum_maxbytes)) {
55     set_config('forum_maxbytes', 512000);  // Default maximum size for all forums
56 }
58 if (!isset($CFG->forum_trackreadposts)) {
59     set_config('forum_trackreadposts', true);  // Default whether user needs to mark a post as read
60 }
62 if (!isset($CFG->forum_oldpostdays)) {
63     set_config('forum_oldpostdays', 14);  // Default number of days that a post is considered old
64 }
66 if (!isset($CFG->forum_usermarksread)) {
67     set_config('forum_usermarksread', false);  // Default whether user needs to mark a post as read
68 }
70 if (!isset($CFG->forum_cleanreadtime)) {
71     set_config('forum_cleanreadtime', 2);  // Default time (hour) to execute 'clean_read_records' cron
72 }
74 if (!isset($CFG->forum_replytouser)) {
75     set_config('forum_replytouser', true);  // Default maximum size for all forums
76 }
78 if (empty($USER->id) or isguest()) {
79     $CFG->forum_trackreadposts = false;  // This feature never works when a user isn't logged in
80 }
82 if (!isset($CFG->forum_enabletimedposts)) {   // Newish feature that is not quite ready for production in 1.6
83     $CFG->forum_enabletimedposts = false;
84 }
87 /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
89 function forum_add_instance($forum) {
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     global $CFG;
97     $forum->timemodified = time();
99     if (!isset($forum->assessed)) {
100         $forum->assessed = 0;
101     }
103     if (empty($forum->ratingtime)) {
104         $forum->assesstimestart  = 0;
105         $forum->assesstimefinish = 0;
106     }
108     if (!$forum->id = insert_record('forum', $forum)) {
109         return false;
110     }
112     if ($forum->type == 'single') {  // Create related discussion.
113         $discussion->course   = $forum->course;
114         $discussion->forum    = $forum->id;
115         $discussion->name     = $forum->name;
116         $discussion->intro    = $forum->intro;
117         $discussion->assessed = $forum->assessed;
118         $discussion->format   = $forum->format;
119         $discussion->mailnow  = false;
121         if (! forum_add_discussion($discussion, $discussion->intro)) {
122             error('Could not add the discussion for this forum');
123         }
124     }
126     if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) { // all users should be subscribed initially
127         $users = get_course_users($forum->course);
128         foreach ($users as $user) {
129             forum_subscribe($user->id, $forum->id);
130         }
131     }
133     return $forum->id;
137 function forum_update_instance($forum) {
138 // Given an object containing all the necessary data,
139 // (defined by the form in mod.html) this function
140 // will update an existing instance with new data.
142     $forum->timemodified = time();
143     $forum->id = $forum->instance;
145     if (empty($forum->assessed)) {
146         $forum->assessed = 0;
147     }
149     if (empty($forum->ratingtime)) {
150         $forum->assesstimestart  = 0;
151         $forum->assesstimefinish = 0;
152     }
154     if ($forum->type == 'single') {  // Update related discussion and post.
155         if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
156             if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
157                 notify('Warning! There is more than one discussion in this forum - using the most recent');
158                 $discussion = array_pop($discussions);
159             } else {
160                 error('Could not find the discussion in this forum');
161             }
162         }
163         if (! $post = get_record('forum_posts', 'id', $discussion->firstpost)) {
164             error('Could not find the first post in this forum discussion');
165         }
167         $post->subject  = $forum->name;
168         $post->message  = $forum->intro;
169         $post->modified = $forum->timemodified;
171         if (! update_record('forum_posts', $post)) {
172             error('Could not update the first post');
173         }
175         $discussion->name = $forum->name;
177         if (! update_record('forum_discussions', $discussion)) {
178             error('Could not update the discussion');
179         }
180     }
182     return update_record('forum', $forum);
186 function forum_delete_instance($id) {
187 // Given an ID of an instance of this module,
188 // this function will permanently delete the instance
189 // and any data that depends on it.
191     if (!$forum = get_record('forum', 'id', $id)) {
192         return false;
193     }
195     $result = true;
197     if ($discussions = get_records('forum_discussions', 'forum', $forum->id)) {
198         foreach ($discussions as $discussion) {
199             if (!forum_delete_discussion($discussion, true)) {
200                 $result = false;
201             }
202         }
203     }
205     if (!delete_records('forum_subscriptions', 'forum', $forum->id)) {
206         $result = false;
207     }
209     forum_tp_delete_read_records(-1, -1, -1, $forum->id);
211     if (!delete_records('forum', 'id', $forum->id)) {
212         $result = false;
213     }
215     return $result;
219 function forum_cron() {
220 /// Function to be run periodically according to the moodle cron
221 /// Finds all posts that have yet to be mailed out, and mails them
222 /// out to all subscribers
224     global $CFG, $USER;
225     static $strforums = NULL;
227     static $cachecm = array();
229     if ($strforums === NULL) {
230         $strforums = get_string('forums', 'forum');
231     }
233     $realuser = clone($USER);
235     /// Posts older than 2 days will not be mailed.  This is to avoid the problem where
236     /// cron has not been running for a long time, and then suddenly people are flooded
237     /// with mail from the past few weeks or months
239     $timenow   = time();
240     $endtime   = $timenow - $CFG->maxeditingtime;
241     $starttime = $endtime - 48 * 3600;   /// Two days earlier
243     $CFG->enablerecordcache = true;      // We want all the caching we can get
245     if ($posts = forum_get_unmailed_posts($starttime, $endtime)) {
247         /// Mark them all now as being mailed.  It's unlikely but possible there
248         /// might be an error later so that a post is NOT actually mailed out,
249         /// but since mail isn't crucial, we can accept this risk.  Doing it now
250         /// prevents the risk of duplicated mails, which is a worse problem.
252         if (!forum_mark_old_posts_as_mailed($endtime)) {
253             mtrace('Errors occurred while trying to mark some posts as being mailed.');
254             return false;  // Don't continue trying to mail them, in case we are in a cron loop
255         }
257         @set_time_limit(0);   /// so that script does not get timed out when posting to many users
259         $urlinfo = parse_url($CFG->wwwroot);
260         $hostname = $urlinfo['host'];
262         foreach ($posts as $post) {
264             mtrace(get_string('processingpost', 'forum', $post->id), '');
266             /// Check the consistency of the data first
268             if (! $userfrom = get_record('user', 'id', $post->userid)) {
269                 mtrace('Could not find user '.$post->userid);
270                 continue;
271             }
273             if (! $discussion = get_record('forum_discussions', 'id', $post->discussion)) {
274                 mtrace('Could not find discussion '.$post->discussion);
275                 continue;
276             }
278             if (! $forum = get_record('forum', 'id', $discussion->forum)) {
279                 mtrace('Could not find forum '.$discussion->forum);
280                 continue;
281             }
283             if (! $course = get_record('course', 'id', $forum->course)) {
284                 mtrace('Could not find course '.$forum->course);
285                 continue;
286             }
288             $cleanforumname = str_replace('"', "'", strip_tags($forum->name));
290             $userfrom->customheaders = array (  // Headers to make emails easier to track
291                        'Precedence: Bulk',
292                        'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
293                        'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
294                        'Message-Id: <moodlepost'.$post->id.'@'.$hostname.'>',
295                        'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>',
296                        'References: <moodlepost'.$post->parent.'@'.$hostname.'>',
297                        'X-Course-Id: '.$course->id,
298                        'X-Course-Name: '.strip_tags($course->fullname)
299             );
302             // Get coursemodule record (and cache these)
304             if (!empty($cachecm[$forum->id])) {
305                 $cm = $cachecm[$forum->id];
307             } else if ($cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
308                 $cachecm[$forum->id] = $cm;
310             } else {
311                 $cm = new object;
312                 $cm->id = 0;
313                 $cachecm[$forum->id] = $cm;
314             }
316             $groupmode = false;
317             if (!empty($cm->id)) {
318                 if ($groupmode = groupmode($course, $cm) and $discussion->groupid > 0) {   // Groups are being used
319                     if (! groups_group_exists($discussion->groupid)) { // Can't find group //TODO:
320                         continue;                                            // Be safe and don't send it to anyone
321                     }
322                 }
323             }
325             $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);   // Cached already
327             if ($users = forum_subscribed_users($course, $forum, 0, true)) {
329                 $mailcount=0;
330                 $errorcount=0;
331                 foreach ($users as $userto) {
332                     if ($groupmode) {    // Look for a reason not to send this email
333                         if (!empty($group->id)) {
334                             if (!ismember($group->id, $userto->id)) {
335                                 if (!has_capability('moodle/site:accessallgroups', $modcontext, false, $userto->id)) {
336                                     continue;
337                                 }
338                             }
339                         }
340                     }
342                     // make sure we're allowed to see it...
343                     if (!forum_user_can_see_post($forum, $discussion, $post, $userto)) {
344                         continue;
345                     }
347                     if ($userto->maildigest > 0) {
348                         // This user wants the mails to be in digest form
349                         $queue = New stdClass;
350                         $queue->userid = $userto->id;
351                         $queue->discussionid = $discussion->id;
352                         $queue->postid = $post->id;
353                         if (!insert_record('forum_queue', $queue)) {
354                             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.");
355                         }
356                         continue;
357                     }
359                     /// Override the language and timezone of the "current" user, so that
360                     /// mail is customised for the receiver.
361                     $USER = $userto;
362                     course_setup($course);
364                     $postsubject = "$course->shortname: ".format_string($post->subject,true);
365                     $posttext = forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto);
366                     $posthtml = forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto);
368                     if (!$mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext,
369                                                      $posthtml, '', '', $CFG->forum_replytouser)) {
370                         mtrace("Error: mod/forum/cron.php: Could not send out mail for id $post->id to user $userto->id".
371                              " ($userto->email) .. not trying again.");
372                         add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
373                                    substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
374                         $errorcount++;
375                     } else if ($mailresult === 'emailstop') {
376                         add_to_log($course->id, 'forum', 'mail blocked', "discuss.php?d=$discussion->id#p$post->id",
377                                    substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
378                     } else {
379                         $mailcount++;
381                     /// Mark post as read if forum_usermarksread is set off
382                         if (!$CFG->forum_usermarksread && forum_tp_can_track_forums($forum, $userto) &&
383                             forum_tp_is_tracked($forum, $userto->id)) {
384                             if (!forum_tp_mark_post_read($userto->id, $post, $forum->id)) {
385                                 mtrace("Error: mod/forum/cron.php: Could not mark post $post->id read for user $userto->id".
386                                      " while sending email.");
387                             }
388                         }
389                     }
390                 }
392                 mtrace(".... mailed to $mailcount users.");
393                 if ($errorcount) {
394                     set_field("forum_posts", "mailed", "2", "id", "$post->id");
395                 }
396             }
397         }
398     }
400     $sitetimezone = $CFG->timezone;
402     /// Now see if there are any digest mails waiting to be sent, and if we should send them
404     if (!isset($CFG->digestmailtimelast)) {    // To catch the first time
405         set_config('digestmailtimelast', 0);
406     }
408     $timenow = time();
409     $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
411     if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
413         set_config('digestmailtimelast', $timenow);
415         mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
417         $digestposts = get_records('forum_queue');
418         if(!empty($digestposts)) {
420             @set_time_limit(0);   /// so that script does not get timed out when posting to many users
422             // We have work to do
423             $usermailcount = 0;
424             $site = get_site();
426             $users = array();
427             $posts = array();
428             $discussions = array();
429             $discussionposts = array();
430             $userdiscussions = array();
431             foreach($digestposts as $digestpost) {
432                 if(!isset($users[$digestpost->userid])) {
433                     $users[$digestpost->userid] = get_record('user', 'id', $digestpost->userid);
434                 }
435                 if(!isset($discussions[$digestpost->discussionid])) {
436                     $discussions[$digestpost->discussionid] = get_record('forum_discussions', 'id', $digestpost->discussionid);
437                 }
438                 if(!isset($posts[$digestpost->postid])) {
439                     $posts[$digestpost->postid] = get_record('forum_posts', 'id', $digestpost->postid);
440                 }
441                 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
442                 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
443             }
445             // Data collected, start sending out emails to each user
447             foreach($userdiscussions as $userid => $thesediscussions) {
449                 mtrace(get_string('processingdigest', 'forum', $userid),'... ');
451                 // First of all delete all the queue entries for this user
452                 delete_records('forum_queue', 'userid', $userid);
453                 $userto = $users[$userid];
455                 /// Override the language and timezone of the "current" user, so that
456                 /// mail is customised for the receiver.
457                 $USER = $userto;
458                 course_setup(SITEID);
461                 $postsubject = get_string('digestmailsubject', 'forum', $site->shortname);
462                 $headerdata = New stdClass;
463                 $headerdata->sitename = $site->fullname;
464                 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
466                 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
467                 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
469                 $posthtml = "<head>";
470                 foreach ($CFG->stylesheets as $stylesheet) {
471                     $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
472                 }
473                 $posthtml .= "</head>\n<body>\n";
474                 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
476                 foreach($thesediscussions as $discussionid) {
477                     $discussion = $discussions[$discussionid];
478                     if(empty($discussion)) {
479                         mtrace("Error: Could not find discussion $discussionid");
480                         continue;
481                     }
483                     if (! $forum = get_record("forum", "id", "$discussion->forum")) {
484                         mtrace("Could not find forum $discussion->forum");
485                         continue;
486                     }
487                     if (! $course = get_record("course", "id", "$forum->course")) {
488                         mtrace("Could not find course $forum->course");
489                         continue;
490                     }
492                     //override language
493                     course_setup($course);
495                     $canunsubscribe = ! forum_is_forcesubscribed($forum->id);
496                     $canreply = forum_user_can_post($forum, $userto);
499                     $posttext .= "\n \n";
500                     $posttext .= '=====================================================================';
501                     $posttext .= "\n \n";
502                     $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
503                     if ($discussion->name != $forum->name) {
504                         $posttext  .= " -> ".format_string($discussion->name,true);
505                     }
506                     $posttext .= "\n";
508                     $posthtml .= "<p><font face=\"sans-serif\">".
509                     "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
510                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
511                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
512                     if ($discussion->name == $forum->name) {
513                         $posthtml .= "</font></p>";
514                     } else {
515                         $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
516                     }
517                     $posthtml .= '<p>';
519                     $postsarray = $discussionposts[$discussionid];
520                     sort($postsarray);
522                 /// Create an empty array to use for marking read posts.
523                 /// (I'm sure there's already a structure I can use here, but I can't be sure.)
524                     $markread = array();
526                     foreach ($postsarray as $postid) {
527                         if (! $post = get_record("forum_posts", "id", "$postid")) {
528                             mtrace("Error: Could not find post $postid");
529                             continue;
530                         }
531                         if (! $userfrom = get_record("user", "id", "$post->userid")) {
532                             mtrace("Error: Could not find user $post->userid");
533                             continue;
534                         }
536                         $userfrom->customheaders = array ("Precedence: Bulk");
538                         if ($userto->maildigest == 2) {
539                             // Subjects only
540                             $by = New stdClass;
541                             $by->name = fullname($userfrom);
542                             $by->date = userdate($post->modified);
543                             $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
544                             $posttext .= "\n---------------------------------------------------------------------";
546                             $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
547                             $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>';
549                         } else {
550                             // The full treatment
551                             $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true);
552                             $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
554                         /// Create an array of postid's for this user to mark as read.
555                             if (!$CFG->forum_usermarksread &&
556                                 forum_tp_can_track_forums($forum, $userto) &&
557                                 forum_tp_is_tracked($forum, $userto->id)) {
558                                 $markread[$post->id]->post = $post;
559                                 $markread[$post->id]->forumid = $forum->id;
560                             }
561                         }
562                     }
563                     if ($canunsubscribe) {
564                         $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>";
565                     } else {
566                         $posthtml .= "\n<div align=\"right\"><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
567                     }
568                     $posthtml .= '<hr size="1" noshade="noshade" /></p>';
569                 }
570                 $posthtml .= '</body>';
572                 if($userto->mailformat != 1) {
573                     // This user DOESN'T want to receive HTML
574                     $posthtml = '';
575                 }
577                 if (!$mailresult =  email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml,
578                                                   '', '', $CFG->forum_replytouser)) {
579                     mtrace("ERROR!");
580                     echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
581                     add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
582                 } else if ($mailresult === 'emailstop') {
583                     add_to_log($course->id, 'forum', 'mail digest blocked', '', '', $cm->id, $userto->id);
584                 } else {
585                     mtrace("success.");
586                     $usermailcount++;
588                 /// Mark post as read if forum_usermarksread is set off
589                     if (!$CFG->forum_usermarksread &&
590                         forum_tp_can_track_forums($forum->id, $userto) &&
591                         forum_tp_is_tracked($forum->id, $userto->id)) {
592                         foreach ($markread as $postinfo) {
593                             if (!forum_tp_mark_post_read($userto->id, $postinfo->post, $postinfo->forumid)) {
594                                 mtrace("Error: mod/forum/cron.php: Could not mark post $postid read for user $userto->id".
595                                      " while sending digest email.");
596                             }
597                         }
598                     }
599                 }
600             }
601         }
602     }
604     if(!empty($usermailcount)) {
605         mtrace(get_string('digestsentusers', 'forum', $usermailcount));
606     }
608     $USER = $realuser;
609     course_setup(SITEID); // reset cron user language, theme and timezone settings
611     if (!empty($CFG->forum_lastreadclean)) {
612         $timenow = time();
613         if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
614             set_config('forum_lastreadclean', $timenow);
615             forum_tp_clean_read_records();
616         }
617     } else {
618         set_config('forum_lastreadclean', time());
619     }
622     return true;
625 function forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
626     global $CFG;
628     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
629         error('Course Module ID was incorrect');
630     }
631     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
632     $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
634     $by = New stdClass;
635     $by->name = fullname($userfrom, $viewfullnames);
636     $by->date = userdate($post->modified, "", $userto->timezone);
638     $strbynameondate = get_string('bynameondate', 'forum', $by);
640     $strforums = get_string('forums', 'forum');
642     $canunsubscribe = ! forum_is_forcesubscribed($forum->id);
643     $canreply = forum_user_can_post($forum, $userto);
645     $posttext = '';
647     if (!$bare) {
648         $posttext  = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
650         if ($discussion->name != $forum->name) {
651             $posttext  .= " -> ".format_string($discussion->name,true);
652         }
653     }
655     $posttext .= "\n---------------------------------------------------------------------\n";
656     $posttext .= format_string($post->subject,true);
657     if ($bare) {
658         $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
659     }
660     $posttext .= "\n".$strbynameondate."\n";
661     $posttext .= "---------------------------------------------------------------------\n";
662     $posttext .= format_text_email(trusttext_strip($post->message), $post->format);
663     $posttext .= "\n\n";
664     if ($post->attachment) {
665         $post->course = $course->id;
666         $post->forum = $forum->id;
667         $posttext .= forum_print_attachments($post, "text");
668     }
669     if (!$bare && $canreply) {
670         $posttext .= "---------------------------------------------------------------------\n";
671         $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
672         $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
673     }
674     if (!$bare && $canunsubscribe) {
675         $posttext .= "\n---------------------------------------------------------------------\n";
676         $posttext .= get_string("unsubscribe", "forum");
677         $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
678     }
680     return $posttext;
683 function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto) {
684     global $CFG;
686     if ($userto->mailformat != 1) {  // Needs to be HTML
687         return '';
688     }
690     $strforums = get_string('forums', 'forum');
691     $canreply = forum_user_can_post($forum, $userto);
692     $canunsubscribe = ! forum_is_forcesubscribed($forum->id);
694     $posthtml = '<head>';
695     foreach ($CFG->stylesheets as $stylesheet) {
696         $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
697     }
698     $posthtml .= '</head>';
699     $posthtml .= "\n<body id=\"email\">\n\n";
701     $posthtml .= '<div class="navbar">'.
702     '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
703     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
704     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
705     if ($discussion->name == $forum->name) {
706         $posthtml .= '</div>';
707     } else {
708         $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
709                      format_string($discussion->name,true).'</a></div>';
710     }
711     $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
713     if ($canunsubscribe) {
714         $posthtml .= '<br /><div class="unsubscribelink"><a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.
715                      get_string('unsubscribe', 'forum').'</a></div>';
716     }
718     $posthtml .= '</body>';
720     return $posthtml;
723 function forum_user_outline($course, $user, $mod, $forum) {
725     if ($posts = forum_get_user_posts($forum->id, $user->id)) {
726         $result->info = get_string("numposts", "forum", count($posts));
728         $lastpost = array_pop($posts);
729         $result->time = $lastpost->modified;
730         return $result;
731     }
732     return NULL;
736 function forum_user_complete($course, $user, $mod, $forum) {
737     global $CFG;
739     if ($posts = forum_get_user_posts($forum->id, $user->id)) {
740         foreach ($posts as $post) {
742             $post->forum = $forum->id;
743             forum_print_post($post, $course->id, $ownpost=false, $reply=false, $link=false, $rate=false);
744         }
746     } else {
747         echo "<p>".get_string("noposts", "forum")."</p>";
748     }
751 function forum_print_overview($courses,&$htmlarray) {
752     global $USER, $CFG;
753     $LIKE = sql_ilike();
755     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
756         return array();
757     }
759     if (!$forums = get_all_instances_in_courses('forum',$courses)) {
760         return;
761     }
764     // get all forum logs in ONE query (much better!)
765     $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {$CFG->prefix}log l "
766         ." JOIN {$CFG->prefix}course_modules cm ON cm.id = cmid "
767         ." WHERE (";
768     foreach ($courses as $course) {
769         $sql .= '(l.course = '.$course->id.' AND l.time > '.$course->lastaccess.') OR ';
770     }
771     $sql = substr($sql,0,-3); // take off the last OR
773     $sql .= ") AND l.module = 'forum' AND action $LIKE 'add post%' "
774         ." AND userid != ".$USER->id." GROUP BY cmid,l.course,instance";
776     if (!$new = get_records_sql($sql)) {
777         $new = array(); // avoid warnings
778     }
780     // also get all forum tracking stuff ONCE.
781     $trackingforums = array();
782     foreach ($forums as $forum) {
783         if (forum_tp_can_track_forums($forum)) {
784             $trackingforums[$forum->id] = $forum;
785         }
786     }
788     if (count($trackingforums) > 0) {
789         $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
790         $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
791             ' FROM '.$CFG->prefix.'forum_posts p '.
792             ' JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
793             ' LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$USER->id.' WHERE (';
794         foreach ($trackingforums as $track) {
795             $sql .= '(d.forum = '.$track->id.' AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = '.get_current_group($track->course,false).')) OR ';
796         }
797         $sql = substr($sql,0,-3); // take off the last OR
798         $sql .= ') AND p.modified >= '.$cutoffdate.' AND r.id is NULL GROUP BY d.forum,d.course';
800         if (!$unread = get_records_sql($sql)) {
801             $unread = array();
802         }
803     } else {
804         $unread = array();
805     }
807     if (empty($unread) and empty($new)) {
808         return;
809     }
811     $strforum = get_string('modulename','forum');
812     $strnumunread = get_string('overviewnumunread','forum');
813     $strnumpostssince = get_string('overviewnumpostssince','forum');
815     foreach ($forums as $forum) {
816         $str = '';
817         $count = 0;
818         $thisunread = 0;
819         $showunread = false;
820         // either we have something from logs, or trackposts, or nothing.
821         if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
822             $count = $new[$forum->id]->count;
823         }
824         if (array_key_exists($forum->id,$unread)) {
825             $thisunread = $unread[$forum->id]->count;
826             $showunread = true;
827         }
828         if ($count > 0 || $thisunread > 0) {
829             $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
830                 $forum->name.'</a></div>';
831             $str .= '<div class="info">';
832             $str .= $count.' '.$strnumpostssince;
833             if (!empty($showunread)) {
834                 $str .= '<br />'.$thisunread .' '.$strnumunread;
835             }
836             $str .= '</div></div>';
837         }
838         if (!empty($str)) {
839             if (!array_key_exists($forum->course,$htmlarray)) {
840                 $htmlarray[$forum->course] = array();
841             }
842             if (!array_key_exists('forum',$htmlarray[$forum->course])) {
843                 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
844             }
845             $htmlarray[$forum->course]['forum'] .= $str;
846         }
847     }
850 function forum_print_recent_activity($course, $isteacher, $timestart) {
851 /// Given a course and a date, prints a summary of all the new
852 /// messages posted in the course since that date
854     global $CFG;
855     $LIKE = sql_ilike();
857     $heading = false;
858     $content = false;
860     if (!$logs = get_records_select('log', 'time > \''.$timestart.'\' AND '.
861                                            'course = \''.$course->id.'\' AND '.
862                                            'module = \'forum\' AND '.
863                                            'action '.$LIKE.' \'add %\' ', 'time ASC')){
864         return false;
865     }
867     $strftimerecent = get_string('strftimerecent');
869     $mygroupid = mygroupid($course->id);
870     $groupmode = array();   /// To cache group modes
872     foreach ($logs as $log) {
873         //Get post info, I'll need it later
874         if ($post = forum_get_post_from_log($log)) {
875             //Create a temp valid module structure (course,id)
876             $tempmod->course = $log->course;
877             $tempmod->id = $post->forum;
878             //Obtain the visible property from the instance
879             $coursecontext = get_context_instance(CONTEXT_COURSE, $tempmod->course);
880             $modvisible = instance_is_visible('forum', $tempmod)
881                             || has_capability('moodle/course:viewhiddenactivities', $coursecontext);
882         }
884         //Only if the post exists and mod is visible
885         if ($post && $modvisible) {
887             if (!isset($cm[$post->forum])) {
888                 $cm[$post->forum] = get_coursemodule_from_instance('forum', $post->forum, $course->id);
889             }
890             $modcontext = get_context_instance(CONTEXT_MODULE, $cm[$post->forum]->id);
892             /// Check whether this is belongs to a discussion in a group that
893             /// should NOT be accessible to the current user
894             if (!has_capability('moodle/site:accessallgroups', $modcontext)
895                     && $post->groupid != -1) {   /// Open discussions have groupid -1
897                 $groupmode[$post->forum] = groupmode($course, $cm[$post->forum]);
899                 if ($groupmode[$post->forum]) {
900                     //hope i didn't break anything
901                     if (!@in_array($mygroupid, $post->groupid))/*$mygroupid != $post->groupid*/{
902                         continue;
903                     }
904                 }
905             }
907             if (! $heading) {
908                 print_headline(get_string('newforumposts', 'forum').':');
909                 $heading = true;
910                 $content = true;
911             }
912             $date = userdate($post->modified, $strftimerecent);
914             $subjectclass = ($log->action == 'add discussion') ? ' bold' : '';
916             echo '<div class="head">'.
917                    '<div class="date">'.$date.'</div>'.
918                    '<div class="name">'.fullname($post, has_capability('moodle/site:viewfullnames', $coursecontext)).'</div>'.
919                  '</div>';
920             echo '<div class="info'.$subjectclass.'">';
921             echo '"<a href="'.$CFG->wwwroot.'/mod/forum/'.str_replace('&', '&amp;', $log->url).'">';
922             $post->subject = break_up_long_words(format_string($post->subject,true));
923             echo $post->subject;
924             echo '</a>"</div>';
925         }
926     }
927     return $content;
931 function forum_grades($forumid) {
932 /// Must return an array of grades, indexed by user, and a max grade.
934     if (!$forum = get_record("forum", "id", $forumid)) {
935         return false;
936     }
937     if (!$forum->assessed) {
938         return false;
939     }
940     $scalemenu = make_grades_menu($forum->scale);
942     $currentuser = 0;
943     $ratingsuser = array();
945     if ($ratings = forum_get_user_grades($forumid)) {
946         foreach ($ratings as $rating) {     // Ordered by user
947             if ($currentuser and $rating->userid != $currentuser) {
948                 if (!empty($ratingsuser)) {
949                     if ($forum->scale < 0) {
950                         $return->grades[$currentuser] = forum_get_ratings_mean(0, $scalemenu, $ratingsuser);
951                         $return->grades[$currentuser] .= "<br />".forum_get_ratings_summary(0, $scalemenu, $ratingsuser);
952                     } else {
953                         $total = 0;
954                         $count = 0;
955                         foreach ($ratingsuser as $ra) {
956                             $total += $ra;
957                             $count ++;
958                         }
959                         $return->grades[$currentuser] = format_float($total/$count, 2);
960                     }
961                 } else {
962                     $return->grades[$currentuser] = "";
963                 }
964                 $ratingsuser = array();
965             }
966             $ratingsuser[] = $rating->rating;
967             $currentuser = $rating->userid;
968         }
969         if (!empty($ratingsuser)) {
970             if ($forum->scale < 0) {
971                 $return->grades[$currentuser] = forum_get_ratings_mean(0, $scalemenu, $ratingsuser);
972                 $return->grades[$currentuser] .= "<br />".forum_get_ratings_summary(0, $scalemenu, $ratingsuser);
973             } else {
974                 $total = 0;
975                 $count = 0;
976                 foreach ($ratingsuser as $ra) {
977                     $total += $ra;
978                     $count ++;
979                 }
980                 $return->grades[$currentuser] = format_float((float)$total/(float)$count, 2);
981             }
982         } else {
983             $return->grades[$currentuser] = "";
984         }
985     } else {
986         $return->grades = array();
987     }
989     if ($forum->scale < 0) {
990         $return->maxgrade = "";
991     } else {
992         $return->maxgrade = $forum->scale;
993     }
994     return $return;
997 function forum_get_participants($forumid) {
998 //Returns the users with data in one forum
999 //(users with records in forum_subscriptions, forum_posts and forum_ratings, students)
1001     global $CFG;
1003     //Get students from forum_subscriptions
1004     $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
1005                                          FROM {$CFG->prefix}user u,
1006                                               {$CFG->prefix}forum_subscriptions s
1007                                          WHERE s.forum = '$forumid' and
1008                                                u.id = s.userid");
1009     //Get students from forum_posts
1010     $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
1011                                  FROM {$CFG->prefix}user u,
1012                                       {$CFG->prefix}forum_discussions d,
1013                                       {$CFG->prefix}forum_posts p
1014                                  WHERE d.forum = '$forumid' and
1015                                        p.discussion = d.id and
1016                                        u.id = p.userid");
1018     //Get students from forum_ratings
1019     $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
1020                                    FROM {$CFG->prefix}user u,
1021                                         {$CFG->prefix}forum_discussions d,
1022                                         {$CFG->prefix}forum_posts p,
1023                                         {$CFG->prefix}forum_ratings r
1024                                    WHERE d.forum = '$forumid' and
1025                                          p.discussion = d.id and
1026                                          r.post = p.id and
1027                                          u.id = r.userid");
1029     //Add st_posts to st_subscriptions
1030     if ($st_posts) {
1031         foreach ($st_posts as $st_post) {
1032             $st_subscriptions[$st_post->id] = $st_post;
1033         }
1034     }
1035     //Add st_ratings to st_subscriptions
1036     if ($st_ratings) {
1037         foreach ($st_ratings as $st_rating) {
1038             $st_subscriptions[$st_rating->id] = $st_rating;
1039         }
1040     }
1041     //Return st_subscriptions array (it contains an array of unique users)
1042     return ($st_subscriptions);
1045 function forum_scale_used ($forumid,$scaleid) {
1046 //This function returns if a scale is being used by one forum
1048     $return = false;
1050     $rec = get_record("forum","id","$forumid","scale","-$scaleid");
1052     if (!empty($rec) && !empty($scaleid)) {
1053         $return = true;
1054     }
1056     return $return;
1059 /// SQL FUNCTIONS ///////////////////////////////////////////////////////////
1061 function forum_get_post_full($postid) {
1062 /// Gets a post with all info ready for forum_print_post
1063 /// Most of these joins are just to get the forum id
1064     global $CFG;
1066     return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1067                             FROM {$CFG->prefix}forum_posts p
1068                        LEFT JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
1069                        LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1070                            WHERE p.id = '$postid'");
1073 function forum_get_discussion_posts($discussion, $sort, $forumid) {
1074 /// Gets posts with all info ready for forum_print_post
1075 /// We pass forumid in because we always know it so no need to make a
1076 /// complicated join to find it out.
1077     global $CFG;
1079     return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1080                               FROM {$CFG->prefix}forum_posts p
1081                          LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1082                              WHERE p.discussion = $discussion
1083                                AND p.parent > 0 $sort");
1086 function forum_get_child_posts($parent, $forumid) {
1087 /// Gets posts with all info ready for forum_print_post
1088 /// We pass forumid in because we always know it so no need to make a
1089 /// complicated join to find it out.
1090     global $CFG;
1092     return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
1093                               FROM {$CFG->prefix}forum_posts p
1094                          LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1095                              WHERE p.parent = '$parent'
1096                           ORDER BY p.created ASC");
1099 /**
1100  * An array of forum objects that the user is allowed to read/search through.
1101  * @param $userid
1102  * @param $courseid - if 0, we look for forums throughout the whole site.
1103  * @return array of forum objects, or false if no matches
1104  *         Forum objects have the following attributes:
1105  *         id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1106  *         viewhiddentimedposts
1107  */
1108 function forum_get_readable_forums($userid, $courseid=0) {
1110     global $CFG, $USER;
1112     if (!$forummod = get_record('modules', 'name', 'forum')) {
1113         error('The forum module is not installed');
1114     }
1116     if ($courseid) {
1117         $courses = get_records('course', 'id', $courseid);
1118     } else {
1119         $courses = get_records('course');
1120     }
1121     if (!$courses) {
1122         return false;
1123     }
1125     $readableforums = array();
1127     foreach($courses as $course) {
1128         $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
1130         if (has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
1131             $selecthidden = ' AND cm.visible = 1';
1132         } else {
1133             $selecthidden = '';
1134         }
1136         $selectforums = "SELECT f.id AS id,
1137                                 f.name AS name,
1138                                 f.type AS type,
1139                                 f.course AS course,
1140                                 cm.id AS cmid,
1141                                 cm.visible AS cmvisible,
1142                                 cm.groupmode AS cmgroupmode
1143                            FROM {$CFG->prefix}course_modules cm,
1144                                 {$CFG->prefix}forum f
1145                           WHERE cm.instance = f.id
1146                             AND cm.course = {$course->id}
1147                             AND cm.module = {$forummod->id}
1148                                 $selecthidden
1149                                 ORDER BY f.name ASC";
1151         if ($forums = get_records_sql($selectforums)) {
1153             $group = user_group($course->id, $userid);
1155             foreach ($forums as $forum) {
1156                 $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid);
1158                 // Evaluate groupmode.
1159                 $cm = new object;
1160                 $cm->id = $forum->cmid;
1161                 $cm->groupmode = $forum->cmgroupmode;
1162                 $forum->cmgroupmode = groupmode($course, $cm);
1164                 if ($forum->cmgroupmode == SEPARATEGROUPS
1165                         && !has_capability('moodle/site:accessallgroups', $forumcontext)) {
1166                     $forum->accessallgroups = false;
1167                     $forum->accessgroup = $group->id;  // The user can only access
1168                                                        // discussions for this group.
1169                 } else {
1170                     $forum->accessallgroups = true;
1171                 }
1173                 if (has_capability('mod/forum:viewdiscussion', $forumcontext)) {
1175                     $forum->viewhiddentimedposts
1176                         = has_capability('mod/forum:viewhiddentimedposts', $forumcontext);
1178                     if ($forum->type == 'qanda'
1179                             && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) {
1181                         // We need to check whether the user has posted in the qanda forum.
1182                         $forum->onlydiscussions = array();  // Holds discussion ids for the discussions
1183                                                             // the user is allowed to see in this forum.
1185                         if ($discussionspostedin =
1186                                     forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1187                             foreach ($discussionspostedin as $d) {
1188                                 array_push($forum->onlydiscussions, $d->id);
1189                             }
1190                         }
1191                     }
1192                     array_push($readableforums, $forum);
1193                 }
1194             }
1195         }
1196     } // End foreach $courses
1198     //print_object($courses);
1199     //print_object($readableforums);
1201     return $readableforums;
1204 /**
1205  * Returns a list of posts found using an array of search terms.
1206  * @param $searchterms - array of search terms, e.g. word +word -word
1207  * @param $courseid - if 0, we search through the whole site
1208  * @param $page
1209  * @param $recordsperpage=50
1210  * @param &$totalcount
1211  * @param $extrasql
1212  * @return array of posts found
1213  */
1214 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
1215                             &$totalcount, $extrasql='') {
1216     global $CFG, $USER;
1217     require_once($CFG->libdir.'/searchlib.php');
1219     $forums = forum_get_readable_forums($USER->id, $courseid);
1221     if (count($forums) == 0) {
1222         return false;
1223     }
1225     for ($i=0; $i<count($forums); $i++) {
1226         if ($i == 0) {
1227             $selectdiscussion = " ((d.forum = {$forums[$i]->id}";
1228         } else {
1229             $selectdiscussion .= " OR (d.forum = {$forums[$i]->id}";
1230         }
1231         if (!empty($CFG->forum_enabletimedposts) && !$forums[$i]->viewhiddentimedposts) {
1232             $now = time();
1233             $selectdiscussion .= " AND ( d.userid = {$USER->id}
1234                                    OR ((d.timestart = 0 OR d.timestart <= $now)
1235                                    AND (d.timeend = 0 OR d.timeend > $now)) )";
1236         }
1237         if ($forums[$i]->type == 'qanda' && isset($forums[$i]->onlydiscussions)) {
1238             // This is a qanda forum.
1239             if (is_array($forums[$i]->onlydiscussions)) {
1240                 // Show question posts as well as posts from discussions in
1241                 // which the user has posted a reply.
1242                 $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions);
1243                 $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)";
1244             } else {
1245                 // Show only the question posts.
1246                 $selectdiscussion .= ' AND (p.parent = 0)';
1247             }
1248         }
1249         if (!$forums[$i]->accessallgroups) {
1250             if (!empty($forums[$i]->accessgroup)) {
1251                 $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}";
1252                 $selectdiscussion .= ' OR d.groupid = -1)';  // -1 means open for all groups.
1253             } else {
1254                 // User isn't in any group. Only search discussions that are
1255                 // open to all groups.
1256                 $selectdiscussion .= ' AND d.groupid = -1';
1257             }
1258         }
1259         $selectdiscussion .= ")\n";
1260     }
1261     $selectdiscussion .= ")";
1264     // Some differences SQL
1265     $LIKE = sql_ilike();
1266     $NOTLIKE = 'NOT ' . $LIKE;
1267     if ($CFG->dbfamily == 'postgres') {
1268         $REGEXP = '~*';
1269         $NOTREGEXP = '!~*';
1270     } else {
1271         $REGEXP = 'REGEXP';
1272         $NOTREGEXP = 'NOT REGEXP';
1273     }
1275     $messagesearch = '';
1276     $searchstring = '';
1278     // Need to concat these back together for parser to work.
1279     foreach($searchterms as $searchterm){
1280         if ($searchstring != '') {
1281             $searchstring .= ' ';
1282         }
1283         $searchstring .= $searchterm;
1284     }
1286     // We need to allow quoted strings for the search. The quotes *should* be stripped
1287     // by the parser, but this should be examined carefully for security implications.
1288     $searchstring = str_replace("\\\"","\"",$searchstring);
1289     $parser = new search_parser();
1290     $lexer = new search_lexer($parser);
1292     if ($lexer->parse($searchstring)) {
1293         $parsearray = $parser->get_parsed_array();
1294         $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
1295                                              'p.userid', 'u.id', 'u.firstname',
1296                                              'u.lastname', 'p.modified', 'd.forum');
1297     }
1299     $fromsql = "{$CFG->prefix}forum_posts p,
1300                   {$CFG->prefix}forum_discussions d,
1301                   {$CFG->prefix}user u";
1303     $selectsql = " $messagesearch
1304                AND p.discussion = d.id
1305                AND p.userid = u.id
1306                AND $selectdiscussion
1307                    $extrasql";
1309     $countsql = "SELECT COUNT(*)
1310                    FROM $fromsql
1311                   WHERE $selectsql";
1313     $searchsql = "SELECT p.*,
1314                          d.forum,
1315                          u.firstname,
1316                          u.lastname,
1317                          u.email,
1318                          u.picture
1319                     FROM $fromsql
1320                    WHERE $selectsql
1321                 ORDER BY p.modified DESC";
1323     $totalcount = count_records_sql($countsql);
1325     return get_records_sql($searchsql, $limitfrom, $limitnum);
1328 function forum_get_ratings($postid, $sort="u.firstname ASC") {
1329 /// Returns a list of ratings for a particular post - sorted.
1330     global $CFG;
1331     return get_records_sql("SELECT u.*, r.rating, r.time
1332                               FROM {$CFG->prefix}forum_ratings r,
1333                                    {$CFG->prefix}user u
1334                              WHERE r.post = '$postid'
1335                                AND r.userid = u.id
1336                              ORDER BY $sort");
1339 function forum_get_unmailed_posts($starttime, $endtime) {
1340 /// Returns a list of all new posts that have not been mailed yet
1341     global $CFG;
1342     $now = time();
1343     return get_records_sql("SELECT p.*, d.course
1344                               FROM {$CFG->prefix}forum_posts p,
1345                                    {$CFG->prefix}forum_discussions d
1346                              WHERE p.mailed = 0
1347                                AND (p.created >= '$starttime' OR d.timestart > 0)
1348                                AND (p.created < '$endtime' OR p.mailnow = 1)
1349                                AND p.discussion = d.id
1350                                AND ((d.timestart = 0 OR d.timestart <= '$now')
1351                                AND (d.timeend = 0 OR d.timeend > '$now'))
1352                           ORDER BY p.modified ASC");
1355 function forum_mark_old_posts_as_mailed($endtime) {
1356 /// Marks posts before a certain time as being mailed already
1357     global $CFG;
1358 /// Find out posts those are not showing immediately so we can exclude them
1359     $now = time();
1360     $delayed_posts = get_records_sql("SELECT p.id, p.discussion
1361                                         FROM {$CFG->prefix}forum_posts p,
1362                                              {$CFG->prefix}forum_discussions d
1363                                        WHERE p.mailed = 0
1364                                          AND p.discussion = d.id
1365                                          AND d.timestart > '$now'");
1366     $delayed_ids = array();
1367     if ($delayed_posts) {
1368         foreach ($delayed_posts as $post) {
1369             $delayed_ids[] = $post->id;
1370         }
1371     } else {
1372         $delayed_ids[] = 0;
1373     }
1374     return execute_sql("UPDATE {$CFG->prefix}forum_posts
1375                            SET mailed = '1'
1376                          WHERE id NOT IN (".implode(',',$delayed_ids).")
1377                            AND (created < '$endtime' OR mailnow = 1)
1378                            AND mailed ='0'", false);
1381 function forum_get_user_posts($forumid, $userid) {
1382 /// Get all the posts for a user in a forum suitable for forum_print_post
1383     global $CFG;
1385     return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
1386                               FROM {$CFG->prefix}forum f,
1387                                    {$CFG->prefix}forum_discussions d,
1388                                    {$CFG->prefix}forum_posts p,
1389                                    {$CFG->prefix}user u
1390                              WHERE f.id = '$forumid'
1391                                AND d.forum = f.id
1392                                AND p.discussion = d.id
1393                                AND p.userid = '$userid'
1394                                AND p.userid = u.id
1395                           ORDER BY p.modified ASC");
1398 function forum_get_post_from_log($log) {
1399 /// Given a log entry, return the forum post details for it.
1400     global $CFG;
1402     if ($log->action == "add post") {
1404         return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1405                                            u.firstname, u.lastname, u.email, u.picture
1406                                  FROM {$CFG->prefix}forum_discussions d,
1407                                       {$CFG->prefix}forum_posts p,
1408                                       {$CFG->prefix}forum f,
1409                                       {$CFG->prefix}user u
1410                                 WHERE p.id = '$log->info'
1411                                   AND d.id = p.discussion
1412                                   AND p.userid = u.id
1413                                   AND u.deleted <> '1'
1414                                   AND f.id = d.forum");
1417     } else if ($log->action == "add discussion") {
1419         return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1420                                            u.firstname, u.lastname, u.email, u.picture
1421                                  FROM {$CFG->prefix}forum_discussions d,
1422                                       {$CFG->prefix}forum_posts p,
1423                                       {$CFG->prefix}forum f,
1424                                       {$CFG->prefix}user u
1425                                 WHERE d.id = '$log->info'
1426                                   AND d.firstpost = p.id
1427                                   AND p.userid = u.id
1428                                   AND u.deleted <> '1'
1429                                   AND f.id = d.forum");
1430     }
1431     return NULL;
1434 function forum_get_firstpost_from_discussion($discussionid) {
1435 /// Given a discussion id, return the first post from the discussion
1436     global $CFG;
1438     return get_record_sql("SELECT p.*
1439                              FROM {$CFG->prefix}forum_discussions d,
1440                                   {$CFG->prefix}forum_posts p
1441                             WHERE d.id = '$discussionid'
1442                               AND d.firstpost = p.id ");
1446 function forum_get_user_grades($forumid) {
1447 /// Get all user grades for a forum
1448     global $CFG;
1450     return get_records_sql("SELECT r.id, p.userid, r.rating
1451                               FROM {$CFG->prefix}forum_discussions d,
1452                                    {$CFG->prefix}forum_posts p,
1453                                    {$CFG->prefix}forum_ratings r
1454                              WHERE d.forum = '$forumid'
1455                                AND p.discussion = d.id
1456                                AND r.post = p.id
1457                              ORDER by p.userid ");
1461 function forum_count_discussion_replies($forum='0', $course='0', $user='0') {
1462 // Returns an array of counts of replies to each discussion (optionally in one forum or course and/or user)
1463     global $CFG;
1465     $forumselect = $courseselect = $userselect = '';
1467     if ($forum) {
1468         $forumselect = " AND d.forum = '$forum'";
1469     }
1470     if ($course) {
1471         $courseselect = " AND d.course = '$course'";
1472     }
1473     if ($user) {
1474         $userselect = " AND d.userid = '$user'";
1475     }
1476     return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid
1477                               FROM {$CFG->prefix}forum_posts p,
1478                                    {$CFG->prefix}forum_discussions d
1479                              WHERE p.parent > 0 $forumselect $courseselect $userselect
1480                                AND p.discussion = d.id
1481                           GROUP BY p.discussion");
1484 function forum_count_unrated_posts($discussionid, $userid) {
1485 // How many unrated posts are in the given discussion for a given user?
1486     global $CFG;
1487     if ($posts = get_record_sql("SELECT count(*) as num
1488                                    FROM {$CFG->prefix}forum_posts
1489                                   WHERE parent > 0
1490                                     AND discussion = '$discussionid'
1491                                     AND userid <> '$userid' ")) {
1493         if ($rated = get_record_sql("SELECT count(*) as num
1494                                        FROM {$CFG->prefix}forum_posts p,
1495                                             {$CFG->prefix}forum_ratings r
1496                                       WHERE p.discussion = '$discussionid'
1497                                         AND p.id = r.post
1498                                         AND r.userid = '$userid'")) {
1499             $difference = $posts->num - $rated->num;
1500             if ($difference > 0) {
1501                 return $difference;
1502             } else {
1503                 return 0;    // Just in case there was a counting error
1504             }
1505         } else {
1506             return $posts->num;
1507         }
1508     } else {
1509         return 0;
1510     }
1513 function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC",
1514                                $user=0, $fullpost=true, $visiblegroups=-1, $limit=0, $userlastmodified=false) {
1515 /// Get all discussions in a forum
1516     global $CFG, $USER;
1518     $timelimit = '';
1520     if (!empty($CFG->forum_enabletimedposts)) {
1522         if (!$cm = get_coursemodule_from_instance('forum', $forum)) {
1523             error('Course Module ID was incorrect');
1524         }
1525         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1527         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
1528             $now = time();
1529             $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
1530             if (!empty($USER->id)) {
1531                 $timelimit .= " OR d.userid = '$USER->id'";
1532             }
1533             $timelimit .= ')';
1534         }
1535     }
1537     if ($user) {
1538         $userselect = " AND u.id = '$user' ";
1539     } else {
1540         $userselect = "";
1541     }
1543     $limitfrom = 0;
1544     $limitnum = 0;
1545     if ($limit) {
1546         $limitnum = $limit;
1547     }
1549     if ($visiblegroups == -1) {
1550         $groupselect = "";
1551     } else  {
1552         $groupselect = " AND (d.groupid = '$visiblegroups' OR d.groupid = '-1') ";
1553     }
1555     if (empty($forumsort)) {
1556         $forumsort = "d.timemodified DESC";
1557     }
1558     if (empty($fullpost)) {
1559         $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
1560     } else {
1561         $postdata = "p.*";
1562     }
1564     if (empty($userlastmodified)) {  // We don't need to know this
1565         $umfields = '';
1566         $umtable = '';
1567     } else {
1568         $umfields = ', um.firstname AS umfirstname, um.lastname AS umlastname';
1569         $umtable = ' LEFT JOIN '.$CFG->prefix.'user um on (d.usermodified = um.id)';
1570     }
1572     //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.
1573     if ($CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'oracle') {
1574         return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1575                                    u.firstname, u.lastname, u.email, u.picture $umfields
1576                               FROM {$CFG->prefix}forum_discussions d
1577                               JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
1578                               JOIN {$CFG->prefix}user u ON p.userid = u.id
1579                                    $umtable
1580                              WHERE d.forum = '$forum'
1581                                AND p.parent = 0
1582                                    $timelimit $groupselect $userselect
1583                           ORDER BY $forumsort", $limitfrom, $limitnum);
1584     } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query)
1585         return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1586                                    u.firstname, u.lastname, u.email, u.picture $umfields
1587                               FROM ({$CFG->prefix}forum_posts p,
1588                                    {$CFG->prefix}user u,
1589                                    {$CFG->prefix}forum_discussions d)
1590                                    $umtable
1591                              WHERE d.forum = '$forum'
1592                                AND p.discussion = d.id
1593                                AND p.parent = 0
1594                                AND p.userid = u.id $timelimit $groupselect $userselect
1595                           ORDER BY $forumsort", $limitfrom, $limitnum);
1596     }
1601 function forum_get_user_discussions($courseid, $userid, $groupid=0) {
1602 /// Get all discussions started by a particular user in a course (or group)
1603 /// This function no longer used ...
1604     global $CFG;
1606     if ($groupid) {
1607         $groupselect = " AND d.groupid = '$groupid' ";
1608     } else  {
1609         $groupselect = "";
1610     }
1612     return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture,
1613                                    f.type as forumtype, f.name as forumname, f.id as forumid
1614                               FROM {$CFG->prefix}forum_discussions d,
1615                                    {$CFG->prefix}forum_posts p,
1616                                    {$CFG->prefix}user u,
1617                                    {$CFG->prefix}forum f
1618                              WHERE d.course = '$courseid'
1619                                AND p.discussion = d.id
1620                                AND p.parent = 0
1621                                AND p.userid = u.id
1622                                AND u.id = '$userid'
1623                                AND d.forum = f.id $groupselect
1624                           ORDER BY p.created DESC");
1627 function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) {
1628 /// Returns list of user objects that are subscribed to this forum
1629     global $CFG;
1631     static $resultscache = array();
1633     if ($cache && isset($resultscache[$forum->id][$groupid])) {
1634         return $resultscache[$forum->id][$groupid];
1635     }
1637     if ($groupid) {
1638         $grouptables = ', '. groups_members_from_sql();
1639         $groupselect = 'AND'.groups_members_where_sql($groupid, 'u.id');
1640     } else  {
1641         $grouptables = '';
1642         $groupselect = '';
1643     }
1645     if (forum_is_forcesubscribed($forum->id)) {
1646         $results = get_course_users($course->id);     // Otherwise get everyone in the course
1647     } else {
1648         $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop,
1649                                    u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums
1650                               FROM {$CFG->prefix}user u,
1651                                    {$CFG->prefix}forum_subscriptions s $grouptables
1652                              WHERE s.forum = '$forum->id'
1653                                AND s.userid = u.id
1654                                AND u.deleted <> 1  $groupselect
1655                           ORDER BY u.email ASC");
1656     }
1657         // Guest user should never be subscribed to a forum.
1658         if ($guest = guest_user()) {
1659                 unset($results[$guest->id]);
1660         }
1662     if ($cache) {
1663         $resultscache[$forum->id][$groupid] = $results;
1664     }
1666     return $results;
1671 /// OTHER FUNCTIONS ///////////////////////////////////////////////////////////
1674 function forum_get_course_forum($courseid, $type) {
1675 // How to set up special 1-per-course forums
1676     global $CFG;
1678     if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
1679         // There should always only be ONE, but with the right combination of
1680         // errors there might be more.  In this case, just return the oldest one (lowest ID).
1681         foreach ($forums as $forum) {
1682             return $forum;   // ie the first one
1683         }
1684     }
1686     // Doesn't exist, so create one now.
1687     $forum->course = $courseid;
1688     $forum->type = "$type";
1689     switch ($forum->type) {
1690         case "news":
1691             $forum->name  = addslashes(get_string("namenews", "forum"));
1692             $forum->intro = addslashes(get_string("intronews", "forum"));
1693             $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
1694             $forum->assessed = 0;
1695             if ($courseid == SITEID) {
1696                 $forum->name  = get_string("sitenews");
1697                 $forum->forcesubscribe = 0;
1698             }
1699             break;
1700         case "social":
1701             $forum->name  = addslashes(get_string("namesocial", "forum"));
1702             $forum->intro = addslashes(get_string("introsocial", "forum"));
1703             $forum->assessed = 0;
1704             $forum->forcesubscribe = 0;
1705             break;
1706         default:
1707             notify("That forum type doesn't exist!");
1708             return false;
1709             break;
1710     }
1712     $forum->timemodified = time();
1713     $forum->id = insert_record("forum", $forum);
1715     if (! $module = get_record("modules", "name", "forum")) {
1716         notify("Could not find forum module!!");
1717         return false;
1718     }
1719     $mod->course = $courseid;
1720     $mod->module = $module->id;
1721     $mod->instance = $forum->id;
1722     $mod->section = 0;
1723     if (! $mod->coursemodule = add_course_module($mod) ) {   // assumes course/lib.php is loaded
1724         notify("Could not add a new course module to the course '$course->fullname'");
1725         return false;
1726     }
1727     if (! $sectionid = add_mod_to_section($mod) ) {   // assumes course/lib.php is loaded
1728         notify("Could not add the new course module to that section");
1729         return false;
1730     }
1731     if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
1732         notify("Could not update the course module with the correct section");
1733         return false;
1734     }
1735     include_once("$CFG->dirroot/course/lib.php");
1736     rebuild_course_cache($courseid);
1738     return get_record("forum", "id", "$forum->id");
1742 function forum_make_mail_post(&$post, $user, $touser, $course,
1743                               $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
1745     // Given the data about a posting, builds up the HTML to display it and
1746     // returns the HTML in a string.  This is designed for sending via HTML email.
1748     global $CFG;
1750     static $formattedtext;        // Cached version of formatted text for a post
1751     static $formattedtextid;      // The ID number of the post
1753     $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
1755     if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) {
1756         mtrace('Course Module ID was incorrect');
1757     }
1758     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1761     if (empty($formattedtextid) or $formattedtextid != $post->id) {    // Recalculate the formatting
1762         $options = new Object;
1763         $options->para = true;
1764         $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);
1765         $formattedtextid = $post->id;
1766     }
1768     $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
1770     $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
1771     $output .= print_user_picture($user->id, $course->id, $user->picture, false, true);
1772     $output .= '</td>';
1774     if ($post->parent) {
1775         $output .= '<td class="topic">';
1776     } else {
1777         $output .= '<td class="topic starter">';
1778     }
1779     $output .= '<div class="subject">'.format_string($post->subject).'</div>';
1781     $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext, $touser->id));
1782     $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
1783     $by->date = userdate($post->modified, '', $touser->timezone);
1784     $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
1786     $output .= '</td></tr>';
1788     $output .= '<tr><td class="left side" valign="top">';
1789     if ($group = user_group($course->id, $user->id)) {
1790         $output .= print_group_picture($group, $course->id, false, true, true);
1791     } else {
1792         $output .= '&nbsp;';
1793     }
1795     $output .= '</td><td class="content">';
1797     if ($post->attachment) {
1798         $post->course = $course->id;
1799         $output .= '<div class="attachments">';
1800         $output .= forum_print_attachments($post, 'html');
1801         $output .= "</div>";
1802     }
1804     $output .= $formattedtext;
1806 /// Commands
1807     $commands = array();
1809     if ($post->parent) {
1810         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
1811                       $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
1812     }
1814     if ($reply) {
1815         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
1816                       get_string('reply', 'forum').'</a>';
1817     }
1819     $output .= '<div class="commands">';
1820     $output .= implode(' | ', $commands);
1821     $output .= '</div>';
1823 /// Context link to post if required
1824     if ($link) {
1825         $output .= '<div class="link">';
1826         $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
1827                      get_string('postincontext', 'forum').'</a>';
1828         $output .= '</div>';
1829     }
1831     if ($footer) {
1832         $output .= '<div class="footer">'.$footer.'</div>';
1833     }
1834     $output .= '</td></tr></table>'."\n\n";
1836     return $output;
1840 function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false,
1841                           $ratings=NULL, $footer="", $highlight="", $post_read=-99) {
1843     global $USER, $CFG, $SESSION;
1845     static $stredit, $strdelete, $strreply, $strparent, $strprune;
1846     static $strpruneheading, $threadedmode;
1847     static $strmarkread, $strmarkunread, $istracked;
1850     $discussion = get_record('forum_discussions', 'id', $post->discussion);
1851     if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum)) {
1852         error('Course Module ID was incorrect');
1853     }
1854     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1857     if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
1858         if (empty($SESSION->forum_search)) {
1859             // just viewing, return
1860             return;
1861         }
1862         echo '<a id="p'.$post->id.'"></a>';
1863         echo '<table cellspacing="0" class="forumpost">';
1864         echo '<tr class="header"><td class="picture left">';
1865         //        print_user_picture($post->userid, $courseid, $post->picture);
1866         echo '</td>';
1867         if ($post->parent) {
1868             echo '<td class="topic">';
1869         } else {
1870             echo '<td class="topic starter">';
1871         }
1872         echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
1873         echo '<div class="author">';
1874         print_string('forumauthorhidden','forum');
1875         echo '</div></td></tr>';
1877         echo '<tr><td class="left side">';
1878         echo '&nbsp;';
1880         /// Actual content
1882         echo '</td><td class="content">'."\n";
1883         echo get_string('forumbodyhidden','forum');
1884         echo '</td></tr></table>';
1885         return;
1886     }
1888     if (empty($stredit)) {
1889         $stredit = get_string('edit', 'forum');
1890         $strdelete = get_string('delete', 'forum');
1891         $strreply = get_string('reply', 'forum');
1892         $strparent = get_string('parent', 'forum');
1893         $strpruneheading = get_string('pruneheading', 'forum');
1894         $strprune = get_string('prune', 'forum');
1895         $threadedmode = (!empty($USER->mode) and ($USER->mode == FORUM_MODE_THREADED));
1896         $strmarkread = get_string('markread', 'forum');
1897         $strmarkunread = get_string('markunread', 'forum');
1899         if (!empty($post->forum)) {
1900             $istracked = (forum_tp_can_track_forums($post->forum) &&
1901                           forum_tp_is_tracked($post->forum));
1902         } else {
1903             $istracked = false;
1904         }
1905     }
1907     if ($istracked) {
1908         if ($post_read == -99) {    // If we don't know yet...
1909         /// The front page can display a news item post to non-logged in users. This should
1910         /// always appear as 'read'.
1911             $post_read = empty($USER) || forum_tp_is_post_read($USER->id, $post);
1912         }
1913         if ($post_read) {
1914             $read_style = ' read';
1915         } else {
1916             $read_style = ' unread';
1917             echo '<a name="unread"></a>';
1918         }
1919     } else {
1920         $read_style = '';
1921     }
1923     echo '<a id="p'.$post->id.'"></a>';
1924     echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
1926     echo '<tr class="header"><td class="picture left">';
1927     print_user_picture($post->userid, $courseid, $post->picture);
1928     echo '</td>';
1930     if ($post->parent) {
1931         echo '<td class="topic">';
1932     } else {
1933         echo '<td class="topic starter">';
1934     }
1936     echo '<div class="subject">'.format_string($post->subject).'</div>';
1938     echo '<div class="author">';
1939     $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
1940     $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
1941                 $post->userid.'&amp;course='.$courseid.'">'.$fullname.'</a>';
1942     $by->date = userdate($post->modified);
1943     print_string('bynameondate', 'forum', $by);
1944     echo '</div></td></tr>';
1946     echo '<tr><td class="left side">';
1947     if ($group = user_group($courseid, $post->userid)) {
1948         print_group_picture($group, $courseid, false, false, true);
1949     } else {
1950         echo '&nbsp;';
1951     }
1953 /// Actual content
1955     echo '</td><td class="content">'."\n";
1957     if ($post->attachment) {
1958         $post->course = $courseid;
1959         $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
1960         echo '<div class="attachments">';
1961         $attachedimages = forum_print_attachments($post);
1962         echo '</div>';
1963     } else {
1964         $attachedimages = '';
1965     }
1968     $options = new Object;
1969     $options->para = false;
1970     $options->trusttext = true;
1971     if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) {
1972         // Print shortened version
1973         echo format_text(forum_shorten_post($post->message), $post->format, $options, $courseid);
1974         $numwords = count_words(strip_tags($post->message));
1975         echo '<p><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
1976         echo get_string('readtherest', 'forum');
1977         echo '</a> ('.get_string('numwords', '', $numwords).')...</p>';
1978     } else {
1979         // Print whole message
1980         if ($highlight) {
1981             echo highlight($highlight, format_text($post->message, $post->format, $options, $courseid));
1982         } else {
1983             echo format_text($post->message, $post->format, $options, $courseid);
1984         }
1985         echo $attachedimages;
1986     }
1989 /// Commands
1991     $commands = array();
1993     if ($istracked) {
1994         /// SPECIAL CASE: The front page can display a news item post to non-logged in users.
1995         /// Don't display the mark read / unread controls in this case.
1996         if ($CFG->forum_usermarksread && !empty($USER)) {
1997             if ($post_read) {
1998                 $mcmd = '&amp;mark=unread&amp;postid='.$post->id;
1999                 $mtxt = $strmarkunread;
2000             } else {
2001                 $mcmd = '&amp;mark=read&amp;postid='.$post->id;
2002                 $mtxt = $strmarkread;
2003             }
2004             if ($threadedmode) {
2005                 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2006                               $post->discussion.'&amp;parent='.$post->id.$mcmd.'">'.$mtxt.'</a>';
2007             } else {
2008                 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2009                               $post->discussion.$mcmd.'#p'.$post->id.'">'.$mtxt.'</a>';
2010             }
2011         }
2012     }
2014     if ($post->parent) {
2015         if ($threadedmode) {
2016             $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2017                           $post->discussion.'&amp;parent='.$post->parent.'">'.$strparent.'</a>';
2018         } else {
2019             $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2020                           $post->discussion.'#p'.$post->parent.'">'.$strparent.'</a>';
2021         }
2022     }
2024     $forumtype = get_field('forum', 'type', 'id', $post->forum);
2026     $age = time() - $post->created;
2027     /// Hack for allow to edit news posts those are not displayed yet until they are displayed
2028     if (!$post->parent
2029         && $forumtype == 'news'
2030         && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) {
2031         $age = 0;
2032     }
2033     $editanypost = has_capability('mod/forum:editanypost', $modcontext);
2037     if ($ownpost or $editanypost) {
2038         if (($age < $CFG->maxeditingtime) or $editanypost) {
2039             $commands[] =  '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?edit='.$post->id.'">'.$stredit.'</a>';
2040         }
2041     }
2043     if (has_capability('mod/forum:splitdiscussions', $modcontext)
2044                 && $post->parent && $forumtype != 'single') {
2046         $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?prune='.$post->id.
2047                       '" title="'.$strpruneheading.'">'.$strprune.'</a>';
2048     }
2050     if (($ownpost and $age < $CFG->maxeditingtime
2051                 and has_capability('mod/forum:deleteownpost', $modcontext))
2052                 or has_capability('mod/forum:deleteanypost', $modcontext)) {
2053         $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?delete='.$post->id.'">'.$strdelete.'</a>';
2054     }
2056     if ($reply) {
2057         $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.$strreply.'</a>';
2058     }
2060     echo '<div class="commands">';
2061     echo implode(' | ', $commands);
2062     echo '</div>';
2065 /// Ratings
2067     $ratingsmenuused = false;
2068     if (!empty($ratings) and !empty($USER->id)) {
2069         echo '<div class="ratings">';
2070         $useratings = true;
2071         if ($ratings->assesstimestart and $ratings->assesstimefinish) {
2072             if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) {
2073                 $useratings = false;
2074             }
2075         }
2076         if ($useratings) {
2077             $mypost = ($USER->id == $post->userid);
2079             $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext);
2081             if ($canviewallratings and !$mypost) {
2082                 forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings);
2083                 if (!empty($ratings->allow)) {
2084                     echo '&nbsp;';
2085                     forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2086                     $ratingsmenuused = true;
2087                 }
2089             } else if ($mypost) {
2090                 forum_print_ratings_mean($post->id, $ratings->scale, true);
2092             } else if (!empty($ratings->allow) ) {
2093                 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2094                 $ratingsmenuused = true;
2095             }
2096         }
2097         echo '</div>';
2098     }
2100 /// Link to post if required
2102     if ($link) {
2103         echo '<div class="link">';
2104         if ($post->replies == 1) {
2105             $replystring = get_string('repliesone', 'forum', $post->replies);
2106         } else {
2107             $replystring = get_string('repliesmany', 'forum', $post->replies);
2108         }
2109         echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.
2110              get_string('discussthistopic', 'forum').'</a>&nbsp;('.$replystring.')';
2111         echo '</div>';
2112     }
2114     if ($footer) {
2115         echo '<div class="footer">'.$footer.'</div>';
2116     }
2117     echo '</td></tr></table>'."\n\n";
2119     if ($istracked && !$CFG->forum_usermarksread && !empty($post->forum)) {
2120         forum_tp_mark_post_read($USER->id, $post, $post->forum);
2121     }
2123     return $ratingsmenuused;
2127 /**
2128 * This function prints the overview of a discussion in the forum listing.
2129 * It needs some discussion information and some post information, these
2130 * happen to be combined for efficiency in the $post parameter by the function
2131 * that calls this one: forum_print_latest_discussions()
2133 * @param object $post The post object (passed by reference for speed).
2134 * @param object $forum The forum object.
2135 * @param int $group Current group.
2136 * @param string $datestring Format to use for the dates.
2137 * @param boolean $cantrack Is tracking enabled for this forum.
2138 * @param boolean $forumtracked Is the user tracking this forum.
2139 */
2140 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
2141                                         $cantrack=true, $forumtracked=true) {
2143     global $USER, $CFG;
2145     static $rowcount;
2146     static $strmarkalldread;
2149     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2150         error('Course Module ID was incorrect');
2151     }
2152     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2155     if (!isset($rowcount)) {
2156         $rowcount = 0;
2157         $strmarkalldread = get_string('markalldread', 'forum');
2158     } else {
2159         $rowcount = ($rowcount + 1) % 2;
2160     }
2162     $post->subject = format_string($post->subject,true);
2164     echo "\n\n";
2165     echo '<tr class="discussion r'.$rowcount.'">';
2167     // Topic
2168     echo '<td class="topic starter">';
2169     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
2170     echo "</td>\n";
2172     // Picture
2173     echo '<td class="picture">';
2174     print_user_picture($post->userid, $forum->course, $post->picture);
2175     echo "</td>\n";
2177     // User name
2178     $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
2179     echo '<td class="author">';
2180     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
2181     echo "</td>\n";
2183     // Group picture
2184     if ($group !== -1) {  // Groups are active - group is a group data object or NULL
2185         echo '<td class="picture group">';
2186         if (!empty($group->picture) and empty($group->hidepicture)) {
2187             print_group_picture($group, $forum->course, false, false, true);
2188         } else if (isset($group->id)) {
2189             echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
2190         }
2191         echo "</td>\n";
2192     }
2194     if (has_capability('mod/forum:viewdiscussion', $modcontext)) {   // Show the column with replies
2195         echo '<td class="replies">';
2196         echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2197         echo $post->replies.'</a>';
2198         echo "</td>\n";
2200         if ($cantrack) {
2201             echo '<td class="replies">';
2202             if ($forumtracked) {
2203                 if ($post->unread > 0) {
2204                     echo '<span class="unread">';
2205                     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
2206                     echo $post->unread;
2207                     echo '</a>';
2208                     echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
2209                          $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
2210                          '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
2211                     echo '</span>';
2212                 } else {
2213                     echo '<span class="read">';
2214                     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
2215                     echo $post->unread;
2216                     echo '</a>';
2217                     echo '</span>';
2218                 }
2219             } else {
2220                 echo '<span class="read">';
2221                 echo '-';
2222                 echo '</span>';
2223             }
2224             echo "</td>\n";
2225         }
2226     }
2228     echo '<td class="lastpost">';
2229     $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified;  // Just in case
2230     $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
2231     $usermodified->id        = $post->usermodified;
2232     $usermodified->firstname = $post->umfirstname;
2233     $usermodified->lastname  = $post->umlastname;
2234     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
2235          fullname($usermodified).'</a><br />';
2236     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
2237           userdate($usedate, $datestring).'</a>';
2238     echo "</td>\n";
2240     echo "</tr>\n\n";
2245 function forum_shorten_post($message) {
2246 // Given a post object that we already know has a long message
2247 // this function truncates the message nicely to the first
2248 // sane place between $CFG->forum_longpost and $CFG->forum_shortpost
2250    global $CFG;
2252    $i = 0;
2253    $tag = false;
2254    $length = strlen($message);
2255    $count = 0;
2256    $stopzone = false;
2257    $truncate = 0;
2259    for ($i=0; $i<$length; $i++) {
2260        $char = $message[$i];
2262        switch ($char) {
2263            case "<":
2264                $tag = true;
2265                break;
2266            case ">":
2267                $tag = false;
2268                break;
2269            default:
2270                if (!$tag) {
2271                    if ($stopzone) {
2272                        if ($char == ".") {
2273                            $truncate = $i+1;
2274                            break 2;
2275                        }
2276                    }
2277                    $count++;
2278                }
2279                break;
2280        }
2281        if (!$stopzone) {
2282            if ($count > $CFG->forum_shortpost) {
2283                $stopzone = true;
2284            }
2285        }
2286    }
2288    if (!$truncate) {
2289        $truncate = $i;
2290    }
2292    return substr($message, 0, $truncate);
2296 function forum_print_ratings_mean($postid, $scale, $link=true) {
2297 /// Print the multiple ratings on a post given to the current user by others.
2298 /// Scale is an array of ratings
2300     static $strrate;
2302     $mean = forum_get_ratings_mean($postid, $scale);
2304     if ($mean !== "") {
2306         if (empty($strratings)) {
2307             $strratings = get_string("ratings", "forum");
2308         }
2310         echo "$strratings: ";
2311         if ($link) {
2312             link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600);
2313         } else {
2314             echo "$mean ";
2315         }
2316     }
2320 function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
2321 /// Return the mean rating of a post given to the current user by others.
2322 /// Scale is an array of possible ratings in the scale
2323 /// Ratings is an optional simple array of actual ratings (just integers)
2325     if (!$ratings) {
2326         $ratings = array();
2327         if ($rates = get_records("forum_ratings", "post", $postid)) {
2328             foreach ($rates as $rate) {
2329                 $ratings[] = $rate->rating;
2330             }
2331         }
2332     }
2334     $count = count($ratings);
2336     if ($count == 0) {
2337         return "";
2339     } else if ($count == 1) {
2340         return $scale[$ratings[0]];
2342     } else {
2343         $total = 0;
2344         foreach ($ratings as $rating) {
2345             $total += $rating;
2346         }
2347         $mean = round( ((float)$total/(float)$count) + 0.001);  // Little fudge factor so that 0.5 goes UP
2349         if (isset($scale[$mean])) {
2350             return $scale[$mean]." ($count)";
2351         } else {
2352             return "$mean ($count)";    // Should never happen, hopefully
2353         }
2354     }
2357 function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
2358 /// Return a summary of post ratings given to the current user by others.
2359 /// Scale is an array of possible ratings in the scale
2360 /// Ratings is an optional simple array of actual ratings (just integers)
2362     if (!$ratings) {
2363         $ratings = array();
2364         if ($rates = get_records("forum_ratings", "post", $postid)) {
2365             foreach ($rates as $rate) {
2366                 $rating[] = $rate->rating;
2367             }
2368         }
2369     }
2372     if (!$count = count($ratings)) {
2373         return "";
2374     }
2377     foreach ($scale as $key => $scaleitem) {
2378         $sumrating[$key] = 0;
2379     }
2381     foreach ($ratings as $rating) {
2382         $sumrating[$rating]++;
2383     }
2385     $summary = "";
2386     foreach ($scale as $key => $scaleitem) {
2387         $summary = $sumrating[$key].$summary;
2388         if ($key > 1) {
2389             $summary = "/$summary";
2390         }
2391     }
2392     return $summary;
2395 function forum_print_rating_menu($postid, $userid, $scale) {
2396 /// Print the menu of ratings as part of a larger form.
2397 /// If the post has already been - set that value.
2398 /// Scale is an array of ratings
2400     static $strrate;
2402     if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
2403         $rating->rating = FORUM_UNSET_POST_RATING;
2404     }
2406     if (empty($strrate)) {
2407         $strrate = get_string("rate", "forum");
2408     }
2409     $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale;
2410     choose_from_menu($scale, $postid, $rating->rating, '');
2413 /**
2414  * Print the drop down that allows the user to select how they want to have
2415  * the discussion displayed.
2416  * @param $id - forum id if $forumtype is 'single',
2417  *              discussion id for any other forum type
2418  * @param $mode - forum layout mode
2419  * @param $forumtype - optional
2420  */
2421 function forum_print_mode_form($id, $mode, $forumtype='') {
2422     global $FORUM_LAYOUT_MODES;
2424     if ($forumtype == 'single') {
2425         popup_form("view.php?f=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2426     } else {
2427         popup_form("discuss.php?d=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2428     }
2431 function forum_search_form($course, $search='') {
2432     global $CFG;
2434     $output  = '<div class="forumsearch">';
2435     $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
2436     $output .= '<fieldset class="invisiblefieldset">';
2437     $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
2438     $output .= '<input name="search" type="text" size="18" value="'.$search.'" alt="search" />';
2439     $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
2440     $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
2441     $output .= '</fieldset>';
2442     $output .= '</form>';
2443     $output .= '</div>';
2445     return $output;
2449 function forum_set_return() {
2450     global $CFG, $SESSION;
2452     if (! isset($SESSION->fromdiscussion)) {
2453         if (!empty($_SERVER['HTTP_REFERER'])) {
2454             $referer = $_SERVER['HTTP_REFERER'];
2455         } else {
2456             $referer = "";
2457         }
2458         // If the referer is NOT a login screen then save it.
2459         if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
2460             $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
2461         }
2462     }
2466 function forum_go_back_to($default) {
2467     global $SESSION;
2469     if (!empty($SESSION->fromdiscussion)) {
2470         $returnto = $SESSION->fromdiscussion;
2471         unset($SESSION->fromdiscussion);
2472         return $returnto;
2473     } else {
2474         return $default;
2475     }
2478 function forum_file_area_name($post) {
2479 //  Creates a directory file name, suitable for make_upload_directory()
2480     global $CFG;
2482     return "$post->course/$CFG->moddata/forum/$post->forum/$post->id";
2485 function forum_file_area($post) {
2486     return make_upload_directory( forum_file_area_name($post) );
2489 function forum_delete_old_attachments($post, $exception="") {
2490 // Deletes all the user files in the attachments area for a post
2491 // EXCEPT for any file named $exception
2493     if ($basedir = forum_file_area($post)) {
2494         if ($files = get_directory_list($basedir)) {
2495             foreach ($files as $file) {
2496                 if ($file != $exception) {
2497                     unlink("$basedir/$file");
2498                     notify("Existing file '$file' has been deleted!");
2499                 }
2500             }
2501         }
2502         if (!$exception) {  // Delete directory as well, if empty
2503             rmdir("$basedir");
2504         }
2505     }
2508 function forum_move_attachments($discussion, $forumid) {
2509 /// Given a discussion object that is being moved to forumid,
2510 /// this function checks all posts in that discussion
2511 /// for attachments, and if any are found, these are
2512 /// moved to the new forum directory.
2514     global $CFG;
2516     require_once($CFG->dirroot.'/lib/uploadlib.php');
2518     $return = true;
2520     if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
2521         foreach ($posts as $oldpost) {
2522             $oldpost->course = $discussion->course;
2523             $oldpost->forum = $discussion->forum;
2524             $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
2525             if (is_dir($oldpostdir)) {
2526                 $newpost = $oldpost;
2527                 $newpost->forum = $forumid;
2528                 $newpostdir = forum_file_area_name($newpost);
2529                 // take off the last directory because otherwise we're renaming to a directory that already exists
2530                 // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
2531                 make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
2532                 $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost);
2533                 $files = get_directory_list($oldpostdir); // get it before we rename it.
2534                 if (! @rename($oldpostdir, $newpostdir)) {
2535                     $return = false;
2536                 }
2537                 foreach ($files as $file) {
2538                     clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
2539                 }
2540             }
2541         }
2542     }
2543     return $return;
2546 function forum_print_attachments($post, $return=NULL) {
2547 // if return=html, then return a html string.
2548 // if return=text, then return a text-only string.
2549 // otherwise, print HTML for non-images, and return image HTML
2551     global $CFG;
2553     $filearea = forum_file_area_name($post);
2555     $imagereturn = "";
2556     $output = "";
2558     if ($basedir = forum_file_area($post)) {
2559         if ($files = get_directory_list($basedir)) {
2560             $strattachment = get_string("attachment", "forum");
2561             foreach ($files as $file) {
2562                 $icon = mimeinfo("icon", $file);
2563                 $type = mimeinfo("type", $file);
2564                 if ($CFG->slasharguments) {
2565                     $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
2566                 } else {
2567                     $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
2568                 }
2569                 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
2571                 if ($return == "html") {
2572                     $output .= "<a href=\"$ffurl\">$image</a> ";
2573                     $output .= "<a href=\"$ffurl\">$file</a><br />";
2575                 } else if ($return == "text") {
2576                     $output .= "$strattachment $file:\n$ffurl\n";
2578                 } else {
2579                     if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
2580                         $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
2581                     } else {
2582                         echo "<a href=\"$ffurl\">$image</a> ";
2583                         echo filter_text("<a href=\"$ffurl\">$file</a><br />");
2584                     }
2585                 }
2586             }
2587         }
2588     }
2590     if ($return) {
2591         return $output;
2592     }
2594     return $imagereturn;
2596 /**
2597  * If successful, this function returns the name of the file
2598  * @param $post is a full post record, including course and forum
2599  * @param $newfile is a full upload array from $_FILES
2600  * @param $message is a string to hold the messages.
2601  */
2603 function forum_add_attachment($post, $inputname,&$message) {
2605     global $CFG;
2607     if (!$forum = get_record("forum", "id", $post->forum)) {
2608         return "";
2609     }
2611     if (!$course = get_record("course", "id", $forum->course)) {
2612         return "";
2613     }
2615     require_once($CFG->dirroot.'/lib/uploadlib.php');
2616     $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true);
2617     $dir = forum_file_area_name($post);
2618     if ($um->process_file_uploads($dir)) {
2619         $message .= $um->get_errors();
2620         return $um->get_new_filename();
2621     }
2622     $message .= $um->get_errors();
2623     return null;
2626 function forum_add_new_post($post,&$message) {
2628     global $USER, $CFG;
2630     $post->created = $post->modified = time();
2631     $post->mailed = "0";
2632     $post->userid = $USER->id;
2633     $post->attachment = "";
2635     if (! $post->id = insert_record("forum_posts", $post)) {
2636         return false;
2637     }
2639     if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
2640         set_field("forum_posts", "attachment", $post->attachment, "id", $post->id);
2641     }
2643     // Update discussion modified date
2644     set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2645     set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2647     if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2648         forum_tp_mark_post_read($post->userid, $post, $post->forum);
2649     }
2651     return $post->id;
2654 function forum_update_post($post,&$message) {
2656     global $USER, $CFG;
2658     $post->modified = time();
2660     if (!$post->parent) {   // Post is a discussion starter - update discussion title too
2661         set_field("forum_discussions", "name", $post->subject, "id", $post->discussion);
2662     }
2664     if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
2665         $post->attachment = $newfilename;
2666     } else {
2667         unset($post->attachment);
2668     }
2670     // Update discussion modified date
2671     set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
2672     set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
2674     if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2675         forum_tp_mark_post_read($post->userid, $post, $post->forum);
2676     }
2678     return update_record("forum_posts", $post);
2681 function forum_add_discussion($discussion,&$message) {
2682 // Given an object containing all the necessary data,
2683 // create a new discussion and return the id
2685     GLOBAL $USER, $CFG;
2687     $timenow = time();
2689     // The first post is stored as a real post, and linked
2690     // to from the discuss entry.
2692     $post->discussion  = 0;
2693     $post->parent      = 0;
2694     $post->userid      = $USER->id;
2695     $post->created     = $timenow;
2696     $post->modified    = $timenow;
2697     $post->mailed      = 0;
2698     $post->subject     = $discussion->name;
2699     $post->message     = $discussion->intro;
2700     $post->attachment  = "";
2701     $post->forum       = $discussion->forum;
2702     $post->course      = $discussion->course;
2703     $post->format      = $discussion->format;
2704     $post->mailnow     = $discussion->mailnow;
2706     if (! $post->id = insert_record("forum_posts", $post) ) {
2707         return 0;
2708     }
2710     if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
2711         set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors
2712     }
2714     // Now do the main entry for the discussion,
2715     // linking to this first post
2717     $discussion->firstpost    = $post->id;
2718     $discussion->timemodified = $timenow;
2719     $discussion->usermodified = $post->userid;
2720     $discussion->userid = $USER->id;
2722     if (! $post->discussion = insert_record("forum_discussions", $discussion) ) {
2723         delete_records("forum_posts", "id", $post->id);
2724         return 0;
2725     }
2727     // Finally, set the pointer on the post.
2728     if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) {
2729         delete_records("forum_posts", "id", $post->id);
2730         delete_records("forum_discussions", "id", $post->discussion);
2731         return 0;
2732     }
2734     if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
2735         forum_tp_mark_post_read($post->userid, $post, $post->forum);
2736     }
2738     return $post->discussion;
2742 function forum_delete_discussion($discussion, $fulldelete=false) {
2743 // $discussion is a discussion record object
2745     $result = true;
2747     if ($posts = get_records("forum_posts", "discussion", $discussion->id)) {
2748         foreach ($posts as $post) {
2749             $post->course = $discussion->course;
2750             $post->forum  = $discussion->forum;
2751             if (! delete_records("forum_ratings", "post", "$post->id")) {
2752                 $result = false;
2753             }
2754             if (! forum_delete_post($post, $fulldelete)) {
2755                 $result = false;
2756             }
2757         }
2758     }
2760     forum_tp_delete_read_records(-1, -1, $discussion->id);
2762     if (! delete_records("forum_discussions", "id", "$discussion->id")) {
2763         $result = false;
2764     }
2766     return $result;
2770 function forum_delete_post($post, $children=false) {
2771    if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
2772        if ($children) {
2773            foreach ($childposts as $childpost) {
2774                forum_delete_post($childpost, true);
2775            }
2776        } else {
2777            return false;
2778        }
2779    }
2780    if (delete_records("forum_posts", "id", $post->id)) {
2781        delete_records("forum_ratings", "post", $post->id);  // Just in case
2783        forum_tp_delete_read_records(-1, $post->id);
2785        if ($post->attachment) {
2786            $discussion = get_record("forum_discussions", "id", $post->discussion);
2787            $post->course = $discussion->course;
2788            $post->forum  = $discussion->forum;
2789            forum_delete_old_attachments($post);
2790        }
2792    /// Just in case we are deleting the last post
2793        forum_discussion_update_last_post($post->discussion);
2795        return true;
2796    }
2797    return false;
2800 function forum_count_replies($post, $children=true) {
2801     $count = 0;
2803     if ($children) {
2804         if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
2805            foreach ($childposts as $childpost) {
2806                $count ++;                   // For this child
2807                $count += forum_count_replies($childpost, true);
2808            }
2809         }
2810     } else {
2811         $count += count_records('forum_posts', 'parent', $post->id);
2812     }
2814     return $count;
2818 function forum_forcesubscribe($forumid, $value=1) {
2819     return set_field("forum", "forcesubscribe", $value, "id", $forumid);
2822 function forum_is_forcesubscribed($forumid) {
2823     return (get_field("forum", "forcesubscribe", "id", $forumid) == 1);
2826 function forum_is_subscribed($userid, $forumid) {
2827     if (forum_is_forcesubscribed($forumid)) {
2828         return true;
2829     }
2830     return record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid);
2833 function forum_subscribe($userid, $forumid) {
2834 /// Adds user to the subscriber list
2836     if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) {
2837         return true;
2838     }
2840     $sub->userid  = $userid;
2841     $sub->forum = $forumid;
2843     return insert_record("forum_subscriptions", $sub);
2846 function forum_unsubscribe($userid, $forumid) {
2847 /// Removes user from the subscriber list
2848     return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid);
2851 function forum_post_subscription($post) {
2852 /// Given a new post, subscribes or unsubscribes as appropriate.
2853 /// Returns some text which describes what happened.
2855     global $USER;
2857     $subscribed=forum_is_subscribed($USER->id, $post->forum);
2858     if ((isset($post->subscribe) && $post->subscribe && $subscribed)
2859         || (!$post->subscribe && !$subscribed)) {
2860         return "";
2861     }
2863     if (!$forum = get_record("forum", "id", $post->forum)) {
2864         return "";
2865     }
2867     $info->name  = fullname($USER);
2868     $info->forum = $forum->name;
2870     if (!empty($post->subscribe)) {
2871         forum_subscribe($USER->id, $post->forum);
2872         return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
2873     }
2875     forum_unsubscribe($USER->id, $post->forum);
2876     return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
2880 function forum_user_has_posted_discussion($forumid, $userid) {
2881     if ($discussions = forum_get_discussions($forumid, '', $userid)) {
2882         return true;
2883     } else {
2884         return false;
2885     }
2888 function forum_discussions_user_has_posted_in($forumid, $userid) {
2889     global $CFG;
2891     $haspostedsql = "SELECT d.id AS id,
2892                             d.*
2893                        FROM {$CFG->prefix}forum_posts p,
2894                             {$CFG->prefix}forum_discussions d
2895                       WHERE p.discussion = d.id
2896                         AND d.forum = $forumid
2897                         AND p.userid = $userid";
2899     return get_records_sql($haspostedsql);
2902 function forum_user_has_posted($forumid, $did, $userid) {
2903     return record_exists('forum_posts','discussion',$did,'userid',$userid);
2906 function forum_user_can_post_discussion($forum, $currentgroup=false, $groupmode='') {
2907 // $forum is an object
2908     global $USER, $SESSION;
2910     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2911         error('Course Module ID was incorrect');
2912     }
2913     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
2915     if (!has_capability('mod/forum:startdiscussion', $context)) {
2916         return false;
2917     }
2919     if ($forum->type == "eachuser") {
2920         return (!forum_user_has_posted_discussion($forum->id, $USER->id));
2921     } else if ($currentgroup) {
2922         return (has_capability('moodle/site:accessallgroups', $context)
2923                 or ismember($currentgroup));
2924     } else {
2925         //else it might be group 0 in visible mode
2926         if ($groupmode == VISIBLEGROUPS){
2927             return (ismember($currentgroup));
2928         }
2929         else {
2930             return true;
2931         }
2932     }
2935 /**
2936  * This function checks whether the user can reply to posts in a forum
2937  * discussion. Use forum_user_can_post_discussion() to check whether the user
2938  * can start dicussions.
2939  * @param $forum - forum object
2940  * @param $user - user object
2941  */
2942 function forum_user_can_post($forum, $user=NULL) {
2944     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2945         error('Course Module ID was incorrect');
2946     }
2947     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
2949     if (isset($user)) {
2950         $canreply = has_capability('mod/forum:replypost', $context, $user->id, false)
2951                 && !has_capability('moodle/legacy:guest', $context, $user->id, false);
2952     } else {
2953         $canreply = has_capability('mod/forum:replypost', $context, NULL, false)
2954                 && !has_capability('moodle/legacy:guest', $context, NULL, false);
2955     }
2957     return $canreply;
2961 //checks to see if a user can view a particular post
2962 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
2964     global $CFG, $USER;
2966     if (!$user){
2967         $user = $USER;
2968     }
2970     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2971     if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
2972         return false;
2973     }
2975 /// If it's a grouped discussion, make sure the user is a member
2976     if ($discussion->groupid > 0) {
2977         if ($cm->groupmode == SEPARATEGROUPS) {
2978             return ismember($discussion->groupid) ||
2979                     has_capability('moodle/site:accessallgroups', $modcontext);
2980         }
2981     }
2982     return true;
2986 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
2987     global $USER;
2989     if (empty($user) || empty($user->id)) {
2990         $user = $USER;
2991     }
2993     // retrieve objects (yuk)
2994     if (is_numeric($forum)) {
2995         if (!$forum = get_record('forum','id',$forum)) {
2996             return false;
2997         }
2998     }
2999     if (is_numeric($discussion)) {
3000         if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3001             return false;
3002         }
3003     }
3005     if (!has_capability('mod/forum:viewdiscussion', $context)) {
3006         return false;
3007     }
3009     if ($forum->type == 'qanda' &&
3010             !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
3011             !has_capability('mod/forum:viewqandawithoutposting', $context)) {
3012         return false;
3013     }
3014     return true;
3018 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL) {
3019     global $USER;
3021     // retrieve objects (yuk)
3022     if (is_numeric($forum)) {
3023         if (!$forum = get_record('forum','id',$forum)) {
3024             return false;
3025         }
3026     }
3028     if (is_numeric($discussion)) {
3029         if (!$discussion = get_record('forum_discussions','id',$discussion)) {
3030             return false;
3031         }
3032     }
3033     if (is_numeric($post)) {
3034         if (!$post = get_record('forum_posts','id',$post)) {
3035             return false;
3036         }
3037     }
3038     if (!isset($post->id) && isset($post->parent)) {
3039         $post->id = $post->parent;
3040     }
3042     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3043         error('Course Module ID was incorrect');
3044     }
3045     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3047     if (empty($user) || empty($user->id)) {
3048         $user = $USER;
3049     }
3051     if (!has_capability('mod/forum:viewdiscussion', $context, $user->id, false)) {
3052         return false;
3053     }
3055     if ($forum->type == 'qanda') {
3056         $firstpost = forum_get_firstpost_from_discussion($discussion->id);
3058         return (forum_user_has_posted($forum->id,$discussion->id,$user->id) ||
3059                 $firstpost->id == $post->id ||
3060                 has_capability('mod/forum:viewqandawithoutposting', $context, false, $user->id));
3061     }
3062     return true;
3066 /**
3067 * Prints the discussion view screen for a forum.
3069 * @param object $course The current course object.
3070 * @param object $forum Forum to be printed.
3071 * @param int $maxdiscussions The maximum number of discussions per page(optional).
3072 * @param string $displayformat The display format to use (optional).
3073 * @param string $sort Sort arguments for database query (optional).
3074 * @param int $currentgroup Group to display discussions for (optional).
3075 * @param int $groupmode Group mode of the forum (optional).
3076 * @param int $page Page mode, page to display (optional).
3078 */
3079 function forum_print_latest_discussions($course, $forum, $maxdiscussions=5, $displayformat='plain', $sort='',
3080                                         $currentgroup=-1, $groupmode=-1, $page=-1) {
3081     global $CFG, $USER;
3083     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
3084         error('Course Module ID was incorrect');
3085     }
3086     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
3089 /// Sort out some defaults
3091     if ((!$maxdiscussions) && ($displayformat == 'plain')) {
3092         $displayformat = 'header';  // Abbreviate display by default
3093     }
3095     $fullpost = false;
3096     if ($displayformat == 'plain') {
3097         $fullpost = true;
3098     }
3101 /// Decide if current user is allowed to see ALL the current discussions or not
3103 /// First check the group stuff
3105     if ($groupmode == -1) {    /// We need to reconstruct groupmode because none was given
3106         if ($cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
3107             $groupmode = groupmode($course, $cm);
3108         } else {
3109             $groupmode = SEPARATEGROUPS;
3110         }
3111     }
3113     if ($currentgroup == -1) {    /// We need to reconstruct currentgroup because none was given
3114         $currentgroup = get_current_group($course->id);
3115     }
3117     if (!$currentgroup and ($groupmode != SEPARATEGROUPS or
3118                 has_capability('moodle/site:accessallgroups', $context)) ) {
3119         $visiblegroups = -1;
3120     } else {
3121         $visiblegroups = $currentgroup;
3122     }
3124 /// If the user can post discussions, then this is a good place to put the
3125 /// button for it. We do not show the button if we are showing site news
3126 /// and the current user is a guest.
3128     // TODO: Add group mode in there, to test for visible group.
3129     if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode)
3130             || (has_capability('moodle/legacy:guest', $context, NULL, false)
3131             && $course->id != SITEID)) {
3133         echo '<div class="singlebutton forumaddnew">';
3134         echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
3135         echo '<fieldset class="invisiblefieldset">';
3136         echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
3137         echo '<input type="submit" value="';
3138         echo ($forum->type == 'news') ? get_string('addanewtopic', 'forum')
3139             : (($forum->type == 'qanda')
3140                ? get_string('addanewquestion','forum')
3141                : get_string('addanewdiscussion', 'forum'));
3142         echo '" />';
3143         echo '</fieldset>';
3144         echo '</form>';
3145         echo "</div>\n";
3146     }
3149 /// Get all the recent discussions we're allowed to see
3151     $getuserlastmodified = ($displayformat == 'header');
3153     if (! $discussions = forum_get_discussions($forum->id, $sort, 0, $fullpost, $visiblegroups,0,$getuserlastmodified) ) {
3154         echo '<div class="forumnodiscuss">';
3155         if ($forum->type == 'news') {
3156             echo '('.get_string('nonews', 'forum').')';
3157         } else if ($forum->type == 'qanda') {
3158             echo '('.get_string('noquestions','forum').')';
3159         } else {
3160             echo '('.get_string('nodiscussions', 'forum').')';
3161         }
3162         echo "</div>\n";
3163         return;
3164     }
3166 /// If no discussions then don't use paging (to avoid some divide by 0 errors)
3168     if ($maxdiscussions <= 0) {
3169         $page = -1;
3170         $maxdiscussions = 0;
3171     }
3173 /// If we want paging
3175     if ($page != -1) {
3176         ///Get the number of discussions found
3177         $numdiscussions = count($discussions);
3179         ///Show the paging bar
3180         print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3182         //Calculate the page "window"
3183         $pagestart = ($page * $maxdiscussions) + 1;
3184         $pageend  = $pagestart + $maxdiscussions - 1;
3185     }
3188     $replies = forum_count_discussion_replies($forum->id);
3190     $canreply = forum_user_can_post($forum);
3193     $discussioncount = 0;
3194     $olddiscussionlink = false;
3195     $strdatestring = get_string('strftimerecentfull');
3197     /// Check if the forum is tracked.
3198     if ($cantrack = forum_tp_can_track_forums($forum)) {
3199         $forumtracked = forum_tp_is_tracked($forum);
3200     } else {
3201         $forumtracked = false;
3202     }
3204     if ($displayformat == 'header') {
3205         echo '<table cellspacing="0" class="forumheaderlist">';
3206         echo '<thead>';
3207         echo '<tr>';
3208         echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
3209         echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
3210         if ($groupmode > 0) {
3211             echo '<th class="header group" scope="col">'.get_string('group').'</th>';
3212         }
3213         if (has_capability('mod/forum:viewdiscussion', $context)) {
3214             echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
3215             /// If the forum can be tracked, display the unread column.
3216             if ($cantrack) {
3217                 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
3218                 if ($forumtracked) {
3219                     echo '&nbsp;<a title="'.get_string('markallread', 'forum').
3220                          '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
3221                          $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
3222                          '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
3223                 }
3224                 echo '</th>';
3225             }
3226         }
3227         echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
3228         echo '</tr>';
3229         echo '</thead>';
3230         echo '<tbody>';
3231     }
3233     foreach ($discussions as $discussion) {
3234         $discussioncount++;
3236         if ($page != -1) {     // We are using paging
3237             if ($discussioncount < $pagestart) {  // Not there yet
3238                 continue;
3239             }
3240             if ($discussioncount > $pageend) {    // All done, finish the loop
3241                 break;
3242             }
3243         //Without paging, old approach
3244         } else if ($maxdiscussions && ($discussioncount > $maxdiscussions)) {
3245             $olddiscussionlink = true;
3246             break;
3247         }
3249         if (!empty($replies[$discussion->discussion])) {
3250             $discussion->replies = $replies[$discussion->discussion]->replies;
3251             $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
3252         } else {
3253             $discussion->replies = 0;
3254         }
3256         /// SPECIAL CASE: The front page can display a news item post to non-logged in users.
3257         /// All posts are read in this case.
3258         if (!$forumtracked) {
3259             $discussion->unread = '-';
3260         } else if (empty($USER)) {
3261             $discussion->unread = 0;
3262         } else {
3263             $discussion->unread = forum_tp_count_discussion_unread_posts($USER->id, $discussion->discussion);
3264         }
3266         if (!empty($USER->id)) {
3267             $ownpost = ($discussion->userid == $USER->id);
3268         } else {
3269             $ownpost=false;
3270         }
3271         // Use discussion name instead of subject of first post
3272         $discussion->subject = $discussion->name;
3274         switch ($displayformat) {
3275             case 'header':
3276                 if ($groupmode > 0) {
3277                     if (isset($groups[$discussion->groupid])) {
3278                         $group = $groups[$discussion->groupid];
3279                     } else {
3280                         $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid); //TODO:
3281                     }
3282                 } else {
3283                     $group = -1;
3284                 }
3285                 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked);
3286             break;
3287             default:
3288                 if ($canreply or $discussion->replies) {
3289                     $link = true;
3290                 } else {
3291                     $link = false;
3292                 }
3294                 $discussion->forum = $forum->id;
3296                 forum_print_post($discussion, $course->id, $ownpost, $reply=0, $link, $assessed=false);
3297             break;
3298         }
3299     }
3301     if ($displayformat == "header") {
3302         echo '</tbody>';
3303         echo '</table>';
3304     }
3306     if ($olddiscussionlink) {
3307         echo '<div class="forumolddiscuss">';
3308         echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
3309         echo get_string('olderdiscussions', 'forum').'</a> ...</div>';
3310     }
3312     if ($page != -1) { ///Show the paging bar
3313         print_paging_bar($numdiscussions, $page, $maxdiscussions, "view.php?f=$forum->id&amp;");
3314     }
3318 function forum_print_discussion($course, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
3320     global $USER, $CFG;
3322     if (!empty($USER->id)) {
3323         $ownpost = ($USER->id == $post->userid);
3324     } else {
3325         $ownpost = false;
3326     }
3327     if ($canreply === NULL) {
3328         $reply = forum_user_can_post($forum);
3329     } else {
3330         $reply = $canreply;
3331     }
3333     $ratings = NULL;
3334     $ratingsmenuused = false;
3335     $ratingsformused = false;
3336     if ($forum->assessed and !empty($USER->id)) {
3337         if ($ratings->scale = make_grades_menu($forum->scale)) {
3338             $ratings->assesstimestart = $forum->assesstimestart;
3339             $ratings->assesstimefinish = $forum->assesstimefinish;
3340             $ratings->allow = $canrate;
3342             if ($ratings->allow) {
3343                 echo '<form id="form" method="post" action="rate.php">';
3344                 echo '<div class="ratingform">';
3345                 echo '<input type="hidden" name="id" value="'.$course->id.'" />';
3346                 echo '<input type="hidden" name="forumid" value="'.$forum->id.'" />';
3347                 $ratingsformused = true;
3348             }
3349         }
3350     }
3352     $post->forum = $forum->id;   // Add the forum id to the post object, later used by forum_print_post
3353     $post->forumtype = $forum->type;
3355     $post->subject = format_string($post->subject);
3357     if (forum_tp_can_track_forums($forum)) {
3358         if ($forumtracked = forum_tp_is_tracked($forum)) {
3359             $user_read_array = forum_tp_get_discussion_read_records($USER->id, $post->discussion);
3360         } else {
3361             $user_read_array = array();
3362         }
3363     } else {
3364         $forumtracked = false;
3365         $user_read_array = array();
3366     }
3368     if (forum_print_post($post, $course->id, $ownpost, $reply, $link=false, $ratings,
3369                          '', '', (!$forumtracked || isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3370         $ratingsmenuused = true;
3371     }
3373     switch ($mode) {
3374         case FORUM_MODE_FLATOLDEST :
3375         case FORUM_MODE_FLATNEWEST :
3376         default:
3377             if (forum_print_posts_flat($post->discussion, $course->id, $mode, $ratings, $reply,
3378                                        $user_read_array, $post->forum)) {
3379                 $ratingsmenuused = true;
3380             }
3381             break;
3383         case FORUM_MODE_THREADED :
3384             if (forum_print_posts_threaded($post->id, $course->id, 0, $ratings, $reply,
3385                                            $user_read_array, $post->forum)) {
3386                 $ratingsmenuused = true;
3387             }
3388             break;
3390         case FORUM_MODE_NESTED :
3391             if (forum_print_posts_nested($post->id, $course->id, $ratings, $reply,
3392                                          $user_read_array, $post->forum)) {
3393                 $ratingsmenuused = true;
3394             }
3395             break;
3396     }
3398     if ($ratingsformused) {
3399         if ($ratingsmenuused) {
3400             echo '<div class="ratingsubmit">';
3401             echo '<input type="submit" value="'.get_string('sendinratings', 'forum').'" />';
3402             if ($forum->scale < 0) {
3403                 if ($scale = get_record("scale", "id", abs($forum->scale))) {
3404                     print_scale_menu_helpbutton($course->id, $scale );
3405                 }
3406             }
3407             echo '</div>';
3408         }
3410         echo '</div>';
3411         echo '</form>';
3412     }
3416 function forum_print_posts_flat($discussion, $courseid, $direction, $ratings, $reply, &$user_read_array, $forumid=0) {
3417     global $USER, $CFG;
3419     $link  = false;
3420     $ratingsmenuused = false;
3422     if ($direction < 0) {
3423         $sort = "ORDER BY created DESC";
3424     } else {
3425         $sort = "ORDER BY created ASC";
3426     }
3428     if ($posts = forum_get_discussion_posts($discussion, $sort, $forumid)) {
3429         foreach ($posts as $post) {
3431             $post->subject = format_string($post->subject);
3433             $ownpost = ($USER->id == $post->userid);
3434             if (forum_print_post($post, $courseid, $ownpost, $reply, $link, $ratings,
3435                                  '', '', (isset($user_read_array[$post->id]) || forum_tp_is_post_old($post)))) {
3436                 $ratingsmenuused = true;
3437             }
3438         }
3439     }
3441     return $ratingsmenuused;
3445 function forum_print_posts_threaded($parent, $courseid, $depth, $ratings, $reply, &$user_read_array, $forumid=0) {
3446     global $USER, $CFG;
3448     $link  = false;
3449     $ratingsmenuused = false;
3451     $istracking = forum_tp_can_track_forums($forumid) && forum_tp_is_tracked($forumid);
3453     if ($posts = forum_get_child_posts($parent, $forumid)) {
3455         if (!$cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
3456             error('Course Module ID was incorrect');
3457         }
3458         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3459         $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
3461         foreach ($posts as $post) {
3463             echo '<div class="indent">';
3464             if ($depth > 0) {
3465                 $ownpost = ($USER->id == $post->userid);
3467                 $post->subject = format_string($post->subject);