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