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