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