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