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