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