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