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