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