mod-forum MDL-22220 Added CSS classes for better for post styling
[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;
638 $eventdata->smallmessage = '';
3b120e46 639
505ab5aa
AD
640 $mailresult = message_send($eventdata);
641 if (!$mailresult){
642 mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id $post->id to user $userto->id".
4dad2828 643 " ($userto->email) .. not trying again.");
644 add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
645 substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
646 $errorcount[$post->id]++;
647 } else if ($mailresult === 'emailstop') {
a974c799 648 // should not be reached anymore - see check above
505ab5aa 649 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 650 } else {
651 $mailcount[$post->id]++;
652
0a4ac01b 653 // Mark post as read if forum_usermarksread is set off
90f4745c 654 if (!$CFG->forum_usermarksread) {
655 $userto->markposts[$post->id] = $post->id;
caadf009 656 }
aaf7a9dc 657 }
4dad2828 658
659 mtrace('post '.$post->id. ': '.$post->subject);
aaf7a9dc 660 }
90f4745c 661
662 // mark processed posts as read
663 forum_tp_mark_posts_read($userto, $userto->markposts);
5fac3a5e 664 }
665 }
666
667 if ($posts) {
668 foreach ($posts as $post) {
16b4e5b6 669 mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
5fac3a5e 670 if ($errorcount[$post->id]) {
4e445355 671 $DB->set_field("forum_posts", "mailed", "2", array("id" => "$post->id"));
a974c799 672 }
aaf7a9dc 673 }
674 }
675
8cb121cc 676 // release some memory
677 unset($subscribedusers);
678 unset($mailcount);
679 unset($errorcount);
680
e8b7114d 681 cron_setup_user();
ad9ff3d3 682
9152fc99 683 $sitetimezone = $CFG->timezone;
944a2b28 684
0a4ac01b 685 // Now see if there are any digest mails waiting to be sent, and if we should send them
aaf7a9dc 686
f3c3a4d3 687 mtrace('Starting digest processing...');
688
910b6fa7 689 @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes
690
8f0cd6ef 691 if (!isset($CFG->digestmailtimelast)) { // To catch the first time
ca8e8a10 692 set_config('digestmailtimelast', 0);
693 }
694
695 $timenow = time();
944a2b28 696 $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
ca8e8a10 697
f3c3a4d3 698 // Delete any really old ones (normally there shouldn't be any)
699 $weekago = $timenow - (7 * 24 * 3600);
2ac897cd 700 $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));
910b6fa7 701 mtrace ('Cleaned old digest records');
9f2ded76 702
ca8e8a10 703 if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
b140ae85 704
b140ae85 705 mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
706
4e445355 707 $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime));
910b6fa7 708
4e445355 709 if ($digestposts_rs->valid()) {
8ad64455 710
aaf7a9dc 711 // We have work to do
712 $usermailcount = 0;
aaf7a9dc 713
a974c799 714 //caches - reuse the those filled before too
aaf7a9dc 715 $discussionposts = array();
716 $userdiscussions = array();
a974c799 717
4e445355 718 foreach ($digestposts_rs as $digestpost) {
a974c799 719 if (!isset($users[$digestpost->userid])) {
4e445355 720 if ($user = $DB->get_record('user', array('id' => $digestpost->userid))) {
a974c799 721 $users[$digestpost->userid] = $user;
722 } else {
723 continue;
724 }
725 }
726 $postuser = $users[$digestpost->userid];
727 if ($postuser->emailstop) {
f4528bdc 728 if (!empty($CFG->forum_logblocked)) {
729 add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
730 }
a974c799 731 continue;
732 }
733
734 if (!isset($posts[$digestpost->postid])) {
4e445355 735 if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
a974c799 736 $posts[$digestpost->postid] = $post;
737 } else {
738 continue;
739 }
740 }
741 $discussionid = $digestpost->discussionid;
742 if (!isset($discussions[$discussionid])) {
4e445355 743 if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) {
a974c799 744 $discussions[$discussionid] = $discussion;
745 } else {
746 continue;
747 }
aaf7a9dc 748 }
a974c799 749 $forumid = $discussions[$discussionid]->forum;
750 if (!isset($forums[$forumid])) {
4e445355 751 if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
a974c799 752 $forums[$forumid] = $forum;
753 } else {
754 continue;
755 }
756 }
757
758 $courseid = $forums[$forumid]->course;
759 if (!isset($courses[$courseid])) {
4e445355 760 if ($course = $DB->get_record('course', array('id' => $courseid))) {
a974c799 761 $courses[$courseid] = $course;
762 } else {
763 continue;
764 }
aaf7a9dc 765 }
a974c799 766
767 if (!isset($coursemodules[$forumid])) {
768 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
769 $coursemodules[$forumid] = $cm;
770 } else {
771 continue;
772 }
aaf7a9dc 773 }
774 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
775 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
776 }
4e445355 777 $digestposts_rs->close(); /// Finished iteration, let's close the resultset
aaf7a9dc 778
779 // Data collected, start sending out emails to each user
a974c799 780 foreach ($userdiscussions as $userid => $thesediscussions) {
aaf7a9dc 781
a974c799 782 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
aaf7a9dc 783
e8b7114d 784 cron_setup_user();
90f4745c 785
a974c799 786 mtrace(get_string('processingdigest', 'forum', $userid), '... ');
aaf7a9dc 787
788 // First of all delete all the queue entries for this user
4e445355 789 $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));
aaf7a9dc 790 $userto = $users[$userid];
791
0a4ac01b 792 // Override the language and timezone of the "current" user, so that
793 // mail is customised for the receiver.
e8b7114d 794 cron_setup_user($userto);
aaf7a9dc 795
df1c2c71 796 // init caches
797 $userto->viewfullnames = array();
798 $userto->canpost = array();
90f4745c 799 $userto->markposts = array();
df1c2c71 800
a974c799 801 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
aaf7a9dc 802
39790bd8 803 $headerdata = new stdClass();
a974c799 804 $headerdata->sitename = format_string($site->fullname, true);
839f2456 805 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
aaf7a9dc 806
807 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
808 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
9c674431 809
78c0d909 810 $posthtml = "<head>";
78946b9b
PS
811/* foreach ($CFG->stylesheets as $stylesheet) {
812 //TODO: MDL-21120
78c0d909 813 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
78946b9b 814 }*/
449b1c1a 815 $posthtml .= "</head>\n<body id=\"email\">\n";
a0330747 816 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
aaf7a9dc 817
a974c799 818 foreach ($thesediscussions as $discussionid) {
aaf7a9dc 819
0a4ac01b 820 @set_time_limit(120); // to be reset for each post
a974c799 821
822 $discussion = $discussions[$discussionid];
823 $forum = $forums[$discussion->forum];
824 $course = $courses[$forum->course];
825 $cm = $coursemodules[$forum->id];
65b0e537 826
9152fc99 827 //override language
e8b7114d 828 cron_setup_user($userto, $course);
9152fc99 829
df1c2c71 830 // Fill caches
831 if (!isset($userto->viewfullnames[$forum->id])) {
832 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
833 $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
834 }
8b79a625 835 if (!isset($userto->canpost[$discussion->id])) {
df1c2c71 836 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
8b79a625 837 $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
df1c2c71 838 }
caadf009 839
de85c320 840 $strforums = get_string('forums', 'forum');
841 $canunsubscribe = ! forum_is_forcesubscribed($forum);
8b79a625 842 $canreply = $userto->canpost[$discussion->id];
de85c320 843
aaf7a9dc 844 $posttext .= "\n \n";
845 $posttext .= '=====================================================================';
846 $posttext .= "\n \n";
3849dae8 847 $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
aaf7a9dc 848 if ($discussion->name != $forum->name) {
c78ac798 849 $posttext .= " -> ".format_string($discussion->name,true);
caadf009 850 }
aaf7a9dc 851 $posttext .= "\n";
65b0e537 852
aaf7a9dc 853 $posthtml .= "<p><font face=\"sans-serif\">".
854 "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
855 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
3849dae8 856 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
aaf7a9dc 857 if ($discussion->name == $forum->name) {
858 $posthtml .= "</font></p>";
caadf009 859 } else {
c78ac798 860 $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
caadf009 861 }
aaf7a9dc 862 $posthtml .= '<p>';
863
e1c6dde1 864 $postsarray = $discussionposts[$discussionid];
865 sort($postsarray);
866
857b798b 867 foreach ($postsarray as $postid) {
a974c799 868 $post = $posts[$postid];
869
870 if (array_key_exists($post->userid, $users)) { // we might know him/her already
871 $userfrom = $users[$post->userid];
4e445355 872 } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
df1c2c71 873 $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
874 } else {
a974c799 875 mtrace('Could not find user '.$post->userid);
aaf7a9dc 876 continue;
877 }
878
df1c2c71 879 if (!isset($userfrom->groups[$forum->id])) {
880 if (!isset($userfrom->groups)) {
881 $userfrom->groups = array();
882 $users[$userfrom->id]->groups = array();
883 }
884 $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
885 $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
886 }
887
857b798b 888 $userfrom->customheaders = array ("Precedence: Bulk");
889
890 if ($userto->maildigest == 2) {
aaf7a9dc 891 // Subjects only
39790bd8 892 $by = new stdClass();
aaf7a9dc 893 $by->name = fullname($userfrom);
894 $by->date = userdate($post->modified);
17dc3f3c 895 $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
aaf7a9dc 896 $posttext .= "\n---------------------------------------------------------------------";
897
839f2456 898 $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
0be4d8bf 899 $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 900
901 } else {
aaf7a9dc 902 // The full treatment
0faf6791 903 $posttext .= forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, true);
904 $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
df1c2c71 905
0a4ac01b 906 // Create an array of postid's for this user to mark as read.
90f4745c 907 if (!$CFG->forum_usermarksread) {
908 $userto->markposts[$post->id] = $post->id;
f37da850 909 }
aaf7a9dc 910 }
911 }
912 if ($canunsubscribe) {
ff9b4ea4 913 $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 914 } else {
ff9b4ea4 915 $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
aaf7a9dc 916 }
917 $posthtml .= '<hr size="1" noshade="noshade" /></p>';
caadf009 918 }
a0330747 919 $posthtml .= '</body>';
caadf009 920
6d2e6936 921 if (empty($userto->mailformat) || $userto->mailformat != 1) {
379a42cb 922 // This user DOESN'T want to receive HTML
923 $posthtml = '';
924 }
5e7f2b0b 925
b58bd1a2
AD
926 $attachment = $attachname='';
927 $usetrueaddress = true;
eac02096 928 $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname, $usetrueaddress, $CFG->forum_replytouser);
b58bd1a2
AD
929
930 if (!$mailresult) {
b140ae85 931 mtrace("ERROR!");
aaf7a9dc 932 echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
933 add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
b6268a0e 934 } else if ($mailresult === 'emailstop') {
a974c799 935 // should not happen anymore - see check above
aaf7a9dc 936 } else {
b140ae85 937 mtrace("success.");
aaf7a9dc 938 $usermailcount++;
e3ff14ca 939
90f4745c 940 // Mark post as read if forum_usermarksread is set off
941 forum_tp_mark_posts_read($userto, $userto->markposts);
3d94772d 942 }
caadf009 943 }
caadf009 944 }
226a1d9d 945 /// We have finishied all digest emails, update $CFG->digestmailtimelast
946 set_config('digestmailtimelast', $timenow);
caadf009 947 }
948
e8b7114d 949 cron_setup_user();
de85c320 950
a974c799 951 if (!empty($usermailcount)) {
b140ae85 952 mtrace(get_string('digestsentusers', 'forum', $usermailcount));
aaf7a9dc 953 }
954
8ad64455 955 if (!empty($CFG->forum_lastreadclean)) {
f37da850 956 $timenow = time();
8ad64455 957 if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
958 set_config('forum_lastreadclean', $timenow);
8cb121cc 959 mtrace('Removing old forum read tracking info...');
f37da850 960 forum_tp_clean_read_records();
961 }
962 } else {
8ad64455 963 set_config('forum_lastreadclean', time());
f37da850 964 }
965
966
caadf009 967 return true;
968}
969
0a4ac01b 970/**
1670305d 971 * Builds and returns the body of the email notification in plain text.
972 *
df1ba0f4 973 * @global object
974 * @global object
975 * @uses CONTEXT_MODULE
1670305d 976 * @param object $course
df1ba0f4 977 * @param object $cm
1670305d 978 * @param object $forum
979 * @param object $discussion
980 * @param object $post
981 * @param object $userfrom
982 * @param object $userto
983 * @param boolean $bare
984 * @return string The email body in plain text format.
0a4ac01b 985 */
0faf6791 986function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
15f81ee3 987 global $CFG, $USER;
2b63df96 988
df1c2c71 989 if (!isset($userto->viewfullnames[$forum->id])) {
df1c2c71 990 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
991 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
992 } else {
993 $viewfullnames = $userto->viewfullnames[$forum->id];
994 }
995
8b79a625 996 if (!isset($userto->canpost[$discussion->id])) {
997 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
998 $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
df1c2c71 999 } else {
8b79a625 1000 $canreply = $userto->canpost[$discussion->id];
0fa18d5a 1001 }
2b63df96 1002
aaf7a9dc 1003 $by = New stdClass;
df1c2c71 1004 $by->name = fullname($userfrom, $viewfullnames);
aaf7a9dc 1005 $by->date = userdate($post->modified, "", $userto->timezone);
1006
1007 $strbynameondate = get_string('bynameondate', 'forum', $by);
1008
64762ddc 1009 $strforums = get_string('forums', 'forum');
1010
a9900c73 1011 $canunsubscribe = ! forum_is_forcesubscribed($forum);
aaf7a9dc 1012
1013 $posttext = '';
1014
0fa18d5a 1015 if (!$bare) {
3849dae8 1016 $posttext = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
aaf7a9dc 1017
1018 if ($discussion->name != $forum->name) {
c78ac798 1019 $posttext .= " -> ".format_string($discussion->name,true);
aaf7a9dc 1020 }
1021 }
1022
1023 $posttext .= "\n---------------------------------------------------------------------\n";
17dc3f3c 1024 $posttext .= format_string($post->subject,true);
0fa18d5a 1025 if ($bare) {
0be4d8bf 1026 $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
aaf7a9dc 1027 }
1028 $posttext .= "\n".$strbynameondate."\n";
1029 $posttext .= "---------------------------------------------------------------------\n";
e2d7687f 1030 $posttext .= format_text_email($post->message, $post->messageformat);
aaf7a9dc 1031 $posttext .= "\n\n";
0faf6791 1032 $posttext .= forum_print_attachments($post, $cm, "text");
1033
aaf7a9dc 1034 if (!$bare && $canreply) {
1035 $posttext .= "---------------------------------------------------------------------\n";
1036 $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
1037 $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
1038 }
1039 if (!$bare && $canunsubscribe) {
1040 $posttext .= "\n---------------------------------------------------------------------\n";
1041 $posttext .= get_string("unsubscribe", "forum");
1042 $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
1043 }
1044
1045 return $posttext;
1046}
1047
0a4ac01b 1048/**
1670305d 1049 * Builds and returns the body of the email notification in html format.
1050 *
df1ba0f4 1051 * @global object
1670305d 1052 * @param object $course
df1ba0f4 1053 * @param object $cm
1670305d 1054 * @param object $forum
1055 * @param object $discussion
1056 * @param object $post
1057 * @param object $userfrom
1058 * @param object $userto
1059 * @return string The email text in HTML format
0a4ac01b 1060 */
0faf6791 1061function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto) {
aaf7a9dc 1062 global $CFG;
1063
a0288610 1064 if ($userto->mailformat != 1) { // Needs to be HTML
1065 return '';
1066 }
aaf7a9dc 1067
8b79a625 1068 if (!isset($userto->canpost[$discussion->id])) {
1069 $canreply = forum_user_can_post($forum, $discussion, $userto);
df1c2c71 1070 } else {
8b79a625 1071 $canreply = $userto->canpost[$discussion->id];
9f2ded76 1072 }
1073
a0288610 1074 $strforums = get_string('forums', 'forum');
a9900c73 1075 $canunsubscribe = ! forum_is_forcesubscribed($forum);
aaf7a9dc 1076
a0288610 1077 $posthtml = '<head>';
78946b9b
PS
1078/* foreach ($CFG->stylesheets as $stylesheet) {
1079 //TODO: MDL-21120
a0288610 1080 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
78946b9b 1081 }*/
a0288610 1082 $posthtml .= '</head>';
f2379d2d 1083 $posthtml .= "\n<body id=\"email\">\n\n";
aaf7a9dc 1084
f2379d2d 1085 $posthtml .= '<div class="navbar">'.
a0288610 1086 '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
1087 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
1088 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
1089 if ($discussion->name == $forum->name) {
1090 $posthtml .= '</div>';
aaf7a9dc 1091 } else {
a0288610 1092 $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
1093 format_string($discussion->name,true).'</a></div>';
aaf7a9dc 1094 }
0faf6791 1095 $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
a0288610 1096
1097 if ($canunsubscribe) {
85db96c5 1098 $posthtml .= '<hr /><div class="mdl-align unsubscribelink">
739da48c 1099 <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
1100 <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
a0288610 1101 }
1102
f2379d2d 1103 $posthtml .= '</body>';
1104
a0288610 1105 return $posthtml;
aaf7a9dc 1106}
1670305d 1107
1108
0a4ac01b 1109/**
13bbe067 1110 *
1670305d 1111 * @param object $course
1112 * @param object $user
1113 * @param object $mod TODO this is not used in this function, refactor
1114 * @param object $forum
1115 * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
0a4ac01b 1116 */
caadf009 1117function forum_user_outline($course, $user, $mod, $forum) {
1a96363a
NC
1118 global $CFG;
1119 require_once("$CFG->libdir/gradelib.php");
1120 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
1121 if (empty($grades->items[0]->grades)) {
1122 $grade = false;
1123 } else {
1124 $grade = reset($grades->items[0]->grades);
1125 }
1126
1127 $count = forum_count_user_posts($forum->id, $user->id);
1128
1129 if ($count && $count->postcount > 0) {
39790bd8 1130 $result = new stdClass();
1a96363a
NC
1131 $result->info = get_string("numposts", "forum", $count->postcount);
1132 $result->time = $count->lastpost;
1133 if ($grade) {
1134 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
90f4745c 1135 }
1a96363a
NC
1136 return $result;
1137 } else if ($grade) {
39790bd8 1138 $result = new stdClass();
1a96363a
NC
1139 $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
1140 $result->time = $grade->dategraded;
1141 return $result;
caadf009 1142 }
1143 return NULL;
1144}
1145
1146
0a4ac01b 1147/**
df1ba0f4 1148 * @global object
1149 * @global object
1150 * @param object $coure
1151 * @param object $user
1152 * @param object $mod
1153 * @param object $forum
0a4ac01b 1154 */
caadf009 1155function forum_user_complete($course, $user, $mod, $forum) {
1a96363a
NC
1156 global $CFG,$USER, $OUTPUT;
1157 require_once("$CFG->libdir/gradelib.php");
1158
1159 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
1160 if (!empty($grades->items[0]->grades)) {
1161 $grade = reset($grades->items[0]->grades);
1162 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
1163 if ($grade->str_feedback) {
1164 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
1165 }
1166 }
caadf009 1167
1f48942e 1168 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
e3ff14ca 1169
65bcf17b 1170 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 1171 print_error('invalidcoursemodule');
65bcf17b 1172 }
90f4745c 1173 $discussions = forum_get_user_involved_discussions($forum->id, $user->id);
65bcf17b 1174
1175 foreach ($posts as $post) {
5e7f2b0b 1176 if (!isset($discussions[$post->discussion])) {
90f4745c 1177 continue;
1178 }
7a8d8f22 1179 $discussion = $discussions[$post->discussion];
5e7f2b0b 1180
63e87951 1181 forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false);
5e7f2b0b 1182 }
caadf009 1183 } else {
41905731 1184 echo "<p>".get_string("noposts", "forum")."</p>";
caadf009 1185 }
caadf009 1186}
1187
c38965fb 1188
1189
1190
5e7f2b0b 1191
1192
0a4ac01b 1193/**
df1ba0f4 1194 * @global object
1195 * @global object
1196 * @global object
1197 * @param array $courses
1198 * @param array $htmlarray
0a4ac01b 1199 */
185cfb09 1200function forum_print_overview($courses,&$htmlarray) {
261c6ef0 1201 global $USER, $CFG, $DB, $SESSION;
493f0820 1202
185cfb09 1203 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
1204 return array();
1205 }
f8716988 1206
185cfb09 1207 if (!$forums = get_all_instances_in_courses('forum',$courses)) {
f8716988 1208 return;
1209 }
185cfb09 1210
5aa23eea 1211
f8716988 1212 // get all forum logs in ONE query (much better!)
4e445355 1213 $params = array();
1214 $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {log} l "
1215 ." JOIN {course_modules} cm ON cm.id = cmid "
185cfb09 1216 ." WHERE (";
1217 foreach ($courses as $course) {
4e445355 1218 $sql .= '(l.course = ? AND l.time > ?) OR ';
1219 $params[] = $course->id;
1220 $params[] = $course->lastaccess;
185cfb09 1221 }
1222 $sql = substr($sql,0,-3); // take off the last OR
1223
b55d0869 1224 $sql .= ") AND l.module = 'forum' AND action = 'add post' "
4e445355 1225 ." AND userid != ? GROUP BY cmid,l.course,instance";
1226
1227 $params[] = $USER->id;
2b63df96 1228
4e445355 1229 if (!$new = $DB->get_records_sql($sql, $params)) {
185cfb09 1230 $new = array(); // avoid warnings
1231 }
2b63df96 1232
185cfb09 1233 // also get all forum tracking stuff ONCE.
1234 $trackingforums = array();
1235 foreach ($forums as $forum) {
1236 if (forum_tp_can_track_forums($forum)) {
1237 $trackingforums[$forum->id] = $forum;
1238 }
1239 }
2b63df96 1240
185cfb09 1241 if (count($trackingforums) > 0) {
1242 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
1243 $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
4e445355 1244 ' FROM {forum_posts} p '.
1245 ' JOIN {forum_discussions} d ON p.discussion = d.id '.
1246 ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE (';
1247 $params = array($USER->id);
1248
d3553951 1249 foreach ($trackingforums as $track) {
4e445355 1250 $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR ';
1251 $params[] = $track->id;
261c6ef0 1252 if (isset($SESSION->currentgroup[$track->course])) {
1253 $groupid = $SESSION->currentgroup[$track->course];
1254 } else {
1255 $groupid = groups_get_all_groups($track->course, $USER->id);
1256 if (is_array($groupid)) {
1257 $groupid = array_shift(array_keys($groupid));
1258 $SESSION->currentgroup[$track->course] = $groupid;
1259 } else {
1260 $groupid = 0;
1261 }
1262 }
1263 $params[] = $groupid;
185cfb09 1264 }
1265 $sql = substr($sql,0,-3); // take off the last OR
4e445355 1266 $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course';
1267 $params[] = $cutoffdate;
185cfb09 1268
4e445355 1269 if (!$unread = $DB->get_records_sql($sql, $params)) {
185cfb09 1270 $unread = array();
1271 }
1272 } else {
1273 $unread = array();
95d71ad3 1274 }
185cfb09 1275
9cba7a8c 1276 if (empty($unread) and empty($new)) {
1277 return;
1278 }
1279
1280 $strforum = get_string('modulename','forum');
1281 $strnumunread = get_string('overviewnumunread','forum');
1282 $strnumpostssince = get_string('overviewnumpostssince','forum');
1283
f8716988 1284 foreach ($forums as $forum) {
185cfb09 1285 $str = '';
f8716988 1286 $count = 0;
185cfb09 1287 $thisunread = 0;
f8716988 1288 $showunread = false;
1289 // either we have something from logs, or trackposts, or nothing.
1290 if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
1291 $count = $new[$forum->id]->count;
90558ec4 1292 }
185cfb09 1293 if (array_key_exists($forum->id,$unread)) {
1294 $thisunread = $unread[$forum->id]->count;
f8716988 1295 $showunread = true;
0d6b9d4f 1296 }
185cfb09 1297 if ($count > 0 || $thisunread > 0) {
e23800b7 1298 $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
1299 $forum->name.'</a></div>';
1300 $str .= '<div class="info">';
1301 $str .= $count.' '.$strnumpostssince;
f8716988 1302 if (!empty($showunread)) {
e23800b7 1303 $str .= '<br />'.$thisunread .' '.$strnumunread;
f8716988 1304 }
e23800b7 1305 $str .= '</div></div>';
f8716988 1306 }
2b63df96 1307 if (!empty($str)) {
185cfb09 1308 if (!array_key_exists($forum->course,$htmlarray)) {
1309 $htmlarray[$forum->course] = array();
1310 }
1311 if (!array_key_exists('forum',$htmlarray[$forum->course])) {
1312 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
1313 }
1314 $htmlarray[$forum->course]['forum'] .= $str;
1315 }
2b63df96 1316 }
0d6b9d4f 1317}
1318
0a4ac01b 1319/**
1320 * Given a course and a date, prints a summary of all the new
1321 * messages posted in the course since that date
df1ba0f4 1322 *
1323 * @global object
1324 * @global object
1325 * @global object
1326 * @uses CONTEXT_MODULE
1327 * @uses VISIBLEGROUPS
90f4745c 1328 * @param object $course
1329 * @param bool $viewfullnames capability
1330 * @param int $timestart
1331 * @return bool success
0a4ac01b 1332 */
dd97c328 1333function forum_print_recent_activity($course, $viewfullnames, $timestart) {
cb860491 1334 global $CFG, $USER, $DB, $OUTPUT;
caadf009 1335
dd97c328 1336 // do not use log table if possible, it may be huge and is expensive to join with other tables
caadf009 1337
4e445355 1338 if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1339 d.timestart, d.timeend, d.userid AS duserid,
1340 u.firstname, u.lastname, u.email, u.picture
1341 FROM {forum_posts} p
1342 JOIN {forum_discussions} d ON d.id = p.discussion
1343 JOIN {forum} f ON f.id = d.forum
1344 JOIN {user} u ON u.id = p.userid
1345 WHERE p.created > ? AND f.course = ?
1346 ORDER BY p.id ASC", array($timestart, $course->id))) { // order by initial posting date
dd97c328 1347 return false;
1b5910c4 1348 }
1349
dd97c328 1350 $modinfo =& get_fast_modinfo($course);
dcde9f02 1351
dd97c328 1352 $groupmodes = array();
1353 $cms = array();
d05956ac 1354
dd97c328 1355 $strftimerecent = get_string('strftimerecent');
d05956ac 1356
dd97c328 1357 $printposts = array();
1358 foreach ($posts as $post) {
1359 if (!isset($modinfo->instances['forum'][$post->forum])) {
1360 // not visible
1361 continue;
1362 }
1363 $cm = $modinfo->instances['forum'][$post->forum];
1364 if (!$cm->uservisible) {
1365 continue;
1366 }
6b7de0bb 1367 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1368
1369 if (!has_capability('mod/forum:viewdiscussion', $context)) {
1370 continue;
1371 }
583b57b4 1372
dd97c328 1373 if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
1374 and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
6b7de0bb 1375 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
dd97c328 1376 continue;
ac1d9a22 1377 }
dd97c328 1378 }
583b57b4 1379
dd97c328 1380 $groupmode = groups_get_activity_groupmode($cm, $course);
b91d6dcd 1381
dd97c328 1382 if ($groupmode) {
6b7de0bb 1383 if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
dd97c328 1384 // oki (Open discussions have groupid -1)
1385 } else {
1386 // separate mode
1387 if (isguestuser()) {
1388 // shortcut
1389 continue;
1390 }
2b63df96 1391
dd97c328 1392 if (is_null($modinfo->groups)) {
1393 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
1b5910c4 1394 }
ac1d9a22 1395
dd97c328 1396 if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
1397 continue;
1398 }
ac1d9a22 1399 }
dd97c328 1400 }
8f7dc7f1 1401
dd97c328 1402 $printposts[] = $post;
1403 }
1404 unset($posts);
8f7dc7f1 1405
dd97c328 1406 if (!$printposts) {
1407 return false;
1408 }
1409
cb860491 1410 echo $OUTPUT->heading(get_string('newforumposts', 'forum').':', 3);
dd97c328 1411 echo "\n<ul class='unlist'>\n";
1412
1413 foreach ($printposts as $post) {
1414 $subjectclass = empty($post->parent) ? ' bold' : '';
1415
1416 echo '<li><div class="head">'.
1417 '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
1418 '<div class="name">'.fullname($post, $viewfullnames).'</div>'.
1419 '</div>';
1420 echo '<div class="info'.$subjectclass.'">';
1421 if (empty($post->parent)) {
1422 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
1423 } else {
1424 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
caadf009 1425 }
dd97c328 1426 $post->subject = break_up_long_words(format_string($post->subject, true));
1427 echo $post->subject;
1428 echo "</a>\"</div></li>\n";
caadf009 1429 }
dd97c328 1430
1306c5ea 1431 echo "</ul>\n";
dd97c328 1432
1433 return true;
caadf009 1434}
1435
353228d8 1436/**
1437 * Return grade for given user or all users.
1438 *
df1ba0f4 1439 * @global object
1440 * @global object
1441 * @param object $forum
353228d8 1442 * @param int $userid optional user id, 0 means all users
1443 * @return array array of grades, false if none
1444 */
612607bd 1445function forum_get_user_grades($forum, $userid=0) {
63e87951 1446 global $CFG;
df997f84 1447
63e87951
AD
1448 require_once($CFG->dirroot.'/rating/lib.php');
1449 $rm = new rating_manager();
df997f84 1450
63e87951 1451 $ratingoptions = new stdclass();
353228d8 1452
63e87951
AD
1453 //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
1454 $ratingoptions->modulename = 'forum';
1455 $ratingoptions->moduleid = $forum->id;
1456 //$ratingoptions->cmidnumber = $forum->cmidnumber;
4e445355 1457
63e87951
AD
1458 $ratingoptions->userid = $userid;
1459 $ratingoptions->aggregationmethod = $forum->assessed;
1460 $ratingoptions->scaleid = $forum->scale;
1461 $ratingoptions->itemtable = 'forum_posts';
1462 $ratingoptions->itemtableusercolumn = 'userid';
353228d8 1463
63e87951 1464 return $rm->get_user_grades($ratingoptions);
353228d8 1465}
caadf009 1466
0a4ac01b 1467/**
775f811a 1468 * Update activity grades
353228d8 1469 *
df1ba0f4 1470 * @global object
1471 * @global object
775f811a 1472 * @param object $forum
1473 * @param int $userid specific user only, 0 means all
6b7de0bb 1474 * @param boolean $nullifnone return null if grade does not exist
90f4745c 1475 * @return void
0a4ac01b 1476 */
775f811a 1477function forum_update_grades($forum, $userid=0, $nullifnone=true) {
4e445355 1478 global $CFG, $DB;
775f811a 1479 require_once($CFG->libdir.'/gradelib.php');
caadf009 1480
775f811a 1481 if (!$forum->assessed) {
1482 forum_grade_item_update($forum);
02ebf404 1483
775f811a 1484 } else if ($grades = forum_get_user_grades($forum, $userid)) {
1485 forum_grade_item_update($forum, $grades);
eafb9d9e 1486
775f811a 1487 } else if ($userid and $nullifnone) {
39790bd8 1488 $grade = new stdClass();
775f811a 1489 $grade->userid = $userid;
1490 $grade->rawgrade = NULL;
1491 forum_grade_item_update($forum, $grade);
02ebf404 1492
353228d8 1493 } else {
775f811a 1494 forum_grade_item_update($forum);
1495 }
1496}
1497
1498/**
1499 * Update all grades in gradebook.
df1ba0f4 1500 * @global object
775f811a 1501 */
1502function forum_upgrade_grades() {
1503 global $DB;
1504
1505 $sql = "SELECT COUNT('x')
1506 FROM {forum} f, {course_modules} cm, {modules} m
1507 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1508 $count = $DB->count_records_sql($sql);
1509
1510 $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid
1511 FROM {forum} f, {course_modules} cm, {modules} m
1512 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1513 if ($rs = $DB->get_recordset_sql($sql)) {
775f811a 1514 $pbar = new progress_bar('forumupgradegrades', 500, true);
1515 $i=0;
1516 foreach ($rs as $forum) {
1517 $i++;
1518 upgrade_set_timeout(60*5); // set up timeout, may also abort execution
1519 forum_update_grades($forum, 0, false);
1520 $pbar->update($i, $count, "Updating Forum grades ($i/$count).");
caadf009 1521 }
775f811a 1522 $rs->close();
353228d8 1523 }
1524}
1525
1526/**
612607bd 1527 * Create/update grade item for given forum
353228d8 1528 *
df1ba0f4 1529 * @global object
1530 * @uses GRADE_TYPE_NONE
1531 * @uses GRADE_TYPE_VALUE
1532 * @uses GRADE_TYPE_SCALE
353228d8 1533 * @param object $forum object with extra cmidnumber
df1ba0f4 1534 * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
612607bd 1535 * @return int 0 if ok
353228d8 1536 */
0b5a80a1 1537function forum_grade_item_update($forum, $grades=NULL) {
612607bd 1538 global $CFG;
1539 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1540 require_once($CFG->libdir.'/gradelib.php');
353228d8 1541 }
1542
612607bd 1543 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
353228d8 1544
5980d52f 1545 if (!$forum->assessed or $forum->scale == 0) {
612607bd 1546 $params['gradetype'] = GRADE_TYPE_NONE;
353228d8 1547
1548 } else if ($forum->scale > 0) {
1549 $params['gradetype'] = GRADE_TYPE_VALUE;
1550 $params['grademax'] = $forum->scale;
1551 $params['grademin'] = 0;
1552
1553 } else if ($forum->scale < 0) {
1554 $params['gradetype'] = GRADE_TYPE_SCALE;
1555 $params['scaleid'] = -$forum->scale;
1556 }
1557
0b5a80a1 1558 if ($grades === 'reset') {
1559 $params['reset'] = true;
1560 $grades = NULL;
1561 }
1562
1563 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
353228d8 1564}
1565
1566/**
1567 * Delete grade item for given forum
1568 *
df1ba0f4 1569 * @global object
353228d8 1570 * @param object $forum object
1571 * @return object grade_item
1572 */
1573function forum_grade_item_delete($forum) {
612607bd 1574 global $CFG;
1575 require_once($CFG->libdir.'/gradelib.php');
1576
b67ec72f 1577 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
caadf009 1578}
1579
353228d8 1580
0a4ac01b 1581/**
1582 * Returns the users with data in one forum
63e87951 1583 * (users with records in forum_subscriptions, forum_posts, students)
df1ba0f4 1584 *
1585 * @global object
1586 * @global object
90f4745c 1587 * @param int $forumid
1588 * @return mixed array or false if none
0a4ac01b 1589 */
05855091 1590function forum_get_participants($forumid) {
05855091 1591
4e445355 1592 global $CFG, $DB;
05855091 1593
1594 //Get students from forum_subscriptions
4e445355 1595 $st_subscriptions = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1596 FROM {user} u,
1597 {forum_subscriptions} s
1598 WHERE s.forum = ? AND
1599 u.id = s.userid", array($forumid));
05855091 1600 //Get students from forum_posts
4e445355 1601 $st_posts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1602 FROM {user} u,
1603 {forum_discussions} d,
1604 {forum_posts} p
1605 WHERE d.forum = ? AND
1606 p.discussion = d.id AND
1607 u.id = p.userid", array($forumid));
05855091 1608
63e87951 1609 //Get students from the ratings table
4e445355 1610 $st_ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1611 FROM {user} u,
1612 {forum_discussions} d,
1613 {forum_posts} p,
63e87951 1614 {ratings} r
4e445355 1615 WHERE d.forum = ? AND
1616 p.discussion = d.id AND
1617 r.post = p.id AND
1618 u.id = r.userid", array($forumid));
05855091 1619
1620 //Add st_posts to st_subscriptions
1621 if ($st_posts) {
1622 foreach ($st_posts as $st_post) {
1623 $st_subscriptions[$st_post->id] = $st_post;
1624 }
1625 }
1626 //Add st_ratings to st_subscriptions
1627 if ($st_ratings) {
1628 foreach ($st_ratings as $st_rating) {
1629 $st_subscriptions[$st_rating->id] = $st_rating;
1630 }
1631 }
1632 //Return st_subscriptions array (it contains an array of unique users)
1633 return ($st_subscriptions);
1634}
caadf009 1635
0a4ac01b 1636/**
90f4745c 1637 * This function returns if a scale is being used by one forum
df1ba0f4 1638 *
1639 * @global object
90f4745c 1640 * @param int $forumid
1641 * @param int $scaleid negative number
1642 * @return bool
0a4ac01b 1643 */
0f1a97c2 1644function forum_scale_used ($forumid,$scaleid) {
4e445355 1645 global $DB;
0f1a97c2 1646 $return = false;
65b0e537 1647
4e445355 1648 $rec = $DB->get_record("forum",array("id" => "$forumid","scale" => "-$scaleid"));
65b0e537 1649
fa22fd5f 1650 if (!empty($rec) && !empty($scaleid)) {
0f1a97c2 1651 $return = true;
1652 }
65b0e537 1653
0f1a97c2 1654 return $return;
1655}
1656
85c9ebb9 1657/**
1658 * Checks if scale is being used by any instance of forum
1659 *
1660 * This is used to find out if scale used anywhere
df1ba0f4 1661 *
1662 * @global object
85c9ebb9 1663 * @param $scaleid int
1664 * @return boolean True if the scale is used by any forum
1665 */
1666function forum_scale_used_anywhere($scaleid) {
4e445355 1667 global $DB;
1668 if ($scaleid and $DB->record_exists('forum', array('scale' => -$scaleid))) {
85c9ebb9 1669 return true;
1670 } else {
1671 return false;
1672 }
1673}
1674
0a4ac01b 1675// SQL FUNCTIONS ///////////////////////////////////////////////////////////
9fa49e22 1676
0a4ac01b 1677/**
1678 * Gets a post with all info ready for forum_print_post
1679 * Most of these joins are just to get the forum id
df1ba0f4 1680 *
1681 * @global object
1682 * @global object
90f4745c 1683 * @param int $postid
1684 * @return mixed array of posts or false
0a4ac01b 1685 */
1f48942e 1686function forum_get_post_full($postid) {
4e445355 1687 global $CFG, $DB;
1f48942e 1688
4e445355 1689 return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1690 FROM {forum_posts} p
1691 JOIN {forum_discussions} d ON p.discussion = d.id
1692 LEFT JOIN {user} u ON p.userid = u.id
1693 WHERE p.id = ?", array($postid));
1f48942e 1694}
1695
0a4ac01b 1696/**
1697 * Gets posts with all info ready for forum_print_post
1698 * We pass forumid in because we always know it so no need to make a
1699 * complicated join to find it out.
df1ba0f4 1700 *
1701 * @global object
1702 * @global object
90f4745c 1703 * @return mixed array of posts or false
0a4ac01b 1704 */
77efef3e 1705function forum_get_discussion_posts($discussion, $sort, $forumid) {
4e445355 1706 global $CFG, $DB;
1f48942e 1707
4e445355 1708 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1709 FROM {forum_posts} p
1710 LEFT JOIN {user} u ON p.userid = u.id
1711 WHERE p.discussion = ?
1712 AND p.parent > 0 $sort", array($discussion));
1f48942e 1713}
1714
65bcf17b 1715/**
1716 * Gets all posts in discussion including top parent.
df1ba0f4 1717 *
1718 * @global object
1719 * @global object
1720 * @global object
90f4745c 1721 * @param int $discussionid
1722 * @param string $sort
1723 * @param bool $tracking does user track the forum?
1724 * @return array of posts
65bcf17b 1725 */
90f4745c 1726function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
4e445355 1727 global $CFG, $DB, $USER;
65bcf17b 1728
3c2bf848 1729 $tr_sel = "";
1730 $tr_join = "";
4e445355 1731 $params = array();
3c2bf848 1732
90f4745c 1733 if ($tracking) {
1734 $now = time();
1735 $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
1736 $tr_sel = ", fr.id AS postread";
4e445355 1737 $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)";
1738 $params[] = $USER->id;
90f4745c 1739 }
1740
4e445355 1741 $params[] = $discussionid;
1742 if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
1743 FROM {forum_posts} p
1744 LEFT JOIN {user} u ON p.userid = u.id
90f4745c 1745 $tr_join
4e445355 1746 WHERE p.discussion = ?
1747 ORDER BY $sort", $params)) {
65bcf17b 1748 return array();
1749 }
1750
1751 foreach ($posts as $pid=>$p) {
90f4745c 1752 if ($tracking) {
1753 if (forum_tp_is_post_old($p)) {
6b7de0bb 1754 $posts[$pid]->postread = true;
90f4745c 1755 }
1756 }
65bcf17b 1757 if (!$p->parent) {
1758 continue;
1759 }
1760 if (!isset($posts[$p->parent])) {
1761 continue; // parent does not exist??
1762 }
1763 if (!isset($posts[$p->parent]->children)) {
1764 $posts[$p->parent]->children = array();
1765 }
1766 $posts[$p->parent]->children[$pid] =& $posts[$pid];
1767 }
1768
1769 return $posts;
1770}
1771
0a4ac01b 1772/**
1773 * Gets posts with all info ready for forum_print_post
1774 * We pass forumid in because we always know it so no need to make a
1775 * complicated join to find it out.
df1ba0f4 1776 *
1777 * @global object
1778 * @global object
1779 * @param int $parent
1780 * @param int $forumid
1781 * @return array
0a4ac01b 1782 */
77efef3e 1783function forum_get_child_posts($parent, $forumid) {
4e445355 1784 global $CFG, $DB;
1f48942e 1785
4e445355 1786 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1787 FROM {forum_posts} p
1788 LEFT JOIN {user} u ON p.userid = u.id
1789 WHERE p.parent = ?
1790 ORDER BY p.created ASC", array($parent));
1f48942e 1791}
1792
42fb3c85 1793/**
1794 * An array of forum objects that the user is allowed to read/search through.
df1ba0f4 1795 *
1796 * @global object
1797 * @global object
1798 * @global object
1799 * @param int $userid
1800 * @param int $courseid if 0, we look for forums throughout the whole site.
42fb3c85 1801 * @return array of forum objects, or false if no matches
1802 * Forum objects have the following attributes:
1803 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1804 * viewhiddentimedposts
1805 */
1806function forum_get_readable_forums($userid, $courseid=0) {
2b63df96 1807
4e445355 1808 global $CFG, $DB, $USER;
6b7de0bb 1809 require_once($CFG->dirroot.'/course/lib.php');
2b63df96 1810
4e445355 1811 if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) {
12e57b92 1812 print_error('notinstalled', 'forum');
42fb3c85 1813 }
2b63df96 1814
42fb3c85 1815 if ($courseid) {
4e445355 1816 $courses = $DB->get_records('course', array('id' => $courseid));
42fb3c85 1817 } else {
65bcf17b 1818 // If no course is specified, then the user can see SITE + his courses.
4e445355 1819 $courses1 = $DB->get_records('course', array('id' => SITEID));
df997f84 1820 $courses2 = enrol_get_users_courses($userid, true);
6155150c 1821 $courses = array_merge($courses1, $courses2);
42fb3c85 1822 }
1823 if (!$courses) {
6b7de0bb 1824 return array();
42fb3c85 1825 }
1826
1827 $readableforums = array();
2b63df96 1828
6527b5c2 1829 foreach ($courses as $course) {
1830
6b7de0bb 1831 $modinfo =& get_fast_modinfo($course);
1832 if (is_null($modinfo->groups)) {
1833 $modinfo->groups = groups_get_user_groups($course->id, $userid);
42fb3c85 1834 }
2b63df96 1835
6b7de0bb 1836 if (empty($modinfo->instances['forum'])) {
1837 // hmm, no forums?
1838 continue;
1839 }
2b63df96 1840
4e445355 1841 $courseforums = $DB->get_records('forum', array('course' => $course->id));
2b63df96 1842
6b7de0bb 1843 foreach ($modinfo->instances['forum'] as $forumid => $cm) {
1844 if (!$cm->uservisible or !isset($courseforums[$forumid])) {
1845 continue;
1846 }
1847 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1848 $forum = $courseforums[$forumid];
d50704bf 1849
6b7de0bb 1850 if (!has_capability('mod/forum:viewdiscussion', $context)) {
1851 continue;
1852 }
6527b5c2 1853
6b7de0bb 1854 /// group access
1855 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1856 if (is_null($modinfo->groups)) {
1857 $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
1858 }
98da6021 1859 if (isset($modinfo->groups[$cm->groupingid])) {
6b7de0bb 1860 $forum->onlygroups = $modinfo->groups[$cm->groupingid];
1861 $forum->onlygroups[] = -1;
1862 } else {
1863 $forum->onlygroups = array(-1);
1864 }
1865 }
2b63df96 1866
6b7de0bb 1867 /// hidden timed discussions
1868 $forum->viewhiddentimedposts = true;
1869 if (!empty($CFG->forum_enabletimedposts)) {
1870 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
1871 $forum->viewhiddentimedposts = false;
1872 }
1873 }
d50704bf 1874
6b7de0bb 1875 /// qanda access
1876 if ($forum->type == 'qanda'
1877 && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
2b63df96 1878
6b7de0bb 1879 // We need to check whether the user has posted in the qanda forum.
1880 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
1881 // the user is allowed to see in this forum.
1882 if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1883 foreach ($discussionspostedin as $d) {
1884 $forum->onlydiscussions[] = $d->id;
d50704bf 1885 }
42fb3c85 1886 }
1887 }
6b7de0bb 1888
1889 $readableforums[$forum->id] = $forum;
42fb3c85 1890 }
6b7de0bb 1891
1892 unset($modinfo);
1893
42fb3c85 1894 } // End foreach $courses
2b63df96 1895
42fb3c85 1896 return $readableforums;
1897}
1898
bbbf2d40 1899/**
1900 * Returns a list of posts found using an array of search terms.
df1ba0f4 1901 *
1902 * @global object
1903 * @global object
1904 * @global object
1905 * @param array $searchterms array of search terms, e.g. word +word -word
1906 * @param int $courseid if 0, we search through the whole site
1907 * @param int $limitfrom
1908 * @param int $limitnum
1909 * @param int &$totalcount
1910 * @param string $extrasql
1911 * @return array|bool Array of posts found or false
42fb3c85 1912 */
2b63df96 1913function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
42fb3c85 1914 &$totalcount, $extrasql='') {
4e445355 1915 global $CFG, $DB, $USER;
42fb3c85 1916 require_once($CFG->libdir.'/searchlib.php');
1917
1918 $forums = forum_get_readable_forums($USER->id, $courseid);
2b63df96 1919
67875aa1 1920 if (count($forums) == 0) {
6b7de0bb 1921 $totalcount = 0;
67875aa1 1922 return false;
1923 }
42fb3c85 1924
6b7de0bb 1925 $now = round(time(), -2); // db friendly
1926
1927 $fullaccess = array();
1928 $where = array();
4e445355 1929 $params = array();
6b7de0bb 1930
1931 foreach ($forums as $forumid => $forum) {
1932 $select = array();
1933
1934 if (!$forum->viewhiddentimedposts) {
004efe66 1935 $select[] = "(d.userid = :userid OR (d.timestart < : AND (d.timeend = 0 OR d.timeend > :timeend)))";
1936 $params = array('userid'=>$USER->id, 'timestart'=>$now, 'timeend'=>$now);
42fb3c85 1937 }
6b7de0bb 1938
ad9c22aa 1939 $cm = get_coursemodule_from_instance('forum', $forumid);
1940 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1941
1942 if ($forum->type == 'qanda'
1943 && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
6b7de0bb 1944 if (!empty($forum->onlydiscussions)) {
004efe66 1945 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda0');
4e445355 1946 $params = array_merge($params, $discussionid_params);
1947 $select[] = "(d.id $discussionid_sql OR p.parent = 0)";
d50704bf 1948 } else {
6b7de0bb 1949 $select[] = "p.parent = 0";
d50704bf 1950 }
1951 }
6b7de0bb 1952
1953 if (!empty($forum->onlygroups)) {
004efe66 1954 list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps0');
4e445355 1955 $params = array_merge($params, $groupid_params);
1956 $select[] = "d.groupid $groupid_sql";
42fb3c85 1957 }
6b7de0bb 1958
1959 if ($select) {
1960 $selects = implode(" AND ", $select);
004efe66 1961 $where[] = "(d.forum = :forum AND $selects)";
1962 $params['forum'] = $forumid;
6b7de0bb 1963 } else {
1964 $fullaccess[] = $forumid;
1965 }
1966 }
1967
1968 if ($fullaccess) {
004efe66 1969 list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula0');
4e445355 1970 $params = array_merge($params, $fullid_params);
1971 $where[] = "(d.forum $fullid_sql)";
42fb3c85 1972 }
42fb3c85 1973
6b7de0bb 1974 $selectdiscussion = "(".implode(" OR ", $where).")";
42fb3c85 1975
42fb3c85 1976 $messagesearch = '';
1977 $searchstring = '';
2b63df96 1978
42fb3c85 1979 // Need to concat these back together for parser to work.
1980 foreach($searchterms as $searchterm){
1981 if ($searchstring != '') {
1982 $searchstring .= ' ';
1983 }
1984 $searchstring .= $searchterm;
1985 }
1986
1987 // We need to allow quoted strings for the search. The quotes *should* be stripped
1988 // by the parser, but this should be examined carefully for security implications.
1989 $searchstring = str_replace("\\\"","\"",$searchstring);
1990 $parser = new search_parser();
1991 $lexer = new search_lexer($parser);
1992
1993 if ($lexer->parse($searchstring)) {
1994 $parsearray = $parser->get_parsed_array();
0a4ac01b 1995 // Experimental feature under 1.8! MDL-8830
1996 // Use alternative text searches if defined
1997 // This feature only works under mysql until properly implemented for other DBs
1998 // Requires manual creation of text index for forum_posts before enabling it:
1999 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
2000 // Experimental feature under 1.8! MDL-8830
532daab4 2001 if (!empty($CFG->forum_usetextsearches)) {
004efe66 2002 list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
532daab4 2003 'p.userid', 'u.id', 'u.firstname',
2004 'u.lastname', 'p.modified', 'd.forum');
2005 } else {
004efe66 2006 list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject',
532daab4 2007 'p.userid', 'u.id', 'u.firstname',
2008 'u.lastname', 'p.modified', 'd.forum');
2009 }
004efe66 2010 $params = array_merge($params, $msparams);
42fb3c85 2011 }
2012
4e445355 2013 $fromsql = "{forum_posts} p,
2014 {forum_discussions} d,
2015 {user} u";
42fb3c85 2016
2017 $selectsql = " $messagesearch
2018 AND p.discussion = d.id
2019 AND p.userid = u.id
2020 AND $selectdiscussion
2021 $extrasql";
2022
2023 $countsql = "SELECT COUNT(*)
2024 FROM $fromsql
2025 WHERE $selectsql";
2026
7f094149 2027 $searchsql = "SELECT p.*,
42fb3c85 2028 d.forum,
2029 u.firstname,
2030 u.lastname,
2031 u.email,
8ba59d07 2032 u.picture,
3a11c09f
PS
2033 u.imagealt,
2034 u.email
42fb3c85 2035 FROM $fromsql
2036 WHERE $selectsql
2037 ORDER BY p.modified DESC";
2038
4e445355 2039 $totalcount = $DB->count_records_sql($countsql, $params);
d50704bf 2040
4e445355 2041 return $DB->get_records_sql($searchsql, $params, $limitfrom, $limitnum);
42fb3c85 2042}
2043
0a4ac01b 2044/**
2045 * Returns a list of ratings for a particular post - sorted.
df1ba0f4 2046 *
2047 * @global object
2048 * @global object
90f4745c 2049 * @param int $postid
2050 * @param string $sort
df1ba0f4 2051 * @return array Array of ratings or false
0a4ac01b 2052 */
63e87951
AD
2053function forum_get_ratings($context, $postid, $sort="u.firstname ASC") {
2054 global $PAGE;
2055
2056 $options = new stdclass();
2057 $options->context = $PAGE->context;
2058 $options->itemid = $postid;
2059 $options->sort = "ORDER BY $sort";
2060
0bb69ab3
PS
2061 $rm = new rating_manager();
2062 $rm->get_all_ratings_for_item($options);
9fa49e22 2063}
2064
0a4ac01b 2065/**
2066 * Returns a list of all new posts that have not been mailed yet
df1ba0f4 2067 *
2068 * @global object
2069 * @global object
2070 * @param int $starttime posts created after this time
2071 * @param int $endtime posts created before this
2072 * @param int $now used for timed discussions only
2073 * @return array
0a4ac01b 2074 */
90f4745c 2075function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
4e445355 2076 global $CFG, $DB;
90f4745c 2077
4e445355 2078 $params = array($starttime, $endtime);
90f4745c 2079 if (!empty($CFG->forum_enabletimedposts)) {
2080 if (empty($now)) {
2081 $now = time();
2082 }
4e445355 2083 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2084 $params[] = $now;
2085 $params[] = $now;
90f4745c 2086 } else {
2087 $timedsql = "";
2088 }
2089
4e445355 2090 return $DB->get_records_sql("SELECT p.*, d.course, d.forum
2091 FROM {forum_posts} p
2092 JOIN {forum_discussions} d ON d.id = p.discussion
65b0e537 2093 WHERE p.mailed = 0
4e445355 2094 AND p.created >= ?
2095 AND (p.created < ? OR p.mailnow = 1)
90f4745c 2096 $timedsql
4e445355 2097 ORDER BY p.modified ASC", $params);
1f48942e 2098}
2099
0a4ac01b 2100/**
2101 * Marks posts before a certain time as being mailed already
df1ba0f4 2102 *
2103 * @global object
2104 * @global object
2105 * @param int $endtime
2106 * @param int $now Defaults to time()
2107 * @return bool
0a4ac01b 2108 */
90f4745c 2109function forum_mark_old_posts_as_mailed($endtime, $now=null) {
4e445355 2110 global $CFG, $DB;
90f4745c 2111 if (empty($now)) {
2112 $now = time();
2113 }
2114
2115 if (empty($CFG->forum_enabletimedposts)) {
4e445355 2116 return $DB->execute("UPDATE {forum_posts}
90f4745c 2117 SET mailed = '1'
4e445355 2118 WHERE (created < ? OR mailnow = 1)
3342790c 2119 AND mailed = 0", array($endtime));
90f4745c 2120
0f620d4b 2121 } else {
4e445355 2122 return $DB->execute("UPDATE {forum_posts}
90f4745c 2123 SET mailed = '1'
2124 WHERE discussion NOT IN (SELECT d.id
4e445355 2125 FROM {forum_discussions} d
2126 WHERE d.timestart > ?)
2127 AND (created < ? OR mailnow = 1)
3342790c 2128 AND mailed = 0", array($now, $endtime));
0f620d4b 2129 }
3ecca1ee 2130}
2131
0a4ac01b 2132/**
2133 * Get all the posts for a user in a forum suitable for forum_print_post
df1ba0f4 2134 *
2135 * @global object
2136 * @global object
2137 * @uses CONTEXT_MODULE
2138 * @return array
0a4ac01b 2139 */
1f48942e 2140function forum_get_user_posts($forumid, $userid) {
4e445355 2141 global $CFG, $DB;
1f48942e 2142
90f4745c 2143 $timedsql = "";
4e445355 2144 $params = array($forumid, $userid);
2145
90f4745c 2146 if (!empty($CFG->forum_enabletimedposts)) {
2147 $cm = get_coursemodule_from_instance('forum', $forumid);
2148 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2149 $now = time();
4e445355 2150 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2151 $params[] = $now;
2152 $params[] = $now;
6b7de0bb 2153 }
90f4745c 2154 }
2155
4e445355 2156 return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
2157 FROM {forum} f
2158 JOIN {forum_discussions} d ON d.forum = f.id
2159 JOIN {forum_posts} p ON p.discussion = d.id
2160 JOIN {user} u ON u.id = p.userid
2161 WHERE f.id = ?
2162 AND p.userid = ?
90f4745c 2163 $timedsql
4e445355 2164 ORDER BY p.modified ASC", $params);
1f48942e 2165}
2166
90f4745c 2167/**
2168 * Get all the discussions user participated in
df1ba0f4 2169 *
2170 * @global object
2171 * @global object
2172 * @uses CONTEXT_MODULE
90f4745c 2173 * @param int $forumid
2174 * @param int $userid
df1ba0f4 2175 * @return array Array or false
90f4745c 2176 */
2177function forum_get_user_involved_discussions($forumid, $userid) {
4e445355 2178 global $CFG, $DB;
90f4745c 2179
2180 $timedsql = "";
4e445355 2181 $params = array($forumid, $userid);
90f4745c 2182 if (!empty($CFG->forum_enabletimedposts)) {
2183 $cm = get_coursemodule_from_instance('forum', $forumid);
2184 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2185 $now = time();
4e445355 2186 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2187 $params[] = $now;
2188 $params[] = $now;
6b7de0bb 2189 }
90f4745c 2190 }
2191
4e445355 2192 return $DB->get_records_sql("SELECT DISTINCT d.*
2193 FROM {forum} f
2194 JOIN {forum_discussions} d ON d.forum = f.id
2195 JOIN {forum_posts} p ON p.discussion = d.id
2196 WHERE f.id = ?
2197 AND p.userid = ?
2198 $timedsql", $params);
90f4745c 2199}
2200
2201/**
2202 * Get all the posts for a user in a forum suitable for forum_print_post
df1ba0f4 2203 *
2204 * @global object
2205 * @global object
90f4745c 2206 * @param int $forumid
2207 * @param int $userid
2208 * @return array of counts or false
2209 */
2210function forum_count_user_posts($forumid, $userid) {
4e445355 2211 global $CFG, $DB;
90f4745c 2212
2213 $timedsql = "";
4e445355 2214 $params = array($forumid, $userid);
90f4745c 2215 if (!empty($CFG->forum_enabletimedposts)) {
2216 $cm = get_coursemodule_from_instance('forum', $forumid);
2217 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2218 $now = time();
4e445355 2219 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2220 $params[] = $now;
2221 $params[] = $now;
6b7de0bb 2222 }
90f4745c 2223 }
2224
4e445355 2225 return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
2226 FROM {forum} f
2227 JOIN {forum_discussions} d ON d.forum = f.id
2228 JOIN {forum_posts} p ON p.discussion = d.id
2229 JOIN {user} u ON u.id = p.userid
2230 WHERE f.id = ?
2231 AND p.userid = ?
2232 $timedsql", $params);
90f4745c 2233}
2234
0a4ac01b 2235/**
2236 * Given a log entry, return the forum post details for it.
df1ba0f4 2237 *
2238 * @global object
2239 * @global object
2240 * @param object $log
2241 * @return array|null
0a4ac01b 2242 */
1f48942e 2243function forum_get_post_from_log($log) {
4e445355 2244 global $CFG, $DB;
1f48942e 2245
2246 if ($log->action == "add post") {
2247
4e445355 2248 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 2249 u.firstname, u.lastname, u.email, u.picture
4e445355 2250 FROM {forum_discussions} d,
2251 {forum_posts} p,
2252 {forum} f,
2253 {user} u
2254 WHERE p.id = ?
65b0e537 2255 AND d.id = p.discussion
2256 AND p.userid = u.id
8f7dc7f1 2257 AND u.deleted <> '1'
4e445355 2258 AND f.id = d.forum", array($log->info));
1f48942e 2259
2260
2261 } else if ($log->action == "add discussion") {
2262
4e445355 2263 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 2264 u.firstname, u.lastname, u.email, u.picture
4e445355 2265 FROM {forum_discussions} d,
2266 {forum_posts} p,
2267 {forum} f,
2268 {user} u
2269 WHERE d.id = ?
65b0e537 2270 AND d.firstpost = p.id
2271 AND p.userid = u.id
8f7dc7f1 2272 AND u.deleted <> '1'
4e445355 2273 AND f.id = d.forum", array($log->info));
1f48942e 2274 }
2275 return NULL;
2276}
2277
0a4ac01b 2278/**
2279 * Given a discussion id, return the first post from the discussion
df1ba0f4 2280 *
2281 * @global object
2282 * @global object
2283 * @param int $dicsussionid
2284 * @return array
0a4ac01b 2285 */
d05956ac 2286function forum_get_firstpost_from_discussion($discussionid) {
4e445355 2287 global $CFG, $DB;
d05956ac 2288
4e445355 2289 return $DB->get_record_sql("SELECT p.*
2290 FROM {forum_discussions} d,
2291 {forum_posts} p
2292 WHERE d.id = ?
2293 AND d.firstpost = p.id ", array($discussionid));
d05956ac 2294}
2295
0a4ac01b 2296/**
90f4745c 2297 * Returns an array of counts of replies to each discussion
df1ba0f4 2298 *
2299 * @global object
2300 * @global object
2301 * @param int $forumid
2302 * @param string $forumsort
2303 * @param int $limit
2304 * @param int $page
2305 * @param int $perpage
2306 * @return array
0a4ac01b 2307 */
90f4745c 2308function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
4e445355 2309 global $CFG, $DB;
1f48942e 2310
90f4745c 2311 if ($limit > 0) {
2312 $limitfrom = 0;
2313 $limitnum = $limit;
2314 } else if ($page != -1) {
2315 $limitfrom = $page*$perpage;
2316 $limitnum = $perpage;
2317 } else {
2318 $limitfrom = 0;
2319 $limitnum = 0;
2320 }
2321
2322 if ($forumsort == "") {
2323 $orderby = "";
2324 $groupby = "";
2325
2326 } else {
2327 $orderby = "ORDER BY $forumsort";
2328 $groupby = ", ".strtolower($forumsort);
2329 $groupby = str_replace('desc', '', $groupby);
2330 $groupby = str_replace('asc', '', $groupby);
2331 }
2332
bfeb10b7 2333 if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
2334 $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
4e445355 2335 FROM {forum_posts} p
2336 JOIN {forum_discussions} d ON p.discussion = d.id
2337 WHERE p.parent > 0 AND d.forum = ?
bfeb10b7 2338 GROUP BY p.discussion";
4e445355 2339 return $DB->get_records_sql($sql, array($forumid));
bfeb10b7 2340
90f4745c 2341 } else {
bfeb10b7 2342 $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
4e445355 2343 FROM {forum_posts} p
2344 JOIN {forum_discussions} d ON p.discussion = d.id
2345 WHERE d.forum = ?
bfeb10b7 2346 GROUP BY p.discussion $groupby
2347 $orderby";
4e445355 2348 return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum);
90f4745c 2349 }
2350}
2351
df1ba0f4 2352/**
2353 * @global object
2354 * @global object
2355 * @global object
2356 * @staticvar array $cache
2357 * @param object $forum
2358 * @param object $cm
2359 * @param object $course
2360 * @return mixed
2361 */
90f4745c 2362function forum_count_discussions($forum, $cm, $course) {
4e445355 2363 global $CFG, $DB, $USER;
90f4745c 2364
2365 static $cache = array();
2366
2367 $now = round(time(), -2); // db cache friendliness
2368
4e445355 2369 $params = array($course->id);
2370
90f4745c 2371 if (!isset($cache[$course->id])) {
2372 if (!empty($CFG->forum_enabletimedposts)) {
4e445355 2373 $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
2374 $params[] = $now;
2375 $params[] = $now;
90f4745c 2376 } else {
2377 $timedsql = "";
2378 }
2379
2380 $sql = "SELECT f.id, COUNT(d.id) as dcount
4e445355 2381 FROM {forum} f
2382 JOIN {forum_discussions} d ON d.forum = f.id
2383 WHERE f.course = ?
90f4745c 2384 $timedsql
2385 GROUP BY f.id";
a48e8c4b 2386
4e445355 2387 if ($counts = $DB->get_records_sql($sql, $params)) {
90f4745c 2388 foreach ($counts as $count) {
2389 $counts[$count->id] = $count->dcount;
2390 }
2391 $cache[$course->id] = $counts;
2392 } else {
2393 $cache[$course->id] = array();
2394 }
a48e8c4b 2395 }
90f4745c 2396
2397 if (empty($cache[$course->id][$forum->id])) {
2398 return 0;
a48e8c4b 2399 }
90f4745c 2400
2401 $groupmode = groups_get_activity_groupmode($cm, $course);
2402
2403 if ($groupmode != SEPARATEGROUPS) {
2404 return $cache[$course->id][$forum->id];
1f48942e 2405 }
90f4745c 2406
948091f4 2407 if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
90f4745c 2408 return $cache[$course->id][$forum->id];
2409 }
2410
2411 require_once($CFG->dirroot.'/course/lib.php');
2412
2413 $modinfo =& get_fast_modinfo($course);
2414 if (is_null($modinfo->groups)) {
2415 $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
2416 }
2417
98da6021
PS
2418 if (array_key_exists($cm->groupingid, $modinfo->groups)) {
2419 $mygroups = $modinfo->groups[$cm->groupingid];
90f4745c 2420 } else {
98da6021 2421 $mygroups = false; // Will be set below
90f4745c 2422 }
2423
2424 // add all groups posts
2425 if (empty($mygroups)) {
2426 $mygroups = array(-1=>-1);
2427 } else {
2428 $mygroups[-1] = -1;
2429 }
4e445355 2430
2431 list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups);
2432 $params[] = $forum->id;
90f4745c 2433
2434 if (!empty($CFG->forum_enabletimedposts)) {
2435 $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
4e445355 2436 $params[] = $now;
2437 $params[] = $now;
90f4745c 2438 } else {
2439 $timedsql = "";
2440 }
2441
2442 $sql = "SELECT COUNT(d.id)
4e445355 2443 FROM {forum_discussions} d
342650a7 2444 WHERE d.groupid $mygroups_sql AND d.forum = ?
90f4745c 2445 $timedsql";
2446
4e445355 2447 return $DB->get_field_sql($sql, $params);
1f48942e 2448}
2449
0a4ac01b 2450/**
63e87951 2451 * How many posts by other users are unrated by a given user in the given discussion?
df1ba0f4 2452 *
2453 * @global object
2454 * @global object
2455 * @param int $discussionid
2456 * @param int $userid
2457 * @return mixed
0a4ac01b 2458 */
1f48942e 2459function forum_count_unrated_posts($discussionid, $userid) {
4e445355 2460 global $CFG, $DB;
2461 if ($posts = $DB->get_record_sql("SELECT count(*) as num
2462 FROM {forum_posts}
65b0e537 2463 WHERE parent > 0
4e445355 2464 AND discussion = ?
2465 AND userid <> ? ", array($discussionid, $userid))) {
1f48942e 2466
4e445355 2467 if ($rated = $DB->get_record_sql("SELECT count(*) as num
2468 FROM {forum_posts} p,
63e87951 2469 {rating} r
4e445355 2470 WHERE p.discussion = ?
63e87951 2471 AND p.id = r.itemid
4e445355 2472 AND r.userid = ?", array($discussionid, $userid))) {
1f48942e 2473 $difference = $posts->num - $rated->num;
2474 if ($difference > 0) {
2475 return $difference;
2476 } else {
2477 return 0; // Just in case there was a counting error
2478 }
2479 } else {
2480 return $posts->num;
2481 }
2482 } else {
2483 return 0;
2484 }
2485}
2486
0a4ac01b 2487/**
2488 * Get all discussions in a forum
df1ba0f4 2489 *
2490 * @global object
2491 * @global object
2492 * @global object
2493 * @uses CONTEXT_MODULE
2494 * @uses VISIBLEGROUPS
2495 * @param object $cm
2496 * @param string $forumsort
2497 * @param bool $fullpost
2498 * @param int $unused
2499 * @param int $limit
2500 * @param bool $userlastmodified
2501 * @param int $page
2502 * @param int $perpage
2503 * @return array
0a4ac01b 2504 */
90f4745c 2505function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
4e445355 2506 global $CFG, $DB, $USER;
0fcac008 2507
3d284127 2508 $timelimit = '';
2509
90f4745c 2510 $now = round(time(), -2);
4e445355 2511 $params = array($cm->instance);
90f4745c 2512
4436a63b 2513 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2b63df96 2514
4436a63b 2515 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { /// User must have perms to view discussions
2516 return array();
2517 }
2518
2519 if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
2b63df96 2520
0468976c 2521 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
4e445355 2522 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
2523 $params[] = $now;
2524 $params[] = $now;
90f4745c 2525 if (isloggedin()) {
4e445355 2526 $timelimit .= " OR d.userid = ?";
2527 $params[] = $USER->id;
3d284127 2528 }
90f4745c 2529 $timelimit .= ")";
fbc21e82 2530 }
fbc21e82 2531 }
1f48942e 2532
90f4745c 2533 if ($limit > 0) {
2534 $limitfrom = 0;
2535 $limitnum = $limit;
2536 } else if ($page != -1) {
2537 $limitfrom = $page*$perpage;
2538 $limitnum = $perpage;
2539 } else {
2540 $limitfrom = 0;
2541 $limitnum = 0;
90ec387a 2542 }
8f0cd6ef 2543
90f4745c 2544 $groupmode = groups_get_activity_groupmode($cm);
2545 $currentgroup = groups_get_activity_group($cm);
353228d8 2546
fffa8b35 2547 if ($groupmode) {
2548 if (empty($modcontext)) {
2549 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2550 }
2551
2552 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2553 if ($currentgroup) {
4e445355 2554 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2555 $params[] = $currentgroup;
fffa8b35 2556 } else {
2557 $groupselect = "";
2558 }
2559
2560 } else {
2561 //seprate groups without access all
2562 if ($currentgroup) {
4e445355 2563 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2564 $params[] = $currentgroup;
fffa8b35 2565 } else {
2566 $groupselect = "AND d.groupid = -1";
2567 }
2568 }
353228d8 2569 } else {
02509fe6 2570 $groupselect = "";
2571 }
2862b309 2572
fffa8b35 2573
29507631 2574 if (empty($forumsort)) {
2575 $forumsort = "d.timemodified DESC";
2576 }
2ab968e9 2577 if (empty($fullpost)) {
b879effb 2578 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
2ab968e9 2579 } else {
2580 $postdata = "p.*";
2581 }
9197e147 2582
13597d01 2583 if (empty($userlastmodified)) { // We don't need to know this
fffa8b35 2584 $umfields = "";
2585 $umtable = "";
13597d01 2586 } else {
fffa8b35 2587 $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
4e445355 2588 $umtable = " LEFT JOIN {user} um ON (d.usermodified = um.id)";
90f4745c 2589 }
2590
2591 $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
2592 u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
4e445355 2593 FROM {forum_discussions} d
2594 JOIN {forum_posts} p ON p.discussion = d.id
2595 JOIN {user} u ON p.userid = u.id
90f4745c 2596 $umtable
4e445355 2597 WHERE d.forum = ? AND p.parent = 0
90f4745c 2598 $timelimit $groupselect
2599 ORDER BY $forumsort";
4e445355 2600 return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
90f4745c 2601}
2602
df1ba0f4 2603/**
2604 *
2605 * @global object
2606 * @global object
2607 * @global object
2608 * @uses CONTEXT_MODULE
2609 * @uses VISIBLEGROUPS
2610 * @param object $cm
2611 * @return array
2612 */
bfeb10b7 2613function forum_get_discussions_unread($cm) {
4e445355 2614 global $CFG, $DB, $USER;
90f4745c 2615
2616 $now = round(time(), -2);
ee151230 2617 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
35716b86 2618
ee151230 2619 $params = array();
90f4745c 2620 $groupmode = groups_get_activity_groupmode($cm);
2621 $currentgroup = groups_get_activity_group($cm);
2622
2623 if ($groupmode) {
2624 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2625
2626 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2627 if ($currentgroup) {
ee151230
AD
2628 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
2629 $params['currentgroup'] = $currentgroup;
90f4745c 2630 } else {
2631 $groupselect = "";
2632 }
2633
2634 } else {
ee151230 2635 //separate groups without access all
90f4745c 2636 if ($currentgroup) {
ee151230
AD
2637 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
2638 $params['currentgroup'] = $currentgroup;
90f4745c 2639 } else {
2640 $groupselect = "AND d.groupid = -1";
2641 }
2642 }
2643 } else {
2644 $groupselect = "";
13597d01 2645 }
2646
90f4745c 2647 if (!empty($CFG->forum_enabletimedposts)) {
ee151230
AD
2648 $timedsql = "AND d.timestart < :now1 AND (d.timeend = 0 OR d.timeend > :now2)";
2649 $params['now1'] = $now;
2650 $params['now2'] = $now;
90f4745c 2651 } else {
2652 $timedsql = "";
2653 }
2654
2655 $sql = "SELECT d.id, COUNT(p.id) AS unread
4e445355 2656 FROM {forum_discussions} d
2657 JOIN {forum_posts} p ON p.discussion = d.id
2658 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id)
90f4745c 2659 WHERE d.forum = {$cm->instance}
ee151230 2660 AND p.modified >= :cutoffdate AND r.id is NULL
90f4745c 2661 $groupselect
4e445355 2662 $timedsql
bfeb10b7 2663 GROUP BY d.id";
ee151230
AD
2664 $params['cutoffdate'] = $cutoffdate;
2665
4e445355 2666 if ($unreads = $DB->get_records_sql($sql, $params)) {
90f4745c 2667 foreach ($unreads as $unread) {
2668 $unreads[$unread->id] = $unread->unread;
2669 }
2670 return $unreads;
2671 } else {
2672 return array();
2673 }
1f48942e 2674}
2675
df1ba0f4 2676/**
2677 * @global object
2678 * @global object
2679 * @global object
2680 * @uses CONEXT_MODULE
2681 * @uses VISIBLEGROUPS
2682 * @param object $cm
2683 * @return array
2684 */
90f4745c 2685function forum_get_discussions_count($cm) {
4e445355 2686 global $CFG, $DB, $USER;
90f4745c 2687
2688 $now = round(time(), -2);
4e445355 2689 $params = array($cm->instance);
90f4745c 2690 $groupmode = groups_get_activity_groupmode($cm);
2691 $currentgroup = groups_get_activity_group($cm);
2692
2693 if ($groupmode) {
2694 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2695
2696 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2697 if ($currentgroup) {
4e445355 2698 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2699 $params[] = $currentgroup;
90f4745c 2700 } else {
2701 $groupselect = "";
2702 }
2703
2704 } else {
2705 //seprate groups without access all
2706 if ($currentgroup) {
4e445355 2707 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2708 $params[] = $currentgroup;
90f4745c 2709 } else {
2710 $groupselect = "AND d.groupid = -1";
2711 }
2712 }
2713 } else {
2714 $groupselect = "";
2715 }
2716
2717 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
2718
2719 $timelimit = "";
2720
2721 if (!empty($CFG->forum_enabletimedposts)) {
2722
2723 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2724
2725 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
4e445355 2726 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
2727 $params[] = $now;
2728 $params[] = $now;
90f4745c 2729 if (isloggedin()) {
4e445355 2730 $timelimit .= " OR d.userid = ?";
2731 $params[] = $USER->id;
90f4745c 2732 }
2733 $timelimit .= ")";
2734 }
2735 }
2736
2737 $sql = "SELECT COUNT(d.id)
4e445355 2738 FROM {forum_discussions} d
2739 JOIN {forum_posts} p ON p.discussion = d.id
2740 WHERE d.forum = ? AND p.parent = 0
2741 $groupselect $timelimit";
90f4745c 2742
4e445355 2743 return $DB->get_field_sql($sql, $params);
90f4745c 2744}
1f48942e 2745
2746
0a4ac01b 2747/**
2748 * Get all discussions started by a particular user in a course (or group)
2749 * This function no longer used ...
df1ba0f4 2750 *
2751 * @todo Remove this function if no longer used
2752 * @global object
2753 * @global object
2754 * @param int $courseid
2755 * @param int $userid
2756 * @param int $groupid
2757 * @return array
0a4ac01b 2758 */
b656e2a9 2759function forum_get_user_discussions($courseid, $userid, $groupid=0) {
4e445355 2760 global $CFG, $DB;
2761 $params = array($courseid, $userid);
b656e2a9 2762 if ($groupid) {
4e445355 2763 $groupselect = " AND d.groupid = ? ";
2764 $params[] = $groupid;
b656e2a9 2765 } else {
2766 $groupselect = "";
2767 }
2768
4e445355 2769 return $DB->get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
ebc3bd2b 2770 f.type as forumtype, f.name as forumname, f.id as forumid
4e445355 2771 FROM {forum_discussions} d,
2772 {forum_posts} p,
2773 {user} u,
2774 {forum} f
2775 WHERE d.course = ?
65b0e537 2776 AND p.discussion = d.id
2777 AND p.parent = 0
2778 AND p.userid = u.id
4e445355 2779 AND u.id = ?
b656e2a9 2780 AND d.forum = f.id $groupselect
4e445355 2781 ORDER BY p.created DESC", $params);
1f48942e 2782}
2783
0a4ac01b 2784/**
702dc57b 2785 * Get the list of potential subscribers to a forum.
8d8d0bfa 2786 *
2787 * @param object $forumcontext the forum context.
2788 * @param integer $groupid the id of a group, or 0 for all groups.
2789 * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
2790 * @param string $sort sort order. As for get_users_by_capability.
2791 * @return array list of users.
0a4ac01b 2792 */
8d8d0bfa 2793function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
2794 return get_users_by_capability($forumcontext, 'mod/forum:initialsubscriptions', $fields, $sort, '', '', $groupid, '', false, true);
2795}
0a4ac01b 2796
8d8d0bfa 2797/**
2798 * Returns list of user objects that are subscribed to this forum
2799 *
df1ba0f4 2800 * @global object
2801 * @global object
8d8d0bfa 2802 * @param object $course the course
2803 * @param forum $forum the forum
2804 * @param integer $groupid group id, or 0 for all.
2805 * @param object $context the forum context, to save re-fetching it where possible.
2806 * @return array list of users.
2807 */
2808function forum_subscribed_users($course, $forum, $groupid=0, $context = NULL) {
4e445355 2809 global $CFG, $DB;
2810 $params = array($forum->id);
1f48942e 2811
6673d7bd 2812 if ($groupid) {
4e445355 2813 $grouptables = ", {groups_members} gm ";
2814 $groupselect = "AND gm.groupid = ? AND u.id = gm.userid";
2815 $params[] = $groupid;
6673d7bd 2816 } else {
669f2499 2817 $grouptables = '';
2818 $groupselect = '';
6673d7bd 2819 }
2820
4e445355 2821 $fields ="u.id,
2822 u.username,
2823 u.firstname,
2824 u.lastname,
2825 u.maildisplay,
2826 u.mailformat,
2827 u.maildigest,
2828 u.emailstop,
2829 u.imagealt,
2830 u.email,
2831 u.city,
2832 u.country,
2833 u.lastaccess,
2834 u.lastlogin,
2835 u.picture,
2836 u.timezone,
2837 u.theme,
2838 u.lang,
2839 u.trackforums,
2840 u.mnethostid";
2841
a9900c73 2842 if (forum_is_forcesubscribed($forum)) {
48dcaf58 2843 if (empty($context)) {
2844 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
2845 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
8d8d0bfa 2846 }
c37fdcb5 2847 $sort = "u.email ASC";
8d8d0bfa 2848 $results = forum_get_potential_subscribers($context, $groupid, $fields, $sort);
669f2499 2849 } else {
4e445355 2850 $results = $DB->get_records_sql("SELECT $fields
2851 FROM {user} u,
2852 {forum_subscriptions} s $grouptables
2853 WHERE s.forum = ?
65b0e537 2854 AND s.userid = u.id
df1c2c71 2855 AND u.deleted = 0 $groupselect
4e445355 2856 ORDER BY u.email ASC", $params);
669f2499 2857 }
2858
df1c2c71 2859 static $guestid = null;
2860
2861 if (is_null($guestid)) {
2862 if ($guest = guest_user()) {
2863 $guestid = $guest->id;
2864 } else {
2865 $guestid = 0;
2866 }
669f2499 2867 }
2868
df1c2c71 2869 // Guest user should never be subscribed to a forum.
2870 unset($results[$guestid]);
2871
669f2499 2872 return $results;
1f48942e 2873}
9fa49e22 2874
067675c0 2875
2876
0a4ac01b 2877// OTHER FUNCTIONS ///////////////////////////////////////////////////////////
f93f848a 2878
2879
df1ba0f4 2880/**
2881 * @global object
2882 * @global object
2883 * @param int $courseid
2884 * @param string $type
2885 */
11b0c469 2886function forum_get_course_forum($courseid, $type) {
2887// How to set up special 1-per-course forums
9146b979 2888 global $CFG, $DB, $OUTPUT;
a6fcdf98 2889
4e445355 2890 if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $type), "id ASC")) {
65b0e537 2891 // There should always only be ONE, but with the right combination of
29cbd93a 2892 // errors there might be more. In this case, just return the oldest one (lowest ID).
2893 foreach ($forums as $forum) {
2894 return $forum; // ie the first one
11b0c469 2895 }
8daaf761 2896 }
e6874d9f 2897
8daaf761 2898 // Doesn't exist, so create one now.
2899 $forum->course = $courseid;
2900 $forum->type = "$type";
2901 switch ($forum->type) {
2902 case "news":
294ce987 2903 $forum->name = get_string("namenews", "forum");
2904 $forum->intro = get_string("intronews", "forum");
906fef94 2905 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
8daaf761 2906 $forum->assessed = 0;
709f0ec8 2907 if ($courseid == SITEID) {
2908 $forum->name = get_string("sitenews");
2909 $forum->forcesubscribe = 0;
8f0cd6ef 2910 }
8daaf761 2911 break;
2912 case "social":
294ce987 2913 $forum->name = get_string("namesocial", "forum");
2914 $forum->intro = get_string("introsocial", "forum");
8daaf761 2915 $forum->assessed = 0;
2916 $forum->forcesubscribe = 0;
2917 break;
1c7b8b93
NC
2918 case "blog":
2919 $forum->name = get_string('blogforum', 'forum');
2920 $forum->intro = get_string('introblog', 'forum');
2921 $forum->assessed = 0;
2922 $forum->forcesubscribe = 0;
2923 break;
8daaf761 2924 default:
9146b979 2925 echo $OUTPUT->notification("That forum type doesn't exist!");
8daaf761 2926 return false;
2927 break;
2928 }
2929
2930 $forum->timemodified = time();
4e445355 2931 $forum->id = $DB->insert_record("forum", $forum);
8daaf761 2932
4e445355 2933 if (! $module = $DB->get_record("modules", array("name" => "forum"))) {
9146b979 2934 echo $OUTPUT->notification("Could not find forum module!!");
e1b5643f 2935 return false;
82aa0e8d 2936 }
39790bd8 2937 $mod = new stdClass();
e1b5643f 2938 $mod->course = $courseid;
2939 $mod->module = $module->id;
2940 $mod->instance = $forum->id;
2941 $mod->section = 0;
2942 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
11cd754e 2943 echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'");
e1b5643f 2944 return false;
2945 }
2946 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
9146b979 2947 echo $OUTPUT->notification("Could not add the new course module to that section");
e1b5643f 2948 return false;
2949 }
f685e830
PS
2950 $DB->set_field("course_modules", "section", $sectionid, array("id" => $mod->coursemodule));
2951
e1b5643f 2952 include_once("$CFG->dirroot/course/lib.php");
2953 rebuild_course_cache($courseid);
65b0e537 2954
4e445355 2955 return $DB->get_record("forum", array("id" => "$forum->id"));
82aa0e8d 2956}
2957
f93f848a 2958
0a4ac01b 2959/**
df1ba0f4 2960 * Given the data about a posting, builds up the HTML to display it and
2961 * returns the HTML in a string. This is designed for sending via HTML email.
2962 *
2963 * @global object
2964 * @param object $course
2965 * @param object $cm
2966 * @param object $forum
2967 * @param object $discussion
2968 * @param object $post
2969 * @param object $userform
2970 * @param object $userto
2971 * @param bool $ownpost
2972 * @param bool $reply
2973 * @param bool $link
2974 * @param bool $rate
2975 * @param string $footer
2976 * @return string
2977 */
0faf6791 2978function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto,
11b0c469 2979 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2b63df96 2980
59e28d8f 2981 global $CFG, $OUTPUT;
501cdbd8 2982
df1c2c71 2983 if (!isset($userto->viewfullnames[$forum->id])) {
2984 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 2985 print_error('invalidcoursemodule');
df1c2c71 2986 }
2987 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2988 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
2989 } else {
2990 $viewfullnames = $userto->viewfullnames[$forum->id];
7613e6d7 2991 }
7613e6d7 2992
1306c5ea 2993 // format the post body
39790bd8 2994 $options = new stdClass();
1306c5ea 2995 $options->para = true;
e2d7687f 2996 $formattedtext = format_text($post->message, $post->messageformat, $options, $course->id);
0d851f90 2997
add3201e 2998 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
501cdbd8 2999
add3201e 3000 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
812dbaf7 3001 $output .= $OUTPUT->user_picture($userfrom, array('courseid'=>$course->id));
add3201e 3002 $output .= '</td>';
501cdbd8 3003
3004 if ($post->parent) {
add3201e 3005 $output .= '<td class="topic">';
501cdbd8 3006 } else {
add3201e 3007 $output .= '<td class="topic starter">';
501cdbd8 3008 }
add3201e 3009 $output .= '<div class="subject">'.format_string($post->subject).'</div>';
1b26d5e7 3010
df1c2c71 3011 $fullname = fullname($userfrom, $viewfullnames);
39790bd8 3012 $by = new stdClass();
df1c2c71 3013 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
3014 $by->date = userdate($post->modified, '', $userto->timezone);
add3201e 3015 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
3016
3017 $output .= '</td></tr>';
3018
7b54f563 3019 $output .= '<tr><td class="left side" valign="top">';
df1c2c71 3020
3021 if (isset($userfrom->groups)) {
3022 $groups = $userfrom->groups[$forum->id];
3023 } else {
3024 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 3025 print_error('invalidcoursemodule');
df1c2c71 3026 }
3027 $group = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
3028 }
3029
3030 if ($groups) {
3031 $output .= print_group_picture($groups, $course->id, false, true, true);
add3201e 3032 } else {
3033 $output .= '&nbsp;';
3034 }
1b26d5e7 3035
add3201e 3036 $output .= '</td><td class="content">';
501cdbd8 3037
0faf6791 3038 $attachments = forum_print_attachments($post, $cm, 'html');
3039 if ($attachments !== '') {
add3201e 3040 $output .= '<div class="attachments">';
0faf6791 3041 $output .= $attachments;
3042 $output .= '</div>';
7f6689e4 3043 }
3044
0d851f90 3045 $output .= $formattedtext;
501cdbd8 3046
0a4ac01b 3047// Commands
add3201e 3048 $commands = array();
501cdbd8 3049
2e2e71a8 3050 if ($post->parent) {
add3201e 3051 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
3052 $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
2e2e71a8 3053 }
ce45515e 3054
add3201e 3055 if ($reply) {
3056 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
3057 get_string('reply', 'forum').'</a>';
501cdbd8 3058 }
3059
add3201e 3060 $output .= '<div class="commands">';
3061 $output .= implode(' | ', $commands);
3062 $output .= '</div>';
3063
0a4ac01b 3064// Context link to post if required
501cdbd8 3065 if ($link) {
add3201e 3066 $output .= '<div class="link">';
0be4d8bf 3067 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
2b63df96 3068 get_string('postincontext', 'forum').'</a>';
add3201e 3069 $output .= '</div>';
501cdbd8 3070 }
add3201e 3071
501cdbd8 3072 if ($footer) {
add3201e 3073 $output .= '<div class="footer">'.$footer.'</div>';
501cdbd8 3074 }
add3201e 3075 $output .= '</td></tr></table>'."\n\n";
3076
501cdbd8 3077 return $output;
3078}
3079
0a4ac01b 3080/**
21976af1 3081 * Print a forum post
65bcf17b 3082 *
df1ba0f4 3083 * @global object
3084 * @global object
3085 * @uses FORUM_MODE_THREADED
3086 * @uses PORTFOLIO_FORMAT_PLAINHTML
3087 * @uses PORTFOLIO_FORMAT_FILE
3088 * @uses PORTFOLIO_FORMAT_RICHHTML
3089 * @uses PORTFOLIO_ADD_TEXT_LINK
3090 * @uses CONTEXT_MODULE
21976af1 3091 * @param object $post The post to print.
df1ba0f4 3092 * @param object $discussion
3093 * @param object $forum
3094 * @param object $cm
3095 * @param object $course
21976af1 3096 * @param boolean $ownpost Whether this post belongs to the current user.
65bcf17b 3097 * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
21976af1 3098 * @param boolean $link Just print a shortened version of the post as a link to the full post.
21976af1 3099 * @param string $footer Extra stuff to print after the message.
3100 * @param string $highlight Space-separated list of terms to highlight.
3101 * @param int $post_read true, false or -99. If we already know whether this user
3102 * has read this post, pass that in, otherwise, pass in -99, and this
3103 * function will work it out.
3104 * @param boolean $dummyifcantsee When forum_user_can_see_post says that
65bcf17b 3105 * the current user can't see this post, if this argument is true
21976af1 3106 * (the default) then print a dummy 'you can't see this post' post.
3107 * If false, don't output anything at all.
df1ba0f4 3108 * @param bool|null $istracked
63e87951 3109 * @return void
0a4ac01b 3110 */
65bcf17b 3111function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
63e87951 3112 $footer="", $highlight="", $post_read=null, $dummyifcantsee=true, $istracked=null) {
74f5d1e3 3113
5d9827e4 3114 global $USER, $CFG, $OUTPUT;
501cdbd8 3115
951e1073 3116 static $stredit, $strdelete, $strreply, $strparent, $strprune;
5d82704e 3117 static $strpruneheading, $displaymode;
90f4745c 3118 static $strmarkread, $strmarkunread;
f37da850 3119
8d96a7b4 3120 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3121
65bcf17b 3122 $post->course = $course->id;
3123 $post->forum = $forum->id;
64f93798 3124 $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
951e1073 3125
65bcf17b 3126 // caching