libdir.'/filelib.php'); require_once($CFG->libdir.'/eventslib.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 /////////////////////////////////////////////////////////// /** * Code to be executed when a module is installed * now is just used to register the module as message provider */ function forum_install() { $eventdata = new object(); $eventdata->modulename = 'forum'; $eventdata->modulefile = 'mod/forum/index.php'; events_trigger('message_provider_register', $eventdata); return true; } /** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) 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, $DB; $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 = $DB->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 $users = get_users_by_capability(get_context_instance(CONTEXT_COURSE, $forum->course), 'mod/forum:initialsubscriptions', 'u.id', '', '','','',null, false); foreach ($users as $user) { forum_subscribe($user->id, $forum->id); } } forum_grade_item_update($forum); return $forum->id; } /** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) 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 $DB; $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 = $DB->get_record('forum', array('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 = $DB->get_record('forum_discussions', array('forum'=>$forum->id))) { if ($discussions = $DB->get_records('forum_discussions', array('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 = $DB->get_record('forum_posts', array('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 (! $DB->update_record('forum_posts', ($post))) { error('Could not update the first post'); } $discussion->name = $forum->name; if (! $DB->update_record('forum_discussions', ($discussion))) { error('Could not update the discussion'); } } if (!$DB->update_record('forum', $forum)) { error('Can not update 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) { global $DB; if (!$forum = $DB->get_record('forum', array('id'=>$id))) { return false; } $result = true; if ($discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id))) { foreach ($discussions as $discussion) { if (!forum_delete_discussion($discussion, true)) { $result = false; } } } if (!$DB->delete_records('forum_subscriptions', array('forum'=>$forum->id))) { $result = false; } forum_tp_delete_read_records(-1, -1, -1, $forum->id); if (!$DB->delete_records('forum', array('id'=>$forum->id))) { $result = false; } forum_grade_item_delete($forum); return $result; } /** * Indicates API features that the forum supports. * * @param string $feature * @return mixed True if yes (some features may use other values) */ function forum_supports($feature) { switch($feature) { case FEATURE_COMPLETION_TRACKS_VIEWS: return true; case FEATURE_COMPLETION_HAS_RULES: return true; default: return null; } } /** * Obtains the automatic completion state for this forum based on any conditions * in forum settings. * * @param object $course Course * @param object $cm Course-module * @param int $userid User ID * @param bool $type Type of comparison (or/and; can be used as return value if no conditions) * @return bool True if completed, false if not. (If no conditions, then return * value depends on comparison type) */ function forum_get_completion_state($course,$cm,$userid,$type) { global $CFG,$DB; // Get forum details if(!($forum=$DB->get_record('forum',array('id'=>$cm->instance)))) { throw new Exception("Can't find forum {$cm->instance}"); } $result=$type; // Default return value $postcountparams=array('userid'=>$userid,'forumid'=>$forum->id); $postcountsql=" SELECT COUNT(1) FROM {forum_posts} fp INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id WHERE fp.userid=:userid AND fd.forum=:forumid"; if($forum->completiondiscussions) { $value = $forum->completiondiscussions <= $DB->count_records('forum_discussions',array('forum'=>$forum->id,'userid'=>$userid)); if($type==COMPLETION_AND) { $result=$result && $value; } else { $result=$result || $value; } } if($forum->completionreplies) { $value = $forum->completionreplies <= $DB->get_field_sql( $postcountsql.' AND fp.parent<>0',$postcountparams); if($type==COMPLETION_AND) { $result=$result && $value; } else { $result=$result || $value; } } if($forum->completionposts) { $value = $forum->completionposts <= $DB->get_field_sql($postcountsql,$postcountparams); if($type==COMPLETION_AND) { $result=$result && $value; } else { $result=$result || $value; } } 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, $DB; $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 = $DB->get_record('forum_discussions', array('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 = $DB->get_record('forum', array('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 = $DB->get_record('course', array('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, false)) { 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; } // 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 = $DB->get_record('user', array('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 (!$DB->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)) { */ $eventdata = new object(); $eventdata->modulename = 'forum'; $eventdata->userfrom = $userfrom; $eventdata->userto = $userto; $eventdata->subject = $postsubject; $eventdata->fullmessage = $posttext; $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = $posthtml; $eventdata->smallmessage = ''; if ( events_trigger('message_send', $eventdata) > 0){ 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]) { $DB->set_field("forum_posts", "mailed", "2", array("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); $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago)); mtrace ('Cleaned old digest records'); if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) { mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone)); $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime)); if ($digestposts_rs->valid()) { // We have work to do $usermailcount = 0; //caches - reuse the those filled before too $discussionposts = array(); $userdiscussions = array(); foreach ($digestposts_rs as $digestpost) { if (!isset($users[$digestpost->userid])) { if ($user = $DB->get_record('user', array('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 = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) { $posts[$digestpost->postid] = $post; } else { continue; } } $discussionid = $digestpost->discussionid; if (!isset($discussions[$discussionid])) { if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) { $discussions[$discussionid] = $discussion; } else { continue; } } $forumid = $discussions[$discussionid]->forum; if (!isset($forums[$forumid])) { if ($forum = $DB->get_record('forum', array('id' => $forumid))) { $forums[$forumid] = $forum; } else { continue; } } $courseid = $forums[$forumid]->course; if (!isset($courses[$courseid])) { if ($course = $DB->get_record('course', array('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; } $digestposts_rs->close(); /// 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 $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $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.'&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); // 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 .= "

". "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); 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 = $DB->get_record('user', array('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 = "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($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
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)) { */ $eventdata = new object(); $eventdata->modulename = 'forum'; $eventdata->userfrom = $site->shortname; $eventdata->userto = $userto; $eventdata->subject = $postsubject; $eventdata->fullmessage = $posttext; $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = $posthtml; $eventdata->smallmessage = ''; if ( events_trigger('message_send', $eventdata) > 0){ 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 = ''; 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($course, $forum, $discussion, $post, $userfrom, $userto, 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 ($count = forum_count_user_posts($forum->id, $user->id)) { if ($count->postcount > 0) { $result = new object(); $result->info = get_string("numposts", "forum", $count->postcount); $result->time = $count->lastpost; return $result; } } return NULL; } /** * */ function forum_user_complete($course, $user, $mod, $forum) { global $CFG; 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); foreach ($posts as $post) { if (!isset($discussions[$forum->discussion])) { continue; } $discussion = $discussions[$forum->discussion]; forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false, false); } } else { echo "

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

"; } } /** * */ function forum_print_overview($courses,&$htmlarray) { global $USER, $CFG; $LIKE = $DB->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!) $params = array(); $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {log} l " ." JOIN {course_modules} cm ON cm.id = cmid " ." WHERE ("; foreach ($courses as $course) { $sql .= '(l.course = ? AND l.time > ?) OR '; $params[] = $course->id; $params[] = $course->lastaccess; } $sql = substr($sql,0,-3); // take off the last OR $sql .= ") AND l.module = 'forum' AND action $LIKE 'add post%' " ." AND userid != ? GROUP BY cmid,l.course,instance"; $params[] = $USER->id; if (!$new = $DB->get_records_sql($sql, $params)) { $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 {forum_posts} p '. ' JOIN {forum_discussions} d ON p.discussion = d.id '. ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE ('; $params = array($USER->id); foreach ($trackingforums as $track) { $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR '; $params[] = $track->id; $params[] = get_current_group($track->course); } $sql = substr($sql,0,-3); // take off the last OR $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course'; $params[] = $cutoffdate; if (!$unread = $DB->get_records_sql($sql, $params)) { $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 * @param object $course * @param bool $viewfullnames capability * @param int $timestart * @return bool success */ function forum_print_recent_activity($course, $viewfullnames, $timestart) { global $CFG, $USER, $DB; // do not use log table if possible, it may be huge and is expensive to join with other tables if (!$posts = $DB->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 {forum_posts} p JOIN {forum_discussions} d ON d.id = p.discussion JOIN {forum} f ON f.id = d.forum JOIN {user} u ON u.id = p.userid WHERE p.created > ? AND f.course = ? ORDER BY p.id ASC", array($timestart, $course->id))) { // 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\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, $DB; $params= array(); if ($userid) { $params[] = $userid; $user = "AND u.id = ?"; } else { $user = ""; } $params[] = $forum->id; $aggtype = $forum->assessed; switch ($aggtype) { case FORUM_AGGREGATE_COUNT : $sql = "SELECT u.id, u.id AS userid, COUNT(fr.rating) AS rawgrade FROM {user} u, {forum_posts} fp, {forum_ratings} fr, {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 = ? $user GROUP BY u.id"; break; case FORUM_AGGREGATE_MAX : $sql = "SELECT u.id, u.id AS userid, MAX(fr.rating) AS rawgrade FROM {user} u, {forum_posts} fp, {forum_ratings} fr, {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 = ? $user GROUP BY u.id"; break; case FORUM_AGGREGATE_MIN : $sql = "SELECT u.id, u.id AS userid, MIN(fr.rating) AS rawgrade FROM {user} u, {forum_posts} fp, {forum_ratings} fr, {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 = ? $user GROUP BY u.id"; break; case FORUM_AGGREGATE_SUM : $sql = "SELECT u.id, u.id AS userid, SUM(fr.rating) AS rawgrade FROM {user} u, {forum_posts} fp, {forum_ratings} fr, {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 = ? $user GROUP BY u.id"; break; default : //avg $sql = "SELECT u.id, u.id AS userid, AVG(fr.rating) AS rawgrade FROM {user} u, {forum_posts} fp, {forum_ratings} fr, {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 = ? $user GROUP BY u.id"; break; } if ($results = $DB->get_records_sql($sql, $params)) { // 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 = $DB->get_record('scale', array('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, $DB; 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 {forum} f, {course_modules} cm, {modules} m WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id"; if ($rs = $DB->get_recordset_sql($sql)) { foreach ($rs as $forum) { if ($forum->assessed) { forum_update_grades($forum, 0, false); } else { forum_grade_item_update($forum); } } $rs->close(); } } } /** * 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, $DB; //Get students from forum_subscriptions $st_subscriptions = $DB->get_records_sql("SELECT DISTINCT u.id, u.id FROM {user} u, {forum_subscriptions} s WHERE s.forum = ? AND u.id = s.userid", array($forumid)); //Get students from forum_posts $st_posts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id FROM {user} u, {forum_discussions} d, {forum_posts} p WHERE d.forum = ? AND p.discussion = d.id AND u.id = p.userid", array($forumid)); //Get students from forum_ratings $st_ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id FROM {user} u, {forum_discussions} d, {forum_posts} p, {forum_ratings} r WHERE d.forum = ? AND p.discussion = d.id AND r.post = p.id AND u.id = r.userid", array($forumid)); //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) { global $DB; $return = false; $rec = $DB->get_record("forum",array("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) { global $DB; if ($scaleid and $DB->record_exists('forum', array('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, $DB; return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt FROM {forum_posts} p JOIN {forum_discussions} d ON p.discussion = d.id LEFT JOIN {user} u ON p.userid = u.id WHERE p.id = ?", array($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, $DB; return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt FROM {forum_posts} p LEFT JOIN {user} u ON p.userid = u.id WHERE p.discussion = ? AND p.parent > 0 $sort", array($discussion)); } /** * 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, $DB, $USER; $tr_sel = ""; $tr_join = ""; $params = array(); if ($tracking) { $now = time(); $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600); $tr_sel = ", fr.id AS postread"; $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)"; $params[] = $USER->id; } $params[] = $discussionid; if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel FROM {forum_posts} p LEFT JOIN {user} u ON p.userid = u.id $tr_join WHERE p.discussion = ? ORDER BY $sort", $params)) { 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, $DB; return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt FROM {forum_posts} p LEFT JOIN {user} u ON p.userid = u.id WHERE p.parent = ? ORDER BY p.created ASC", array($parent)); } /** * 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, $DB, $USER; require_once($CFG->dirroot.'/course/lib.php'); if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) { error('The forum module is not installed'); } if ($courseid) { $courses = $DB->get_records('course', array('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 = $DB->get_records('course', array('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 = $DB->get_records('forum', array('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]; 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, $DB, $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(); $params = array(); foreach ($forums as $forumid => $forum) { $select = array(); if (!$forum->viewhiddentimedposts) { $select[] = "(d.userid = :userid OR (d.timestart < : AND (d.timeend = 0 OR d.timeend > :timeend)))"; $params = array('userid'=>$USER->id, 'timestart'=>$now, 'timeend'=>$now); } if ($forum->type == 'qanda') { if (!empty($forum->onlydiscussions)) { list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda0'); $params = array_merge($params, $discussionid_params); $select[] = "(d.id $discussionid_sql OR p.parent = 0)"; } else { $select[] = "p.parent = 0"; } } if (!empty($forum->onlygroups)) { list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps0'); $params = array_merge($params, $groupid_params); $select[] = "d.groupid $groupid_sql"; } if ($select) { $selects = implode(" AND ", $select); $where[] = "(d.forum = :forum AND $selects)"; $params['forum'] = $forumid; } else { $fullaccess[] = $forumid; } } if ($fullaccess) { list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula0'); $params = array_merge($params, $fullid_params); $where[] = "(d.forum $fullid_sql)"; } $selectdiscussion = "(".implode(" OR ", $where).")"; $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)) { list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject', 'p.userid', 'u.id', 'u.firstname', 'u.lastname', 'p.modified', 'd.forum'); } else { list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject', 'p.userid', 'u.id', 'u.firstname', 'u.lastname', 'p.modified', 'd.forum'); } $params = array_merge($params, $msparams); } $fromsql = "{forum_posts} p, {forum_discussions} d, {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 = $DB->count_records_sql($countsql, $params); return $DB->get_records_sql($searchsql, $params, $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, $DB; return $DB->get_records_sql("SELECT r.id, r.userid, p.id AS postid, r.rating FROM {forum_ratings} r, {forum_posts} p WHERE r.post = p.id AND p.discussion = ? ORDER BY p.id ASC", array($discussion->id)); } /** * 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, $DB; return $DB->get_records_sql("SELECT u.*, r.rating, r.time FROM {forum_ratings} r, {user} u WHERE r.post = ? AND r.userid = u.id ORDER BY $sort", array($postid)); } /** * 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, $DB; $params = array($starttime, $endtime); if (!empty($CFG->forum_enabletimedposts)) { if (empty($now)) { $now = time(); } $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; } else { $timedsql = ""; } return $DB->get_records_sql("SELECT p.*, d.course, d.forum FROM {forum_posts} p JOIN {forum_discussions} d ON d.id = p.discussion WHERE p.mailed = 0 AND p.created >= ? AND (p.created < ? OR p.mailnow = 1) $timedsql ORDER BY p.modified ASC", $params); } /** * Marks posts before a certain time as being mailed already */ function forum_mark_old_posts_as_mailed($endtime, $now=null) { global $CFG, $DB; if (empty($now)) { $now = time(); } if (empty($CFG->forum_enabletimedposts)) { return $DB->execute("UPDATE {forum_posts} SET mailed = '1' WHERE (created < ? OR mailnow = 1) AND mailed = 0", false, array($endtime)); } else { return $DB->execute("UPDATE {forum_posts} SET mailed = '1' WHERE discussion NOT IN (SELECT d.id FROM {forum_discussions} d WHERE d.timestart > ?) AND (created < ? OR mailnow = 1) AND mailed = 0", false, array($now, $endtime)); } } /** * Get all the posts for a user in a forum suitable for forum_print_post */ function forum_get_user_posts($forumid, $userid) { global $CFG, $DB; $timedsql = ""; $params = array($forumid, $userid); 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 < ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; } } return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt FROM {forum} f JOIN {forum_discussions} d ON d.forum = f.id JOIN {forum_posts} p ON p.discussion = d.id JOIN {user} u ON u.id = p.userid WHERE f.id = ? AND p.userid = ? $timedsql ORDER BY p.modified ASC", $params); } /** * 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, $DB; $timedsql = ""; $params = array($forumid, $userid); 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 < ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; } } return $DB->get_records_sql("SELECT DISTINCT d.* FROM {forum} f JOIN {forum_discussions} d ON d.forum = f.id JOIN {forum_posts} p ON p.discussion = d.id WHERE f.id = ? AND p.userid = ? $timedsql", $params); } /** * 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, $DB; $timedsql = ""; $params = array($forumid, $userid); 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 < ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; } } return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost FROM {forum} f JOIN {forum_discussions} d ON d.forum = f.id JOIN {forum_posts} p ON p.discussion = d.id JOIN {user} u ON u.id = p.userid WHERE f.id = ? AND p.userid = ? $timedsql", $params); } /** * Given a log entry, return the forum post details for it. */ function forum_get_post_from_log($log) { global $CFG, $DB; if ($log->action == "add post") { return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, u.firstname, u.lastname, u.email, u.picture FROM {forum_discussions} d, {forum_posts} p, {forum} f, {user} u WHERE p.id = ? AND d.id = p.discussion AND p.userid = u.id AND u.deleted <> '1' AND f.id = d.forum", array($log->info)); } else if ($log->action == "add discussion") { return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, u.firstname, u.lastname, u.email, u.picture FROM {forum_discussions} d, {forum_posts} p, {forum} f, {user} u WHERE d.id = ? AND d.firstpost = p.id AND p.userid = u.id AND u.deleted <> '1' AND f.id = d.forum", array($log->info)); } return NULL; } /** * Given a discussion id, return the first post from the discussion */ function forum_get_firstpost_from_discussion($discussionid) { global $CFG, $DB; return $DB->get_record_sql("SELECT p.* FROM {forum_discussions} d, {forum_posts} p WHERE d.id = ? AND d.firstpost = p.id ", array($discussionid)); } /** * 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, $DB; 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 {forum_posts} p JOIN {forum_discussions} d ON p.discussion = d.id WHERE p.parent > 0 AND d.forum = ? GROUP BY p.discussion"; return $DB->get_records_sql($sql, array($forumid)); } else { $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid FROM {forum_posts} p JOIN {forum_discussions} d ON p.discussion = d.id WHERE d.forum = ? GROUP BY p.discussion $groupby $orderby"; return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum); } } function forum_count_discussions($forum, $cm, $course) { global $CFG, $DB, $USER; static $cache = array(); $now = round(time(), -2); // db cache friendliness $params = array($course->id); if (!isset($cache[$course->id])) { if (!empty($CFG->forum_enabletimedposts)) { $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)"; $params[] = $now; $params[] = $now; } else { $timedsql = ""; } $sql = "SELECT f.id, COUNT(d.id) as dcount FROM {forum} f JOIN {forum_discussions} d ON d.forum = f.id WHERE f.course = ? $timedsql GROUP BY f.id"; if ($counts = $DB->get_records_sql($sql, $params)) { 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; } list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups); $params[] = $forum->id; if (!empty($CFG->forum_enabletimedposts)) { $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)"; $params[] = $now; $params[] = $now; } else { $timedsql = ""; } $sql = "SELECT COUNT(d.id) FROM {forum_discussions} d WHERE d.groupid IN ($mygroups) AND d.forum = ? $timedsql"; return $DB->get_field_sql($sql, $params); } /** * How many unrated posts are in the given discussion for a given user? */ function forum_count_unrated_posts($discussionid, $userid) { global $CFG, $DB; if ($posts = $DB->get_record_sql("SELECT count(*) as num FROM {forum_posts} WHERE parent > 0 AND discussion = ? AND userid <> ? ", array($discussionid, $userid))) { if ($rated = $DB->get_record_sql("SELECT count(*) as num FROM {forum_posts} p, {forum_ratings} r WHERE p.discussion = ? AND p.id = r.post AND r.userid = ?", array($discussionid, $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, $DB, $USER; $timelimit = ''; $modcontext = null; $now = round(time(), -2); $params = array($cm->instance); if (!empty($CFG->forum_enabletimedposts)) { $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) { $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; if (isloggedin()) { $timelimit .= " OR d.userid = ?"; $params[] = $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 = ? OR d.groupid = -1)"; $params[] = $currentgroup; } else { $groupselect = ""; } } else { //seprate groups without access all if ($currentgroup) { $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; $params[] = $currentgroup; } 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 {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 {forum_discussions} d JOIN {forum_posts} p ON p.discussion = d.id JOIN {user} u ON p.userid = u.id $umtable WHERE d.forum = ? AND p.parent = 0 $timelimit $groupselect ORDER BY $forumsort"; return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); } function forum_get_discussions_unread($cm) { global $CFG, $DB, $USER; $now = round(time(), -2); $params = array($cutoffdate); $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 = ? OR d.groupid = -1)"; $params[] = $currentgroup; } else { $groupselect = ""; } } else { //seprate groups without access all if ($currentgroup) { $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; $params[] = $currentgroup; } 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 < ? AND (d.timeend = 0 OR d.timeend > ?)"; $params[] = $now; $params[] = $now; } else { $timedsql = ""; } $sql = "SELECT d.id, COUNT(p.id) AS unread FROM {forum_discussions} d JOIN {forum_posts} p ON p.discussion = d.id LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id) WHERE d.forum = {$cm->instance} AND p.modified >= ? AND r.id is NULL $groupselect $timedsql GROUP BY d.id"; if ($unreads = $DB->get_records_sql($sql, $params)) { foreach ($unreads as $unread) { $unreads[$unread->id] = $unread->unread; } return $unreads; } else { return array(); } } function forum_get_discussions_count($cm) { global $CFG, $DB, $USER; $now = round(time(), -2); $params = array($cm->instance); $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 = ? OR d.groupid = -1)"; $params[] = $currentgroup; } else { $groupselect = ""; } } else { //seprate groups without access all if ($currentgroup) { $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; $params[] = $currentgroup; } 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 <= ? AND (d.timeend = 0 OR d.timeend > ?))"; $params[] = $now; $params[] = $now; if (isloggedin()) { $timelimit .= " OR d.userid = ?"; $params[] = $USER->id; } $timelimit .= ")"; } } $sql = "SELECT COUNT(d.id) FROM {forum_discussions} d JOIN {forum_posts} p ON p.discussion = d.id WHERE d.forum = ? AND p.parent = 0 $groupselect $timelimit"; return $DB->get_field_sql($sql, $params); } /** * 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, $DB; $params = array($courseid, $userid); if ($groupid) { $groupselect = " AND d.groupid = ? "; $params[] = $groupid; } else { $groupselect = ""; } return $DB->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 {forum_discussions} d, {forum_posts} p, {user} u, {forum} f WHERE d.course = ? AND p.discussion = d.id AND p.parent = 0 AND p.userid = u.id AND u.id = ? AND d.forum = f.id $groupselect ORDER BY p.created DESC", $params); } /** * Returns list of user objects that are subscribed to this forum */ function forum_subscribed_users($course, $forum, $groupid=0) { global $CFG, $DB; $params = array($forum->id); if ($groupid) { $grouptables = ", {groups_members} gm "; $groupselect = "AND gm.groupid = ? AND u.id = gm.userid"; $params[] = $groupid; } else { $grouptables = ''; $groupselect = ''; } $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"; if (forum_is_forcesubscribed($forum)) { $context = get_context_instance(CONTEXT_COURSE, $course->id); $sort = "u.email ASC"; $results = get_users_by_capability($context, 'mod/forum:initialsubscriptions', $fields, $sort, '','','','', false, true); } else { $results = $DB->get_records_sql("SELECT $fields FROM {user} u, {forum_subscriptions} s $grouptables WHERE s.forum = ? AND s.userid = u.id AND u.deleted = 0 $groupselect ORDER BY u.email ASC", $params); } 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, $DB; if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $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 = get_string("namenews", "forum"); $forum->intro = 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 = get_string("namesocial", "forum"); $forum->intro = 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 = $DB->insert_record("forum", $forum); if (! $module = $DB->get_record("modules", array("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 (! $DB->set_field("course_modules", "section", $sectionid, array("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 $DB->get_record("forum", array("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); $output = ''; $output .= ''; if ($post->parent) { $output .= ''; $output .= '
'; $output .= print_user_picture($userfrom, $course->id, $userfrom->picture, false, true); $output .= ''; } else { $output .= ''; } $output .= '
'.format_string($post->subject).'
'; $fullname = fullname($userfrom, $viewfullnames); $by = new object(); $by->name = ''.$fullname.''; $by->date = userdate($post->modified, '', $userto->timezone); $output .= '
'.get_string('bynameondate', 'forum', $by).'
'; $output .= '
'; if (isset($userfrom->groups)) { $groups = $userfrom->groups[$forum->id]; } else { if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) { error('Course Module ID was incorrect'); } $group = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); } if ($groups) { $output .= print_group_picture($groups, $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, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false, $ratings=NULL, $footer="", $highlight="", $post_read=null, $dummyifcantsee=true, $istracked=null) { global $USER, $CFG; static $stredit, $strdelete, $strreply, $strparent, $strprune; static $strpruneheading, $displaymode; static $strmarkread, $strmarkunread; $post->course = $course->id; $post->forum = $forum->id; // caching if (!isset($cm->cache)) { $cm->cache = new object(); } if (!isset($cm->cache->caps)) { $cm->cache->caps = array(); $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); $cm->cache->caps['mod/forum:viewdiscussion'] = has_capability('mod/forum:viewdiscussion', $modcontext); $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modcontext); $cm->cache->caps['mod/forum:editanypost'] = has_capability('mod/forum:editanypost', $modcontext); $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext); $cm->cache->caps['mod/forum:deleteownpost'] = has_capability('mod/forum:deleteownpost', $modcontext); $cm->cache->caps['mod/forum:deleteanypost'] = has_capability('mod/forum:deleteanypost', $modcontext); $cm->cache->caps['mod/forum:viewanyrating'] = has_capability('mod/forum:viewanyrating', $modcontext); } if (!isset($cm->uservisible)) { $cm->uservisible = coursemodule_visible_for_user($cm); } if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 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'); } $read_style = ''; // ignore trackign status if not tracked or tracked param missing if ($istracked) { if (is_null($post_read)) { debugging('fetching post_read info'); $post_read = forum_tp_is_post_read($USER->id, $post); } if ($post_read) { $read_style = ' read'; } else { $read_style = ' unread'; echo ''; } } echo ''; echo ''; // Picture $postuser = new object(); $postuser->id = $post->userid; $postuser->firstname = $post->firstname; $postuser->lastname = $post->lastname; $postuser->imagealt = $post->imagealt; $postuser->picture = $post->picture; echo ''; if ($post->parent) { echo ''; echo '
'; print_user_picture($postuser, $course->id); echo ''; } else { echo ''; } if (!empty($post->subjectnoformat)) { echo '
'.$post->subject.'
'; } else { echo '
'.format_string($post->subject).'
'; } echo '
'; $fullname = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']); $by = new object(); $by->name = ''.$fullname.''; $by->date = userdate($post->modified); print_string('bynameondate', 'forum', $by); echo '
'; if (isset($cm->cache->usersgroups)) { $groups = array(); if (isset($cm->cache->usersgroups[$post->userid])) { foreach ($cm->cache->usersgroups[$post->userid] as $gid) { $groups[$gid] = $cm->cache->groups[$gid]; } } } else { $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid); } if ($groups) { print_group_picture($groups, $course->id, false, false, true); } else { echo ' '; } // Actual content echo ''."\n"; if ($post->attachment) { 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, $course->id); $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, $course->id)); } else { echo format_text($post->message, $post->format, $options, $course->id); } 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 and isloggedin()) { 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.''; } } $age = time() - $post->created; // Hack for allow to edit news posts those are not displayed yet until they are displayed if (!$post->parent and $forum->type == 'news' and $discussion->timestart > time()) { $age = 0; } $editanypost = $cm->cache->caps['mod/forum:editanypost']; if ($ownpost or $editanypost) { if (($age < $CFG->maxeditingtime) or $editanypost) { $commands[] = ''.$stredit.''; } } if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') { $commands[] = ''.$strprune.''; } if (($ownpost and $age < $CFG->maxeditingtime and $cm->cache->caps['mod/forum:deleteownpost']) or $cm->cache->caps['mod/forum:deleteanypost']) { $commands[] = ''.$strdelete.''; } if ($reply) { $commands[] = ''.$strreply.''; } if (true) { // @todo penny replace this later with a capability check $p = array( 'postid' => $post->id, ); //$commands[] = portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); } echo '
'; echo implode(' | ', $commands); echo '
'; // Ratings $ratingsmenuused = false; if (!empty($ratings) and isloggedin()) { 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 = $cm->cache->caps['mod/forum:viewanyrating']; if (isset($cm->cache->ratings)) { if (isset($cm->cache->ratings[$post->id])) { $allratings = $cm->cache->ratings[$post->id]; } else { $allratings = array(); // no reatings present yet } } else { $allratings = NULL; // not preloaded } if (isset($cm->cache->myratings)) { if (isset($cm->cache->myratings[$post->id])) { $myrating = $cm->cache->myratings[$post->id]; } else { $myrating = FORUM_UNSET_POST_RATING; // no reatings present yet } } else { $myrating = NULL; // not preloaded } if ($canviewallratings and !$mypost) { forum_print_ratings($post->id, $ratings->scale, $forum->assessed, $canviewallratings, $allratings); if (!empty($ratings->allow)) { echo ' '; forum_print_rating_menu($post->id, $USER->id, $ratings->scale, $myrating); $ratingsmenuused = true; } } else if ($mypost) { forum_print_ratings($post->id, $ratings->scale, $forum->assessed, true, $allratings); } else if (!empty($ratings->allow) ) { forum_print_rating_menu($post->id, $USER->id, $ratings->scale, $myrating); $ratingsmenuused = true; } } echo '
'; } // Link to post if required if ($link) { echo ''; } if ($footer) { echo ''; } echo '
'."\n\n"; if ($istracked && !$CFG->forum_usermarksread && !$post_read) { forum_tp_mark_post_read($USER->id, $post, $forum->id); } 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, $modcontext=NULL) { global $USER, $CFG; static $rowcount; static $strmarkalldread; if (empty($modcontext)) { 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 $postuser = new object; $postuser->id = $post->userid; $postuser->firstname = $post->firstname; $postuser->lastname = $post->lastname; $postuser->imagealt = $post->imagealt; $postuser->picture = $post->picture; echo ''; print_user_picture($postuser, $forum->course); 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 = new object(); $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. * Forumid prevents the double lookup of the forumid in discussion to determine the aggregate type * Scale is an array of ratings */ function forum_print_ratings($postid, $scale, $aggregatetype, $link=true, $ratings=null) { $strratings = ''; switch ($aggregatetype) { case FORUM_AGGREGATE_AVG : $agg = forum_get_ratings_mean($postid, $scale, $ratings); $strratings = get_string("aggregateavg", "forum"); break; case FORUM_AGGREGATE_COUNT : $agg = forum_get_ratings_count($postid, $scale, $ratings); $strratings = get_string("aggregatecount", "forum"); break; case FORUM_AGGREGATE_MAX : $agg = forum_get_ratings_max($postid, $scale, $ratings); $strratings = get_string("aggregatemax", "forum"); break; case FORUM_AGGREGATE_MIN : $agg = forum_get_ratings_min($postid, $scale, $ratings); $strratings = get_string("aggregatemin", "forum"); break; case FORUM_AGGREGATE_SUM : $agg = forum_get_ratings_sum($postid, $scale, $ratings); $strratings = get_string("aggregatesum", "forum"); break; } if ($agg !== "") { if (empty($strratings)) { $strratings = get_string("ratings", "forum"); } echo "$strratings: "; if ($link) { link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $agg, 400, 600); } else { echo "$agg "; } } } /** * 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) * Forumid is the forum id field needed - passing it avoids a double query of lookup up the discusion and then the forum id to get the aggregate type */ function forum_get_ratings_mean($postid, $scale, $ratings=NULL) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("post" => $postid))) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); if ($count == 0 ) { return ""; } else if ($count == 1) { $rating = reset($ratings); return $scale[$rating]; } 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 the count of the ratings of a post given to the current user by others. * Scale is an array of possible ratings in the scale - the end of the scale is the highest or max grade * Ratings is an optional simple array of actual ratings (just integers) */ function forum_get_ratings_count($postid, $scale, $ratings=NULL) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("post" => $postid))) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); $scalecount = count($scale)-1; //this should give us the last element of the scale aka the max grade with $scale[$scalecount] if ($count > $scale[$scalecount]) { //if the count exceeds the forum scale (i.e. max grade then set the score to the max grade $count = $scale[$scalecount]; } return $scale[$count]; } /** * Return the max 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_max($postid, $scale, $ratings=NULL) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("post" => $postid))) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); $max = max($ratings); if ($count == 0 ) { return ""; } else if ($count == 1) { //this works for max $rating = reset($ratings); return $scale[$rating]; } else { if (isset($scale[$max])) { return $scale[$max]." ($count)"; } else { return "$max ($count)"; // Should never happen, hopefully } } } /** * Return the min 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_min($postid, $scale, $ratings=NULL) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("post" => $postid))) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); $min = min($ratings); if ($count == 0 ) { return ""; } else if ($count == 1) { $rating = reset($ratings); return $scale[$rating]; //this works for min } else { if (isset($scale[$min])) { return $scale[$min]." ($count)"; } else { return "$min ($count)"; // Should never happen, hopefully } } } /** * Return the sum or total of ratings 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_sum($postid, $scale, $ratings=NULL) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("post" => $postid))) { foreach ($rates as $rate) { $ratings[] = $rate->rating; } } } $count = count($ratings); $scalecount = count($scale)-1; //this should give us the last element of the scale aka the max grade with $scale[$scalecount] if ($count == 0 ) { return ""; } else if ($count == 1) { //this works for max. $rating = reset($ratings); return $scale[$rating]; } else { $total = 0; foreach ($ratings as $rating) { $total += $rating; } if ($total > $scale[$scalecount]) { //if the total exceeds the max grade then set it to the max grade $total = $scale[$scalecount]; } if (isset($scale[$total])) { return $scale[$total]." ($count)"; } else { return "$total ($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) { global $DB; if (is_null($ratings)) { $ratings = array(); if ($rates = $DB->get_records("forum_ratings", array("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, $myrating=NULL) { static $strrate; global $DB; if (is_null($myrating)) { if (!$rating = $DB->get_record("forum_ratings", array("userid" => $userid, "post" => $postid))) { $myrating = FORUM_UNSET_POST_RATING; } else { $myrating = $rating->rating; } } if (empty($strrate)) { $strrate = get_string("rate", "forum"); } $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale; choose_from_menu($scale, $postid, $myrating, ''); } /** * 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='') { if ($forumtype == 'single') { echo '
'; popup_form("view.php?f=$id&mode=", forum_get_layout_modes(), "mode", $mode, ""); echo '
'; } else { popup_form("discuss.php?d=$id&mode=", forum_get_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, $DB; if (!isset($post->forum) or !isset($post->course)) { debugging('missing forum or course', DEBUG_DEVELOPER); if (!$discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion))) { return false; } if (!$forum = $DB->get_record('forum', array('id' => $discussion->forum))) { return false; } $forumid = $forum->id; $courseid = $forum->course; } else { $forumid = $post->forum; $courseid = $post->course; } return "$courseid/$CFG->moddata/forum/$forumid/$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, $DB; require_once($CFG->dirroot.'/lib/uploadlib.php'); $return = true; if ($posts = $DB->get_records_select("forum_posts", "discussion = ? AND attachment <> ''", array($discussion->id))) { 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); $ffurl = get_file_url("$filearea/$file"); $image = "pixpath/f/$icon\" class=\"icon\" alt=\"\" />"; if ($return == "html") { $output .= "$image "; $output .= "$file"; if (true) { // 'todo penny replace this with a capability check require_once($CFG->libdir . '/portfoliolib.php'); $p = array( 'postid' => $post->id, 'attachment' => 1, ); $output .= portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false, true); } $output .= "
"; } 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 (true) { // 'todo penny replace this with a capability check require_once($CFG->libdir . '/portfoliolib.php'); $p = array( 'postid' => $post->id, 'attachment' => 1, ); portfolio_add_button('forum_portfolio_caller', $p, '/mod/forum/lib.php', false); } echo '
'; } } } } } 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, $DB; if (!$forum = $DB->get_record("forum", array("id"=>$post->forum))) { return ""; } if (!$course = $DB->get_record("course", array("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, $DB; $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion)); $forum = $DB->get_record('forum', array('id' => $discussion->forum)); $post->created = $post->modified = time(); $post->mailed = "0"; $post->userid = $USER->id; $post->attachment = ""; $post->forum = $forum->id; // speedup $post->course = $forum->course; // speedup if (! $post->id = $DB->insert_record("forum_posts", $post)) { return false; } if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) { $DB->set_field("forum_posts", "attachment", $post->attachment, array("id" => $post->id)); } // Update discussion modified date $DB->set_field("forum_discussions", "timemodified", $post->modified, array("id" => $post->discussion)); $DB->set_field("forum_discussions", "usermodified", $post->userid, array("id" => $post->discussion)); if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return $post->id; } /** * */ function forum_update_post($post,&$message) { global $USER, $CFG, $DB; $forum = $DB->get_record('forum', array('id' => $post->forum)); $post->modified = time(); $updatediscussion = new object(); $updatediscussion->id = $post->discussion; $updatediscussion->timemodified = $post->modified; // last modified tracking $updatediscussion->usermodified = $post->userid; // last modified tracking if (!$post->parent) { // Post is a discussion starter - update discussion title and times too $updatediscussion->name = $post->subject; $updatediscussion->timestart = $post->timestart; $updatediscussion->timeend = $post->timeend; } if (!$DB->update_record('forum_discussions', $updatediscussion)) { return false; } if ($newfilename = forum_add_attachment($post, 'attachment',$message)) { $post->attachment = $newfilename; } else { unset($post->attachment); } if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return $DB->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, $DB; $timenow = time(); // The first post is stored as a real post, and linked // to from the discuss entry. $forum = $DB->get_record('forum', array('id'=>$discussion->forum)); $post = new object(); $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 = $forum->id; // speedup $post->course = $forum->course; // speedup $post->format = $discussion->format; $post->mailnow = $discussion->mailnow; if (! $post->id = $DB->insert_record("forum_posts", $post) ) { return 0; } if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) { $DB->set_field("forum_posts", "attachment", $post->attachment, array("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 = $DB->insert_record("forum_discussions", $discussion) ) { $DB->delete_records("forum_posts", array("id"=>$post->id)); return 0; } // Finally, set the pointer on the post. if (! $DB->set_field("forum_posts", "discussion", $post->discussion, array("id"=>$post->id))) { $DB->delete_records("forum_posts", array("id"=>$post->id)); $DB->delete_records("forum_discussions", array("id"=>$post->discussion)); return 0; } if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { forum_tp_mark_post_read($post->userid, $post, $post->forum); } return $post->discussion; } /** * Deletes a discussion and handles all associated cleanup. * @param object $discussion Discussion to delete * @param bool $fulldelete True when deleting entire forum * @param object $course Course (required if fulldelete is false) * @param object $cm Course-module (required if fulldelete is false) * @param object $forum Forum (required if fulldelete is false) */ function forum_delete_discussion($discussion, $fulldelete=false,$course=null,$cm=null,$forum=null) { global $DB; $result = true; if ($posts = $DB->get_records("forum_posts", array("discussion" => $discussion->id))) { foreach ($posts as $post) { $post->course = $discussion->course; $post->forum = $discussion->forum; if (! forum_delete_post($post, 'ignore',$course, $cm, $forum, $fulldelete)) { $result = false; } } } forum_tp_delete_read_records(-1, -1, $discussion->id); if (! $DB->delete_records("forum_discussions", array("id" => "$discussion->id"))) { $result = false; } // Update completion state if we are tracking completion based on number of posts $completion=new completion_info($course); if(!$fulldelete && // But don't bother when deleting whole thing $completion->is_enabled($cm)==COMPLETION_TRACKING_AUTOMATIC && ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) { $completion->update_state($cm,COMPLETION_INCOMPLETE,$discussion->userid); } return $result; } /** * Deletes a single forum post. * @param object $post Forum post object * @param mixed $children Whether to delete children. If false, returns false * if there are any children (without deleting the post). If true, * recursively deletes all children. If set to special value 'ignore', deletes * post regardless of children (this is for use only when deleting all posts * in a disussion). * @param object $course Course * @param object $cm Course-module * @param object $forum Forum * @param bool $skipcompletion True to skip updating completion state if it * would otherwise be updated, i.e. when deleting entire forum anyway. */ function forum_delete_post($post, $children, $course, $cm, $forum, $skipcompletion=false) { global $DB; if ($children!='ignore' && ($childposts = $DB->get_records('forum_posts', array('parent'=>$post->id)))) { if ($children) { foreach ($childposts as $childpost) { forum_delete_post($childpost, true, $course, $cm, $forum, $skipcompletion); } } else { return false; } } if ($DB->delete_records("forum_posts", array("id" => $post->id))) { $DB->delete_records("forum_ratings", array("post" => $post->id)); forum_tp_delete_read_records(-1, $post->id); if ($post->attachment) { $discussion = $DB->get_record("forum_discussions", array("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); // Update completion state if we are tracking completion based on number of posts $completion=new completion_info($course); if(!$skipcompletion && // But don't bother when deleting whole thing $completion->is_enabled($cm)==COMPLETION_TRACKING_AUTOMATIC && ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) { $completion->update_state($cm,COMPLETION_INCOMPLETE,$post->userid); } return true; } return false; } /** * */ function forum_count_replies($post, $children=true) { global $DB; $count = 0; if ($children) { if ($childposts = $DB->get_records('forum_posts', array('parent' => $post->id))) { foreach ($childposts as $childpost) { $count ++; // For this child $count += forum_count_replies($childpost, true); } } } else { $count += $DB->count_records('forum_posts', array('parent' => $post->id)); } return $count; } /** * */ function forum_forcesubscribe($forumid, $value=1) { global $DB; return $DB->set_field("forum", "forcesubscribe", $value, array("id" => $forumid)); } /** * */ function forum_is_forcesubscribed($forum) { global $DB; if (isset($forum->forcesubscribe)) { // then we use that return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE); } else { // Check the database return ($DB->get_field('forum', 'forcesubscribe', array('id' => $forum)) == FORUM_FORCESUBSCRIBE); } } /** * */ function forum_is_subscribed($userid, $forum) { global $DB; if (is_numeric($forum)) { $forum = $DB->get_record('forum', array('id' => $forum)); } if (forum_is_forcesubscribed($forum)) { return true; } return $DB->record_exists("forum_subscriptions", array("userid" => $userid, "forum" => $forum->id)); } function forum_get_subscribed_forums($course) { global $USER, $CFG, $DB; $sql = "SELECT f.id FROM {forum} f LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?) WHERE f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE." AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)"; if ($subscribed = $DB->get_records_sql($sql, array($USER->id))) { foreach ($subscribed as $s) { $subscribed[$s->id] = $s->id; } return $subscribed; } else { return array(); } } /** * Adds user to the subscriber list */ function forum_subscribe($userid, $forumid) { global $DB; if ($DB->record_exists("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid))) { return true; } $sub = new object(); $sub->userid = $userid; $sub->forum = $forumid; return $DB->insert_record("forum_subscriptions", $sub); } /** * Removes user from the subscriber list */ function forum_unsubscribe($userid, $forumid) { global $DB; return $DB->delete_records("forum_subscriptions", array("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, $DB; $subscribed=forum_is_subscribed($USER->id, $post->forum); if ((isset($post->subscribe) && $post->subscribe && $subscribed) || (!$post->subscribe && !$subscribed)) { return ""; } if (!$forum = $DB->get_record("forum", array("id" => $post->forum))) { return ""; } $info = new object(); $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, $fakelink=true, $backtoindex=false, $subscribed_forums=null) { 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)) { 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 (is_null($subscribed_forums)) { $subscribed = forum_is_subscribed($USER->id, $forum); } else { $subscribed = !empty($subscribed_forums[$forum->id]); } if ($subscribed) { $linktext = $messages['subscribed']; $linktitle = get_string('subscribestop', 'forum'); } else { $linktext = $messages['unsubscribed']; $linktitle = get_string('subscribestart', 'forum'); } $options = array(); if ($backtoindex) { $backtoindexlink = '&backtoindex=1'; $options['backtoindex'] = 1; } else { $backtoindexlink = ''; } $link = ''; if ($fakelink) { $link .= ''; // use