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