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