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