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