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); $FORUM_LAYOUT_MODES = array ( FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), FORUM_MODE_NESTED => get_string('modenested', 'forum') ); // These are course content forums that can be added to the course manually $FORUM_TYPES = array ('general' => get_string('generalforum', 'forum'), 'eachuser' => get_string('eachuserforum', 'forum'), 'single' => get_string('singleforum', 'forum'), 'qanda' => get_string('qandaforum', 'forum') ); $FORUM_OPEN_MODES = array ('2' => get_string('openmode2', 'forum'), '1' => get_string('openmode1', 'forum'), '0' => get_string('openmode0', 'forum') ); if (!isset($CFG->forum_displaymode)) { set_config('forum_displaymode', FORUM_MODE_NESTED); } if (!isset($CFG->forum_shortpost)) { set_config('forum_shortpost', 300); // Less non-HTML characters than this is short } if (!isset($CFG->forum_longpost)) { set_config('forum_longpost', 600); // More non-HTML characters than this is long } if (!isset($CFG->forum_manydiscussions)) { set_config('forum_manydiscussions', 100); // Number of discussions on a page } if (!isset($CFG->forum_maxbytes)) { set_config('forum_maxbytes', 512000); // Default maximum size for all forums } if (!isset($CFG->forum_trackreadposts)) { set_config('forum_trackreadposts', true); // Default whether user needs to mark a post as read } if (!isset($CFG->forum_oldpostdays)) { set_config('forum_oldpostdays', 14); // Default number of days that a post is considered old } if (!isset($CFG->forum_usermarksread)) { set_config('forum_usermarksread', false); // Default whether user needs to mark a post as read } if (!isset($CFG->forum_cleanreadtime)) { set_config('forum_cleanreadtime', 2); // Default time (hour) to execute 'clean_read_records' cron } if (!isset($CFG->forum_replytouser)) { set_config('forum_replytouser', true); // Default maximum size for all forums } if (empty($USER->id) or isguest()) { $CFG->forum_trackreadposts = false; // This feature never works when a user isn't logged in } if (!isset($CFG->forum_enabletimedposts)) { // Newish feature that is not quite ready for production in 1.6 $CFG->forum_enabletimedposts = false; } /// 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. */ 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; 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 $users = get_course_users($forum->course); 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. */ function forum_update_instance($forum) { $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; } 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 { error('Could not find the discussion in 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; 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. */ 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 */ function forum_cron() { global $CFG, $USER; $CFG->enablerecordcache = true; // We want all the caching we can get $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(); $postinfos = 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)) { // 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])) { if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, true)) { foreach ($subusers as $postuser) { // do not try to mail users with stopped email if ($postuser->emailstop) { add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id); continue; } // this user is subscribed to this forum $subscribedusers[$forumid][] = $postuser->id; // this user is a user we have to process later $users[$postuser->id] = $postuser; } } } $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); // we might want to add another layer - forums here (by checking array_keys($subscribedusers)) // so that we can skip many posts foreach ($posts as $pid => $post) { // 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)) { mtrace('Could not find user '.$post->userid); continue; } // 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 (empty($subscribedusers[$forum->id]) || !in_array($userto->id, $subscribedusers[$forum->id])) { continue; // user does not subscribe to this forum } // Get the context (from cache) $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); // Cached already // setup global $COURSE properly - needed for roles and languages course_setup($course); // More environment // Make sure groups allow this user to see this email if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm)) { // 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)) { 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; 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.'" id.'@'.$hostname.'>', 'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id, 'Message-ID: id.'@'.$hostname.'>', 'In-Reply-To: parent.'@'.$hostname.'>', 'References: parent.'@'.$hostname.'>', 'X-Course-Id: '.$course->id, 'X-Course-Name: '.format_string($course->fullname, true) ); $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 && forum_tp_can_track_forums($forum, $userto) && forum_tp_is_tracked($forum, $userto->id)) { if (!forum_tp_mark_post_read($userto->id, $post, $forum->id)) { mtrace("Error: mod/forum/cron.php: Could not mark post $post->id read for user $userto->id". " while sending email."); } } } mtrace('post '.$post->id. ': '.$post->subject); } } } 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"); } } } $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 if (!isset($CFG->digestmailtimelast)) { // To catch the first time set_config('digestmailtimelast', 0); } $timenow = time(); $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600); if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) { set_config('digestmailtimelast', $timenow); mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone)); if ($digestposts = get_records('forum_queue')) { // We have work to do $usermailcount = 0; //caches - reuse the those filled before too $discussionposts = array(); $userdiscussions = array(); foreach ($digestposts as $digestpost) { 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) { 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; } // 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 mtrace(get_string('processingdigest', 'forum', $userid), '... '); // First of all delete all the queue entries for this user delete_records('forum_queue', 'userid', $userid); $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); $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.'&course='.$site->id; $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n"; $headerdata->userprefs = ''.get_string('digestmailprefs', 'forum').''; $posthtml = ""; foreach ($CFG->stylesheets as $stylesheet) { $posthtml .= ''."\n"; } $posthtml .= "\n\n"; $posthtml .= '

'.get_string('digestmailheader', 'forum', $headerdata).'



'; 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); $strforums = get_string('forums', 'forum'); $canunsubscribe = ! forum_is_forcesubscribed($forum); $canreply = forum_user_can_post($forum, $userto); $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 .= "

". "wwwroot/course/view.php?id=$course->id\">$course->shortname -> ". "wwwroot/mod/forum/index.php?id=$course->id\">$strforums -> ". "wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true).""; if ($discussion->name == $forum->name) { $posthtml .= "

"; } else { $posthtml .= " -> wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."

"; } $posthtml .= '

'; $postsarray = $discussionposts[$discussionid]; sort($postsarray); // Create an empty array to use for marking read posts. // (I'm sure there's already a structure I can use here, but I can't be sure.) $markread = array(); 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)) { mtrace('Could not find user '.$post->userid); continue; } $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 = "wwwroot/user/view.php?id=$userfrom->id&course=$course->id\">$by->name"; $posthtml .= '

'.format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by).'
'; } else { // The full treatment $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true); $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false); // Create an array of postid's for this user to mark as read. if (!$CFG->forum_usermarksread && forum_tp_can_track_forums($forum, $userto) && forum_tp_is_tracked($forum, $userto->id)) { $markread[$post->id]->post = $post; $markread[$post->id]->forumid = $forum->id; } } } if ($canunsubscribe) { $posthtml .= "\n
wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."
"; } else { $posthtml .= "\n
".get_string("everyoneissubscribed", "forum")."
"; } $posthtml .= '

'; } $posthtml .= ''; 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 if (!$CFG->forum_usermarksread && forum_tp_can_track_forums($forum->id, $userto) && forum_tp_is_tracked($forum->id, $userto->id)) { foreach ($markread as $postinfo) { if (!forum_tp_mark_post_read($userto->id, $postinfo->post, $postinfo->forumid)) { mtrace("Error: mod/forum/cron.php: Could not mark post $postid read for user $userto->id". " while sending digest email."); } } } } } } } if (!empty($usermailcount)) { mtrace(get_string('digestsentusers', 'forum', $usermailcount)); } $USER = $cronuser; course_setup(SITEID); // reset cron user language, theme and timezone settings if (!empty($CFG->forum_lastreadclean)) { $timenow = time(); if ($CFG->forum_lastreadclean + (24*3600) < $timenow) { set_config('forum_lastreadclean', $timenow); 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 (!$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); $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); $canreply = forum_user_can_post($forum, $userto); $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 ''; } $strforums = get_string('forums', 'forum'); $canreply = forum_user_can_post($forum, $userto); $canunsubscribe = ! forum_is_forcesubscribed($forum); $posthtml = ''; foreach ($CFG->stylesheets as $stylesheet) { $posthtml .= ''."\n"; } $posthtml .= ''; $posthtml .= "\n\n\n"; $posthtml .= ''; } else { $posthtml .= ' » '. format_string($discussion->name,true).''; } $posthtml .= forum_make_mail_post($post, $userfrom, $userto, $course, false, $canreply, true, false); if ($canunsubscribe) { $posthtml .= '
'; } $posthtml .= ''; 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) { if ($posts = forum_get_user_posts($forum->id, $user->id)) { $result->info = get_string("numposts", "forum", count($posts)); $lastpost = array_pop($posts); $result->time = $lastpost->modified; return $result; } return NULL; } /** * */ function forum_user_complete($course, $user, $mod, $forum) { global $CFG; if ($posts = forum_get_user_posts($forum->id, $user->id)) { foreach ($posts as $post) { $post->forum = $forum->id; forum_print_post($post, $course->id, $ownpost=false, $reply=false, $link=false, $rate=false); } } else { echo "

".get_string("noposts", "forum")."

"; } } /** * */ function forum_print_overview($courses,&$htmlarray) { global $USER, $CFG; $LIKE = sql_ilike(); 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 $LIKE '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 .= '
'.$strforum.': '. $forum->name.'
'; $str .= '
'; $str .= $count.' '.$strnumpostssince; if (!empty($showunread)) { $str .= '
'.$thisunread .' '.$strnumunread; } $str .= '
'; } 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 */ function forum_print_recent_activity($course, $isteacher, $timestart) { global $CFG; $LIKE = sql_ilike(); $heading = false; $content = false; if (!$logs = get_records_select('log', 'time > \''.$timestart.'\' AND '. 'course = \''.$course->id.'\' AND '. 'module = \'forum\' AND '. 'action '.$LIKE.' \'add %\' ', 'time ASC')){ return false; } $strftimerecent = get_string('strftimerecent'); $mygroupid = mygroupid($course->id); $groupmode = array(); // To cache group modes $count = 0; foreach ($logs as $log) { //Get post info, I'll need it later if ($post = forum_get_post_from_log($log)) { //Create a temp valid module structure (course,id) $tempmod = new object; $tempmod->course = $log->course; $tempmod->id = $post->forum; //Obtain the visible property from the instance $coursecontext = get_context_instance(CONTEXT_COURSE, $tempmod->course); $modvisible = instance_is_visible('forum', $tempmod) || has_capability('moodle/course:viewhiddenactivities', $coursecontext); } //Only if the post exists and mod is visible if ($post && $modvisible) { if (!isset($cm[$post->forum])) { $cm[$post->forum] = get_coursemodule_from_instance('forum', $post->forum, $course->id); } $modcontext = get_context_instance(CONTEXT_MODULE, $cm[$post->forum]->id); // Check whether this is belongs to a discussion in a group that // should NOT be accessible to the current user if (!has_capability('moodle/site:accessallgroups', $modcontext) && $post->groupid != -1) { // Open discussions have groupid -1 $groupmode[$post->forum] = groups_get_activity_groupmode($cm[$post->forum]); if ($groupmode[$post->forum]) { //hope i didn't break anything if (!@in_array($mygroupid, $post->groupid))/*$mygroupid != $post->groupid*/{ continue; } } } if (! $heading) { print_headline(get_string('newforumposts', 'forum').':', 3); $heading = true; $content = true; } $date = userdate($post->modified, $strftimerecent); $subjectclass = ($log->action == 'add discussion') ? ' bold' : ''; //Accessibility: markup as a list. if ($count < 1) { echo "\n\n"; return $content; } /** * 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" : ""; $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"; return get_records_sql($sql); } /** * Update grades by firing grade_updated event * * @param object $forum null means all forums * @param int $userid specific user only, 0 mean all */ 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)) { grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades); } else if ($userid and $nullifnone) { $grade = new object(); $grade->userid = $userid; $grade->rawgrade = NULL; grade_update('mod/forum', $data->course, 'mod', 'forum', $forum->id, 0, $grade); } } 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)) { if ($rs->RecordCount() > 0) { while ($forum = rs_fetch_next_record($rs)) { forum_grade_item_update($forum); if ($forum->assessed) { forum_update_grades($forum, 0, false); } } } rs_close($rs); } } } /** * Create/update grade item for given forum * * @param object $forum object with extra cmidnumber * @return int 0 if ok */ function forum_grade_item_update($forum) { 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; } return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, $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) */ 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); } /** * */ function forum_scale_used ($forumid,$scaleid) { //This function returns if a scale is being used by one forum $return = false; $rec = get_record("forum","id","$forumid","scale","-$scaleid"); if (!empty($rec) && !empty($scaleid)) { $return = true; } return $return; } // SQL FUNCTIONS /////////////////////////////////////////////////////////// /** * Gets a post with all info ready for forum_print_post * Most of these joins are just to get the forum id */ function forum_get_post_full($postid) { global $CFG; return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture FROM {$CFG->prefix}forum_posts p LEFT 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. */ 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 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 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 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; 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 false; } $readableforums = array(); foreach ($courses as $course) { $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) { $selecthidden = ' AND cm.visible = 1'; } else { $selecthidden = ''; } $selectforums = "SELECT f.id AS id, f.name AS name, f.type AS type, f.course AS course, cm.id AS cmid, cm.visible AS cmvisible, cm.groupmode AS cmgroupmode, cm.groupingid AS cmgroupingid, cm.groupmembersonly AS cmgroupmembersonly FROM {$CFG->prefix}course_modules cm, {$CFG->prefix}forum f WHERE cm.instance = f.id AND cm.course = {$course->id} AND cm.module = {$forummod->id} $selecthidden ORDER BY f.name ASC"; if ($forums = get_records_sql($selectforums)) { $group = groups_get_all_groups($course->id, $userid); foreach ($forums as $forum) { $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid); if (has_capability('mod/forum:viewdiscussion', $forumcontext)) { // Evaluate groupmode. $cm = new object; $cm->id = $forum->cmid; $cm->groupmode = $forum->cmgroupmode; $cm->groupingid = $forum->cmgroupingid; $cm->groupmembersonly = $forum->cmgroupmembersonly; $cm->course = $forum->course; $forum->cmgroupmode = groups_get_activity_groupmode($cm); if (!groups_course_module_visible($cm)) { continue; } if ($forum->cmgroupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $forumcontext)) { $forum->accessallgroups = false; $forum->accessgroup = $group->id; // The user can only access // discussions for this group. } else { $forum->accessallgroups = true; } $forum->viewhiddentimedposts = has_capability('mod/forum:viewhiddentimedposts', $forumcontext); if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) { // 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) { array_push($forum->onlydiscussions, $d->id); } } } array_push($readableforums, $forum); } } } } // 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) { return false; } for ($i=0; $iforum_enabletimedposts) && !$forums[$i]->viewhiddentimedposts) { $now = time(); $selectdiscussion .= " AND ( d.userid = {$USER->id} OR ((d.timestart = 0 OR d.timestart <= $now) AND (d.timeend = 0 OR d.timeend > $now)) )"; } if ($forums[$i]->type == 'qanda' && isset($forums[$i]->onlydiscussions)) { // This is a qanda forum. if (is_array($forums[$i]->onlydiscussions)) { // Show question posts as well as posts from discussions in // which the user has posted a reply. $onlydiscussions = implode(' OR d.id = ', $forums[$i]->onlydiscussions); $selectdiscussion .= " AND ((d.id = $onlydiscussions) OR p.parent = 0)"; } else { // Show only the question posts. $selectdiscussion .= ' AND (p.parent = 0)'; } } if (!$forums[$i]->accessallgroups) { if (!empty($forums[$i]->accessgroup)) { $selectdiscussion .= " AND (d.groupid = {$forums[$i]->accessgroup}"; $selectdiscussion .= ' OR d.groupid = -1)'; // -1 means open for all groups. } else { // User isn't in any group. Only search discussions that are // open to all groups. $selectdiscussion .= ' AND d.groupid = -1'; } } $selectdiscussion .= ")\n"; } $selectdiscussion .= ")"; // 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 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 a particular post - sorted. */ 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 */ function forum_get_unmailed_posts($starttime, $endtime) { global $CFG; $now = time(); return get_records_sql("SELECT p.*, d.course FROM {$CFG->prefix}forum_posts p, {$CFG->prefix}forum_discussions d WHERE p.mailed = 0 AND (p.created >= '$starttime' OR d.timestart > 0) AND (p.created < '$endtime' OR p.mailnow = 1) AND p.discussion = d.id AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')) ORDER BY p.modified ASC"); } /** * Marks posts before a certain time as being mailed already */ function forum_mark_old_posts_as_mailed($endtime) { global $CFG; // Find out posts those are not showing immediately so we can exclude them $now = time(); $delayed_posts = get_records_sql("SELECT p.id, p.discussion FROM {$CFG->prefix}forum_posts p, {$CFG->prefix}forum_discussions d WHERE p.mailed = 0 AND p.discussion = d.id AND d.timestart > '$now'"); $delayed_ids = array(); if ($delayed_posts) { foreach ($delayed_posts as $post) { $delayed_ids[] = $post->id; } } else { $delayed_ids[] = 0; } return execute_sql("UPDATE {$CFG->prefix}forum_posts SET mailed = '1' WHERE id NOT IN (".implode(',',$delayed_ids).") 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; return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture FROM {$CFG->prefix}forum f, {$CFG->prefix}forum_discussions d, {$CFG->prefix}forum_posts p, {$CFG->prefix}user u WHERE f.id = '$forumid' AND d.forum = f.id AND p.discussion = d.id AND p.userid = '$userid' AND p.userid = u.id ORDER BY p.modified ASC"); } /** * 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 (optionally in one forum or course and/or user) */ function forum_count_discussion_replies($forum='0', $course='0', $user='0') { global $CFG; $forumselect = $courseselect = $userselect = ''; if ($forum) { $forumselect = " AND d.forum = '$forum'"; } if ($course) { $courseselect = " AND d.course = '$course'"; } if ($user) { $userselect = " AND d.userid = '$user'"; } return get_records_sql("SELECT p.discussion, (count(*)) as replies, max(p.id) as lastpostid FROM {$CFG->prefix}forum_posts p, {$CFG->prefix}forum_discussions d WHERE p.parent > 0 $forumselect $courseselect $userselect AND p.discussion = d.id GROUP BY p.discussion"); } /** * 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($forum="0", $forumsort="d.timemodified DESC", $user=0, $fullpost=true, $currentgroup=-1, $limit=0, $userlastmodified=false) { global $CFG, $USER; $timelimit = ''; if (!empty($CFG->forum_enabletimedposts)) { if (!$cm = get_coursemodule_from_instance('forum', $forum)) { error('Course Module ID was incorrect'); } $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) { $now = time(); $timelimit = " AND ((d.timestart = 0 OR d.timestart <= '$now') AND (d.timeend = 0 OR d.timeend > '$now')"; if (!empty($USER->id)) { $timelimit .= " OR d.userid = '$USER->id'"; } $timelimit .= ')'; } } if ($user) { $userselect = " AND u.id = '$user' "; } else { $userselect = ""; } $limitfrom = 0; $limitnum = 0; if ($limit) { $limitnum = $limit; } if ($currentgroup == -1) { $currentgroup = get_current_group($cm->course); } if ($currentgroup) { $groupselect = " AND (d.groupid = '$currentgroup' OR 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)'; } //TODO: there must be a nice way to do this that keeps both postgres and mysql 3.2x happy but I can't find it right now. if ($CFG->dbfamily == 'postgres' || $CFG->dbfamily == 'mssql' || $CFG->dbfamily == 'oracle') { return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, u.firstname, u.lastname, u.email, u.picture $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 = '$forum' AND p.parent = 0 $timelimit $groupselect $userselect ORDER BY $forumsort", $limitfrom, $limitnum); } else { // MySQL query. TODO: Check if this is needed (MySQL 4.1 should work with the above query) return get_records_sql("SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, u.firstname, u.lastname, u.email, u.picture $umfields FROM ({$CFG->prefix}forum_posts p, {$CFG->prefix}user u, {$CFG->prefix}forum_discussions d) $umtable WHERE d.forum = '$forum' AND p.discussion = d.id AND p.parent = 0 AND p.userid = u.id $timelimit $groupselect $userselect ORDER BY $forumsort", $limitfrom, $limitnum); } } /** * 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, 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"); } /** * Returns list of user objects that are subscribed to this forum */ function forum_subscribed_users($course, $forum, $groupid=0, $cache=false) { global $CFG; static $resultscache = array(); if ($cache && isset($resultscache[$forum->id][$groupid])) { return $resultscache[$forum->id][$groupid]; } 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)) { $results = get_course_users($course->id); // Otherwise get everyone in the course } else { $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop, u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums FROM {$CFG->prefix}user u, {$CFG->prefix}forum_subscriptions s $grouptables WHERE s.forum = '$forum->id' AND s.userid = u.id AND u.deleted <> 1 $groupselect ORDER BY u.email ASC"); } // Guest user should never be subscribed to a forum. if ($guest = guest_user()) { unset($results[$guest->id]); } if ($cache) { $resultscache[$forum->id][$groupid] = $results; } 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->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(&$post, $user, $touser, $course, $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") { global $CFG, $USER; // the old caching was removed for now, because it did not work due to recent changes in cron $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion); if (!$cm = get_coursemodule_from_instance('forum', $post->forum)) { mtrace('Course Module ID was incorrect'); } $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); // format the post body $options = new object(); $options->para = true; $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id); $output = ''; $output .= ''; if ($post->parent) { $output .= ''; $output .= '
'; $output .= print_user_picture($user->id, $course->id, $user->picture, false, true); $output .= ''; } else { $output .= ''; } $output .= '
'.format_string($post->subject).'
'; $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $modcontext)); $by = new object(); $by->name = ''.$fullname.''; $by->date = userdate($post->modified, '', $touser->timezone); $output .= '
'.get_string('bynameondate', 'forum', $by).'
'; $output .= '
'; if ($group = groups_get_all_groups($course->id, $user->id)) { $output .= print_group_picture($group, $course->id, false, true, true); } else { $output .= ' '; } $output .= ''; if ($post->attachment) { $post->course = $course->id; $output .= '
'; $output .= forum_print_attachments($post, 'html'); $output .= "
"; } $output .= $formattedtext; // Commands $commands = array(); if ($post->parent) { $commands[] = ''.get_string('parent', 'forum').''; } if ($reply) { $commands[] = ''. get_string('reply', 'forum').''; } $output .= '
'; $output .= implode(' | ', $commands); $output .= '
'; // Context link to post if required if ($link) { $output .= ''; } if ($footer) { $output .= ''; } $output .= '
'."\n\n"; return $output; } /** * Print a forum post * * @param object $post The post to print. * @param integer $courseid The course this post belongs to. * @param boolean $ownpost Whether this post belongs to the current user. * @param boolean $reply Whether to print a 'reply' link at the bottom of the message. * @param boolean $link Just print a shortened version of the post as a link to the full post. * @param object $ratings -- I don't really know -- * @param string $footer Extra stuff to print after the message. * @param string $highlight Space-separated list of terms to highlight. * @param int $post_read true, false or -99. If we already know whether this user * has read this post, pass that in, otherwise, pass in -99, and this * function will work it out. * @param boolean $dummyifcantsee When forum_user_can_see_post says that * the current user can't see this post, if this argument is true * (the default) then print a dummy 'you can't see this post' post. * If false, don't output anything at all. */ function forum_print_post(&$post, $courseid, $ownpost=false, $reply=false, $link=false, $ratings=NULL, $footer="", $highlight="", $post_read=-99, $dummyifcantsee=true) { global $USER, $CFG; static $stredit, $strdelete, $strreply, $strparent, $strprune; static $strpruneheading, $displaymode; static $strmarkread, $strmarkunread, $istracked; $discussion = get_record('forum_discussions', 'id', $post->discussion); if (!$cm = get_coursemodule_from_instance('forum', $discussion->forum)) { error('Course Module ID was incorrect'); } $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); if (!forum_user_can_see_post($post->forum,$post->discussion,$post)) { if (!$dummyifcantsee) { return; } echo ''; echo ''; echo ''; if ($post->parent) { echo ''; echo '
'; // print_user_picture($post->userid, $courseid, $post->picture); echo ''; } else { echo ''; } echo '
'.get_string('forumsubjecthidden','forum').'
'; echo '
'; print_string('forumauthorhidden','forum'); echo '
'; echo ' '; // Actual content echo ''."\n"; echo get_string('forumbodyhidden','forum'); echo '
'; return; } if (empty($stredit)) { $stredit = get_string('edit', 'forum'); $strdelete = get_string('delete', 'forum'); $strreply = get_string('reply', 'forum'); $strparent = get_string('parent', 'forum'); $strpruneheading = get_string('pruneheading', 'forum'); $strprune = get_string('prune', 'forum'); $displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode); $strmarkread = get_string('markread', 'forum'); $strmarkunread = get_string('markunread', 'forum'); if (!empty($post->forum)) { $istracked = (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)); } else { $istracked = false; } } if ($istracked) { if ($post_read == -99) { // If we don't know yet... // The front page can display a news item post to non-logged in users. This should // always appear as 'read'. $post_read = empty($USER) || forum_tp_is_post_read($USER->id, $post); } if ($post_read) { $read_style = ' read'; } else { $read_style = ' unread'; echo ''; } } else { $read_style = ''; } echo ''; echo ''; echo ''; if ($post->parent) { echo ''; echo '
'; print_user_picture($post->userid, $courseid, $post->picture); echo ''; } else { echo ''; } echo '
'.format_string($post->subject).'
'; echo '
'; $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext)); $by->name = ''.$fullname.''; $by->date = userdate($post->modified); print_string('bynameondate', 'forum', $by); echo '
'; if ($group = groups_get_all_groups($courseid, $post->userid)) { print_group_picture($group, $courseid, false, false, true); } else { echo ' '; } // Actual content echo ''."\n"; if ($post->attachment) { $post->course = $courseid; $post->forum = get_field('forum_discussions', 'forum', 'id', $post->discussion); echo '
'; $attachedimages = forum_print_attachments($post); echo '
'; } else { $attachedimages = ''; } $options = new Object; $options->para = false; $options->trusttext = true; if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) { // Print shortened version echo format_text(forum_shorten_post($post->message), $post->format, $options, $courseid); $numwords = count_words(strip_tags($post->message)); echo '

'; echo get_string('readtherest', 'forum'); echo ' ('.get_string('numwords', '', $numwords).')...

'; } else { // Print whole message if ($highlight) { echo highlight($highlight, format_text($post->message, $post->format, $options, $courseid)); } else { echo format_text($post->message, $post->format, $options, $courseid); } echo $attachedimages; } // Commands $commands = array(); if ($istracked) { // SPECIAL CASE: The front page can display a news item post to non-logged in users. // Don't display the mark read / unread controls in this case. if ($CFG->forum_usermarksread && !empty($USER)) { if ($post_read) { $mcmd = '&mark=unread&postid='.$post->id; $mtxt = $strmarkunread; } else { $mcmd = '&mark=read&postid='.$post->id; $mtxt = $strmarkread; } if ($displaymode == FORUM_MODE_THREADED) { $commands[] = ''.$mtxt.''; } else { $commands[] = ''.$mtxt.''; } } } if ($post->parent) { // Zoom in to the parent specifically if ($displaymode == FORUM_MODE_THREADED) { $commands[] = ''.$strparent.''; } else { $commands[] = ''.$strparent.''; } } $forumtype = get_field('forum', 'type', 'id', $post->forum); $age = time() - $post->created; // Hack for allow to edit news posts those are not displayed yet until they are displayed if (!$post->parent && $forumtype == 'news' && get_field_sql("SELECT id FROM {$CFG->prefix}forum_discussions WHERE id = $post->discussion AND timestart > ".time())) { $age = 0; } $editanypost = has_capability('mod/forum:editanypost', $modcontext); if ($ownpost or $editanypost) { if (($age < $CFG->maxeditingtime) or $editanypost) { $commands[] = ''.$stredit.''; } } if (has_capability('mod/forum:splitdiscussions', $modcontext) && $post->parent && $forumtype != 'single') { $commands[] = ''.$strprune.''; } if (($ownpost and $age < $CFG->maxeditingtime and has_capability('mod/forum:deleteownpost', $modcontext)) or has_capability('mod/forum:deleteanypost', $modcontext)) { $commands[] = ''.$strdelete.''; } if ($reply) { $commands[] = ''.$strreply.''; } echo '
'; echo implode(' | ', $commands); echo '
'; // Ratings $ratingsmenuused = false; if (!empty($ratings) and !empty($USER->id)) { echo '
'; $useratings = true; if ($ratings->assesstimestart and $ratings->assesstimefinish) { if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) { $useratings = false; } } if ($useratings) { $mypost = ($USER->id == $post->userid); $canviewallratings = has_capability('mod/forum:viewanyrating', $modcontext); if ($canviewallratings and !$mypost) { forum_print_ratings_mean($post->id, $ratings->scale, $canviewallratings); if (!empty($ratings->allow)) { echo ' '; forum_print_rating_menu($post->id, $USER->id, $ratings->scale); $ratingsmenuused = true; } } else if ($mypost) { forum_print_ratings_mean($post->id, $ratings->scale, true); } else if (!empty($ratings->allow) ) { forum_print_rating_menu($post->id, $USER->id, $ratings->scale); $ratingsmenuused = true; } } echo '
'; } // Link to post if required if ($link) { echo ''; } if ($footer) { echo ''; } echo '
'."\n\n"; if ($istracked && !$CFG->forum_usermarksread && !empty($post->forum)) { forum_tp_mark_post_read($USER->id, $post, $post->forum); } return $ratingsmenuused; } /** * This function prints the overview of a discussion in the forum listing. * It needs some discussion information and some post information, these * happen to be combined for efficiency in the $post parameter by the function * that calls this one: forum_print_latest_discussions() * * @param object $post The post object (passed by reference for speed). * @param object $forum The forum object. * @param int $group Current group. * @param string $datestring Format to use for the dates. * @param boolean $cantrack Is tracking enabled for this forum. * @param boolean $forumtracked Is the user tracking this forum. * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course */ function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="", $cantrack=true, $forumtracked=true, $canviewparticipants=true) { global $USER, $CFG; static $rowcount; static $strmarkalldread; if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { error('Course Module ID was incorrect'); } $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); if (!isset($rowcount)) { $rowcount = 0; $strmarkalldread = get_string('markalldread', 'forum'); } else { $rowcount = ($rowcount + 1) % 2; } $post->subject = format_string($post->subject,true); echo "\n\n"; echo ''; // Topic echo ''; echo ''.$post->subject.''; echo "\n"; // Picture echo ''; print_user_picture($post->userid, $forum->course, $post->picture); echo "\n"; // User name $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext)); echo ''; echo ''.$fullname.''; echo "\n"; // Group picture if ($group !== -1) { // Groups are active - group is a group data object or NULL echo ''; if (!empty($group->picture) and empty($group->hidepicture)) { print_group_picture($group, $forum->course, false, false, true); } else if (isset($group->id)) { if($canviewparticipants) { echo ''.$group->name.''; } else { echo $group->name; } } echo "\n"; } if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies echo ''; echo ''; echo $post->replies.''; echo "\n"; if ($cantrack) { echo ''; if ($forumtracked) { if ($post->unread > 0) { echo ''; echo ''; echo $post->unread; echo ''; echo '' . ''.$strmarkalldread.''; echo ''; } else { echo ''; echo $post->unread; echo ''; } } else { echo ''; echo '-'; echo ''; } echo "\n"; } } echo ''; $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case $parenturl = (empty($post->lastpostid)) ? '' : '&parent='.$post->lastpostid; $usermodified->id = $post->usermodified; $usermodified->firstname = $post->umfirstname; $usermodified->lastname = $post->umlastname; echo ''. fullname($usermodified).'
'; echo ''. userdate($usedate, $datestring).''; echo "\n"; echo "\n\n"; } /** * Given a post object that we already know has a long message * this function truncates the message nicely to the first * sane place between $CFG->forum_longpost and $CFG->forum_shortpost */ function forum_shorten_post($message) { global $CFG; $i = 0; $tag = false; $length = strlen($message); $count = 0; $stopzone = false; $truncate = 0; for ($i=0; $i<$length; $i++) { $char = $message[$i]; switch ($char) { case "<": $tag = true; break; case ">": $tag = false; break; default: if (!$tag) { if ($stopzone) { if ($char == ".") { $truncate = $i+1; break 2; } } $count++; } break; } if (!$stopzone) { if ($count > $CFG->forum_shortpost) { $stopzone = true; } } } if (!$truncate) { $truncate = $i; } return substr($message, 0, $truncate); } /** * Print the multiple ratings on a post given to the current user by others. * Scale is an array of ratings */ function forum_print_ratings_mean($postid, $scale, $link=true) { static $strrate; $mean = forum_get_ratings_mean($postid, $scale); if ($mean !== "") { if (empty($strratings)) { $strratings = get_string("ratings", "forum"); } echo "$strratings: "; if ($link) { link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $mean, 400, 600); } else { echo "$mean "; } } } /** * Return the mean rating of a post given to the current user by others. * Scale is an array of possible ratings in the scale * Ratings is an optional simple array of actual ratings (just integers) */ function forum_get_ratings_mean($postid, $scale, $ratings=NULL) { if (!$ratings) { $ratings = array(); if ($rates = get_records("forum_ratings", "post", $postid)) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); if ($count == 0) { return ""; } else if ($count == 1) { return $scale[$ratings[0]]; } else { $total = 0; foreach ($ratings as $rating) { $total += $rating; } $mean = round( ((float)$total/(float)$count) + 0.001); // Little fudge factor so that 0.5 goes UP if (isset($scale[$mean])) { return $scale[$mean]." ($count)"; } else { return "$mean ($count)"; // Should never happen, hopefully } } } /** * Return a summary of post ratings given to the current user by others. * Scale is an array of possible ratings in the scale * Ratings is an optional simple array of actual ratings (just integers) */ function forum_get_ratings_summary($postid, $scale, $ratings=NULL) { if (!$ratings) { $ratings = array(); if ($rates = get_records("forum_ratings", "post", $postid)) { foreach ($rates as $rate) { $rating[] = $rate->rating; } } } if (!$count = count($ratings)) { return ""; } foreach ($scale as $key => $scaleitem) { $sumrating[$key] = 0; } foreach ($ratings as $rating) { $sumrating[$rating]++; } $summary = ""; foreach ($scale as $key => $scaleitem) { $summary = $sumrating[$key].$summary; if ($key > 1) { $summary = "/$summary"; } } return $summary; } /** * Print the menu of ratings as part of a larger form. * If the post has already been - set that value. * Scale is an array of ratings */ function forum_print_rating_menu($postid, $userid, $scale) { static $strrate; if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) { $rating->rating = FORUM_UNSET_POST_RATING; } if (empty($strrate)) { $strrate = get_string("rate", "forum"); } $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale; choose_from_menu($scale, $postid, $rating->rating, ''); } /** * Print the drop down that allows the user to select how they want to have * the discussion displayed. * @param $id - forum id if $forumtype is 'single', * discussion id for any other forum type * @param $mode - forum layout mode * @param $forumtype - optional */ function forum_print_mode_form($id, $mode, $forumtype='') { global $FORUM_LAYOUT_MODES; if ($forumtype == 'single') { popup_form("view.php?f=$id&mode=", $FORUM_LAYOUT_MODES, "mode", $mode, ""); } else { popup_form("discuss.php?d=$id&mode=", $FORUM_LAYOUT_MODES, "mode", $mode, ""); } } /** * */ function forum_search_form($course, $search='') { global $CFG; $output = '
'; $output .= '
'; $output .= '
'; $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true); $output .= ''; $output .= ''; $output .= ''; $output .= '
'; $output .= '
'; $output .= '
'; return $output; } /** * */ function forum_set_return() { global $CFG, $SESSION; if (! isset($SESSION->fromdiscussion)) { if (!empty($_SERVER['HTTP_REFERER'])) { $referer = $_SERVER['HTTP_REFERER']; } else { $referer = ""; } // If the referer is NOT a login screen then save it. if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) { $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"]; } } } /** * */ function forum_go_back_to($default) { global $SESSION; if (!empty($SESSION->fromdiscussion)) { $returnto = $SESSION->fromdiscussion; unset($SESSION->fromdiscussion); return $returnto; } else { return $default; } } /** * Creates a directory file name, suitable for make_upload_directory() */ function forum_file_area_name($post) { global $CFG; return "$post->course/$CFG->moddata/forum/$post->forum/$post->id"; } /** * */ function forum_file_area($post) { return make_upload_directory( forum_file_area_name($post) ); } /** * */ function forum_delete_old_attachments($post, $exception="") { /** * Deletes all the user files in the attachments area for a post * EXCEPT for any file named $exception */ if ($basedir = forum_file_area($post)) { if ($files = get_directory_list($basedir)) { foreach ($files as $file) { if ($file != $exception) { unlink("$basedir/$file"); notify("Existing file '$file' has been deleted!"); } } } if (!$exception) { // Delete directory as well, if empty rmdir("$basedir"); } } } /** * Given a discussion object that is being moved to forumid, * this function checks all posts in that discussion * for attachments, and if any are found, these are * moved to the new forum directory. */ function forum_move_attachments($discussion, $forumid) { global $CFG; require_once($CFG->dirroot.'/lib/uploadlib.php'); $return = true; if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) { foreach ($posts as $oldpost) { $oldpost->course = $discussion->course; $oldpost->forum = $discussion->forum; $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost); if (is_dir($oldpostdir)) { $newpost = $oldpost; $newpost->forum = $forumid; $newpostdir = forum_file_area_name($newpost); // take off the last directory because otherwise we're renaming to a directory that already exists // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too. make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/'))); $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost); $files = get_directory_list($oldpostdir); // get it before we rename it. if (! @rename($oldpostdir, $newpostdir)) { $return = false; } foreach ($files as $file) { clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file); } } } } return $return; } /** * if return=html, then return a html string. * if return=text, then return a text-only string. * otherwise, print HTML for non-images, and return image HTML */ function forum_print_attachments($post, $return=NULL) { global $CFG; $filearea = forum_file_area_name($post); $imagereturn = ""; $output = ""; if ($basedir = forum_file_area($post)) { if ($files = get_directory_list($basedir)) { $strattachment = get_string("attachment", "forum"); foreach ($files as $file) { $icon = mimeinfo("icon", $file); $type = mimeinfo("type", $file); if ($CFG->slasharguments) { $ffurl = "$CFG->wwwroot/file.php/$filearea/$file"; } else { $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file"; } $image = "pixpath/f/$icon\" class=\"icon\" alt=\"\" />"; if ($return == "html") { $output .= "$image "; $output .= "$file
"; } else if ($return == "text") { $output .= "$strattachment $file:\n$ffurl\n"; } else { if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links $imagereturn .= "
\"\""; } else { echo "$image "; echo filter_text("$file
"); } } } } } if ($return) { return $output; } return $imagereturn; } /** * If successful, this function returns the name of the file * @param $post is a full post record, including course and forum * @param $newfile is a full upload array from $_FILES * @param $message is a string to hold the messages. */ /** * */ function forum_add_attachment($post, $inputname,&$message) { global $CFG; if (!$forum = get_record("forum", "id", $post->forum)) { return ""; } if (!$course = get_record("course", "id", $forum->course)) { return ""; } require_once($CFG->dirroot.'/lib/uploadlib.php'); $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true); $dir = forum_file_area_name($post); if ($um->process_file_uploads($dir)) { $message .= $um->get_errors(); return $um->get_new_filename(); } $message .= $um->get_errors(); return null; } /** * */ function forum_add_new_post($post,&$message) { global $USER, $CFG; $post->created = $post->modified = time(); $post->mailed = "0"; $post->userid = $USER->id; $post->attachment = ""; if (! $post->id = insert_record("forum_posts", $post)) { return false; } if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) { set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); } // Update discussion modified date set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion); set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion); if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return $post->id; } /** * */ function forum_update_post($post,&$message) { global $USER, $CFG; $post->modified = time(); if (!$post->parent) { // Post is a discussion starter - update discussion title too set_field("forum_discussions", "name", $post->subject, "id", $post->discussion); } if ($newfilename = forum_add_attachment($post, 'attachment',$message)) { $post->attachment = $newfilename; } else { unset($post->attachment); } // Update discussion modified date set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion); set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion); if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return update_record("forum_posts", $post); } /** * Given an object containing all the necessary data, * create a new discussion and return the id */ function forum_add_discussion($discussion,&$message) { GLOBAL $USER, $CFG; $timenow = time(); // The first post is stored as a real post, and linked // to from the discuss entry. $post->discussion = 0; $post->parent = 0; $post->userid = $USER->id; $post->created = $timenow; $post->modified = $timenow; $post->mailed = 0; $post->subject = $discussion->name; $post->message = $discussion->intro; $post->attachment = ""; $post->forum = $discussion->forum; $post->course = $discussion->course; $post->format = $discussion->format; $post->mailnow = $discussion->mailnow; if (! $post->id = insert_record("forum_posts", $post) ) { return 0; } if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) { set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors } // Now do the main entry for the discussion, // linking to this first post $discussion->firstpost = $post->id; $discussion->timemodified = $timenow; $discussion->usermodified = $post->userid; $discussion->userid = $USER->id; if (! $post->discussion = insert_record("forum_discussions", $discussion) ) { delete_records("forum_posts", "id", $post->id); return 0; } // Finally, set the pointer on the post. if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) { delete_records("forum_posts", "id", $post->id); delete_records("forum_discussions", "id", $post->discussion); return 0; } if (forum_tp_can_track_forums($post->forum) && forum_tp_is_tracked($post->forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return $post->discussion; } /** * */ function forum_delete_discussion($discussion, $fulldelete=false) { // $discussion is a discussion record object $result = true; if ($posts = get_records("forum_posts", "discussion", $discussion->id)) { foreach ($posts as $post) { $post->course = $discussion->course; $post->forum = $discussion->forum; if (! delete_records("forum_ratings", "post", "$post->id")) { $result = false; } if (! forum_delete_post($post, $fulldelete)) { $result = false; } } } forum_tp_delete_read_records(-1, -1, $discussion->id); if (! delete_records("forum_discussions", "id", "$discussion->id")) { $result = false; } return $result; } /** * */ function forum_delete_post($post, $children=false) { if ($childposts = get_records('forum_posts', 'parent', $post->id)) { if ($children) { foreach ($childposts as $childpost) { forum_delete_post($childpost, true); } } else { return false; } } if (delete_records("forum_posts", "id", $post->id)) { delete_records("forum_ratings", "post", $post->id); // Just in case forum_tp_delete_read_records(-1, $post->id); if ($post->attachment) { $discussion = get_record("forum_discussions", "id", $post->discussion); $post->course = $discussion->course; $post->forum = $discussion->forum; forum_delete_old_attachments($post); } // Just in case we are deleting the last post forum_discussion_update_last_post($post->discussion); return true; } return false; } /** * */ function forum_count_replies($post, $children=true) { $count = 0; if ($children) { if ($childposts = get_records('forum_posts', 'parent', $post->id)) { foreach ($childposts as $childpost) { $count ++; // For this child $count += forum_count_replies($childpost, true); } } } else { $count += count_records('forum_posts', 'parent', $post->id); } return $count; } /** * */ function forum_forcesubscribe($forumid, $value=1) { return set_field("forum", "forcesubscribe", $value, "id", $forumid); } /** * */ function forum_is_forcesubscribed($forum) { if (isset($forum->forcesubscribe)) { // then we use that return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE); } else { // Check the database return (get_field('forum', 'forcesubscribe', 'id', $forum) == FORUM_FORCESUBSCRIBE); } } /** * */ function forum_is_subscribed($userid, $forumid) { if (forum_is_forcesubscribed($forumid)) { return true; } return record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid); } /** * Adds user to the subscriber list */ function forum_subscribe($userid, $forumid) { if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) { return true; } $sub->userid = $userid; $sub->forum = $forumid; return insert_record("forum_subscriptions", $sub); } /** * Removes user from the subscriber list */ function forum_unsubscribe($userid, $forumid) { return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid); } /** * Given a new post, subscribes or unsubscribes as appropriate. * Returns some text which describes what happened. */ function forum_post_subscription($post) { global $USER; $subscribed=forum_is_subscribed($USER->id, $post->forum); if ((isset($post->subscribe) && $post->subscribe && $subscribed) || (!$post->subscribe && !$subscribed)) { return ""; } if (!$forum = get_record("forum", "id", $post->forum)) { return ""; } $info->name = fullname($USER); $info->forum = $forum->name; if (!empty($post->subscribe)) { forum_subscribe($USER->id, $post->forum); return "

".get_string("nowsubscribed", "forum", $info)."

"; } forum_unsubscribe($USER->id, $post->forum); return "

".get_string("nownotsubscribed", "forum", $info)."

"; } /** * Generate and return the subscribe or unsubscribe link for a forum. * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe. * @param object $context the context object for this forum. * @param array $messages text used for the link in its various states * (subscribed, unsubscribed, forcesubscribed or cantsubscribe). * Any strings not passed in are taken from the $defaultmessages array * at the top of the function. * @param */ function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false) { global $CFG, $USER; $defaultmessages = array( 'subscribed' => get_string('unsubscribe', 'forum'), 'unsubscribed' => get_string('subscribe', 'forum'), 'cantaccessgroup' => get_string('no'), 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'), 'cantsubscribe' => get_string('disallowsubscribe','forum') ); $messages = $messages + $defaultmessages; if (forum_is_forcesubscribed($forum->id)) { return $messages['forcesubscribed']; } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) { return $messages['cantsubscribe']; } else if ($cantaccessagroup) { return $messages['cantaccessgroup']; } else { if (forum_is_subscribed($USER->id, $forum->id)) { $linktext = $messages['subscribed']; $linktitle = get_string('subscribestop', 'forum'); } else { $linktext = $messages['unsubscribed']; $linktitle = get_string('subscribestart', 'forum'); } $link = ''; // use