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