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