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