Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

6977 lines (5858 sloc) 254.355 kb
<?php // $Id$
require_once($CFG->libdir.'/filelib.php');
/// CONSTANTS ///////////////////////////////////////////////////////////
define('FORUM_MODE_FLATOLDEST', 1);
define('FORUM_MODE_FLATNEWEST', -1);
define('FORUM_MODE_THREADED', 2);
define('FORUM_MODE_NESTED', 3);
define('FORUM_FORCESUBSCRIBE', 1);
define('FORUM_INITIALSUBSCRIBE', 2);
define('FORUM_DISALLOWSUBSCRIBE',3);
define('FORUM_TRACKING_OFF', 0);
define('FORUM_TRACKING_OPTIONAL', 1);
define('FORUM_TRACKING_ON', 2);
define('FORUM_UNSET_POST_RATING', -999);
define ('FORUM_AGGREGATE_NONE', 0); //no ratings
define ('FORUM_AGGREGATE_AVG', 1);
define ('FORUM_AGGREGATE_COUNT', 2);
define ('FORUM_AGGREGATE_MAX', 3);
define ('FORUM_AGGREGATE_MIN', 4);
define ('FORUM_AGGREGATE_SUM', 5);
/// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
/**
* Given an object containing all the necessary data,
* (defined by the form in mod.html) this function
* will create a new instance and return the id number
* of the new instance.
* @param object $forum add forum instance (with magic quotes)
* @return int intance id
*/
function forum_add_instance($forum) {
global $CFG;
$forum->timemodified = time();
if (empty($forum->assessed)) {
$forum->assessed = 0;
}
if (empty($forum->ratingtime) or empty($forum->assessed)) {
$forum->assesstimestart = 0;
$forum->assesstimefinish = 0;
}
if (!$forum->id = insert_record('forum', $forum)) {
return false;
}
if ($forum->type == 'single') { // Create related discussion.
$discussion = new object();
$discussion->course = $forum->course;
$discussion->forum = $forum->id;
$discussion->name = $forum->name;
$discussion->intro = $forum->intro;
$discussion->assessed = $forum->assessed;
$discussion->format = $forum->type;
$discussion->mailnow = false;
$discussion->groupid = -1;
if (! forum_add_discussion($discussion, $discussion->intro)) {
error('Could not add the discussion for this forum');
}
}
if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) {
/// all users should be subscribed initially
/// Note: forum_get_potential_subscribers should take the forum context,
/// but that does not exist yet, becuase the forum is only half build at this
/// stage. However, because the forum is brand new, we know that there are
/// no role assignments or overrides in the forum context, so using the
/// course context gives the same list of users.
$users = forum_get_potential_subscribers(get_context_instance(CONTEXT_COURSE, $forum->course), 0, 'u.id, u.email', '');
foreach ($users as $user) {
forum_subscribe($user->id, $forum->id);
}
}
$forum = stripslashes_recursive($forum);
forum_grade_item_update($forum);
return $forum->id;
}
/**
* Given an object containing all the necessary data,
* (defined by the form in mod.html) this function
* will update an existing instance with new data.
* @param object $forum forum instance (with magic quotes)
* @return bool success
*/
function forum_update_instance($forum) {
global $USER;
$forum->timemodified = time();
$forum->id = $forum->instance;
if (empty($forum->assessed)) {
$forum->assessed = 0;
}
if (empty($forum->ratingtime) or empty($forum->assessed)) {
$forum->assesstimestart = 0;
$forum->assesstimefinish = 0;
}
$oldforum = get_record('forum', 'id', $forum->id);
// MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum
// if scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond?
// 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
if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) {
forum_update_grades($forum); // recalculate grades for the forum
}
if ($forum->type == 'single') { // Update related discussion and post.
if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
notify('Warning! There is more than one discussion in this forum - using the most recent');
$discussion = array_pop($discussions);
} else {
// try to recover by creating initial discussion - MDL-16262
$discussion = new object();
$discussion->course = $forum->course;
$discussion->forum = $forum->id;
$discussion->name = $forum->name;
$discussion->intro = $forum->intro;
$discussion->assessed = $forum->assessed;
$discussion->format = $forum->type;
$discussion->mailnow = false;
$discussion->groupid = -1;
forum_add_discussion($discussion, $discussion->intro);
if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
error('Could not add the discussion for this forum');
}
}
}
if (! $post = get_record('forum_posts', 'id', $discussion->firstpost)) {
error('Could not find the first post in this forum discussion');
}
$post->subject = $forum->name;
$post->message = $forum->intro;
$post->modified = $forum->timemodified;
$post->userid = $USER->id; // MDL-18599, so that current teacher can take ownership of activities
if (! update_record('forum_posts', ($post))) {
error('Could not update the first post');
}
$discussion->name = $forum->name;
if (! update_record('forum_discussions', ($discussion))) {
error('Could not update the discussion');
}
}
if (!update_record('forum', $forum)) {
error('Can not update forum');
}
$forum = stripslashes_recursive($forum);
forum_grade_item_update($forum);
return true;
}
/**
* Given an ID of an instance of this module,
* this function will permanently delete the instance
* and any data that depends on it.
* @param int forum instance id
* @return bool success
*/
function forum_delete_instance($id) {
if (!$forum = get_record('forum', 'id', $id)) {
return false;
}
$result = true;
if ($discussions = get_records('forum_discussions', 'forum', $forum->id)) {
foreach ($discussions as $discussion) {
if (!forum_delete_discussion($discussion, true)) {
$result = false;
}
}
}
if (!delete_records('forum_subscriptions', 'forum', $forum->id)) {
$result = false;
}
forum_tp_delete_read_records(-1, -1, -1, $forum->id);
if (!delete_records('forum', 'id', $forum->id)) {
$result = false;
}
forum_grade_item_delete($forum);
return $result;
}
/**
* Function to be run periodically according to the moodle cron
* Finds all posts that have yet to be mailed out, and mails them
* out to all subscribers
* @return void
*/
function forum_cron() {
global $CFG, $USER;
$cronuser = clone($USER);
$site = get_site();
// all users that are subscribed to any post that needs sending
$users = array();
// status arrays
$mailcount = array();
$errorcount = array();
// caches
$discussions = array();
$forums = array();
$courses = array();
$coursemodules = array();
$subscribedusers = array();
// Posts older than 2 days will not be mailed. This is to avoid the problem where
// cron has not been running for a long time, and then suddenly people are flooded
// with mail from the past few weeks or months
$timenow = time();
$endtime = $timenow - $CFG->maxeditingtime;
$starttime = $endtime - 48 * 3600; // Two days earlier
if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
// Mark them all now as being mailed. It's unlikely but possible there
// might be an error later so that a post is NOT actually mailed out,
// but since mail isn't crucial, we can accept this risk. Doing it now
// prevents the risk of duplicated mails, which is a worse problem.
if (!forum_mark_old_posts_as_mailed($endtime)) {
mtrace('Errors occurred while trying to mark some posts as being mailed.');
return false; // Don't continue trying to mail them, in case we are in a cron loop
}
// checking post validity, and adding users to loop through later
foreach ($posts as $pid => $post) {
$discussionid = $post->discussion;
if (!isset($discussions[$discussionid])) {
if ($discussion = get_record('forum_discussions', 'id', $post->discussion)) {
$discussions[$discussionid] = $discussion;
} else {
mtrace('Could not find discussion '.$discussionid);
unset($posts[$pid]);
continue;
}
}
$forumid = $discussions[$discussionid]->forum;
if (!isset($forums[$forumid])) {
if ($forum = get_record('forum', 'id', $forumid)) {
$forums[$forumid] = $forum;
} else {
mtrace('Could not find forum '.$forumid);
unset($posts[$pid]);
continue;
}
}
$courseid = $forums[$forumid]->course;
if (!isset($courses[$courseid])) {
if ($course = get_record('course', 'id', $courseid)) {
$courses[$courseid] = $course;
} else {
mtrace('Could not find course '.$courseid);
unset($posts[$pid]);
continue;
}
}
if (!isset($coursemodules[$forumid])) {
if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
$coursemodules[$forumid] = $cm;
} else {
mtrace('Could not course module for forum '.$forumid);
unset($posts[$pid]);
continue;
}
}
// caching subscribed users of each forum
if (!isset($subscribedusers[$forumid])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $coursemodules[$forumid]->id);
if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext)) {
foreach ($subusers as $postuser) {
// do not try to mail users with stopped email
if ($postuser->emailstop) {
if (!empty($CFG->forum_logblocked)) {
add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
}
continue;
}
// this user is subscribed to this forum
$subscribedusers[$forumid][$postuser->id] = $postuser->id;
// this user is a user we have to process later
$users[$postuser->id] = $postuser;
}
unset($subusers); // release memory
}
}
$mailcount[$pid] = 0;
$errorcount[$pid] = 0;
}
}
if ($users && $posts) {
$urlinfo = parse_url($CFG->wwwroot);
$hostname = $urlinfo['host'];
foreach ($users as $userto) {
@set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
// set this so that the capabilities are cached, and environment matches receiving user
$USER = $userto;
mtrace('Processing user '.$userto->id);
// init caches
$userto->viewfullnames = array();
$userto->canpost = array();
$userto->markposts = array();
$userto->enrolledin = array();
// reset the caches
foreach ($coursemodules as $forumid=>$unused) {
$coursemodules[$forumid]->cache = new object();
$coursemodules[$forumid]->cache->caps = array();
unset($coursemodules[$forumid]->uservisible);
}
foreach ($posts as $pid => $post) {
// Set up the environment for the post, discussion, forum, course
$discussion = $discussions[$post->discussion];
$forum = $forums[$discussion->forum];
$course = $courses[$forum->course];
$cm =& $coursemodules[$forum->id];
// Do some checks to see if we can bail out now
if (!isset($subscribedusers[$forum->id][$userto->id])) {
continue; // user does not subscribe to this forum
}
// Verify user is enrollend in course - if not do not send any email
if (!isset($userto->enrolledin[$course->id])) {
$userto->enrolledin[$course->id] = has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $course->id));
}
if (!$userto->enrolledin[$course->id]) {
// oops - this user should not receive anything from this course
continue;
}
// Don't send email if the forum is Q&A and the user has not posted
if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id)) {
mtrace('Did not email '.$userto->id.' because user has not posted in discussion');
continue;
}
// Get info about the sending user
if (array_key_exists($post->userid, $users)) { // we might know him/her already
$userfrom = $users[$post->userid];
} else if ($userfrom = get_record('user', 'id', $post->userid)) {
$users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
} else {
mtrace('Could not find user '.$post->userid);
continue;
}
// setup global $COURSE properly - needed for roles and languages
course_setup($course); // More environment
// Fill caches
if (!isset($userto->viewfullnames[$forum->id])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
}
if (!isset($userto->canpost[$discussion->id])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
}
if (!isset($userfrom->groups[$forum->id])) {
if (!isset($userfrom->groups)) {
$userfrom->groups = array();
$users[$userfrom->id]->groups = array();
}
$userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
$users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
}
// Make sure groups allow this user to see this email
if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used
if (!groups_group_exists($discussion->groupid)) { // Can't find group
continue; // Be safe and don't send it to anyone
}
if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
// do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
continue;
}
}
// Make sure we're allowed to see it...
if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
mtrace('user '.$userto->id. ' can not see '.$post->id);
continue;
}
// OK so we need to send the email.
// Does the user want this post in a digest? If so postpone it for now.
if ($userto->maildigest > 0) {
// This user wants the mails to be in digest form
$queue = new object();
$queue->userid = $userto->id;
$queue->discussionid = $discussion->id;
$queue->postid = $post->id;
$queue->timemodified = $post->created;
if (!insert_record('forum_queue', $queue)) {
mtrace("Error: mod/forum/cron.php: Could not queue for digest mail for id $post->id to user $userto->id ($userto->email) .. not trying again.");
}
continue;
}
// Prepare to actually send the post now, and build up the content
$cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
$userfrom->customheaders = array ( // Headers to make emails easier to track
'Precedence: Bulk',
'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>',
'X-Course-Id: '.$course->id,
'X-Course-Name: '.format_string($course->fullname, true)
);
if ($post->parent) { // This post is a reply, so add headers for threading (see MDL-22551)
$userfrom->customheaders[] = 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>';
$userfrom->customheaders[] = 'References: <moodlepost'.$post->parent.'@'.$hostname.'>';
}
$postsubject = "$course->shortname: ".format_string($post->subject,true);
$posttext = forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto);
$posthtml = forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto);
// Send the post now!
mtrace('Sending ', '');
if (!$mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext,
$posthtml, '', '', $CFG->forum_replytouser)) {
mtrace("Error: mod/forum/cron.php: Could not send out mail for id $post->id to user $userto->id".
" ($userto->email) .. not trying again.");
add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
$errorcount[$post->id]++;
} else if ($mailresult === 'emailstop') {
// should not be reached anymore - see check above
} else {
$mailcount[$post->id]++;
// Mark post as read if forum_usermarksread is set off
if (!$CFG->forum_usermarksread) {
$userto->markposts[$post->id] = $post->id;
}
}
mtrace('post '.$post->id. ': '.$post->subject);
}
// mark processed posts as read
forum_tp_mark_posts_read($userto, $userto->markposts);
}
}
if ($posts) {
foreach ($posts as $post) {
mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
if ($errorcount[$post->id]) {
set_field("forum_posts", "mailed", "2", "id", "$post->id");
}
}
}
// release some memory
unset($subscribedusers);
unset($mailcount);
unset($errorcount);
$USER = clone($cronuser);
course_setup(SITEID);
$sitetimezone = $CFG->timezone;
// Now see if there are any digest mails waiting to be sent, and if we should send them
mtrace('Starting digest processing...');
@set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes
if (!isset($CFG->digestmailtimelast)) { // To catch the first time
set_config('digestmailtimelast', 0);
}
$timenow = time();
$digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
// Delete any really old ones (normally there shouldn't be any)
$weekago = $timenow - (7 * 24 * 3600);
delete_records_select('forum_queue', "timemodified < $weekago");
mtrace ('Cleaned old digest records');
if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
$digestposts_rs = get_recordset_select('forum_queue', "timemodified < $digesttime");
if (!rs_EOF($digestposts_rs)) {
// We have work to do
$usermailcount = 0;
//caches - reuse the those filled before too
$discussionposts = array();
$userdiscussions = array();
while ($digestpost = rs_fetch_next_record($digestposts_rs)) {
if (!isset($users[$digestpost->userid])) {
if ($user = get_record('user', 'id', $digestpost->userid)) {
$users[$digestpost->userid] = $user;
} else {
continue;
}
}
$postuser = $users[$digestpost->userid];
if ($postuser->emailstop) {
if (!empty($CFG->forum_logblocked)) {
add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
}
continue;
}
if (!isset($posts[$digestpost->postid])) {
if ($post = get_record('forum_posts', 'id', $digestpost->postid)) {
$posts[$digestpost->postid] = $post;
} else {
continue;
}
}
$discussionid = $digestpost->discussionid;
if (!isset($discussions[$discussionid])) {
if ($discussion = get_record('forum_discussions', 'id', $discussionid)) {
$discussions[$discussionid] = $discussion;
} else {
continue;
}
}
$forumid = $discussions[$discussionid]->forum;
if (!isset($forums[$forumid])) {
if ($forum = get_record('forum', 'id', $forumid)) {
$forums[$forumid] = $forum;
} else {
continue;
}
}
$courseid = $forums[$forumid]->course;
if (!isset($courses[$courseid])) {
if ($course = get_record('course', 'id', $courseid)) {
$courses[$courseid] = $course;
} else {
continue;
}
}
if (!isset($coursemodules[$forumid])) {
if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
$coursemodules[$forumid] = $cm;
} else {
continue;
}
}
$userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
$discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
}
rs_close($digestposts_rs); /// Finished iteration, let's close the resultset
// Data collected, start sending out emails to each user
foreach ($userdiscussions as $userid => $thesediscussions) {
@set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
$USER = $cronuser;
course_setup(SITEID); // reset cron user language, theme and timezone settings
mtrace(get_string('processingdigest', 'forum', $userid), '... ');
// First of all delete all the queue entries for this user
delete_records_select('forum_queue', "userid = $userid AND timemodified < $digesttime");
$userto = $users[$userid];
// Override the language and timezone of the "current" user, so that
// mail is customised for the receiver.
$USER = $userto;
course_setup(SITEID);
// init caches
$userto->viewfullnames = array();
$userto->canpost = array();
$userto->markposts = array();
$postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
$headerdata = new object();
$headerdata->sitename = format_string($site->fullname, true);
$headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
$posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
$headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
$posthtml = "<head>";
foreach ($CFG->stylesheets as $stylesheet) {
$posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
}
$posthtml .= "</head>\n<body id=\"email\">\n";
$posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
foreach ($thesediscussions as $discussionid) {
@set_time_limit(120); // to be reset for each post
$discussion = $discussions[$discussionid];
$forum = $forums[$discussion->forum];
$course = $courses[$forum->course];
$cm = $coursemodules[$forum->id];
//override language
course_setup($course);
// Fill caches
if (!isset($userto->viewfullnames[$forum->id])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
}
if (!isset($userto->canpost[$discussion->id])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
}
$strforums = get_string('forums', 'forum');
$canunsubscribe = ! forum_is_forcesubscribed($forum);
$canreply = $userto->canpost[$discussion->id];
$posttext .= "\n \n";
$posttext .= '=====================================================================';
$posttext .= "\n \n";
$posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
if ($discussion->name != $forum->name) {
$posttext .= " -> ".format_string($discussion->name,true);
}
$posttext .= "\n";
$posthtml .= "<p><font face=\"sans-serif\">".
"<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
"<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
"<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
if ($discussion->name == $forum->name) {
$posthtml .= "</font></p>";
} else {
$posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
}
$posthtml .= '<p>';
$postsarray = $discussionposts[$discussionid];
sort($postsarray);
foreach ($postsarray as $postid) {
$post = $posts[$postid];
if (array_key_exists($post->userid, $users)) { // we might know him/her already
$userfrom = $users[$post->userid];
} else if ($userfrom = get_record('user', 'id', $post->userid)) {
$users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
} else {
mtrace('Could not find user '.$post->userid);
continue;
}
if (!isset($userfrom->groups[$forum->id])) {
if (!isset($userfrom->groups)) {
$userfrom->groups = array();
$users[$userfrom->id]->groups = array();
}
$userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
$users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
}
$userfrom->customheaders = array ("Precedence: Bulk");
if ($userto->maildigest == 2) {
// Subjects only
$by = new object();
$by->name = fullname($userfrom);
$by->date = userdate($post->modified);
$posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
$posttext .= "\n---------------------------------------------------------------------";
$by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
$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>';
} else {
// The full treatment
$posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true);
$posthtml .= forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
// Create an array of postid's for this user to mark as read.
if (!$CFG->forum_usermarksread) {
$userto->markposts[$post->id] = $post->id;
}
}
}
if ($canunsubscribe) {
$posthtml .= "\n<div class='mdl-right'><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>";
} else {
$posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
}
$posthtml .= '<hr size="1" noshade="noshade" /></p>';
}
$posthtml .= '</body>';
if ($userto->mailformat != 1) {
// This user DOESN'T want to receive HTML
$posthtml = '';
}
if (!$mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml,
'', '', $CFG->forum_replytouser)) {
mtrace("ERROR!");
echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
} else if ($mailresult === 'emailstop') {
// should not happen anymore - see check above
} else {
mtrace("success.");
$usermailcount++;
// Mark post as read if forum_usermarksread is set off
forum_tp_mark_posts_read($userto, $userto->markposts);
}
}
}
/// We have finishied all digest emails, update $CFG->digestmailtimelast
set_config('digestmailtimelast', $timenow);
}
$USER = $cronuser;
course_setup(SITEID); // reset cron user language, theme and timezone settings
if (!empty($usermailcount)) {
mtrace(get_string('digestsentusers', 'forum', $usermailcount));
}
if (!empty($CFG->forum_lastreadclean)) {
$timenow = time();
if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
set_config('forum_lastreadclean', $timenow);
mtrace('Removing old forum read tracking info...');
forum_tp_clean_read_records();
}
} else {
set_config('forum_lastreadclean', time());
}
return true;
}
/**
* Builds and returns the body of the email notification in plain text.
*
* @param object $course
* @param object $forum
* @param object $discussion
* @param object $post
* @param object $userfrom
* @param object $userto
* @param boolean $bare
* @return string The email body in plain text format.
*/
function forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
global $CFG, $USER;
if (!isset($userto->viewfullnames[$forum->id])) {
if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
error('Course Module ID was incorrect');
}
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
} else {
$viewfullnames = $userto->viewfullnames[$forum->id];
}
if (!isset($userto->canpost[$discussion->id])) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
} else {
$canreply = $userto->canpost[$discussion->id];
}
$by = New stdClass;
$by->name = fullname($userfrom, $viewfullnames);
$by->date = userdate($post->modified, "", $userto->timezone);
$strbynameondate = get_string('bynameondate', 'forum', $by);
$strforums = get_string('forums', 'forum');
$canunsubscribe = ! forum_is_forcesubscribed($forum);
$posttext = '';
if (!$bare) {
$posttext = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
if ($discussion->name != $forum->name) {
$posttext .= " -> ".format_string($discussion->name,true);
}
}
$posttext .= "\n---------------------------------------------------------------------\n";
$posttext .= format_string($post->subject,true);
if ($bare) {
$posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
}
$posttext .= "\n".$strbynameondate."\n";
$posttext .= "---------------------------------------------------------------------\n";
$posttext .= format_text_email(trusttext_strip($post->message), $post->format);
$posttext .= "\n\n";
if ($post->attachment) {
$post->course = $course->id;
$post->forum = $forum->id;
$posttext .= forum_print_attachments($post, "text");
}
if (!$bare && $canreply) {
$posttext .= "---------------------------------------------------------------------\n";
$posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
$posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
}
if (!$bare && $canunsubscribe) {
$posttext .= "\n---------------------------------------------------------------------\n";
$posttext .= get_string("unsubscribe", "forum");
$posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
}
return $posttext;
}
/**
* Builds and returns the body of the email notification in html format.
*
* @param object $course
* @param object $forum
* @param object $discussion
* @param object $post
* @param object $userfrom
* @param object $userto
* @return string The email text in HTML format
*/
function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto) {
global $CFG;
if ($userto->mailformat != 1) { // Needs to be HTML
return '';
}
if (!isset($userto->canpost[$discussion->id])) {
$canreply = forum_user_can_post($forum, $discussion, $userto);
} else {
$canreply = $userto->canpost[$discussion->id];
}
$strforums = get_string('forums', 'forum');
$canunsubscribe = ! forum_is_forcesubscribed($forum);
$posthtml = '<head>';
foreach ($CFG->stylesheets as $stylesheet) {
$posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
}
$posthtml .= '</head>';
$posthtml .= "\n<body id=\"email\">\n\n";
$posthtml .= '<div class="navbar">'.
'<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
'<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
'<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
if ($discussion->name == $forum->name) {
$posthtml .= '</div>';
} else {
$posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
format_string($discussion->name,true).'</a></div>';
}
$posthtml .= forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
if ($canunsubscribe) {
$posthtml .= '<hr /><div class="mdl-align unsubscribelink">
<a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
<a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
}
$posthtml .= '</body>';
return $posthtml;
}
/**
*
* @param object $course
* @param object $user
* @param object $mod TODO this is not used in this function, refactor
* @param object $forum
* @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
*/
function forum_user_outline($course, $user, $mod, $forum) {
global $CFG;
require_once("$CFG->libdir/gradelib.php");
$grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
if (empty($grades->items[0]->grades)) {
$grade = false;
} else {
$grade = reset($grades->items[0]->grades);
}
$count = forum_count_user_posts($forum->id, $user->id);
if ($count && $count->postcount > 0) {
$result = new object();
$result->info = get_string("numposts", "forum", $count->postcount);
$result->time = $count->lastpost;
if ($grade) {
$result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
}
return $result;
} else if ($grade) {
$result = new object();
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}
return NULL;
}
/**
*
*/
function forum_user_complete($course, $user, $mod, $forum) {
global $CFG,$USER;
require_once("$CFG->libdir/gradelib.php");
$grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
if (!empty($grades->items[0]->grades)) {
$grade = reset($grades->items[0]->grades);
echo '<p>'.get_string('grade').': '.$grade->str_long_grade.'</p>';
if ($grade->str_feedback) {
echo '<p>'.get_string('feedback').': '.$grade->str_feedback.'</p>';
}
}
if ($posts = forum_get_user_posts($forum->id, $user->id)) {
if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
error('Course Module ID was incorrect');
}
$discussions = forum_get_user_involved_discussions($forum->id, $user->id);
// preload all user ratings for these discussions - one query only and minimal memory
$cm->cache->ratings = array();
$cm->cache->myratings = array();
if ($postratings = forum_get_all_user_ratings($user->id, $discussions)) {
foreach ($postratings as $pr) {
if (!isset($cm->cache->ratings[$pr->postid])) {
$cm->cache->ratings[$pr->postid] = array();
}
$cm->cache->ratings[$pr->postid][$pr->id] = $pr->rating;
if ($pr->userid == $USER->id) {
$cm->cache->myratings[$pr->postid] = $pr->rating;
}
}
unset($postratings);
}
foreach ($posts as $post) {
if (!isset($discussions[$post->discussion])) {
continue;
}
$discussion = $discussions[$post->discussion];
$ratings = null;
if ($forum->assessed) {
if ($scale = make_grades_menu($forum->scale)) {
$ratings =new object();
$ratings->scale = $scale;
$ratings->assesstimestart = $forum->assesstimestart;
$ratings->assesstimefinish = $forum->assesstimefinish;
$ratings->allow = false;
}
}
forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false, $ratings);
}
} else {
echo "<p>".get_string("noposts", "forum")."</p>";
}
}
/**
*
*/
function forum_print_overview($courses,&$htmlarray) {
global $USER, $CFG;
//$LIKE = sql_ilike();//no longer using like in queries. MDL-20578
if (empty($courses) || !is_array($courses) || count($courses) == 0) {
return array();
}
if (!$forums = get_all_instances_in_courses('forum',$courses)) {
return;
}
// get all forum logs in ONE query (much better!)
$sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {$CFG->prefix}log l "
." JOIN {$CFG->prefix}course_modules cm ON cm.id = cmid "
." WHERE (";
foreach ($courses as $course) {
$sql .= '(l.course = '.$course->id.' AND l.time > '.$course->lastaccess.') OR ';
}
$sql = substr($sql,0,-3); // take off the last OR
$sql .= ") AND l.module = 'forum' AND action = 'add post' "
." AND userid != ".$USER->id." GROUP BY cmid,l.course,instance";
if (!$new = get_records_sql($sql)) {
$new = array(); // avoid warnings
}
// also get all forum tracking stuff ONCE.
$trackingforums = array();
foreach ($forums as $forum) {
if (forum_tp_can_track_forums($forum)) {
$trackingforums[$forum->id] = $forum;
}
}
if (count($trackingforums) > 0) {
$cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
$sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
' FROM '.$CFG->prefix.'forum_posts p '.
' JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
' LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$USER->id.' WHERE (';
foreach ($trackingforums as $track) {
$sql .= '(d.forum = '.$track->id.' AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = '.get_current_group($track->course).')) OR ';
}
$sql = substr($sql,0,-3); // take off the last OR
$sql .= ') AND p.modified >= '.$cutoffdate.' AND r.id is NULL GROUP BY d.forum,d.course';
if (!$unread = get_records_sql($sql)) {
$unread = array();
}
} else {
$unread = array();
}
if (empty($unread) and empty($new)) {
return;
}
$strforum = get_string('modulename','forum');
$strnumunread = get_string('overviewnumunread','forum');
$strnumpostssince = get_string('overviewnumpostssince','forum');
foreach ($forums as $forum) {
$str = '';
$count = 0;
$thisunread = 0;
$showunread = false;
// either we have something from logs, or trackposts, or nothing.
if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
$count = $new[$forum->id]->count;
}
if (array_key_exists($forum->id,$unread)) {
$thisunread = $unread[$forum->id]->count;
$showunread = true;
}
if ($count > 0 || $thisunread > 0) {
$str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
$forum->name.'</a></div>';
$str .= '<div class="info">';
$str .= $count.' '.$strnumpostssince;
if (!empty($showunread)) {
$str .= '<br />'.$thisunread .' '.$strnumunread;
}
$str .= '</div></div>';
}
if (!empty($str)) {
if (!array_key_exists($forum->course,$htmlarray)) {
$htmlarray[$forum->course] = array();
}
if (!array_key_exists('forum',$htmlarray[$forum->course])) {
$htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
}
$htmlarray[$forum->course]['forum'] .= $str;
}
}
}
/**
* Given a course and a date, prints a summary of all the new
* messages posted in the course since that date
* @param object $course
* @param bool $viewfullnames capability
* @param int $timestart
* @return bool success
*/
function forum_print_recent_activity($course, $viewfullnames, $timestart) {
global $CFG, $USER;
// do not use log table if possible, it may be huge and is expensive to join with other tables
if (!$posts = get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
d.timestart, d.timeend, d.userid AS duserid,
u.firstname, u.lastname, u.email, u.picture
FROM {$CFG->prefix}forum_posts p
JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
JOIN {$CFG->prefix}forum f ON f.id = d.forum
JOIN {$CFG->prefix}user u ON u.id = p.userid
WHERE p.created > $timestart AND f.course = {$course->id}
ORDER BY p.id ASC")) { // order by initial posting date
return false;
}
$modinfo =& get_fast_modinfo($course);
$groupmodes = array();
$cms = array();
$strftimerecent = get_string('strftimerecent');
$printposts = array();
foreach ($posts as $post) {
if (!isset($modinfo->instances['forum'][$post->forum])) {
// not visible
continue;
}
$cm = $modinfo->instances['forum'][$post->forum];
if (!$cm->uservisible) {
continue;
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
if (!has_capability('mod/forum:viewdiscussion', $context)) {
continue;
}
if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
continue;
}
}
$groupmode = groups_get_activity_groupmode($cm, $course);
if ($groupmode) {
if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
// oki (Open discussions have groupid -1)
} else {
// separate mode
if (isguestuser()) {
// shortcut
continue;
}
if (is_null($modinfo->groups)) {
$modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
}
if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
continue;
}
}
}
$printposts[] = $post;
}
unset($posts);
if (!$printposts) {
return false;
}
print_headline(get_string('newforumposts', 'forum').':', 3);
echo "\n<ul class='unlist'>\n";
foreach ($printposts as $post) {
$subjectclass = empty($post->parent) ? ' bold' : '';
echo '<li><div class="head">'.
'<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
'<div class="name">'.fullname($post, $viewfullnames).'</div>'.
'</div>';
echo '<div class="info'.$subjectclass.'">';
if (empty($post->parent)) {
echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
} else {
echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
}
$post->subject = break_up_long_words(format_string($post->subject, true));
echo $post->subject;
echo "</a>\"</div></li>\n";
}
echo "</ul>\n";
return true;
}
/**
* Return grade for given user or all users.
*
* @param int $forumid id of forum
* @param int $userid optional user id, 0 means all users
* @return array array of grades, false if none
*/
function forum_get_user_grades($forum, $userid=0) {
global $CFG;
$user = $userid ? "AND u.id = $userid" : "";
$aggtype = $forum->assessed;
switch ($aggtype) {
case FORUM_AGGREGATE_COUNT :
$sql = "SELECT u.id, u.id AS userid, COUNT(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_MAX :
$sql = "SELECT u.id, u.id AS userid, MAX(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_MIN :
$sql = "SELECT u.id, u.id AS userid, MIN(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
case FORUM_AGGREGATE_SUM :
$sql = "SELECT u.id, u.id AS userid, SUM(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
default : //avg
$sql = "SELECT u.id, u.id AS userid, AVG(fr.rating) AS rawgrade
FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
{$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
AND fr.userid != u.id AND fd.forum = $forum->id
$user
GROUP BY u.id";
break;
}
if ($results = get_records_sql($sql)) {
// it could throw off the grading if count and sum returned a rawgrade higher than scale
// so to prevent it we review the results and ensure that rawgrade does not exceed the scale, if it does we set rawgrade = scale (i.e. full credit)
foreach ($results as $rid=>$result) {
if ($forum->scale >= 0) {
//numeric
if ($result->rawgrade > $forum->scale) {
$results[$rid]->rawgrade = $forum->scale;
}
} else {
//scales
if ($scale = get_record('scale', 'id', -$forum->scale)) {
$scale = explode(',', $scale->scale);
$max = count($scale);
if ($result->rawgrade > $max) {
$results[$rid]->rawgrade = $max;
}
}
}
}
}
return $results;
}
/**
* Update grades by firing grade_updated event
*
* @param object $forum null means all forums
* @param int $userid specific user only, 0 mean all
* @param boolean $nullifnone return null if grade does not exist
* @return void
*/
function forum_update_grades($forum=null, $userid=0, $nullifnone=true) {
global $CFG;
if ($forum != null) {
require_once($CFG->libdir.'/gradelib.php');
if ($grades = forum_get_user_grades($forum, $userid)) {
forum_grade_item_update($forum, $grades);
} else if ($userid and $nullifnone) {
$grade = new object();
$grade->userid = $userid;
$grade->rawgrade = NULL;
forum_grade_item_update($forum, $grade);
} else {
forum_grade_item_update($forum);
}
} else {
$sql = "SELECT f.*, cm.idnumber as cmidnumber
FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
if ($rs = get_recordset_sql($sql)) {
while ($forum = rs_fetch_next_record($rs)) {
if ($forum->assessed) {
forum_update_grades($forum, 0, false);
} else {
forum_grade_item_update($forum);
}
}
rs_close($rs);
}
}
}
/**
* Create/update grade item for given forum
*
* @param object $forum object with extra cmidnumber
* @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
* @return int 0 if ok
*/
function forum_grade_item_update($forum, $grades=NULL) {
global $CFG;
if (!function_exists('grade_update')) { //workaround for buggy PHP versions
require_once($CFG->libdir.'/gradelib.php');
}
$params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
if (!$forum->assessed or $forum->scale == 0) {
$params['gradetype'] = GRADE_TYPE_NONE;
} else if ($forum->scale > 0) {
$params['gradetype'] = GRADE_TYPE_VALUE;
$params['grademax'] = $forum->scale;
$params['grademin'] = 0;
} else if ($forum->scale < 0) {
$params['gradetype'] = GRADE_TYPE_SCALE;
$params['scaleid'] = -$forum->scale;
}
if ($grades === 'reset') {
$params['reset'] = true;
$grades = NULL;
}
return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
}
/**
* Delete grade item for given forum
*
* @param object $forum object
* @return object grade_item
*/
function forum_grade_item_delete($forum) {
global $CFG;
require_once($CFG->libdir.'/gradelib.php');
return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
}
/**
* Returns the users with data in one forum
* (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
* @param int $forumid
* @return mixed array or false if none
*/
function forum_get_participants($forumid) {
global $CFG;
//Get students from forum_subscriptions
$st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
FROM {$CFG->prefix}user u,
{$CFG->prefix}forum_subscriptions s
WHERE s.forum = '$forumid' and
u.id = s.userid");
//Get students from forum_posts
$st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
FROM {$CFG->prefix}user u,
{$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p
WHERE d.forum = '$forumid' and
p.discussion = d.id and
u.id = p.userid");
//Get students from forum_ratings
$st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
FROM {$CFG->prefix}user u,
{$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p,
{$CFG->prefix}forum_ratings r
WHERE d.forum = '$forumid' and
p.discussion = d.id and
r.post = p.id and
u.id = r.userid");
//Add st_posts to st_subscriptions
if ($st_posts) {
foreach ($st_posts as $st_post) {
$st_subscriptions[$st_post->id] = $st_post;
}
}
//Add st_ratings to st_subscriptions
if ($st_ratings) {
foreach ($st_ratings as $st_rating) {
$st_subscriptions[$st_rating->id] = $st_rating;
}
}
//Return st_subscriptions array (it contains an array of unique users)
return ($st_subscriptions);
}
/**
* This function returns if a scale is being used by one forum
* @param int $forumid
* @param int $scaleid negative number
* @return bool
*/
function forum_scale_used ($forumid,$scaleid) {
$return = false;
$rec = get_record("forum","id","$forumid","scale","-$scaleid");
if (!empty($rec) && !empty($scaleid)) {
$return = true;
}
return $return;
}
/**
* Checks if scale is being used by any instance of forum
*
* This is used to find out if scale used anywhere
* @param $scaleid int
* @return boolean True if the scale is used by any forum
*/
function forum_scale_used_anywhere($scaleid) {
if ($scaleid and record_exists('forum', 'scale', -$scaleid)) {
return true;
} else {
return false;
}
}
// SQL FUNCTIONS ///////////////////////////////////////////////////////////
/**
* Gets a post with all info ready for forum_print_post
* Most of these joins are just to get the forum id
* @param int $postid
* @return mixed array of posts or false
*/
function forum_get_post_full($postid) {
global $CFG;
return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
FROM {$CFG->prefix}forum_posts p
JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
WHERE p.id = '$postid'");
}
/**
* Gets posts with all info ready for forum_print_post
* We pass forumid in because we always know it so no need to make a
* complicated join to find it out.
* @return mixed array of posts or false
*/
function forum_get_discussion_posts($discussion, $sort, $forumid) {
global $CFG;
return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
FROM {$CFG->prefix}forum_posts p
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
WHERE p.discussion = $discussion
AND p.parent > 0 $sort");
}
/**
* Gets all posts in discussion including top parent.
* @param int $discussionid
* @param string $sort
* @param bool $tracking does user track the forum?
* @return array of posts
*/
function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
global $CFG, $USER;
$tr_sel = "";
$tr_join = "";
if ($tracking) {
$now = time();
$cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
$tr_sel = ", fr.id AS postread";
$tr_join = "LEFT JOIN {$CFG->prefix}forum_read fr ON (fr.postid = p.id AND fr.userid = $USER->id)";
}
if (!$posts = get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
FROM {$CFG->prefix}forum_posts p
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
$tr_join
WHERE p.discussion = $discussionid
ORDER BY $sort")) {
return array();
}
foreach ($posts as $pid=>$p) {
if ($tracking) {
if (forum_tp_is_post_old($p)) {
$posts[$pid]->postread = true;
}
}
if (!$p->parent) {
continue;
}
if (!isset($posts[$p->parent])) {
continue; // parent does not exist??
}
if (!isset($posts[$p->parent]->children)) {
$posts[$p->parent]->children = array();
}
$posts[$p->parent]->children[$pid] =& $posts[$pid];
}
return $posts;
}
/**
* Gets posts with all info ready for forum_print_post
* We pass forumid in because we always know it so no need to make a
* complicated join to find it out.
*/
function forum_get_child_posts($parent, $forumid) {
global $CFG;
return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
FROM {$CFG->prefix}forum_posts p
LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
WHERE p.parent = '$parent'
ORDER BY p.created ASC");
}
/**
* An array of forum objects that the user is allowed to read/search through.
* @param $userid
* @param $courseid - if 0, we look for forums throughout the whole site.
* @return array of forum objects, or false if no matches
* Forum objects have the following attributes:
* id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
* viewhiddentimedposts
*/
function forum_get_readable_forums($userid, $courseid=0) {
global $CFG, $USER;
require_once($CFG->dirroot.'/course/lib.php');
if (!$forummod = get_record('modules', 'name', 'forum')) {
error('The forum module is not installed');
}
if ($courseid) {
$courses = get_records('course', 'id', $courseid);
} else {
// If no course is specified, then the user can see SITE + his courses.
// And admins can see all courses, so pass the $doanything flag enabled
$courses1 = get_records('course', 'id', SITEID);
$courses2 = get_my_courses($userid, null, null, true);
$courses = array_merge($courses1, $courses2);
}
if (!$courses) {
return array();
}
$readableforums = array();
foreach ($courses as $course) {
$modinfo =& get_fast_modinfo($course);
if (is_null($modinfo->groups)) {
$modinfo->groups = groups_get_user_groups($course->id, $userid);
}
if (empty($modinfo->instances['forum'])) {
// hmm, no forums?
continue;
}
$courseforums = get_records('forum', 'course', $course->id);
foreach ($modinfo->instances['forum'] as $forumid => $cm) {
if (!$cm->uservisible or !isset($courseforums[$forumid])) {
continue;
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$forum = $courseforums[$forumid];
$forum->context = $context;
$forum->cm = $cm;
if (!has_capability('mod/forum:viewdiscussion', $context)) {
continue;
}
/// group access
if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
if (is_null($modinfo->groups)) {
$modinfo->groups = groups_get_user_groups($course->id, $USER->id);
}
if (empty($CFG->enablegroupings)) {
$forum->onlygroups = $modinfo->groups[0];
$forum->onlygroups[] = -1;
} else if (isset($modinfo->groups[$cm->groupingid])) {
$forum->onlygroups = $modinfo->groups[$cm->groupingid];
$forum->onlygroups[] = -1;
} else {
$forum->onlygroups = array(-1);
}
}
/// hidden timed discussions
$forum->viewhiddentimedposts = true;
if (!empty($CFG->forum_enabletimedposts)) {
if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
$forum->viewhiddentimedposts = false;
}
}
/// qanda access
if ($forum->type == 'qanda'
&& !has_capability('mod/forum:viewqandawithoutposting', $context)) {
// We need to check whether the user has posted in the qanda forum.
$forum->onlydiscussions = array(); // Holds discussion ids for the discussions
// the user is allowed to see in this forum.
if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
foreach ($discussionspostedin as $d) {
$forum->onlydiscussions[] = $d->id;
}
}
}
$readableforums[$forum->id] = $forum;
}
unset($modinfo);
} // End foreach $courses
//print_object($courses);
//print_object($readableforums);
return $readableforums;
}
/**
* Returns a list of posts found using an array of search terms.
* @param $searchterms - array of search terms, e.g. word +word -word
* @param $courseid - if 0, we search through the whole site
* @param $page
* @param $recordsperpage=50
* @param &$totalcount
* @param $extrasql
* @return array of posts found
*/
function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
&$totalcount, $extrasql='') {
global $CFG, $USER;
require_once($CFG->libdir.'/searchlib.php');
$forums = forum_get_readable_forums($USER->id, $courseid);
if (count($forums) == 0) {
$totalcount = 0;
return false;
}
$now = round(time(), -2); // db friendly
$fullaccess = array();
$where = array();
foreach ($forums as $forumid => $forum) {
$select = array();
if (!$forum->viewhiddentimedposts) {
$select[] = "(d.userid = {$USER->id} OR (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)))";
}
$cm = $forum->cm;
$context = $forum->context;
if ($forum->type == 'qanda'
&& !has_capability('mod/forum:viewqandawithoutposting', $context)) {
if (!empty($forum->onlydiscussions)) {
$discussionsids = implode(',', $forum->onlydiscussions);
$select[] = "(d.id IN ($discussionsids) OR p.parent = 0)";
} else {
$select[] = "p.parent = 0";
}
}
if (!empty($forum->onlygroups)) {
$groupids = implode(',', $forum->onlygroups);
$select[] = "d.groupid IN ($groupids)";
}
if ($select) {
$selects = implode(" AND ", $select);
$where[] = "(d.forum = $forumid AND $selects)";
} else {
$fullaccess[] = $forumid;
}
}
if ($fullaccess) {
$fullids = implode(',', $fullaccess);
$where[] = "(d.forum IN ($fullids))";
}
$selectdiscussion = "(".implode(" OR ", $where).")";
// Some differences SQL
$LIKE = sql_ilike();
$NOTLIKE = 'NOT ' . $LIKE;
if ($CFG->dbfamily == 'postgres') {
$REGEXP = '~*';
$NOTREGEXP = '!~*';
} else {
$REGEXP = 'REGEXP';
$NOTREGEXP = 'NOT REGEXP';
}
$messagesearch = '';
$searchstring = '';
// Need to concat these back together for parser to work.
foreach($searchterms as $searchterm){
if ($searchstring != '') {
$searchstring .= ' ';
}
$searchstring .= $searchterm;
}
// We need to allow quoted strings for the search. The quotes *should* be stripped
// by the parser, but this should be examined carefully for security implications.
$searchstring = str_replace("\\\"","\"",$searchstring);
$parser = new search_parser();
$lexer = new search_lexer($parser);
if ($lexer->parse($searchstring)) {
$parsearray = $parser->get_parsed_array();
// Experimental feature under 1.8! MDL-8830
// Use alternative text searches if defined
// This feature only works under mysql until properly implemented for other DBs
// Requires manual creation of text index for forum_posts before enabling it:
// CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
// Experimental feature under 1.8! MDL-8830
if (!empty($CFG->forum_usetextsearches)) {
$messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
'p.userid', 'u.id', 'u.firstname',
'u.lastname', 'p.modified', 'd.forum');
} else {
$messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
'p.userid', 'u.id', 'u.firstname',
'u.lastname', 'p.modified', 'd.forum');
}
}
$fromsql = "{$CFG->prefix}forum_posts p,
{$CFG->prefix}forum_discussions d,
{$CFG->prefix}user u";
$selectsql = " $messagesearch
AND p.discussion = d.id
AND p.userid = u.id
AND $selectdiscussion
$extrasql";
$countsql = "SELECT COUNT(*)
FROM $fromsql
WHERE $selectsql";
$searchsql = "SELECT p.*,
d.forum,
u.firstname,
u.lastname,
u.email,
u.picture,
u.imagealt
FROM $fromsql
WHERE $selectsql
ORDER BY p.modified DESC";
$totalcount = count_records_sql($countsql);
return get_records_sql($searchsql, $limitfrom, $limitnum);
}
/**
* Returns a list of ratings for all posts in discussion
* @param object $discussion
* @return array of ratings or false
*/
function forum_get_all_discussion_ratings($discussion) {
global $CFG;
return get_records_sql("SELECT r.id, r.userid, p.id AS postid, r.rating
FROM {$CFG->prefix}forum_ratings r,
{$CFG->prefix}forum_posts p
WHERE r.post = p.id AND p.discussion = $discussion->id
ORDER BY p.id ASC");
}
/**
* Returns a list of ratings for one specific user for all posts in discussion
* @global object $CFG
* @param object $discussions the discussions for which we return all ratings
* @param int $userid the user for who we return all ratings
* @return object
*/
function forum_get_all_user_ratings($userid, $discussions) {
global $CFG;
foreach ($discussions as $discussion) {
if (!isset($discussionsid)){
$discussionsid = $discussion->id;
}
else {
$discussionsid .= ",".$discussion->id;
}
}
$sql = "SELECT r.id, r.userid, p.id AS postid, r.rating
FROM {$CFG->prefix}forum_ratings r,
{$CFG->prefix}forum_posts p
WHERE r.post = p.id AND p.userid = $userid";
//postgres compability
if (!isset($discussionsid)) {
$sql .=" AND p.discussion IN (".$discussionsid.")";
}
$sql .=" ORDER BY p.id ASC";
return get_records_sql($sql);
}
/**
* Returns a list of ratings for a particular post - sorted.
* @param int $postid
* @param string $sort
* @return array of ratings or false
*/
function forum_get_ratings($postid, $sort="u.firstname ASC") {
global $CFG;
return get_records_sql("SELECT u.*, r.rating, r.time
FROM {$CFG->prefix}forum_ratings r,
{$CFG->prefix}user u
WHERE r.post = '$postid'
AND r.userid = u.id
ORDER BY $sort");
}
/**
* Returns a list of all new posts that have not been mailed yet
* @param int $starttime - posts created after this time
* @param int $endtime - posts created before this
* @param int $now - used for timed discussions only
*/
function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
global $CFG;
if (!empty($CFG->forum_enabletimedposts)) {
if (empty($now)) {
$now = time();
}
$timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
} else {
$timedsql = "";
}
return get_records_sql("SELECT p.*, d.course, d.forum
FROM {$CFG->prefix}forum_posts p
JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
WHERE p.mailed = 0
AND p.created >= $starttime
AND (p.created < $endtime OR p.mailnow = 1)
$timedsql
ORDER BY p.modified ASC");
}
/**
* Marks posts before a certain time as being mailed already
*/
function forum_mark_old_posts_as_mailed($endtime, $now=null) {
global $CFG;
if (empty($now)) {
$now = time();
}
if (empty($CFG->forum_enabletimedposts)) {
return execute_sql("UPDATE {$CFG->prefix}forum_posts
SET mailed = '1'
WHERE (created < $endtime OR mailnow = 1)
AND mailed = 0", false);
} else {
return execute_sql("UPDATE {$CFG->prefix}forum_posts
SET mailed = '1'
WHERE discussion NOT IN (SELECT d.id
FROM {$CFG->prefix}forum_discussions d
WHERE d.timestart > $now)
AND (created < $endtime OR mailnow = 1)
AND mailed = 0", false);
}
}
/**
* Get all the posts for a user in a forum suitable for forum_print_post
*/
function forum_get_user_posts($forumid, $userid) {
global $CFG;
$timedsql = "";
if (!empty($CFG->forum_enabletimedposts)) {
$cm = get_coursemodule_from_instance('forum', $forumid);
if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
$now = time();
$timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
}
}
return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
FROM {$CFG->prefix}forum f
JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
JOIN {$CFG->prefix}user u ON u.id = p.userid
WHERE f.id = $forumid
AND p.userid = $userid
$timedsql
ORDER BY p.modified ASC");
}
/**
* Get all the discussions user participated in
* @param int $forumid
* @param int $userid
* @return array or false
*/
function forum_get_user_involved_discussions($forumid, $userid) {
global $CFG;
$timedsql = "";
if (!empty($CFG->forum_enabletimedposts)) {
$cm = get_coursemodule_from_instance('forum', $forumid);
if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
$now = time();
$timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
}
}
return get_records_sql("SELECT DISTINCT d.*
FROM {$CFG->prefix}forum f
JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
WHERE f.id = $forumid
AND p.userid = $userid
$timedsql");
}
/**
* Get all the posts for a user in a forum suitable for forum_print_post
* @param int $forumid
* @param int $userid
* @return array of counts or false
*/
function forum_count_user_posts($forumid, $userid) {
global $CFG;
$timedsql = "";
if (!empty($CFG->forum_enabletimedposts)) {
$cm = get_coursemodule_from_instance('forum', $forumid);
if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
$now = time();
$timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
}
}
return get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
FROM {$CFG->prefix}forum f
JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
JOIN {$CFG->prefix}user u ON u.id = p.userid
WHERE f.id = $forumid
AND p.userid = $userid
$timedsql");
}
/**
* Given a log entry, return the forum post details for it.
*/
function forum_get_post_from_log($log) {
global $CFG;
if ($log->action == "add post") {
return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
u.firstname, u.lastname, u.email, u.picture
FROM {$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p,
{$CFG->prefix}forum f,
{$CFG->prefix}user u
WHERE p.id = '$log->info'
AND d.id = p.discussion
AND p.userid = u.id
AND u.deleted <> '1'
AND f.id = d.forum");
} else if ($log->action == "add discussion") {
return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
u.firstname, u.lastname, u.email, u.picture
FROM {$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p,
{$CFG->prefix}forum f,
{$CFG->prefix}user u
WHERE d.id = '$log->info'
AND d.firstpost = p.id
AND p.userid = u.id
AND u.deleted <> '1'
AND f.id = d.forum");
}
return NULL;
}
/**
* Given a discussion id, return the first post from the discussion
*/
function forum_get_firstpost_from_discussion($discussionid) {
global $CFG;
return get_record_sql("SELECT p.*
FROM {$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p
WHERE d.id = '$discussionid'
AND d.firstpost = p.id ");
}
/**
* Returns an array of counts of replies to each discussion
*/
function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
global $CFG;
if ($limit > 0) {
$limitfrom = 0;
$limitnum = $limit;
} else if ($page != -1) {
$limitfrom = $page*$perpage;
$limitnum = $perpage;
} else {
$limitfrom = 0;
$limitnum = 0;
}
if ($forumsort == "") {
$orderby = "";
$groupby = "";
} else {
$orderby = "ORDER BY $forumsort";
$groupby = ", ".strtolower($forumsort);
$groupby = str_replace('desc', '', $groupby);
$groupby = str_replace('asc', '', $groupby);
}
if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
$sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
FROM {$CFG->prefix}forum_posts p
JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
WHERE p.parent > 0 AND d.forum = $forumid
GROUP BY p.discussion";
return get_records_sql($sql);
} else {
$sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
FROM {$CFG->prefix}forum_posts p
JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
WHERE d.forum = $forumid
GROUP BY p.discussion $groupby
$orderby";
return get_records_sql("SELECT * FROM ($sql) sq", $limitfrom, $limitnum);
}
}
function forum_count_discussions($forum, $cm, $course) {
global $CFG, $USER;
static $cache = array();
$now = round(time(), -2); // db cache friendliness
if (!isset($cache[$course->id])) {
if (!empty($CFG->forum_enabletimedposts)) {
$timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
} else {
$timedsql = "";
}
$sql = "SELECT f.id, COUNT(d.id) as dcount
FROM {$CFG->prefix}forum f
JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
WHERE f.course = $course->id
$timedsql
GROUP BY f.id";
if ($counts = get_records_sql($sql)) {
foreach ($counts as $count) {
$counts[$count->id] = $count->dcount;
}
$cache[$course->id] = $counts;
} else {
$cache[$course->id] = array();
}
}
if (empty($cache[$course->id][$forum->id])) {
return 0;
}
$groupmode = groups_get_activity_groupmode($cm, $course);
if ($groupmode != SEPARATEGROUPS) {
return $cache[$course->id][$forum->id];
}
if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
return $cache[$course->id][$forum->id];
}
require_once($CFG->dirroot.'/course/lib.php');
$modinfo =& get_fast_modinfo($course);
if (is_null($modinfo->groups)) {
$modinfo->groups = groups_get_user_groups($course->id, $USER->id);
}
if (empty($CFG->enablegroupings)) {
$mygroups = $modinfo->groups[0];
} else {
$mygroups = $modinfo->groups[$cm->groupingid];
}
// add all groups posts
if (empty($mygroups)) {
$mygroups = array(-1=>-1);
} else {
$mygroups[-1] = -1;
}
$mygroups = implode(',', $mygroups);
if (!empty($CFG->forum_enabletimedposts)) {
$timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
} else {
$timedsql = "";
}
$sql = "SELECT COUNT(d.id)
FROM {$CFG->prefix}forum_discussions d
WHERE d.forum = $forum->id AND d.groupid IN ($mygroups)
$timedsql";
return get_field_sql($sql);
}
/**
* How many unrated posts are in the given discussion for a given user?
*/
function forum_count_unrated_posts($discussionid, $userid) {
global $CFG;
if ($posts = get_record_sql("SELECT count(*) as num
FROM {$CFG->prefix}forum_posts
WHERE parent > 0
AND discussion = '$discussionid'
AND userid <> '$userid' ")) {
if ($rated = get_record_sql("SELECT count(*) as num
FROM {$CFG->prefix}forum_posts p,
{$CFG->prefix}forum_ratings r
WHERE p.discussion = '$discussionid'
AND p.id = r.post
AND r.userid = '$userid'")) {
$difference = $posts->num - $rated->num;
if ($difference > 0) {
return $difference;
} else {
return 0; // Just in case there was a counting error
}
} else {
return $posts->num;
}
} else {
return 0;
}
}
/**
* Get all discussions in a forum
*/
function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
global $CFG, $USER;
$timelimit = '';
$modcontext = null;
$now = round(time(), -2);
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { /// User must have perms to view discussions
return array();
}
if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
$timelimit = " AND ((d.timestart <= $now AND (d.timeend = 0 OR d.timeend > $now))";
if (isloggedin()) {
$timelimit .= " OR d.userid = $USER->id";
}
$timelimit .= ")";
}
}
if ($limit > 0) {
$limitfrom = 0;
$limitnum = $limit;
} else if ($page != -1) {
$limitfrom = $page*$perpage;
$limitnum = $perpage;
} else {
$limitfrom = 0;
$limitnum = 0;
}
$groupmode = groups_get_activity_groupmode($cm);
$currentgroup = groups_get_activity_group($cm);
if ($groupmode) {
if (empty($modcontext)) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
}
if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "";
}
} else {
//seprate groups without access all
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "AND d.groupid = -1";
}
}
} else {
$groupselect = "";
}
if (empty($forumsort)) {
$forumsort = "d.timemodified DESC";
}
if (empty($fullpost)) {
$postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
} else {
$postdata = "p.*";
}
if (empty($userlastmodified)) { // We don't need to know this
$umfields = "";
$umtable = "";
} else {
$umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
$umtable = " LEFT JOIN {$CFG->prefix}user um ON (d.usermodified = um.id)";
}
$sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
FROM {$CFG->prefix}forum_discussions d
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
JOIN {$CFG->prefix}user u ON p.userid = u.id
$umtable
WHERE d.forum = {$cm->instance} AND p.parent = 0
$timelimit $groupselect
ORDER BY $forumsort";
return get_records_sql($sql, $limitfrom, $limitnum);
}
function forum_get_discussions_unread($cm) {
global $CFG, $USER;
$now = round(time(), -2);
$groupmode = groups_get_activity_groupmode($cm);
$currentgroup = groups_get_activity_group($cm);
if ($groupmode) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "";
}
} else {
//seprate groups without access all
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "AND d.groupid = -1";
}
}
} else {
$groupselect = "";
}
$cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
if (!empty($CFG->forum_enabletimedposts)) {
$timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
} else {
$timedsql = "";
}
$sql = "SELECT d.id, COUNT(p.id) AS unread
FROM {$CFG->prefix}forum_discussions d
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $USER->id)
WHERE d.forum = {$cm->instance}
AND p.modified >= $cutoffdate AND r.id is NULL
$timedsql
$groupselect
GROUP BY d.id";
if ($unreads = get_records_sql($sql)) {
foreach ($unreads as $unread) {
$unreads[$unread->id] = $unread->unread;
}
return $unreads;
} else {
return array();
}
}
function forum_get_discussions_count($cm) {
global $CFG, $USER;
$now = round(time(), -2);
$groupmode = groups_get_activity_groupmode($cm);
$currentgroup = groups_get_activity_group($cm);
if ($groupmode) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "";
}
} else {
//seprate groups without access all
if ($currentgroup) {
$groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
} else {
$groupselect = "AND d.groupid = -1";
}
}
} else {
$groupselect = "";
}
$cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
$timelimit = "";
if (!empty($CFG->forum_enabletimedposts)) {
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
$timelimit = " AND ((d.timestart <= $now AND (d.timeend = 0 OR d.timeend > $now))";
if (isloggedin()) {
$timelimit .= " OR d.userid = $USER->id";
}
$timelimit .= ")";
}
}
$sql = "SELECT COUNT(d.id)
FROM {$CFG->prefix}forum_discussions d
JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
WHERE d.forum = {$cm->instance} AND p.parent = 0
$timelimit $groupselect";
return get_field_sql($sql);
}
/**
* Get all discussions started by a particular user in a course (or group)
* This function no longer used ...
*/
function forum_get_user_discussions($courseid, $userid, $groupid=0) {
global $CFG;
if ($groupid) {
$groupselect = " AND d.groupid = '$groupid' ";
} else {
$groupselect = "";
}
return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
f.type as forumtype, f.name as forumname, f.id as forumid
FROM {$CFG->prefix}forum_discussions d,
{$CFG->prefix}forum_posts p,
{$CFG->prefix}user u,
{$CFG->prefix}forum f
WHERE d.course = '$courseid'
AND p.discussion = d.id
AND p.parent = 0
AND p.userid = u.id
AND u.id = '$userid'
AND d.forum = f.id $groupselect
ORDER BY p.created DESC");
}
/**
* Get the list of potential subscribers to a forum.
*
* @param object $forumcontext the forum context.
* @param integer $groupid the id of a group, or 0 for all groups.
* @param string $fields the list of fields to return for each user. As for get_users_by_capability.
* @param string $sort sort order. As for get_users_by_capability.
* @return array list of users.
*/
function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
return get_users_by_capability($forumcontext, 'mod/forum:initialsubscriptions', $fields, $sort, '', '', $groupid, '', false, true);
}
/**
* Returns list of user objects that are subscribed to this forum
*
* @param object $course the course
* @param forum $forum the forum
* @param integer $groupid group id, or 0 for all.
* @param object $context the forum context, to save re-fetching it where possible.
* @return array list of users.
*/
function forum_subscribed_users($course, $forum, $groupid=0, $context = NULL) {
global $CFG;
if ($groupid) {
$grouptables = ", {$CFG->prefix}groups_members gm ";
$groupselect = "AND gm.groupid = $groupid AND u.id = gm.userid";
} else {
$grouptables = '';
$groupselect = '';
}
if (forum_is_forcesubscribed($forum)) {
if (empty($context)) {
$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
}
$sort = "u.email ASC";
$fields ="u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop, u.imagealt,
u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums, u.mnethostid";
$results = forum_get_potential_subscribers($context, $groupid, $fields, $sort);
} else {
$results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop, u.imagealt,
u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums, u.mnethostid
FROM {$CFG->prefix}user u,
{$CFG->prefix}forum_subscriptions s $grouptables
WHERE s.forum = '$forum->id'
AND s.userid = u.id
AND u.deleted = 0 $groupselect
ORDER BY u.email ASC");
}
static $guestid = null;
if (is_null($guestid)) {
if ($guest = guest_user()) {
$guestid = $guest->id;
} else {
$guestid = 0;
}
}
// Guest user should never be subscribed to a forum.
unset($results[$guestid]);
return $results;
}
// OTHER FUNCTIONS ///////////////////////////////////////////////////////////
function forum_get_course_forum($courseid, $type) {
// How to set up special 1-per-course forums
global $CFG;
if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
// There should always only be ONE, but with the right combination of
// errors there might be more. In this case, just return the oldest one (lowest ID).
foreach ($forums as $forum) {
return $forum; // ie the first one
}
}
// Doesn't exist, so create one now.
$forum->course = $courseid;
$forum->type = "$type";
switch ($forum->type) {
case "news":
$forum->name = addslashes(get_string("namenews", "forum"));
$forum->intro = addslashes(get_string("intronews", "forum"));
$forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
$forum->assessed = 0;
if ($courseid == SITEID) {
$forum->name = get_string("sitenews");
$forum->forcesubscribe = 0;
}
break;
case "social":
$forum->name = addslashes(get_string("namesocial", "forum"));
$forum->intro = addslashes(get_string("introsocial", "forum"));
$forum->assessed = 0;
$forum->forcesubscribe = 0;
break;
default:
notify("That forum type doesn't exist!");
return false;
break;
}
$forum->timemodified = time();
$forum->id = insert_record("forum", $forum);
if (! $module = get_record("modules", "name", "forum")) {
notify("Could not find forum module!!");
return false;
}
$mod = new object();
$mod->course = $courseid;
$mod->module = $module->id;
$mod->instance = $forum->id;
$mod->section = 0;
if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
notify("Could not add a new course module to the course '" . format_string($course->fullname) . "'");
return false;
}
if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
notify("Could not add the new course module to that section");
return false;
}
if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
notify("Could not update the course module with the correct section");
return false;
}
include_once("$CFG->dirroot/course/lib.php");
rebuild_course_cache($courseid);
return get_record("forum", "id", "$forum->id");
}
/**
* Given the data about a posting, builds up the HTML to display it and
* returns the HTML in a string. This is designed for sending via HTML email.
*/
function forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto,
$ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
global $CFG;
if (!isset($userto->viewfullnames[$forum->id])) {
if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
error('Course Module ID was incorrect');
}
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
$viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
} else {
$viewfullnames = $userto->viewfullnames[$forum->id];
}
// format the post body
$options = new object();
$options->para = true;
$formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);