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