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