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