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