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