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