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