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