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