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