Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/forum/lib.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00024 defined('MOODLE_INTERNAL') || die();
00025 
00027 require_once($CFG->libdir.'/filelib.php');
00028 require_once($CFG->libdir.'/eventslib.php');
00029 require_once($CFG->dirroot.'/user/selector/lib.php');
00030 
00032 
00033 define('FORUM_MODE_FLATOLDEST', 1);
00034 define('FORUM_MODE_FLATNEWEST', -1);
00035 define('FORUM_MODE_THREADED', 2);
00036 define('FORUM_MODE_NESTED', 3);
00037 
00038 define('FORUM_CHOOSESUBSCRIBE', 0);
00039 define('FORUM_FORCESUBSCRIBE', 1);
00040 define('FORUM_INITIALSUBSCRIBE', 2);
00041 define('FORUM_DISALLOWSUBSCRIBE',3);
00042 
00043 define('FORUM_TRACKING_OFF', 0);
00044 define('FORUM_TRACKING_OPTIONAL', 1);
00045 define('FORUM_TRACKING_ON', 2);
00046 
00048 
00060 function forum_add_instance($forum, $mform) {
00061     global $CFG, $DB;
00062 
00063     $forum->timemodified = time();
00064 
00065     if (empty($forum->assessed)) {
00066         $forum->assessed = 0;
00067     }
00068 
00069     if (empty($forum->ratingtime) or empty($forum->assessed)) {
00070         $forum->assesstimestart  = 0;
00071         $forum->assesstimefinish = 0;
00072     }
00073 
00074     $forum->id = $DB->insert_record('forum', $forum);
00075     $modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule);
00076 
00077     if ($forum->type == 'single') {  // Create related discussion.
00078         $discussion = new stdClass();
00079         $discussion->course        = $forum->course;
00080         $discussion->forum         = $forum->id;
00081         $discussion->name          = $forum->name;
00082         $discussion->assessed      = $forum->assessed;
00083         $discussion->message       = $forum->intro;
00084         $discussion->messageformat = $forum->introformat;
00085         $discussion->messagetrust  = trusttext_trusted(get_context_instance(CONTEXT_COURSE, $forum->course));
00086         $discussion->mailnow       = false;
00087         $discussion->groupid       = -1;
00088 
00089         $message = '';
00090 
00091         $discussion->id = forum_add_discussion($discussion, null, $message);
00092 
00093         if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) {
00094             // ugly hack - we need to copy the files somehow
00095             $discussion = $DB->get_record('forum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST);
00096             $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST);
00097 
00098             $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message);
00099             $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
00100         }
00101     }
00102 
00103     if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) {
00110         $users = forum_get_potential_subscribers($modcontext, 0, 'u.id, u.email', '');
00111         foreach ($users as $user) {
00112             forum_subscribe($user->id, $forum->id);
00113         }
00114     }
00115 
00116     forum_grade_item_update($forum);
00117 
00118     return $forum->id;
00119 }
00120 
00121 
00131 function forum_update_instance($forum, $mform) {
00132     global $DB, $OUTPUT, $USER;
00133 
00134     $forum->timemodified = time();
00135     $forum->id           = $forum->instance;
00136 
00137     if (empty($forum->assessed)) {
00138         $forum->assessed = 0;
00139     }
00140 
00141     if (empty($forum->ratingtime) or empty($forum->assessed)) {
00142         $forum->assesstimestart  = 0;
00143         $forum->assesstimefinish = 0;
00144     }
00145 
00146     $oldforum = $DB->get_record('forum', array('id'=>$forum->id));
00147 
00148     // MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum
00149     // if  scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond?
00150     // 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
00151     if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) {
00152         forum_update_grades($forum); // recalculate grades for the forum
00153     }
00154 
00155     if ($forum->type == 'single') {  // Update related discussion and post.
00156         $discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id), 'timemodified ASC');
00157         if (!empty($discussions)) {
00158             if (count($discussions) > 1) {
00159                 echo $OUTPUT->notification(get_string('warnformorepost', 'forum'));
00160             }
00161             $discussion = array_pop($discussions);
00162         } else {
00163             // try to recover by creating initial discussion - MDL-16262
00164             $discussion = new stdClass();
00165             $discussion->course          = $forum->course;
00166             $discussion->forum           = $forum->id;
00167             $discussion->name            = $forum->name;
00168             $discussion->assessed        = $forum->assessed;
00169             $discussion->message         = $forum->intro;
00170             $discussion->messageformat   = $forum->introformat;
00171             $discussion->messagetrust    = true;
00172             $discussion->mailnow         = false;
00173             $discussion->groupid         = -1;
00174 
00175             $message = '';
00176 
00177             forum_add_discussion($discussion, null, $message);
00178 
00179             if (! $discussion = $DB->get_record('forum_discussions', array('forum'=>$forum->id))) {
00180                 print_error('cannotadd', 'forum');
00181             }
00182         }
00183         if (! $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost))) {
00184             print_error('cannotfindfirstpost', 'forum');
00185         }
00186 
00187         $cm         = get_coursemodule_from_instance('forum', $forum->id);
00188         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
00189 
00190         if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) {
00191             // ugly hack - we need to copy the files somehow
00192             $discussion = $DB->get_record('forum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST);
00193             $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST);
00194 
00195             $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message);
00196         }
00197 
00198         $post->subject       = $forum->name;
00199         $post->message       = $forum->intro;
00200         $post->messageformat = $forum->introformat;
00201         $post->messagetrust  = trusttext_trusted($modcontext);
00202         $post->modified      = $forum->timemodified;
00203         $post->userid        = $USER->id;    // MDL-18599, so that current teacher can take ownership of activities
00204 
00205         $DB->update_record('forum_posts', $post);
00206         $discussion->name = $forum->name;
00207         $DB->update_record('forum_discussions', $discussion);
00208     }
00209 
00210     $DB->update_record('forum', $forum);
00211 
00212     forum_grade_item_update($forum);
00213 
00214     return true;
00215 }
00216 
00217 
00227 function forum_delete_instance($id) {
00228     global $DB;
00229 
00230     if (!$forum = $DB->get_record('forum', array('id'=>$id))) {
00231         return false;
00232     }
00233     if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
00234         return false;
00235     }
00236     if (!$course = $DB->get_record('course', array('id'=>$cm->course))) {
00237         return false;
00238     }
00239 
00240     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
00241 
00242     // now get rid of all files
00243     $fs = get_file_storage();
00244     $fs->delete_area_files($context->id);
00245 
00246     $result = true;
00247 
00248     if ($discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id))) {
00249         foreach ($discussions as $discussion) {
00250             if (!forum_delete_discussion($discussion, true, $course, $cm, $forum)) {
00251                 $result = false;
00252             }
00253         }
00254     }
00255 
00256     if (!$DB->delete_records('forum_subscriptions', array('forum'=>$forum->id))) {
00257         $result = false;
00258     }
00259 
00260     forum_tp_delete_read_records(-1, -1, -1, $forum->id);
00261 
00262     if (!$DB->delete_records('forum', array('id'=>$forum->id))) {
00263         $result = false;
00264     }
00265 
00266     forum_grade_item_delete($forum);
00267 
00268     return $result;
00269 }
00270 
00271 
00286 function forum_supports($feature) {
00287     switch($feature) {
00288         case FEATURE_GROUPS:                  return true;
00289         case FEATURE_GROUPINGS:               return true;
00290         case FEATURE_GROUPMEMBERSONLY:        return true;
00291         case FEATURE_MOD_INTRO:               return true;
00292         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
00293         case FEATURE_COMPLETION_HAS_RULES:    return true;
00294         case FEATURE_GRADE_HAS_GRADE:         return true;
00295         case FEATURE_GRADE_OUTCOMES:          return true;
00296         case FEATURE_RATE:                    return true;
00297         case FEATURE_BACKUP_MOODLE2:          return true;
00298         case FEATURE_SHOW_DESCRIPTION:        return true;
00299 
00300         default: return null;
00301     }
00302 }
00303 
00304 
00318 function forum_get_completion_state($course,$cm,$userid,$type) {
00319     global $CFG,$DB;
00320 
00321     // Get forum details
00322     if (!($forum=$DB->get_record('forum',array('id'=>$cm->instance)))) {
00323         throw new Exception("Can't find forum {$cm->instance}");
00324     }
00325 
00326     $result=$type; // Default return value
00327 
00328     $postcountparams=array('userid'=>$userid,'forumid'=>$forum->id);
00329     $postcountsql="
00330 SELECT
00331     COUNT(1)
00332 FROM
00333     {forum_posts} fp
00334     INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id
00335 WHERE
00336     fp.userid=:userid AND fd.forum=:forumid";
00337 
00338     if ($forum->completiondiscussions) {
00339         $value = $forum->completiondiscussions <=
00340                  $DB->count_records('forum_discussions',array('forum'=>$forum->id,'userid'=>$userid));
00341         if ($type == COMPLETION_AND) {
00342             $result = $result && $value;
00343         } else {
00344             $result = $result || $value;
00345         }
00346     }
00347     if ($forum->completionreplies) {
00348         $value = $forum->completionreplies <=
00349                  $DB->get_field_sql( $postcountsql.' AND fp.parent<>0',$postcountparams);
00350         if ($type==COMPLETION_AND) {
00351             $result = $result && $value;
00352         } else {
00353             $result = $result || $value;
00354         }
00355     }
00356     if ($forum->completionposts) {
00357         $value = $forum->completionposts <= $DB->get_field_sql($postcountsql,$postcountparams);
00358         if ($type == COMPLETION_AND) {
00359             $result = $result && $value;
00360         } else {
00361             $result = $result || $value;
00362         }
00363     }
00364 
00365     return $result;
00366 }
00367 
00368 
00383 function forum_cron() {
00384     global $CFG, $USER, $DB;
00385 
00386     $site = get_site();
00387 
00388     // all users that are subscribed to any post that needs sending
00389     $users = array();
00390 
00391     // status arrays
00392     $mailcount  = array();
00393     $errorcount = array();
00394 
00395     // caches
00396     $discussions     = array();
00397     $forums          = array();
00398     $courses         = array();
00399     $coursemodules   = array();
00400     $subscribedusers = array();
00401 
00402 
00403     // Posts older than 2 days will not be mailed.  This is to avoid the problem where
00404     // cron has not been running for a long time, and then suddenly people are flooded
00405     // with mail from the past few weeks or months
00406     $timenow   = time();
00407     $endtime   = $timenow - $CFG->maxeditingtime;
00408     $starttime = $endtime - 48 * 3600;   // Two days earlier
00409 
00410     if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
00411         // Mark them all now as being mailed.  It's unlikely but possible there
00412         // might be an error later so that a post is NOT actually mailed out,
00413         // but since mail isn't crucial, we can accept this risk.  Doing it now
00414         // prevents the risk of duplicated mails, which is a worse problem.
00415 
00416         if (!forum_mark_old_posts_as_mailed($endtime)) {
00417             mtrace('Errors occurred while trying to mark some posts as being mailed.');
00418             return false;  // Don't continue trying to mail them, in case we are in a cron loop
00419         }
00420 
00421         // checking post validity, and adding users to loop through later
00422         foreach ($posts as $pid => $post) {
00423 
00424             $discussionid = $post->discussion;
00425             if (!isset($discussions[$discussionid])) {
00426                 if ($discussion = $DB->get_record('forum_discussions', array('id'=> $post->discussion))) {
00427                     $discussions[$discussionid] = $discussion;
00428                 } else {
00429                     mtrace('Could not find discussion '.$discussionid);
00430                     unset($posts[$pid]);
00431                     continue;
00432                 }
00433             }
00434             $forumid = $discussions[$discussionid]->forum;
00435             if (!isset($forums[$forumid])) {
00436                 if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
00437                     $forums[$forumid] = $forum;
00438                 } else {
00439                     mtrace('Could not find forum '.$forumid);
00440                     unset($posts[$pid]);
00441                     continue;
00442                 }
00443             }
00444             $courseid = $forums[$forumid]->course;
00445             if (!isset($courses[$courseid])) {
00446                 if ($course = $DB->get_record('course', array('id' => $courseid))) {
00447                     $courses[$courseid] = $course;
00448                 } else {
00449                     mtrace('Could not find course '.$courseid);
00450                     unset($posts[$pid]);
00451                     continue;
00452                 }
00453             }
00454             if (!isset($coursemodules[$forumid])) {
00455                 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
00456                     $coursemodules[$forumid] = $cm;
00457                 } else {
00458                     mtrace('Could not find course module for forum '.$forumid);
00459                     unset($posts[$pid]);
00460                     continue;
00461                 }
00462             }
00463 
00464 
00465             // caching subscribed users of each forum
00466             if (!isset($subscribedusers[$forumid])) {
00467                 $modcontext = get_context_instance(CONTEXT_MODULE, $coursemodules[$forumid]->id);
00468                 if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext, "u.*")) {
00469                     foreach ($subusers as $postuser) {
00470                         unset($postuser->description); // not necessary
00471                         // this user is subscribed to this forum
00472                         $subscribedusers[$forumid][$postuser->id] = $postuser->id;
00473                         // this user is a user we have to process later
00474                         $users[$postuser->id] = $postuser;
00475                     }
00476                     unset($subusers); // release memory
00477                 }
00478             }
00479 
00480             $mailcount[$pid] = 0;
00481             $errorcount[$pid] = 0;
00482         }
00483     }
00484 
00485     if ($users && $posts) {
00486 
00487         $urlinfo = parse_url($CFG->wwwroot);
00488         $hostname = $urlinfo['host'];
00489 
00490         foreach ($users as $userto) {
00491 
00492             @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
00493 
00494             // set this so that the capabilities are cached, and environment matches receiving user
00495             cron_setup_user($userto);
00496 
00497             mtrace('Processing user '.$userto->id);
00498 
00499             // init caches
00500             $userto->viewfullnames = array();
00501             $userto->canpost       = array();
00502             $userto->markposts     = array();
00503 
00504             // reset the caches
00505             foreach ($coursemodules as $forumid=>$unused) {
00506                 $coursemodules[$forumid]->cache       = new stdClass();
00507                 $coursemodules[$forumid]->cache->caps = array();
00508                 unset($coursemodules[$forumid]->uservisible);
00509             }
00510 
00511             foreach ($posts as $pid => $post) {
00512 
00513                 // Set up the environment for the post, discussion, forum, course
00514                 $discussion = $discussions[$post->discussion];
00515                 $forum      = $forums[$discussion->forum];
00516                 $course     = $courses[$forum->course];
00517                 $cm         =& $coursemodules[$forum->id];
00518 
00519                 // Do some checks  to see if we can bail out now
00520                 // Only active enrolled users are in the list of subscribers
00521                 if (!isset($subscribedusers[$forum->id][$userto->id])) {
00522                     continue; // user does not subscribe to this forum
00523                 }
00524 
00525                 // Don't send email if the forum is Q&A and the user has not posted
00526                 // Initial topics are still mailed
00527                 if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) {
00528                     mtrace('Did not email '.$userto->id.' because user has not posted in discussion');
00529                     continue;
00530                 }
00531 
00532                 // Get info about the sending user
00533                 if (array_key_exists($post->userid, $users)) { // we might know him/her already
00534                     $userfrom = $users[$post->userid];
00535                 } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
00536                     unset($userfrom->description); // not necessary
00537                     $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
00538                 } else {
00539                     mtrace('Could not find user '.$post->userid);
00540                     continue;
00541                 }
00542 
00543                 //if we want to check that userto and userfrom are not the same person this is probably the spot to do it
00544 
00545                 // setup global $COURSE properly - needed for roles and languages
00546                 cron_setup_user($userto, $course);
00547 
00548                 // Fill caches
00549                 if (!isset($userto->viewfullnames[$forum->id])) {
00550                     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
00551                     $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
00552                 }
00553                 if (!isset($userto->canpost[$discussion->id])) {
00554                     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
00555                     $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
00556                 }
00557                 if (!isset($userfrom->groups[$forum->id])) {
00558                     if (!isset($userfrom->groups)) {
00559                         $userfrom->groups = array();
00560                         $users[$userfrom->id]->groups = array();
00561                     }
00562                     $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
00563                     $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
00564                 }
00565 
00566                 // Make sure groups allow this user to see this email
00567                 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
00568                     if (!groups_group_exists($discussion->groupid)) { // Can't find group
00569                         continue;                           // Be safe and don't send it to anyone
00570                     }
00571 
00572                     if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
00573                         // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
00574                         continue;
00575                     }
00576                 }
00577 
00578                 // Make sure we're allowed to see it...
00579                 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
00580                     mtrace('user '.$userto->id. ' can not see '.$post->id);
00581                     continue;
00582                 }
00583 
00584                 // OK so we need to send the email.
00585 
00586                 // Does the user want this post in a digest?  If so postpone it for now.
00587                 if ($userto->maildigest > 0) {
00588                     // This user wants the mails to be in digest form
00589                     $queue = new stdClass();
00590                     $queue->userid       = $userto->id;
00591                     $queue->discussionid = $discussion->id;
00592                     $queue->postid       = $post->id;
00593                     $queue->timemodified = $post->created;
00594                     $DB->insert_record('forum_queue', $queue);
00595                     continue;
00596                 }
00597 
00598 
00599                 // Prepare to actually send the post now, and build up the content
00600 
00601                 $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
00602 
00603                 $userfrom->customheaders = array (  // Headers to make emails easier to track
00604                            'Precedence: Bulk',
00605                            'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
00606                            'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
00607                            'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>',
00608                            'X-Course-Id: '.$course->id,
00609                            'X-Course-Name: '.format_string($course->fullname, true)
00610                 );
00611 
00612                 if ($post->parent) {  // This post is a reply, so add headers for threading (see MDL-22551)
00613                     $userfrom->customheaders[] = 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>';
00614                     $userfrom->customheaders[] = 'References: <moodlepost'.$post->parent.'@'.$hostname.'>';
00615                 }
00616 
00617                 $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
00618 
00619                 $postsubject = "$shortname: ".format_string($post->subject,true);
00620                 $posttext = forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto);
00621                 $posthtml = forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto);
00622 
00623                 // Send the post now!
00624 
00625                 mtrace('Sending ', '');
00626 
00627                 $eventdata = new stdClass();
00628                 $eventdata->component        = 'mod_forum';
00629                 $eventdata->name             = 'posts';
00630                 $eventdata->userfrom         = $userfrom;
00631                 $eventdata->userto           = $userto;
00632                 $eventdata->subject          = $postsubject;
00633                 $eventdata->fullmessage      = $posttext;
00634                 $eventdata->fullmessageformat = FORMAT_PLAIN;
00635                 $eventdata->fullmessagehtml  = $posthtml;
00636                 $eventdata->notification = 1;
00637 
00638                 $smallmessagestrings = new stdClass();
00639                 $smallmessagestrings->user = fullname($userfrom);
00640                 $smallmessagestrings->forumname = "$shortname: ".format_string($forum->name,true).": ".$discussion->name;
00641                 $smallmessagestrings->message = $post->message;
00642                 //make sure strings are in message recipients language
00643                 $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'forum', $smallmessagestrings, $userto->lang);
00644 
00645                 $eventdata->contexturl = "{$CFG->wwwroot}/mod/forum/discuss.php?d={$discussion->id}#p{$post->id}";
00646                 $eventdata->contexturlname = $discussion->name;
00647 
00648                 $mailresult = message_send($eventdata);
00649                 if (!$mailresult){
00650                     mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id $post->id to user $userto->id".
00651                          " ($userto->email) .. not trying again.");
00652                     add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
00653                                substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
00654                     $errorcount[$post->id]++;
00655                 } else {
00656                     $mailcount[$post->id]++;
00657 
00658                 // Mark post as read if forum_usermarksread is set off
00659                     if (!$CFG->forum_usermarksread) {
00660                         $userto->markposts[$post->id] = $post->id;
00661                     }
00662                 }
00663 
00664                 mtrace('post '.$post->id. ': '.$post->subject);
00665             }
00666 
00667             // mark processed posts as read
00668             forum_tp_mark_posts_read($userto, $userto->markposts);
00669         }
00670     }
00671 
00672     if ($posts) {
00673         foreach ($posts as $post) {
00674             mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
00675             if ($errorcount[$post->id]) {
00676                 $DB->set_field("forum_posts", "mailed", "2", array("id" => "$post->id"));
00677             }
00678         }
00679     }
00680 
00681     // release some memory
00682     unset($subscribedusers);
00683     unset($mailcount);
00684     unset($errorcount);
00685 
00686     cron_setup_user();
00687 
00688     $sitetimezone = $CFG->timezone;
00689 
00690     // Now see if there are any digest mails waiting to be sent, and if we should send them
00691 
00692     mtrace('Starting digest processing...');
00693 
00694     @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes
00695 
00696     if (!isset($CFG->digestmailtimelast)) {    // To catch the first time
00697         set_config('digestmailtimelast', 0);
00698     }
00699 
00700     $timenow = time();
00701     $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
00702 
00703     // Delete any really old ones (normally there shouldn't be any)
00704     $weekago = $timenow - (7 * 24 * 3600);
00705     $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));
00706     mtrace ('Cleaned old digest records');
00707 
00708     if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
00709 
00710         mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
00711 
00712         $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime));
00713 
00714         if ($digestposts_rs->valid()) {
00715 
00716             // We have work to do
00717             $usermailcount = 0;
00718 
00719             //caches - reuse the those filled before too
00720             $discussionposts = array();
00721             $userdiscussions = array();
00722 
00723             foreach ($digestposts_rs as $digestpost) {
00724                 if (!isset($users[$digestpost->userid])) {
00725                     if ($user = $DB->get_record('user', array('id' => $digestpost->userid))) {
00726                         $users[$digestpost->userid] = $user;
00727                     } else {
00728                         continue;
00729                     }
00730                 }
00731                 $postuser = $users[$digestpost->userid];
00732 
00733                 if (!isset($posts[$digestpost->postid])) {
00734                     if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
00735                         $posts[$digestpost->postid] = $post;
00736                     } else {
00737                         continue;
00738                     }
00739                 }
00740                 $discussionid = $digestpost->discussionid;
00741                 if (!isset($discussions[$discussionid])) {
00742                     if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) {
00743                         $discussions[$discussionid] = $discussion;
00744                     } else {
00745                         continue;
00746                     }
00747                 }
00748                 $forumid = $discussions[$discussionid]->forum;
00749                 if (!isset($forums[$forumid])) {
00750                     if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
00751                         $forums[$forumid] = $forum;
00752                     } else {
00753                         continue;
00754                     }
00755                 }
00756 
00757                 $courseid = $forums[$forumid]->course;
00758                 if (!isset($courses[$courseid])) {
00759                     if ($course = $DB->get_record('course', array('id' => $courseid))) {
00760                         $courses[$courseid] = $course;
00761                     } else {
00762                         continue;
00763                     }
00764                 }
00765 
00766                 if (!isset($coursemodules[$forumid])) {
00767                     if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
00768                         $coursemodules[$forumid] = $cm;
00769                     } else {
00770                         continue;
00771                     }
00772                 }
00773                 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
00774                 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
00775             }
00776             $digestposts_rs->close(); 
00777 
00778             // Data collected, start sending out emails to each user
00779             foreach ($userdiscussions as $userid => $thesediscussions) {
00780 
00781                 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
00782 
00783                 cron_setup_user();
00784 
00785                 mtrace(get_string('processingdigest', 'forum', $userid), '... ');
00786 
00787                 // First of all delete all the queue entries for this user
00788                 $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));
00789                 $userto = $users[$userid];
00790 
00791                 // Override the language and timezone of the "current" user, so that
00792                 // mail is customised for the receiver.
00793                 cron_setup_user($userto);
00794 
00795                 // init caches
00796                 $userto->viewfullnames = array();
00797                 $userto->canpost       = array();
00798                 $userto->markposts     = array();
00799 
00800                 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
00801 
00802                 $headerdata = new stdClass();
00803                 $headerdata->sitename = format_string($site->fullname, true);
00804                 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
00805 
00806                 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
00807                 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
00808 
00809                 $posthtml = "<head>";
00810 /*                foreach ($CFG->stylesheets as $stylesheet) {
00811                     //TODO: MDL-21120
00812                     $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
00813                 }*/
00814                 $posthtml .= "</head>\n<body id=\"email\">\n";
00815                 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
00816 
00817                 foreach ($thesediscussions as $discussionid) {
00818 
00819                     @set_time_limit(120);   // to be reset for each post
00820 
00821                     $discussion = $discussions[$discussionid];
00822                     $forum      = $forums[$discussion->forum];
00823                     $course     = $courses[$forum->course];
00824                     $cm         = $coursemodules[$forum->id];
00825 
00826                     //override language
00827                     cron_setup_user($userto, $course);
00828 
00829                     // Fill caches
00830                     if (!isset($userto->viewfullnames[$forum->id])) {
00831                         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
00832                         $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
00833                     }
00834                     if (!isset($userto->canpost[$discussion->id])) {
00835                         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
00836                         $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
00837                     }
00838 
00839                     $strforums      = get_string('forums', 'forum');
00840                     $canunsubscribe = ! forum_is_forcesubscribed($forum);
00841                     $canreply       = $userto->canpost[$discussion->id];
00842                     $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
00843 
00844                     $posttext .= "\n \n";
00845                     $posttext .= '=====================================================================';
00846                     $posttext .= "\n \n";
00847                     $posttext .= "$shortname -> $strforums -> ".format_string($forum->name,true);
00848                     if ($discussion->name != $forum->name) {
00849                         $posttext  .= " -> ".format_string($discussion->name,true);
00850                     }
00851                     $posttext .= "\n";
00852 
00853                     $posthtml .= "<p><font face=\"sans-serif\">".
00854                     "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$shortname</a> -> ".
00855                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
00856                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
00857                     if ($discussion->name == $forum->name) {
00858                         $posthtml .= "</font></p>";
00859                     } else {
00860                         $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
00861                     }
00862                     $posthtml .= '<p>';
00863 
00864                     $postsarray = $discussionposts[$discussionid];
00865                     sort($postsarray);
00866 
00867                     foreach ($postsarray as $postid) {
00868                         $post = $posts[$postid];
00869 
00870                         if (array_key_exists($post->userid, $users)) { // we might know him/her already
00871                             $userfrom = $users[$post->userid];
00872                         } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
00873                             $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
00874                         } else {
00875                             mtrace('Could not find user '.$post->userid);
00876                             continue;
00877                         }
00878 
00879                         if (!isset($userfrom->groups[$forum->id])) {
00880                             if (!isset($userfrom->groups)) {
00881                                 $userfrom->groups = array();
00882                                 $users[$userfrom->id]->groups = array();
00883                             }
00884                             $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
00885                             $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
00886                         }
00887 
00888                         $userfrom->customheaders = array ("Precedence: Bulk");
00889 
00890                         if ($userto->maildigest == 2) {
00891                             // Subjects only
00892                             $by = new stdClass();
00893                             $by->name = fullname($userfrom);
00894                             $by->date = userdate($post->modified);
00895                             $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
00896                             $posttext .= "\n---------------------------------------------------------------------";
00897 
00898                             $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
00899                             $posthtml .= '<div><a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'#p'.$post->id.'">'.format_string($post->subject,true).'</a> '.get_string("bynameondate", "forum", $by).'</div>';
00900 
00901                         } else {
00902                             // The full treatment
00903                             $posttext .= forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, true);
00904                             $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
00905 
00906                         // Create an array of postid's for this user to mark as read.
00907                             if (!$CFG->forum_usermarksread) {
00908                                 $userto->markposts[$post->id] = $post->id;
00909                             }
00910                         }
00911                     }
00912                     if ($canunsubscribe) {
00913                         $posthtml .= "\n<div class='mdl-right'><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>";
00914                     } else {
00915                         $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
00916                     }
00917                     $posthtml .= '<hr size="1" noshade="noshade" /></p>';
00918                 }
00919                 $posthtml .= '</body>';
00920 
00921                 if (empty($userto->mailformat) || $userto->mailformat != 1) {
00922                     // This user DOESN'T want to receive HTML
00923                     $posthtml = '';
00924                 }
00925 
00926                 $attachment = $attachname='';
00927                 $usetrueaddress = true;
00928                 //directly email forum digests rather than sending them via messaging
00929                 $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname, $usetrueaddress, $CFG->forum_replytouser);
00930 
00931                 if (!$mailresult) {
00932                     mtrace("ERROR!");
00933                     echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
00934                     add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
00935                 } else {
00936                     mtrace("success.");
00937                     $usermailcount++;
00938 
00939                     // Mark post as read if forum_usermarksread is set off
00940                     forum_tp_mark_posts_read($userto, $userto->markposts);
00941                 }
00942             }
00943         }
00945         set_config('digestmailtimelast', $timenow);
00946     }
00947 
00948     cron_setup_user();
00949 
00950     if (!empty($usermailcount)) {
00951         mtrace(get_string('digestsentusers', 'forum', $usermailcount));
00952     }
00953 
00954     if (!empty($CFG->forum_lastreadclean)) {
00955         $timenow = time();
00956         if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
00957             set_config('forum_lastreadclean', $timenow);
00958             mtrace('Removing old forum read tracking info...');
00959             forum_tp_clean_read_records();
00960         }
00961     } else {
00962         set_config('forum_lastreadclean', time());
00963     }
00964 
00965 
00966     return true;
00967 }
00968 
00985 function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
00986     global $CFG, $USER;
00987 
00988     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
00989 
00990     if (!isset($userto->viewfullnames[$forum->id])) {
00991         $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
00992     } else {
00993         $viewfullnames = $userto->viewfullnames[$forum->id];
00994     }
00995 
00996     if (!isset($userto->canpost[$discussion->id])) {
00997         $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
00998     } else {
00999         $canreply = $userto->canpost[$discussion->id];
01000     }
01001 
01002     $by = New stdClass;
01003     $by->name = fullname($userfrom, $viewfullnames);
01004     $by->date = userdate($post->modified, "", $userto->timezone);
01005 
01006     $strbynameondate = get_string('bynameondate', 'forum', $by);
01007 
01008     $strforums = get_string('forums', 'forum');
01009 
01010     $canunsubscribe = ! forum_is_forcesubscribed($forum);
01011 
01012     $posttext = '';
01013 
01014     if (!$bare) {
01015         $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
01016         $posttext  = "$shortname -> $strforums -> ".format_string($forum->name,true);
01017 
01018         if ($discussion->name != $forum->name) {
01019             $posttext  .= " -> ".format_string($discussion->name,true);
01020         }
01021     }
01022 
01023     // add absolute file links
01024     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
01025 
01026     $posttext .= "\n---------------------------------------------------------------------\n";
01027     $posttext .= format_string($post->subject,true);
01028     if ($bare) {
01029         $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
01030     }
01031     $posttext .= "\n".$strbynameondate."\n";
01032     $posttext .= "---------------------------------------------------------------------\n";
01033     $posttext .= format_text_email($post->message, $post->messageformat);
01034     $posttext .= "\n\n";
01035     $posttext .= forum_print_attachments($post, $cm, "text");
01036 
01037     if (!$bare && $canreply) {
01038         $posttext .= "---------------------------------------------------------------------\n";
01039         $posttext .= get_string("postmailinfo", "forum", $shortname)."\n";
01040         $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
01041     }
01042     if (!$bare && $canunsubscribe) {
01043         $posttext .= "\n---------------------------------------------------------------------\n";
01044         $posttext .= get_string("unsubscribe", "forum");
01045         $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
01046     }
01047 
01048     return $posttext;
01049 }
01050 
01064 function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto) {
01065     global $CFG;
01066 
01067     if ($userto->mailformat != 1) {  // Needs to be HTML
01068         return '';
01069     }
01070 
01071     if (!isset($userto->canpost[$discussion->id])) {
01072         $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course);
01073     } else {
01074         $canreply = $userto->canpost[$discussion->id];
01075     }
01076 
01077     $strforums = get_string('forums', 'forum');
01078     $canunsubscribe = ! forum_is_forcesubscribed($forum);
01079     $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id)));
01080 
01081     $posthtml = '<head>';
01082 /*    foreach ($CFG->stylesheets as $stylesheet) {
01083         //TODO: MDL-21120
01084         $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
01085     }*/
01086     $posthtml .= '</head>';
01087     $posthtml .= "\n<body id=\"email\">\n\n";
01088 
01089     $posthtml .= '<div class="navbar">'.
01090     '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$shortname.'</a> &raquo; '.
01091     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
01092     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
01093     if ($discussion->name == $forum->name) {
01094         $posthtml .= '</div>';
01095     } else {
01096         $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
01097                      format_string($discussion->name,true).'</a></div>';
01098     }
01099     $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
01100 
01101     if ($canunsubscribe) {
01102         $posthtml .= '<hr /><div class="mdl-align unsubscribelink">
01103                       <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
01104                       <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
01105     }
01106 
01107     $posthtml .= '</body>';
01108 
01109     return $posthtml;
01110 }
01111 
01112 
01121 function forum_user_outline($course, $user, $mod, $forum) {
01122     global $CFG;
01123     require_once("$CFG->libdir/gradelib.php");
01124     $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
01125     if (empty($grades->items[0]->grades)) {
01126         $grade = false;
01127     } else {
01128         $grade = reset($grades->items[0]->grades);
01129     }
01130 
01131     $count = forum_count_user_posts($forum->id, $user->id);
01132 
01133     if ($count && $count->postcount > 0) {
01134         $result = new stdClass();
01135         $result->info = get_string("numposts", "forum", $count->postcount);
01136         $result->time = $count->lastpost;
01137         if ($grade) {
01138             $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
01139         }
01140         return $result;
01141     } else if ($grade) {
01142         $result = new stdClass();
01143         $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
01144 
01145         //datesubmitted == time created. dategraded == time modified or time overridden
01146         //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
01147         //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
01148         if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
01149             $result->time = $grade->dategraded;
01150         } else {
01151             $result->time = $grade->datesubmitted;
01152         }
01153 
01154         return $result;
01155     }
01156     return NULL;
01157 }
01158 
01159 
01168 function forum_user_complete($course, $user, $mod, $forum) {
01169     global $CFG,$USER, $OUTPUT;
01170     require_once("$CFG->libdir/gradelib.php");
01171 
01172     $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
01173     if (!empty($grades->items[0]->grades)) {
01174         $grade = reset($grades->items[0]->grades);
01175         echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
01176         if ($grade->str_feedback) {
01177             echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
01178         }
01179     }
01180 
01181     if ($posts = forum_get_user_posts($forum->id, $user->id)) {
01182 
01183         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
01184             print_error('invalidcoursemodule');
01185         }
01186         $discussions = forum_get_user_involved_discussions($forum->id, $user->id);
01187 
01188         foreach ($posts as $post) {
01189             if (!isset($discussions[$post->discussion])) {
01190                 continue;
01191             }
01192             $discussion = $discussions[$post->discussion];
01193 
01194             forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false);
01195         }
01196     } else {
01197         echo "<p>".get_string("noposts", "forum")."</p>";
01198     }
01199 }
01200 
01201 
01202 
01203 
01204 
01205 
01213 function forum_print_overview($courses,&$htmlarray) {
01214     global $USER, $CFG, $DB, $SESSION;
01215 
01216     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
01217         return array();
01218     }
01219 
01220     if (!$forums = get_all_instances_in_courses('forum',$courses)) {
01221         return;
01222     }
01223 
01224 
01225     // get all forum logs in ONE query (much better!)
01226     $params = array();
01227     $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {log} l "
01228         ." JOIN {course_modules} cm ON cm.id = cmid "
01229         ." WHERE (";
01230     foreach ($courses as $course) {
01231         $sql .= '(l.course = ? AND l.time > ?) OR ';
01232         $params[] = $course->id;
01233         $params[] = $course->lastaccess;
01234     }
01235     $sql = substr($sql,0,-3); // take off the last OR
01236 
01237     $sql .= ") AND l.module = 'forum' AND action = 'add post' "
01238         ." AND userid != ? GROUP BY cmid,l.course,instance";
01239 
01240     $params[] = $USER->id;
01241 
01242     if (!$new = $DB->get_records_sql($sql, $params)) {
01243         $new = array(); // avoid warnings
01244     }
01245 
01246     // also get all forum tracking stuff ONCE.
01247     $trackingforums = array();
01248     foreach ($forums as $forum) {
01249         if (forum_tp_can_track_forums($forum)) {
01250             $trackingforums[$forum->id] = $forum;
01251         }
01252     }
01253 
01254     if (count($trackingforums) > 0) {
01255         $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
01256         $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
01257             ' FROM {forum_posts} p '.
01258             ' JOIN {forum_discussions} d ON p.discussion = d.id '.
01259             ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE (';
01260         $params = array($USER->id);
01261 
01262         foreach ($trackingforums as $track) {
01263             $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR ';
01264             $params[] = $track->id;
01265             if (isset($SESSION->currentgroup[$track->course])) {
01266                 $groupid =  $SESSION->currentgroup[$track->course];
01267             } else {
01268                 $groupid = groups_get_all_groups($track->course, $USER->id);
01269                 if (is_array($groupid)) {
01270                     $groupid = array_shift(array_keys($groupid));
01271                     $SESSION->currentgroup[$track->course] = $groupid;
01272                 } else {
01273                     $groupid = 0;
01274                 }
01275             }
01276             $params[] = $groupid;
01277         }
01278         $sql = substr($sql,0,-3); // take off the last OR
01279         $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course';
01280         $params[] = $cutoffdate;
01281 
01282         if (!$unread = $DB->get_records_sql($sql, $params)) {
01283             $unread = array();
01284         }
01285     } else {
01286         $unread = array();
01287     }
01288 
01289     if (empty($unread) and empty($new)) {
01290         return;
01291     }
01292 
01293     $strforum = get_string('modulename','forum');
01294     $strnumunread = get_string('overviewnumunread','forum');
01295     $strnumpostssince = get_string('overviewnumpostssince','forum');
01296 
01297     foreach ($forums as $forum) {
01298         $str = '';
01299         $count = 0;
01300         $thisunread = 0;
01301         $showunread = false;
01302         // either we have something from logs, or trackposts, or nothing.
01303         if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
01304             $count = $new[$forum->id]->count;
01305         }
01306         if (array_key_exists($forum->id,$unread)) {
01307             $thisunread = $unread[$forum->id]->count;
01308             $showunread = true;
01309         }
01310         if ($count > 0 || $thisunread > 0) {
01311             $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
01312                 $forum->name.'</a></div>';
01313             $str .= '<div class="info"><span class="postsincelogin">';
01314             $str .= $count.' '.$strnumpostssince."</span>";
01315             if (!empty($showunread)) {
01316                 $str .= '<div class="unreadposts">'.$thisunread .' '.$strnumunread.'</div>';
01317             }
01318             $str .= '</div></div>';
01319         }
01320         if (!empty($str)) {
01321             if (!array_key_exists($forum->course,$htmlarray)) {
01322                 $htmlarray[$forum->course] = array();
01323             }
01324             if (!array_key_exists('forum',$htmlarray[$forum->course])) {
01325                 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
01326             }
01327             $htmlarray[$forum->course]['forum'] .= $str;
01328         }
01329     }
01330 }
01331 
01346 function forum_print_recent_activity($course, $viewfullnames, $timestart) {
01347     global $CFG, $USER, $DB, $OUTPUT;
01348 
01349     // do not use log table if possible, it may be huge and is expensive to join with other tables
01350 
01351     if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
01352                                               d.timestart, d.timeend, d.userid AS duserid,
01353                                               u.firstname, u.lastname, u.email, u.picture
01354                                          FROM {forum_posts} p
01355                                               JOIN {forum_discussions} d ON d.id = p.discussion
01356                                               JOIN {forum} f             ON f.id = d.forum
01357                                               JOIN {user} u              ON u.id = p.userid
01358                                         WHERE p.created > ? AND f.course = ?
01359                                      ORDER BY p.id ASC", array($timestart, $course->id))) { // order by initial posting date
01360          return false;
01361     }
01362 
01363     $modinfo =& get_fast_modinfo($course);
01364 
01365     $groupmodes = array();
01366     $cms    = array();
01367 
01368     $strftimerecent = get_string('strftimerecent');
01369 
01370     $printposts = array();
01371     foreach ($posts as $post) {
01372         if (!isset($modinfo->instances['forum'][$post->forum])) {
01373             // not visible
01374             continue;
01375         }
01376         $cm = $modinfo->instances['forum'][$post->forum];
01377         if (!$cm->uservisible) {
01378             continue;
01379         }
01380         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
01381 
01382         if (!has_capability('mod/forum:viewdiscussion', $context)) {
01383             continue;
01384         }
01385 
01386         if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
01387           and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
01388             if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
01389                 continue;
01390             }
01391         }
01392 
01393         $groupmode = groups_get_activity_groupmode($cm, $course);
01394 
01395         if ($groupmode) {
01396             if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
01397                 // oki (Open discussions have groupid -1)
01398             } else {
01399                 // separate mode
01400                 if (isguestuser()) {
01401                     // shortcut
01402                     continue;
01403                 }
01404 
01405                 if (is_null($modinfo->groups)) {
01406                     $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
01407                 }
01408 
01409                 if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
01410                     continue;
01411                 }
01412             }
01413         }
01414 
01415         $printposts[] = $post;
01416     }
01417     unset($posts);
01418 
01419     if (!$printposts) {
01420         return false;
01421     }
01422 
01423     echo $OUTPUT->heading(get_string('newforumposts', 'forum').':', 3);
01424     echo "\n<ul class='unlist'>\n";
01425 
01426     foreach ($printposts as $post) {
01427         $subjectclass = empty($post->parent) ? ' bold' : '';
01428 
01429         echo '<li><div class="head">'.
01430                '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
01431                '<div class="name">'.fullname($post, $viewfullnames).'</div>'.
01432              '</div>';
01433         echo '<div class="info'.$subjectclass.'">';
01434         if (empty($post->parent)) {
01435             echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
01436         } else {
01437             echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
01438         }
01439         $post->subject = break_up_long_words(format_string($post->subject, true));
01440         echo $post->subject;
01441         echo "</a>\"</div></li>\n";
01442     }
01443 
01444     echo "</ul>\n";
01445 
01446     return true;
01447 }
01448 
01458 function forum_get_user_grades($forum, $userid = 0) {
01459     global $CFG;
01460 
01461     require_once($CFG->dirroot.'/rating/lib.php');
01462 
01463     $ratingoptions = new stdClass;
01464     $ratingoptions->component = 'mod_forum';
01465     $ratingoptions->ratingarea = 'post';
01466 
01467     //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
01468     $ratingoptions->modulename = 'forum';
01469     $ratingoptions->moduleid   = $forum->id;
01470     $ratingoptions->userid = $userid;
01471     $ratingoptions->aggregationmethod = $forum->assessed;
01472     $ratingoptions->scaleid = $forum->scale;
01473     $ratingoptions->itemtable = 'forum_posts';
01474     $ratingoptions->itemtableusercolumn = 'userid';
01475 
01476     $rm = new rating_manager();
01477     return $rm->get_user_grades($ratingoptions);
01478 }
01479 
01490 function forum_update_grades($forum, $userid=0, $nullifnone=true) {
01491     global $CFG, $DB;
01492     require_once($CFG->libdir.'/gradelib.php');
01493 
01494     if (!$forum->assessed) {
01495         forum_grade_item_update($forum);
01496 
01497     } else if ($grades = forum_get_user_grades($forum, $userid)) {
01498         forum_grade_item_update($forum, $grades);
01499 
01500     } else if ($userid and $nullifnone) {
01501         $grade = new stdClass();
01502         $grade->userid   = $userid;
01503         $grade->rawgrade = NULL;
01504         forum_grade_item_update($forum, $grade);
01505 
01506     } else {
01507         forum_grade_item_update($forum);
01508     }
01509 }
01510 
01515 function forum_upgrade_grades() {
01516     global $DB;
01517 
01518     $sql = "SELECT COUNT('x')
01519               FROM {forum} f, {course_modules} cm, {modules} m
01520              WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
01521     $count = $DB->count_records_sql($sql);
01522 
01523     $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid
01524               FROM {forum} f, {course_modules} cm, {modules} m
01525              WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
01526     $rs = $DB->get_recordset_sql($sql);
01527     if ($rs->valid()) {
01528         $pbar = new progress_bar('forumupgradegrades', 500, true);
01529         $i=0;
01530         foreach ($rs as $forum) {
01531             $i++;
01532             upgrade_set_timeout(60*5); // set up timeout, may also abort execution
01533             forum_update_grades($forum, 0, false);
01534             $pbar->update($i, $count, "Updating Forum grades ($i/$count).");
01535         }
01536     }
01537     $rs->close();
01538 }
01539 
01551 function forum_grade_item_update($forum, $grades=NULL) {
01552     global $CFG;
01553     if (!function_exists('grade_update')) { //workaround for buggy PHP versions
01554         require_once($CFG->libdir.'/gradelib.php');
01555     }
01556 
01557     $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
01558 
01559     if (!$forum->assessed or $forum->scale == 0) {
01560         $params['gradetype'] = GRADE_TYPE_NONE;
01561 
01562     } else if ($forum->scale > 0) {
01563         $params['gradetype'] = GRADE_TYPE_VALUE;
01564         $params['grademax']  = $forum->scale;
01565         $params['grademin']  = 0;
01566 
01567     } else if ($forum->scale < 0) {
01568         $params['gradetype'] = GRADE_TYPE_SCALE;
01569         $params['scaleid']   = -$forum->scale;
01570     }
01571 
01572     if ($grades  === 'reset') {
01573         $params['reset'] = true;
01574         $grades = NULL;
01575     }
01576 
01577     return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
01578 }
01579 
01587 function forum_grade_item_delete($forum) {
01588     global $CFG;
01589     require_once($CFG->libdir.'/gradelib.php');
01590 
01591     return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
01592 }
01593 
01594 
01604 function forum_get_participants($forumid) {
01605 
01606     global $CFG, $DB;
01607 
01608     $params = array('forumid' => $forumid);
01609 
01610     //Get students from forum_subscriptions
01611     $sql = "SELECT DISTINCT u.id, u.id
01612               FROM {user} u,
01613                    {forum_subscriptions} s
01614              WHERE s.forum = :forumid AND
01615                    u.id = s.userid";
01616     $st_subscriptions = $DB->get_records_sql($sql, $params);
01617 
01618     //Get students from forum_posts
01619     $sql = "SELECT DISTINCT u.id, u.id
01620               FROM {user} u,
01621                    {forum_discussions} d,
01622                    {forum_posts} p
01623               WHERE d.forum = :forumid AND
01624                     p.discussion = d.id AND
01625                     u.id = p.userid";
01626     $st_posts = $DB->get_records_sql($sql, $params);
01627 
01628     //Get students from the ratings table
01629     $sql = "SELECT DISTINCT r.userid, r.userid AS id
01630               FROM {forum_discussions} d
01631               JOIN {forum_posts} p ON p.discussion = d.id
01632               JOIN {rating} r on r.itemid = p.id
01633              WHERE d.forum = :forumid AND
01634                    r.component = 'mod_forum' AND
01635                    r.ratingarea = 'post'";
01636     $st_ratings = $DB->get_records_sql($sql, $params);
01637 
01638     //Add st_posts to st_subscriptions
01639     if ($st_posts) {
01640         foreach ($st_posts as $st_post) {
01641             $st_subscriptions[$st_post->id] = $st_post;
01642         }
01643     }
01644     //Add st_ratings to st_subscriptions
01645     if ($st_ratings) {
01646         foreach ($st_ratings as $st_rating) {
01647             $st_subscriptions[$st_rating->id] = $st_rating;
01648         }
01649     }
01650     //Return st_subscriptions array (it contains an array of unique users)
01651     return ($st_subscriptions);
01652 }
01653 
01662 function forum_scale_used ($forumid,$scaleid) {
01663     global $DB;
01664     $return = false;
01665 
01666     $rec = $DB->get_record("forum",array("id" => "$forumid","scale" => "-$scaleid"));
01667 
01668     if (!empty($rec) && !empty($scaleid)) {
01669         $return = true;
01670     }
01671 
01672     return $return;
01673 }
01674 
01684 function forum_scale_used_anywhere($scaleid) {
01685     global $DB;
01686     if ($scaleid and $DB->record_exists('forum', array('scale' => -$scaleid))) {
01687         return true;
01688     } else {
01689         return false;
01690     }
01691 }
01692 
01693 // SQL FUNCTIONS ///////////////////////////////////////////////////////////
01694 
01704 function forum_get_post_full($postid) {
01705     global $CFG, $DB;
01706 
01707     return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
01708                              FROM {forum_posts} p
01709                                   JOIN {forum_discussions} d ON p.discussion = d.id
01710                                   LEFT JOIN {user} u ON p.userid = u.id
01711                             WHERE p.id = ?", array($postid));
01712 }
01713 
01723 function forum_get_discussion_posts($discussion, $sort, $forumid) {
01724     global $CFG, $DB;
01725 
01726     return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
01727                               FROM {forum_posts} p
01728                          LEFT JOIN {user} u ON p.userid = u.id
01729                              WHERE p.discussion = ?
01730                                AND p.parent > 0 $sort", array($discussion));
01731 }
01732 
01744 function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
01745     global $CFG, $DB, $USER;
01746 
01747     $tr_sel  = "";
01748     $tr_join = "";
01749     $params = array();
01750 
01751     if ($tracking) {
01752         $now = time();
01753         $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
01754         $tr_sel  = ", fr.id AS postread";
01755         $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)";
01756         $params[] = $USER->id;
01757     }
01758 
01759     $params[] = $discussionid;
01760     if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
01761                                      FROM {forum_posts} p
01762                                           LEFT JOIN {user} u ON p.userid = u.id
01763                                           $tr_join
01764                                     WHERE p.discussion = ?
01765                                  ORDER BY $sort", $params)) {
01766         return array();
01767     }
01768 
01769     foreach ($posts as $pid=>$p) {
01770         if ($tracking) {
01771             if (forum_tp_is_post_old($p)) {
01772                  $posts[$pid]->postread = true;
01773             }
01774         }
01775         if (!$p->parent) {
01776             continue;
01777         }
01778         if (!isset($posts[$p->parent])) {
01779             continue; // parent does not exist??
01780         }
01781         if (!isset($posts[$p->parent]->children)) {
01782             $posts[$p->parent]->children = array();
01783         }
01784         $posts[$p->parent]->children[$pid] =& $posts[$pid];
01785     }
01786 
01787     return $posts;
01788 }
01789 
01801 function forum_get_child_posts($parent, $forumid) {
01802     global $CFG, $DB;
01803 
01804     return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
01805                               FROM {forum_posts} p
01806                          LEFT JOIN {user} u ON p.userid = u.id
01807                              WHERE p.parent = ?
01808                           ORDER BY p.created ASC", array($parent));
01809 }
01810 
01824 function forum_get_readable_forums($userid, $courseid=0) {
01825 
01826     global $CFG, $DB, $USER;
01827     require_once($CFG->dirroot.'/course/lib.php');
01828 
01829     if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) {
01830         print_error('notinstalled', 'forum');
01831     }
01832 
01833     if ($courseid) {
01834         $courses = $DB->get_records('course', array('id' => $courseid));
01835     } else {
01836         // If no course is specified, then the user can see SITE + his courses.
01837         $courses1 = $DB->get_records('course', array('id' => SITEID));
01838         $courses2 = enrol_get_users_courses($userid, true, array('modinfo'));
01839         $courses = array_merge($courses1, $courses2);
01840     }
01841     if (!$courses) {
01842         return array();
01843     }
01844 
01845     $readableforums = array();
01846 
01847     foreach ($courses as $course) {
01848 
01849         $modinfo =& get_fast_modinfo($course);
01850         if (is_null($modinfo->groups)) {
01851             $modinfo->groups = groups_get_user_groups($course->id, $userid);
01852         }
01853 
01854         if (empty($modinfo->instances['forum'])) {
01855             // hmm, no forums?
01856             continue;
01857         }
01858 
01859         $courseforums = $DB->get_records('forum', array('course' => $course->id));
01860 
01861         foreach ($modinfo->instances['forum'] as $forumid => $cm) {
01862             if (!$cm->uservisible or !isset($courseforums[$forumid])) {
01863                 continue;
01864             }
01865             $context = get_context_instance(CONTEXT_MODULE, $cm->id);
01866             $forum = $courseforums[$forumid];
01867             $forum->context = $context;
01868             $forum->cm = $cm;
01869 
01870             if (!has_capability('mod/forum:viewdiscussion', $context)) {
01871                 continue;
01872             }
01873 
01875             if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
01876                 if (is_null($modinfo->groups)) {
01877                     $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
01878                 }
01879                 if (isset($modinfo->groups[$cm->groupingid])) {
01880                     $forum->onlygroups = $modinfo->groups[$cm->groupingid];
01881                     $forum->onlygroups[] = -1;
01882                 } else {
01883                     $forum->onlygroups = array(-1);
01884                 }
01885             }
01886 
01888             $forum->viewhiddentimedposts = true;
01889             if (!empty($CFG->forum_enabletimedposts)) {
01890                 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
01891                     $forum->viewhiddentimedposts = false;
01892                 }
01893             }
01894 
01896             if ($forum->type == 'qanda'
01897                     && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
01898 
01899                 // We need to check whether the user has posted in the qanda forum.
01900                 $forum->onlydiscussions = array();  // Holds discussion ids for the discussions
01901                                                     // the user is allowed to see in this forum.
01902                 if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
01903                     foreach ($discussionspostedin as $d) {
01904                         $forum->onlydiscussions[] = $d->id;
01905                     }
01906                 }
01907             }
01908 
01909             $readableforums[$forum->id] = $forum;
01910         }
01911 
01912         unset($modinfo);
01913 
01914     } // End foreach $courses
01915 
01916     return $readableforums;
01917 }
01918 
01933 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
01934                             &$totalcount, $extrasql='') {
01935     global $CFG, $DB, $USER;
01936     require_once($CFG->libdir.'/searchlib.php');
01937 
01938     $forums = forum_get_readable_forums($USER->id, $courseid);
01939 
01940     if (count($forums) == 0) {
01941         $totalcount = 0;
01942         return false;
01943     }
01944 
01945     $now = round(time(), -2); // db friendly
01946 
01947     $fullaccess = array();
01948     $where = array();
01949     $params = array();
01950 
01951     foreach ($forums as $forumid => $forum) {
01952         $select = array();
01953 
01954         if (!$forum->viewhiddentimedposts) {
01955             $select[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
01956             $params = array_merge($params, array('userid'.$forumid=>$USER->id, 'timestart'.$forumid=>$now, 'timeend'.$forumid=>$now));
01957         }
01958 
01959         $cm = $forum->cm;
01960         $context = $forum->context;
01961 
01962         if ($forum->type == 'qanda'
01963             && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
01964             if (!empty($forum->onlydiscussions)) {
01965                 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_');
01966                 $params = array_merge($params, $discussionid_params);
01967                 $select[] = "(d.id $discussionid_sql OR p.parent = 0)";
01968             } else {
01969                 $select[] = "p.parent = 0";
01970             }
01971         }
01972 
01973         if (!empty($forum->onlygroups)) {
01974             list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_');
01975             $params = array_merge($params, $groupid_params);
01976             $select[] = "d.groupid $groupid_sql";
01977         }
01978 
01979         if ($select) {
01980             $selects = implode(" AND ", $select);
01981             $where[] = "(d.forum = :forum{$forumid} AND $selects)";
01982             $params['forum'.$forumid] = $forumid;
01983         } else {
01984             $fullaccess[] = $forumid;
01985         }
01986     }
01987 
01988     if ($fullaccess) {
01989         list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula');
01990         $params = array_merge($params, $fullid_params);
01991         $where[] = "(d.forum $fullid_sql)";
01992     }
01993 
01994     $selectdiscussion = "(".implode(" OR ", $where).")";
01995 
01996     $messagesearch = '';
01997     $searchstring = '';
01998 
01999     // Need to concat these back together for parser to work.
02000     foreach($searchterms as $searchterm){
02001         if ($searchstring != '') {
02002             $searchstring .= ' ';
02003         }
02004         $searchstring .= $searchterm;
02005     }
02006 
02007     // We need to allow quoted strings for the search. The quotes *should* be stripped
02008     // by the parser, but this should be examined carefully for security implications.
02009     $searchstring = str_replace("\\\"","\"",$searchstring);
02010     $parser = new search_parser();
02011     $lexer = new search_lexer($parser);
02012 
02013     if ($lexer->parse($searchstring)) {
02014         $parsearray = $parser->get_parsed_array();
02015     // Experimental feature under 1.8! MDL-8830
02016     // Use alternative text searches if defined
02017     // This feature only works under mysql until properly implemented for other DBs
02018     // Requires manual creation of text index for forum_posts before enabling it:
02019     // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
02020     // Experimental feature under 1.8! MDL-8830
02021         if (!empty($CFG->forum_usetextsearches)) {
02022             list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
02023                                                  'p.userid', 'u.id', 'u.firstname',
02024                                                  'u.lastname', 'p.modified', 'd.forum');
02025         } else {
02026             list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject',
02027                                                  'p.userid', 'u.id', 'u.firstname',
02028                                                  'u.lastname', 'p.modified', 'd.forum');
02029         }
02030         $params = array_merge($params, $msparams);
02031     }
02032 
02033     $fromsql = "{forum_posts} p,
02034                   {forum_discussions} d,
02035                   {user} u";
02036 
02037     $selectsql = " $messagesearch
02038                AND p.discussion = d.id
02039                AND p.userid = u.id
02040                AND $selectdiscussion
02041                    $extrasql";
02042 
02043     $countsql = "SELECT COUNT(*)
02044                    FROM $fromsql
02045                   WHERE $selectsql";
02046 
02047     $searchsql = "SELECT p.*,
02048                          d.forum,
02049                          u.firstname,
02050                          u.lastname,
02051                          u.email,
02052                          u.picture,
02053                          u.imagealt,
02054                          u.email
02055                     FROM $fromsql
02056                    WHERE $selectsql
02057                 ORDER BY p.modified DESC";
02058 
02059     $totalcount = $DB->count_records_sql($countsql, $params);
02060 
02061     return $DB->get_records_sql($searchsql, $params, $limitfrom, $limitnum);
02062 }
02063 
02075 function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") {
02076     $options = new stdClass;
02077     $options->context = $context;
02078     $options->component = 'mod_forum';
02079     $options->ratingarea = 'post';
02080     $options->itemid = $postid;
02081     $options->sort = "ORDER BY $sort";
02082 
02083     $rm = new rating_manager();
02084     return $rm->get_all_ratings_for_item($options);
02085 }
02086 
02095 function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
02096     global $CFG, $DB;
02097 
02098     $params = array($starttime, $endtime);
02099     if (!empty($CFG->forum_enabletimedposts)) {
02100         if (empty($now)) {
02101             $now = time();
02102         }
02103         $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
02104         $params[] = $now;
02105         $params[] = $now;
02106     } else {
02107         $timedsql = "";
02108     }
02109 
02110     return $DB->get_records_sql("SELECT p.*, d.course, d.forum
02111                               FROM {forum_posts} p
02112                                    JOIN {forum_discussions} d ON d.id = p.discussion
02113                              WHERE p.mailed = 0
02114                                    AND p.created >= ?
02115                                    AND (p.created < ? OR p.mailnow = 1)
02116                                    $timedsql
02117                           ORDER BY p.modified ASC", $params);
02118 }
02119 
02129 function forum_mark_old_posts_as_mailed($endtime, $now=null) {
02130     global $CFG, $DB;
02131     if (empty($now)) {
02132         $now = time();
02133     }
02134 
02135     if (empty($CFG->forum_enabletimedposts)) {
02136         return $DB->execute("UPDATE {forum_posts}
02137                                SET mailed = '1'
02138                              WHERE (created < ? OR mailnow = 1)
02139                                    AND mailed = 0", array($endtime));
02140 
02141     } else {
02142         return $DB->execute("UPDATE {forum_posts}
02143                                SET mailed = '1'
02144                              WHERE discussion NOT IN (SELECT d.id
02145                                                         FROM {forum_discussions} d
02146                                                        WHERE d.timestart > ?)
02147                                    AND (created < ? OR mailnow = 1)
02148                                    AND mailed = 0", array($now, $endtime));
02149     }
02150 }
02151 
02160 function forum_get_user_posts($forumid, $userid) {
02161     global $CFG, $DB;
02162 
02163     $timedsql = "";
02164     $params = array($forumid, $userid);
02165 
02166     if (!empty($CFG->forum_enabletimedposts)) {
02167         $cm = get_coursemodule_from_instance('forum', $forumid);
02168         if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
02169             $now = time();
02170             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
02171             $params[] = $now;
02172             $params[] = $now;
02173         }
02174     }
02175 
02176     return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
02177                               FROM {forum} f
02178                                    JOIN {forum_discussions} d ON d.forum = f.id
02179                                    JOIN {forum_posts} p       ON p.discussion = d.id
02180                                    JOIN {user} u              ON u.id = p.userid
02181                              WHERE f.id = ?
02182                                    AND p.userid = ?
02183                                    $timedsql
02184                           ORDER BY p.modified ASC", $params);
02185 }
02186 
02197 function forum_get_user_involved_discussions($forumid, $userid) {
02198     global $CFG, $DB;
02199 
02200     $timedsql = "";
02201     $params = array($forumid, $userid);
02202     if (!empty($CFG->forum_enabletimedposts)) {
02203         $cm = get_coursemodule_from_instance('forum', $forumid);
02204         if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
02205             $now = time();
02206             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
02207             $params[] = $now;
02208             $params[] = $now;
02209         }
02210     }
02211 
02212     return $DB->get_records_sql("SELECT DISTINCT d.*
02213                               FROM {forum} f
02214                                    JOIN {forum_discussions} d ON d.forum = f.id
02215                                    JOIN {forum_posts} p       ON p.discussion = d.id
02216                              WHERE f.id = ?
02217                                    AND p.userid = ?
02218                                    $timedsql", $params);
02219 }
02220 
02230 function forum_count_user_posts($forumid, $userid) {
02231     global $CFG, $DB;
02232 
02233     $timedsql = "";
02234     $params = array($forumid, $userid);
02235     if (!empty($CFG->forum_enabletimedposts)) {
02236         $cm = get_coursemodule_from_instance('forum', $forumid);
02237         if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
02238             $now = time();
02239             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
02240             $params[] = $now;
02241             $params[] = $now;
02242         }
02243     }
02244 
02245     return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
02246                              FROM {forum} f
02247                                   JOIN {forum_discussions} d ON d.forum = f.id
02248                                   JOIN {forum_posts} p       ON p.discussion = d.id
02249                                   JOIN {user} u              ON u.id = p.userid
02250                             WHERE f.id = ?
02251                                   AND p.userid = ?
02252                                   $timedsql", $params);
02253 }
02254 
02263 function forum_get_post_from_log($log) {
02264     global $CFG, $DB;
02265 
02266     if ($log->action == "add post") {
02267 
02268         return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
02269                                            u.firstname, u.lastname, u.email, u.picture
02270                                  FROM {forum_discussions} d,
02271                                       {forum_posts} p,
02272                                       {forum} f,
02273                                       {user} u
02274                                 WHERE p.id = ?
02275                                   AND d.id = p.discussion
02276                                   AND p.userid = u.id
02277                                   AND u.deleted <> '1'
02278                                   AND f.id = d.forum", array($log->info));
02279 
02280 
02281     } else if ($log->action == "add discussion") {
02282 
02283         return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
02284                                            u.firstname, u.lastname, u.email, u.picture
02285                                  FROM {forum_discussions} d,
02286                                       {forum_posts} p,
02287                                       {forum} f,
02288                                       {user} u
02289                                 WHERE d.id = ?
02290                                   AND d.firstpost = p.id
02291                                   AND p.userid = u.id
02292                                   AND u.deleted <> '1'
02293                                   AND f.id = d.forum", array($log->info));
02294     }
02295     return NULL;
02296 }
02297 
02306 function forum_get_firstpost_from_discussion($discussionid) {
02307     global $CFG, $DB;
02308 
02309     return $DB->get_record_sql("SELECT p.*
02310                              FROM {forum_discussions} d,
02311                                   {forum_posts} p
02312                             WHERE d.id = ?
02313                               AND d.firstpost = p.id ", array($discussionid));
02314 }
02315 
02328 function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
02329     global $CFG, $DB;
02330 
02331     if ($limit > 0) {
02332         $limitfrom = 0;
02333         $limitnum  = $limit;
02334     } else if ($page != -1) {
02335         $limitfrom = $page*$perpage;
02336         $limitnum  = $perpage;
02337     } else {
02338         $limitfrom = 0;
02339         $limitnum  = 0;
02340     }
02341 
02342     if ($forumsort == "") {
02343         $orderby = "";
02344         $groupby = "";
02345 
02346     } else {
02347         $orderby = "ORDER BY $forumsort";
02348         $groupby = ", ".strtolower($forumsort);
02349         $groupby = str_replace('desc', '', $groupby);
02350         $groupby = str_replace('asc', '', $groupby);
02351     }
02352 
02353     if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
02354         $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
02355                   FROM {forum_posts} p
02356                        JOIN {forum_discussions} d ON p.discussion = d.id
02357                  WHERE p.parent > 0 AND d.forum = ?
02358               GROUP BY p.discussion";
02359         return $DB->get_records_sql($sql, array($forumid));
02360 
02361     } else {
02362         $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
02363                   FROM {forum_posts} p
02364                        JOIN {forum_discussions} d ON p.discussion = d.id
02365                  WHERE d.forum = ?
02366               GROUP BY p.discussion $groupby
02367               $orderby";
02368         return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum);
02369     }
02370 }
02371 
02382 function forum_count_discussions($forum, $cm, $course) {
02383     global $CFG, $DB, $USER;
02384 
02385     static $cache = array();
02386 
02387     $now = round(time(), -2); // db cache friendliness
02388 
02389     $params = array($course->id);
02390 
02391     if (!isset($cache[$course->id])) {
02392         if (!empty($CFG->forum_enabletimedposts)) {
02393             $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
02394             $params[] = $now;
02395             $params[] = $now;
02396         } else {
02397             $timedsql = "";
02398         }
02399 
02400         $sql = "SELECT f.id, COUNT(d.id) as dcount
02401                   FROM {forum} f
02402                        JOIN {forum_discussions} d ON d.forum = f.id
02403                  WHERE f.course = ?
02404                        $timedsql
02405               GROUP BY f.id";
02406 
02407         if ($counts = $DB->get_records_sql($sql, $params)) {
02408             foreach ($counts as $count) {
02409                 $counts[$count->id] = $count->dcount;
02410             }
02411             $cache[$course->id] = $counts;
02412         } else {
02413             $cache[$course->id] = array();
02414         }
02415     }
02416 
02417     if (empty($cache[$course->id][$forum->id])) {
02418         return 0;
02419     }
02420 
02421     $groupmode = groups_get_activity_groupmode($cm, $course);
02422 
02423     if ($groupmode != SEPARATEGROUPS) {
02424         return $cache[$course->id][$forum->id];
02425     }
02426 
02427     if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
02428         return $cache[$course->id][$forum->id];
02429     }
02430 
02431     require_once($CFG->dirroot.'/course/lib.php');
02432 
02433     $modinfo =& get_fast_modinfo($course);
02434     if (is_null($modinfo->groups)) {
02435         $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
02436     }
02437 
02438     if (array_key_exists($cm->groupingid, $modinfo->groups)) {
02439         $mygroups = $modinfo->groups[$cm->groupingid];
02440     } else {
02441         $mygroups = false; // Will be set below
02442     }
02443 
02444     // add all groups posts
02445     if (empty($mygroups)) {
02446         $mygroups = array(-1=>-1);
02447     } else {
02448         $mygroups[-1] = -1;
02449     }
02450 
02451     list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups);
02452     $params[] = $forum->id;
02453 
02454     if (!empty($CFG->forum_enabletimedposts)) {
02455         $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
02456         $params[] = $now;
02457         $params[] = $now;
02458     } else {
02459         $timedsql = "";
02460     }
02461 
02462     $sql = "SELECT COUNT(d.id)
02463               FROM {forum_discussions} d
02464              WHERE d.groupid $mygroups_sql AND d.forum = ?
02465                    $timedsql";
02466 
02467     return $DB->get_field_sql($sql, $params);
02468 }
02469 
02479 function forum_count_unrated_posts($discussionid, $userid) {
02480     global $CFG, $DB;
02481 
02482     $sql = "SELECT COUNT(*) as num
02483               FROM {forum_posts}
02484              WHERE parent > 0
02485                AND discussion = :discussionid
02486                AND userid <> :userid";
02487     $params = array('discussionid' => $discussionid, 'userid' => $userid);
02488     $posts = $DB->get_record_sql($sql, $params);
02489     if ($posts) {
02490         $sql = "SELECT count(*) as num
02491                   FROM {forum_posts} p,
02492                        {rating} r
02493                  WHERE p.discussion = :discussionid AND
02494                        p.id = r.itemid AND
02495                        r.userid = userid AND
02496                        r.component = 'mod_forum' AND
02497                        r.ratingarea = 'post'";
02498         $rated = $DB->get_record_sql($sql, $params);
02499         if ($rated) {
02500             if ($posts->num > $rated->num) {
02501                 return $posts->num - $rated->num;
02502             } else {
02503                 return 0;    // Just in case there was a counting error
02504             }
02505         } else {
02506             return $posts->num;
02507         }
02508     } else {
02509         return 0;
02510     }
02511 }
02512 
02531 function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
02532     global $CFG, $DB, $USER;
02533 
02534     $timelimit = '';
02535 
02536     $now = round(time(), -2);
02537     $params = array($cm->instance);
02538 
02539     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
02540 
02541     if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { 
02542         return array();
02543     }
02544 
02545     if (!empty($CFG->forum_enabletimedposts)) { 
02546 
02547         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
02548             $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
02549             $params[] = $now;
02550             $params[] = $now;
02551             if (isloggedin()) {
02552                 $timelimit .= " OR d.userid = ?";
02553                 $params[] = $USER->id;
02554             }
02555             $timelimit .= ")";
02556         }
02557     }
02558 
02559     if ($limit > 0) {
02560         $limitfrom = 0;
02561         $limitnum  = $limit;
02562     } else if ($page != -1) {
02563         $limitfrom = $page*$perpage;
02564         $limitnum  = $perpage;
02565     } else {
02566         $limitfrom = 0;
02567         $limitnum  = 0;
02568     }
02569 
02570     $groupmode    = groups_get_activity_groupmode($cm);
02571     $currentgroup = groups_get_activity_group($cm);
02572 
02573     if ($groupmode) {
02574         if (empty($modcontext)) {
02575             $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
02576         }
02577 
02578         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
02579             if ($currentgroup) {
02580                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
02581                 $params[] = $currentgroup;
02582             } else {
02583                 $groupselect = "";
02584             }
02585 
02586         } else {
02587             //seprate groups without access all
02588             if ($currentgroup) {
02589                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
02590                 $params[] = $currentgroup;
02591             } else {
02592                 $groupselect = "AND d.groupid = -1";
02593             }
02594         }
02595     } else {
02596         $groupselect = "";
02597     }
02598 
02599 
02600     if (empty($forumsort)) {
02601         $forumsort = "d.timemodified DESC";
02602     }
02603     if (empty($fullpost)) {
02604         $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
02605     } else {
02606         $postdata = "p.*";
02607     }
02608 
02609     if (empty($userlastmodified)) {  // We don't need to know this
02610         $umfields = "";
02611         $umtable  = "";
02612     } else {
02613         $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
02614         $umtable  = " LEFT JOIN {user} um ON (d.usermodified = um.id)";
02615     }
02616 
02617     $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
02618                    u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
02619               FROM {forum_discussions} d
02620                    JOIN {forum_posts} p ON p.discussion = d.id
02621                    JOIN {user} u ON p.userid = u.id
02622                    $umtable
02623              WHERE d.forum = ? AND p.parent = 0
02624                    $timelimit $groupselect
02625           ORDER BY $forumsort";
02626     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
02627 }
02628 
02639 function forum_get_discussions_unread($cm) {
02640     global $CFG, $DB, $USER;
02641 
02642     $now = round(time(), -2);
02643     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
02644 
02645     $params = array();
02646     $groupmode    = groups_get_activity_groupmode($cm);
02647     $currentgroup = groups_get_activity_group($cm);
02648 
02649     if ($groupmode) {
02650         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
02651 
02652         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
02653             if ($currentgroup) {
02654                 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
02655                 $params['currentgroup'] = $currentgroup;
02656             } else {
02657                 $groupselect = "";
02658             }
02659 
02660         } else {
02661             //separate groups without access all
02662             if ($currentgroup) {
02663                 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
02664                 $params['currentgroup'] = $currentgroup;
02665             } else {
02666                 $groupselect = "AND d.groupid = -1";
02667             }
02668         }
02669     } else {
02670         $groupselect = "";
02671     }
02672 
02673     if (!empty($CFG->forum_enabletimedposts)) {
02674         $timedsql = "AND d.timestart < :now1 AND (d.timeend = 0 OR d.timeend > :now2)";
02675         $params['now1'] = $now;
02676         $params['now2'] = $now;
02677     } else {
02678         $timedsql = "";
02679     }
02680 
02681     $sql = "SELECT d.id, COUNT(p.id) AS unread
02682               FROM {forum_discussions} d
02683                    JOIN {forum_posts} p     ON p.discussion = d.id
02684                    LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id)
02685              WHERE d.forum = {$cm->instance}
02686                    AND p.modified >= :cutoffdate AND r.id is NULL
02687                    $groupselect
02688                    $timedsql
02689           GROUP BY d.id";
02690     $params['cutoffdate'] = $cutoffdate;
02691 
02692     if ($unreads = $DB->get_records_sql($sql, $params)) {
02693         foreach ($unreads as $unread) {
02694             $unreads[$unread->id] = $unread->unread;
02695         }
02696         return $unreads;
02697     } else {
02698         return array();
02699     }
02700 }
02701 
02711 function forum_get_discussions_count($cm) {
02712     global $CFG, $DB, $USER;
02713 
02714     $now = round(time(), -2);
02715     $params = array($cm->instance);
02716     $groupmode    = groups_get_activity_groupmode($cm);
02717     $currentgroup = groups_get_activity_group($cm);
02718 
02719     if ($groupmode) {
02720         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
02721 
02722         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
02723             if ($currentgroup) {
02724                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
02725                 $params[] = $currentgroup;
02726             } else {
02727                 $groupselect = "";
02728             }
02729 
02730         } else {
02731             //seprate groups without access all
02732             if ($currentgroup) {
02733                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
02734                 $params[] = $currentgroup;
02735             } else {
02736                 $groupselect = "AND d.groupid = -1";
02737             }
02738         }
02739     } else {
02740         $groupselect = "";
02741     }
02742 
02743     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
02744 
02745     $timelimit = "";
02746 
02747     if (!empty($CFG->forum_enabletimedposts)) {
02748 
02749         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
02750 
02751         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
02752             $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
02753             $params[] = $now;
02754             $params[] = $now;
02755             if (isloggedin()) {
02756                 $timelimit .= " OR d.userid = ?";
02757                 $params[] = $USER->id;
02758             }
02759             $timelimit .= ")";
02760         }
02761     }
02762 
02763     $sql = "SELECT COUNT(d.id)
02764               FROM {forum_discussions} d
02765                    JOIN {forum_posts} p ON p.discussion = d.id
02766              WHERE d.forum = ? AND p.parent = 0
02767                    $groupselect $timelimit";
02768 
02769     return $DB->get_field_sql($sql, $params);
02770 }
02771 
02772 
02785 function forum_get_user_discussions($courseid, $userid, $groupid=0) {
02786     global $CFG, $DB;
02787     $params = array($courseid, $userid);
02788     if ($groupid) {
02789         $groupselect = " AND d.groupid = ? ";
02790         $params[] = $groupid;
02791     } else  {
02792         $groupselect = "";
02793     }
02794 
02795     return $DB->get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
02796                                    f.type as forumtype, f.name as forumname, f.id as forumid
02797                               FROM {forum_discussions} d,
02798                                    {forum_posts} p,
02799                                    {user} u,
02800                                    {forum} f
02801                              WHERE d.course = ?
02802                                AND p.discussion = d.id
02803                                AND p.parent = 0
02804                                AND p.userid = u.id
02805                                AND u.id = ?
02806                                AND d.forum = f.id $groupselect
02807                           ORDER BY p.created DESC", $params);
02808 }
02809 
02819 function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
02820     global $DB;
02821 
02822     // only active enrolled users or everybody on the frontpage
02823     list($esql, $params) = get_enrolled_sql($forumcontext, '', $groupid, true);
02824 
02825     $sql = "SELECT $fields
02826               FROM {user} u
02827               JOIN ($esql) je ON je.id = u.id";
02828     if ($sort) {
02829         $sql = "$sql ORDER BY $sort";
02830     } else {
02831         $sql = "$sql ORDER BY u.lastname ASC, u.firstname ASC";
02832     }
02833 
02834     return $DB->get_records_sql($sql, $params);
02835 }
02836 
02849 function forum_subscribed_users($course, $forum, $groupid=0, $context = null, $fields = null) {
02850     global $CFG, $DB;
02851 
02852     if (empty($fields)) {
02853         $fields ="u.id,
02854                   u.username,
02855                   u.firstname,
02856                   u.lastname,
02857                   u.maildisplay,
02858                   u.mailformat,
02859                   u.maildigest,
02860                   u.imagealt,
02861                   u.email,
02862                   u.emailstop,
02863                   u.city,
02864                   u.country,
02865                   u.lastaccess,
02866                   u.lastlogin,
02867                   u.picture,
02868                   u.timezone,
02869                   u.theme,
02870                   u.lang,
02871                   u.trackforums,
02872                   u.mnethostid";
02873     }
02874 
02875     if (empty($context)) {
02876         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
02877         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
02878     }
02879 
02880     if (forum_is_forcesubscribed($forum)) {
02881         $results = forum_get_potential_subscribers($context, $groupid, $fields, "u.email ASC");
02882 
02883     } else {
02884         // only active enrolled users or everybody on the frontpage
02885         list($esql, $params) = get_enrolled_sql($context, '', $groupid, true);
02886         $params['forumid'] = $forum->id;
02887         $results = $DB->get_records_sql("SELECT $fields
02888                                            FROM {user} u
02889                                            JOIN ($esql) je ON je.id = u.id
02890                                            JOIN {forum_subscriptions} s ON s.userid = u.id
02891                                           WHERE s.forum = :forumid
02892                                        ORDER BY u.email ASC", $params);
02893     }
02894 
02895     // Guest user should never be subscribed to a forum.
02896     unset($results[$CFG->siteguest]);
02897 
02898     return $results;
02899 }
02900 
02901 
02902 
02903 // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
02904 
02905 
02912 function forum_get_course_forum($courseid, $type) {
02913 // How to set up special 1-per-course forums
02914     global $CFG, $DB, $OUTPUT;
02915 
02916     if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $type), "id ASC")) {
02917         // There should always only be ONE, but with the right combination of
02918         // errors there might be more.  In this case, just return the oldest one (lowest ID).
02919         foreach ($forums as $forum) {
02920             return $forum;   // ie the first one
02921         }
02922     }
02923 
02924     // Doesn't exist, so create one now.
02925     $forum = new stdClass();
02926     $forum->course = $courseid;
02927     $forum->type = "$type";
02928     switch ($forum->type) {
02929         case "news":
02930             $forum->name  = get_string("namenews", "forum");
02931             $forum->intro = get_string("intronews", "forum");
02932             $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
02933             $forum->assessed = 0;
02934             if ($courseid == SITEID) {
02935                 $forum->name  = get_string("sitenews");
02936                 $forum->forcesubscribe = 0;
02937             }
02938             break;
02939         case "social":
02940             $forum->name  = get_string("namesocial", "forum");
02941             $forum->intro = get_string("introsocial", "forum");
02942             $forum->assessed = 0;
02943             $forum->forcesubscribe = 0;
02944             break;
02945         case "blog":
02946             $forum->name = get_string('blogforum', 'forum');
02947             $forum->intro = get_string('introblog', 'forum');
02948             $forum->assessed = 0;
02949             $forum->forcesubscribe = 0;
02950             break;
02951         default:
02952             echo $OUTPUT->notification("That forum type doesn't exist!");
02953             return false;
02954             break;
02955     }
02956 
02957     $forum->timemodified = time();
02958     $forum->id = $DB->insert_record("forum", $forum);
02959 
02960     if (! $module = $DB->get_record("modules", array("name" => "forum"))) {
02961         echo $OUTPUT->notification("Could not find forum module!!");
02962         return false;
02963     }
02964     $mod = new stdClass();
02965     $mod->course = $courseid;
02966     $mod->module = $module->id;
02967     $mod->instance = $forum->id;
02968     $mod->section = 0;
02969     if (! $mod->coursemodule = add_course_module($mod) ) {   // assumes course/lib.php is loaded
02970         echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'");
02971         return false;
02972     }
02973     if (! $sectionid = add_mod_to_section($mod) ) {   // assumes course/lib.php is loaded
02974         echo $OUTPUT->notification("Could not add the new course module to that section");
02975         return false;
02976     }
02977     $DB->set_field("course_modules", "section", $sectionid, array("id" => $mod->coursemodule));
02978 
02979     include_once("$CFG->dirroot/course/lib.php");
02980     rebuild_course_cache($courseid);
02981 
02982     return $DB->get_record("forum", array("id" => "$forum->id"));
02983 }
02984 
02985 
03005 function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto,
03006                               $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
03007 
03008     global $CFG, $OUTPUT;
03009 
03010     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
03011 
03012     if (!isset($userto->viewfullnames[$forum->id])) {
03013         $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
03014     } else {
03015         $viewfullnames = $userto->viewfullnames[$forum->id];
03016     }
03017 
03018     // add absolute file links
03019     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
03020 
03021     // format the post body
03022     $options = new stdClass();
03023     $options->para = true;
03024     $formattedtext = format_text($post->message, $post->messageformat, $options, $course->id);
03025 
03026     $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
03027 
03028     $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
03029     $output .= $OUTPUT->user_picture($userfrom, array('courseid'=>$course->id));
03030     $output .= '</td>';
03031 
03032     if ($post->parent) {
03033         $output .= '<td class="topic">';
03034     } else {
03035         $output .= '<td class="topic starter">';
03036     }
03037     $output .= '<div class="subject">'.format_string($post->subject).'</div>';
03038 
03039     $fullname = fullname($userfrom, $viewfullnames);
03040     $by = new stdClass();
03041     $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
03042     $by->date = userdate($post->modified, '', $userto->timezone);
03043     $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
03044 
03045     $output .= '</td></tr>';
03046 
03047     $output .= '<tr><td class="left side" valign="top">';
03048 
03049     if (isset($userfrom->groups)) {
03050         $groups = $userfrom->groups[$forum->id];
03051     } else {
03052         $groups = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
03053     }
03054 
03055     if ($groups) {
03056         $output .= print_group_picture($groups, $course->id, false, true, true);
03057     } else {
03058         $output .= '&nbsp;';
03059     }
03060 
03061     $output .= '</td><td class="content">';
03062 
03063     $attachments = forum_print_attachments($post, $cm, 'html');
03064     if ($attachments !== '') {
03065         $output .= '<div class="attachments">';
03066         $output .= $attachments;
03067         $output .= '</div>';
03068     }
03069 
03070     $output .= $formattedtext;
03071 
03072 // Commands
03073     $commands = array();
03074 
03075     if ($post->parent) {
03076         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
03077                       $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
03078     }
03079 
03080     if ($reply) {
03081         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
03082                       get_string('reply', 'forum').'</a>';
03083     }
03084 
03085     $output .= '<div class="commands">';
03086     $output .= implode(' | ', $commands);
03087     $output .= '</div>';
03088 
03089 // Context link to post if required
03090     if ($link) {
03091         $output .= '<div class="link">';
03092         $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
03093                      get_string('postincontext', 'forum').'</a>';
03094         $output .= '</div>';
03095     }
03096 
03097     if ($footer) {
03098         $output .= '<div class="footer">'.$footer.'</div>';
03099     }
03100     $output .= '</td></tr></table>'."\n\n";
03101 
03102     return $output;
03103 }
03104 
03136 function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
03137                           $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) {
03138     global $USER, $CFG, $OUTPUT;
03139 
03140     require_once($CFG->libdir . '/filelib.php');
03141 
03142     // String cache
03143     static $str;
03144 
03145     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
03146 
03147     $post->course = $course->id;
03148     $post->forum  = $forum->id;
03149     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
03150 
03151     // caching
03152     if (!isset($cm->cache)) {
03153         $cm->cache = new stdClass;
03154     }
03155 
03156     if (!isset($cm->cache->caps)) {
03157         $cm->cache->caps = array();
03158         $cm->cache->caps['mod/forum:viewdiscussion']   = has_capability('mod/forum:viewdiscussion', $modcontext);
03159         $cm->cache->caps['moodle/site:viewfullnames']  = has_capability('moodle/site:viewfullnames', $modcontext);
03160         $cm->cache->caps['mod/forum:editanypost']      = has_capability('mod/forum:editanypost', $modcontext);
03161         $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext);
03162         $cm->cache->caps['mod/forum:deleteownpost']    = has_capability('mod/forum:deleteownpost', $modcontext);
03163         $cm->cache->caps['mod/forum:deleteanypost']    = has_capability('mod/forum:deleteanypost', $modcontext);
03164         $cm->cache->caps['mod/forum:viewanyrating']    = has_capability('mod/forum:viewanyrating', $modcontext);
03165         $cm->cache->caps['mod/forum:exportpost']       = has_capability('mod/forum:exportpost', $modcontext);
03166         $cm->cache->caps['mod/forum:exportownpost']    = has_capability('mod/forum:exportownpost', $modcontext);
03167     }
03168 
03169     if (!isset($cm->uservisible)) {
03170         $cm->uservisible = coursemodule_visible_for_user($cm);
03171     }
03172 
03173     if ($istracked && is_null($postisread)) {
03174         $postisread = forum_tp_is_post_read($USER->id, $post);
03175     }
03176 
03177     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
03178         $output = '';
03179         if (!$dummyifcantsee) {
03180             if ($return) {
03181                 return $output;
03182             }
03183             echo $output;
03184             return;
03185         }
03186         $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id));
03187         $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix'));
03188         $output .= html_writer::start_tag('div', array('class'=>'row header'));
03189         $output .= html_writer::tag('div', '', array('class'=>'left picture')); // Picture
03190         if ($post->parent) {
03191             $output .= html_writer::start_tag('div', array('class'=>'topic'));
03192         } else {
03193             $output .= html_writer::start_tag('div', array('class'=>'topic starter'));
03194         }
03195         $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class'=>'subject')); // Subject
03196         $output .= html_writer::tag('div', get_string('forumauthorhidden','forum'), array('class'=>'author')); // author
03197         $output .= html_writer::end_tag('div');
03198         $output .= html_writer::end_tag('div'); // row
03199         $output .= html_writer::start_tag('div', array('class'=>'row'));
03200         $output .= html_writer::tag('div', '&nbsp;', array('class'=>'left side')); // Groups
03201         $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content
03202         $output .= html_writer::end_tag('div'); // row
03203         $output .= html_writer::end_tag('div'); // forumpost
03204 
03205         if ($return) {
03206             return $output;
03207         }
03208         echo $output;
03209         return;
03210     }
03211 
03212     if (empty($str)) {
03213         $str = new stdClass;
03214         $str->edit         = get_string('edit', 'forum');
03215         $str->delete       = get_string('delete', 'forum');
03216         $str->reply        = get_string('reply', 'forum');
03217         $str->parent       = get_string('parent', 'forum');
03218         $str->pruneheading = get_string('pruneheading', 'forum');
03219         $str->prune        = get_string('prune', 'forum');
03220         $str->displaymode     = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
03221         $str->markread     = get_string('markread', 'forum');
03222         $str->markunread   = get_string('markunread', 'forum');
03223     }
03224 
03225     $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion));
03226 
03227     // Build an object that represents the posting user
03228     $postuser = new stdClass;
03229     $postuser->id        = $post->userid;
03230     $postuser->firstname = $post->firstname;
03231     $postuser->lastname  = $post->lastname;
03232     $postuser->imagealt  = $post->imagealt;
03233     $postuser->picture   = $post->picture;
03234     $postuser->email     = $post->email;
03235     // Some handy things for later on
03236     $postuser->fullname    = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']);
03237     $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id));
03238 
03239     // Prepare the groups the posting user belongs to
03240     if (isset($cm->cache->usersgroups)) {
03241         $groups = array();
03242         if (isset($cm->cache->usersgroups[$post->userid])) {
03243             foreach ($cm->cache->usersgroups[$post->userid] as $gid) {
03244                 $groups[$gid] = $cm->cache->groups[$gid];
03245             }
03246         }
03247     } else {
03248         $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid);
03249     }
03250 
03251     // Prepare the attachements for the post, files then images
03252     list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages');
03253 
03254     // Determine if we need to shorten this post
03255     $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost));
03256 
03257 
03258     // Prepare an array of commands
03259     $commands = array();
03260 
03261     // SPECIAL CASE: The front page can display a news item post to non-logged in users.
03262     // Don't display the mark read / unread controls in this case.
03263     if ($istracked && $CFG->forum_usermarksread && isloggedin()) {
03264         $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread'));
03265         $text = $str->markunread;
03266         if (!$postisread) {
03267             $url->param('mark', 'read');
03268             $text = $str->markread;
03269         }
03270         if ($str->displaymode == FORUM_MODE_THREADED) {
03271             $url->param('parent', $post->parent);
03272         } else {
03273             $url->set_anchor('p'.$post->id);
03274         }
03275         $commands[] = array('url'=>$url, 'text'=>$text);
03276     }
03277 
03278     // Zoom in to the parent specifically
03279     if ($post->parent) {
03280         $url = new moodle_url($discussionlink);
03281         if ($str->displaymode == FORUM_MODE_THREADED) {
03282             $url->param('parent', $post->parent);
03283         } else {
03284             $url->set_anchor('p'.$post->parent);
03285         }
03286         $commands[] = array('url'=>$url, 'text'=>$str->parent);
03287     }
03288 
03289     // Hack for allow to edit news posts those are not displayed yet until they are displayed
03290     $age = time() - $post->created;
03291     if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) {
03292         $age = 0;
03293     }
03294     if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) {
03295         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit);
03296     }
03297 
03298     if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') {
03299         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading);
03300     }
03301 
03302     if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) {
03303         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete);
03304     }
03305 
03306     if ($reply) {
03307         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('reply'=>$post->id)), 'text'=>$str->reply);
03308     }
03309 
03310     if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) {
03311         $p = array('postid' => $post->id);
03312         require_once($CFG->libdir.'/portfoliolib.php');
03313         $button = new portfolio_add_button();
03314         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), '/mod/forum/locallib.php');
03315         if (empty($attachments)) {
03316             $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
03317         } else {
03318             $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
03319         }
03320 
03321         $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
03322         if (!empty($porfoliohtml)) {
03323             $commands[] = $porfoliohtml;
03324         }
03325     }
03326     // Finished building commands
03327 
03328 
03329     // Begin output
03330 
03331     $output  = '';
03332 
03333     if ($istracked) {
03334         if ($postisread) {
03335             $forumpostclass = ' read';
03336         } else {
03337             $forumpostclass = ' unread';
03338             $output .= html_writer::tag('a', '', array('name'=>'unread'));
03339         }
03340     } else {
03341         // ignore trackign status if not tracked or tracked param missing
03342         $forumpostclass = '';
03343     }
03344 
03345     $topicclass = '';
03346     if (empty($post->parent)) {
03347         $topicclass = ' firstpost starter';
03348     }
03349 
03350     $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id));
03351     $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix'.$forumpostclass.$topicclass));
03352     $output .= html_writer::start_tag('div', array('class'=>'row header clearfix'));
03353     $output .= html_writer::start_tag('div', array('class'=>'left picture'));
03354     $output .= $OUTPUT->user_picture($postuser, array('courseid'=>$course->id));
03355     $output .= html_writer::end_tag('div');
03356 
03357 
03358     $output .= html_writer::start_tag('div', array('class'=>'topic'.$topicclass));
03359 
03360     $postsubject = $post->subject;
03361     if (empty($post->subjectnoformat)) {
03362         $postsubject = format_string($postsubject);
03363     }
03364     $output .= html_writer::tag('div', $postsubject, array('class'=>'subject'));
03365 
03366     $by = new stdClass();
03367     $by->name = html_writer::link($postuser->profilelink, $postuser->fullname);
03368     $by->date = userdate($post->modified);
03369     $output .= html_writer::tag('div', get_string('bynameondate', 'forum', $by), array('class'=>'author'));
03370 
03371     $output .= html_writer::end_tag('div'); //topic
03372     $output .= html_writer::end_tag('div'); //row
03373 
03374     $output .= html_writer::start_tag('div', array('class'=>'row maincontent clearfix'));
03375     $output .= html_writer::start_tag('div', array('class'=>'left'));
03376 
03377     $groupoutput = '';
03378     if ($groups) {
03379         $groupoutput = print_group_picture($groups, $course->id, false, true, true);
03380     }
03381     if (empty($groupoutput)) {
03382         $groupoutput = '&nbsp;';
03383     }
03384     $output .= html_writer::tag('div', $groupoutput, array('class'=>'grouppictures'));
03385 
03386     $output .= html_writer::end_tag('div'); //left side
03387     $output .= html_writer::start_tag('div', array('class'=>'no-overflow'));
03388     $output .= html_writer::start_tag('div', array('class'=>'content'));
03389     if (!empty($attachments)) {
03390         $output .= html_writer::tag('div', $attachments, array('class'=>'attachments'));
03391     }
03392 
03393     $options = new stdClass;
03394     $options->para    = false;
03395     $options->trusted = $post->messagetrust;
03396     $options->context = $modcontext;
03397     if ($shortenpost) {
03398         // Prepare shortened version
03399         $postclass    = 'shortenedpost';
03400         $postcontent  = format_text(forum_shorten_post($post->message), $post->messageformat, $options, $course->id);
03401         $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum'));
03402         $postcontent .= html_writer::tag('span', '('.get_string('numwords', 'moodle', count_words(strip_tags($post->message))).')...', array('class'=>'post-word-count'));
03403     } else {
03404         // Prepare whole post
03405         $postclass    = 'fullpost';
03406         $postcontent  = format_text($post->message, $post->messageformat, $options, $course->id);
03407         if (!empty($highlight)) {
03408             $postcontent = highlight($highlight, $postcontent);
03409         }
03410         $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages'));
03411     }
03412     // Output the post content
03413     $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass));
03414     $output .= html_writer::end_tag('div'); // Content
03415     $output .= html_writer::end_tag('div'); // Content mask
03416     $output .= html_writer::end_tag('div'); // Row
03417 
03418     $output .= html_writer::start_tag('div', array('class'=>'row side'));
03419     $output .= html_writer::tag('div','&nbsp;', array('class'=>'left'));
03420     $output .= html_writer::start_tag('div', array('class'=>'options clearfix'));
03421 
03422     // Output ratings
03423     if (!empty($post->rating)) {
03424         $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating'));
03425     }
03426 
03427     // Output the commands
03428     $commandhtml = array();
03429     foreach ($commands as $command) {
03430         if (is_array($command)) {
03431             $commandhtml[] = html_writer::link($command['url'], $command['text']);
03432         } else {
03433             $commandhtml[] = $command;
03434         }
03435     }
03436     $output .= html_writer::tag('div', implode(' | ', $commandhtml), array('class'=>'commands'));
03437 
03438     // Output link to post if required
03439     if ($link) {
03440         if ($post->replies == 1) {
03441             $replystring = get_string('repliesone', 'forum', $post->replies);
03442         } else {
03443             $replystring = get_string('repliesmany', 'forum', $post->replies);
03444         }
03445 
03446         $output .= html_writer::start_tag('div', array('class'=>'link'));
03447         $output .= html_writer::link($discussionlink, get_string('discussthistopic', 'forum'));
03448         $output .= '&nbsp;('.$replystring.')';
03449         $output .= html_writer::end_tag('div'); // link
03450     }
03451 
03452     // Output footer if required
03453     if ($footer) {
03454         $output .= html_writer::tag('div', $footer, array('class'=>'footer'));
03455     }
03456 
03457     // Close remaining open divs
03458     $output .= html_writer::end_tag('div'); // content
03459     $output .= html_writer::end_tag('div'); // row
03460     $output .= html_writer::end_tag('div'); // forumpost
03461 
03462     // Mark the forum post as read if required
03463     if ($istracked && !$CFG->forum_usermarksread && !$postisread) {
03464         forum_tp_mark_post_read($USER->id, $post, $forum->id);
03465     }
03466 
03467     if ($return) {
03468         return $output;
03469     }
03470     echo $output;
03471     return;
03472 }
03473 
03480 function forum_rating_permissions($contextid, $component, $ratingarea) {
03481     $context = get_context_instance_by_id($contextid, MUST_EXIST);
03482     if ($component != 'mod_forum' || $ratingarea != 'post') {
03483         // We don't know about this component/ratingarea so just return null to get the
03484         // default restrictive permissions.
03485         return null;
03486     }
03487     return array(
03488         'view'    => has_capability('mod/forum:viewrating', $context),
03489         'viewany' => has_capability('mod/forum:viewanyrating', $context),
03490         'viewall' => has_capability('mod/forum:viewallratings', $context),
03491         'rate'    => has_capability('mod/forum:rate', $context)
03492     );
03493 }
03494 
03508 function forum_rating_validate($params) {
03509     global $DB, $USER;
03510 
03511     // Check the component is mod_forum
03512     if ($params['component'] != 'mod_forum') {
03513         throw new rating_exception('invalidcomponent');
03514     }
03515 
03516     // Check the ratingarea is post (the only rating area in forum)
03517     if ($params['ratingarea'] != 'post') {
03518         throw new rating_exception('invalidratingarea');
03519     }
03520 
03521     // Check the rateduserid is not the current user .. you can't rate your own posts
03522     if ($params['rateduserid'] == $USER->id) {
03523         throw new rating_exception('nopermissiontorate');
03524     }
03525 
03526     // Fetch all the related records ... we need to do this anyway to call forum_user_can_see_post
03527     $post = $DB->get_record('forum_posts', array('id' => $params['itemid'], 'userid' => $params['rateduserid']), '*', MUST_EXIST);
03528     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion), '*', MUST_EXIST);
03529     $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
03530     $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
03531     $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id , false, MUST_EXIST);
03532     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
03533 
03534     // Make sure the context provided is the context of the forum
03535     if ($context->id != $params['context']->id) {
03536         throw new rating_exception('invalidcontext');
03537     }
03538 
03539     if ($forum->scale != $params['scaleid']) {
03540         //the scale being submitted doesnt match the one in the database
03541         throw new rating_exception('invalidscaleid');
03542     }
03543 
03544     // check the item we're rating was created in the assessable time window
03545     if (!empty($forum->assesstimestart) && !empty($forum->assesstimefinish)) {
03546         if ($post->created < $forum->assesstimestart || $post->created > $forum->assesstimefinish) {
03547             throw new rating_exception('notavailable');
03548         }
03549     }
03550 
03551     //check that the submitted rating is valid for the scale
03552 
03553     // lower limit
03554     if ($params['rating'] < 0  && $params['rating'] != RATING_UNSET_RATING) {
03555         throw new rating_exception('invalidnum');
03556     }
03557 
03558     // upper limit
03559     if ($forum->scale < 0) {
03560         //its a custom scale
03561         $scalerecord = $DB->get_record('scale', array('id' => -$forum->scale));
03562         if ($scalerecord) {
03563             $scalearray = explode(',', $scalerecord->scale);
03564             if ($params['rating'] > count($scalearray)) {
03565                 throw new rating_exception('invalidnum');
03566             }
03567         } else {
03568             throw new rating_exception('invalidscaleid');
03569         }
03570     } else if ($params['rating'] > $forum->scale) {
03571         //if its numeric and submitted rating is above maximum
03572         throw new rating_exception('invalidnum');
03573     }
03574 
03575     // Make sure groups allow this user to see the item they're rating
03576     if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
03577         if (!groups_group_exists($discussion->groupid)) { // Can't find group
03578             throw new rating_exception('cannotfindgroup');//something is wrong
03579         }
03580 
03581         if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
03582             // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
03583             throw new rating_exception('notmemberofgroup');
03584         }
03585     }
03586 
03587     // perform some final capability checks
03588     if (!forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
03589         throw new rating_exception('nopermissiontorate');
03590     }
03591 
03592     return true;
03593 }
03594 
03595 
03612 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
03613                                         $cantrack=true, $forumtracked=true, $canviewparticipants=true, $modcontext=NULL) {
03614 
03615     global $USER, $CFG, $OUTPUT;
03616 
03617     static $rowcount;
03618     static $strmarkalldread;
03619 
03620     if (empty($modcontext)) {
03621         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
03622             print_error('invalidcoursemodule');
03623         }
03624         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
03625     }
03626 
03627     if (!isset($rowcount)) {
03628         $rowcount = 0;
03629         $strmarkalldread = get_string('markalldread', 'forum');
03630     } else {
03631         $rowcount = ($rowcount + 1) % 2;
03632     }
03633 
03634     $post->subject = format_string($post->subject,true);
03635 
03636     echo "\n\n";
03637     echo '<tr class="discussion r'.$rowcount.'">';
03638 
03639     // Topic
03640     echo '<td class="topic starter">';
03641     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
03642     echo "</td>\n";
03643 
03644     // Picture
03645     $postuser = new stdClass();
03646     $postuser->id = $post->userid;
03647     $postuser->firstname = $post->firstname;
03648     $postuser->lastname = $post->lastname;
03649     $postuser->imagealt = $post->imagealt;
03650     $postuser->picture = $post->picture;
03651     $postuser->email = $post->email;
03652 
03653     echo '<td class="picture">';
03654     echo $OUTPUT->user_picture($postuser, array('courseid'=>$forum->course));
03655     echo "</td>\n";
03656 
03657     // User name
03658     $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
03659     echo '<td class="author">';
03660     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
03661     echo "</td>\n";
03662 
03663     // Group picture
03664     if ($group !== -1) {  // Groups are active - group is a group data object or NULL
03665         echo '<td class="picture group">';
03666         if (!empty($group->picture) and empty($group->hidepicture)) {
03667             print_group_picture($group, $forum->course, false, false, true);
03668         } else if (isset($group->id)) {
03669             if($canviewparticipants) {
03670                 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
03671             } else {
03672                 echo $group->name;
03673             }
03674         }
03675         echo "</td>\n";
03676     }
03677 
03678     if (has_capability('mod/forum:viewdiscussion', $modcontext)) {   // Show the column with replies
03679         echo '<td class="replies">';
03680         echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
03681         echo $post->replies.'</a>';
03682         echo "</td>\n";
03683 
03684         if ($cantrack) {
03685             echo '<td class="replies">';
03686             if ($forumtracked) {
03687                 if ($post->unread > 0) {
03688                     echo '<span class="unread">';
03689                     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
03690                     echo $post->unread;
03691                     echo '</a>';
03692                     echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
03693                          $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
03694                          '<img src="'.$OUTPUT->pix_url('t/clear') . '" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
03695                     echo '</span>';
03696                 } else {
03697                     echo '<span class="read">';
03698                     echo $post->unread;
03699                     echo '</span>';
03700                 }
03701             } else {
03702                 echo '<span class="read">';
03703                 echo '-';
03704                 echo '</span>';
03705             }
03706             echo "</td>\n";
03707         }
03708     }
03709 
03710     echo '<td class="lastpost">';
03711     $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified;  // Just in case
03712     $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
03713     $usermodified = new stdClass();
03714     $usermodified->id        = $post->usermodified;
03715     $usermodified->firstname = $post->umfirstname;
03716     $usermodified->lastname  = $post->umlastname;
03717     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
03718          fullname($usermodified).'</a><br />';
03719     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
03720           userdate($usedate, $datestring).'</a>';
03721     echo "</td>\n";
03722 
03723     echo "</tr>\n\n";
03724 
03725 }
03726 
03727 
03737 function forum_shorten_post($message) {
03738 
03739    global $CFG;
03740 
03741    $i = 0;
03742    $tag = false;
03743    $length = strlen($message);
03744    $count = 0;
03745    $stopzone = false;
03746    $truncate = 0;
03747 
03748    for ($i=0; $i<$length; $i++) {
03749        $char = $message[$i];
03750 
03751        switch ($char) {
03752            case "<":
03753                $tag = true;
03754                break;
03755            case ">":
03756                $tag = false;
03757                break;
03758            default:
03759                if (!$tag) {
03760                    if ($stopzone) {
03761                        if ($char == ".") {
03762                            $truncate = $i+1;
03763                            break 2;
03764                        }
03765                    }
03766                    $count++;
03767                }
03768                break;
03769        }
03770        if (!$stopzone) {
03771            if ($count > $CFG->forum_shortpost) {
03772                $stopzone = true;
03773            }
03774        }
03775    }
03776 
03777    if (!$truncate) {
03778        $truncate = $i;
03779    }
03780 
03781    return substr($message, 0, $truncate);
03782 }
03783 
03793 function forum_print_mode_form($id, $mode, $forumtype='') {
03794     global $OUTPUT;
03795     if ($forumtype == 'single') {
03796         $select = new single_select(new moodle_url("/mod/forum/view.php", array('f'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode");
03797         $select->class = "forummode";
03798     } else {
03799         $select = new single_select(new moodle_url("/mod/forum/discuss.php", array('d'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode");
03800     }
03801     echo $OUTPUT->render($select);
03802 }
03803 
03810 function forum_search_form($course, $search='') {
03811     global $CFG, $OUTPUT;
03812 
03813     $output  = '<div class="forumsearch">';
03814     $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
03815     $output .= '<fieldset class="invisiblefieldset">';
03816     $output .= $OUTPUT->help_icon('search');
03817     $output .= '<label class="accesshide" for="search" >'.get_string('search', 'forum').'</label>';
03818     $output .= '<input id="search" name="search" type="text" size="18" value="'.s($search, true).'" alt="search" />';
03819     $output .= '<label class="accesshide" for="searchforums" >'.get_string('searchforums', 'forum').'</label>';
03820     $output .= '<input id="searchforums" value="'.get_string('searchforums', 'forum').'" type="submit" />';
03821     $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
03822     $output .= '</fieldset>';
03823     $output .= '</form>';
03824     $output .= '</div>';
03825 
03826     return $output;
03827 }
03828 
03829 
03834 function forum_set_return() {
03835     global $CFG, $SESSION;
03836 
03837     if (! isset($SESSION->fromdiscussion)) {
03838         if (!empty($_SERVER['HTTP_REFERER'])) {
03839             $referer = $_SERVER['HTTP_REFERER'];
03840         } else {
03841             $referer = "";
03842         }
03843         // If the referer is NOT a login screen then save it.
03844         if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
03845             $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
03846         }
03847     }
03848 }
03849 
03850 
03856 function forum_go_back_to($default) {
03857     global $SESSION;
03858 
03859     if (!empty($SESSION->fromdiscussion)) {
03860         $returnto = $SESSION->fromdiscussion;
03861         unset($SESSION->fromdiscussion);
03862         return $returnto;
03863     } else {
03864         return $default;
03865     }
03866 }
03867 
03880 function forum_move_attachments($discussion, $forumfrom, $forumto) {
03881     global $DB;
03882 
03883     $fs = get_file_storage();
03884 
03885     $newcm = get_coursemodule_from_instance('forum', $forumto);
03886     $oldcm = get_coursemodule_from_instance('forum', $forumfrom);
03887 
03888     $newcontext = get_context_instance(CONTEXT_MODULE, $newcm->id);
03889     $oldcontext = get_context_instance(CONTEXT_MODULE, $oldcm->id);
03890 
03891     // loop through all posts, better not use attachment flag ;-)
03892     if ($posts = $DB->get_records('forum_posts', array('discussion'=>$discussion->id), '', 'id, attachment')) {
03893         foreach ($posts as $post) {
03894             $fs->move_area_files_to_new_context($oldcontext->id,
03895                     $newcontext->id, 'mod_forum', 'post', $post->id);
03896             $attachmentsmoved = $fs->move_area_files_to_new_context($oldcontext->id,
03897                     $newcontext->id, 'mod_forum', 'attachment', $post->id);
03898             if ($attachmentsmoved > 0 && $post->attachment != '1') {
03899                 // Weird - let's fix it
03900                 $post->attachment = '1';
03901                 $DB->update_record('forum_posts', $post);
03902             } else if ($attachmentsmoved == 0 && $post->attachment != '') {
03903                 // Weird - let's fix it
03904                 $post->attachment = '';
03905                 $DB->update_record('forum_posts', $post);
03906             }
03907         }
03908     }
03909 
03910     return true;
03911 }
03912 
03924 function forum_print_attachments($post, $cm, $type) {
03925     global $CFG, $DB, $USER, $OUTPUT;
03926 
03927     if (empty($post->attachment)) {
03928         return $type !== 'separateimages' ? '' : array('', '');
03929     }
03930 
03931     if (!in_array($type, array('separateimages', 'html', 'text'))) {
03932         return $type !== 'separateimages' ? '' : array('', '');
03933     }
03934 
03935     if (!$context = get_context_instance(CONTEXT_MODULE, $cm->id)) {
03936         return $type !== 'separateimages' ? '' : array('', '');
03937     }
03938     $strattachment = get_string('attachment', 'forum');
03939 
03940     $fs = get_file_storage();
03941 
03942     $imagereturn = '';
03943     $output = '';
03944 
03945     $canexport = !empty($CFG->enableportfolios) && (has_capability('mod/forum:exportpost', $context) || ($post->userid == $USER->id && has_capability('mod/forum:exportownpost', $context)));
03946 
03947     if ($canexport) {
03948         require_once($CFG->libdir.'/portfoliolib.php');
03949     }
03950 
03951     $files = $fs->get_area_files($context->id, 'mod_forum', 'attachment', $post->id, "timemodified", false);
03952     if ($files) {
03953         if ($canexport) {
03954             $button = new portfolio_add_button();
03955         }
03956         foreach ($files as $file) {
03957             $filename = $file->get_filename();
03958             $mimetype = $file->get_mimetype();
03959             $iconimage = '<img src="'.$OUTPUT->pix_url(file_mimetype_icon($mimetype)).'" class="icon" alt="'.$mimetype.'" />';
03960             $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context->id.'/mod_forum/attachment/'.$post->id.'/'.$filename);
03961 
03962             if ($type == 'html') {
03963                 $output .= "<a href=\"$path\">$iconimage</a> ";
03964                 $output .= "<a href=\"$path\">".s($filename)."</a>";
03965                 if ($canexport) {
03966                     $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php');
03967                     $button->set_format_by_file($file);
03968                     $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
03969                 }
03970                 $output .= "<br />";
03971 
03972             } else if ($type == 'text') {
03973                 $output .= "$strattachment ".s($filename).":\n$path\n";
03974 
03975             } else { //'returnimages'
03976                 if (in_array($mimetype, array('image/gif', 'image/jpeg', 'image/png'))) {
03977                     // Image attachments don't get printed as links
03978                     $imagereturn .= "<br /><img src=\"$path\" alt=\"\" />";
03979                     if ($canexport) {
03980                         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php');
03981                         $button->set_format_by_file($file);
03982                         $imagereturn .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
03983                     }
03984                 } else {
03985                     $output .= "<a href=\"$path\">$iconimage</a> ";
03986                     $output .= format_text("<a href=\"$path\">".s($filename)."</a>", FORMAT_HTML, array('context'=>$context));
03987                     if ($canexport) {
03988                         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php');
03989                         $button->set_format_by_file($file);
03990                         $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
03991                     }
03992                     $output .= '<br />';
03993                 }
03994             }
03995         }
03996     }
03997 
03998     if ($type !== 'separateimages') {
03999         return $output;
04000 
04001     } else {
04002         return array($output, $imagereturn);
04003     }
04004 }
04005 
04014 function forum_get_file_areas($course, $cm, $context) {
04015     $areas = array();
04016     return $areas;
04017 }
04018 
04033 function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
04034     global $CFG, $DB;
04035 
04036     if ($context->contextlevel != CONTEXT_MODULE) {
04037         return null;
04038     }
04039 
04040     $fileareas = array('attachment', 'post');
04041     if (!in_array($filearea, $fileareas)) {
04042         return null;
04043     }
04044 
04045     if (!$post = $DB->get_record('forum_posts', array('id' => $itemid))) {
04046         return null;
04047     }
04048 
04049     if (!$discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion))) {
04050         return null;
04051     }
04052 
04053     if (!$forum = $DB->get_record('forum', array('id' => $cm->instance))) {
04054         return null;
04055     }
04056 
04057     $fs = get_file_storage();
04058     $filepath = is_null($filepath) ? '/' : $filepath;
04059     $filename = is_null($filename) ? '.' : $filename;
04060     if (!($storedfile = $fs->get_file($context->id, 'mod_forum', $filearea, $itemid, $filepath, $filename))) {
04061         return null;
04062     }
04063 
04064     // Make sure groups allow this user to see this file
04065     if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
04066         if (!groups_group_exists($discussion->groupid)) { // Can't find group
04067             return null;                           // Be safe and don't send it to anyone
04068         }
04069 
04070         if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
04071             // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
04072             return null;
04073         }
04074     }
04075 
04076     // Make sure we're allowed to see it...
04077     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
04078         return null;
04079     }
04080 
04081     $urlbase = $CFG->wwwroot.'/pluginfile.php';
04082     return new file_info_stored($browser, $context, $storedfile, $urlbase, $filearea, $itemid, true, true, false);
04083 }
04084 
04096 function forum_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
04097     global $CFG, $DB;
04098 
04099     if ($context->contextlevel != CONTEXT_MODULE) {
04100         return false;
04101     }
04102 
04103     require_course_login($course, true, $cm);
04104 
04105     $fileareas = array('attachment', 'post');
04106     if (!in_array($filearea, $fileareas)) {
04107         return false;
04108     }
04109 
04110     $postid = (int)array_shift($args);
04111 
04112     if (!$post = $DB->get_record('forum_posts', array('id'=>$postid))) {
04113         return false;
04114     }
04115 
04116     if (!$discussion = $DB->get_record('forum_discussions', array('id'=>$post->discussion))) {
04117         return false;
04118     }
04119 
04120     if (!$forum = $DB->get_record('forum', array('id'=>$cm->instance))) {
04121         return false;
04122     }
04123 
04124     $fs = get_file_storage();
04125     $relativepath = implode('/', $args);
04126     $fullpath = "/$context->id/mod_forum/$filearea/$postid/$relativepath";
04127     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
04128         return false;
04129     }
04130 
04131     // Make sure groups allow this user to see this file
04132     if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
04133         if (!groups_group_exists($discussion->groupid)) { // Can't find group
04134             return false;                           // Be safe and don't send it to anyone
04135         }
04136 
04137         if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
04138             // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
04139             return false;
04140         }
04141     }
04142 
04143     // Make sure we're allowed to see it...
04144     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
04145         return false;
04146     }
04147 
04148 
04149     // finally send the file
04150     send_stored_file($file, 0, 0, true); // download MUST be forced - security!
04151 }
04152 
04164 function forum_add_attachment($post, $forum, $cm, $mform=null, &$message=null) {
04165     global $DB;
04166 
04167     if (empty($mform)) {
04168         return false;
04169     }
04170 
04171     if (empty($post->attachments)) {
04172         return true;   // Nothing to do
04173     }
04174 
04175     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
04176 
04177     $info = file_get_draft_area_info($post->attachments);
04178     $present = ($info['filecount']>0) ? '1' : '';
04179     file_save_draft_area_files($post->attachments, $context->id, 'mod_forum', 'attachment', $post->id);
04180 
04181     $DB->set_field('forum_posts', 'attachment', $present, array('id'=>$post->id));
04182 
04183     return true;
04184 }
04185 
04197 function forum_add_new_post($post, $mform, &$message) {
04198     global $USER, $CFG, $DB;
04199 
04200     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion));
04201     $forum      = $DB->get_record('forum', array('id' => $discussion->forum));
04202     $cm         = get_coursemodule_from_instance('forum', $forum->id);
04203     $context    = get_context_instance(CONTEXT_MODULE, $cm->id);
04204 
04205     $post->created    = $post->modified = time();
04206     $post->mailed     = "0";
04207     $post->userid     = $USER->id;
04208     $post->attachment = "";
04209 
04210     $post->id = $DB->insert_record("forum_posts", $post);
04211     $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message);
04212     $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
04213     forum_add_attachment($post, $forum, $cm, $mform, $message);
04214 
04215     // Update discussion modified date
04216     $DB->set_field("forum_discussions", "timemodified", $post->modified, array("id" => $post->discussion));
04217     $DB->set_field("forum_discussions", "usermodified", $post->userid, array("id" => $post->discussion));
04218 
04219     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
04220         forum_tp_mark_post_read($post->userid, $post, $post->forum);
04221     }
04222 
04223     return $post->id;
04224 }
04225 
04237 function forum_update_post($post, $mform, &$message) {
04238     global $USER, $CFG, $DB;
04239 
04240     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion));
04241     $forum      = $DB->get_record('forum', array('id' => $discussion->forum));
04242     $cm         = get_coursemodule_from_instance('forum', $forum->id);
04243     $context    = get_context_instance(CONTEXT_MODULE, $cm->id);
04244 
04245     $post->modified = time();
04246 
04247     $DB->update_record('forum_posts', $post);
04248 
04249     $discussion->timemodified = $post->modified; // last modified tracking
04250     $discussion->usermodified = $post->userid;   // last modified tracking
04251 
04252     if (!$post->parent) {   // Post is a discussion starter - update discussion title and times too
04253         $discussion->name      = $post->subject;
04254         $discussion->timestart = $post->timestart;
04255         $discussion->timeend   = $post->timeend;
04256     }
04257     $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message);
04258     $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
04259 
04260     $DB->update_record('forum_discussions', $discussion);
04261 
04262     forum_add_attachment($post, $forum, $cm, $mform, $message);
04263 
04264     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
04265         forum_tp_mark_post_read($post->userid, $post, $post->forum);
04266     }
04267 
04268     return true;
04269 }
04270 
04284 function forum_add_discussion($discussion, $mform=null, &$message=null, $userid=null) {
04285     global $USER, $CFG, $DB;
04286 
04287     $timenow = time();
04288 
04289     if (is_null($userid)) {
04290         $userid = $USER->id;
04291     }
04292 
04293     // The first post is stored as a real post, and linked
04294     // to from the discuss entry.
04295 
04296     $forum = $DB->get_record('forum', array('id'=>$discussion->forum));
04297     $cm    = get_coursemodule_from_instance('forum', $forum->id);
04298 
04299     $post = new stdClass();
04300     $post->discussion    = 0;
04301     $post->parent        = 0;
04302     $post->userid        = $userid;
04303     $post->created       = $timenow;
04304     $post->modified      = $timenow;
04305     $post->mailed        = 0;
04306     $post->subject       = $discussion->name;
04307     $post->message       = $discussion->message;
04308     $post->messageformat = $discussion->messageformat;
04309     $post->messagetrust  = $discussion->messagetrust;
04310     $post->attachments   = isset($discussion->attachments) ? $discussion->attachments : null;
04311     $post->forum         = $forum->id;     // speedup
04312     $post->course        = $forum->course; // speedup
04313     $post->mailnow       = $discussion->mailnow;
04314 
04315     $post->id = $DB->insert_record("forum_posts", $post);
04316 
04317     // TODO: Fix the calling code so that there always is a $cm when this function is called
04318     if (!empty($cm->id) && !empty($discussion->itemid)) {   // In "single simple discussions" this may not exist yet
04319         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
04320         $text = file_save_draft_area_files($discussion->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message);
04321         $DB->set_field('forum_posts', 'message', $text, array('id'=>$post->id));
04322     }
04323 
04324     // Now do the main entry for the discussion, linking to this first post
04325 
04326     $discussion->firstpost    = $post->id;
04327     $discussion->timemodified = $timenow;
04328     $discussion->usermodified = $post->userid;
04329     $discussion->userid       = $userid;
04330 
04331     $post->discussion = $DB->insert_record("forum_discussions", $discussion);
04332 
04333     // Finally, set the pointer on the post.
04334     $DB->set_field("forum_posts", "discussion", $post->discussion, array("id"=>$post->id));
04335 
04336     if (!empty($cm->id)) {
04337         forum_add_attachment($post, $forum, $cm, $mform, $message);
04338     }
04339 
04340     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
04341         forum_tp_mark_post_read($post->userid, $post, $post->forum);
04342     }
04343 
04344     return $post->discussion;
04345 }
04346 
04347 
04359 function forum_delete_discussion($discussion, $fulldelete, $course, $cm, $forum) {
04360     global $DB, $CFG;
04361     require_once($CFG->libdir.'/completionlib.php');
04362 
04363     $result = true;
04364 
04365     if ($posts = $DB->get_records("forum_posts", array("discussion" => $discussion->id))) {
04366         foreach ($posts as $post) {
04367             $post->course = $discussion->course;
04368             $post->forum  = $discussion->forum;
04369             if (!forum_delete_post($post, 'ignore', $course, $cm, $forum, $fulldelete)) {
04370                 $result = false;
04371             }
04372         }
04373     }
04374 
04375     forum_tp_delete_read_records(-1, -1, $discussion->id);
04376 
04377     if (!$DB->delete_records("forum_discussions", array("id"=>$discussion->id))) {
04378         $result = false;
04379     }
04380 
04381     // Update completion state if we are tracking completion based on number of posts
04382     // But don't bother when deleting whole thing
04383     if (!$fulldelete) {
04384         $completion = new completion_info($course);
04385         if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC &&
04386            ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) {
04387             $completion->update_state($cm, COMPLETION_INCOMPLETE, $discussion->userid);
04388         }
04389     }
04390 
04391     return $result;
04392 }
04393 
04394 
04412 function forum_delete_post($post, $children, $course, $cm, $forum, $skipcompletion=false) {
04413     global $DB, $CFG;
04414     require_once($CFG->libdir.'/completionlib.php');
04415 
04416     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
04417 
04418     if ($children != 'ignore' && ($childposts = $DB->get_records('forum_posts', array('parent'=>$post->id)))) {
04419        if ($children) {
04420            foreach ($childposts as $childpost) {
04421                forum_delete_post($childpost, true, $course, $cm, $forum, $skipcompletion);
04422            }
04423        } else {
04424            return false;
04425        }
04426     }
04427 
04428     //delete ratings
04429     require_once($CFG->dirroot.'/rating/lib.php');
04430     $delopt = new stdClass;
04431     $delopt->contextid = $context->id;
04432     $delopt->component = 'mod_forum';
04433     $delopt->ratingarea = 'post';
04434     $delopt->itemid = $post->id;
04435     $rm = new rating_manager();
04436     $rm->delete_ratings($delopt);
04437 
04438     //delete attachments
04439     $fs = get_file_storage();
04440     $fs->delete_area_files($context->id, 'mod_forum', 'attachment', $post->id);
04441     $fs->delete_area_files($context->id, 'mod_forum', 'post', $post->id);
04442 
04443     if ($DB->delete_records("forum_posts", array("id" => $post->id))) {
04444 
04445         forum_tp_delete_read_records(-1, $post->id);
04446 
04447     // Just in case we are deleting the last post
04448         forum_discussion_update_last_post($post->discussion);
04449 
04450         // Update completion state if we are tracking completion based on number of posts
04451         // But don't bother when deleting whole thing
04452 
04453         if (!$skipcompletion) {
04454             $completion = new completion_info($course);
04455             if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC &&
04456                ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) {
04457                 $completion->update_state($cm, COMPLETION_INCOMPLETE, $post->userid);
04458             }
04459         }
04460 
04461         return true;
04462     }
04463     return false;
04464 }
04465 
04472 function forum_count_replies($post, $children=true) {
04473     global $DB;
04474     $count = 0;
04475 
04476     if ($children) {
04477         if ($childposts = $DB->get_records('forum_posts', array('parent' => $post->id))) {
04478            foreach ($childposts as $childpost) {
04479                $count ++;                   // For this child
04480                $count += forum_count_replies($childpost, true);
04481            }
04482         }
04483     } else {
04484         $count += $DB->count_records('forum_posts', array('parent' => $post->id));
04485     }
04486 
04487     return $count;
04488 }
04489 
04490 
04497 function forum_forcesubscribe($forumid, $value=1) {
04498     global $DB;
04499     return $DB->set_field("forum", "forcesubscribe", $value, array("id" => $forumid));
04500 }
04501 
04507 function forum_is_forcesubscribed($forum) {
04508     global $DB;
04509     if (isset($forum->forcesubscribe)) {    // then we use that
04510         return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE);
04511     } else {   // Check the database
04512        return ($DB->get_field('forum', 'forcesubscribe', array('id' => $forum)) == FORUM_FORCESUBSCRIBE);
04513     }
04514 }
04515 
04516 function forum_get_forcesubscribed($forum) {
04517     global $DB;
04518     if (isset($forum->forcesubscribe)) {    // then we use that
04519         return $forum->forcesubscribe;
04520     } else {   // Check the database
04521         return $DB->get_field('forum', 'forcesubscribe', array('id' => $forum));
04522     }
04523 }
04524 
04531 function forum_is_subscribed($userid, $forum) {
04532     global $DB;
04533     if (is_numeric($forum)) {
04534         $forum = $DB->get_record('forum', array('id' => $forum));
04535     }
04536     if (forum_is_forcesubscribed($forum)) {
04537         return true;
04538     }
04539     return $DB->record_exists("forum_subscriptions", array("userid" => $userid, "forum" => $forum->id));
04540 }
04541 
04542 function forum_get_subscribed_forums($course) {
04543     global $USER, $CFG, $DB;
04544     $sql = "SELECT f.id
04545               FROM {forum} f
04546                    LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?)
04547              WHERE f.course = ?
04548                    AND f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
04549                    AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)";
04550     if ($subscribed = $DB->get_records_sql($sql, array($USER->id, $course->id))) {
04551         foreach ($subscribed as $s) {
04552             $subscribed[$s->id] = $s->id;
04553         }
04554         return $subscribed;
04555     } else {
04556         return array();
04557     }
04558 }
04559 
04567 function forum_subscribe($userid, $forumid) {
04568     global $DB;
04569 
04570     if ($DB->record_exists("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid))) {
04571         return true;
04572     }
04573 
04574     $sub = new stdClass();
04575     $sub->userid  = $userid;
04576     $sub->forum = $forumid;
04577 
04578     return $DB->insert_record("forum_subscriptions", $sub);
04579 }
04580 
04588 function forum_unsubscribe($userid, $forumid) {
04589     global $DB;
04590     return $DB->delete_records("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid));
04591 }
04592 
04601 function forum_post_subscription($post, $forum) {
04602 
04603     global $USER;
04604 
04605     $action = '';
04606     $subscribed = forum_is_subscribed($USER->id, $forum);
04607 
04608     if ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE) { // database ignored
04609         return "";
04610 
04611     } elseif (($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE)
04612         && !has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $forum->course), $USER->id)) {
04613         if ($subscribed) {
04614             $action = 'unsubscribe'; // sanity check, following MDL-14558
04615         } else {
04616             return "";
04617         }
04618 
04619     } else { // go with the user's choice
04620         if (isset($post->subscribe)) {
04621             // no change
04622             if ((!empty($post->subscribe) && $subscribed)
04623                 || (empty($post->subscribe) && !$subscribed)) {
04624                 return "";
04625 
04626             } elseif (!empty($post->subscribe) && !$subscribed) {
04627                 $action = 'subscribe';
04628 
04629             } elseif (empty($post->subscribe) && $subscribed) {
04630                 $action = 'unsubscribe';
04631             }
04632         }
04633     }
04634 
04635     $info = new stdClass();
04636     $info->name  = fullname($USER);
04637     $info->forum = format_string($forum->name);
04638 
04639     switch ($action) {
04640         case 'subscribe':
04641             forum_subscribe($USER->id, $post->forum);
04642             return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
04643         case 'unsubscribe':
04644             forum_unsubscribe($USER->id, $post->forum);
04645             return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
04646     }
04647 }
04648 
04664 function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false, $fakelink=true, $backtoindex=false, $subscribed_forums=null) {
04665     global $CFG, $USER, $PAGE, $OUTPUT;
04666     $defaultmessages = array(
04667         'subscribed' => get_string('unsubscribe', 'forum'),
04668         'unsubscribed' => get_string('subscribe', 'forum'),
04669         'cantaccessgroup' => get_string('no'),
04670         'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
04671         'cantsubscribe' => get_string('disallowsubscribe','forum')
04672     );
04673     $messages = $messages + $defaultmessages;
04674 
04675     if (forum_is_forcesubscribed($forum)) {
04676         return $messages['forcesubscribed'];
04677     } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) {
04678         return $messages['cantsubscribe'];
04679     } else if ($cantaccessagroup) {
04680         return $messages['cantaccessgroup'];
04681     } else {
04682         if (!is_enrolled($context, $USER, '', true)) {
04683             return '';
04684         }
04685         if (is_null($subscribed_forums)) {
04686             $subscribed = forum_is_subscribed($USER->id, $forum);
04687         } else {
04688             $subscribed = !empty($subscribed_forums[$forum->id]);
04689         }
04690         if ($subscribed) {
04691             $linktext = $messages['subscribed'];
04692             $linktitle = get_string('subscribestop', 'forum');
04693         } else {
04694             $linktext = $messages['unsubscribed'];
04695             $linktitle = get_string('subscribestart', 'forum');
04696         }
04697 
04698         $options = array();
04699         if ($backtoindex) {
04700             $backtoindexlink = '&amp;backtoindex=1';
04701             $options['backtoindex'] = 1;
04702         } else {
04703             $backtoindexlink = '';
04704         }
04705         $link = '';
04706 
04707         if ($fakelink) {
04708             $PAGE->requires->js('/mod/forum/forum.js');
04709             $PAGE->requires->js_function_call('forum_produce_subscribe_link', array($forum->id, $backtoindexlink, $linktext, $linktitle));
04710             $link = "<noscript>";
04711         }
04712         $options['id'] = $forum->id;
04713         $options['sesskey'] = sesskey();
04714         $url = new moodle_url('/mod/forum/subscribe.php', $options);
04715         $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle));
04716         if ($fakelink) {
04717             $link .= '</noscript>';
04718         }
04719 
04720         return $link;
04721     }
04722 }
04723 
04724 
04736 function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) {
04737     global $CFG, $USER, $PAGE, $OUTPUT;
04738 
04739     static $strnotrackforum, $strtrackforum;
04740 
04741     if (isset($messages['trackforum'])) {
04742          $strtrackforum = $messages['trackforum'];
04743     }
04744     if (isset($messages['notrackforum'])) {
04745          $strnotrackforum = $messages['notrackforum'];
04746     }
04747     if (empty($strtrackforum)) {
04748         $strtrackforum = get_string('trackforum', 'forum');
04749     }
04750     if (empty($strnotrackforum)) {
04751         $strnotrackforum = get_string('notrackforum', 'forum');
04752     }
04753 
04754     if (forum_tp_is_tracked($forum)) {
04755         $linktitle = $strnotrackforum;
04756         $linktext = $strnotrackforum;
04757     } else {
04758         $linktitle = $strtrackforum;
04759         $linktext = $strtrackforum;
04760     }
04761 
04762     $link = '';
04763     if ($fakelink) {
04764         $PAGE->requires->js('/mod/forum/forum.js');
04765         $PAGE->requires->js_function_call('forum_produce_tracking_link', Array($forum->id, $linktext, $linktitle));
04766         // use <noscript> to print button in case javascript is not enabled
04767         $link .= '<noscript>';
04768     }
04769     $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forum->id));
04770     $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle));
04771 
04772     if ($fakelink) {
04773         $link .= '</noscript>';
04774     }
04775 
04776     return $link;
04777 }
04778 
04779 
04780 
04790 function forum_user_has_posted_discussion($forumid, $userid) {
04791     global $CFG, $DB;
04792 
04793     $sql = "SELECT 'x'
04794               FROM {forum_discussions} d, {forum_posts} p
04795              WHERE d.forum = ? AND p.discussion = d.id AND p.parent = 0 and p.userid = ?";
04796 
04797     return $DB->record_exists_sql($sql, array($forumid, $userid));
04798 }
04799 
04807 function forum_discussions_user_has_posted_in($forumid, $userid) {
04808     global $CFG, $DB;
04809 
04810     $haspostedsql = "SELECT d.id AS id,
04811                             d.*
04812                        FROM {forum_posts} p,
04813                             {forum_discussions} d
04814                       WHERE p.discussion = d.id
04815                         AND d.forum = ?
04816                         AND p.userid = ?";
04817 
04818     return $DB->get_records_sql($haspostedsql, array($forumid, $userid));
04819 }
04820 
04829 function forum_user_has_posted($forumid, $did, $userid) {
04830     global $DB;
04831 
04832     if (empty($did)) {
04833         // posted in any forum discussion?
04834         $sql = "SELECT 'x'
04835                   FROM {forum_posts} p
04836                   JOIN {forum_discussions} d ON d.id = p.discussion
04837                  WHERE p.userid = :userid AND d.forum = :forumid";
04838         return $DB->record_exists_sql($sql, array('forumid'=>$forumid,'userid'=>$userid));
04839     } else {
04840         return $DB->record_exists('forum_posts', array('discussion'=>$did,'userid'=>$userid));
04841     }
04842 }
04843 
04851 function forum_get_user_posted_time($did, $userid) {
04852     global $DB;
04853 
04854     $posttime = $DB->get_field('forum_posts', 'MIN(created)', array('userid'=>$userid, 'discussion'=>$did));
04855     if (empty($posttime)) {
04856         return false;
04857     }
04858     return $posttime;
04859 }
04860 
04870 function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1, $cm=NULL, $context=NULL) {
04871 // $forum is an object
04872     global $USER;
04873 
04874     // shortcut - guest and not-logged-in users can not post
04875     if (isguestuser() or !isloggedin()) {
04876         return false;
04877     }
04878 
04879     if (!$cm) {
04880         debugging('missing cm', DEBUG_DEVELOPER);
04881         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
04882             print_error('invalidcoursemodule');
04883         }
04884     }
04885 
04886     if (!$context) {
04887         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
04888     }
04889 
04890     if ($currentgroup === null) {
04891         $currentgroup = groups_get_activity_group($cm);
04892     }
04893 
04894     $groupmode = groups_get_activity_groupmode($cm);
04895 
04896     if ($forum->type == 'news') {
04897         $capname = 'mod/forum:addnews';
04898     } else if ($forum->type == 'qanda') {
04899         $capname = 'mod/forum:addquestion';
04900     } else {
04901         $capname = 'mod/forum:startdiscussion';
04902     }
04903 
04904     if (!has_capability($capname, $context)) {
04905         return false;
04906     }
04907 
04908     if ($forum->type == 'single') {
04909         return false;
04910     }
04911 
04912     if ($forum->type == 'eachuser') {
04913         if (forum_user_has_posted_discussion($forum->id, $USER->id)) {
04914             return false;
04915         }
04916     }
04917 
04918     if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
04919         return true;
04920     }
04921 
04922     if ($currentgroup) {
04923         return groups_is_member($currentgroup);
04924     } else {
04925         // no group membership and no accessallgroups means no new discussions
04926         // reverted to 1.7 behaviour in 1.9+,  buggy in 1.8.0-1.9.0
04927         return false;
04928     }
04929 }
04930 
04949 function forum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $course=NULL, $context=NULL) {
04950     global $USER, $DB;
04951     if (empty($user)) {
04952         $user = $USER;
04953     }
04954 
04955     // shortcut - guest and not-logged-in users can not post
04956     if (isguestuser($user) or empty($user->id)) {
04957         return false;
04958     }
04959 
04960     if (!isset($discussion->groupid)) {
04961         debugging('incorrect discussion parameter', DEBUG_DEVELOPER);
04962         return false;
04963     }
04964 
04965     if (!$cm) {
04966         debugging('missing cm', DEBUG_DEVELOPER);
04967         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
04968             print_error('invalidcoursemodule');
04969         }
04970     }
04971 
04972     if (!$course) {
04973         debugging('missing course', DEBUG_DEVELOPER);
04974         if (!$course = $DB->get_record('course', array('id' => $forum->course))) {
04975             print_error('invalidcourseid');
04976         }
04977     }
04978 
04979     if (!$context) {
04980         $context = get_context_instance(CONTEXT_MODULE, $cm->id);
04981     }
04982 
04983     // normal users with temporary guest access can not post, suspended users can not post either
04984     if (!is_viewing($context, $user->id) and !is_enrolled($context, $user->id, '', true)) {
04985         return false;
04986     }
04987 
04988     if ($forum->type == 'news') {
04989         $capname = 'mod/forum:replynews';
04990     } else {
04991         $capname = 'mod/forum:replypost';
04992     }
04993 
04994     if (!has_capability($capname, $context, $user->id)) {
04995         return false;
04996     }
04997 
04998     if (!$groupmode = groups_get_activity_groupmode($cm, $course)) {
04999         return true;
05000     }
05001 
05002     if (has_capability('moodle/site:accessallgroups', $context)) {
05003         return true;
05004     }
05005 
05006     if ($groupmode == VISIBLEGROUPS) {
05007         if ($discussion->groupid == -1) {
05008             // allow students to reply to all participants discussions - this was not possible in Moodle <1.8
05009             return true;
05010         }
05011         return groups_is_member($discussion->groupid);
05012 
05013     } else {
05014         //separate groups
05015         if ($discussion->groupid == -1) {
05016             return false;
05017         }
05018         return groups_is_member($discussion->groupid);
05019     }
05020 }
05021 
05022 
05037 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
05038 
05039     global $CFG, $USER;
05040 
05041     if (!$user){
05042         $user = $USER;
05043     }
05044 
05045     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
05046     if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
05047         return false;
05048     }
05049 
05050 // If it's a grouped discussion, make sure the user is a member
05051     if ($discussion->groupid > 0) {
05052         $groupmode = groups_get_activity_groupmode($cm);
05053         if ($groupmode == SEPARATEGROUPS) {
05054             return groups_is_member($discussion->groupid) || has_capability('moodle/site:accessallgroups', $modcontext);
05055         }
05056     }
05057     return true;
05058 }
05059 
05060 
05071 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
05072     global $USER, $DB;
05073 
05074     if (empty($user) || empty($user->id)) {
05075         $user = $USER;
05076     }
05077 
05078     // retrieve objects (yuk)
05079     if (is_numeric($forum)) {
05080         debugging('missing full forum', DEBUG_DEVELOPER);
05081         if (!$forum = $DB->get_record('forum',array('id'=>$forum))) {
05082             return false;
05083         }
05084     }
05085     if (is_numeric($discussion)) {
05086         debugging('missing full discussion', DEBUG_DEVELOPER);
05087         if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) {
05088             return false;
05089         }
05090     }
05091 
05092     if (!has_capability('mod/forum:viewdiscussion', $context)) {
05093         return false;
05094     }
05095 
05096     if ($forum->type == 'qanda' &&
05097             !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
05098             !has_capability('mod/forum:viewqandawithoutposting', $context)) {
05099         return false;
05100     }
05101     return true;
05102 }
05103 
05104 
05115 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm=NULL) {
05116     global $CFG, $USER, $DB;
05117 
05118     // retrieve objects (yuk)
05119     if (is_numeric($forum)) {
05120         debugging('missing full forum', DEBUG_DEVELOPER);
05121         if (!$forum = $DB->get_record('forum',array('id'=>$forum))) {
05122             return false;
05123         }
05124     }
05125 
05126     if (is_numeric($discussion)) {
05127         debugging('missing full discussion', DEBUG_DEVELOPER);
05128         if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) {
05129             return false;
05130         }
05131     }
05132     if (is_numeric($post)) {
05133         debugging('missing full post', DEBUG_DEVELOPER);
05134         if (!$post = $DB->get_record('forum_posts',array('id'=>$post))) {
05135             return false;
05136         }
05137     }
05138     if (!isset($post->id) && isset($post->parent)) {
05139         $post->id = $post->parent;
05140     }
05141 
05142     if (!$cm) {
05143         debugging('missing cm', DEBUG_DEVELOPER);
05144         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
05145             print_error('invalidcoursemodule');
05146         }
05147     }
05148 
05149     if (empty($user) || empty($user->id)) {
05150         $user = $USER;
05151     }
05152 
05153     $canviewdiscussion = !empty($cm->cache->caps['mod/forum:viewdiscussion']) || has_capability('mod/forum:viewdiscussion', get_context_instance(CONTEXT_MODULE, $cm->id), $user->id);
05154     if (!$canviewdiscussion && !has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), get_context_instance(CONTEXT_USER, $post->userid))) {
05155         return false;
05156     }
05157 
05158     if (isset($cm->uservisible)) {
05159         if (!$cm->uservisible) {
05160             return false;
05161         }
05162     } else {
05163         if (!coursemodule_visible_for_user($cm, $user->id)) {
05164             return false;
05165         }
05166     }
05167 
05168     if ($forum->type == 'qanda') {
05169         $firstpost = forum_get_firstpost_from_discussion($discussion->id);
05170         $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
05171         $userfirstpost = forum_get_user_posted_time($discussion->id, $user->id);
05172 
05173         return (($userfirstpost !== false && (time() - $userfirstpost >= $CFG->maxeditingtime)) ||
05174                 $firstpost->id == $post->id || $post->userid == $user->id || $firstpost->userid == $user->id ||
05175                 has_capability('mod/forum:viewqandawithoutposting', $modcontext, $user->id, false));
05176     }
05177     return true;
05178 }
05179 
05180 
05197 function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $displayformat='plain', $sort='',
05198                                         $currentgroup=-1, $groupmode=-1, $page=-1, $perpage=100, $cm=NULL) {
05199     global $CFG, $USER, $OUTPUT;
05200 
05201     if (!$cm) {
05202         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
05203             print_error('invalidcoursemodule');
05204         }
05205     }
05206     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
05207 
05208     if (empty($sort)) {
05209         $sort = "d.timemodified DESC";
05210     }
05211 
05212     $olddiscussionlink = false;
05213 
05214  // Sort out some defaults
05215     if ($perpage <= 0) {
05216         $perpage = 0;
05217         $page    = -1;
05218     }
05219 
05220     if ($maxdiscussions == 0) {
05221         // all discussions - backwards compatibility
05222         $page    = -1;
05223         $perpage = 0;
05224         if ($displayformat == 'plain') {
05225             $displayformat = 'header';  // Abbreviate display by default
05226         }
05227 
05228     } else if ($maxdiscussions > 0) {
05229         $page    = -1;
05230         $perpage = $maxdiscussions;
05231     }
05232 
05233     $fullpost = false;
05234     if ($displayformat == 'plain') {
05235         $fullpost = true;
05236     }
05237 
05238 
05239 // Decide if current user is allowed to see ALL the current discussions or not
05240 
05241 // First check the group stuff
05242     if ($currentgroup == -1 or $groupmode == -1) {
05243         $groupmode    = groups_get_activity_groupmode($cm, $course);
05244         $currentgroup = groups_get_activity_group($cm);
05245     }
05246 
05247     $groups = array(); //cache
05248 
05249 // If the user can post discussions, then this is a good place to put the
05250 // button for it. We do not show the button if we are showing site news
05251 // and the current user is a guest.
05252 
05253     $canstart = forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context);
05254     if (!$canstart and $forum->type !== 'news') {
05255         if (isguestuser() or !isloggedin()) {
05256             $canstart = true;
05257         }
05258         if (!is_enrolled($context) and !is_viewing($context)) {
05259             // allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link
05260             // normal users with temporary guest access see this button too, they are asked to enrol instead
05261             // do not show the button to users with suspended enrolments here
05262             $canstart = enrol_selfenrol_available($course->id);
05263         }
05264     }
05265 
05266     if ($canstart) {
05267         echo '<div class="singlebutton forumaddnew">';
05268         echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
05269         echo '<div>';
05270         echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
05271         switch ($forum->type) {
05272             case 'news':
05273             case 'blog':
05274                 $buttonadd = get_string('addanewtopic', 'forum');
05275                 break;
05276             case 'qanda':
05277                 $buttonadd = get_string('addanewquestion', 'forum');
05278                 break;
05279             default:
05280                 $buttonadd = get_string('addanewdiscussion', 'forum');
05281                 break;
05282         }
05283         echo '<input type="submit" value="'.$buttonadd.'" />';
05284         echo '</div>';
05285         echo '</form>';
05286         echo "</div>\n";
05287 
05288     } else if (isguestuser() or !isloggedin() or $forum->type == 'news') {
05289         // no button and no info
05290 
05291     } else if ($groupmode and has_capability('mod/forum:startdiscussion', $context)) {
05292         // inform users why they can not post new discussion
05293         if ($currentgroup) {
05294             echo $OUTPUT->notification(get_string('cannotadddiscussion', 'forum'));
05295         } else {
05296             echo $OUTPUT->notification(get_string('cannotadddiscussionall', 'forum'));
05297         }
05298     }
05299 
05300 // Get all the recent discussions we're allowed to see
05301 
05302     $getuserlastmodified = ($displayformat == 'header');
05303 
05304     if (! $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage) ) {
05305         echo '<div class="forumnodiscuss">';
05306         if ($forum->type == 'news') {
05307             echo '('.get_string('nonews', 'forum').')';
05308         } else if ($forum->type == 'qanda') {
05309             echo '('.get_string('noquestions','forum').')';
05310         } else {
05311             echo '('.get_string('nodiscussions', 'forum').')';
05312         }
05313         echo "</div>\n";
05314         return;
05315     }
05316 
05317 // If we want paging
05318     if ($page != -1) {
05320         $numdiscussions = forum_get_discussions_count($cm);
05321 
05323         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
05324         if ($numdiscussions > 1000) {
05325             // saves some memory on sites with very large forums
05326             $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage);
05327         } else {
05328             $replies = forum_count_discussion_replies($forum->id);
05329         }
05330 
05331     } else {
05332         $replies = forum_count_discussion_replies($forum->id);
05333 
05334         if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) {
05335             $olddiscussionlink = true;
05336         }
05337     }
05338 
05339     $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
05340 
05341     $strdatestring = get_string('strftimerecentfull');
05342 
05343     // Check if the forum is tracked.
05344     if ($cantrack = forum_tp_can_track_forums($forum)) {
05345         $forumtracked = forum_tp_is_tracked($forum);
05346     } else {
05347         $forumtracked = false;
05348     }
05349 
05350     if ($forumtracked) {
05351         $unreads = forum_get_discussions_unread($cm);
05352     } else {
05353         $unreads = array();
05354     }
05355 
05356     if ($displayformat == 'header') {
05357         echo '<table cellspacing="0" class="forumheaderlist">';
05358         echo '<thead>';
05359         echo '<tr>';
05360         echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
05361         echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
05362         if ($groupmode > 0) {
05363             echo '<th class="header group" scope="col">'.get_string('group').'</th>';
05364         }
05365         if (has_capability('mod/forum:viewdiscussion', $context)) {
05366             echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
05367             // If the forum can be tracked, display the unread column.
05368             if ($cantrack) {
05369                 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
05370                 if ($forumtracked) {
05371                     echo '&nbsp;<a title="'.get_string('markallread', 'forum').
05372                          '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
05373                          $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
05374                          '<img src="'.$OUTPUT->pix_url('t/clear') . '" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
05375                 }
05376                 echo '</th>';
05377             }
05378         }
05379         echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
05380         echo '</tr>';
05381         echo '</thead>';
05382         echo '<tbody>';
05383     }
05384 
05385     foreach ($discussions as $discussion) {
05386         if (!empty($replies[$discussion->discussion])) {
05387             $discussion->replies = $replies[$discussion->discussion]->replies;
05388             $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
05389         } else {
05390             $discussion->replies = 0;
05391         }
05392 
05393         // SPECIAL CASE: The front page can display a news item post to non-logged in users.
05394         // All posts are read in this case.
05395         if (!$forumtracked) {
05396             $discussion->unread = '-';
05397         } else if (empty($USER)) {
05398             $discussion->unread = 0;
05399         } else {
05400             if (empty($unreads[$discussion->discussion])) {
05401                 $discussion->unread = 0;
05402             } else {
05403                 $discussion->unread = $unreads[$discussion->discussion];
05404             }
05405         }
05406 
05407         if (isloggedin()) {
05408             $ownpost = ($discussion->userid == $USER->id);
05409         } else {
05410             $ownpost=false;
05411         }
05412         // Use discussion name instead of subject of first post
05413         $discussion->subject = $discussion->name;
05414 
05415         switch ($displayformat) {
05416             case 'header':
05417                 if ($groupmode > 0) {
05418                     if (isset($groups[$discussion->groupid])) {
05419                         $group = $groups[$discussion->groupid];
05420                     } else {
05421                         $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid);
05422                     }
05423                 } else {
05424                     $group = -1;
05425                 }
05426                 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
05427                     $canviewparticipants, $context);
05428             break;
05429             default:
05430                 $link = false;
05431 
05432                 if ($discussion->replies) {
05433                     $link = true;
05434                 } else {
05435                     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
05436                     $link = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
05437                 }
05438 
05439                 $discussion->forum = $forum->id;
05440 
05441                 forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false);
05442             break;
05443         }
05444     }
05445 
05446     if ($displayformat == "header") {
05447         echo '</tbody>';
05448         echo '</table>';
05449     }
05450 
05451     if ($olddiscussionlink) {
05452         if ($forum->type == 'news') {
05453             $strolder = get_string('oldertopics', 'forum');
05454         } else {
05455             $strolder = get_string('olderdiscussions', 'forum');
05456         }
05457         echo '<div class="forumolddiscuss">';
05458         echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
05459         echo $strolder.'</a> ...</div>';
05460     }
05461 
05462     if ($page != -1) { 
05463         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
05464     }
05465 }
05466 
05467 
05485 function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
05486     global $USER, $CFG;
05487 
05488     require_once($CFG->dirroot.'/rating/lib.php');
05489 
05490     $ownpost = (isloggedin() && $USER->id == $post->userid);
05491 
05492     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
05493     if ($canreply === NULL) {
05494         $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
05495     } else {
05496         $reply = $canreply;
05497     }
05498 
05499     // $cm holds general cache for forum functions
05500     $cm->cache = new stdClass;
05501     $cm->cache->groups      = groups_get_all_groups($course->id, 0, $cm->groupingid);
05502     $cm->cache->usersgroups = array();
05503 
05504     $posters = array();
05505 
05506     // preload all posts - TODO: improve...
05507     if ($mode == FORUM_MODE_FLATNEWEST) {
05508         $sort = "p.created DESC";
05509     } else {
05510         $sort = "p.created ASC";
05511     }
05512 
05513     $forumtracked = forum_tp_is_tracked($forum);
05514     $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
05515     $post = $posts[$post->id];
05516 
05517     foreach ($posts as $pid=>$p) {
05518         $posters[$p->userid] = $p->userid;
05519     }
05520 
05521     // preload all groups of ppl that posted in this discussion
05522     if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) {
05523         foreach($postersgroups as $pg) {
05524             if (!isset($cm->cache->usersgroups[$pg->userid])) {
05525                 $cm->cache->usersgroups[$pg->userid] = array();
05526             }
05527             $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid;
05528         }
05529         unset($postersgroups);
05530     }
05531 
05532     //load ratings
05533     if ($forum->assessed != RATING_AGGREGATE_NONE) {
05534         $ratingoptions = new stdClass;
05535         $ratingoptions->context = $modcontext;
05536         $ratingoptions->component = 'mod_forum';
05537         $ratingoptions->ratingarea = 'post';
05538         $ratingoptions->items = $posts;
05539         $ratingoptions->aggregate = $forum->assessed;//the aggregation method
05540         $ratingoptions->scaleid = $forum->scale;
05541         $ratingoptions->userid = $USER->id;
05542         if ($forum->type == 'single' or !$discussion->id) {
05543             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id";
05544         } else {
05545             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
05546         }
05547         $ratingoptions->assesstimestart = $forum->assesstimestart;
05548         $ratingoptions->assesstimefinish = $forum->assesstimefinish;
05549 
05550         $rm = new rating_manager();
05551         $posts = $rm->get_ratings($ratingoptions);
05552     }
05553 
05554 
05555     $post->forum = $forum->id;   // Add the forum id to the post object, later used by forum_print_post
05556     $post->forumtype = $forum->type;
05557 
05558     $post->subject = format_string($post->subject);
05559 
05560     $postread = !empty($post->postread);
05561 
05562     forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false,
05563                          '', '', $postread, true, $forumtracked);
05564 
05565     switch ($mode) {
05566         case FORUM_MODE_FLATOLDEST :
05567         case FORUM_MODE_FLATNEWEST :
05568         default:
05569             forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts);
05570             break;
05571 
05572         case FORUM_MODE_THREADED :
05573             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts);
05574             break;
05575 
05576         case FORUM_MODE_NESTED :
05577             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
05578             break;
05579     }
05580 }
05581 
05582 
05598 function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) {
05599     global $USER, $CFG;
05600 
05601     $link  = false;
05602 
05603     if ($mode == FORUM_MODE_FLATNEWEST) {
05604         $sort = "ORDER BY created DESC";
05605     } else {
05606         $sort = "ORDER BY created ASC";
05607     }
05608 
05609     foreach ($posts as $post) {
05610         if (!$post->parent) {
05611             continue;
05612         }
05613         $post->subject = format_string($post->subject);
05614         $ownpost = ($USER->id == $post->userid);
05615 
05616         $postread = !empty($post->postread);
05617 
05618         forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
05619                              '', '', $postread, true, $forumtracked);
05620     }
05621 }
05622 
05631 function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) {
05632     global $USER, $CFG;
05633 
05634     $link  = false;
05635 
05636     if (!empty($posts[$parent->id]->children)) {
05637         $posts = $posts[$parent->id]->children;
05638 
05639         $modcontext       = get_context_instance(CONTEXT_MODULE, $cm->id);
05640         $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
05641 
05642         foreach ($posts as $post) {
05643 
05644             echo '<div class="indent">';
05645             if ($depth > 0) {
05646                 $ownpost = ($USER->id == $post->userid);
05647                 $post->subject = format_string($post->subject);
05648 
05649                 $postread = !empty($post->postread);
05650 
05651                 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
05652                                      '', '', $postread, true, $forumtracked);
05653             } else {
05654                 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
05655                     echo "</div>\n";
05656                     continue;
05657                 }
05658                 $by = new stdClass();
05659                 $by->name = fullname($post, $canviewfullnames);
05660                 $by->date = userdate($post->modified);
05661 
05662                 if ($forumtracked) {
05663                     if (!empty($post->postread)) {
05664                         $style = '<span class="forumthread read">';
05665                     } else {
05666                         $style = '<span class="forumthread unread">';
05667                     }
05668                 } else {
05669                     $style = '<span class="forumthread">';
05670                 }
05671                 echo $style."<a name=\"$post->id\"></a>".
05672                      "<a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a> ";
05673                 print_string("bynameondate", "forum", $by);
05674                 echo "</span>";
05675             }
05676 
05677             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts);
05678             echo "</div>\n";
05679         }
05680     }
05681 }
05682 
05689 function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) {
05690     global $USER, $CFG;
05691 
05692     $link  = false;
05693 
05694     if (!empty($posts[$parent->id]->children)) {
05695         $posts = $posts[$parent->id]->children;
05696 
05697         foreach ($posts as $post) {
05698 
05699             echo '<div class="indent">';
05700             if (!isloggedin()) {
05701                 $ownpost = false;
05702             } else {
05703                 $ownpost = ($USER->id == $post->userid);
05704             }
05705 
05706             $post->subject = format_string($post->subject);
05707             $postread = !empty($post->postread);
05708 
05709             forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
05710                                  '', '', $postread, true, $forumtracked);
05711             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
05712             echo "</div>\n";
05713         }
05714     }
05715 }
05716 
05726 function forum_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0)  {
05727     global $CFG, $COURSE, $USER, $DB;
05728 
05729     if ($COURSE->id == $courseid) {
05730         $course = $COURSE;
05731     } else {
05732         $course = $DB->get_record('course', array('id' => $courseid));
05733     }
05734 
05735     $modinfo =& get_fast_modinfo($course);
05736 
05737     $cm = $modinfo->cms[$cmid];
05738     $params = array($timestart, $cm->instance);
05739 
05740     if ($userid) {
05741         $userselect = "AND u.id = ?";
05742         $params[] = $userid;
05743     } else {
05744         $userselect = "";
05745     }
05746 
05747     if ($groupid) {
05748         $groupselect = "AND gm.groupid = ?";
05749         $groupjoin   = "JOIN {groups_members} gm ON  gm.userid=u.id";
05750         $params[] = $groupid;
05751     } else {
05752         $groupselect = "";
05753         $groupjoin   = "";
05754     }
05755 
05756     if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
05757                                               d.timestart, d.timeend, d.userid AS duserid,
05758                                               u.firstname, u.lastname, u.email, u.picture, u.imagealt, u.email
05759                                          FROM {forum_posts} p
05760                                               JOIN {forum_discussions} d ON d.id = p.discussion
05761                                               JOIN {forum} f             ON f.id = d.forum
05762                                               JOIN {user} u              ON u.id = p.userid
05763                                               $groupjoin
05764                                         WHERE p.created > ? AND f.id = ?
05765                                               $userselect $groupselect
05766                                      ORDER BY p.id ASC", $params)) { // order by initial posting date
05767          return;
05768     }
05769 
05770     $groupmode       = groups_get_activity_groupmode($cm, $course);
05771     $cm_context      = get_context_instance(CONTEXT_MODULE, $cm->id);
05772     $viewhiddentimed = has_capability('mod/forum:viewhiddentimedposts', $cm_context);
05773     $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
05774 
05775     if (is_null($modinfo->groups)) {
05776         $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
05777     }
05778 
05779     $printposts = array();
05780     foreach ($posts as $post) {
05781 
05782         if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
05783           and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
05784             if (!$viewhiddentimed) {
05785                 continue;
05786             }
05787         }
05788 
05789         if ($groupmode) {
05790             if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or $accessallgroups) {
05791                 // oki (Open discussions have groupid -1)
05792             } else {
05793                 // separate mode
05794                 if (isguestuser()) {
05795                     // shortcut
05796                     continue;
05797                 }
05798 
05799                 if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
05800                     continue;
05801                 }
05802             }
05803         }
05804 
05805         $printposts[] = $post;
05806     }
05807 
05808     if (!$printposts) {
05809         return;
05810     }
05811 
05812     $aname = format_string($cm->name,true);
05813 
05814     foreach ($printposts as $post) {
05815         $tmpactivity = new stdClass();
05816 
05817         $tmpactivity->type         = 'forum';
05818         $tmpactivity->cmid         = $cm->id;
05819         $tmpactivity->name         = $aname;
05820         $tmpactivity->sectionnum   = $cm->sectionnum;
05821         $tmpactivity->timestamp    = $post->modified;
05822 
05823         $tmpactivity->content = new stdClass();
05824         $tmpactivity->content->id         = $post->id;
05825         $tmpactivity->content->discussion = $post->discussion;
05826         $tmpactivity->content->subject    = format_string($post->subject);
05827         $tmpactivity->content->parent     = $post->parent;
05828 
05829         $tmpactivity->user = new stdClass();
05830         $tmpactivity->user->id        = $post->userid;
05831         $tmpactivity->user->firstname = $post->firstname;
05832         $tmpactivity->user->lastname  = $post->lastname;
05833         $tmpactivity->user->picture   = $post->picture;
05834         $tmpactivity->user->imagealt  = $post->imagealt;
05835         $tmpactivity->user->email     = $post->email;
05836 
05837         $activities[$index++] = $tmpactivity;
05838     }
05839 
05840     return;
05841 }
05842 
05847 function forum_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
05848     global $CFG, $OUTPUT;
05849 
05850     if ($activity->content->parent) {
05851         $class = 'reply';
05852     } else {
05853         $class = 'discussion';
05854     }
05855 
05856     echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
05857 
05858     echo "<tr><td class=\"userpicture\" valign=\"top\">";
05859     echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid));
05860     echo "</td><td class=\"$class\">";
05861 
05862     echo '<div class="title">';
05863     if ($detail) {
05864         $aname = s($activity->name);
05865         echo "<img src=\"" . $OUTPUT->pix_url('icon', $activity->type) . "\" ".
05866              "class=\"icon\" alt=\"{$aname}\" />";
05867     }
05868     echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d={$activity->content->discussion}"
05869          ."#p{$activity->content->id}\">{$activity->content->subject}</a>";
05870     echo '</div>';
05871 
05872     echo '<div class="user">';
05873     $fullname = fullname($activity->user, $viewfullnames);
05874     echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">"
05875          ."{$fullname}</a> - ".userdate($activity->timestamp);
05876     echo '</div>';
05877       echo "</td></tr></table>";
05878 
05879     return;
05880 }
05881 
05891 function forum_change_discussionid($postid, $discussionid) {
05892     global $DB;
05893     $DB->set_field('forum_posts', 'discussion', $discussionid, array('id' => $postid));
05894     if ($posts = $DB->get_records('forum_posts', array('parent' => $postid))) {
05895         foreach ($posts as $post) {
05896             forum_change_discussionid($post->id, $discussionid);
05897         }
05898     }
05899     return true;
05900 }
05901 
05911 function forum_update_subscriptions_button($courseid, $forumid) {
05912     global $CFG, $USER;
05913 
05914     if (!empty($USER->subscriptionsediting)) {
05915         $string = get_string('turneditingoff');
05916         $edit = "off";
05917     } else {
05918         $string = get_string('turneditingon');
05919         $edit = "on";
05920     }
05921 
05922     return "<form method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
05923            "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
05924            "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
05925            "<input type=\"submit\" value=\"$string\" /></form>";
05926 }
05927 
05934 function forum_user_enrolled($cp) {
05935     global $DB;
05936 
05937     // NOTE: this has to be as fast as possible - we do not want to slow down enrolments!
05938     //       Originally there used to be 'mod/forum:initialsubscriptions' which was
05939     //       introduced because we did not have enrolment information in earlier versions...
05940 
05941     $sql = "SELECT f.id
05942               FROM {forum} f
05943          LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
05944              WHERE f.course = :courseid AND f.forcesubscribe = :initial AND fs.id IS NULL";
05945     $params = array('courseid'=>$cp->courseid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE);
05946 
05947     $forums = $DB->get_records_sql($sql, $params);
05948     foreach ($forums as $forum) {
05949         forum_subscribe($cp->userid, $forum->id);
05950     }
05951 }
05952 
05959 function forum_user_unenrolled($cp) {
05960     global $DB;
05961 
05962     // NOTE: this has to be as fast as possible!
05963 
05964     if ($cp->lastenrol) {
05965         $params = array('userid'=>$cp->userid, 'courseid'=>$cp->courseid);
05966         $forumselect = "IN (SELECT f.id FROM {forum} f WHERE f.course = :courseid)";
05967 
05968         $DB->delete_records_select('forum_subscriptions', "userid = :userid AND forum $forumselect", $params);
05969         $DB->delete_records_select('forum_track_prefs',   "userid = :userid AND forumid $forumselect", $params);
05970         $DB->delete_records_select('forum_read',          "userid = :userid AND forumid $forumselect", $params);
05971     }
05972 }
05973 
05974 // Functions to do with read tracking.
05975 
05985 function forum_tp_mark_posts_read($user, $postids) {
05986     global $CFG, $DB;
05987 
05988     if (!forum_tp_can_track_forums(false, $user)) {
05989         return true;
05990     }
05991 
05992     $status = true;
05993 
05994     $now = time();
05995     $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
05996 
05997     if (empty($postids)) {
05998         return true;
05999 
06000     } else if (count($postids) > 200) {
06001         while ($part = array_splice($postids, 0, 200)) {
06002             $status = forum_tp_mark_posts_read($user, $part) && $status;
06003         }
06004         return $status;
06005     }
06006 
06007     list($usql, $params) = $DB->get_in_or_equal($postids);
06008     $params[] = $user->id;
06009 
06010     $sql = "SELECT id
06011               FROM {forum_read}
06012              WHERE postid $usql AND userid = ?";
06013     if ($existing = $DB->get_records_sql($sql, $params)) {
06014         $existing = array_keys($existing);
06015     } else {
06016         $existing = array();
06017     }
06018 
06019     $new = array_diff($postids, $existing);
06020 
06021     if ($new) {
06022         list($usql, $new_params) = $DB->get_in_or_equal($new);
06023         $params = array($user->id, $now, $now, $user->id);
06024         $params = array_merge($params, $new_params);
06025         $params[] = $cutoffdate;
06026 
06027         $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
06028 
06029                 SELECT ?, p.id, p.discussion, d.forum, ?, ?
06030                   FROM {forum_posts} p
06031                        JOIN {forum_discussions} d       ON d.id = p.discussion
06032                        JOIN {forum} f                   ON f.id = d.forum
06033                        LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id)
06034                  WHERE p.id $usql
06035                        AND p.modified >= ?
06036                        AND (f.trackingtype = ".FORUM_TRACKING_ON."
06037                             OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))";
06038         $status = $DB->execute($sql, $params) && $status;
06039     }
06040 
06041     if ($existing) {
06042         list($usql, $new_params) = $DB->get_in_or_equal($existing);
06043         $params = array($now, $user->id);
06044         $params = array_merge($params, $new_params);
06045 
06046         $sql = "UPDATE {forum_read}
06047                    SET lastread = ?
06048                  WHERE userid = ? AND postid $usql";
06049         $status = $DB->execute($sql, $params) && $status;
06050     }
06051 
06052     return $status;
06053 }
06054 
06062 function forum_tp_add_read_record($userid, $postid) {
06063     global $CFG, $DB;
06064 
06065     $now = time();
06066     $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
06067 
06068     if (!$DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $postid))) {
06069         $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
06070 
06071                 SELECT ?, p.id, p.discussion, d.forum, ?, ?
06072                   FROM {forum_posts} p
06073                        JOIN {forum_discussions} d ON d.id = p.discussion
06074                  WHERE p.id = ? AND p.modified >= ?";
06075         return $DB->execute($sql, array($userid, $now, $now, $postid, $cutoffdate));
06076 
06077     } else {
06078         $sql = "UPDATE {forum_read}
06079                    SET lastread = ?
06080                  WHERE userid = ? AND postid = ?";
06081         return $DB->execute($sql, array($now, $userid, $userid));
06082     }
06083 }
06084 
06096 function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
06097     global $DB;
06098     $select = '';
06099     $params = array();
06100 
06101     if ($userid > -1) {
06102         if ($select != '') $select .= ' AND ';
06103         $select .= 'userid = ?';
06104         $params[] = $userid;
06105     }
06106     if ($postid > -1) {
06107         if ($select != '') $select .= ' AND ';
06108         $select .= 'postid = ?';
06109         $params[] = $postid;
06110     }
06111     if ($discussionid > -1) {
06112         if ($select != '') $select .= ' AND ';
06113         $select .= 'discussionid = ?';
06114         $params[] = $discussionid;
06115     }
06116     if ($forumid > -1) {
06117         if ($select != '') $select .= ' AND ';
06118         $select .= 'forumid = ?';
06119         $params[] = $forumid;
06120     }
06121 
06122     return $DB->get_records_select('forum_read', $select, $params);
06123 }
06124 
06132 function forum_tp_get_discussion_read_records($userid, $discussionid) {
06133     global $DB;
06134     $select = 'userid = ? AND discussionid = ?';
06135     $fields = 'postid, firstread, lastread';
06136     return $DB->get_records_select('forum_read', $select, array($userid, $discussionid), '', $fields);
06137 }
06138 
06144 function forum_tp_mark_post_read($userid, $post, $forumid) {
06145     if (!forum_tp_is_post_old($post)) {
06146         return forum_tp_add_read_record($userid, $post->id);
06147     } else {
06148         return true;
06149     }
06150 }
06151 
06162 function forum_tp_mark_forum_read($user, $forumid, $groupid=false) {
06163     global $CFG, $DB;
06164 
06165     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
06166 
06167     $groupsel = "";
06168     $params = array($user->id, $forumid, $cutoffdate);
06169 
06170     if ($groupid !== false) {
06171         $groupsel = " AND (d.groupid = ? OR d.groupid = -1)";
06172         $params[] = $groupid;
06173     }
06174 
06175     $sql = "SELECT p.id
06176               FROM {forum_posts} p
06177                    LEFT JOIN {forum_discussions} d ON d.id = p.discussion
06178                    LEFT JOIN {forum_read} r        ON (r.postid = p.id AND r.userid = ?)
06179              WHERE d.forum = ?
06180                    AND p.modified >= ? AND r.id is NULL
06181                    $groupsel";
06182 
06183     if ($posts = $DB->get_records_sql($sql, $params)) {
06184         $postids = array_keys($posts);
06185         return forum_tp_mark_posts_read($user, $postids);
06186     }
06187 
06188     return true;
06189 }
06190 
06200 function forum_tp_mark_discussion_read($user, $discussionid) {
06201     global $CFG, $DB;
06202 
06203     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
06204 
06205     $sql = "SELECT p.id
06206               FROM {forum_posts} p
06207                    LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?)
06208              WHERE p.discussion = ?
06209                    AND p.modified >= ? AND r.id is NULL";
06210 
06211     if ($posts = $DB->get_records_sql($sql, array($user->id, $discussionid, $cutoffdate))) {
06212         $postids = array_keys($posts);
06213         return forum_tp_mark_posts_read($user, $postids);
06214     }
06215 
06216     return true;
06217 }
06218 
06224 function forum_tp_is_post_read($userid, $post) {
06225     global $DB;
06226     return (forum_tp_is_post_old($post) ||
06227             $DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $post->id)));
06228 }
06229 
06235 function forum_tp_is_post_old($post, $time=null) {
06236     global $CFG;
06237 
06238     if (is_null($time)) {
06239         $time = time();
06240     }
06241     return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600)));
06242 }
06243 
06253 function forum_tp_count_discussion_read_records($userid, $discussionid) {
06254     global $CFG, $DB;
06255 
06256     $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
06257 
06258     $sql = 'SELECT COUNT(DISTINCT p.id) '.
06259            'FROM {forum_discussions} d '.
06260            'LEFT JOIN {forum_read} r ON d.id = r.discussionid AND r.userid = ? '.
06261            'LEFT JOIN {forum_posts} p ON p.discussion = d.id '.
06262                 'AND (p.modified < ? OR p.id = r.postid) '.
06263            'WHERE d.id = ? ';
06264 
06265     return ($DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid)));
06266 }
06267 
06277 function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
06278     global $CFG, $DB;
06279 
06280     $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
06281 
06282     $sql = 'SELECT COUNT(p.id) '.
06283            'FROM {forum_posts} p '.
06284            'LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? '.
06285            'WHERE p.discussion = ? '.
06286                 'AND p.modified >= ? AND r.id is NULL';
06287 
06288     return $DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid));
06289 }
06290 
06299 function forum_tp_count_forum_posts($forumid, $groupid=false) {
06300     global $CFG, $DB;
06301     $params = array($forumid);
06302     $sql = 'SELECT COUNT(*) '.
06303            'FROM {forum_posts} fp,{forum_discussions} fd '.
06304            'WHERE fd.forum = ? AND fp.discussion = fd.id';
06305     if ($groupid !== false) {
06306         $sql .= ' AND (fd.groupid = ? OR fd.groupid = -1)';
06307         $params[] = $groupid;
06308     }
06309     $count = $DB->count_records_sql($sql, $params);
06310 
06311 
06312     return $count;
06313 }
06314 
06324 function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
06325     global $CFG, $DB;
06326 
06327     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
06328 
06329     $groupsel = '';
06330     $params = array($userid, $forumid, $cutoffdate);
06331     if ($groupid !== false) {
06332         $groupsel = "AND (d.groupid = ? OR d.groupid = -1)";
06333         $params[] = $groupid;
06334     }
06335 
06336     $sql = "SELECT COUNT(p.id)
06337               FROM  {forum_posts} p
06338                     JOIN {forum_discussions} d ON d.id = p.discussion
06339                     LEFT JOIN {forum_read} r   ON (r.postid = p.id AND r.userid= ?)
06340               WHERE d.forum = ?
06341                     AND (p.modified < $cutoffdate OR (p.modified >= ? AND r.id IS NOT NULL))
06342                     $groupsel";
06343 
06344     return $DB->get_field_sql($sql, $params);
06345 }
06346 
06357 function forum_tp_get_course_unread_posts($userid, $courseid) {
06358     global $CFG, $DB;
06359 
06360     $now = round(time(), -2); // db cache friendliness
06361     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
06362     $params = array($userid, $userid, $courseid, $cutoffdate);
06363 
06364     if (!empty($CFG->forum_enabletimedposts)) {
06365         $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
06366         $params[] = $now;
06367         $params[] = $now;
06368     } else {
06369         $timedsql = "";
06370     }
06371 
06372     $sql = "SELECT f.id, COUNT(p.id) AS unread
06373               FROM {forum_posts} p
06374                    JOIN {forum_discussions} d       ON d.id = p.discussion
06375                    JOIN {forum} f                   ON f.id = d.forum
06376                    JOIN {course} c                  ON c.id = f.course
06377                    LEFT JOIN {forum_read} r         ON (r.postid = p.id AND r.userid = ?)
06378                    LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id)
06379              WHERE f.course = ?
06380                    AND p.modified >= ? AND r.id is NULL
06381                    AND (f.trackingtype = ".FORUM_TRACKING_ON."
06382                         OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))
06383                    $timedsql
06384           GROUP BY f.id";
06385 
06386     if ($return = $DB->get_records_sql($sql, $params)) {
06387         return $return;
06388     }
06389 
06390     return array();
06391 }
06392 
06403 function forum_tp_count_forum_unread_posts($cm, $course) {
06404     global $CFG, $USER, $DB;
06405 
06406     static $readcache = array();
06407 
06408     $forumid = $cm->instance;
06409 
06410     if (!isset($readcache[$course->id])) {
06411         $readcache[$course->id] = array();
06412         if ($counts = forum_tp_get_course_unread_posts($USER->id, $course->id)) {
06413             foreach ($counts as $count) {
06414                 $readcache[$course->id][$count->id] = $count->unread;
06415             }
06416         }
06417     }
06418 
06419     if (empty($readcache[$course->id][$forumid])) {
06420         // no need to check group mode ;-)
06421         return 0;
06422     }
06423 
06424     $groupmode = groups_get_activity_groupmode($cm, $course);
06425 
06426     if ($groupmode != SEPARATEGROUPS) {
06427         return $readcache[$course->id][$forumid];
06428     }
06429 
06430     if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
06431         return $readcache[$course->id][$forumid];
06432     }
06433 
06434     require_once($CFG->dirroot.'/course/lib.php');
06435 
06436     $modinfo =& get_fast_modinfo($course);
06437     if (is_null($modinfo->groups)) {
06438         $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
06439     }
06440 
06441     $mygroups = $modinfo->groups[$cm->groupingid];
06442 
06443     // add all groups posts
06444     if (empty($mygroups)) {
06445         $mygroups = array(-1=>-1);
06446     } else {
06447         $mygroups[-1] = -1;
06448     }
06449 
06450     list ($groups_sql, $groups_params) = $DB->get_in_or_equal($mygroups);
06451 
06452     $now = round(time(), -2); // db cache friendliness
06453     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
06454     $params = array($USER->id, $forumid, $cutoffdate);
06455 
06456     if (!empty($CFG->forum_enabletimedposts)) {
06457         $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
06458         $params[] = $now;
06459         $params[] = $now;
06460     } else {
06461         $timedsql = "";
06462     }
06463 
06464     $params = array_merge($params, $groups_params);
06465 
06466     $sql = "SELECT COUNT(p.id)
06467               FROM {forum_posts} p
06468                    JOIN {forum_discussions} d ON p.discussion = d.id
06469                    LEFT JOIN {forum_read} r   ON (r.postid = p.id AND r.userid = ?)
06470              WHERE d.forum = ?
06471                    AND p.modified >= ? AND r.id is NULL
06472                    $timedsql
06473                    AND d.groupid $groups_sql";
06474 
06475     return $DB->get_field_sql($sql, $params);
06476 }
06477 
06488 function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
06489     global $DB;
06490     $params = array();
06491 
06492     $select = '';
06493     if ($userid > -1) {
06494         if ($select != '') $select .= ' AND ';
06495         $select .= 'userid = ?';
06496         $params[] = $userid;
06497     }
06498     if ($postid > -1) {
06499         if ($select != '') $select .= ' AND ';
06500         $select .= 'postid = ?';
06501         $params[] = $postid;
06502     }
06503     if ($discussionid > -1) {
06504         if ($select != '') $select .= ' AND ';
06505         $select .= 'discussionid = ?';
06506         $params[] = $discussionid;
06507     }
06508     if ($forumid > -1) {
06509         if ($select != '') $select .= ' AND ';
06510         $select .= 'forumid = ?';
06511         $params[] = $forumid;
06512     }
06513     if ($select == '') {
06514         return false;
06515     }
06516     else {
06517         return $DB->delete_records_select('forum_read', $select, $params);
06518     }
06519 }
06529 function forum_tp_get_untracked_forums($userid, $courseid) {
06530     global $CFG, $DB;
06531 
06532     $sql = "SELECT f.id
06533               FROM {forum} f
06534                    LEFT JOIN {forum_track_prefs} ft ON (ft.forumid = f.id AND ft.userid = ?)
06535              WHERE f.course = ?
06536                    AND (f.trackingtype = ".FORUM_TRACKING_OFF."
06537                         OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND ft.id IS NOT NULL))";
06538 
06539     if ($forums = $DB->get_records_sql($sql, array($userid, $courseid))) {
06540         foreach ($forums as $forum) {
06541             $forums[$forum->id] = $forum;
06542         }
06543         return $forums;
06544 
06545     } else {
06546         return array();
06547     }
06548 }
06549 
06562 function forum_tp_can_track_forums($forum=false, $user=false) {
06563     global $USER, $CFG, $DB;
06564 
06565     // if possible, avoid expensive
06566     // queries
06567     if (empty($CFG->forum_trackreadposts)) {
06568         return false;
06569     }
06570 
06571     if ($user === false) {
06572         $user = $USER;
06573     }
06574 
06575     if (isguestuser($user) or empty($user->id)) {
06576         return false;
06577     }
06578 
06579     if ($forum === false) {
06580         // general abitily to track forums
06581         return (bool)$user->trackforums;
06582     }
06583 
06584 
06585     // Work toward always passing an object...
06586     if (is_numeric($forum)) {
06587         debugging('Better use proper forum object.', DEBUG_DEVELOPER);
06588         $forum = $DB->get_record('forum', array('id' => $forum), '', 'id,trackingtype');
06589     }
06590 
06591     $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
06592     $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
06593 
06594     return ($forumforced || $forumallows)  && !empty($user->trackforums);
06595 }
06596 
06608 function forum_tp_is_tracked($forum, $user=false) {
06609     global $USER, $CFG, $DB;
06610 
06611     if ($user === false) {
06612         $user = $USER;
06613     }
06614 
06615     if (isguestuser($user) or empty($user->id)) {
06616         return false;
06617     }
06618 
06619     // Work toward always passing an object...
06620     if (is_numeric($forum)) {
06621         debugging('Better use proper forum object.', DEBUG_DEVELOPER);
06622         $forum = $DB->get_record('forum', array('id' => $forum));
06623     }
06624 
06625     if (!forum_tp_can_track_forums($forum, $user)) {
06626         return false;
06627     }
06628 
06629     $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
06630     $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
06631 
06632     return $forumforced ||
06633            ($forumallows && $DB->get_record('forum_track_prefs', array('userid' => $user->id, 'forumid' => $forum->id)) === false);
06634 }
06635 
06642 function forum_tp_start_tracking($forumid, $userid=false) {
06643     global $USER, $DB;
06644 
06645     if ($userid === false) {
06646         $userid = $USER->id;
06647     }
06648 
06649     return $DB->delete_records('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid));
06650 }
06651 
06658 function forum_tp_stop_tracking($forumid, $userid=false) {
06659     global $USER, $DB;
06660 
06661     if ($userid === false) {
06662         $userid = $USER->id;
06663     }
06664 
06665     if (!$DB->record_exists('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid))) {
06666         $track_prefs = new stdClass();
06667         $track_prefs->userid = $userid;
06668         $track_prefs->forumid = $forumid;
06669         $DB->insert_record('forum_track_prefs', $track_prefs);
06670     }
06671 
06672     return forum_tp_delete_read_records($userid, -1, -1, $forumid);
06673 }
06674 
06675 
06682 function forum_tp_clean_read_records() {
06683     global $CFG, $DB;
06684 
06685     if (!isset($CFG->forum_oldpostdays)) {
06686         return;
06687     }
06688 // Look for records older than the cutoffdate that are still in the forum_read table.
06689     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
06690 
06691     //first get the oldest tracking present - we need tis to speedup the next delete query
06692     $sql = "SELECT MIN(fp.modified) AS first
06693               FROM {forum_posts} fp
06694                    JOIN {forum_read} fr ON fr.postid=fp.id";
06695     if (!$first = $DB->get_field_sql($sql)) {
06696         // nothing to delete;
06697         return;
06698     }
06699 
06700     // now delete old tracking info
06701     $sql = "DELETE
06702               FROM {forum_read}
06703              WHERE postid IN (SELECT fp.id
06704                                 FROM {forum_posts} fp
06705                                WHERE fp.modified >= ? AND fp.modified < ?)";
06706     $DB->execute($sql, array($first, $cutoffdate));
06707 }
06708 
06717 function forum_discussion_update_last_post($discussionid) {
06718     global $CFG, $DB;
06719 
06720 // Check the given discussion exists
06721     if (!$DB->record_exists('forum_discussions', array('id' => $discussionid))) {
06722         return false;
06723     }
06724 
06725 // Use SQL to find the last post for this discussion
06726     $sql = "SELECT id, userid, modified
06727               FROM {forum_posts}
06728              WHERE discussion=?
06729              ORDER BY modified DESC";
06730 
06731 // Lets go find the last post
06732     if (($lastposts = $DB->get_records_sql($sql, array($discussionid), 0, 1))) {
06733         $lastpost = reset($lastposts);
06734         $discussionobject = new stdClass();
06735         $discussionobject->id           = $discussionid;
06736         $discussionobject->usermodified = $lastpost->userid;
06737         $discussionobject->timemodified = $lastpost->modified;
06738         $DB->update_record('forum_discussions', $discussionobject);
06739         return $lastpost->id;
06740     }
06741 
06742 // To get here either we couldn't find a post for the discussion (weird)
06743 // or we couldn't update the discussion record (weird x2)
06744     return false;
06745 }
06746 
06747 
06751 function forum_get_view_actions() {
06752     return array('view discussion', 'search', 'forum', 'forums', 'subscribers', 'view forum');
06753 }
06754 
06758 function forum_get_post_actions() {
06759     return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
06760 }
06761 
06770 function forum_get_separate_modules($courseid) {
06771 
06772     global $CFG,$DB;
06773     $forummodule = $DB->get_record("modules", array("name" => "forum"));
06774 
06775     $sql = 'SELECT f.id, f.id FROM {forum} f, {course_modules} cm WHERE
06776            f.id = cm.instance AND cm.module =? AND cm.visible = 1 AND cm.course = ?
06777            AND cm.groupmode ='.SEPARATEGROUPS;
06778 
06779     return $DB->get_records_sql($sql, array($forummodule->id, $courseid));
06780 
06781 }
06782 
06791 function forum_check_throttling($forum, $cm=null) {
06792     global $USER, $CFG, $DB, $OUTPUT;
06793 
06794     if (is_numeric($forum)) {
06795         $forum = $DB->get_record('forum',array('id'=>$forum));
06796     }
06797     if (!is_object($forum)) {
06798         return false;  // this is broken.
06799     }
06800 
06801     if (empty($forum->blockafter)) {
06802         return true;
06803     }
06804 
06805     if (empty($forum->blockperiod)) {
06806         return true;
06807     }
06808 
06809     if (!$cm) {
06810         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
06811             print_error('invalidcoursemodule');
06812         }
06813     }
06814 
06815     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
06816     if(has_capability('mod/forum:postwithoutthrottling', $modcontext)) {
06817         return true;
06818     }
06819 
06820     // get the number of posts in the last period we care about
06821     $timenow = time();
06822     $timeafter = $timenow - $forum->blockperiod;
06823 
06824     $numposts = $DB->count_records_sql('SELECT COUNT(p.id) FROM {forum_posts} p'
06825                                       .' JOIN {forum_discussions} d'
06826                                       .' ON p.discussion = d.id WHERE d.forum = ?'
06827                                       .' AND p.userid = ? AND p.created > ?', array($forum->id, $USER->id, $timeafter));
06828 
06829     $a = new stdClass();
06830     $a->blockafter = $forum->blockafter;
06831     $a->numposts = $numposts;
06832     $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
06833 
06834     if ($forum->blockafter <= $numposts) {
06835         print_error('forumblockingtoomanyposts', 'error', $CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id, $a);
06836     }
06837     if ($forum->warnafter <= $numposts) {
06838         echo $OUTPUT->notification(get_string('forumblockingalmosttoomanyposts','forum',$a));
06839     }
06840 
06841 
06842 }
06843 
06844 
06853 function forum_reset_gradebook($courseid, $type='') {
06854     global $CFG, $DB;
06855 
06856     $wheresql = '';
06857     $params = array($courseid);
06858     if ($type) {
06859         $wheresql = "AND f.type=?";
06860         $params[] = $type;
06861     }
06862 
06863     $sql = "SELECT f.*, cm.idnumber as cmidnumber, f.course as courseid
06864               FROM {forum} f, {course_modules} cm, {modules} m
06865              WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id AND f.course=? $wheresql";
06866 
06867     if ($forums = $DB->get_records_sql($sql, $params)) {
06868         foreach ($forums as $forum) {
06869             forum_grade_item_update($forum, 'reset');
06870         }
06871     }
06872 }
06873 
06884 function forum_reset_userdata($data) {
06885     global $CFG, $DB;
06886     require_once($CFG->dirroot.'/rating/lib.php');
06887 
06888     $componentstr = get_string('modulenameplural', 'forum');
06889     $status = array();
06890 
06891     $params = array($data->courseid);
06892 
06893     $removeposts = false;
06894     $typesql     = "";
06895     if (!empty($data->reset_forum_all)) {
06896         $removeposts = true;
06897         $typesstr    = get_string('resetforumsall', 'forum');
06898         $types       = array();
06899     } else if (!empty($data->reset_forum_types)){
06900         $removeposts = true;
06901         $typesql     = "";
06902         $types       = array();
06903         $forum_types_all = forum_get_forum_types_all();
06904         foreach ($data->reset_forum_types as $type) {
06905             if (!array_key_exists($type, $forum_types_all)) {
06906                 continue;
06907             }
06908             $typesql .= " AND f.type=?";
06909             $types[] = $forum_types_all[$type];
06910             $params[] = $type;
06911         }
06912         $typesstr = get_string('resetforums', 'forum').': '.implode(', ', $types);
06913     }
06914     $alldiscussionssql = "SELECT fd.id
06915                             FROM {forum_discussions} fd, {forum} f
06916                            WHERE f.course=? AND f.id=fd.forum";
06917 
06918     $allforumssql      = "SELECT f.id
06919                             FROM {forum} f
06920                            WHERE f.course=?";
06921 
06922     $allpostssql       = "SELECT fp.id
06923                             FROM {forum_posts} fp, {forum_discussions} fd, {forum} f
06924                            WHERE f.course=? AND f.id=fd.forum AND fd.id=fp.discussion";
06925 
06926     $forumssql = $forums = $rm = null;
06927 
06928     if( $removeposts || !empty($data->reset_forum_ratings) ) {
06929         $forumssql      = "$allforumssql $typesql";
06930         $forums = $forums = $DB->get_records_sql($forumssql, $params);
06931         $rm = new rating_manager();;
06932         $ratingdeloptions = new stdClass;
06933         $ratingdeloptions->component = 'mod_forum';
06934         $ratingdeloptions->ratingarea = 'post';
06935     }
06936 
06937     if ($removeposts) {
06938         $discussionssql = "$alldiscussionssql $typesql";
06939         $postssql       = "$allpostssql $typesql";
06940 
06941         // now get rid of all attachments
06942         $fs = get_file_storage();
06943         if ($forums) {
06944             foreach ($forums as $forumid=>$unused) {
06945                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
06946                     continue;
06947                 }
06948                 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
06949                 $fs->delete_area_files($context->id, 'mod_forum', 'attachment');
06950                 $fs->delete_area_files($context->id, 'mod_forum', 'post');
06951 
06952                 //remove ratings
06953                 $ratingdeloptions->contextid = $context->id;
06954                 $rm->delete_ratings($ratingdeloptions);
06955             }
06956         }
06957 
06958         // first delete all read flags
06959         $DB->delete_records_select('forum_read', "forumid IN ($forumssql)", $params);
06960 
06961         // remove tracking prefs
06962         $DB->delete_records_select('forum_track_prefs', "forumid IN ($forumssql)", $params);
06963 
06964         // remove posts from queue
06965         $DB->delete_records_select('forum_queue', "discussionid IN ($discussionssql)", $params);
06966 
06967         // all posts - initial posts must be kept in single simple discussion forums
06968         $DB->delete_records_select('forum_posts', "discussion IN ($discussionssql) AND parent <> 0", $params); // first all children
06969         $DB->delete_records_select('forum_posts', "discussion IN ($discussionssql AND f.type <> 'single') AND parent = 0", $params); // now the initial posts for non single simple
06970 
06971         // finally all discussions except single simple forums
06972         $DB->delete_records_select('forum_discussions', "forum IN ($forumssql AND f.type <> 'single')", $params);
06973 
06974         // remove all grades from gradebook
06975         if (empty($data->reset_gradebook_grades)) {
06976             if (empty($types)) {
06977                 forum_reset_gradebook($data->courseid);
06978             } else {
06979                 foreach ($types as $type) {
06980                     forum_reset_gradebook($data->courseid, $type);
06981                 }
06982             }
06983         }
06984 
06985         $status[] = array('component'=>$componentstr, 'item'=>$typesstr, 'error'=>false);
06986     }
06987 
06988     // remove all ratings in this course's forums
06989     if (!empty($data->reset_forum_ratings)) {
06990         if ($forums) {
06991             foreach ($forums as $forumid=>$unused) {
06992                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
06993                     continue;
06994                 }
06995                 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
06996 
06997                 //remove ratings
06998                 $ratingdeloptions->contextid = $context->id;
06999                 $rm->delete_ratings($ratingdeloptions);
07000             }
07001         }
07002 
07003         // remove all grades from gradebook
07004         if (empty($data->reset_gradebook_grades)) {
07005             forum_reset_gradebook($data->courseid);
07006         }
07007     }
07008 
07009     // remove all subscriptions unconditionally - even for users still enrolled in course
07010     if (!empty($data->reset_forum_subscriptions)) {
07011         $DB->delete_records_select('forum_subscriptions', "forum IN ($allforumssql)", $params);
07012         $status[] = array('component'=>$componentstr, 'item'=>get_string('resetsubscriptions','forum'), 'error'=>false);
07013     }
07014 
07015     // remove all tracking prefs unconditionally - even for users still enrolled in course
07016     if (!empty($data->reset_forum_track_prefs)) {
07017         $DB->delete_records_select('forum_track_prefs', "forumid IN ($allforumssql)", $params);
07018         $status[] = array('component'=>$componentstr, 'item'=>get_string('resettrackprefs','forum'), 'error'=>false);
07019     }
07020 
07022     if ($data->timeshift) {
07023         shift_course_mod_dates('forum', array('assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
07024         $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
07025     }
07026 
07027     return $status;
07028 }
07029 
07035 function forum_reset_course_form_definition(&$mform) {
07036     $mform->addElement('header', 'forumheader', get_string('modulenameplural', 'forum'));
07037 
07038     $mform->addElement('checkbox', 'reset_forum_all', get_string('resetforumsall','forum'));
07039 
07040     $mform->addElement('select', 'reset_forum_types', get_string('resetforums', 'forum'), forum_get_forum_types_all(), array('multiple' => 'multiple'));
07041     $mform->setAdvanced('reset_forum_types');
07042     $mform->disabledIf('reset_forum_types', 'reset_forum_all', 'checked');
07043 
07044     $mform->addElement('checkbox', 'reset_forum_subscriptions', get_string('resetsubscriptions','forum'));
07045     $mform->setAdvanced('reset_forum_subscriptions');
07046 
07047     $mform->addElement('checkbox', 'reset_forum_track_prefs', get_string('resettrackprefs','forum'));
07048     $mform->setAdvanced('reset_forum_track_prefs');
07049     $mform->disabledIf('reset_forum_track_prefs', 'reset_forum_all', 'checked');
07050 
07051     $mform->addElement('checkbox', 'reset_forum_ratings', get_string('deleteallratings'));
07052     $mform->disabledIf('reset_forum_ratings', 'reset_forum_all', 'checked');
07053 }
07054 
07059 function forum_reset_course_form_defaults($course) {
07060     return array('reset_forum_all'=>1, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1);
07061 }
07062 
07077 function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
07078                                 $studentroles=array(), $guestroles=array(), $cmid=NULL) {
07079 
07080     global $CFG, $DB, $OUTPUT;
07081 
07082     if (!isset($forum->open) && !isset($forum->assesspublic)) {
07083         // We assume that this forum has already been converted to use the
07084         // Roles System. Columns forum.open and forum.assesspublic get dropped
07085         // once the forum module has been upgraded to use Roles.
07086         return false;
07087     }
07088 
07089     if ($forum->type == 'teacher') {
07090 
07091         // Teacher forums should be converted to normal forums that
07092         // use the Roles System to implement the old behavior.
07093         // Note:
07094         //   Seems that teacher forums were never backed up in 1.6 since they
07095         //   didn't have an entry in the course_modules table.
07096         require_once($CFG->dirroot.'/course/lib.php');
07097 
07098         if ($DB->count_records('forum_discussions', array('forum' => $forum->id)) == 0) {
07099             // Delete empty teacher forums.
07100             $DB->delete_records('forum', array('id' => $forum->id));
07101         } else {
07102             // Create a course module for the forum and assign it to
07103             // section 0 in the course.
07104             $mod = new stdClass();
07105             $mod->course = $forum->course;
07106             $mod->module = $forummodid;
07107             $mod->instance = $forum->id;
07108             $mod->section = 0;
07109             $mod->visible = 0;     // Hide the forum
07110             $mod->visibleold = 0;  // Hide the forum
07111             $mod->groupmode = 0;
07112 
07113             if (!$cmid = add_course_module($mod)) {
07114                 print_error('cannotcreateinstanceforteacher', 'forum');
07115             } else {
07116                 $mod->coursemodule = $cmid;
07117                 if (!$sectionid = add_mod_to_section($mod)) {
07118                     print_error('cannotaddteacherforumto', 'forum');
07119                 } else {
07120                     $DB->set_field('course_modules', 'section', $sectionid, array('id' => $cmid));
07121                 }
07122             }
07123 
07124             // Change the forum type to general.
07125             $forum->type = 'general';
07126             $DB->update_record('forum', $forum);
07127 
07128             $context = get_context_instance(CONTEXT_MODULE, $cmid);
07129 
07130             // Create overrides for default student and guest roles (prevent).
07131             foreach ($studentroles as $studentrole) {
07132                 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
07133                 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id);
07134                 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
07135                 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
07136                 assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
07137                 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
07138                 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
07139                 assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id);
07140                 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id);
07141                 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id);
07142                 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id);
07143                 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id);
07144                 assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id);
07145                 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id);
07146                 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id);
07147                 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id);
07148                 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $studentrole->id, $context->id);
07149             }
07150             foreach ($guestroles as $guestrole) {
07151                 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
07152                 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id);
07153                 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
07154                 assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id);
07155                 assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id);
07156                 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id);
07157                 assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id);
07158                 assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id);
07159                 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id);
07160                 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id);
07161                 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id);
07162                 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id);
07163                 assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id);
07164                 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id);
07165                 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id);
07166                 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id);
07167                 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $guestrole->id, $context->id);
07168             }
07169         }
07170     } else {
07171         // Non-teacher forum.
07172 
07173         if (empty($cmid)) {
07174             // We were not given the course_module id. Try to find it.
07175             if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
07176                 echo $OUTPUT->notification('Could not get the course module for the forum');
07177                 return false;
07178             } else {
07179                 $cmid = $cm->id;
07180             }
07181         }
07182         $context = get_context_instance(CONTEXT_MODULE, $cmid);
07183 
07184         // $forum->open defines what students can do:
07185         //   0 = No discussions, no replies
07186         //   1 = No discussions, but replies are allowed
07187         //   2 = Discussions and replies are allowed
07188         switch ($forum->open) {
07189             case 0:
07190                 foreach ($studentroles as $studentrole) {
07191                     assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
07192                     assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
07193                 }
07194                 break;
07195             case 1:
07196                 foreach ($studentroles as $studentrole) {
07197                     assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
07198                     assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
07199                 }
07200                 break;
07201             case 2:
07202                 foreach ($studentroles as $studentrole) {
07203                     assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id);
07204                     assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
07205                 }
07206                 break;
07207         }
07208 
07209         // $forum->assessed defines whether forum rating is turned
07210         // on (1 or 2) and who can rate posts:
07211         //   1 = Everyone can rate posts
07212         //   2 = Only teachers can rate posts
07213         switch ($forum->assessed) {
07214             case 1:
07215                 foreach ($studentroles as $studentrole) {
07216                     assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id);
07217                 }
07218                 foreach ($teacherroles as $teacherrole) {
07219                     assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
07220                 }
07221                 break;
07222             case 2:
07223                 foreach ($studentroles as $studentrole) {
07224                     assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
07225                 }
07226                 foreach ($teacherroles as $teacherrole) {
07227                     assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
07228                 }
07229                 break;
07230         }
07231 
07232         // $forum->assesspublic defines whether students can see
07233         // everybody's ratings:
07234         //   0 = Students can only see their own ratings
07235         //   1 = Students can see everyone's ratings
07236         switch ($forum->assesspublic) {
07237             case 0:
07238                 foreach ($studentroles as $studentrole) {
07239                     assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
07240                 }
07241                 foreach ($teacherroles as $teacherrole) {
07242                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
07243                 }
07244                 break;
07245             case 1:
07246                 foreach ($studentroles as $studentrole) {
07247                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id);
07248                 }
07249                 foreach ($teacherroles as $teacherrole) {
07250                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
07251                 }
07252                 break;
07253         }
07254 
07255         if (empty($cm)) {
07256             $cm = $DB->get_record('course_modules', array('id' => $cmid));
07257         }
07258 
07259         // $cm->groupmode:
07260         // 0 - No groups
07261         // 1 - Separate groups
07262         // 2 - Visible groups
07263         switch ($cm->groupmode) {
07264             case 0:
07265                 break;
07266             case 1:
07267                 foreach ($studentroles as $studentrole) {
07268                     assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
07269                 }
07270                 foreach ($teacherroles as $teacherrole) {
07271                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
07272                 }
07273                 break;
07274             case 2:
07275                 foreach ($studentroles as $studentrole) {
07276                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
07277                 }
07278                 foreach ($teacherroles as $teacherrole) {
07279                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
07280                 }
07281                 break;
07282         }
07283     }
07284     return true;
07285 }
07286 
07292 function forum_get_layout_modes() {
07293     return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
07294                   FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
07295                   FORUM_MODE_THREADED   => get_string('modethreaded', 'forum'),
07296                   FORUM_MODE_NESTED     => get_string('modenested', 'forum'));
07297 }
07298 
07304 function forum_get_forum_types() {
07305     return array ('general'  => get_string('generalforum', 'forum'),
07306                   'eachuser' => get_string('eachuserforum', 'forum'),
07307                   'single'   => get_string('singleforum', 'forum'),
07308                   'qanda'    => get_string('qandaforum', 'forum'),
07309                   'blog'     => get_string('blogforum', 'forum'));
07310 }
07311 
07317 function forum_get_forum_types_all() {
07318     return array ('news'     => get_string('namenews','forum'),
07319                   'social'   => get_string('namesocial','forum'),
07320                   'general'  => get_string('generalforum', 'forum'),
07321                   'eachuser' => get_string('eachuserforum', 'forum'),
07322                   'single'   => get_string('singleforum', 'forum'),
07323                   'qanda'    => get_string('qandaforum', 'forum'),
07324                   'blog'     => get_string('blogforum', 'forum'));
07325 }
07326 
07332 function forum_get_open_modes() {
07333     return array ('2' => get_string('openmode2', 'forum'),
07334                   '1' => get_string('openmode1', 'forum'),
07335                   '0' => get_string('openmode0', 'forum') );
07336 }
07337 
07343 function forum_get_extra_capabilities() {
07344     return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/site:trustcontent', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate');
07345 }
07346 
07347 
07357 /*************************************************
07358 function forum_extend_navigation($navref, $course, $module, $cm) {
07359     global $CFG, $OUTPUT, $USER;
07360 
07361     $limit = 5;
07362 
07363     $discussions = forum_get_discussions($cm,"d.timemodified DESC", false, -1, $limit);
07364     $discussioncount = forum_get_discussions_count($cm);
07365     if (!is_array($discussions) || count($discussions)==0) {
07366         return;
07367     }
07368     $discussionnode = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')');
07369     $discussionnode->mainnavonly = true;
07370     $discussionnode->display = false; // Do not display on navigation (only on navbar)
07371 
07372     foreach ($discussions as $discussion) {
07373         $icon = new pix_icon('i/feedback', '');
07374         $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$discussion->discussion));
07375         $discussionnode->add($discussion->subject, $url, navigation_node::TYPE_SETTING, null, null, $icon);
07376     }
07377 
07378     if ($discussioncount > count($discussions)) {
07379         if (!empty($navref->action)) {
07380             $url = $navref->action;
07381         } else {
07382             $url = new moodle_url('/mod/forum/view.php', array('id'=>$cm->id));
07383         }
07384         $discussionnode->add(get_string('viewalldiscussions', 'forum'), $url, navigation_node::TYPE_SETTING, null, null, $icon);
07385     }
07386 
07387     $index = 0;
07388     $recentposts = array();
07389     $lastlogin = time() - COURSE_MAX_RECENT_PERIOD;
07390     if (!isguestuser() and !empty($USER->lastcourseaccess[$course->id])) {
07391         if ($USER->lastcourseaccess[$course->id] > $lastlogin) {
07392             $lastlogin = $USER->lastcourseaccess[$course->id];
07393         }
07394     }
07395     forum_get_recent_mod_activity($recentposts, $index, $lastlogin, $course->id, $cm->id);
07396 
07397     if (is_array($recentposts) && count($recentposts)>0) {
07398         $recentnode = $navref->add(get_string('recentactivity').' ('.count($recentposts).')');
07399         $recentnode->mainnavonly = true;
07400         $recentnode->display = false;
07401         foreach ($recentposts as $post) {
07402             $icon = new pix_icon('i/feedback', '');
07403             $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->content->discussion));
07404             $title = $post->content->subject."\n".userdate($post->timestamp, get_string('strftimerecent', 'langconfig'))."\n".$post->user->firstname.' '.$post->user->lastname;
07405             $recentnode->add($title, $url, navigation_node::TYPE_SETTING, null, null, $icon);
07406         }
07407     }
07408 }
07409 *************************/
07410 
07417 function forum_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $forumnode) {
07418     global $USER, $PAGE, $CFG, $DB, $OUTPUT;
07419 
07420     $forumobject = $DB->get_record("forum", array("id" => $PAGE->cm->instance));
07421     if (empty($PAGE->cm->context)) {
07422         $PAGE->cm->context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->instance);
07423     }
07424 
07425     // for some actions you need to be enrolled, beiing admin is not enough sometimes here
07426     $enrolled = is_enrolled($PAGE->cm->context, $USER, '', false);
07427     $activeenrolled = is_enrolled($PAGE->cm->context, $USER, '', true);
07428 
07429     $canmanage  = has_capability('mod/forum:managesubscriptions', $PAGE->cm->context);
07430     $subscriptionmode = forum_get_forcesubscribed($forumobject);
07431     $cansubscribe = ($activeenrolled && $subscriptionmode != FORUM_FORCESUBSCRIBE && ($subscriptionmode != FORUM_DISALLOWSUBSCRIBE || $canmanage));
07432 
07433     if ($canmanage) {
07434         $mode = $forumnode->add(get_string('subscriptionmode', 'forum'), null, navigation_node::TYPE_CONTAINER);
07435 
07436         $allowchoice = $mode->add(get_string('subscriptionoptional', 'forum'), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_CHOOSESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING);
07437         $forceforever = $mode->add(get_string("subscriptionforced", "forum"), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_FORCESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING);
07438         $forceinitially = $mode->add(get_string("subscriptionauto", "forum"), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_INITIALSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING);
07439         $disallowchoice = $mode->add(get_string('subscriptiondisabled', 'forum'), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_DISALLOWSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING);
07440 
07441         switch ($subscriptionmode) {
07442             case FORUM_CHOOSESUBSCRIBE : // 0
07443                 $allowchoice->action = null;
07444                 $allowchoice->add_class('activesetting');
07445                 break;
07446             case FORUM_FORCESUBSCRIBE : // 1
07447                 $forceforever->action = null;
07448                 $forceforever->add_class('activesetting');
07449                 break;
07450             case FORUM_INITIALSUBSCRIBE : // 2
07451                 $forceinitially->action = null;
07452                 $forceinitially->add_class('activesetting');
07453                 break;
07454             case FORUM_DISALLOWSUBSCRIBE : // 3
07455                 $disallowchoice->action = null;
07456                 $disallowchoice->add_class('activesetting');
07457                 break;
07458         }
07459 
07460     } else if ($activeenrolled) {
07461 
07462         switch ($subscriptionmode) {
07463             case FORUM_CHOOSESUBSCRIBE : // 0
07464                 $notenode = $forumnode->add(get_string('subscriptionoptional', 'forum'));
07465                 break;
07466             case FORUM_FORCESUBSCRIBE : // 1
07467                 $notenode = $forumnode->add(get_string('subscriptionforced', 'forum'));
07468                 break;
07469             case FORUM_INITIALSUBSCRIBE : // 2
07470                 $notenode = $forumnode->add(get_string('subscriptionauto', 'forum'));
07471                 break;
07472             case FORUM_DISALLOWSUBSCRIBE : // 3
07473                 $notenode = $forumnode->add(get_string('subscriptiondisabled', 'forum'));
07474                 break;
07475         }
07476     }
07477 
07478     if ($cansubscribe) {
07479         if (forum_is_subscribed($USER->id, $forumobject)) {
07480             $linktext = get_string('unsubscribe', 'forum');
07481         } else {
07482             $linktext = get_string('subscribe', 'forum');
07483         }
07484         $url = new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'sesskey'=>sesskey()));
07485         $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING);
07486     }
07487 
07488     if (has_capability('mod/forum:viewsubscribers', $PAGE->cm->context)){
07489         $url = new moodle_url('/mod/forum/subscribers.php', array('id'=>$forumobject->id));
07490         $forumnode->add(get_string('showsubscribers', 'forum'), $url, navigation_node::TYPE_SETTING);
07491     }
07492 
07493     if ($enrolled && forum_tp_can_track_forums($forumobject)) { // keep tracking info for users with suspended enrolments
07494         if ($forumobject->trackingtype != FORUM_TRACKING_OPTIONAL) {
07495             //tracking forced on or off in forum settings so dont provide a link here to change it
07496             //could add unclickable text like for forced subscription but not sure this justifies adding another menu item
07497         } else {
07498             if (forum_tp_is_tracked($forumobject)) {
07499                 $linktext = get_string('notrackforum', 'forum');
07500             } else {
07501                 $linktext = get_string('trackforum', 'forum');
07502             }
07503             $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forumobject->id));
07504             $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING);
07505         }
07506     }
07507 
07508     if (!isloggedin() && $PAGE->course->id == SITEID) {
07509         $userid = guest_user()->id;
07510     } else {
07511         $userid = $USER->id;
07512     }
07513 
07514     $hascourseaccess = ($PAGE->course->id == SITEID) || can_access_course($PAGE->course, $userid);
07515     $enablerssfeeds = !empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds);
07516 
07517     if ($enablerssfeeds && $forumobject->rsstype && $forumobject->rssarticles && $hascourseaccess) {
07518 
07519         if (!function_exists('rss_get_url')) {
07520             require_once("$CFG->libdir/rsslib.php");
07521         }
07522 
07523         if ($forumobject->rsstype == 1) {
07524             $string = get_string('rsssubscriberssdiscussions','forum');
07525         } else {
07526             $string = get_string('rsssubscriberssposts','forum');
07527         }
07528 
07529         $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $userid, "mod_forum", $forumobject->id));
07530         $forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
07531     }
07532 }
07533 
07540 abstract class forum_subscriber_selector_base extends user_selector_base {
07541 
07546     protected $forumid = null;
07551     protected $context = null;
07556     protected $currentgroup = null;
07557 
07563     public function __construct($name, $options) {
07564         $options['accesscontext'] = $options['context'];
07565         parent::__construct($name, $options);
07566         if (isset($options['context'])) {
07567             $this->context = $options['context'];
07568         }
07569         if (isset($options['currentgroup'])) {
07570             $this->currentgroup = $options['currentgroup'];
07571         }
07572         if (isset($options['forumid'])) {
07573             $this->forumid = $options['forumid'];
07574         }
07575     }
07576 
07582     protected function get_options() {
07583         global $CFG;
07584         $options = parent::get_options();
07585         $options['file'] =  substr(__FILE__, strlen($CFG->dirroot.'/'));
07586         $options['context'] = $this->context;
07587         $options['currentgroup'] = $this->currentgroup;
07588         $options['forumid'] = $this->forumid;
07589         return $options;
07590     }
07591 
07592 }
07593 
07600 class forum_potential_subscriber_selector extends forum_subscriber_selector_base {
07601 
07606     protected $forcesubscribed = false;
07611     protected $existingsubscribers = array();
07612 
07618     public function __construct($name, $options) {
07619         parent::__construct($name, $options);
07620         if (isset($options['forcesubscribed'])) {
07621             $this->forcesubscribed=true;
07622         }
07623     }
07624 
07629     protected function get_options() {
07630         $options = parent::get_options();
07631         if ($this->forcesubscribed===true) {
07632             $options['forcesubscribed']=1;
07633         }
07634         return $options;
07635     }
07636 
07646     public function find_users($search) {
07647         global $DB;
07648 
07649         $availableusers = forum_get_potential_subscribers($this->context, $this->currentgroup, $this->required_fields_sql('u'), 'u.firstname ASC, u.lastname ASC');
07650 
07651         if (empty($availableusers)) {
07652             $availableusers = array();
07653         } else if ($search) {
07654             $search = strtolower($search);
07655             foreach ($availableusers as $key=>$user) {
07656                 if (stripos($user->firstname, $search) === false && stripos($user->lastname, $search) === false) {
07657                     unset($availableusers[$key]);
07658                 }
07659             }
07660         }
07661 
07662         // Unset any existing subscribers
07663         if (count($this->existingsubscribers)>0 && !$this->forcesubscribed) {
07664             foreach ($this->existingsubscribers as $group) {
07665                 foreach ($group as $user) {
07666                     if (array_key_exists($user->id, $availableusers)) {
07667                         unset($availableusers[$user->id]);
07668                     }
07669                 }
07670             }
07671         }
07672 
07673         if ($this->forcesubscribed) {
07674             return array(get_string("existingsubscribers", 'forum') => $availableusers);
07675         } else {
07676             return array(get_string("potentialsubscribers", 'forum') => $availableusers);
07677         }
07678     }
07679 
07684     public function set_existing_subscribers(array $users) {
07685         $this->existingsubscribers = $users;
07686     }
07687 
07691     public function set_force_subscribed($setting=true) {
07692         $this->forcesubscribed = true;
07693     }
07694 }
07695 
07702 class forum_existing_subscriber_selector extends forum_subscriber_selector_base {
07703 
07710     public function find_users($search) {
07711         global $DB;
07712         list($wherecondition, $params) = $this->search_sql($search, 'u');
07713         $params['forumid'] = $this->forumid;
07714 
07715         // only active enrolled or everybody on the frontpage
07716         list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true);
07717         $params = array_merge($params, $eparams);
07718 
07719         $fields = $this->required_fields_sql('u');
07720 
07721         $subscribers = $DB->get_records_sql("SELECT $fields
07722                                                FROM {user} u
07723                                                JOIN ($esql) je ON je.id = u.id
07724                                                JOIN {forum_subscriptions} s ON s.userid = u.id
07725                                               WHERE $wherecondition AND s.forum = :forumid
07726                                            ORDER BY u.lastname ASC, u.firstname ASC", $params);
07727 
07728         return array(get_string("existingsubscribers", 'forum') => $subscribers);
07729     }
07730 
07731 }
07732 
07738 function forum_cm_info_view(cm_info $cm) {
07739     global $CFG;
07740 
07741     // Get tracking status (once per request)
07742     static $initialised;
07743     static $usetracking, $strunreadpostsone;
07744     if (!isset($initialised)) {
07745         if ($usetracking = forum_tp_can_track_forums()) {
07746             $strunreadpostsone = get_string('unreadpostsone', 'forum');
07747         }
07748         $initialised = true;
07749     }
07750 
07751     if ($usetracking) {
07752         if ($unread = forum_tp_count_forum_unread_posts($cm, $cm->get_course())) {
07753             $out = '<span class="unread"> <a href="' . $cm->get_url() . '">';
07754             if ($unread == 1) {
07755                 $out .= $strunreadpostsone;
07756             } else {
07757                 $out .= get_string('unreadpostsnumber', 'forum', $unread);
07758             }
07759             $out .= '</a></span>';
07760             $cm->set_after_link($out);
07761         }
07762     }
07763 }
07764 
07771 function forum_page_type_list($pagetype, $parentcontext, $currentcontext) {
07772     $forum_pagetype = array(
07773         'mod-forum-*'=>get_string('page-mod-forum-x', 'forum'),
07774         'mod-forum-view'=>get_string('page-mod-forum-view', 'forum'),
07775         'mod-forum-discuss'=>get_string('page-mod-forum-discuss', 'forum')
07776     );
07777     return $forum_pagetype;
07778 }
07779 
07791 function forum_get_courses_user_posted_in($user, $discussionsonly = false, $includecontexts = true, $limitfrom = null, $limitnum = null) {
07792     global $DB;
07793 
07794     // If we are only after discussions we need only look at the forum_discussions
07795     // table and join to the userid there. If we are looking for posts then we need
07796     // to join to the forum_posts table.
07797     if (!$discussionsonly) {
07798         $joinsql = 'JOIN {forum_discussions} fd ON fd.course = c.id
07799                     JOIN {forum_posts} fp ON fp.discussion = fd.id';
07800         $wheresql = 'fp.userid = :userid';
07801         $params = array('userid' => $user->id);
07802     } else {
07803         $joinsql = 'JOIN {forum_discussions} fd ON fd.course = c.id';
07804         $wheresql = 'fd.userid = :userid';
07805         $params = array('userid' => $user->id);
07806     }
07807 
07808     // Join to the context table so that we can preload contexts if required.
07809     if ($includecontexts) {
07810         list($ctxselect, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
07811     } else {
07812         $ctxselect = '';
07813         $ctxjoin = '';
07814     }
07815 
07816     // Now we need to get all of the courses to search.
07817     // All courses where the user has posted within a forum will be returned.
07818     $sql = "SELECT DISTINCT c.* $ctxselect
07819             FROM {course} c
07820             $joinsql
07821             $ctxjoin
07822             WHERE $wheresql";
07823     $courses = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
07824     if ($includecontexts) {
07825         array_map('context_instance_preload', $courses);
07826     }
07827     return $courses;
07828 }
07829 
07843 function forum_get_forums_user_posted_in($user, array $courseids = null, $discussionsonly = false, $limitfrom = null, $limitnum = null) {
07844     global $DB;
07845 
07846     $where = array("m.name = 'forum'");
07847     $params = array();
07848     if (!is_null($courseids)) {
07849         list($coursewhere, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'courseid');
07850         $where[] = 'f.course '.$coursewhere;
07851     }
07852     if (!$discussionsonly) {
07853         $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id
07854                     JOIN {forum_posts} fp ON fp.discussion = fd.id';
07855         $where[] = 'fp.userid = :userid';
07856     } else {
07857         $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id';
07858         $where[] = 'fd.userid = :userid';
07859     }
07860     $params['userid'] = $user->id;
07861     $wheresql = join(' AND ', $where);
07862 
07863     $sql = "SELECT DISTINCT f.*, cm.id AS cmid
07864             FROM {forum} f
07865             JOIN {course_modules} cm ON cm.instance = f.id
07866             JOIN {modules} m ON m.id = cm.module
07867             $joinsql
07868             WHERE $wheresql";
07869     $courseforums = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
07870     return $courseforums;
07871 }
07872 
07901 function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false, $discussionsonly = false, $limitfrom = 0, $limitnum = 50) {
07902     global $DB, $USER, $CFG;
07903 
07904     $return = new stdClass;
07905     $return->totalcount = 0;    // The total number of posts that the current user is able to view
07906     $return->courses = array(); // The courses the current user can access
07907     $return->forums = array();  // The forums that the current user can access that contain posts
07908     $return->posts = array();   // The posts to display
07909 
07910     // First up a small sanity check. If there are no courses to check we can
07911     // return immediately, there is obviously nothing to search.
07912     if (empty($courses)) {
07913         return $return;
07914     }
07915 
07916     // A couple of quick setups
07917     $isloggedin = isloggedin();
07918     $isguestuser = $isloggedin && isguestuser();
07919     $iscurrentuser = $isloggedin && $USER->id == $user->id;
07920 
07921     // Checkout whether or not the current user has capabilities over the requested
07922     // user and if so they have the capabilities required to view the requested
07923     // users content.
07924     $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST);
07925     $hascapsonuser = !$iscurrentuser && $DB->record_exists('role_assignments', array('userid' => $USER->id, 'contextid' => $usercontext->id));
07926     $hascapsonuser = $hascapsonuser && has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), $usercontext);
07927 
07928     // Before we actually search each course we need to check the user's access to the
07929     // course. If the user doesn't have the appropraite access then we either throw an
07930     // error if a particular course was requested or we just skip over the course.
07931     foreach ($courses as $course) {
07932         $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
07933         if ($iscurrentuser || $hascapsonuser) {
07934             // If it is the current user, or the current user has capabilities to the
07935             // requested user then all we need to do is check the requested users
07936             // current access to the course.
07937             // Note: There is no need to check group access or anything of the like
07938             // as either the current user is the requested user, or has granted
07939             // capabilities on the requested user. Either way they can see what the
07940             // requested user posted, although its VERY unlikely in the `parent` situation
07941             // that the current user will be able to view the posts in context.
07942             if (!is_viewing($coursecontext, $user) && !is_enrolled($coursecontext, $user)) {
07943                 // Need to have full access to a course to see the rest of own info
07944                 if ($musthaveaccess) {
07945                     print_error('errorenrolmentrequired', 'forum');
07946                 }
07947                 continue;
07948             }
07949         } else {
07950             // Check whether the current user is enrolled or has access to view the course
07951             // if they don't we immediately have a problem.
07952             if (!can_access_course($course)) {
07953                 if ($musthaveaccess) {
07954                     print_error('errorenrolmentrequired', 'forum');
07955                 }
07956                 continue;
07957             }
07958 
07959             // Check whether the requested user is enrolled or has access to view the course
07960             // if they don't we immediately have a problem.
07961             if (!can_access_course($course, $user)) {
07962                 if ($musthaveaccess) {
07963                     print_error('notenrolled', 'forum');
07964                 }
07965                 continue;
07966             }
07967 
07968             // If groups are in use and enforced throughout the course then make sure
07969             // we can meet in at least one course level group.
07970             // Note that we check if either the current user or the requested user have
07971             // the capability to access all groups. This is because with that capability
07972             // a user in group A could post in the group B forum. Grrrr.
07973             if (groups_get_course_groupmode($course) == SEPARATEGROUPS && $course->groupmodeforce
07974               && !has_capability('moodle/site:accessallgroups', $coursecontext) && !has_capability('moodle/site:accessallgroups', $coursecontext, $user->id)) {
07975                 // If its the guest user to bad... the guest user cannot access groups
07976                 if (!$isloggedin or $isguestuser) {
07977                     // do not use require_login() here because we might have already used require_login($course)
07978                     if ($musthaveaccess) {
07979                         redirect(get_login_url());
07980                     }
07981                     continue;
07982                 }
07983                 // Get the groups of the current user
07984                 $mygroups = array_keys(groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid, 'g.id, g.name'));
07985                 // Get the groups the requested user is a member of
07986                 $usergroups = array_keys(groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name'));
07987                 // Check whether they are members of the same group. If they are great.
07988                 $intersect = array_intersect($mygroups, $usergroups);
07989                 if (empty($intersect)) {
07990                     // But they're not... if it was a specific course throw an error otherwise
07991                     // just skip this course so that it is not searched.
07992                     if ($musthaveaccess) {
07993                         print_error("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id");
07994                     }
07995                     continue;
07996                 }
07997             }
07998         }
07999         // Woo hoo we got this far which means the current user can search this
08000         // this course for the requested user. Although this is only the course accessibility
08001         // handling that is complete, the forum accessibility tests are yet to come.
08002         $return->courses[$course->id] = $course;
08003     }
08004     // No longer beed $courses array - lose it not it may be big
08005     unset($courses);
08006 
08007     // Make sure that we have some courses to search
08008     if (empty($return->courses)) {
08009         // If we don't have any courses to search then the reality is that the current
08010         // user doesn't have access to any courses is which the requested user has posted.
08011         // Although we do know at this point that the requested user has posts.
08012         if ($musthaveaccess) {
08013             print_error('permissiondenied');
08014         } else {
08015             return $return;
08016         }
08017     }
08018 
08019     // Next step: Collect all of the forums that we will want to search.
08020     // It is important to note that this step isn't actually about searching, it is
08021     // about determining which forums we can search by testing accessibility.
08022     $forums = forum_get_forums_user_posted_in($user, array_keys($return->courses), $discussionsonly);
08023 
08024     // Will be used to build the where conditions for the search
08025     $forumsearchwhere = array();
08026     // Will be used to store the where condition params for the search
08027     $forumsearchparams = array();
08028     // Will record forums where the user can freely access everything
08029     $forumsearchfullaccess = array();
08030     // DB caching friendly
08031     $now = round(time(), -2);
08032     // For each course to search we want to find the forums the user has posted in
08033     // and providing the current user can access the forum create a search condition
08034     // for the forum to get the requested users posts.
08035     foreach ($return->courses as $course) {
08036         // Now we need to get the forums
08037         $modinfo = get_fast_modinfo($course);
08038         if (empty($modinfo->instances['forum'])) {
08039             // hmmm, no forums? well at least its easy... skip!
08040             continue;
08041         }
08042         // Iterate
08043         foreach ($modinfo->get_instances_of('forum') as $forumid => $cm) {
08044             if (!$cm->uservisible or !isset($forums[$forumid])) {
08045                 continue;
08046             }
08047             // Get the forum in question
08048             $forum = $forums[$forumid];
08049             // This is needed for functionality later on in the forum code....
08050             $forum->cm = $cm;
08051 
08052             // Check that either the current user can view the forum, or that the
08053             // current user has capabilities over the requested user and the requested
08054             // user can view the discussion
08055             if (!has_capability('mod/forum:viewdiscussion', $cm->context) && !($hascapsonuser && has_capability('mod/forum:viewdiscussion', $cm->context, $user->id))) {
08056                 continue;
08057             }
08058 
08059             // This will contain forum specific where clauses
08060             $forumsearchselect = array();
08061             if (!$iscurrentuser && !$hascapsonuser) {
08062                 // Make sure we check group access
08063                 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $cm->context)) {
08064                     $groups = $modinfo->get_groups($cm->groupingid);
08065                     $groups[] = -1;
08066                     list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_');
08067                     $forumsearchparams = array_merge($forumsearchparams, $groupid_params);
08068                     $forumsearchselect[] = "d.groupid $groupid_sql";
08069                 }
08070 
08071                 // hidden timed discussions
08072                 if (!empty($CFG->forum_enabletimedposts) && !has_capability('mod/forum:viewhiddentimedposts', $cm->context)) {
08073                     $forumsearchselect[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
08074                     $forumsearchparams['userid'.$forumid] = $user->id;
08075                     $forumsearchparams['timestart'.$forumid] = $now;
08076                     $forumsearchparams['timeend'.$forumid] = $now;
08077                 }
08078 
08079                 // qanda access
08080                 if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $cm->context)) {
08081                     // We need to check whether the user has posted in the qanda forum.
08082                     $discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $user->id);
08083                     if (!empty($discussionspostedin)) {
08084                         $forumonlydiscussions = array();  // Holds discussion ids for the discussions the user is allowed to see in this forum.
08085                         foreach ($discussionspostedin as $d) {
08086                             $forumonlydiscussions[] = $d->id;
08087                         }
08088                         list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forumonlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_');
08089                         $forumsearchparams = array_merge($forumsearchparams, $discussionid_params);
08090                         $forumsearchselect[] = "(d.id $discussionid_sql OR p.parent = 0)";
08091                     } else {
08092                         $forumsearchselect[] = "p.parent = 0";
08093                     }
08094 
08095                 }
08096 
08097                 if (count($forumsearchselect) > 0) {
08098                     $forumsearchwhere[] = "(d.forum = :forum{$forumid} AND ".implode(" AND ", $forumsearchselect).")";
08099                     $forumsearchparams['forum'.$forumid] = $forumid;
08100                 } else {
08101                     $forumsearchfullaccess[] = $forumid;
08102                 }
08103             } else {
08104                 // The current user/parent can see all of their own posts
08105                 $forumsearchfullaccess[] = $forumid;
08106             }
08107         }
08108     }
08109 
08110     // If we dont have any search conditions, and we don't have any forums where
08111     // the user has full access then we just return the default.
08112     if (empty($forumsearchwhere) && empty($forumsearchfullaccess)) {
08113         return $return;
08114     }
08115 
08116     // Prepare a where condition for the full access forums.
08117     if (count($forumsearchfullaccess) > 0) {
08118         list($fullidsql, $fullidparams) = $DB->get_in_or_equal($forumsearchfullaccess, SQL_PARAMS_NAMED, 'fula');
08119         $forumsearchparams = array_merge($forumsearchparams, $fullidparams);
08120         $forumsearchwhere[] = "(d.forum $fullidsql)";
08121     }
08122 
08123     // Prepare SQL to both count and search
08124     $userfields = user_picture::fields('u', null, 'userid');
08125     $countsql = 'SELECT COUNT(*) ';
08126     $selectsql = 'SELECT p.*, d.forum, d.name AS discussionname, '.$userfields.' ';
08127     $wheresql = implode(" OR ", $forumsearchwhere);
08128 
08129     if ($discussionsonly) {
08130         if ($wheresql == '') {
08131             $wheresql = 'p.parent = 0';
08132         } else {
08133             $wheresql = 'p.parent = 0 AND ('.$wheresql.')';
08134         }
08135     }
08136 
08137     $sql = "FROM {forum_posts} p
08138             JOIN {forum_discussions} d ON d.id = p.discussion
08139             JOIN {user} u ON u.id = p.userid
08140            WHERE ($wheresql)
08141              AND p.userid = :userid ";
08142     $orderby = "ORDER BY p.modified DESC";
08143     $forumsearchparams['userid'] = $user->id;
08144 
08145     // Set the total number posts made by the requested user that the current user can see
08146     $return->totalcount = $DB->count_records_sql($countsql.$sql, $forumsearchparams);
08147     // Set the collection of posts that has been requested
08148     $return->posts = $DB->get_records_sql($selectsql.$sql.$orderby, $forumsearchparams, $limitfrom, $limitnum);
08149 
08150     // We need to build an array of forums for which posts will be displayed.
08151     // We do this here to save the caller needing to retrieve them themselves before
08152     // printing these forums posts. Given we have the forums already there is
08153     // practically no overhead here.
08154     foreach ($return->posts as $post) {
08155         if (!array_key_exists($post->forum, $return->forums)) {
08156             $return->forums[$post->forum] = $forums[$post->forum];
08157         }
08158     }
08159 
08160     return $return;
08161 }
 All Data Structures Namespaces Files Functions Variables Enumerations