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