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