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