forum MDL-25909 - Fix undefined variable
[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
df1c2c71 983 if (!isset($userto->viewfullnames[$forum->id])) {
df1c2c71 984 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
985 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
986 } else {
987 $viewfullnames = $userto->viewfullnames[$forum->id];
988 }
989
8b79a625 990 if (!isset($userto->canpost[$discussion->id])) {
991 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
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
1017 $posttext .= "\n---------------------------------------------------------------------\n";
17dc3f3c 1018 $posttext .= format_string($post->subject,true);
0fa18d5a 1019 if ($bare) {
0be4d8bf 1020 $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
aaf7a9dc 1021 }
1022 $posttext .= "\n".$strbynameondate."\n";
1023 $posttext .= "---------------------------------------------------------------------\n";
e2d7687f 1024 $posttext .= format_text_email($post->message, $post->messageformat);
aaf7a9dc 1025 $posttext .= "\n\n";
0faf6791 1026 $posttext .= forum_print_attachments($post, $cm, "text");
1027
aaf7a9dc 1028 if (!$bare && $canreply) {
1029 $posttext .= "---------------------------------------------------------------------\n";
1030 $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
1031 $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
1032 }
1033 if (!$bare && $canunsubscribe) {
1034 $posttext .= "\n---------------------------------------------------------------------\n";
1035 $posttext .= get_string("unsubscribe", "forum");
1036 $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
1037 }
1038
1039 return $posttext;
1040}
1041
0a4ac01b 1042/**
1670305d 1043 * Builds and returns the body of the email notification in html format.
1044 *
df1ba0f4 1045 * @global object
1670305d 1046 * @param object $course
df1ba0f4 1047 * @param object $cm
1670305d 1048 * @param object $forum
1049 * @param object $discussion
1050 * @param object $post
1051 * @param object $userfrom
1052 * @param object $userto
1053 * @return string The email text in HTML format
0a4ac01b 1054 */
0faf6791 1055function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto) {
aaf7a9dc 1056 global $CFG;
1057
a0288610 1058 if ($userto->mailformat != 1) { // Needs to be HTML
1059 return '';
1060 }
aaf7a9dc 1061
8b79a625 1062 if (!isset($userto->canpost[$discussion->id])) {
1063 $canreply = forum_user_can_post($forum, $discussion, $userto);
df1c2c71 1064 } else {
8b79a625 1065 $canreply = $userto->canpost[$discussion->id];
9f2ded76 1066 }
1067
a0288610 1068 $strforums = get_string('forums', 'forum');
a9900c73 1069 $canunsubscribe = ! forum_is_forcesubscribed($forum);
aaf7a9dc 1070
a0288610 1071 $posthtml = '<head>';
78946b9b
PS
1072/* foreach ($CFG->stylesheets as $stylesheet) {
1073 //TODO: MDL-21120
a0288610 1074 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
78946b9b 1075 }*/
a0288610 1076 $posthtml .= '</head>';
f2379d2d 1077 $posthtml .= "\n<body id=\"email\">\n\n";
aaf7a9dc 1078
f2379d2d 1079 $posthtml .= '<div class="navbar">'.
a0288610 1080 '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
1081 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
1082 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
1083 if ($discussion->name == $forum->name) {
1084 $posthtml .= '</div>';
aaf7a9dc 1085 } else {
a0288610 1086 $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
1087 format_string($discussion->name,true).'</a></div>';
aaf7a9dc 1088 }
0faf6791 1089 $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
a0288610 1090
1091 if ($canunsubscribe) {
85db96c5 1092 $posthtml .= '<hr /><div class="mdl-align unsubscribelink">
739da48c 1093 <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
1094 <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
a0288610 1095 }
1096
f2379d2d 1097 $posthtml .= '</body>';
1098
a0288610 1099 return $posthtml;
aaf7a9dc 1100}
1670305d 1101
1102
0a4ac01b 1103/**
13bbe067 1104 *
1670305d 1105 * @param object $course
1106 * @param object $user
1107 * @param object $mod TODO this is not used in this function, refactor
1108 * @param object $forum
1109 * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
0a4ac01b 1110 */
caadf009 1111function forum_user_outline($course, $user, $mod, $forum) {
1a96363a
NC
1112 global $CFG;
1113 require_once("$CFG->libdir/gradelib.php");
1114 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
1115 if (empty($grades->items[0]->grades)) {
1116 $grade = false;
1117 } else {
1118 $grade = reset($grades->items[0]->grades);
1119 }
1120
1121 $count = forum_count_user_posts($forum->id, $user->id);
1122
1123 if ($count && $count->postcount > 0) {
39790bd8 1124 $result = new stdClass();
1a96363a
NC
1125 $result->info = get_string("numposts", "forum", $count->postcount);
1126 $result->time = $count->lastpost;
1127 if ($grade) {
1128 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
90f4745c 1129 }
1a96363a
NC
1130 return $result;
1131 } else if ($grade) {
39790bd8 1132 $result = new stdClass();
1a96363a
NC
1133 $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
1134 $result->time = $grade->dategraded;
1135 return $result;
caadf009 1136 }
1137 return NULL;
1138}
1139
1140
0a4ac01b 1141/**
df1ba0f4 1142 * @global object
1143 * @global object
1144 * @param object $coure
1145 * @param object $user
1146 * @param object $mod
1147 * @param object $forum
0a4ac01b 1148 */
caadf009 1149function forum_user_complete($course, $user, $mod, $forum) {
1a96363a
NC
1150 global $CFG,$USER, $OUTPUT;
1151 require_once("$CFG->libdir/gradelib.php");
1152
1153 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
1154 if (!empty($grades->items[0]->grades)) {
1155 $grade = reset($grades->items[0]->grades);
1156 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
1157 if ($grade->str_feedback) {
1158 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
1159 }
1160 }
caadf009 1161
1f48942e 1162 if ($posts = forum_get_user_posts($forum->id, $user->id)) {
e3ff14ca 1163
65bcf17b 1164 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 1165 print_error('invalidcoursemodule');
65bcf17b 1166 }
90f4745c 1167 $discussions = forum_get_user_involved_discussions($forum->id, $user->id);
65bcf17b 1168
1169 foreach ($posts as $post) {
5e7f2b0b 1170 if (!isset($discussions[$post->discussion])) {
90f4745c 1171 continue;
1172 }
7a8d8f22 1173 $discussion = $discussions[$post->discussion];
5e7f2b0b 1174
63e87951 1175 forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false);
5e7f2b0b 1176 }
caadf009 1177 } else {
41905731 1178 echo "<p>".get_string("noposts", "forum")."</p>";
caadf009 1179 }
caadf009 1180}
1181
c38965fb 1182
1183
1184
5e7f2b0b 1185
1186
0a4ac01b 1187/**
df1ba0f4 1188 * @global object
1189 * @global object
1190 * @global object
1191 * @param array $courses
1192 * @param array $htmlarray
0a4ac01b 1193 */
185cfb09 1194function forum_print_overview($courses,&$htmlarray) {
261c6ef0 1195 global $USER, $CFG, $DB, $SESSION;
493f0820 1196
185cfb09 1197 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
1198 return array();
1199 }
f8716988 1200
185cfb09 1201 if (!$forums = get_all_instances_in_courses('forum',$courses)) {
f8716988 1202 return;
1203 }
185cfb09 1204
5aa23eea 1205
f8716988 1206 // get all forum logs in ONE query (much better!)
4e445355 1207 $params = array();
1208 $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {log} l "
1209 ." JOIN {course_modules} cm ON cm.id = cmid "
185cfb09 1210 ." WHERE (";
1211 foreach ($courses as $course) {
4e445355 1212 $sql .= '(l.course = ? AND l.time > ?) OR ';
1213 $params[] = $course->id;
1214 $params[] = $course->lastaccess;
185cfb09 1215 }
1216 $sql = substr($sql,0,-3); // take off the last OR
1217
b55d0869 1218 $sql .= ") AND l.module = 'forum' AND action = 'add post' "
4e445355 1219 ." AND userid != ? GROUP BY cmid,l.course,instance";
1220
1221 $params[] = $USER->id;
2b63df96 1222
4e445355 1223 if (!$new = $DB->get_records_sql($sql, $params)) {
185cfb09 1224 $new = array(); // avoid warnings
1225 }
2b63df96 1226
185cfb09 1227 // also get all forum tracking stuff ONCE.
1228 $trackingforums = array();
1229 foreach ($forums as $forum) {
1230 if (forum_tp_can_track_forums($forum)) {
1231 $trackingforums[$forum->id] = $forum;
1232 }
1233 }
2b63df96 1234
185cfb09 1235 if (count($trackingforums) > 0) {
1236 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
1237 $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
4e445355 1238 ' FROM {forum_posts} p '.
1239 ' JOIN {forum_discussions} d ON p.discussion = d.id '.
1240 ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE (';
1241 $params = array($USER->id);
1242
d3553951 1243 foreach ($trackingforums as $track) {
4e445355 1244 $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR ';
1245 $params[] = $track->id;
261c6ef0 1246 if (isset($SESSION->currentgroup[$track->course])) {
1247 $groupid = $SESSION->currentgroup[$track->course];
1248 } else {
1249 $groupid = groups_get_all_groups($track->course, $USER->id);
1250 if (is_array($groupid)) {
1251 $groupid = array_shift(array_keys($groupid));
1252 $SESSION->currentgroup[$track->course] = $groupid;
1253 } else {
1254 $groupid = 0;
1255 }
1256 }
1257 $params[] = $groupid;
185cfb09 1258 }
1259 $sql = substr($sql,0,-3); // take off the last OR
4e445355 1260 $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course';
1261 $params[] = $cutoffdate;
185cfb09 1262
4e445355 1263 if (!$unread = $DB->get_records_sql($sql, $params)) {
185cfb09 1264 $unread = array();
1265 }
1266 } else {
1267 $unread = array();
95d71ad3 1268 }
185cfb09 1269
9cba7a8c 1270 if (empty($unread) and empty($new)) {
1271 return;
1272 }
1273
1274 $strforum = get_string('modulename','forum');
1275 $strnumunread = get_string('overviewnumunread','forum');
1276 $strnumpostssince = get_string('overviewnumpostssince','forum');
1277
f8716988 1278 foreach ($forums as $forum) {
185cfb09 1279 $str = '';
f8716988 1280 $count = 0;
185cfb09 1281 $thisunread = 0;
f8716988 1282 $showunread = false;
1283 // either we have something from logs, or trackposts, or nothing.
1284 if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
1285 $count = $new[$forum->id]->count;
90558ec4 1286 }
185cfb09 1287 if (array_key_exists($forum->id,$unread)) {
1288 $thisunread = $unread[$forum->id]->count;
f8716988 1289 $showunread = true;
0d6b9d4f 1290 }
185cfb09 1291 if ($count > 0 || $thisunread > 0) {
e23800b7 1292 $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
1293 $forum->name.'</a></div>';
1294 $str .= '<div class="info">';
1295 $str .= $count.' '.$strnumpostssince;
f8716988 1296 if (!empty($showunread)) {
e23800b7 1297 $str .= '<br />'.$thisunread .' '.$strnumunread;
f8716988 1298 }
e23800b7 1299 $str .= '</div></div>';
f8716988 1300 }
2b63df96 1301 if (!empty($str)) {
185cfb09 1302 if (!array_key_exists($forum->course,$htmlarray)) {
1303 $htmlarray[$forum->course] = array();
1304 }
1305 if (!array_key_exists('forum',$htmlarray[$forum->course])) {
1306 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
1307 }
1308 $htmlarray[$forum->course]['forum'] .= $str;
1309 }
2b63df96 1310 }
0d6b9d4f 1311}
1312
0a4ac01b 1313/**
1314 * Given a course and a date, prints a summary of all the new
1315 * messages posted in the course since that date
df1ba0f4 1316 *
1317 * @global object
1318 * @global object
1319 * @global object
1320 * @uses CONTEXT_MODULE
1321 * @uses VISIBLEGROUPS
90f4745c 1322 * @param object $course
1323 * @param bool $viewfullnames capability
1324 * @param int $timestart
1325 * @return bool success
0a4ac01b 1326 */
dd97c328 1327function forum_print_recent_activity($course, $viewfullnames, $timestart) {
cb860491 1328 global $CFG, $USER, $DB, $OUTPUT;
caadf009 1329
dd97c328 1330 // do not use log table if possible, it may be huge and is expensive to join with other tables
caadf009 1331
4e445355 1332 if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
1333 d.timestart, d.timeend, d.userid AS duserid,
1334 u.firstname, u.lastname, u.email, u.picture
1335 FROM {forum_posts} p
1336 JOIN {forum_discussions} d ON d.id = p.discussion
1337 JOIN {forum} f ON f.id = d.forum
1338 JOIN {user} u ON u.id = p.userid
1339 WHERE p.created > ? AND f.course = ?
1340 ORDER BY p.id ASC", array($timestart, $course->id))) { // order by initial posting date
dd97c328 1341 return false;
1b5910c4 1342 }
1343
dd97c328 1344 $modinfo =& get_fast_modinfo($course);
dcde9f02 1345
dd97c328 1346 $groupmodes = array();
1347 $cms = array();
d05956ac 1348
dd97c328 1349 $strftimerecent = get_string('strftimerecent');
d05956ac 1350
dd97c328 1351 $printposts = array();
1352 foreach ($posts as $post) {
1353 if (!isset($modinfo->instances['forum'][$post->forum])) {
1354 // not visible
1355 continue;
1356 }
1357 $cm = $modinfo->instances['forum'][$post->forum];
1358 if (!$cm->uservisible) {
1359 continue;
1360 }
6b7de0bb 1361 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1362
1363 if (!has_capability('mod/forum:viewdiscussion', $context)) {
1364 continue;
1365 }
583b57b4 1366
dd97c328 1367 if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
1368 and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
6b7de0bb 1369 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
dd97c328 1370 continue;
ac1d9a22 1371 }
dd97c328 1372 }
583b57b4 1373
dd97c328 1374 $groupmode = groups_get_activity_groupmode($cm, $course);
b91d6dcd 1375
dd97c328 1376 if ($groupmode) {
6b7de0bb 1377 if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
dd97c328 1378 // oki (Open discussions have groupid -1)
1379 } else {
1380 // separate mode
1381 if (isguestuser()) {
1382 // shortcut
1383 continue;
1384 }
2b63df96 1385
dd97c328 1386 if (is_null($modinfo->groups)) {
1387 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
1b5910c4 1388 }
ac1d9a22 1389
dd97c328 1390 if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
1391 continue;
1392 }
ac1d9a22 1393 }
dd97c328 1394 }
8f7dc7f1 1395
dd97c328 1396 $printposts[] = $post;
1397 }
1398 unset($posts);
8f7dc7f1 1399
dd97c328 1400 if (!$printposts) {
1401 return false;
1402 }
1403
cb860491 1404 echo $OUTPUT->heading(get_string('newforumposts', 'forum').':', 3);
dd97c328 1405 echo "\n<ul class='unlist'>\n";
1406
1407 foreach ($printposts as $post) {
1408 $subjectclass = empty($post->parent) ? ' bold' : '';
1409
1410 echo '<li><div class="head">'.
1411 '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
1412 '<div class="name">'.fullname($post, $viewfullnames).'</div>'.
1413 '</div>';
1414 echo '<div class="info'.$subjectclass.'">';
1415 if (empty($post->parent)) {
1416 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
1417 } else {
1418 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
caadf009 1419 }
dd97c328 1420 $post->subject = break_up_long_words(format_string($post->subject, true));
1421 echo $post->subject;
1422 echo "</a>\"</div></li>\n";
caadf009 1423 }
dd97c328 1424
1306c5ea 1425 echo "</ul>\n";
dd97c328 1426
1427 return true;
caadf009 1428}
1429
353228d8 1430/**
1431 * Return grade for given user or all users.
1432 *
df1ba0f4 1433 * @global object
1434 * @global object
1435 * @param object $forum
353228d8 1436 * @param int $userid optional user id, 0 means all users
1437 * @return array array of grades, false if none
1438 */
612607bd 1439function forum_get_user_grades($forum, $userid=0) {
63e87951 1440 global $CFG;
df997f84 1441
63e87951
AD
1442 require_once($CFG->dirroot.'/rating/lib.php');
1443 $rm = new rating_manager();
df997f84 1444
63e87951 1445 $ratingoptions = new stdclass();
353228d8 1446
63e87951
AD
1447 //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
1448 $ratingoptions->modulename = 'forum';
1449 $ratingoptions->moduleid = $forum->id;
1450 //$ratingoptions->cmidnumber = $forum->cmidnumber;
4e445355 1451
63e87951
AD
1452 $ratingoptions->userid = $userid;
1453 $ratingoptions->aggregationmethod = $forum->assessed;
1454 $ratingoptions->scaleid = $forum->scale;
1455 $ratingoptions->itemtable = 'forum_posts';
1456 $ratingoptions->itemtableusercolumn = 'userid';
353228d8 1457
63e87951 1458 return $rm->get_user_grades($ratingoptions);
353228d8 1459}
caadf009 1460
0a4ac01b 1461/**
775f811a 1462 * Update activity grades
353228d8 1463 *
df1ba0f4 1464 * @global object
1465 * @global object
775f811a 1466 * @param object $forum
1467 * @param int $userid specific user only, 0 means all
6b7de0bb 1468 * @param boolean $nullifnone return null if grade does not exist
90f4745c 1469 * @return void
0a4ac01b 1470 */
775f811a 1471function forum_update_grades($forum, $userid=0, $nullifnone=true) {
4e445355 1472 global $CFG, $DB;
775f811a 1473 require_once($CFG->libdir.'/gradelib.php');
caadf009 1474
775f811a 1475 if (!$forum->assessed) {
1476 forum_grade_item_update($forum);
02ebf404 1477
775f811a 1478 } else if ($grades = forum_get_user_grades($forum, $userid)) {
1479 forum_grade_item_update($forum, $grades);
eafb9d9e 1480
775f811a 1481 } else if ($userid and $nullifnone) {
39790bd8 1482 $grade = new stdClass();
775f811a 1483 $grade->userid = $userid;
1484 $grade->rawgrade = NULL;
1485 forum_grade_item_update($forum, $grade);
02ebf404 1486
353228d8 1487 } else {
775f811a 1488 forum_grade_item_update($forum);
1489 }
1490}
1491
1492/**
1493 * Update all grades in gradebook.
df1ba0f4 1494 * @global object
775f811a 1495 */
1496function forum_upgrade_grades() {
1497 global $DB;
1498
1499 $sql = "SELECT COUNT('x')
1500 FROM {forum} f, {course_modules} cm, {modules} m
1501 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1502 $count = $DB->count_records_sql($sql);
1503
1504 $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid
1505 FROM {forum} f, {course_modules} cm, {modules} m
1506 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
1507 if ($rs = $DB->get_recordset_sql($sql)) {
775f811a 1508 $pbar = new progress_bar('forumupgradegrades', 500, true);
1509 $i=0;
1510 foreach ($rs as $forum) {
1511 $i++;
1512 upgrade_set_timeout(60*5); // set up timeout, may also abort execution
1513 forum_update_grades($forum, 0, false);
1514 $pbar->update($i, $count, "Updating Forum grades ($i/$count).");
caadf009 1515 }
775f811a 1516 $rs->close();
353228d8 1517 }
1518}
1519
1520/**
612607bd 1521 * Create/update grade item for given forum
353228d8 1522 *
df1ba0f4 1523 * @global object
1524 * @uses GRADE_TYPE_NONE
1525 * @uses GRADE_TYPE_VALUE
1526 * @uses GRADE_TYPE_SCALE
353228d8 1527 * @param object $forum object with extra cmidnumber
df1ba0f4 1528 * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
612607bd 1529 * @return int 0 if ok
353228d8 1530 */
0b5a80a1 1531function forum_grade_item_update($forum, $grades=NULL) {
612607bd 1532 global $CFG;
1533 if (!function_exists('grade_update')) { //workaround for buggy PHP versions
1534 require_once($CFG->libdir.'/gradelib.php');
353228d8 1535 }
1536
612607bd 1537 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
353228d8 1538
5980d52f 1539 if (!$forum->assessed or $forum->scale == 0) {
612607bd 1540 $params['gradetype'] = GRADE_TYPE_NONE;
353228d8 1541
1542 } else if ($forum->scale > 0) {
1543 $params['gradetype'] = GRADE_TYPE_VALUE;
1544 $params['grademax'] = $forum->scale;
1545 $params['grademin'] = 0;
1546
1547 } else if ($forum->scale < 0) {
1548 $params['gradetype'] = GRADE_TYPE_SCALE;
1549 $params['scaleid'] = -$forum->scale;
1550 }
1551
0b5a80a1 1552 if ($grades === 'reset') {
1553 $params['reset'] = true;
1554 $grades = NULL;
1555 }
1556
1557 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
353228d8 1558}
1559
1560/**
1561 * Delete grade item for given forum
1562 *
df1ba0f4 1563 * @global object
353228d8 1564 * @param object $forum object
1565 * @return object grade_item
1566 */
1567function forum_grade_item_delete($forum) {
612607bd 1568 global $CFG;
1569 require_once($CFG->libdir.'/gradelib.php');
1570
b67ec72f 1571 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
caadf009 1572}
1573
353228d8 1574
0a4ac01b 1575/**
1576 * Returns the users with data in one forum
63e87951 1577 * (users with records in forum_subscriptions, forum_posts, students)
df1ba0f4 1578 *
1579 * @global object
1580 * @global object
90f4745c 1581 * @param int $forumid
1582 * @return mixed array or false if none
0a4ac01b 1583 */
05855091 1584function forum_get_participants($forumid) {
05855091 1585
4e445355 1586 global $CFG, $DB;
05855091 1587
1588 //Get students from forum_subscriptions
4e445355 1589 $st_subscriptions = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1590 FROM {user} u,
1591 {forum_subscriptions} s
1592 WHERE s.forum = ? AND
1593 u.id = s.userid", array($forumid));
05855091 1594 //Get students from forum_posts
4e445355 1595 $st_posts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1596 FROM {user} u,
1597 {forum_discussions} d,
1598 {forum_posts} p
1599 WHERE d.forum = ? AND
1600 p.discussion = d.id AND
1601 u.id = p.userid", array($forumid));
05855091 1602
63e87951 1603 //Get students from the ratings table
4e445355 1604 $st_ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
1605 FROM {user} u,
1606 {forum_discussions} d,
1607 {forum_posts} p,
63e87951 1608 {ratings} r
4e445355 1609 WHERE d.forum = ? AND
1610 p.discussion = d.id AND
1611 r.post = p.id AND
1612 u.id = r.userid", array($forumid));
05855091 1613
1614 //Add st_posts to st_subscriptions
1615 if ($st_posts) {
1616 foreach ($st_posts as $st_post) {
1617 $st_subscriptions[$st_post->id] = $st_post;
1618 }
1619 }
1620 //Add st_ratings to st_subscriptions
1621 if ($st_ratings) {
1622 foreach ($st_ratings as $st_rating) {
1623 $st_subscriptions[$st_rating->id] = $st_rating;
1624 }
1625 }
1626 //Return st_subscriptions array (it contains an array of unique users)
1627 return ($st_subscriptions);
1628}
caadf009 1629
0a4ac01b 1630/**
90f4745c 1631 * This function returns if a scale is being used by one forum
df1ba0f4 1632 *
1633 * @global object
90f4745c 1634 * @param int $forumid
1635 * @param int $scaleid negative number
1636 * @return bool
0a4ac01b 1637 */
0f1a97c2 1638function forum_scale_used ($forumid,$scaleid) {
4e445355 1639 global $DB;
0f1a97c2 1640 $return = false;
65b0e537 1641
4e445355 1642 $rec = $DB->get_record("forum",array("id" => "$forumid","scale" => "-$scaleid"));
65b0e537 1643
fa22fd5f 1644 if (!empty($rec) && !empty($scaleid)) {
0f1a97c2 1645 $return = true;
1646 }
65b0e537 1647
0f1a97c2 1648 return $return;
1649}
1650
85c9ebb9 1651/**
1652 * Checks if scale is being used by any instance of forum
1653 *
1654 * This is used to find out if scale used anywhere
df1ba0f4 1655 *
1656 * @global object
85c9ebb9 1657 * @param $scaleid int
1658 * @return boolean True if the scale is used by any forum
1659 */
1660function forum_scale_used_anywhere($scaleid) {
4e445355 1661 global $DB;
1662 if ($scaleid and $DB->record_exists('forum', array('scale' => -$scaleid))) {
85c9ebb9 1663 return true;
1664 } else {
1665 return false;
1666 }
1667}
1668
0a4ac01b 1669// SQL FUNCTIONS ///////////////////////////////////////////////////////////
9fa49e22 1670
0a4ac01b 1671/**
1672 * Gets a post with all info ready for forum_print_post
1673 * Most of these joins are just to get the forum id
df1ba0f4 1674 *
1675 * @global object
1676 * @global object
90f4745c 1677 * @param int $postid
1678 * @return mixed array of posts or false
0a4ac01b 1679 */
1f48942e 1680function forum_get_post_full($postid) {
4e445355 1681 global $CFG, $DB;
1f48942e 1682
4e445355 1683 return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1684 FROM {forum_posts} p
1685 JOIN {forum_discussions} d ON p.discussion = d.id
1686 LEFT JOIN {user} u ON p.userid = u.id
1687 WHERE p.id = ?", array($postid));
1f48942e 1688}
1689
0a4ac01b 1690/**
1691 * Gets posts with all info ready for forum_print_post
1692 * We pass forumid in because we always know it so no need to make a
1693 * complicated join to find it out.
df1ba0f4 1694 *
1695 * @global object
1696 * @global object
90f4745c 1697 * @return mixed array of posts or false
0a4ac01b 1698 */
77efef3e 1699function forum_get_discussion_posts($discussion, $sort, $forumid) {
4e445355 1700 global $CFG, $DB;
1f48942e 1701
4e445355 1702 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1703 FROM {forum_posts} p
1704 LEFT JOIN {user} u ON p.userid = u.id
1705 WHERE p.discussion = ?
1706 AND p.parent > 0 $sort", array($discussion));
1f48942e 1707}
1708
65bcf17b 1709/**
1710 * Gets all posts in discussion including top parent.
df1ba0f4 1711 *
1712 * @global object
1713 * @global object
1714 * @global object
90f4745c 1715 * @param int $discussionid
1716 * @param string $sort
1717 * @param bool $tracking does user track the forum?
1718 * @return array of posts
65bcf17b 1719 */
90f4745c 1720function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
4e445355 1721 global $CFG, $DB, $USER;
65bcf17b 1722
3c2bf848 1723 $tr_sel = "";
1724 $tr_join = "";
4e445355 1725 $params = array();
3c2bf848 1726
90f4745c 1727 if ($tracking) {
1728 $now = time();
1729 $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
1730 $tr_sel = ", fr.id AS postread";
4e445355 1731 $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)";
1732 $params[] = $USER->id;
90f4745c 1733 }
1734
4e445355 1735 $params[] = $discussionid;
1736 if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
1737 FROM {forum_posts} p
1738 LEFT JOIN {user} u ON p.userid = u.id
90f4745c 1739 $tr_join
4e445355 1740 WHERE p.discussion = ?
1741 ORDER BY $sort", $params)) {
65bcf17b 1742 return array();
1743 }
1744
1745 foreach ($posts as $pid=>$p) {
90f4745c 1746 if ($tracking) {
1747 if (forum_tp_is_post_old($p)) {
6b7de0bb 1748 $posts[$pid]->postread = true;
90f4745c 1749 }
1750 }
65bcf17b 1751 if (!$p->parent) {
1752 continue;
1753 }
1754 if (!isset($posts[$p->parent])) {
1755 continue; // parent does not exist??
1756 }
1757 if (!isset($posts[$p->parent]->children)) {
1758 $posts[$p->parent]->children = array();
1759 }
1760 $posts[$p->parent]->children[$pid] =& $posts[$pid];
1761 }
1762
1763 return $posts;
1764}
1765
0a4ac01b 1766/**
1767 * Gets posts with all info ready for forum_print_post
1768 * We pass forumid in because we always know it so no need to make a
1769 * complicated join to find it out.
df1ba0f4 1770 *
1771 * @global object
1772 * @global object
1773 * @param int $parent
1774 * @param int $forumid
1775 * @return array
0a4ac01b 1776 */
77efef3e 1777function forum_get_child_posts($parent, $forumid) {
4e445355 1778 global $CFG, $DB;
1f48942e 1779
4e445355 1780 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
1781 FROM {forum_posts} p
1782 LEFT JOIN {user} u ON p.userid = u.id
1783 WHERE p.parent = ?
1784 ORDER BY p.created ASC", array($parent));
1f48942e 1785}
1786
42fb3c85 1787/**
1788 * An array of forum objects that the user is allowed to read/search through.
df1ba0f4 1789 *
1790 * @global object
1791 * @global object
1792 * @global object
1793 * @param int $userid
1794 * @param int $courseid if 0, we look for forums throughout the whole site.
42fb3c85 1795 * @return array of forum objects, or false if no matches
1796 * Forum objects have the following attributes:
1797 * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
1798 * viewhiddentimedposts
1799 */
1800function forum_get_readable_forums($userid, $courseid=0) {
2b63df96 1801
4e445355 1802 global $CFG, $DB, $USER;
6b7de0bb 1803 require_once($CFG->dirroot.'/course/lib.php');
2b63df96 1804
4e445355 1805 if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) {
12e57b92 1806 print_error('notinstalled', 'forum');
42fb3c85 1807 }
2b63df96 1808
42fb3c85 1809 if ($courseid) {
4e445355 1810 $courses = $DB->get_records('course', array('id' => $courseid));
42fb3c85 1811 } else {
65bcf17b 1812 // If no course is specified, then the user can see SITE + his courses.
4e445355 1813 $courses1 = $DB->get_records('course', array('id' => SITEID));
df997f84 1814 $courses2 = enrol_get_users_courses($userid, true);
6155150c 1815 $courses = array_merge($courses1, $courses2);
42fb3c85 1816 }
1817 if (!$courses) {
6b7de0bb 1818 return array();
42fb3c85 1819 }
1820
1821 $readableforums = array();
2b63df96 1822
6527b5c2 1823 foreach ($courses as $course) {
1824
6b7de0bb 1825 $modinfo =& get_fast_modinfo($course);
1826 if (is_null($modinfo->groups)) {
1827 $modinfo->groups = groups_get_user_groups($course->id, $userid);
42fb3c85 1828 }
2b63df96 1829
6b7de0bb 1830 if (empty($modinfo->instances['forum'])) {
1831 // hmm, no forums?
1832 continue;
1833 }
2b63df96 1834
4e445355 1835 $courseforums = $DB->get_records('forum', array('course' => $course->id));
2b63df96 1836
6b7de0bb 1837 foreach ($modinfo->instances['forum'] as $forumid => $cm) {
1838 if (!$cm->uservisible or !isset($courseforums[$forumid])) {
1839 continue;
1840 }
1841 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1842 $forum = $courseforums[$forumid];
2e945910
AB
1843 $forum->context = $context;
1844 $forum->cm = $cm;
d50704bf 1845
6b7de0bb 1846 if (!has_capability('mod/forum:viewdiscussion', $context)) {
1847 continue;
1848 }
6527b5c2 1849
6b7de0bb 1850 /// group access
1851 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
1852 if (is_null($modinfo->groups)) {
1853 $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
1854 }
98da6021 1855 if (isset($modinfo->groups[$cm->groupingid])) {
6b7de0bb 1856 $forum->onlygroups = $modinfo->groups[$cm->groupingid];
1857 $forum->onlygroups[] = -1;
1858 } else {
1859 $forum->onlygroups = array(-1);
1860 }
1861 }
2b63df96 1862
6b7de0bb 1863 /// hidden timed discussions
1864 $forum->viewhiddentimedposts = true;
1865 if (!empty($CFG->forum_enabletimedposts)) {
1866 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
1867 $forum->viewhiddentimedposts = false;
1868 }
1869 }
d50704bf 1870
6b7de0bb 1871 /// qanda access
1872 if ($forum->type == 'qanda'
1873 && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
2b63df96 1874
6b7de0bb 1875 // We need to check whether the user has posted in the qanda forum.
1876 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
1877 // the user is allowed to see in this forum.
1878 if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
1879 foreach ($discussionspostedin as $d) {
1880 $forum->onlydiscussions[] = $d->id;
d50704bf 1881 }
42fb3c85 1882 }
1883 }
6b7de0bb 1884
1885 $readableforums[$forum->id] = $forum;
42fb3c85 1886 }
6b7de0bb 1887
1888 unset($modinfo);
1889
42fb3c85 1890 } // End foreach $courses
2b63df96 1891
42fb3c85 1892 return $readableforums;
1893}
1894
bbbf2d40 1895/**
1896 * Returns a list of posts found using an array of search terms.
df1ba0f4 1897 *
1898 * @global object
1899 * @global object
1900 * @global object
1901 * @param array $searchterms array of search terms, e.g. word +word -word
1902 * @param int $courseid if 0, we search through the whole site
1903 * @param int $limitfrom
1904 * @param int $limitnum
1905 * @param int &$totalcount
1906 * @param string $extrasql
1907 * @return array|bool Array of posts found or false
42fb3c85 1908 */
2b63df96 1909function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
42fb3c85 1910 &$totalcount, $extrasql='') {
4e445355 1911 global $CFG, $DB, $USER;
42fb3c85 1912 require_once($CFG->libdir.'/searchlib.php');
1913
1914 $forums = forum_get_readable_forums($USER->id, $courseid);
2b63df96 1915
67875aa1 1916 if (count($forums) == 0) {
6b7de0bb 1917 $totalcount = 0;
67875aa1 1918 return false;
1919 }
42fb3c85 1920
6b7de0bb 1921 $now = round(time(), -2); // db friendly
1922
1923 $fullaccess = array();
1924 $where = array();
4e445355 1925 $params = array();
6b7de0bb 1926
1927 foreach ($forums as $forumid => $forum) {
1928 $select = array();
1929
1930 if (!$forum->viewhiddentimedposts) {
004efe66 1931 $select[] = "(d.userid = :userid OR (d.timestart < : AND (d.timeend = 0 OR d.timeend > :timeend)))";
1932 $params = array('userid'=>$USER->id, 'timestart'=>$now, 'timeend'=>$now);
42fb3c85 1933 }
6b7de0bb 1934
2e945910
AB
1935 $cm = $forum->cm;
1936 $context = $forum->context;
ad9c22aa 1937
1938 if ($forum->type == 'qanda'
1939 && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
6b7de0bb 1940 if (!empty($forum->onlydiscussions)) {
004efe66 1941 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda0');
4e445355 1942 $params = array_merge($params, $discussionid_params);
1943 $select[] = "(d.id $discussionid_sql OR p.parent = 0)";
d50704bf 1944 } else {
6b7de0bb 1945 $select[] = "p.parent = 0";
d50704bf 1946 }
1947 }
6b7de0bb 1948
1949 if (!empty($forum->onlygroups)) {
004efe66 1950 list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps0');
4e445355 1951 $params = array_merge($params, $groupid_params);
1952 $select[] = "d.groupid $groupid_sql";
42fb3c85 1953 }
6b7de0bb 1954
1955 if ($select) {
1956 $selects = implode(" AND ", $select);
004efe66 1957 $where[] = "(d.forum = :forum AND $selects)";
1958 $params['forum'] = $forumid;
6b7de0bb 1959 } else {
1960 $fullaccess[] = $forumid;
1961 }
1962 }
1963
1964 if ($fullaccess) {
004efe66 1965 list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula0');
4e445355 1966 $params = array_merge($params, $fullid_params);
1967 $where[] = "(d.forum $fullid_sql)";
42fb3c85 1968 }
42fb3c85 1969
6b7de0bb 1970 $selectdiscussion = "(".implode(" OR ", $where).")";
42fb3c85 1971
42fb3c85 1972 $messagesearch = '';
1973 $searchstring = '';
2b63df96 1974
42fb3c85 1975 // Need to concat these back together for parser to work.
1976 foreach($searchterms as $searchterm){
1977 if ($searchstring != '') {
1978 $searchstring .= ' ';
1979 }
1980 $searchstring .= $searchterm;
1981 }
1982
1983 // We need to allow quoted strings for the search. The quotes *should* be stripped
1984 // by the parser, but this should be examined carefully for security implications.
1985 $searchstring = str_replace("\\\"","\"",$searchstring);
1986 $parser = new search_parser();
1987 $lexer = new search_lexer($parser);
1988
1989 if ($lexer->parse($searchstring)) {
1990 $parsearray = $parser->get_parsed_array();
0a4ac01b 1991 // Experimental feature under 1.8! MDL-8830
1992 // Use alternative text searches if defined
1993 // This feature only works under mysql until properly implemented for other DBs
1994 // Requires manual creation of text index for forum_posts before enabling it:
1995 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
1996 // Experimental feature under 1.8! MDL-8830
532daab4 1997 if (!empty($CFG->forum_usetextsearches)) {
004efe66 1998 list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
532daab4 1999 'p.userid', 'u.id', 'u.firstname',
2000 'u.lastname', 'p.modified', 'd.forum');
2001 } else {
004efe66 2002 list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject',
532daab4 2003 'p.userid', 'u.id', 'u.firstname',
2004 'u.lastname', 'p.modified', 'd.forum');
2005 }
004efe66 2006 $params = array_merge($params, $msparams);
42fb3c85 2007 }
2008
4e445355 2009 $fromsql = "{forum_posts} p,
2010 {forum_discussions} d,
2011 {user} u";
42fb3c85 2012
2013 $selectsql = " $messagesearch
2014 AND p.discussion = d.id
2015 AND p.userid = u.id
2016 AND $selectdiscussion
2017 $extrasql";
2018
2019 $countsql = "SELECT COUNT(*)
2020 FROM $fromsql
2021 WHERE $selectsql";
2022
7f094149 2023 $searchsql = "SELECT p.*,
42fb3c85 2024 d.forum,
2025 u.firstname,
2026 u.lastname,
2027 u.email,
8ba59d07 2028 u.picture,
3a11c09f
PS
2029 u.imagealt,
2030 u.email
42fb3c85 2031 FROM $fromsql
2032 WHERE $selectsql
2033 ORDER BY p.modified DESC";
2034
4e445355 2035 $totalcount = $DB->count_records_sql($countsql, $params);
d50704bf 2036
4e445355 2037 return $DB->get_records_sql($searchsql, $params, $limitfrom, $limitnum);
42fb3c85 2038}
2039
0a4ac01b 2040/**
2041 * Returns a list of ratings for a particular post - sorted.
df1ba0f4 2042 *
2043 * @global object
2044 * @global object
90f4745c 2045 * @param int $postid
2046 * @param string $sort
df1ba0f4 2047 * @return array Array of ratings or false
0a4ac01b 2048 */
63e87951
AD
2049function forum_get_ratings($context, $postid, $sort="u.firstname ASC") {
2050 global $PAGE;
2051
2052 $options = new stdclass();
2053 $options->context = $PAGE->context;
2054 $options->itemid = $postid;
2055 $options->sort = "ORDER BY $sort";
2056
0bb69ab3
PS
2057 $rm = new rating_manager();
2058 $rm->get_all_ratings_for_item($options);
9fa49e22 2059}
2060
0a4ac01b 2061/**
2062 * Returns a list of all new posts that have not been mailed yet
df1ba0f4 2063 *
2064 * @global object
2065 * @global object
2066 * @param int $starttime posts created after this time
2067 * @param int $endtime posts created before this
2068 * @param int $now used for timed discussions only
2069 * @return array
0a4ac01b 2070 */
90f4745c 2071function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
4e445355 2072 global $CFG, $DB;
90f4745c 2073
4e445355 2074 $params = array($starttime, $endtime);
90f4745c 2075 if (!empty($CFG->forum_enabletimedposts)) {
2076 if (empty($now)) {
2077 $now = time();
2078 }
4e445355 2079 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2080 $params[] = $now;
2081 $params[] = $now;
90f4745c 2082 } else {
2083 $timedsql = "";
2084 }
2085
4e445355 2086 return $DB->get_records_sql("SELECT p.*, d.course, d.forum
2087 FROM {forum_posts} p
2088 JOIN {forum_discussions} d ON d.id = p.discussion
65b0e537 2089 WHERE p.mailed = 0
4e445355 2090 AND p.created >= ?
2091 AND (p.created < ? OR p.mailnow = 1)
90f4745c 2092 $timedsql
4e445355 2093 ORDER BY p.modified ASC", $params);
1f48942e 2094}
2095
0a4ac01b 2096/**
2097 * Marks posts before a certain time as being mailed already
df1ba0f4 2098 *
2099 * @global object
2100 * @global object
2101 * @param int $endtime
2102 * @param int $now Defaults to time()
2103 * @return bool
0a4ac01b 2104 */
90f4745c 2105function forum_mark_old_posts_as_mailed($endtime, $now=null) {
4e445355 2106 global $CFG, $DB;
90f4745c 2107 if (empty($now)) {
2108 $now = time();
2109 }
2110
2111 if (empty($CFG->forum_enabletimedposts)) {
4e445355 2112 return $DB->execute("UPDATE {forum_posts}
90f4745c 2113 SET mailed = '1'
4e445355 2114 WHERE (created < ? OR mailnow = 1)
3342790c 2115 AND mailed = 0", array($endtime));
90f4745c 2116
0f620d4b 2117 } else {
4e445355 2118 return $DB->execute("UPDATE {forum_posts}
90f4745c 2119 SET mailed = '1'
2120 WHERE discussion NOT IN (SELECT d.id
4e445355 2121 FROM {forum_discussions} d
2122 WHERE d.timestart > ?)
2123 AND (created < ? OR mailnow = 1)
3342790c 2124 AND mailed = 0", array($now, $endtime));
0f620d4b 2125 }
3ecca1ee 2126}
2127
0a4ac01b 2128/**
2129 * Get all the posts for a user in a forum suitable for forum_print_post
df1ba0f4 2130 *
2131 * @global object
2132 * @global object
2133 * @uses CONTEXT_MODULE
2134 * @return array
0a4ac01b 2135 */
1f48942e 2136function forum_get_user_posts($forumid, $userid) {
4e445355 2137 global $CFG, $DB;
1f48942e 2138
90f4745c 2139 $timedsql = "";
4e445355 2140 $params = array($forumid, $userid);
2141
90f4745c 2142 if (!empty($CFG->forum_enabletimedposts)) {
2143 $cm = get_coursemodule_from_instance('forum', $forumid);
2144 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2145 $now = time();
4e445355 2146 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2147 $params[] = $now;
2148 $params[] = $now;
6b7de0bb 2149 }
90f4745c 2150 }
2151
4e445355 2152 return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
2153 FROM {forum} f
2154 JOIN {forum_discussions} d ON d.forum = f.id
2155 JOIN {forum_posts} p ON p.discussion = d.id
2156 JOIN {user} u ON u.id = p.userid
2157 WHERE f.id = ?
2158 AND p.userid = ?
90f4745c 2159 $timedsql
4e445355 2160 ORDER BY p.modified ASC", $params);
1f48942e 2161}
2162
90f4745c 2163/**
2164 * Get all the discussions user participated in
df1ba0f4 2165 *
2166 * @global object
2167 * @global object
2168 * @uses CONTEXT_MODULE
90f4745c 2169 * @param int $forumid
2170 * @param int $userid
df1ba0f4 2171 * @return array Array or false
90f4745c 2172 */
2173function forum_get_user_involved_discussions($forumid, $userid) {
4e445355 2174 global $CFG, $DB;
90f4745c 2175
2176 $timedsql = "";
4e445355 2177 $params = array($forumid, $userid);
90f4745c 2178 if (!empty($CFG->forum_enabletimedposts)) {
2179 $cm = get_coursemodule_from_instance('forum', $forumid);
2180 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2181 $now = time();
4e445355 2182 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2183 $params[] = $now;
2184 $params[] = $now;
6b7de0bb 2185 }
90f4745c 2186 }
2187
4e445355 2188 return $DB->get_records_sql("SELECT DISTINCT d.*
2189 FROM {forum} f
2190 JOIN {forum_discussions} d ON d.forum = f.id
2191 JOIN {forum_posts} p ON p.discussion = d.id
2192 WHERE f.id = ?
2193 AND p.userid = ?
2194 $timedsql", $params);
90f4745c 2195}
2196
2197/**
2198 * Get all the posts for a user in a forum suitable for forum_print_post
df1ba0f4 2199 *
2200 * @global object
2201 * @global object
90f4745c 2202 * @param int $forumid
2203 * @param int $userid
2204 * @return array of counts or false
2205 */
2206function forum_count_user_posts($forumid, $userid) {
4e445355 2207 global $CFG, $DB;
90f4745c 2208
2209 $timedsql = "";
4e445355 2210 $params = array($forumid, $userid);
90f4745c 2211 if (!empty($CFG->forum_enabletimedposts)) {
2212 $cm = get_coursemodule_from_instance('forum', $forumid);
2213 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
2214 $now = time();
4e445355 2215 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
2216 $params[] = $now;
2217 $params[] = $now;
6b7de0bb 2218 }
90f4745c 2219 }
2220
4e445355 2221 return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
2222 FROM {forum} f
2223 JOIN {forum_discussions} d ON d.forum = f.id
2224 JOIN {forum_posts} p ON p.discussion = d.id
2225 JOIN {user} u ON u.id = p.userid
2226 WHERE f.id = ?
2227 AND p.userid = ?
2228 $timedsql", $params);
90f4745c 2229}
2230
0a4ac01b 2231/**
2232 * Given a log entry, return the forum post details for it.
df1ba0f4 2233 *
2234 * @global object
2235 * @global object
2236 * @param object $log
2237 * @return array|null
0a4ac01b 2238 */
1f48942e 2239function forum_get_post_from_log($log) {
4e445355 2240 global $CFG, $DB;
1f48942e 2241
2242 if ($log->action == "add post") {
2243
4e445355 2244 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 2245 u.firstname, u.lastname, u.email, u.picture
4e445355 2246 FROM {forum_discussions} d,
2247 {forum_posts} p,
2248 {forum} f,
2249 {user} u
2250 WHERE p.id = ?
65b0e537 2251 AND d.id = p.discussion
2252 AND p.userid = u.id
8f7dc7f1 2253 AND u.deleted <> '1'
4e445355 2254 AND f.id = d.forum", array($log->info));
1f48942e 2255
2256
2257 } else if ($log->action == "add discussion") {
2258
4e445355 2259 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
8f7dc7f1 2260 u.firstname, u.lastname, u.email, u.picture
4e445355 2261 FROM {forum_discussions} d,
2262 {forum_posts} p,
2263 {forum} f,
2264 {user} u
2265 WHERE d.id = ?
65b0e537 2266 AND d.firstpost = p.id
2267 AND p.userid = u.id
8f7dc7f1 2268 AND u.deleted <> '1'
4e445355 2269 AND f.id = d.forum", array($log->info));
1f48942e 2270 }
2271 return NULL;
2272}
2273
0a4ac01b 2274/**
2275 * Given a discussion id, return the first post from the discussion
df1ba0f4 2276 *
2277 * @global object
2278 * @global object
2279 * @param int $dicsussionid
2280 * @return array
0a4ac01b 2281 */
d05956ac 2282function forum_get_firstpost_from_discussion($discussionid) {
4e445355 2283 global $CFG, $DB;
d05956ac 2284
4e445355 2285 return $DB->get_record_sql("SELECT p.*
2286 FROM {forum_discussions} d,
2287 {forum_posts} p
2288 WHERE d.id = ?
2289 AND d.firstpost = p.id ", array($discussionid));
d05956ac 2290}
2291
0a4ac01b 2292/**
90f4745c 2293 * Returns an array of counts of replies to each discussion
df1ba0f4 2294 *
2295 * @global object
2296 * @global object
2297 * @param int $forumid
2298 * @param string $forumsort
2299 * @param int $limit
2300 * @param int $page
2301 * @param int $perpage
2302 * @return array
0a4ac01b 2303 */
90f4745c 2304function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
4e445355 2305 global $CFG, $DB;
1f48942e 2306
90f4745c 2307 if ($limit > 0) {
2308 $limitfrom = 0;
2309 $limitnum = $limit;
2310 } else if ($page != -1) {
2311 $limitfrom = $page*$perpage;
2312 $limitnum = $perpage;
2313 } else {
2314 $limitfrom = 0;
2315 $limitnum = 0;
2316 }
2317
2318 if ($forumsort == "") {
2319 $orderby = "";
2320 $groupby = "";
2321
2322 } else {
2323 $orderby = "ORDER BY $forumsort";
2324 $groupby = ", ".strtolower($forumsort);
2325 $groupby = str_replace('desc', '', $groupby);
2326 $groupby = str_replace('asc', '', $groupby);
2327 }
2328
bfeb10b7 2329 if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
2330 $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
4e445355 2331 FROM {forum_posts} p
2332 JOIN {forum_discussions} d ON p.discussion = d.id
2333 WHERE p.parent > 0 AND d.forum = ?
bfeb10b7 2334 GROUP BY p.discussion";
4e445355 2335 return $DB->get_records_sql($sql, array($forumid));
bfeb10b7 2336
90f4745c 2337 } else {
bfeb10b7 2338 $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
4e445355 2339 FROM {forum_posts} p
2340 JOIN {forum_discussions} d ON p.discussion = d.id
2341 WHERE d.forum = ?
bfeb10b7 2342 GROUP BY p.discussion $groupby
2343 $orderby";
4e445355 2344 return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum);
90f4745c 2345 }
2346}
2347
df1ba0f4 2348/**
2349 * @global object
2350 * @global object
2351 * @global object
2352 * @staticvar array $cache
2353 * @param object $forum
2354 * @param object $cm
2355 * @param object $course
2356 * @return mixed
2357 */
90f4745c 2358function forum_count_discussions($forum, $cm, $course) {
4e445355 2359 global $CFG, $DB, $USER;
90f4745c 2360
2361 static $cache = array();
2362
2363 $now = round(time(), -2); // db cache friendliness
2364
4e445355 2365 $params = array($course->id);
2366
90f4745c 2367 if (!isset($cache[$course->id])) {
2368 if (!empty($CFG->forum_enabletimedposts)) {
4e445355 2369 $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
2370 $params[] = $now;
2371 $params[] = $now;
90f4745c 2372 } else {
2373 $timedsql = "";
2374 }
2375
2376 $sql = "SELECT f.id, COUNT(d.id) as dcount
4e445355 2377 FROM {forum} f
2378 JOIN {forum_discussions} d ON d.forum = f.id
2379 WHERE f.course = ?
90f4745c 2380 $timedsql
2381 GROUP BY f.id";
a48e8c4b 2382
4e445355 2383 if ($counts = $DB->get_records_sql($sql, $params)) {
90f4745c 2384 foreach ($counts as $count) {
2385 $counts[$count->id] = $count->dcount;
2386 }
2387 $cache[$course->id] = $counts;
2388 } else {
2389 $cache[$course->id] = array();
2390 }
a48e8c4b 2391 }
90f4745c 2392
2393 if (empty($cache[$course->id][$forum->id])) {
2394 return 0;
a48e8c4b 2395 }
90f4745c 2396
2397 $groupmode = groups_get_activity_groupmode($cm, $course);
2398
2399 if ($groupmode != SEPARATEGROUPS) {
2400 return $cache[$course->id][$forum->id];
1f48942e 2401 }
90f4745c 2402
948091f4 2403 if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
90f4745c 2404 return $cache[$course->id][$forum->id];
2405 }
2406
2407 require_once($CFG->dirroot.'/course/lib.php');
2408
2409 $modinfo =& get_fast_modinfo($course);
2410 if (is_null($modinfo->groups)) {
2411 $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
2412 }
2413
98da6021
PS
2414 if (array_key_exists($cm->groupingid, $modinfo->groups)) {
2415 $mygroups = $modinfo->groups[$cm->groupingid];
90f4745c 2416 } else {
98da6021 2417 $mygroups = false; // Will be set below
90f4745c 2418 }
2419
2420 // add all groups posts
2421 if (empty($mygroups)) {
2422 $mygroups = array(-1=>-1);
2423 } else {
2424 $mygroups[-1] = -1;
2425 }
4e445355 2426
2427 list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups);
2428 $params[] = $forum->id;
90f4745c 2429
2430 if (!empty($CFG->forum_enabletimedposts)) {
2431 $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
4e445355 2432 $params[] = $now;
2433 $params[] = $now;
90f4745c 2434 } else {
2435 $timedsql = "";
2436 }
2437
2438 $sql = "SELECT COUNT(d.id)
4e445355 2439 FROM {forum_discussions} d
342650a7 2440 WHERE d.groupid $mygroups_sql AND d.forum = ?
90f4745c 2441 $timedsql";
2442
4e445355 2443 return $DB->get_field_sql($sql, $params);
1f48942e 2444}
2445
0a4ac01b 2446/**
63e87951 2447 * How many posts by other users are unrated by a given user in the given discussion?
df1ba0f4 2448 *
2449 * @global object
2450 * @global object
2451 * @param int $discussionid
2452 * @param int $userid
2453 * @return mixed
0a4ac01b 2454 */
1f48942e 2455function forum_count_unrated_posts($discussionid, $userid) {
4e445355 2456 global $CFG, $DB;
2457 if ($posts = $DB->get_record_sql("SELECT count(*) as num
2458 FROM {forum_posts}
65b0e537 2459 WHERE parent > 0
4e445355 2460 AND discussion = ?
2461 AND userid <> ? ", array($discussionid, $userid))) {
1f48942e 2462
4e445355 2463 if ($rated = $DB->get_record_sql("SELECT count(*) as num
2464 FROM {forum_posts} p,
63e87951 2465 {rating} r
4e445355 2466 WHERE p.discussion = ?
63e87951 2467 AND p.id = r.itemid
4e445355 2468 AND r.userid = ?", array($discussionid, $userid))) {
1f48942e 2469 $difference = $posts->num - $rated->num;
2470 if ($difference > 0) {
2471 return $difference;
2472 } else {
2473 return 0; // Just in case there was a counting error
2474 }
2475 } else {
2476 return $posts->num;
2477 }
2478 } else {
2479 return 0;
2480 }
2481}
2482
0a4ac01b 2483/**
2484 * Get all discussions in a forum
df1ba0f4 2485 *
2486 * @global object
2487 * @global object
2488 * @global object
2489 * @uses CONTEXT_MODULE
2490 * @uses VISIBLEGROUPS
2491 * @param object $cm
2492 * @param string $forumsort
2493 * @param bool $fullpost
2494 * @param int $unused
2495 * @param int $limit
2496 * @param bool $userlastmodified
2497 * @param int $page
2498 * @param int $perpage
2499 * @return array
0a4ac01b 2500 */
90f4745c 2501function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
4e445355 2502 global $CFG, $DB, $USER;
0fcac008 2503
3d284127 2504 $timelimit = '';
2505
90f4745c 2506 $now = round(time(), -2);
4e445355 2507 $params = array($cm->instance);
90f4745c 2508
4436a63b 2509 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2b63df96 2510
4436a63b 2511 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { /// User must have perms to view discussions
2512 return array();
2513 }
2514
2515 if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
2b63df96 2516
0468976c 2517 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
4e445355 2518 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
2519 $params[] = $now;
2520 $params[] = $now;
90f4745c 2521 if (isloggedin()) {
4e445355 2522 $timelimit .= " OR d.userid = ?";
2523 $params[] = $USER->id;
3d284127 2524 }
90f4745c 2525 $timelimit .= ")";
fbc21e82 2526 }
fbc21e82 2527 }
1f48942e 2528
90f4745c 2529 if ($limit > 0) {
2530 $limitfrom = 0;
2531 $limitnum = $limit;
2532 } else if ($page != -1) {
2533 $limitfrom = $page*$perpage;
2534 $limitnum = $perpage;
2535 } else {
2536 $limitfrom = 0;
2537 $limitnum = 0;
90ec387a 2538 }
8f0cd6ef 2539
90f4745c 2540 $groupmode = groups_get_activity_groupmode($cm);
2541 $currentgroup = groups_get_activity_group($cm);
353228d8 2542
fffa8b35 2543 if ($groupmode) {
2544 if (empty($modcontext)) {
2545 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2546 }
2547
2548 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2549 if ($currentgroup) {
4e445355 2550 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2551 $params[] = $currentgroup;
fffa8b35 2552 } else {
2553 $groupselect = "";
2554 }
2555
2556 } else {
2557 //seprate groups without access all
2558 if ($currentgroup) {
4e445355 2559 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2560 $params[] = $currentgroup;
fffa8b35 2561 } else {
2562 $groupselect = "AND d.groupid = -1";
2563 }
2564 }
353228d8 2565 } else {
02509fe6 2566 $groupselect = "";
2567 }
2862b309 2568
fffa8b35 2569
29507631 2570 if (empty($forumsort)) {
2571 $forumsort = "d.timemodified DESC";
2572 }
2ab968e9 2573 if (empty($fullpost)) {
b879effb 2574 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
2ab968e9 2575 } else {
2576 $postdata = "p.*";
2577 }
9197e147 2578
13597d01 2579 if (empty($userlastmodified)) { // We don't need to know this
fffa8b35 2580 $umfields = "";
2581 $umtable = "";
13597d01 2582 } else {
fffa8b35 2583 $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
4e445355 2584 $umtable = " LEFT JOIN {user} um ON (d.usermodified = um.id)";
90f4745c 2585 }
2586
2587 $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
2588 u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
4e445355 2589 FROM {forum_discussions} d
2590 JOIN {forum_posts} p ON p.discussion = d.id
2591 JOIN {user} u ON p.userid = u.id
90f4745c 2592 $umtable
4e445355 2593 WHERE d.forum = ? AND p.parent = 0
90f4745c 2594 $timelimit $groupselect
2595 ORDER BY $forumsort";
4e445355 2596 return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
90f4745c 2597}
2598
df1ba0f4 2599/**
2600 *
2601 * @global object
2602 * @global object
2603 * @global object
2604 * @uses CONTEXT_MODULE
2605 * @uses VISIBLEGROUPS
2606 * @param object $cm
2607 * @return array
2608 */
bfeb10b7 2609function forum_get_discussions_unread($cm) {
4e445355 2610 global $CFG, $DB, $USER;
90f4745c 2611
2612 $now = round(time(), -2);
ee151230 2613 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
35716b86 2614
ee151230 2615 $params = array();
90f4745c 2616 $groupmode = groups_get_activity_groupmode($cm);
2617 $currentgroup = groups_get_activity_group($cm);
2618
2619 if ($groupmode) {
2620 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2621
2622 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2623 if ($currentgroup) {
ee151230
AD
2624 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
2625 $params['currentgroup'] = $currentgroup;
90f4745c 2626 } else {
2627 $groupselect = "";
2628 }
2629
2630 } else {
ee151230 2631 //separate groups without access all
90f4745c 2632 if ($currentgroup) {
ee151230
AD
2633 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
2634 $params['currentgroup'] = $currentgroup;
90f4745c 2635 } else {
2636 $groupselect = "AND d.groupid = -1";
2637 }
2638 }
2639 } else {
2640 $groupselect = "";
13597d01 2641 }
2642
90f4745c 2643 if (!empty($CFG->forum_enabletimedposts)) {
ee151230
AD
2644 $timedsql = "AND d.timestart < :now1 AND (d.timeend = 0 OR d.timeend > :now2)";
2645 $params['now1'] = $now;
2646 $params['now2'] = $now;
90f4745c 2647 } else {
2648 $timedsql = "";
2649 }
2650
2651 $sql = "SELECT d.id, COUNT(p.id) AS unread
4e445355 2652 FROM {forum_discussions} d
2653 JOIN {forum_posts} p ON p.discussion = d.id
2654 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id)
90f4745c 2655 WHERE d.forum = {$cm->instance}
ee151230 2656 AND p.modified >= :cutoffdate AND r.id is NULL
90f4745c 2657 $groupselect
4e445355 2658 $timedsql
bfeb10b7 2659 GROUP BY d.id";
ee151230
AD
2660 $params['cutoffdate'] = $cutoffdate;
2661
4e445355 2662 if ($unreads = $DB->get_records_sql($sql, $params)) {
90f4745c 2663 foreach ($unreads as $unread) {
2664 $unreads[$unread->id] = $unread->unread;
2665 }
2666 return $unreads;
2667 } else {
2668 return array();
2669 }
1f48942e 2670}
2671
df1ba0f4 2672/**
2673 * @global object
2674 * @global object
2675 * @global object
2676 * @uses CONEXT_MODULE
2677 * @uses VISIBLEGROUPS
2678 * @param object $cm
2679 * @return array
2680 */
90f4745c 2681function forum_get_discussions_count($cm) {
4e445355 2682 global $CFG, $DB, $USER;
90f4745c 2683
2684 $now = round(time(), -2);
4e445355 2685 $params = array($cm->instance);
90f4745c 2686 $groupmode = groups_get_activity_groupmode($cm);
2687 $currentgroup = groups_get_activity_group($cm);
2688
2689 if ($groupmode) {
2690 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2691
2692 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
2693 if ($currentgroup) {
4e445355 2694 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2695 $params[] = $currentgroup;
90f4745c 2696 } else {
2697 $groupselect = "";
2698 }
2699
2700 } else {
2701 //seprate groups without access all
2702 if ($currentgroup) {
4e445355 2703 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
2704 $params[] = $currentgroup;
90f4745c 2705 } else {
2706 $groupselect = "AND d.groupid = -1";
2707 }
2708 }
2709 } else {
2710 $groupselect = "";
2711 }
2712
2713 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
2714
2715 $timelimit = "";
2716
2717 if (!empty($CFG->forum_enabletimedposts)) {
2718
2719 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2720
2721 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
4e445355 2722 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
2723 $params[] = $now;
2724 $params[] = $now;
90f4745c 2725 if (isloggedin()) {
4e445355 2726 $timelimit .= " OR d.userid = ?";
2727 $params[] = $USER->id;
90f4745c 2728 }
2729 $timelimit .= ")";
2730 }
2731 }
2732
2733 $sql = "SELECT COUNT(d.id)
4e445355 2734 FROM {forum_discussions} d
2735 JOIN {forum_posts} p ON p.discussion = d.id
2736 WHERE d.forum = ? AND p.parent = 0
2737 $groupselect $timelimit";
90f4745c 2738
4e445355 2739 return $DB->get_field_sql($sql, $params);
90f4745c 2740}
1f48942e 2741
2742
0a4ac01b 2743/**
2744 * Get all discussions started by a particular user in a course (or group)
2745 * This function no longer used ...
df1ba0f4 2746 *
2747 * @todo Remove this function if no longer used
2748 * @global object
2749 * @global object
2750 * @param int $courseid
2751 * @param int $userid
2752 * @param int $groupid
2753 * @return array
0a4ac01b 2754 */
b656e2a9 2755function forum_get_user_discussions($courseid, $userid, $groupid=0) {
4e445355 2756 global $CFG, $DB;
2757 $params = array($courseid, $userid);
b656e2a9 2758 if ($groupid) {
4e445355 2759 $groupselect = " AND d.groupid = ? ";
2760 $params[] = $groupid;
b656e2a9 2761 } else {
2762 $groupselect = "";
2763 }
2764
4e445355 2765 return $DB->get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
ebc3bd2b 2766 f.type as forumtype, f.name as forumname, f.id as forumid
4e445355 2767 FROM {forum_discussions} d,
2768 {forum_posts} p,
2769 {user} u,
2770 {forum} f
2771 WHERE d.course = ?
65b0e537 2772 AND p.discussion = d.id
2773 AND p.parent = 0
2774 AND p.userid = u.id
4e445355 2775 AND u.id = ?
b656e2a9 2776 AND d.forum = f.id $groupselect
4e445355 2777 ORDER BY p.created DESC", $params);
1f48942e 2778}
2779
0a4ac01b 2780/**
702dc57b 2781 * Get the list of potential subscribers to a forum.
8d8d0bfa 2782 *
2783 * @param object $forumcontext the forum context.
2784 * @param integer $groupid the id of a group, or 0 for all groups.
2785 * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
2786 * @param string $sort sort order. As for get_users_by_capability.
2787 * @return array list of users.
0a4ac01b 2788 */
8d8d0bfa 2789function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
2790 return get_users_by_capability($forumcontext, 'mod/forum:initialsubscriptions', $fields, $sort, '', '', $groupid, '', false, true);
2791}
0a4ac01b 2792
8d8d0bfa 2793/**
2794 * Returns list of user objects that are subscribed to this forum
2795 *
df1ba0f4 2796 * @global object
2797 * @global object
8d8d0bfa 2798 * @param object $course the course
2799 * @param forum $forum the forum
2800 * @param integer $groupid group id, or 0 for all.
2801 * @param object $context the forum context, to save re-fetching it where possible.
c35ae136 2802 * @param string $fields requested user fields (with "u." table prefix)
8d8d0bfa 2803 * @return array list of users.
2804 */
c35ae136 2805function forum_subscribed_users($course, $forum, $groupid=0, $context = null, $fields = null) {
4e445355 2806 global $CFG, $DB;
2807 $params = array($forum->id);
1f48942e 2808
6673d7bd 2809 if ($groupid) {
4e445355 2810 $grouptables = ", {groups_members} gm ";
2811 $groupselect = "AND gm.groupid = ? AND u.id = gm.userid";
2812 $params[] = $groupid;
6673d7bd 2813 } else {
669f2499 2814 $grouptables = '';
2815 $groupselect = '';
6673d7bd 2816 }
2817
c35ae136
PS
2818 if (empty($fields)) {
2819 $fields ="u.id,
2820 u.username,
2821 u.firstname,
2822 u.lastname,
2823 u.maildisplay,
2824 u.mailformat,
2825 u.maildigest,
2826 u.imagealt,
2827 u.email,
2828 u.city,
2829 u.country,
2830 u.lastaccess,
2831 u.lastlogin,
2832 u.picture,
2833 u.timezone,
2834 u.theme,
2835 u.lang,
2836 u.trackforums,
2837 u.mnethostid";
2838 }
4e445355 2839
a9900c73 2840 if (forum_is_forcesubscribed($forum)) {
48dcaf58 2841 if (empty($context)) {
2842 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
2843 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
8d8d0bfa 2844 }
c37fdcb5 2845 $sort = "u.email ASC";
8d8d0bfa 2846 $results = forum_get_potential_subscribers($context, $groupid, $fields, $sort);
669f2499 2847 } else {
4e445355 2848 $results = $DB->get_records_sql("SELECT $fields
2849 FROM {user} u,
2850 {forum_subscriptions} s $grouptables
2851 WHERE s.forum = ?
65b0e537 2852 AND s.userid = u.id
df1c2c71 2853 AND u.deleted = 0 $groupselect
4e445355 2854 ORDER BY u.email ASC", $params);
669f2499 2855 }
2856
df1c2c71 2857 static $guestid = null;
2858
2859 if (is_null($guestid)) {
2860 if ($guest = guest_user()) {
2861 $guestid = $guest->id;
2862 } else {
2863 $guestid = 0;
2864 }
669f2499 2865 }
2866
df1c2c71 2867 // Guest user should never be subscribed to a forum.
2868 unset($results[$guestid]);
2869
669f2499 2870 return $results;
1f48942e 2871}
9fa49e22 2872
067675c0 2873
2874
0a4ac01b 2875// OTHER FUNCTIONS ///////////////////////////////////////////////////////////
f93f848a 2876
2877
df1ba0f4 2878/**
2879 * @global object
2880 * @global object
2881 * @param int $courseid
2882 * @param string $type
2883 */
11b0c469 2884function forum_get_course_forum($courseid, $type) {
2885// How to set up special 1-per-course forums
9146b979 2886 global $CFG, $DB, $OUTPUT;
a6fcdf98 2887
4e445355 2888 if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $type), "id ASC")) {
65b0e537 2889 // There should always only be ONE, but with the right combination of
29cbd93a 2890 // errors there might be more. In this case, just return the oldest one (lowest ID).
2891 foreach ($forums as $forum) {
2892 return $forum; // ie the first one
11b0c469 2893 }
8daaf761 2894 }
e6874d9f 2895
8daaf761 2896 // Doesn't exist, so create one now.
2897 $forum->course = $courseid;
2898 $forum->type = "$type";
2899 switch ($forum->type) {
2900 case "news":
294ce987 2901 $forum->name = get_string("namenews", "forum");
2902 $forum->intro = get_string("intronews", "forum");
906fef94 2903 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
8daaf761 2904 $forum->assessed = 0;
709f0ec8 2905 if ($courseid == SITEID) {
2906 $forum->name = get_string("sitenews");
2907 $forum->forcesubscribe = 0;
8f0cd6ef 2908 }
8daaf761 2909 break;
2910 case "social":
294ce987 2911 $forum->name = get_string("namesocial", "forum");
2912 $forum->intro = get_string("introsocial", "forum");
8daaf761 2913 $forum->assessed = 0;
2914 $forum->forcesubscribe = 0;
2915 break;
1c7b8b93
NC
2916 case "blog":
2917 $forum->name = get_string('blogforum', 'forum');
2918 $forum->intro = get_string('introblog', 'forum');
2919 $forum->assessed = 0;
2920 $forum->forcesubscribe = 0;
2921 break;
8daaf761 2922 default:
9146b979 2923 echo $OUTPUT->notification("That forum type doesn't exist!");
8daaf761 2924 return false;
2925 break;
2926 }
2927
2928 $forum->timemodified = time();
4e445355 2929 $forum->id = $DB->insert_record("forum", $forum);
8daaf761 2930
4e445355 2931 if (! $module = $DB->get_record("modules", array("name" => "forum"))) {
9146b979 2932 echo $OUTPUT->notification("Could not find forum module!!");
e1b5643f 2933 return false;
82aa0e8d 2934 }
39790bd8 2935 $mod = new stdClass();
e1b5643f 2936 $mod->course = $courseid;
2937 $mod->module = $module->id;
2938 $mod->instance = $forum->id;
2939 $mod->section = 0;
2940 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
11cd754e 2941 echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'");
e1b5643f 2942 return false;
2943 }
2944 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
9146b979 2945 echo $OUTPUT->notification("Could not add the new course module to that section");
e1b5643f 2946 return false;
2947 }
f685e830
PS
2948 $DB->set_field("course_modules", "section", $sectionid, array("id" => $mod->coursemodule));
2949
e1b5643f 2950 include_once("$CFG->dirroot/course/lib.php");
2951 rebuild_course_cache($courseid);
65b0e537 2952
4e445355 2953 return $DB->get_record("forum", array("id" => "$forum->id"));
82aa0e8d 2954}
2955
f93f848a 2956
0a4ac01b 2957/**
df1ba0f4 2958 * Given the data about a posting, builds up the HTML to display it and
2959 * returns the HTML in a string. This is designed for sending via HTML email.
2960 *
2961 * @global object
2962 * @param object $course
2963 * @param object $cm
2964 * @param object $forum
2965 * @param object $discussion
2966 * @param object $post
2967 * @param object $userform
2968 * @param object $userto
2969 * @param bool $ownpost
2970 * @param bool $reply
2971 * @param bool $link
2972 * @param bool $rate
2973 * @param string $footer
2974 * @return string
2975 */
0faf6791 2976function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto,
11b0c469 2977 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
2b63df96 2978
59e28d8f 2979 global $CFG, $OUTPUT;
501cdbd8 2980
df1c2c71 2981 if (!isset($userto->viewfullnames[$forum->id])) {
2982 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 2983 print_error('invalidcoursemodule');
df1c2c71 2984 }
2985 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
2986 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
2987 } else {
2988 $viewfullnames = $userto->viewfullnames[$forum->id];
7613e6d7 2989 }
7613e6d7 2990
1306c5ea 2991 // format the post body
39790bd8 2992 $options = new stdClass();
1306c5ea 2993 $options->para = true;
e2d7687f 2994 $formattedtext = format_text($post->message, $post->messageformat, $options, $course->id);
0d851f90 2995
add3201e 2996 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
501cdbd8 2997
add3201e 2998 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
812dbaf7 2999 $output .= $OUTPUT->user_picture($userfrom, array('courseid'=>$course->id));
add3201e 3000 $output .= '</td>';
501cdbd8 3001
3002 if ($post->parent) {
add3201e 3003 $output .= '<td class="topic">';
501cdbd8 3004 } else {
add3201e 3005 $output .= '<td class="topic starter">';
501cdbd8 3006 }
add3201e 3007 $output .= '<div class="subject">'.format_string($post->subject).'</div>';
1b26d5e7 3008
df1c2c71 3009 $fullname = fullname($userfrom, $viewfullnames);
39790bd8 3010 $by = new stdClass();
df1c2c71 3011 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
3012 $by->date = userdate($post->modified, '', $userto->timezone);
add3201e 3013 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
3014
3015 $output .= '</td></tr>';
3016
7b54f563 3017 $output .= '<tr><td class="left side" valign="top">';
df1c2c71 3018
3019 if (isset($userfrom->groups)) {
3020 $groups = $userfrom->groups[$forum->id];
3021 } else {
3022 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
12e57b92 3023 print_error('invalidcoursemodule');
df1c2c71 3024 }
3025 $group = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
3026 }
3027
3028 if ($groups) {
3029 $output .= print_group_picture($groups, $course->id, false, true, true);
add3201e 3030 } else {
3031 $output .= '&nbsp;';
3032 }
1b26d5e7 3033
add3201e 3034 $output .= '</td><td class="content">';
501cdbd8 3035
0faf6791 3036 $attachments = forum_print_attachments($post, $cm, 'html');
3037 if ($attachments !== '') {
add3201e 3038 $output .= '<div class="attachments">';
0faf6791 3039 $output .= $attachments;
3040 $output .= '</div>';
7f6689e4 3041 }
3042
0d851f90 3043 $output .= $formattedtext;
501cdbd8 3044
0a4ac01b 3045// Commands
add3201e 3046 $commands = array();
501cdbd8 3047
2e2e71a8 3048 if ($post->parent) {
add3201e 3049 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
3050 $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
2e2e71a8 3051 }
ce45515e 3052
add3201e 3053 if ($reply) {
3054 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
3055 get_string('reply', 'forum').'</a>';
501cdbd8 3056 }
3057
add3201e 3058 $output .= '<div class="commands">';
3059 $output .= implode(' | ', $commands);
3060 $output .= '</div>';
3061
0a4ac01b 3062// Context link to post if required
501cdbd8 3063 if ($link) {
add3201e 3064 $output .= '<div class="link">';
0be4d8bf 3065 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
2b63df96 3066 get_string('postincontext', 'forum').'</a>';
add3201e 3067 $output .= '</div>';
501cdbd8 3068 }
add3201e 3069
501cdbd8 3070 if ($footer) {
add3201e 3071 $output .= '<div class="footer">'.$footer.'</div>';
501cdbd8 3072 }
add3201e 3073 $output .= '</td></tr></table>'."\n\n";
3074
501cdbd8 3075 return $output;
3076}
3077
0a4ac01b 3078/**
21976af1 3079 * Print a forum post
65bcf17b 3080 *
df1ba0f4 3081 * @global object
3082 * @global object
3083 * @uses FORUM_MODE_THREADED
3084 * @uses PORTFOLIO_FORMAT_PLAINHTML
3085 * @uses PORTFOLIO_FORMAT_FILE
3086 * @uses PORTFOLIO_FORMAT_RICHHTML
3087 * @uses PORTFOLIO_ADD_TEXT_LINK
3088 * @uses CONTEXT_MODULE
21976af1 3089 * @param object $post The post to print.
df1ba0f4 3090 * @param object $discussion
3091 * @param object $forum
3092 * @param object $cm
3093 * @param object $course
21976af1 3094 * @param boolean $ownpost Whether this post belongs to the current user.
65bcf17b 3095 * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
21976af1 3096 * @param boolean $link Just print a shortened version of the post as a link to the full post.
21976af1 3097 * @param string $footer Extra stuff to print after the message.
3098 * @param string $highlight Space-separated list of terms to highlight.
3099 * @param int $post_read true, false or -99. If we already know whether this user
3100 * has read this post, pass that in, otherwise, pass in -99, and this
3101 * function will work it out.
3102 * @param boolean $dummyifcantsee When forum_user_can_see_post says that
65bcf17b 3103 * the current user can't see this post, if this argument is true
21976af1 3104 * (the default) then print a dummy 'you can't see this post' post.
3105 * If false, don't output anything at all.
df1ba0f4 3106 * @param bool|null $istracked
63e87951 3107 * @return void
0a4ac01b 3108 */
65bcf17b 3109function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
367a75fa 3110 $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) {
5d9827e4 3111 global $USER, $CFG, $OUTPUT;
501cdbd8 3112
367a75fa
SH
3113 // String cache
3114 static $str;
f37da850 3115
8d96a7b4 3116 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
3117
65bcf17b 3118 $post->course = $course->id;
3119 $post->forum = $forum->id;
64f93798 3120 $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
951e1073 3121
65bcf17b 3122 // caching