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