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