Now the my page retrieves all the fields (modinfo included) from the course,
[moodle.git] / mod / forum / lib.php
CommitLineData
41905731 1<?php // $Id$
f93f848a 2
f1e0649c 3require_once($CFG->libdir.'/filelib.php');
7f6689e4 4
501cdbd8 5/// CONSTANTS ///////////////////////////////////////////////////////////
f93f848a 6
d3583b41 7define('FORUM_MODE_FLATOLDEST', 1);
8define('FORUM_MODE_FLATNEWEST', -1);
9define('FORUM_MODE_THREADED', 2);
10define('FORUM_MODE_NESTED', 3);
2e2e71a8 11
d3583b41 12define('FORUM_FORCESUBSCRIBE', 1);
13define('FORUM_INITIALSUBSCRIBE', 2);
098d27d4 14define('FORUM_DISALLOWSUBSCRIBE',3);
709f0ec8 15
eaf50aef 16define('FORUM_TRACKING_OFF', 0);
17define('FORUM_TRACKING_OPTIONAL', 1);
18define('FORUM_TRACKING_ON', 2);
19
1a7cdb11 20define('FORUM_UNSET_POST_RATING', -999);
21
d3583b41 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') );
f93f848a 26
11b0c469 27// These are course content forums that can be added to the course manually
d3583b41 28$FORUM_TYPES = array ('general' => get_string('generalforum', 'forum'),
29 'eachuser' => get_string('eachuserforum', 'forum'),
098d27d4 30 'single' => get_string('singleforum', 'forum'),
31 'qanda' => get_string('qandaforum', 'forum')
32 );
f93f848a 33
d3583b41 34$FORUM_OPEN_MODES = array ('2' => get_string('openmode2', 'forum'),
35 '1' => get_string('openmode1', 'forum'),
36 '0' => get_string('openmode0', 'forum') );
70c476a7 37
5be7800c 38if (!isset($CFG->forum_displaymode)) {
d3583b41 39 set_config('forum_displaymode', FORUM_MODE_NESTED);
65b0e537 40}
73bb0835 41
5be7800c 42if (!isset($CFG->forum_shortpost)) {
d3583b41 43 set_config('forum_shortpost', 300); // Less non-HTML characters than this is short
65b0e537 44}
4d871a72 45
5be7800c 46if (!isset($CFG->forum_longpost)) {
d3583b41 47 set_config('forum_longpost', 600); // More non-HTML characters than this is long
65b0e537 48}
4d871a72 49
5be7800c 50if (!isset($CFG->forum_manydiscussions)) {
d3583b41 51 set_config('forum_manydiscussions', 100); // Number of discussions on a page
65b0e537 52}
e07635f4 53
4909e176 54if (!isset($CFG->forum_maxbytes)) {
d3583b41 55 set_config('forum_maxbytes', 512000); // Default maximum size for all forums
65b0e537 56}
4909e176 57
f37da850 58if (!isset($CFG->forum_trackreadposts)) {
d3583b41 59 set_config('forum_trackreadposts', true); // Default whether user needs to mark a post as read
f37da850 60}
61
62if (!isset($CFG->forum_oldpostdays)) {
81c679ce 63 set_config('forum_oldpostdays', 14); // Default number of days that a post is considered old
f37da850 64}
65
66if (!isset($CFG->forum_usermarksread)) {
d3583b41 67 set_config('forum_usermarksread', false); // Default whether user needs to mark a post as read
f37da850 68}
69
70if (!isset($CFG->forum_cleanreadtime)) {
d3583b41 71 set_config('forum_cleanreadtime', 2); // Default time (hour) to execute 'clean_read_records' cron
f37da850 72}
73
1246ea56 74if (!isset($CFG->forum_replytouser)) {
d3583b41 75 set_config('forum_replytouser', true); // Default maximum size for all forums
1246ea56 76}
77
27888a20 78if (empty($USER->id) or isguest()) {
79 $CFG->forum_trackreadposts = false; // This feature never works when a user isn't logged in
80}
4909e176 81
3d284127 82if (!isset($CFG->forum_enabletimedposts)) { // Newish feature that is not quite ready for production in 1.6
83 $CFG->forum_enabletimedposts = false;
84}
85
e07635f4 86
caadf009 87/// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
88
0a4ac01b 89/**
90 * Given an object containing all the necessary data,
91 * (defined by the form in mod.html) this function
92 * will create a new instance and return the id number
93 * of the new instance.
94*/
caadf009 95function forum_add_instance($forum) {
caadf009 96 global $CFG;
97
98 $forum->timemodified = time();
99
353228d8 100 if (empty($forum->assessed)) {
f2f56406 101 $forum->assessed = 0;
102 }
2b63df96 103
353228d8 104 if (empty($forum->ratingtime) or empty($forum->assessed)) {
98914efd 105 $forum->assesstimestart = 0;
106 $forum->assesstimefinish = 0;
107 }
caadf009 108
0fa18d5a 109 if (!$forum->id = insert_record('forum', $forum)) {
cb9a975f 110 return false;
111 }
112
d3583b41 113 if ($forum->type == 'single') { // Create related discussion.
353228d8 114 $discussion = new object();
caadf009 115 $discussion->course = $forum->course;
116 $discussion->forum = $forum->id;
117 $discussion->name = $forum->name;
118 $discussion->intro = $forum->intro;
119 $discussion->assessed = $forum->assessed;
632730fd 120 $discussion->format = $forum->type;
41547057 121 $discussion->mailnow = false;
caadf009 122
93c86520 123 if (! forum_add_discussion($discussion, $discussion->intro)) {
d3583b41 124 error('Could not add the discussion for this forum');
caadf009 125 }
126 }
8f0cd6ef 127
709f0ec8 128 if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) { // all users should be subscribed initially
129 $users = get_course_users($forum->course);
130 foreach ($users as $user) {
131 forum_subscribe($user->id, $forum->id);
132 }
133 }
caadf009 134
353228d8 135 $forum = stripslashes_recursive($forum);
612607bd 136 forum_grade_item_update($forum);
353228d8 137
caadf009 138 return $forum->id;
139}
140
141
0a4ac01b 142/**
143 * Given an object containing all the necessary data,
144 * (defined by the form in mod.html) this function
145 * will update an existing instance with new data.
146*/
caadf009 147function forum_update_instance($forum) {
caadf009 148 $forum->timemodified = time();
353228d8 149 $forum->id = $forum->instance;
caadf009 150
f0da6b85 151 if (empty($forum->assessed)) {
f2f56406 152 $forum->assessed = 0;
153 }
2b63df96 154
353228d8 155 if (empty($forum->ratingtime) or empty($forum->assessed)) {
98914efd 156 $forum->assesstimestart = 0;
157 $forum->assesstimefinish = 0;
158 }
159
d3583b41 160 if ($forum->type == 'single') { // Update related discussion and post.
161 if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
162 if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
163 notify('Warning! There is more than one discussion in this forum - using the most recent');
caadf009 164 $discussion = array_pop($discussions);
165 } else {
d3583b41 166 error('Could not find the discussion in this forum');
caadf009 167 }
168 }
d3583b41 169 if (! $post = get_record('forum_posts', 'id', $discussion->firstpost)) {
170 error('Could not find the first post in this forum discussion');
caadf009 171 }
172
173 $post->subject = $forum->name;
174 $post->message = $forum->intro;
175 $post->modified = $forum->timemodified;
176
353228d8 177 if (! update_record('forum_posts', ($post))) {
d3583b41 178 error('Could not update the first post');
caadf009 179 }
180
181 $discussion->name = $forum->name;
182
353228d8 183 if (! update_record('forum_discussions', ($discussion))) {
d3583b41 184 error('Could not update the discussion');
caadf009 185 }
186 }
187
353228d8 188 if (!update_record('forum', $forum)) {
189 error('Can not update forum');
190 }
191
192 $forum = stripslashes_recursive($forum);
193 forum_grade_item_update($forum);
194
195 return true;
caadf009 196}
197
198
0a4ac01b 199/**
200 * Given an ID of an instance of this module,
201 * this function will permanently delete the instance
202 * and any data that depends on it.
203 */
caadf009 204function forum_delete_instance($id) {
caadf009 205
0fa18d5a 206 if (!$forum = get_record('forum', 'id', $id)) {
caadf009 207 return false;
208 }
209
210 $result = true;
211
d3583b41 212 if ($discussions = get_records('forum_discussions', 'forum', $forum->id)) {
caadf009 213 foreach ($discussions as $discussion) {
0fa18d5a 214 if (!forum_delete_discussion($discussion, true)) {
caadf009 215 $result = false;
216 }
217 }
218 }
219
0fa18d5a 220 if (!delete_records('forum_subscriptions', 'forum', $forum->id)) {
caadf009 221 $result = false;
222 }
223
f37da850 224 forum_tp_delete_read_records(-1, -1, -1, $forum->id);
225
0fa18d5a 226 if (!delete_records('forum', 'id', $forum->id)) {
caadf009 227 $result = false;
228 }
229
353228d8 230 forum_grade_item_delete($forum);
231
caadf009 232 return $result;
233}
234
235
0a4ac01b 236/**
237 * Function to be run periodically according to the moodle cron
238 * Finds all posts that have yet to be mailed out, and mails them
239 * out to all subscribers
240*/
0fa18d5a 241function forum_cron() {
d30867b0 242 global $CFG, $USER;
857b798b 243
a974c799 244 $CFG->enablerecordcache = true; // We want all the caching we can get
669f2499 245
a974c799 246 $cronuser = clone($USER);
247 $site = get_site();
248
249 // all users that are subscribed to any post that needs sending
250 $users = array();
251
252 // status arrays
253 $mailcount = array();
254 $errorcount = array();
255
256 // caches
257 $discussions = array();
258 $forums = array();
259 $courses = array();
260 $coursemodules = array();
261 $postinfos = array();
262 $subscribedusers = array();
aaf7a9dc 263
ec2137ba 264
0a4ac01b 265 // Posts older than 2 days will not be mailed. This is to avoid the problem where
266 // cron has not been running for a long time, and then suddenly people are flooded
267 // with mail from the past few weeks or months
3ecca1ee 268 $timenow = time();
269 $endtime = $timenow - $CFG->maxeditingtime;
0a4ac01b 270 $starttime = $endtime - 48 * 3600; // Two days earlier
3ecca1ee 271
a974c799 272 if ($posts = forum_get_unmailed_posts($starttime, $endtime)) {
0a4ac01b 273 // Mark them all now as being mailed. It's unlikely but possible there
274 // might be an error later so that a post is NOT actually mailed out,
275 // but since mail isn't crucial, we can accept this risk. Doing it now
276 // prevents the risk of duplicated mails, which is a worse problem.
16b4e5b6 277
5fac3a5e 278 if (!forum_mark_old_posts_as_mailed($endtime)) {
279 mtrace('Errors occurred while trying to mark some posts as being mailed.');
280 return false; // Don't continue trying to mail them, in case we are in a cron loop
281 }
282
283 // checking post validity, and adding users to loop through later
284 foreach ($posts as $pid => $post) {
285
a974c799 286 $discussionid = $post->discussion;
287 if (!isset($discussions[$discussionid])) {
288 if ($discussion = get_record('forum_discussions', 'id', $post->discussion)) {
289 $discussions[$discussionid] = $discussion;
290 } else {
291 mtrace('Could not find discussion '.$discussionid);
292 unset($posts[$pid]);
293 continue;
294 }
5fac3a5e 295 }
a974c799 296 $forumid = $discussions[$discussionid]->forum;
297 if (!isset($forums[$forumid])) {
298 if ($forum = get_record('forum', 'id', $forumid)) {
299 $forums[$forumid] = $forum;
300 } else {
301 mtrace('Could not find forum '.$forumid);
302 unset($posts[$pid]);
303 continue;
304 }
5fac3a5e 305 }
a974c799 306 $courseid = $forums[$forumid]->course;
307 if (!isset($courses[$courseid])) {
308 if ($course = get_record('course', 'id', $courseid)) {
309 $courses[$courseid] = $course;
310 } else {
311 mtrace('Could not find course '.$courseid);
312 unset($posts[$pid]);
313 continue;
314 }
5fac3a5e 315 }
a974c799 316 if (!isset($coursemodules[$forumid])) {
317 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
318 $coursemodules[$forumid] = $cm;
319 } else {
320 mtrace('Could not course module for forum '.$forumid);
321 unset($posts[$pid]);
322 continue;
323 }
324 }
325
326
5fac3a5e 327 // caching subscribed users of each forum
a974c799 328 if (!isset($subscribedusers[$forumid])) {
329 if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, true)) {
704ca25c 330 foreach ($subusers as $postuser) {
a974c799 331 // do not try to mail users with stopped email
332 if ($postuser->emailstop) {
333 add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
334 continue;
335 }
704ca25c 336 // this user is subscribed to this forum
a974c799 337 $subscribedusers[$forumid][] = $postuser->id;
704ca25c 338 // this user is a user we have to process later
a974c799 339 $users[$postuser->id] = $postuser;
704ca25c 340 }
a974c799 341 }
5fac3a5e 342 }
a974c799 343
5fac3a5e 344 $mailcount[$pid] = 0;
345 $errorcount[$pid] = 0;
a974c799 346 }
5fac3a5e 347 }
caadf009 348
4dad2828 349 if ($users && $posts) {
edffca15 350
857b798b 351 $urlinfo = parse_url($CFG->wwwroot);
352 $hostname = $urlinfo['host'];
353
5fac3a5e 354 foreach ($users as $userto) {
a974c799 355
356 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
357
4dad2828 358 // set this so that the capabilities are cached, and environment matches receiving user
5fac3a5e 359 $USER = $userto;
a974c799 360
16b4e5b6 361 mtrace('Processing user '.$userto->id);
caadf009 362
0a4ac01b 363 // we might want to add another layer - forums here (by checking array_keys($subscribedusers))
364 // so that we can skip many posts
669f2499 365
4dad2828 366 foreach ($posts as $pid => $post) {
caadf009 367
a974c799 368 // Get info about the sending user
369 if (array_key_exists($post->userid, $users)) { // we might know him/her already
370 $userfrom = $users[$post->userid];
371 } else if (!$userfrom = get_record('user', 'id', $post->userid)) {
4dad2828 372 mtrace('Could not find user '.$post->userid);
373 continue;
374 }
918e9805 375
4dad2828 376 // Set up the environment for the post, discussion, forum, course
a974c799 377 $discussion = $discussions[$post->discussion];
378 $forum = $forums[$discussion->forum];
379 $course = $courses[$forum->course];
380 $cm = $coursemodules[$forum->id];
4dad2828 381
382 // Do some checks to see if we can bail out now
4dad2828 383 if (empty($subscribedusers[$forum->id]) || !in_array($userto->id, $subscribedusers[$forum->id])) {
a974c799 384 continue; // user does not subscribe to this forum
4dad2828 385 }
4dad2828 386
a974c799 387 // Get the context (from cache)
4dad2828 388 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); // Cached already
4dad2828 389
a974c799 390 // setup global $COURSE properly - needed for roles and languages
391 course_setup($course); // More environment
4dad2828 392
a974c799 393 // Make sure groups allow this user to see this email
38bd362a 394 if ($discussion->groupid > 0 and $groupmode = groupmode($course, $cm)) { // Groups are being used
395 if (! groups_group_exists($discussion->groupid)) { // Can't find group
396 continue; // Be safe and don't send it to anyone
5fac3a5e 397 }
918e9805 398
38bd362a 399 if (!ismember($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
400 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
401 continue;
9197e147 402 }
4dad2828 403 }
2b63df96 404
4dad2828 405 // Make sure we're allowed to see it...
406 if (!forum_user_can_see_post($forum, $discussion, $post)) {
407 mtrace('user '.$userto->id. ' can not see '.$post->id);
408 continue;
409 }
410
411 // OK so we need to send the email.
412
413 // Does the user want this post in a digest? If so postpone it for now.
414 if ($userto->maildigest > 0) {
415 // This user wants the mails to be in digest form
a974c799 416 $queue = new object();
417 $queue->userid = $userto->id;
4dad2828 418 $queue->discussionid = $discussion->id;
a974c799 419 $queue->postid = $post->id;
4dad2828 420 if (!insert_record('forum_queue', $queue)) {
421 mtrace("Error: mod/forum/cron.php: Could not queue for digest mail for id $post->id to user $userto->id ($userto->email) .. not trying again.");
aaf7a9dc 422 }
4dad2828 423 continue;
424 }
65b0e537 425
4dad2828 426
427 // Prepare to actually send the post now, and build up the content
428
a974c799 429 $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
4dad2828 430
431 $userfrom->customheaders = array ( // Headers to make emails easier to track
432 'Precedence: Bulk',
433 'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
434 'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
435 'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>',
436 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>',
437 'References: <moodlepost'.$post->parent.'@'.$hostname.'>',
438 'X-Course-Id: '.$course->id,
a974c799 439 'X-Course-Name: '.format_string($course->fullname, true)
4dad2828 440 );
a974c799 441
4dad2828 442
443 $postsubject = "$course->shortname: ".format_string($post->subject,true);
444 $posttext = forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto);
445 $posthtml = forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto);
446
447 // Send the post now!
448
449 mtrace('Sending ', '');
a974c799 450
4dad2828 451 if (!$mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext,
452 $posthtml, '', '', $CFG->forum_replytouser)) {
453 mtrace("Error: mod/forum/cron.php: Could not send out mail for id $post->id to user $userto->id".
454 " ($userto->email) .. not trying again.");
455 add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
456 substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
457 $errorcount[$post->id]++;
458 } else if ($mailresult === 'emailstop') {
a974c799 459 // should not be reached anymore - see check above
4dad2828 460 } else {
461 $mailcount[$post->id]++;
462
0a4ac01b 463 // Mark post as read if forum_usermarksread is set off
4dad2828 464 if (!$CFG->forum_usermarksread && forum_tp_can_track_forums($forum, $userto) &&
465 forum_tp_is_tracked($forum, $userto->id)) {
466 if (!forum_tp_mark_post_read($userto->id, $post, $forum->id)) {
467 mtrace("Error: mod/forum/cron.php: Could not mark post $post->id read for user $userto->id".
468 " while sending email.");
f37da850 469 }
caadf009 470 }
aaf7a9dc 471 }
4dad2828 472
473 mtrace('post '.$post->id. ': '.$post->subject);
aaf7a9dc 474 }
5fac3a5e 475 }
476 }
477
478 if ($posts) {
479 foreach ($posts as $post) {
16b4e5b6 480 mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
5fac3a5e 481 if ($errorcount[$post->id]) {
482 set_field("forum_posts", "mailed", "2", "id", "$post->id");
a974c799 483 }
aaf7a9dc 484 }
485 }
486
ad9ff3d3 487 $USER = clone($cronuser);
488 course_setup(SITEID);
489
9152fc99 490 $sitetimezone = $CFG->timezone;
944a2b28 491
0a4ac01b 492 // Now see if there are any digest mails waiting to be sent, and if we should send them
aaf7a9dc 493
8f0cd6ef 494 if (!isset($CFG->digestmailtimelast)) { // To catch the first time
ca8e8a10 495 set_config('digestmailtimelast', 0);
496 }
497
498 $timenow = time();
944a2b28 499 $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
ca8e8a10 500
501 if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
b140ae85 502
ca8e8a10 503 set_config('digestmailtimelast', $timenow);
aaf7a9dc 504
b140ae85 505 mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
506
a974c799 507 if ($digestposts = get_records('forum_queue')) {
8ad64455 508
aaf7a9dc 509 // We have work to do
510 $usermailcount = 0;
aaf7a9dc 511
a974c799 512 //caches - reuse the those filled before too
aaf7a9dc 513 $discussionposts = array();
514 $userdiscussions = array();
a974c799 515
516 foreach ($digestposts as $digestpost) {
517 if (!isset($users[$digestpost->userid])) {
518 if ($user = get_record('user', 'id', $digestpost->userid)) {
519 $users[$digestpost->userid] = $user;
520 } else {
521 continue;
522 }
523 }
524 $postuser = $users[$digestpost->userid];
525 if ($postuser->emailstop) {
526 add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
527 continue;
528 }
529
530 if (!isset($posts[$digestpost->postid])) {
531 if ($post = get_record('forum_posts', 'id', $digestpost->postid)) {
532 $posts[$digestpost->postid] = $post;
533 } else {
534 continue;
535 }
536 }
537 $discussionid = $digestpost->discussionid;
538 if (!isset($discussions[$discussionid])) {
539 if ($discussion = get_record('forum_discussions', 'id', $discussionid)) {
540 $discussions[$discussionid] = $discussion;
541 } else {
542 continue;
543 }
aaf7a9dc 544 }
a974c799 545 $forumid = $discussions[$discussionid]->forum;
546 if (!isset($forums[$forumid])) {
547 if ($forum = get_record('forum', 'id', $forumid)) {
548 $forums[$forumid] = $forum;
549 } else {
550 continue;
551 }
552 }
553
554 $courseid = $forums[$forumid]->course;
555 if (!isset($courses[$courseid])) {
556 if ($course = get_record('course', 'id', $courseid)) {
557 $courses[$courseid] = $course;
558 } else {
559 continue;
560 }
aaf7a9dc 561 }
a974c799 562
563 if (!isset($coursemodules[$forumid])) {
564 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
565 $coursemodules[$forumid] = $cm;
566 } else {
567 continue;
568 }
aaf7a9dc 569 }
570 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
571 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
572 }
573
574 // Data collected, start sending out emails to each user
a974c799 575 foreach ($userdiscussions as $userid => $thesediscussions) {
aaf7a9dc 576
a974c799 577 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
aaf7a9dc 578
a974c799 579 mtrace(get_string('processingdigest', 'forum', $userid), '... ');
aaf7a9dc 580
581 // First of all delete all the queue entries for this user
582 delete_records('forum_queue', 'userid', $userid);
583 $userto = $users[$userid];
584
0a4ac01b 585 // Override the language and timezone of the "current" user, so that
586 // mail is customised for the receiver.
9152fc99 587 $USER = $userto;
588 course_setup(SITEID);
aaf7a9dc 589
a974c799 590 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
aaf7a9dc 591
a974c799 592 $headerdata = new object();
593 $headerdata->sitename = format_string($site->fullname, true);
839f2456 594 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
aaf7a9dc 595
596 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
597 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
9c674431 598
78c0d909 599 $posthtml = "<head>";
600 foreach ($CFG->stylesheets as $stylesheet) {
601 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
602 }
603 $posthtml .= "</head>\n<body>\n";
a0330747 604 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
aaf7a9dc 605
a974c799 606 foreach ($thesediscussions as $discussionid) {
aaf7a9dc 607
0a4ac01b 608 @set_time_limit(120); // to be reset for each post
a974c799 609
610 $discussion = $discussions[$discussionid];
611 $forum = $forums[$discussion->forum];
612 $course = $courses[$forum->course];
613 $cm = $coursemodules[$forum->id];
65b0e537 614
9152fc99 615 //override language
616 course_setup($course);
617
a974c799 618 $strforums = get_string('forums', 'forum');
aaf7a9dc 619 $canunsubscribe = ! forum_is_forcesubscribed($forum->id);
620 $canreply = forum_user_can_post($forum, $userto);
caadf009 621
caadf009 622
aaf7a9dc 623 $posttext .= "\n \n";
624 $posttext .= '=====================================================================';
625 $posttext .= "\n \n";
3849dae8 626 $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
aaf7a9dc 627 if ($discussion->name != $forum->name) {
c78ac798 628 $posttext .= " -> ".format_string($discussion->name,true);
caadf009 629 }
aaf7a9dc 630 $posttext .= "\n";
65b0e537 631
aaf7a9dc 632 $posthtml .= "<p><font face=\"sans-serif\">".
633 "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
634 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
3849dae8 635 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
aaf7a9dc 636 if ($discussion->name == $forum->name) {
637 $posthtml .= "</font></p>";
caadf009 638 } else {
c78ac798 639 $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
caadf009 640 }
aaf7a9dc 641 $posthtml .= '<p>';
642
e1c6dde1 643 $postsarray = $discussionposts[$discussionid];
644 sort($postsarray);
645
0a4ac01b 646 // Create an empty array to use for marking read posts.
647 // (I'm sure there's already a structure I can use here, but I can't be sure.)
f37da850 648 $markread = array();
649
857b798b 650 foreach ($postsarray as $postid) {
a974c799 651 $post = $posts[$postid];
652
653 if (array_key_exists($post->userid, $users)) { // we might know him/her already
654 $userfrom = $users[$post->userid];
655 } else if (!$userfrom = get_record('user', 'id', $post->userid)) {
656 mtrace('Could not find user '.$post->userid);
aaf7a9dc 657 continue;
658 }
659
857b798b 660 $userfrom->customheaders = array ("Precedence: Bulk");
661
662 if ($userto->maildigest == 2) {
aaf7a9dc 663 // Subjects only
a974c799 664 $by = new object();
aaf7a9dc 665 $by->name = fullname($userfrom);
666 $by->date = userdate($post->modified);
17dc3f3c 667 $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
aaf7a9dc 668 $posttext .= "\n---------------------------------------------------------------------";
669
839f2456 670 $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
0be4d8bf 671 $posthtml .= '<div><a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'#p'.$post->id.'">'.format_string($post->subject,true).'</a> '.get_string("bynameondate", "forum", $by).'</div>';
857b798b 672
673 } else {
aaf7a9dc 674 // The full treatment
675 $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true);
add3201e 676 $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
f37da850 677
0a4ac01b 678 // Create an array of postid's for this user to mark as read.
2b63df96 679 if (!$CFG->forum_usermarksread &&
680 forum_tp_can_track_forums($forum, $userto) &&
eaf50aef 681 forum_tp_is_tracked($forum, $userto->id)) {
f37da850 682 $markread[$post->id]->post = $post;
683 $markread[$post->id]->forumid = $forum->id;
684 }
aaf7a9dc 685 }
686 }
687 if ($canunsubscribe) {
688 $posthtml .= "\n<div align=\"right\"><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>";
857b798b 689 } else {
aaf7a9dc 690 $posthtml .= "\n<div align=\"right\"><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
691 }
692 $posthtml .= '<hr size="1" noshade="noshade" /></p>';
caadf009 693 }
a0330747 694 $posthtml .= '</body>';
caadf009 695
a974c799 696 if ($userto->mailformat != 1) {
379a42cb 697 // This user DOESN'T want to receive HTML
698 $posthtml = '';
699 }
700
8f0cd6ef 701 if (!$mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml,
50a26903 702 '', '', $CFG->forum_replytouser)) {
b140ae85 703 mtrace("ERROR!");
aaf7a9dc 704 echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
705 add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
b6268a0e 706 } else if ($mailresult === 'emailstop') {
a974c799 707 // should not happen anymore - see check above
aaf7a9dc 708 } else {
b140ae85 709 mtrace("success.");
aaf7a9dc 710 $usermailcount++;
e3ff14ca 711
0a4ac01b 712 // Mark post as read if forum_usermarksread is set off
2b63df96 713 if (!$CFG->forum_usermarksread &&
714 forum_tp_can_track_forums($forum->id, $userto) &&
6ea5ebd4 715 forum_tp_is_tracked($forum->id, $userto->id)) {
716 foreach ($markread as $postinfo) {
717 if (!forum_tp_mark_post_read($userto->id, $postinfo->post, $postinfo->forumid)) {
718 mtrace("Error: mod/forum/cron.php: Could not mark post $postid read for user $userto->id".
719 " while sending digest email.");
f37da850 720 }
721 }
6ea5ebd4 722 }
3d94772d 723 }
caadf009 724 }
caadf009 725 }
726 }
727
a974c799 728 if (!empty($usermailcount)) {
b140ae85 729 mtrace(get_string('digestsentusers', 'forum', $usermailcount));
aaf7a9dc 730 }
731
a974c799 732 $USER = $cronuser;
9152fc99 733 course_setup(SITEID); // reset cron user language, theme and timezone settings
ec2137ba 734
8ad64455 735 if (!empty($CFG->forum_lastreadclean)) {
f37da850 736 $timenow = time();
8ad64455 737 if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
738 set_config('forum_lastreadclean', $timenow);
f37da850 739 forum_tp_clean_read_records();
740 }
741 } else {
8ad64455 742 set_config('forum_lastreadclean', time());
f37da850 743 }
744
745
caadf009 746 return true;
747}
748
0a4ac01b 749/**
1670305d 750 * Builds and returns the body of the email notification in plain text.
751 *
752 * @param object $course
753 * @param object $forum
754 * @param object $discussion
755 * @param object $post
756 * @param object $userfrom
757 * @param object $userto
758 * @param boolean $bare
759 * @return string The email body in plain text format.
0a4ac01b 760 */
aaf7a9dc 761function forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
15f81ee3 762 global $CFG, $USER;
2b63df96 763
0fa18d5a 764 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
765 error('Course Module ID was incorrect');
766 }
767 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
1306c5ea 768 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
2b63df96 769
aaf7a9dc 770 $by = New stdClass;
0fa18d5a 771 $by->name = fullname($userfrom, $viewfullnames);
aaf7a9dc 772 $by->date = userdate($post->modified, "", $userto->timezone);
773
774 $strbynameondate = get_string('bynameondate', 'forum', $by);
775
64762ddc 776 $strforums = get_string('forums', 'forum');
777
aaf7a9dc 778 $canunsubscribe = ! forum_is_forcesubscribed($forum->id);
779 $canreply = forum_user_can_post($forum, $userto);
780
781 $posttext = '';
782
0fa18d5a 783 if (!$bare) {
3849dae8 784 $posttext = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
aaf7a9dc 785
786 if ($discussion->name != $forum->name) {
c78ac798 787 $posttext .= " -> ".format_string($discussion->name,true);
aaf7a9dc 788 }
789 }
790
791 $posttext .= "\n---------------------------------------------------------------------\n";
17dc3f3c 792 $posttext .= format_string($post->subject,true);
0fa18d5a 793 if ($bare) {
0be4d8bf 794 $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
aaf7a9dc 795 }
796 $posttext .= "\n".$strbynameondate."\n";
797 $posttext .= "---------------------------------------------------------------------\n";
f2b5d7e3 798 $posttext .= format_text_email(trusttext_strip($post->message), $post->format);
aaf7a9dc 799 $posttext .= "\n\n";
800 if ($post->attachment) {
801 $post->course = $course->id;
802 $post->forum = $forum->id;
803 $posttext .= forum_print_attachments($post, "text");
804 }
805 if (!$bare && $canreply) {
806 $posttext .= "---------------------------------------------------------------------\n";
807 $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
808 $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
809 }
810 if (!$bare && $canunsubscribe) {
811 $posttext .= "\n---------------------------------------------------------------------\n";
812 $posttext .= get_string("unsubscribe", "forum");
813 $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
814 }
815
816 return $posttext;
817}
818
0a4ac01b 819/**
1670305d 820 * Builds and returns the body of the email notification in html format.
821 *
822 * @param object $course
823 * @param object $forum
824 * @param object $discussion
825 * @param object $post
826 * @param object $userfrom
827 * @param object $userto
828 * @return string The email text in HTML format
0a4ac01b 829 */
aaf7a9dc 830function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto) {
831 global $CFG;
832
a0288610 833 if ($userto->mailformat != 1) { // Needs to be HTML
834 return '';
835 }
aaf7a9dc 836
a0288610 837 $strforums = get_string('forums', 'forum');
838 $canreply = forum_user_can_post($forum, $userto);
918e9805 839 $canunsubscribe = ! $forum->forcesubscribe;
aaf7a9dc 840
a0288610 841 $posthtml = '<head>';
842 foreach ($CFG->stylesheets as $stylesheet) {
843 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
844 }
845 $posthtml .= '</head>';
f2379d2d 846 $posthtml .= "\n<body id=\"email\">\n\n";
aaf7a9dc 847
f2379d2d 848 $posthtml .= '<div class="navbar">'.
a0288610 849 '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
850 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
851 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
852 if ($discussion->name == $forum->name) {
853 $posthtml .= '</div>';
aaf7a9dc 854 } else {
a0288610 855 $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
856 format_string($discussion->name,true).'</a></div>';
aaf7a9dc 857 }
a0288610 858 $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false);
859
860 if ($canunsubscribe) {
861 $posthtml .= '<br /><div class="unsubscribelink"><a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.
862 get_string('unsubscribe', 'forum').'</a></div>';
863 }
864
f2379d2d 865 $posthtml .= '</body>';
866
a0288610 867 return $posthtml;
aaf7a9dc 868}
1670305d 869
870
0a4ac01b 871/**
1670305d 872 *
873 * @param object $course
874 * @param object $user
875 * @param object $mod TODO this is not used in this function, refactor
876 * @param object $forum
877 * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
0a4ac01b 878 */
caadf009 879function forum_user_outline($course, $user, $mod, $forum) {
880
1f48942e 881 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
caadf009 882 $result->info = get_string("numposts", "forum", count($posts));
883
884 $lastpost = array_pop($posts);
885 $result->time = $lastpost->modified;
886 return $result;
887 }
888 return NULL;
889}
890
891
0a4ac01b 892/**
893 * TODO document
894 */
caadf009 895function forum_user_complete($course, $user, $mod, $forum) {
896 global $CFG;
897
1f48942e 898 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
caadf009 899 foreach ($posts as $post) {
e3ff14ca 900
f37da850 901 $post->forum = $forum->id;
497588fe 902 forum_print_post($post, $course->id, $ownpost=false, $reply=false, $link=false, $rate=false);
caadf009 903 }
904
905 } else {
41905731 906 echo "<p>".get_string("noposts", "forum")."</p>";
caadf009 907 }
caadf009 908}
909
0a4ac01b 910/**
911 * TODO document
912 */
185cfb09 913function forum_print_overview($courses,&$htmlarray) {
0d6b9d4f 914 global $USER, $CFG;
a8f4522d 915 $LIKE = sql_ilike();
9cba7a8c 916
185cfb09 917 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
918 return array();
919 }
f8716988 920
185cfb09 921 if (!$forums = get_all_instances_in_courses('forum',$courses)) {
f8716988 922 return;
923 }
185cfb09 924
5aa23eea 925
f8716988 926 // get all forum logs in ONE query (much better!)
185cfb09 927 $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {$CFG->prefix}log l "
928 ." JOIN {$CFG->prefix}course_modules cm ON cm.id = cmid "
929 ." WHERE (";
930 foreach ($courses as $course) {
931 $sql .= '(l.course = '.$course->id.' AND l.time > '.$course->lastaccess.') OR ';
932 }
933 $sql = substr($sql,0,-3); // take off the last OR
934
a8f4522d 935 $sql .= ") AND l.module = 'forum' AND action $LIKE 'add post%' "
185cfb09 936 ." AND userid != ".$USER->id." GROUP BY cmid,l.course,instance";
2b63df96 937
185cfb09 938 if (!$new = get_records_sql($sql)) {
939 $new = array(); // avoid warnings
940 }
2b63df96 941
185cfb09 942 // also get all forum tracking stuff ONCE.
943 $trackingforums = array();
944 foreach ($forums as $forum) {
945 if (forum_tp_can_track_forums($forum)) {
946 $trackingforums[$forum->id] = $forum;
947 }
948 }
2b63df96 949
185cfb09 950 if (count($trackingforums) > 0) {
951 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
952 $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
953 ' FROM '.$CFG->prefix.'forum_posts p '.
954 ' JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
955 ' LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$USER->id.' WHERE (';
d3553951 956 foreach ($trackingforums as $track) {
353228d8 957 $sql .= '(d.forum = '.$track->id.' AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = '.get_current_group($track->course).')) OR ';
185cfb09 958 }
959 $sql = substr($sql,0,-3); // take off the last OR
960 $sql .= ') AND p.modified >= '.$cutoffdate.' AND r.id is NULL GROUP BY d.forum,d.course';
961
962 if (!$unread = get_records_sql($sql)) {
963 $unread = array();
964 }
965 } else {
966 $unread = array();
95d71ad3 967 }
185cfb09 968
9cba7a8c 969 if (empty($unread) and empty($new)) {
970 return;
971 }
972
973 $strforum = get_string('modulename','forum');
974 $strnumunread = get_string('overviewnumunread','forum');
975 $strnumpostssince = get_string('overviewnumpostssince','forum');
976
f8716988 977 foreach ($forums as $forum) {
185cfb09 978 $str = '';
f8716988 979 $count = 0;
185cfb09 980 $thisunread = 0;
f8716988 981 $showunread = false;
982 // either we have something from logs, or trackposts, or nothing.
983 if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
984 $count = $new[$forum->id]->count;
90558ec4 985 }
185cfb09 986 if (array_key_exists($forum->id,$unread)) {
987 $thisunread = $unread[$forum->id]->count;
f8716988 988 $showunread = true;
0d6b9d4f 989 }
185cfb09 990 if ($count > 0 || $thisunread > 0) {
e23800b7 991 $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
992 $forum->name.'</a></div>';
993 $str .= '<div class="info">';
994 $str .= $count.' '.$strnumpostssince;
f8716988 995 if (!empty($showunread)) {
e23800b7 996 $str .= '<br />'.$thisunread .' '.$strnumunread;
f8716988 997 }
e23800b7 998 $str .= '</div></div>';
f8716988 999 }
2b63df96 1000 if (!empty($str)) {
185cfb09 1001 if (!array_key_exists($forum->course,$htmlarray)) {
1002 $htmlarray[$forum->course] = array();
1003 }
1004 if (!array_key_exists('forum',$htmlarray[$forum->course])) {
1005 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
1006 }
1007 $htmlarray[$forum->course]['forum'] .= $str;
1008 }
2b63df96 1009 }
0d6b9d4f 1010}
1011
0a4ac01b 1012/**
1013 * Given a course and a date, prints a summary of all the new
1014 * messages posted in the course since that date
1015 */
1b5910c4 1016function forum_print_recent_activity($course, $isteacher, $timestart) {
cc3655a2 1017
3d891989 1018 global $CFG;
a8f4522d 1019 $LIKE = sql_ilike();
caadf009 1020
1021 $heading = false;
1022 $content = false;
1023
8f7dc7f1 1024 if (!$logs = get_records_select('log', 'time > \''.$timestart.'\' AND '.
1025 'course = \''.$course->id.'\' AND '.
1026 'module = \'forum\' AND '.
90eca94b 1027 'action '.$LIKE.' \'add %\' ', 'time ASC')){
1b5910c4 1028 return false;
1029 }
1030
8f7dc7f1 1031 $strftimerecent = get_string('strftimerecent');
dcde9f02 1032
10b63984 1033 $mygroupid = mygroupid($course->id);
0a4ac01b 1034 $groupmode = array(); // To cache group modes
d05956ac 1035
1306c5ea 1036 $count = 0;
1b5910c4 1037 foreach ($logs as $log) {
1038 //Get post info, I'll need it later
ac1d9a22 1039 if ($post = forum_get_post_from_log($log)) {
1040 //Create a temp valid module structure (course,id)
1306c5ea 1041 $tempmod = new object;
ac1d9a22 1042 $tempmod->course = $log->course;
1043 $tempmod->id = $post->forum;
1044 //Obtain the visible property from the instance
10b63984 1045 $coursecontext = get_context_instance(CONTEXT_COURSE, $tempmod->course);
583b57b4 1046 $modvisible = instance_is_visible('forum', $tempmod)
1047 || has_capability('moodle/course:viewhiddenactivities', $coursecontext);
ac1d9a22 1048 }
d05956ac 1049
ac1d9a22 1050 //Only if the post exists and mod is visible
1051 if ($post && $modvisible) {
583b57b4 1052
1053 if (!isset($cm[$post->forum])) {
1054 $cm[$post->forum] = get_coursemodule_from_instance('forum', $post->forum, $course->id);
ac1d9a22 1055 }
583b57b4 1056 $modcontext = get_context_instance(CONTEXT_MODULE, $cm[$post->forum]->id);
1057
0a4ac01b 1058 // Check whether this is belongs to a discussion in a group that
1059 // should NOT be accessible to the current user
583b57b4 1060 if (!has_capability('moodle/site:accessallgroups', $modcontext)
0a4ac01b 1061 && $post->groupid != -1) { // Open discussions have groupid -1
b91d6dcd 1062
583b57b4 1063 $groupmode[$post->forum] = groupmode($course, $cm[$post->forum]);
2b63df96 1064
8f7dc7f1 1065 if ($groupmode[$post->forum]) {
fa22fd5f 1066 //hope i didn't break anything
1067 if (!@in_array($mygroupid, $post->groupid))/*$mygroupid != $post->groupid*/{
ac1d9a22 1068 continue;
1069 }
1b5910c4 1070 }
caadf009 1071 }
ac1d9a22 1072
1073 if (! $heading) {
1306c5ea 1074 print_headline(get_string('newforumposts', 'forum').':', 3);
ac1d9a22 1075 $heading = true;
1076 $content = true;
1077 }
1078 $date = userdate($post->modified, $strftimerecent);
8f7dc7f1 1079
1080 $subjectclass = ($log->action == 'add discussion') ? ' bold' : '';
1081
1306c5ea 1082 //Accessibility: markup as a list.
1083 if ($count < 1) {
1084 echo "\n<ul class='unlist'>\n";
1085 }
1086 $count++;
1087 echo '<li><div class="head">'.
a4f7aac1 1088 '<div class="date">'.$date.'</div>'.
583b57b4 1089 '<div class="name">'.fullname($post, has_capability('moodle/site:viewfullnames', $coursecontext)).'</div>'.
a4f7aac1 1090 '</div>';
1091 echo '<div class="info'.$subjectclass.'">';
8f7dc7f1 1092 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/'.str_replace('&', '&amp;', $log->url).'">';
17dc3f3c 1093 $post->subject = break_up_long_words(format_string($post->subject,true));
8f7dc7f1 1094 echo $post->subject;
1306c5ea 1095 echo "</a>\"</div></li>\n";
caadf009 1096 }
1097 }
1306c5ea 1098 echo "</ul>\n";
caadf009 1099 return $content;
1100}
1101
353228d8 1102/**
1103 * Return grade for given user or all users.
1104 *
1105 * @param int $forumid id of forum
1106 * @param int $userid optional user id, 0 means all users
1107 * @return array array of grades, false if none
1108 */
612607bd 1109function forum_get_user_grades($forum, $userid=0) {
353228d8 1110 global $CFG;
1111
1112 $user = $userid ? "AND u.id = $userid" : "";
1113
ac9b0805 1114 $sql = "SELECT u.id, u.id AS userid, avg(fr.rating) AS rawgrade
353228d8 1115 FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
1116 {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
1117 WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
612607bd 1118 AND fr.userid != u.id AND fd.forum = $forum->id
353228d8 1119 $user
1120 GROUP BY u.id";
1121
1122 return get_records_sql($sql);
1123}
caadf009 1124
0a4ac01b 1125/**
353228d8 1126 * Update grades by firing grade_updated event
1127 *
612607bd 1128 * @param object $forum null means all forums
353228d8 1129 * @param int $userid specific user only, 0 mean all
0a4ac01b 1130 */
612607bd 1131function forum_update_grades($forum=null, $userid=0, $nullifnone=true) {
353228d8 1132 global $CFG;
caadf009 1133
612607bd 1134 if ($forum != null) {
92382b6d 1135 require_once($CFG->libdir.'/gradelib.php');
612607bd 1136 if ($grades = forum_get_user_grades($forum, $userid)) {
b67ec72f 1137 grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades);
02ebf404 1138
353228d8 1139 } else if ($userid and $nullifnone) {
612607bd 1140 $grade = new object();
1141 $grade->itemid = $forum->id;
1142 $grade->userid = $userid;
ac9b0805 1143 $grade->rawgrade = NULL;
b67ec72f 1144 grade_update('mod/forum', $data->course, 'mod', 'forum', $forum->id, 0, $grade);
353228d8 1145 }
02ebf404 1146
353228d8 1147 } else {
1148 $sql = "SELECT f.*, cm.idnumber as cmidnumber
1149 FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
1150 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1151 if ($rs = get_recordset_sql($sql)) {
1152 if ($rs->RecordCount() > 0) {
1153 while ($forum = rs_fetch_next_record($rs)) {
612607bd 1154 forum_grade_item_update($forum);
1155 if ($forum->assessed) {
1156 forum_update_grades($forum, 0, false);
d6bdd9d5 1157 }
02ebf404 1158 }
caadf009 1159 }
353228d8 1160 rs_close($rs);
caadf009 1161 }
353228d8 1162 }
1163}
1164
1165/**
612607bd 1166 * Create/update grade item for given forum
353228d8 1167 *
1168 * @param object $forum object with extra cmidnumber
612607bd 1169 * @return int 0 if ok
353228d8 1170 */
1171function forum_grade_item_update($forum) {
612607bd 1172 global $CFG;
1173 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1174 require_once($CFG->libdir.'/gradelib.php');
353228d8 1175 }
1176
612607bd 1177 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
353228d8 1178
5980d52f 1179 if (!$forum->assessed or $forum->scale == 0) {
612607bd 1180 $params['gradetype'] = GRADE_TYPE_NONE;
353228d8 1181
1182 } else if ($forum->scale > 0) {
1183 $params['gradetype'] = GRADE_TYPE_VALUE;
1184 $params['grademax'] = $forum->scale;
1185 $params['grademin'] = 0;
1186
1187 } else if ($forum->scale < 0) {
1188 $params['gradetype'] = GRADE_TYPE_SCALE;
1189 $params['scaleid'] = -$forum->scale;
1190 }
1191
b67ec72f 1192 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, $params);
353228d8 1193}
1194
1195/**
1196 * Delete grade item for given forum
1197 *
1198 * @param object $forum object
1199 * @return object grade_item
1200 */
1201function forum_grade_item_delete($forum) {
612607bd 1202 global $CFG;
1203 require_once($CFG->libdir.'/gradelib.php');
1204
b67ec72f 1205 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
caadf009 1206}
1207
353228d8 1208
0a4ac01b 1209/**
1210 * Returns the users with data in one forum
1211 * (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
1212 */
05855091 1213function forum_get_participants($forumid) {
05855091 1214
1215 global $CFG;
1216
1217 //Get students from forum_subscriptions
95e72c12 1218 $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
05855091 1219 FROM {$CFG->prefix}user u,
1220 {$CFG->prefix}forum_subscriptions s
1221 WHERE s.forum = '$forumid' and
65b0e537 1222 u.id = s.userid");
05855091 1223 //Get students from forum_posts
95e72c12 1224 $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
05855091 1225 FROM {$CFG->prefix}user u,
1226 {$CFG->prefix}forum_discussions d,
1227 {$CFG->prefix}forum_posts p
1228 WHERE d.forum = '$forumid' and
1229 p.discussion = d.id and
1230 u.id = p.userid");
1231
1232 //Get students from forum_ratings
95e72c12 1233 $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
05855091 1234 FROM {$CFG->prefix}user u,
1235 {$CFG->prefix}forum_discussions d,
1236 {$CFG->prefix}forum_posts p,
1237 {$CFG->prefix}forum_ratings r
1238 WHERE d.forum = '$forumid' and
1239 p.discussion = d.id and
1240 r.post = p.id and
1241 u.id = r.userid");
1242
1243 //Add st_posts to st_subscriptions
1244 if ($st_posts) {
1245 foreach ($st_posts as $st_post) {
1246 $st_subscriptions[$st_post->id] = $st_post;
1247 }
1248 }
1249 //Add st_ratings to st_subscriptions
1250 if ($st_ratings) {
1251 foreach ($st_ratings as $st_rating) {
1252 $st_subscriptions[$st_rating->id] = $st_rating;
1253 }
1254 }
1255 //Return st_subscriptions array (it contains an array of unique users)
1256 return ($st_subscriptions);
1257}
caadf009 1258
0a4ac01b 1259/**
1260 * TODO document
1261 */
0f1a97c2 1262function forum_scale_used ($forumid,$scaleid) {
1263//This function returns if a scale is being used by one forum
65b0e537 1264
0f1a97c2 1265 $return = false;
65b0e537 1266
0f1a97c2 1267 $rec = get_record("forum","id","$forumid","scale","-$scaleid");
65b0e537 1268
fa22fd5f 1269 if (!empty($rec) && !empty($scaleid)) {
0f1a97c2 1270 $return = true;
1271 }
65b0e537 1272
0f1a97c2 1273 return $return;
1274}
1275
0a4ac01b 1276// SQL FUNCTIONS ///////////////////////////////////////////////////////////
9fa49e22 1277
0a4ac01b 1278/**
1279 * Gets a post with all info ready for forum_print_post
1280 * Most of these joins are just to get the forum id
1281 */
1f48942e 1282function forum_get_post_full($postid) {
1f48942e 1283 global $CFG;
1284
8fbdc992 1285 return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
6572ab71 1286 FROM {$CFG->prefix}forum_posts p
1287 LEFT JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
6572ab71 1288 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
1289 WHERE p.id = '$postid'");
1f48942e 1290}
1291
0a4ac01b 1292/**
1293 * Gets posts with all info ready for forum_print_post
1294 * We pass forumid in because we always know it so no need to make a
1295 * complicated join to find it out.
1296 */
77efef3e 1297function forum_get_discussion_posts($discussion, $sort, $forumid) {
1f48942e 1298 global $CFG;
1299
77efef3e 1300 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
10426cef 1301 FROM {$CFG->prefix}forum_posts p
10426cef 1302 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
65b0e537 1303 WHERE p.discussion = $discussion
10426cef 1304 AND p.parent > 0 $sort");
1f48942e 1305}
1306
0a4ac01b 1307/**
1308 * Gets posts with all info ready for forum_print_post
1309 * We pass forumid in because we always know it so no need to make a
1310 * complicated join to find it out.
1311 */
77efef3e 1312function forum_get_child_posts($parent, $forumid) {
1f48942e 1313 global $CFG;
1314
77efef3e 1315 return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture
10426cef 1316 FROM {$CFG->prefix}forum_posts p
10426cef 1317 LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
65b0e537 1318 WHERE p.parent = '$parent'
1f48942e 1319 ORDER BY p.created ASC");
1320}
1321
42fb3c85 1322/**
1323 * An array of forum objects that the user is allowed to read/search through.
1324 * @param $userid
1325 * @param $courseid - if 0, we look for forums throughout the whole site.
1326 * @return array of forum objects, or false if no matches
1327 * Forum objects have the following attributes:
1328 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1329 * viewhiddentimedposts
1330 */
1331function forum_get_readable_forums($userid, $courseid=0) {
2b63df96 1332
d50704bf 1333 global $CFG, $USER;
2b63df96 1334
42fb3c85 1335 if (!$forummod = get_record('modules', 'name', 'forum')) {
1336 error('The forum module is not installed');
1337 }
2b63df96 1338
42fb3c85 1339 if ($courseid) {
1340 $courses = get_records('course', 'id', $courseid);
1341 } else {
0a4ac01b 1342 // If no course is specified, then the user can see SITE + his courses.
1343 // And admins can see all courses, so pass the $doanything flag enabled
6155150c 1344 $courses1 = get_records('course', 'id', SITEID);
0a66585b 1345 $courses2 = get_my_courses($userid, 'visible DESC,sortorder ASC', null, true);
6155150c 1346 $courses = array_merge($courses1, $courses2);
42fb3c85 1347 }
1348 if (!$courses) {
1349 return false;
1350 }
1351
1352 $readableforums = array();
2b63df96 1353
6527b5c2 1354 foreach ($courses as $course) {
1355
42fb3c85 1356 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
2b63df96 1357
86c4258a 1358 if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) {
42fb3c85 1359 $selecthidden = ' AND cm.visible = 1';
1360 } else {
1361 $selecthidden = '';
1362 }
2b63df96 1363
0b9873f2 1364 $selectforums = "SELECT f.id AS id,
583b57b4 1365 f.name AS name,
42fb3c85 1366 f.type AS type,
1367 f.course AS course,
1368 cm.id AS cmid,
1369 cm.visible AS cmvisible,
1370 cm.groupmode AS cmgroupmode
f274fe07 1371 FROM {$CFG->prefix}course_modules cm,
1372 {$CFG->prefix}forum f
42fb3c85 1373 WHERE cm.instance = f.id
1374 AND cm.course = {$course->id}
1375 AND cm.module = {$forummod->id}
583b57b4 1376 $selecthidden
1377 ORDER BY f.name ASC";
2b63df96 1378
42fb3c85 1379 if ($forums = get_records_sql($selectforums)) {
2b63df96 1380
42fb3c85 1381 $group = user_group($course->id, $userid);
2b63df96 1382
42fb3c85 1383 foreach ($forums as $forum) {
1384 $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid);
2b63df96 1385
9222e721 1386 if (has_capability('mod/forum:viewdiscussion', $forumcontext)) {
d50704bf 1387
6527b5c2 1388 // Evaluate groupmode.
1389 $cm = new object;
1390 $cm->id = $forum->cmid;
1391 $cm->groupmode = $forum->cmgroupmode;
1392 $forum->cmgroupmode = groupmode($course, $cm);
1393
1394 if ($forum->cmgroupmode == SEPARATEGROUPS
1395 && !has_capability('moodle/site:accessallgroups', $forumcontext)) {
1396 $forum->accessallgroups = false;
1397 $forum->accessgroup = $group->id; // The user can only access
1398 // discussions for this group.
1399 } else {
1400 $forum->accessallgroups = true;
1401 }
1402
42fb3c85 1403 $forum->viewhiddentimedposts
1404 = has_capability('mod/forum:viewhiddentimedposts', $forumcontext);
2b63df96 1405
d50704bf 1406 if ($forum->type == 'qanda'
1407 && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) {
1408
1409 // We need to check whether the user has posted in the qanda forum.
d50704bf 1410 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
1411 // the user is allowed to see in this forum.
2b63df96 1412
dcd8e589 1413 if ($discussionspostedin =
1414 forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1415 foreach ($discussionspostedin as $d) {
1416 array_push($forum->onlydiscussions, $d->id);
1417 }
d50704bf 1418 }
1419 }
42fb3c85 1420 array_push($readableforums, $forum);
1421 }
1422 }
1423 }
1424 } // End foreach $courses
2b63df96 1425
67875aa1 1426 //print_object($courses);
1427 //print_object($readableforums);
2b63df96 1428
42fb3c85 1429 return $readableforums;
1430}
1431
bbbf2d40 1432/**
1433 * Returns a list of posts found using an array of search terms.
42fb3c85 1434 * @param $searchterms - array of search terms, e.g. word +word -word
1435 * @param $courseid - if 0, we search through the whole site
bbbf2d40 1436 * @param $page
1437 * @param $recordsperpage=50
1438 * @param &$totalcount
bbbf2d40 1439 * @param $extrasql
42fb3c85 1440 * @return array of posts found
1441 */
2b63df96 1442function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
42fb3c85 1443 &$totalcount, $extrasql='') {
1444 global $CFG, $USER;
1445 require_once($CFG->libdir.'/searchlib.php');
1446
1447 $forums = forum_get_readable_forums($USER->id, $courseid);
2b63df96 1448
67875aa1 1449 if (count($forums) == 0) {
1450 return false;
1451 }
42fb3c85 1452
1453 for ($i=0; $i<count($forums); $i++) {
1454 if ($i == 0) {
1455 $selectdiscussion = " ((d.forum = {$forums[$i]->id}";
1456 } else {
1457 $selectdiscussion .= " OR (d.forum = {$forums[$i]->id}";
1458 }
1459 if (!empty($CFG->forum_enabletimedposts) && !$forums[$i]->viewhiddentimedposts) {
1460 $now = time();
1461 $selectdiscussion .= " AND ( d.userid = {$USER->id}
1462 OR ((d.timestart = 0 OR d.timestart <= $now)
1463 AND (d.timeend = 0 OR d.timeend > $now)) )";
1464 }
cac40c27 1465 if ($forums[$i]->type == 'qanda' && isset($forums[$i]->onlydiscussions)) {
d50704bf 1466 // This is a qanda forum.
1467 if (is_array($forums[$i]->onlydiscussions)) {
1468 // Show question posts as well as posts from discussions in
1469 // which the user has posted a reply.
1470 $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions);
1471 $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)";
1472 } else {
1473 // Show only the question posts.
1474 $selectdiscussion .= ' AND (p.parent = 0)';
1475 }
1476 }
42fb3c85 1477 if (!$forums[$i]->accessallgroups) {
1478 if (!empty($forums[$i]->accessgroup)) {
1479 $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}";
1480 $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups.
1481 } else {
1482 // User isn't in any group. Only search discussions that are
1483 // open to all groups.
1484 $selectdiscussion .= ' AND d.groupid = -1';
1485 }
1486 }
1487 $selectdiscussion .= ")\n";
1488 }
1489 $selectdiscussion .= ")";
1490
1491
a8f4522d 1492 // Some differences SQL
1493 $LIKE = sql_ilike();
1494 $NOTLIKE = 'NOT ' . $LIKE;
a4bad45c 1495 if ($CFG->dbfamily == 'postgres') {
42fb3c85 1496 $REGEXP = '~*';
1497 $NOTREGEXP = '!~*';
a8f4522d 1498 } else {
42fb3c85 1499 $REGEXP = 'REGEXP';
1500 $NOTREGEXP = 'NOT REGEXP';
1501 }
1502
1503 $messagesearch = '';
1504 $searchstring = '';
2b63df96 1505
42fb3c85 1506 // Need to concat these back together for parser to work.
1507 foreach($searchterms as $searchterm){
1508 if ($searchstring != '') {
1509 $searchstring .= ' ';
1510 }
1511 $searchstring .= $searchterm;
1512 }
1513
1514 // We need to allow quoted strings for the search. The quotes *should* be stripped
1515 // by the parser, but this should be examined carefully for security implications.
1516 $searchstring = str_replace("\\\"","\"",$searchstring);
1517 $parser = new search_parser();
1518 $lexer = new search_lexer($parser);
1519
1520 if ($lexer->parse($searchstring)) {
1521 $parsearray = $parser->get_parsed_array();
0a4ac01b 1522 // Experimental feature under 1.8! MDL-8830
1523 // Use alternative text searches if defined
1524 // This feature only works under mysql until properly implemented for other DBs
1525 // Requires manual creation of text index for forum_posts before enabling it:
1526 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
1527 // Experimental feature under 1.8! MDL-8830
532daab4 1528 if (!empty($CFG->forum_usetextsearches)) {
1529 $messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
1530 'p.userid', 'u.id', 'u.firstname',
1531 'u.lastname', 'p.modified', 'd.forum');
1532 } else {
1533 $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
1534 'p.userid', 'u.id', 'u.firstname',
1535 'u.lastname', 'p.modified', 'd.forum');
1536 }
42fb3c85 1537 }
1538
1539 $fromsql = "{$CFG->prefix}forum_posts p,
1540 {$CFG->prefix}forum_discussions d,
1541 {$CFG->prefix}user u";
1542
1543 $selectsql = " $messagesearch
1544 AND p.discussion = d.id
1545 AND p.userid = u.id
1546 AND $selectdiscussion
1547 $extrasql";
1548
1549 $countsql = "SELECT COUNT(*)
1550 FROM $fromsql
1551 WHERE $selectsql";
1552
7f094149 1553 $searchsql = "SELECT p.*,
42fb3c85 1554 d.forum,
1555 u.firstname,
1556 u.lastname,
1557 u.email,
1558 u.picture
1559 FROM $fromsql
1560 WHERE $selectsql
1561 ORDER BY p.modified DESC";
1562
b1342e18 1563 $totalcount = count_records_sql($countsql);
d50704bf 1564
67875aa1 1565 return get_records_sql($searchsql, $limitfrom, $limitnum);
42fb3c85 1566}
1567
0a4ac01b 1568/**
1569 * Returns a list of ratings for a particular post - sorted.
1570 */
9fa49e22 1571function forum_get_ratings($postid, $sort="u.firstname ASC") {
9fa49e22 1572 global $CFG;
65b0e537 1573 return get_records_sql("SELECT u.*, r.rating, r.time
1574 FROM {$CFG->prefix}forum_ratings r,
9fa49e22 1575 {$CFG->prefix}user u
65b0e537 1576 WHERE r.post = '$postid'
1577 AND r.userid = u.id
9fa49e22 1578 ORDER BY $sort");
1579}
1580
0a4ac01b 1581/**
1582 * Returns a list of all new posts that have not been mailed yet
1583 */
3ecca1ee 1584function forum_get_unmailed_posts($starttime, $endtime) {
1f48942e 1585 global $CFG;
fbc21e82 1586 $now = time();
65b0e537 1587 return get_records_sql("SELECT p.*, d.course
1588 FROM {$CFG->prefix}forum_posts p,
1f48942e 1589 {$CFG->prefix}forum_discussions d
65b0e537 1590 WHERE p.mailed = 0
b12d055a 1591 AND (p.created >= '$starttime' OR d.timestart > 0)
41547057 1592 AND (p.created < '$endtime' OR p.mailnow = 1)
c274b0e0 1593 AND p.discussion = d.id
fbc21e82 1594 AND ((d.timestart = 0 OR d.timestart <= '$now')
1595 AND (d.timeend = 0 OR d.timeend > '$now'))
0fcac008 1596 ORDER BY p.modified ASC");
1f48942e 1597}
1598
0a4ac01b 1599/**
1600 * Marks posts before a certain time as being mailed already
1601 */
3ecca1ee 1602function forum_mark_old_posts_as_mailed($endtime) {
3ecca1ee 1603 global $CFG;
0a4ac01b 1604// Find out posts those are not showing immediately so we can exclude them
0f620d4b 1605 $now = time();
1606 $delayed_posts = get_records_sql("SELECT p.id, p.discussion
1607 FROM {$CFG->prefix}forum_posts p,
1608 {$CFG->prefix}forum_discussions d
1609 WHERE p.mailed = 0
1610 AND p.discussion = d.id
1611 AND d.timestart > '$now'");
1612 $delayed_ids = array();
1613 if ($delayed_posts) {
1614 foreach ($delayed_posts as $post) {
1615 $delayed_ids[] = $post->id;
1616 }
1617 } else {
1618 $delayed_ids[] = 0;
1619 }
c274b0e0 1620 return execute_sql("UPDATE {$CFG->prefix}forum_posts
1621 SET mailed = '1'
9ed7d7e1 1622 WHERE id NOT IN (".implode(',',$delayed_ids).")
1623 AND (created < '$endtime' OR mailnow = 1)
1624 AND mailed ='0'", false);
3ecca1ee 1625}
1626
0a4ac01b 1627/**
1628 * Get all the posts for a user in a forum suitable for forum_print_post
1629 */
1f48942e 1630function forum_get_user_posts($forumid, $userid) {
1f48942e 1631 global $CFG;
1632
77efef3e 1633 return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture
65b0e537 1634 FROM {$CFG->prefix}forum f,
1635 {$CFG->prefix}forum_discussions d,
1636 {$CFG->prefix}forum_posts p,
1637 {$CFG->prefix}user u
1638 WHERE f.id = '$forumid'
1639 AND d.forum = f.id
1f48942e 1640 AND p.discussion = d.id
65b0e537 1641 AND p.userid = '$userid'
ebc3bd2b 1642 AND p.userid = u.id
1f48942e 1643 ORDER BY p.modified ASC");
1644}
1645
0a4ac01b 1646/**
1647 * Given a log entry, return the forum post details for it.
1648 */
1f48942e 1649function forum_get_post_from_log($log) {
1f48942e 1650 global $CFG;
1651
1652 if ($log->action == "add post") {
1653
2b63df96 1654 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 1655 u.firstname, u.lastname, u.email, u.picture
65b0e537 1656 FROM {$CFG->prefix}forum_discussions d,
1657 {$CFG->prefix}forum_posts p,
8f7dc7f1 1658 {$CFG->prefix}forum f,
65b0e537 1659 {$CFG->prefix}user u
1660 WHERE p.id = '$log->info'
1661 AND d.id = p.discussion
1662 AND p.userid = u.id
8f7dc7f1 1663 AND u.deleted <> '1'
1664 AND f.id = d.forum");
1f48942e 1665
1666
1667 } else if ($log->action == "add discussion") {
1668
2b63df96 1669 return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 1670 u.firstname, u.lastname, u.email, u.picture
65b0e537 1671 FROM {$CFG->prefix}forum_discussions d,
1672 {$CFG->prefix}forum_posts p,
8f7dc7f1 1673 {$CFG->prefix}forum f,
65b0e537 1674 {$CFG->prefix}user u
1675 WHERE d.id = '$log->info'
1676 AND d.firstpost = p.id
1677 AND p.userid = u.id
8f7dc7f1 1678 AND u.deleted <> '1'
1679 AND f.id = d.forum");
1f48942e 1680 }
1681 return NULL;
1682}
1683
0a4ac01b 1684/**
1685 * Given a discussion id, return the first post from the discussion
1686 */
d05956ac 1687function forum_get_firstpost_from_discussion($discussionid) {
d05956ac 1688 global $CFG;
1689
1690 return get_record_sql("SELECT p.*
65b0e537 1691 FROM {$CFG->prefix}forum_discussions d,
d05956ac 1692 {$CFG->prefix}forum_posts p
65b0e537 1693 WHERE d.id = '$discussionid'
d05956ac 1694 AND d.firstpost = p.id ");
1695}
1696
0a4ac01b 1697/**
1698 * Returns an array of counts of replies to each discussion (optionally in one forum or course and/or user)
1699 */
a48e8c4b 1700function forum_count_discussion_replies($forum='0', $course='0', $user='0') {
1f48942e 1701 global $CFG;
1702
a48e8c4b 1703 $forumselect = $courseselect = $userselect = '';
1704
1f48942e 1705 if ($forum) {
1706 $forumselect = " AND d.forum = '$forum'";
a48e8c4b 1707 }
1708 if ($course) {
1709 $courseselect = " AND d.course = '$course'";
1710 }
1711 if ($user) {
1712 $userselect = " AND d.userid = '$user'";
1f48942e 1713 }
3599e487 1714 return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid
65b0e537 1715 FROM {$CFG->prefix}forum_posts p,
1f48942e 1716 {$CFG->prefix}forum_discussions d
a48e8c4b 1717 WHERE p.parent > 0 $forumselect $courseselect $userselect
1c887009 1718 AND p.discussion = d.id
1f48942e 1719 GROUP BY p.discussion");
1720}
1721
0a4ac01b 1722/**
1723 * How many unrated posts are in the given discussion for a given user?
1724 */
1f48942e 1725function forum_count_unrated_posts($discussionid, $userid) {
1f48942e 1726 global $CFG;
1727 if ($posts = get_record_sql("SELECT count(*) as num
1728 FROM {$CFG->prefix}forum_posts
65b0e537 1729 WHERE parent > 0
1730 AND discussion = '$discussionid'
ebc3bd2b 1731 AND userid <> '$userid' ")) {
1f48942e 1732
65b0e537 1733 if ($rated = get_record_sql("SELECT count(*) as num
1734 FROM {$CFG->prefix}forum_posts p,
1f48942e 1735 {$CFG->prefix}forum_ratings r
1736 WHERE p.discussion = '$discussionid'
65b0e537 1737 AND p.id = r.post
ebc3bd2b 1738 AND r.userid = '$userid'")) {
1f48942e 1739 $difference = $posts->num - $rated->num;
1740 if ($difference > 0) {
1741 return $difference;
1742 } else {
1743 return 0; // Just in case there was a counting error
1744 }
1745 } else {
1746 return $posts->num;
1747 }
1748 } else {
1749 return 0;
1750 }
1751}
1752
0a4ac01b 1753/**
1754 * Get all discussions in a forum
1755 */
65b0e537 1756function forum_get_discussions($forum="0", $forumsort="d.timemodified DESC",
353228d8 1757 $user=0, $fullpost=true, $currentgroup=-1, $limit=0, $userlastmodified=false) {
fbc21e82 1758 global $CFG, $USER;
0fcac008 1759
3d284127 1760 $timelimit = '';
1761
1762 if (!empty($CFG->forum_enabletimedposts)) {
2b63df96 1763
7613e6d7 1764 if (!$cm = get_coursemodule_from_instance('forum', $forum)) {
1765 error('Course Module ID was incorrect');
1766 }
1767 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2b63df96 1768
0468976c 1769 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
3d284127 1770 $now = time();
1771 $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')";
1772 if (!empty($USER->id)) {
1773 $timelimit .= " OR d.userid = '$USER->id'";
1774 }
1775 $timelimit .= ')';
fbc21e82 1776 }
fbc21e82 1777 }
1f48942e 1778
1779 if ($user) {
1780 $userselect = " AND u.id = '$user' ";
1781 } else {
1782 $userselect = "";
1783 }
2862b309 1784
9eabd190 1785 $limitfrom = 0;
1786 $limitnum = 0;
90ec387a 1787 if ($limit) {
9eabd190 1788 $limitnum = $limit;
90ec387a 1789 }
8f0cd6ef 1790
353228d8 1791 if ($currentgroup == -1) {
1792 $currentgroup = get_current_group($cm->course);
1793 }
1794
1795 if ($currentgroup) {
1796 $groupselect = " AND (d.groupid = '$currentgroup' OR d.groupid = -1) ";
1797 } else {
02509fe6 1798 $groupselect = "";
1799 }
2862b309 1800
29507631 1801 if (empty($forumsort)) {
1802 $forumsort = "d.timemodified DESC";
1803 }
2ab968e9 1804 if (empty($fullpost)) {
b879effb 1805 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
2ab968e9 1806 } else {
1807 $postdata = "p.*";
1808 }
9197e147 1809
13597d01 1810 if (empty($userlastmodified)) { // We don't need to know this
1811 $umfields = '';
1812 $umtable = '';
13597d01 1813 } else {
096de9ad 1814 $umfields = ', um.firstname AS umfirstname, um.lastname AS umlastname';
cfdc10e6 1815 $umtable = ' LEFT JOIN '.$CFG->prefix.'user um on (d.usermodified = um.id)';
13597d01 1816 }
1817
80d3523e 1818 //TODO: there must be a nice way to do this that keeps both postgres and mysql 3.2x happy but I can't find it right now.
a4bad45c 1819 if ($CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'oracle') {
80d3523e 1820 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
13597d01 1821 u.firstname, u.lastname, u.email, u.picture $umfields
93d58b30 1822 FROM {$CFG->prefix}forum_discussions d
1823 JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
1824 JOIN {$CFG->prefix}user u ON p.userid = u.id
13597d01 1825 $umtable
65b0e537 1826 WHERE d.forum = '$forum'
65b0e537 1827 AND p.parent = 0
2b63df96 1828 $timelimit $groupselect $userselect
9eabd190 1829 ORDER BY $forumsort", $limitfrom, $limitnum);
a4bad45c 1830 } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query)
80d3523e 1831 return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid,
1832 u.firstname, u.lastname, u.email, u.picture $umfields
fd6590a9 1833 FROM ({$CFG->prefix}forum_posts p,
b73b9d81 1834 {$CFG->prefix}user u,
fd6590a9 1835 {$CFG->prefix}forum_discussions d)
80d3523e 1836 $umtable
1837 WHERE d.forum = '$forum'
1838 AND p.discussion = d.id
1839 AND p.parent = 0
80329e02 1840 AND p.userid = u.id $timelimit $groupselect $userselect
9eabd190 1841 ORDER BY $forumsort", $limitfrom, $limitnum);
80d3523e 1842 }
1f48942e 1843}
1844
1845
1846
0a4ac01b 1847/**
1848 * Get all discussions started by a particular user in a course (or group)
1849 * This function no longer used ...
1850 */
b656e2a9 1851function forum_get_user_discussions($courseid, $userid, $groupid=0) {
1f48942e 1852 global $CFG;
1853
b656e2a9 1854 if ($groupid) {
1855 $groupselect = " AND d.groupid = '$groupid' ";
1856 } else {
1857 $groupselect = "";
1858 }
1859
2862b309 1860 return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture,
ebc3bd2b 1861 f.type as forumtype, f.name as forumname, f.id as forumid
65b0e537 1862 FROM {$CFG->prefix}forum_discussions d,
1863 {$CFG->prefix}forum_posts p,
1864 {$CFG->prefix}user u,
1f48942e 1865 {$CFG->prefix}forum f
65b0e537 1866 WHERE d.course = '$courseid'
1867 AND p.discussion = d.id
1868 AND p.parent = 0
1869 AND p.userid = u.id
1870 AND u.id = '$userid'
b656e2a9 1871 AND d.forum = f.id $groupselect
b8bf90c5 1872 ORDER BY p.created DESC");
1f48942e 1873}
1874
0a4ac01b 1875/**
1876 * Returns list of user objects that are subscribed to this forum
1877 */
669f2499 1878function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) {
0a4ac01b 1879
1f48942e 1880 global $CFG;
1881
669f2499 1882 static $resultscache = array();
1883
1884 if ($cache && isset($resultscache[$forum->id][$groupid])) {
1885 return $resultscache[$forum->id][$groupid];
1886 }
1887
6673d7bd 1888 if ($groupid) {
e25cb710 1889 $grouptables = ', '. groups_members_from_sql();
1890 $groupselect = 'AND'.groups_members_where_sql($groupid, 'u.id');
6673d7bd 1891 } else {
669f2499 1892 $grouptables = '';
1893 $groupselect = '';
6673d7bd 1894 }
1895
709f0ec8 1896 if (forum_is_forcesubscribed($forum->id)) {
669f2499 1897 $results = get_course_users($course->id); // Otherwise get everyone in the course
1898 } else {
1899 $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop,
9152fc99 1900 u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums
65b0e537 1901 FROM {$CFG->prefix}user u,
6673d7bd 1902 {$CFG->prefix}forum_subscriptions s $grouptables
1f48942e 1903 WHERE s.forum = '$forum->id'
65b0e537 1904 AND s.userid = u.id
6673d7bd 1905 AND u.deleted <> 1 $groupselect
7c81b6e2 1906 ORDER BY u.email ASC");
669f2499 1907 }
a044c05d 1908 // Guest user should never be subscribed to a forum.
1909 if ($guest = guest_user()) {
1910 unset($results[$guest->id]);
1911 }
669f2499 1912
1913 if ($cache) {
1914 $resultscache[$forum->id][$groupid] = $results;
1915 }
1916
1917 return $results;
1f48942e 1918}
9fa49e22 1919
067675c0 1920
1921
0a4ac01b 1922// OTHER FUNCTIONS ///////////////////////////////////////////////////////////
f93f848a 1923
1924
11b0c469 1925function forum_get_course_forum($courseid, $type) {
1926// How to set up special 1-per-course forums
a6fcdf98 1927 global $CFG;
1928
29cbd93a 1929 if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
65b0e537 1930 // There should always only be ONE, but with the right combination of
29cbd93a 1931 // errors there might be more. In this case, just return the oldest one (lowest ID).
1932 foreach ($forums as $forum) {
1933 return $forum; // ie the first one
11b0c469 1934 }
8daaf761 1935 }
e6874d9f 1936
8daaf761 1937 // Doesn't exist, so create one now.
1938 $forum->course = $courseid;
1939 $forum->type = "$type";
1940 switch ($forum->type) {
1941 case "news":
65a3ef30 1942 $forum->name = addslashes(get_string("namenews", "forum"));
1943 $forum->intro = addslashes(get_string("intronews", "forum"));
906fef94 1944 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
8daaf761 1945 $forum->assessed = 0;
709f0ec8 1946 if ($courseid == SITEID) {
1947 $forum->name = get_string("sitenews");
1948 $forum->forcesubscribe = 0;
8f0cd6ef 1949 }
8daaf761 1950 break;
1951 case "social":
65a3ef30 1952 $forum->name = addslashes(get_string("namesocial", "forum"));
1953 $forum->intro = addslashes(get_string("introsocial", "forum"));
8daaf761 1954 $forum->assessed = 0;
1955 $forum->forcesubscribe = 0;
1956 break;
8daaf761 1957 default:
1958 notify("That forum type doesn't exist!");
1959 return false;
1960 break;
1961 }
1962
1963 $forum->timemodified = time();
1964 $forum->id = insert_record("forum", $forum);
1965
e1b5643f 1966 if (! $module = get_record("modules", "name", "forum")) {
1967 notify("Could not find forum module!!");
1968 return false;
82aa0e8d 1969 }
e1b5643f 1970 $mod->course = $courseid;
1971 $mod->module = $module->id;
1972 $mod->instance = $forum->id;
1973 $mod->section = 0;
1974 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
1306c5ea 1975 notify("Could not add a new course module to the course '" . format_string($course->fullname) . "'");
e1b5643f 1976 return false;
1977 }
1978 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
1979 notify("Could not add the new course module to that section");
1980 return false;
1981 }
1982 if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
1983 notify("Could not update the course module with the correct section");
1984 return false;
1985 }
1986 include_once("$CFG->dirroot/course/lib.php");
1987 rebuild_course_cache($courseid);
65b0e537 1988
8daaf761 1989 return get_record("forum", "id", "$forum->id");
82aa0e8d 1990}
1991
f93f848a 1992
0a4ac01b 1993/**
1994* Given the data about a posting, builds up the HTML to display it and
1995* returns the HTML in a string. This is designed for sending via HTML email.
1996*/
65b0e537 1997function forum_make_mail_post(&$post, $user, $touser, $course,
11b0c469 1998 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2b63df96 1999
501cdbd8 2000
15f81ee3 2001 global $CFG, $USER;
501cdbd8 2002
1306c5ea 2003 // the old caching was removed for now, because it did not work due to recent changes in cron
0d851f90 2004
ee8e0008 2005 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2b63df96 2006
ee8e0008 2007 if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) {
2a692058 2008 mtrace('Course Module ID was incorrect');
7613e6d7 2009 }
2010 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2011
1306c5ea 2012 // format the post body
2013 $options = new object();
2014 $options->para = true;
2015 $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);
0d851f90 2016
add3201e 2017 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
501cdbd8 2018
add3201e 2019 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
501cdbd8 2020 $output .= print_user_picture($user->id, $course->id, $user->picture, false, true);
add3201e 2021 $output .= '</td>';
501cdbd8 2022
2023 if ($post->parent) {
add3201e 2024 $output .= '<td class="topic">';
501cdbd8 2025 } else {
add3201e 2026 $output .= '<td class="topic starter">';
501cdbd8 2027 }
add3201e 2028 $output .= '<div class="subject">'.format_string($post->subject).'</div>';
1b26d5e7 2029
15f81ee3 2030 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext));
1306c5ea 2031 $by = new object();
add3201e 2032 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
2033 $by->date = userdate($post->modified, '', $touser->timezone);
2034 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
2035
2036 $output .= '</td></tr>';
2037
7b54f563 2038 $output .= '<tr><td class="left side" valign="top">';
add3201e 2039 if ($group = user_group($course->id, $user->id)) {
f393f545 2040 $output .= print_group_picture($group, $course->id, false, true, true);
add3201e 2041 } else {
2042 $output .= '&nbsp;';
2043 }
1b26d5e7 2044
add3201e 2045 $output .= '</td><td class="content">';
501cdbd8 2046
7f6689e4 2047 if ($post->attachment) {
2048 $post->course = $course->id;
add3201e 2049 $output .= '<div class="attachments">';
2050 $output .= forum_print_attachments($post, 'html');
72d497d4 2051 $output .= "</div>";
7f6689e4 2052 }
2053
0d851f90 2054 $output .= $formattedtext;
501cdbd8 2055
0a4ac01b 2056// Commands
add3201e 2057 $commands = array();
501cdbd8 2058
2e2e71a8 2059 if ($post->parent) {
add3201e 2060 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2061 $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
2e2e71a8 2062 }
ce45515e 2063
add3201e 2064 if ($reply) {
2065 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
2066 get_string('reply', 'forum').'</a>';
501cdbd8 2067 }
2068
add3201e 2069 $output .= '<div class="commands">';
2070 $output .= implode(' | ', $commands);
2071 $output .= '</div>';
2072
0a4ac01b 2073// Context link to post if required
501cdbd8 2074 if ($link) {
add3201e 2075 $output .= '<div class="link">';
0be4d8bf 2076 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
2b63df96 2077 get_string('postincontext', 'forum').'</a>';
add3201e 2078 $output .= '</div>';
501cdbd8 2079 }
add3201e 2080
501cdbd8 2081 if ($footer) {
add3201e 2082 $output .= '<div class="footer">'.$footer.'</div>';
501cdbd8 2083 }
add3201e 2084 $output .= '</td></tr></table>'."\n\n";
2085
501cdbd8 2086 return $output;
2087}
2088
2089
0a4ac01b 2090/**
2091 * TODO document
2092 */
65b0e537 2093function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false,
f37da850 2094 $ratings=NULL, $footer="", $highlight="", $post_read=-99) {
74f5d1e3 2095
098d27d4 2096 global $USER, $CFG, $SESSION;
501cdbd8 2097
951e1073 2098 static $stredit, $strdelete, $strreply, $strparent, $strprune;
2099 static $strpruneheading, $threadedmode;
77efef3e 2100 static $strmarkread, $strmarkunread, $istracked;
f37da850 2101
951e1073 2102
87ca0adf 2103 $discussion = get_record('forum_discussions', 'id', $post->discussion);
951e1073 2104 if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum)) {
2105 error('Course Module ID was incorrect');
2106 }
2107 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2108
2109
098d27d4 2110 if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) {
2111 if (empty($SESSION->forum_search)) {
2112 // just viewing, return
2113 return;
2b63df96 2114 }
0be4d8bf 2115 echo '<a id="p'.$post->id.'"></a>';
098d27d4 2116 echo '<table cellspacing="0" class="forumpost">';
2117 echo '<tr class="header"><td class="picture left">';
2118 // print_user_picture($post->userid, $courseid, $post->picture);
2119 echo '</td>';
2120 if ($post->parent) {
2121 echo '<td class="topic">';
2122 } else {
2123 echo '<td class="topic starter">';
2124 }
2125 echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
2126 echo '<div class="author">';
2127 print_string('forumauthorhidden','forum');
2128 echo '</div></td></tr>';
2b63df96 2129
098d27d4 2130 echo '<tr><td class="left side">';
2131 echo '&nbsp;';
2b63df96 2132
0a4ac01b 2133 // Actual content
2b63df96 2134
098d27d4 2135 echo '</td><td class="content">'."\n";
2136 echo get_string('forumbodyhidden','forum');
2137 echo '</td></tr></table>';
2138 return;
2139 }
2140
2e2e71a8 2141 if (empty($stredit)) {
359f2758 2142 $stredit = get_string('edit', 'forum');
2143 $strdelete = get_string('delete', 'forum');
2144 $strreply = get_string('reply', 'forum');
2145 $strparent = get_string('parent', 'forum');
2146 $strpruneheading = get_string('pruneheading', 'forum');
d494dd83 2147 $strprune = get_string('prune', 'forum');
2e2e71a8 2148 $threadedmode = (!empty($USER->mode) and ($USER->mode == FORUM_MODE_THREADED));
f37da850 2149 $strmarkread = get_string('markread', 'forum');
2150 $strmarkunread = get_string('markunread', 'forum');
77efef3e 2151
2152 if (!empty($post->forum)) {
2b63df96 2153 $istracked = (forum_tp_can_track_forums($post->forum) &&
77efef3e 2154 forum_tp_is_tracked($post->forum));
2155 } else {
2156 $istracked = false;
2157 }
f37da850 2158 }
2159
eaf50aef 2160 if ($istracked) {
f37da850 2161 if ($post_read == -99) { // If we don't know yet...
0a4ac01b 2162 // The front page can display a news item post to non-logged in users. This should
2163 // always appear as 'read'.
489de4ae 2164 $post_read = empty($USER) || forum_tp_is_post_read($USER->id, $post);
f37da850 2165 }
2166 if ($post_read) {
2167 $read_style = ' read';
2168 } else {
2169 $read_style = ' unread';
2170 echo '<a name="unread"></a>';
2171 }
2172 } else {
2173 $read_style = '';
2e2e71a8 2174 }
2175
0be4d8bf 2176 echo '<a id="p'.$post->id.'"></a>';
33200577 2177 echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
501cdbd8 2178
d3583b41 2179 echo '<tr class="header"><td class="picture left">';
501cdbd8 2180 print_user_picture($post->userid, $courseid, $post->picture);
d3583b41 2181 echo '</td>';
501cdbd8 2182
2183 if ($post->parent) {
d3583b41 2184 echo '<td class="topic">';
501cdbd8 2185 } else {
d3583b41 2186 echo '<td class="topic starter">';
501cdbd8 2187 }
83ec9098 2188
47c9c8f5 2189 echo '<div class="subject">'.format_string($post->subject).'</div>';
e3ff14ca 2190
d3583b41 2191 echo '<div class="author">';
951e1073 2192 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
d3583b41 2193 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
2194 $post->userid.'&amp;course='.$courseid.'">'.$fullname.'</a>';
d62413e8 2195 $by->date = userdate($post->modified);
d3583b41 2196 print_string('bynameondate', 'forum', $by);
2197 echo '</div></td></tr>';
1b26d5e7 2198
d3583b41 2199 echo '<tr><td class="left side">';
507407a7 2200 if ($group = user_group($courseid, $post->userid)) {
a4fbb0b2 2201 print_group_picture($group, $courseid, false, false, true);
507407a7 2202 } else {
d3583b41 2203 echo '&nbsp;';
507407a7 2204 }
d3583b41 2205
0a4ac01b 2206// Actual content
d3583b41 2207
2208 echo '</td><td class="content">'."\n";
501cdbd8 2209
7f6689e4 2210 if ($post->attachment) {
2211 $post->course = $courseid;
d3583b41 2212 $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion);
2213 echo '<div class="attachments">';
72d497d4 2214 $attachedimages = forum_print_attachments($post);
d3583b41 2215 echo '</div>';
e9c2dc1f 2216 } else {
d3583b41 2217 $attachedimages = '';
7f6689e4 2218 }
2219
d3583b41 2220
326ce3c4 2221 $options = new Object;
2222 $options->para = false;
f2b5d7e3 2223 $options->trusttext = true;
5be7800c 2224 if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) {
aa153f29 2225 // Print shortened version
326ce3c4 2226 echo format_text(forum_shorten_post($post->message), $post->format, $options, $courseid);
c585fa17 2227 $numwords = count_words(strip_tags($post->message));
d3583b41 2228 echo '<p><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2229 echo get_string('readtherest', 'forum');
2230 echo '</a> ('.get_string('numwords', '', $numwords).')...</p>';
501cdbd8 2231 } else {
aa153f29 2232 // Print whole message
88438a58 2233 if ($highlight) {
326ce3c4 2234 echo highlight($highlight, format_text($post->message, $post->format, $options, $courseid));
88438a58 2235 } else {
326ce3c4 2236 echo format_text($post->message, $post->format, $options, $courseid);
88438a58 2237 }
65b0e537 2238 echo $attachedimages;
501cdbd8 2239 }
2240
d3583b41 2241
0a4ac01b 2242// Commands
d3583b41 2243
359f2758 2244 $commands = array();
501cdbd8 2245
eaf50aef 2246 if ($istracked) {
0a4ac01b 2247 // SPECIAL CASE: The front page can display a news item post to non-logged in users.
2248 // Don't display the mark read / unread controls in this case.
489de4ae 2249 if ($CFG->forum_usermarksread && !empty($USER)) {
f37da850 2250 if ($post_read) {
2251 $mcmd = '&amp;mark=unread&amp;postid='.$post->id;
2252 $mtxt = $strmarkunread;
2253 } else {
2254 $mcmd = '&amp;mark=read&amp;postid='.$post->id;
2255 $mtxt = $strmarkread;
2256 }
2257 if ($threadedmode) {
d3583b41 2258 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2259 $post->discussion.'&amp;parent='.$post->id.$mcmd.'">'.$mtxt.'</a>';
f37da850 2260 } else {
d3583b41 2261 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
0be4d8bf 2262 $post->discussion.$mcmd.'#p'.$post->id.'">'.$mtxt.'</a>';
f37da850 2263 }
2264 }
2265 }
2266
2e2e71a8 2267 if ($post->parent) {
2268 if ($threadedmode) {
d3583b41 2269 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
2270 $post->discussion.'&amp;parent='.$post->parent.'">'.$strparent.'</a>';
2e2e71a8 2271 } else {
d3583b41 2272 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
0be4d8bf 2273 $post->discussion.'#p'.$post->parent.'">'.$strparent.'</a>';
2e2e71a8 2274 }
2275 }
2276
66035aaf 2277 $forumtype = get_field('forum', 'type', 'id', $post->forum);
2b63df96 2278
501cdbd8 2279 $age = time() - $post->created;
0a4ac01b 2280 // Hack for allow to edit news posts those are not displayed yet until they are displayed
fbc21e82 2281 if (!$post->parent
66035aaf 2282 && $forumtype == 'news'
fbc21e82 2283 && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) {
2284 $age = 0;
2285 }
951e1073 2286 $editanypost = has_capability('mod/forum:editanypost', $modcontext);
9fbccd39 2287
6e372b25 2288
2289
a4ea3ef3 2290 if ($ownpost or $editanypost) {
951e1073 2291 if (($age < $CFG->maxeditingtime) or $editanypost) {
d3583b41 2292 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?edit='.$post->id.'">'.$stredit.'</a>';
501cdbd8 2293 }
64eacd6f 2294 }
aaf7a9dc 2295
66035aaf 2296 if (has_capability('mod/forum:splitdiscussions', $modcontext)
2297 && $post->parent && $forumtype != 'single') {
2298
d3583b41 2299 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?prune='.$post->id.
2300 '" title="'.$strpruneheading.'">'.$strprune.'</a>';
cf84431b 2301 }
aaf7a9dc 2302
9fbccd39 2303 if (($ownpost and $age < $CFG->maxeditingtime
2304 and has_capability('mod/forum:deleteownpost', $modcontext))
2305 or has_capability('mod/forum:deleteanypost', $modcontext)) {
d3583b41 2306 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?delete='.$post->id.'">'.$strdelete.'</a>';
64eacd6f 2307 }
359f2758 2308
a4ea3ef3 2309 if ($reply) {
d3583b41 2310 $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.$strreply.'</a>';
501cdbd8 2311 }
501cdbd8 2312
d3583b41 2313 echo '<div class="commands">';
2314 echo implode(' | ', $commands);
2315 echo '</div>';
2316
2317
0a4ac01b 2318// Ratings
74f5d1e3 2319
2320 $ratingsmenuused = false;
02ebf404 2321 if (!empty($ratings) and !empty($USER->id)) {
d3583b41 2322 echo '<div class="ratings">';
98914efd 2323 $useratings = true;
2324 if ($ratings->assesstimestart and $ratings->assesstimefinish) {
2325 if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) {
2326 $useratings = false;
2327 }
2328 }
2329 if ($useratings) {
3bd98ad4 2330 $mypost = ($USER->id == $post->userid);
2b63df96 2331
951e1073 2332 $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext);
2b63df96 2333
2334 if ($canviewallratings and !$mypost) {
951e1073 2335 forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings);
d395046a 2336 if (!empty($ratings->allow)) {
d3583b41 2337 echo '&nbsp;';
d395046a 2338 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
2339 $ratingsmenuused = true;
2340 }
3bd98ad4 2341
2342 } else if ($mypost) {
2343 forum_print_ratings_mean($post->id, $ratings->scale, true);
2344
98914efd 2345 } else if (!empty($ratings->allow) ) {
2346 forum_print_rating_menu($post->id, $USER->id, $ratings->scale);
74f5d1e3 2347 $ratingsmenuused = true;
2a3cda19 2348 }
501cdbd8 2349 }
d3583b41 2350 echo '</div>';
501cdbd8 2351 }
65b0e537 2352
0a4ac01b 2353// Link to post if required
d3583b41 2354
501cdbd8 2355 if ($link) {
d3583b41 2356 echo '<div class="link">';
501cdbd8 2357 if ($post->replies == 1) {
d3583b41 2358 $replystring = get_string('repliesone', 'forum', $post->replies);
501cdbd8 2359 } else {
d3583b41 2360 $replystring = get_string('repliesmany', 'forum', $post->replies);
501cdbd8 2361 }
d3583b41 2362 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.
2363 get_string('discussthistopic', 'forum').'</a>&nbsp;('.$replystring.')';
2364 echo '</div>';
501cdbd8 2365 }
d3583b41 2366
501cdbd8 2367 if ($footer) {
d3583b41 2368 echo '<div class="footer">'.$footer.'</div>';
501cdbd8 2369 }
d3583b41 2370 echo '</td></tr></table>'."\n\n";
74f5d1e3 2371
eaf50aef 2372 if ($istracked && !$CFG->forum_usermarksread && !empty($post->forum)) {
f37da850 2373 forum_tp_mark_post_read($USER->id, $post, $post->forum);
2374 }
2375
74f5d1e3 2376 return $ratingsmenuused;
501cdbd8 2377}
42fb3c85 2378
2379
eaf50aef 2380/**
0a4ac01b 2381 * This function prints the overview of a discussion in the forum listing.
2382 * It needs some discussion information and some post information, these
2383 * happen to be combined for efficiency in the $post parameter by the function
2384 * that calls this one: forum_print_latest_discussions()
2385 *
2386 * @param object $post The post object (passed by reference for speed).
2387 * @param object $forum The forum object.
2388 * @param int $group Current group.
2389 * @param string $datestring Format to use for the dates.
2390 * @param boolean $cantrack Is tracking enabled for this forum.
2391 * @param boolean $forumtracked Is the user tracking this forum.
2392 * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
2393 */
951e1073 2394function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
18d6ef01 2395 $cantrack=true, $forumtracked=true, $canviewparticipants=true) {
43921b8a 2396
d30867b0 2397 global $USER, $CFG;
3335f6fb 2398
f51e8d7e 2399 static $rowcount;
5733262d 2400 static $strmarkalldread;
f51e8d7e 2401
2b63df96 2402
951e1073 2403 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
2404 error('Course Module ID was incorrect');
2405 }
2406 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2b63df96 2407
951e1073 2408
f51e8d7e 2409 if (!isset($rowcount)) {
2410 $rowcount = 0;
5733262d 2411 $strmarkalldread = get_string('markalldread', 'forum');
f51e8d7e 2412 } else {
2413 $rowcount = ($rowcount + 1) % 2;
2414 }
2415
17dc3f3c 2416 $post->subject = format_string($post->subject,true);
436a7cae 2417
f51e8d7e 2418 echo "\n\n";
2419 echo '<tr class="discussion r'.$rowcount.'">';
3335f6fb 2420
29507631 2421 // Topic
f51e8d7e 2422 echo '<td class="topic starter">';
2423 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
29507631 2424 echo "</td>\n";
2425
2426 // Picture
f51e8d7e 2427 echo '<td class="picture">';
a796d0b8 2428 print_user_picture($post->userid, $forum->course, $post->picture);
29507631 2429 echo "</td>\n";
3335f6fb 2430
29507631 2431 // User name
951e1073 2432 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
f51e8d7e 2433 echo '<td class="author">';
2434 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
29507631 2435 echo "</td>\n";
2436
3a68fbbb 2437 // Group picture
425b4f1a 2438 if ($group !== -1) { // Groups are active - group is a group data object or NULL
f51e8d7e 2439 echo '<td class="picture group">';
c2b552fe 2440 if (!empty($group->picture) and empty($group->hidepicture)) {
3a68fbbb 2441 print_group_picture($group, $forum->course, false, false, true);
2442 } else if (isset($group->id)) {
18d6ef01 2443 if($canviewparticipants) {
2444 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
2445 } else {
2446 echo $group->name;
2447 }
3a68fbbb 2448 }
2449 echo "</td>\n";
2450 }
2451
f0da6b85 2452 if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies
f51e8d7e 2453 echo '<td class="replies">';
2454 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
2455 echo $post->replies.'</a>';
a796d0b8 2456 echo "</td>\n";
e3ff14ca 2457
eaf50aef 2458 if ($cantrack) {
f51e8d7e 2459 echo '<td class="replies">';
eaf50aef 2460 if ($forumtracked) {
2461 if ($post->unread > 0) {
2462 echo '<span class="unread">';
2463 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
2464 echo $post->unread;
2465 echo '</a>';
c39748f4 2466 echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
eaf50aef 2467 $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
0d905d9f 2468 '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
eaf50aef 2469 echo '</span>';
2470 } else {
2471 echo '<span class="read">';
eaf50aef 2472 echo $post->unread;
eaf50aef 2473 echo '</span>';
2474 }
f51e8d7e 2475 } else {
343af274 2476 echo '<span class="read">';
eaf50aef 2477 echo '-';
343af274 2478 echo '</span>';
3a68fbbb 2479 }
f37da850 2480 echo "</td>\n";
2481 }
a796d0b8 2482 }
3335f6fb 2483
f51e8d7e 2484 echo '<td class="lastpost">';
43921b8a 2485 $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case
839f2456 2486 $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
098d27d4 2487 $usermodified->id = $post->usermodified;
268c6485 2488 $usermodified->firstname = $post->umfirstname;
2489 $usermodified->lastname = $post->umlastname;
f51e8d7e 2490 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
2491 fullname($usermodified).'</a><br />';
ac00b904 2492 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
43921b8a 2493 userdate($usedate, $datestring).'</a>';
29507631 2494 echo "</td>\n";
2495
f51e8d7e 2496 echo "</tr>\n\n";
3335f6fb 2497
3335f6fb 2498}
2499
2500
0a4ac01b 2501/**
2502 * Given a post object that we already know has a long message
2503 * this function truncates the message nicely to the first
2504 * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
2505 */
aa153f29 2506function forum_shorten_post($message) {
5be7800c 2507
2508 global $CFG;
c585fa17 2509
2510 $i = 0;
2511 $tag = false;
2512 $length = strlen($message);
2513 $count = 0;
2514 $stopzone = false;
2515 $truncate = 0;
2516
2517 for ($i=0; $i<$length; $i++) {
a8afb411 2518 $char = $message[$i];
c585fa17 2519
2520 switch ($char) {
65b0e537 2521 case "<":
c585fa17 2522 $tag = true;
2523 break;
65b0e537 2524 case ">":
c585fa17 2525 $tag = false;
2526 break;
2527 default:
2528 if (!$tag) {
2529 if ($stopzone) {
67f0b4cc 2530 if ($char == ".") {
a8afb411 2531 $truncate = $i+1;
c585fa17 2532 break 2;
2533 }
2534 }
2535 $count++;
2536 }
a8afb411 2537 break;
c585fa17 2538 }
2539 if (!$stopzone) {
5be7800c 2540 if ($count > $CFG->forum_shortpost) {
c585fa17 2541 $stopzone = true;
2542 }
2543 }
2544 }
aa153f29 2545
c585fa17 2546 if (!$truncate) {
a8afb411 2547 $truncate = $i;
c585fa17 2548 }
2549
67f0b4cc 2550 return substr($message, 0, $truncate);
aa153f29 2551}
2552
501cdbd8 2553
0a4ac01b 2554/**
2555 * Print the multiple ratings on a post given to the current user by others.
2556 * Scale is an array of ratings
2557 */
3bd98ad4 2558function forum_print_ratings_mean($postid, $scale, $link=true) {
02ebf404 2559
2560 static $strrate;
05c47ef7 2561
2562 $mean = forum_get_ratings_mean($postid, $scale);
65b0e537 2563
05c47ef7 2564 if ($mean !== "") {
02ebf404 2565
2566 if (empty($strratings)) {
2567 $strratings = get_string("ratings", "forum");
501cdbd8 2568 }
501cdbd8 2569
02ebf404 2570 echo "$strratings: ";
3bd98ad4 2571 if ($link) {
2572 link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600);
2573 } else {
2574 echo "$mean ";
2575 }
501cdbd8 2576 }
2577}
2578
501cdbd8 2579
0a4ac01b 2580/**
2581 * Return the mean rating of a post given to the current user by others.
2582 * Scale is an array of possible ratings in the scale
2583 * Ratings is an optional simple array of actual ratings (just integers)
2584 */
0761d83f 2585function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
02ebf404 2586
2587 if (!$ratings) {
2588 $ratings = array();
2589 if ($rates = get_records("forum_ratings", "post", $postid)) {
2590 foreach ($rates as $rate) {
2591 $ratings[] = $rate->rating;
2592 }
501cdbd8 2593 }
501cdbd8 2594 }
02ebf404 2595
0761d83f 2596 $count = count($ratings);
2597
2598 if ($count == 0) {
02ebf404 2599 return "";
02ebf404 2600
0761d83f 2601 } else if ($count == 1) {
2602 return $scale[$ratings[0]];
2603
02ebf404 2604 } else {
0761d83f 2605 $total = 0;
2606 foreach ($ratings as $rating) {
2607 $total += $rating;
2608 }
2609 $mean = round( ((float)$total/(float)$count) + 0.001); // Little fudge factor so that 0.5 goes UP
65b0e537 2610
0761d83f 2611 if (isset($scale[$mean])) {
2612 return $scale[$mean]." ($count)";
2613 } else {
2614 return "$mean ($count)"; // Should never happen, hopefully
2615 }
02ebf404 2616 }
2617}
2618
0a4ac01b 2619/**
2620 * Return a summary of post ratings given to the current user by others.
2621 * Scale is an array of possible ratings in the scale
2622 * Ratings is an optional simple array of actual ratings (just integers)
2623 */
02ebf404 2624function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
02ebf404 2625
2626 if (!$ratings) {
2627 $ratings = array();
2628 if ($rates = get_records("forum_ratings", "post", $postid)) {
2629 foreach ($rates as $rate) {
2630 $rating[] = $rate->rating;
2631 }
2632 }
2633 }
2634
2635
2636 if (!$count = count($ratings)) {
2637 return "";
2638 }
2639
2640
2641 foreach ($scale as $key => $scaleitem) {
2642 $sumrating[$key] = 0;
2643 }
2644
2645 foreach ($ratings as $rating) {
2646 $sumrating[$rating]++;
2647 }
2648
2649 $summary = "";
2650 foreach ($scale as $key => $scaleitem) {
2651 $summary = $sumrating[$key].$summary;
2652 if ($key > 1) {
2653 $summary = "/$summary";
2654 }
2655 }
2656 return $summary;
2657}
2658
0a4ac01b 2659/**
2660 * Print the menu of ratings as part of a larger form.
2661 * If the post has already been - set that value.
2662 * Scale is an array of ratings
2663 */
02ebf404 2664function forum_print_rating_menu($postid, $userid, $scale) {
02ebf404 2665
2666 static $strrate;
2667
2668 if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
1a7cdb11 2669 $rating->rating = FORUM_UNSET_POST_RATING;
02ebf404 2670 }
2671
2672 if (empty($strrate)) {
2673 $strrate = get_string("rate", "forum");
2674 }
1a7cdb11 2675 $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale;
2676 choose_from_menu($scale, $postid, $rating->rating, '');
501cdbd8 2677}
2678
83da3d28 2679/**
2680 * Print the drop down that allows the user to select how they want to have
2681 * the discussion displayed.
2682 * @param $id - forum id if $forumtype is 'single',
2683 * discussion id for any other forum type
2684 * @param $mode - forum layout mode
2685 * @param $forumtype - optional
2686 */
2687function forum_print_mode_form($id, $mode, $forumtype='') {
f0ef40c5 2688 global $FORUM_LAYOUT_MODES;
501cdbd8 2689
83da3d28 2690 if ($forumtype == 'single') {
2691 popup_form("view.php?f=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2692 } else {
2693 popup_form("discuss.php?d=$id&amp;mode=", $FORUM_LAYOUT_MODES, "mode", $mode, "");
2694 }
501cdbd8 2695}
2696
0a4ac01b 2697/**
2698 * TODO document
2699 */
6f1cc8d6 2700function forum_search_form($course, $search='') {
501cdbd8 2701 global $CFG;
2702
81d22774 2703 $output = '<div class="forumsearch">';
2704 $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
d69e9c15 2705 $output .= '<fieldset class="invisiblefieldset">';
bbbf2d40 2706 $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
6f1cc8d6 2707 $output .= '<input name="search" type="text" size="18" value="'.$search.'" alt="search" />';
2708 $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
2709 $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
81d22774 2710 $output .= '</fieldset>';
6f1cc8d6 2711 $output .= '</form>';
81d22774 2712 $output .= '</div>';
5e367a2d 2713
6f1cc8d6 2714 return $output;
501cdbd8 2715}
2716
2717
0a4ac01b 2718/**
2719 * TODO document
2720 */
11b0c469 2721function forum_set_return() {
607809b3 2722 global $CFG, $SESSION;
501cdbd8 2723
28e1e8b9 2724 if (! isset($SESSION->fromdiscussion)) {
48d38fad 2725 if (!empty($_SERVER['HTTP_REFERER'])) {
2726 $referer = $_SERVER['HTTP_REFERER'];
2727 } else {
2728 $referer = "";
2729 }
28e1e8b9 2730 // If the referer is NOT a login screen then save it.
48d38fad 2731 if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
607809b3 2732 $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
28e1e8b9 2733 }
501cdbd8 2734 }
2735}
2736
2737
0a4ac01b 2738/**
2739 * TODO document
2740 */
11b0c469 2741function forum_go_back_to($default) {
501cdbd8 2742 global $SESSION;
2743
9c9f7d77 2744 if (!empty($SESSION->fromdiscussion)) {
501cdbd8 2745 $returnto = $SESSION->fromdiscussion;
2746 unset($SESSION->fromdiscussion);
2747 return $returnto;
2748 } else {
2749 return $default;
2750 }
2751}
2752
0a4ac01b 2753/**
2754 * Creates a directory file name, suitable for make_upload_directory()
2755 */
7f6689e4 2756function forum_file_area_name($post) {
7f6689e4 2757 global $CFG;
2758
2759 return "$post->course/$CFG->moddata/forum/$post->forum/$post->id";
2760}
2761
0a4ac01b 2762/**
2763 * TODO document
2764 */
7f6689e4 2765function forum_file_area($post) {
2766 return make_upload_directory( forum_file_area_name($post) );
2767}
2768
0a4ac01b 2769/**
2770 * TODO document
2771 */
7f6689e4 2772function forum_delete_old_attachments($post, $exception="") {
7f6689e4 2773
0a4ac01b 2774/**
2775 * Deletes all the user files in the attachments area for a post
2776 * EXCEPT for any file named $exception
2777 */
7f6689e4 2778 if ($basedir = forum_file_area($post)) {
2779 if ($files = get_directory_list($basedir)) {
2780 foreach ($files as $file) {
2781 if ($file != $exception) {
2782 unlink("$basedir/$file");
2783 notify("Existing file '$file' has been deleted!");
2784 }
2785 }
2786 }
2787 if (!$exception) { // Delete directory as well, if empty
2788 rmdir("$basedir");
2789 }
2790 }
2791}
2792
0a4ac01b 2793/**
2794 * Given a discussion object that is being moved to forumid,
2795 * this function checks all posts in that discussion
2796 * for attachments, and if any are found, these are
2797 * moved to the new forum directory.
2798 */
cc2b7ea5 2799function forum_move_attachments($discussion, $forumid) {
cc2b7ea5 2800
2801 global $CFG;
2802
18b8fbfa 2803 require_once($CFG->dirroot.'/lib/uploadlib.php');
2804
cc2b7ea5 2805 $return = true;
2806
2807 if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
2808 foreach ($posts as $oldpost) {
2809 $oldpost->course = $discussion->course;
2810 $oldpost->forum = $discussion->forum;
2811 $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
2812 if (is_dir($oldpostdir)) {
2813 $newpost = $oldpost;
2814 $newpost->forum = $forumid;
03c9562b 2815 $newpostdir = forum_file_area_name($newpost);
2816 // take off the last directory because otherwise we're renaming to a directory that already exists
2817 // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
2818 make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
2819 $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost);
18b8fbfa 2820 $files = get_directory_list($oldpostdir); // get it before we rename it.
cc2b7ea5 2821 if (! @rename($oldpostdir, $newpostdir)) {
2822 $return = false;
2823 }
18b8fbfa 2824 foreach ($files as $file) {
2825 clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
2826 }
cc2b7ea5 2827 }
2828 }
2829 }
2830 return $return;
2831}
2832
0a4ac01b 2833/**
2834 * if return=html, then return a html string.
2835 * if return=text, then return a text-only string.
2836 * otherwise, print HTML for non-images, and return image HTML
2837 */
7f6689e4 2838function forum_print_attachments($post, $return=NULL) {
7f6689e4 2839
2840 global $CFG;
2841
2842 $filearea = forum_file_area_name($post);
2843
72d497d4 2844 $imagereturn = "";
2845 $output = "";
2846
7f6689e4 2847 if ($basedir = forum_file_area($post)) {
2848 if ($files = get_directory_list($basedir)) {
2849 $strattachment = get_string("attachment", "forum");
2850 foreach ($files as $file) {
2851 $icon = mimeinfo("icon", $file);
feaf5d06 2852 $type = mimeinfo("type", $file);
7f6689e4 2853 if ($CFG->slasharguments) {
2a1975cb 2854 $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
7f6689e4 2855 } else {
2a1975cb 2856 $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
7f6689e4 2857 }
0d905d9f 2858 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
7f6689e4 2859
2860 if ($return == "html") {
2a1975cb 2861 $output .= "<a href=\"$ffurl\">$image</a> ";
2862 $output .= "<a href=\"$ffurl\">$file</a><br />";
7f6689e4 2863
2864 } else if ($return == "text") {
2a1975cb 2865 $output .= "$strattachment $file:\n$ffurl\n";
7f6689e4 2866
2867 } else {
feaf5d06 2868 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
2a1975cb 2869 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
72d497d4 2870 } else {
2a1975cb 2871 echo "<a href=\"$ffurl\">$image</a> ";
bce7a133 2872 echo filter_text("<a href=\"$ffurl\">$file</a><br />");
72d497d4 2873 }
7f6689e4 2874 }
2875 }
2876 }
2877 }
72d497d4 2878
7f6689e4 2879 if ($return) {
2880 return $output;
2881 }
72d497d4 2882
2883 return $imagereturn;
7f6689e4 2884}
db290a6e 2885/**
e3ff14ca 2886 * If successful, this function returns the name of the file
db290a6e 2887 * @param $post is a full post record, including course and forum
2888 * @param $newfile is a full upload array from $_FILES
2889 * @param $message is a string to hold the messages.
e3ff14ca 2890 */
7f6689e4 2891
0a4ac01b 2892/**
2893 * TODO document
2894 */
db290a6e 2895function forum_add_attachment($post, $inputname,&$message) {
7f6689e4 2896
9dd0b378 2897 global $CFG;
2898
4909e176 2899 if (!$forum = get_record("forum", "id", $post->forum)) {
2900 return "";
2901 }
2902
2903 if (!$course = get_record("course", "id", $forum->course)) {
2904 return "";
2905 }
2906
5ddaa2e2 2907 require_once($CFG->dirroot.'/lib/uploadlib.php');
db290a6e 2908 $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true);
5ddaa2e2 2909 $dir = forum_file_area_name($post);
2910 if ($um->process_file_uploads($dir)) {
db290a6e 2911 $message .= $um->get_errors();
5ddaa2e2 2912 return $um->get_new_filename();
7f6689e4 2913 }
db290a6e 2914 $message .= $um->get_errors();
feaf5d06 2915 return null;
7f6689e4 2916}
501cdbd8 2917
0a4ac01b 2918/**
2919 * TODO document
2920 */
db290a6e 2921function forum_add_new_post($post,&$message) {
501cdbd8 2922
f37da850 2923 global $USER, $CFG;
e3ff14ca 2924
ffe11640 2925 $post->created = $post->modified = time();
501cdbd8 2926 $post->mailed = "0";
a56f0d60 2927 $post->userid = $USER->id;
7f6689e4 2928 $post->attachment = "";
2929
65b0e537 2930 if (! $post->id = insert_record("forum_posts", $post)) {
7f6689e4 2931 return false;
2932 }
2933
db290a6e 2934 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
7f6689e4 2935 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id);
2936 }
29507631 2937
2938 // Update discussion modified date
2939 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
016cd6af 2940 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
65b0e537 2941
eaf50aef 2942 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
f37da850 2943 forum_tp_mark_post_read($post->userid, $post, $post->forum);
2944 }
2945
7f6689e4 2946 return $post->id;
501cdbd8 2947}
2948
0a4ac01b 2949/**
2950 * TODO document
2951 */
db290a6e 2952function forum_update_post($post,&$message) {
501cdbd8 2953
f37da850 2954 global $USER, $CFG;
a56f0d60 2955
ffe11640 2956 $post->modified = time();
0936de64 2957
0ab85112 2958 if (!$post->parent) { // Post is a discussion starter - update discussion title too
2959 set_field("forum_discussions", "name", $post->subject, "id", $post->discussion);
2960 }
29507631 2961
db290a6e 2962 if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
7f6689e4 2963 $post->attachment = $newfilename;
2964 } else {
2965 unset($post->attachment);
2966 }
29507631 2967
2968 // Update discussion modified date
2969 set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
016cd6af 2970 set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
29507631 2971
eaf50aef 2972 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
f37da850 2973 forum_tp_mark_post_read($post->userid, $post, $post->forum);
2974 }
2975
ffe11640 2976 return update_record("forum_posts", $post);
501cdbd8 2977}
2978
0a4ac01b 2979/**
2980 * Given an object containing all the necessary data,
2981 * create a new discussion and return the id
2982 */
db290a6e 2983function forum_add_discussion($discussion,&$message) {
501cdbd8 2984
f37da850 2985 GLOBAL $USER, $CFG;
501cdbd8 2986
2987 $timenow = time();
2988
65b0e537 2989 // The first post is stored as a real post, and linked
501cdbd8 2990 // to from the discuss entry.
2991
2992 $post->discussion = 0;
2993 $post->parent = 0;
ebc3bd2b 2994 $post->userid = $USER->id;
501cdbd8 2995 $post->created = $timenow;
2996 $post->modified = $timenow;
2997 $post->mailed = 0;
2998 $post->subject = $discussion->name;
2999 $post->message = $discussion->intro;
7f6689e4 3000 $post->attachment = "";
3001 $post->forum = $discussion->forum;
3002 $post->course = $discussion->course;
9f0b8269 3003 $post->format = $discussion->format;
41547057 3004 $post->mailnow = $discussion->mailnow;
501cdbd8 3005
3006 if (! $post->id = insert_record("forum_posts", $post) ) {
3007 return 0;
3008 }
3009
db290a6e 3010 if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
7f6689e4 3011 set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors
3012 }
3013
65b0e537 3014 // Now do the main entry for the discussion,
02509fe6 3015 // linking to this first post
04eba58f 3016
caadf009 3017 $discussion->firstpost = $post->id;
3018 $discussion->timemodified = $timenow;
016cd6af 3019 $discussion->usermodified = $post->userid;
a56f0d60 3020 $discussion->userid = $USER->id;
04eba58f 3021
4e00a4d9 3022 if (! $post->discussion = insert_record("forum_discussions", $discussion) ) {
caadf009 3023 delete_records("forum_posts", "id", $post->id);
3024 return 0;
04eba58f 3025 }
3026
caadf009 3027 // Finally, set the pointer on the post.
4e00a4d9 3028 if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) {
caadf009 3029 delete_records("forum_posts", "id", $post->id);
4e00a4d9 3030 delete_records("forum_discussions", "id", $post->discussion);
caadf009 3031 return 0;
04eba58f 3032 }
04eba58f 3033
eaf50aef 3034 if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) {
f37da850 3035 forum_tp_mark_post_read($post->userid, $post, $post->forum);
3036 }
3037
4e00a4d9 3038 return $post->discussion;
caadf009 3039}
04eba58f 3040
04eba58f 3041
0a4ac01b 3042/**
3043 * TODO document
3044 */
e2c1dbe9 3045function forum_delete_discussion($discussion, $fulldelete=false) {
caadf009 3046// $discussion is a discussion record object
04eba58f 3047
3048 $result = true;
3049
caadf009 3050 if ($posts = get_records("forum_posts", "discussion", $discussion->id)) {
3051 foreach ($posts as $post) {
3052 $post->course = $discussion->course;
3053 $post->forum = $discussion->forum;
3054 if (! delete_records("forum_ratings", "post", "$post->id")) {
3055 $result = false;
3056 }
e2c1dbe9 3057 if (! forum_delete_post($post, $fulldelete)) {
04eba58f 3058 $result = false;
3059 }
3060 }
3061 }
3062
f37da850 3063 forum_tp_delete_read_records(-1, -1, $discussion->id);
3064
caadf009 3065 if (! delete_records("forum_discussions", "id", "$discussion->id")) {
04eba58f 3066 $result = false;
3067 }
3068
3069 return $result;
3070}
3071
3072
0a4ac01b 3073/**
3074 * TODO document
3075 */
b82faacd 3076function forum_delete_post($post, $children=false) {
1da8c568 3077 if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
3078 if ($children) {
b82faacd 3079 foreach ($childposts as $childpost) {
3080 forum_delete_post($childpost, true);
3081 }
1da8c568 3082 } else {
3083 return false;
b82faacd 3084 }
3085 }
caadf009 3086 if (delete_records("forum_posts", "id", $post->id)) {
3087 delete_records("forum_ratings", "post", $post->id); // Just in case
f37da850 3088
b82faacd 3089 forum_tp_delete_read_records(-1, $post->id);
f37da850 3090
caadf009 3091 if ($post->attachment) {
3092 $discussion = get_record("forum_discussions", "id", $post->discussion);