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