Guest User

file2.php

a guest
Nov 27th, 2013
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 313.60 KB | None | 0 0
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16.  
  17. /**
  18.  * @package    mod
  19.  * @subpackage forum
  20.  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  21.  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22.  */
  23.  
  24. defined('MOODLE_INTERNAL') || die();
  25.  
  26. /** Include required files */
  27. require_once($CFG->libdir.'/filelib.php');
  28. require_once($CFG->libdir.'/eventslib.php');
  29. require_once($CFG->dirroot.'/user/selector/lib.php');
  30. require_once($CFG->dirroot.'/mod/forum/post_form.php');
  31.  
  32. /// CONSTANTS ///////////////////////////////////////////////////////////
  33.  
  34. define('FORUM_MODE_FLATOLDEST', 1);
  35. define('FORUM_MODE_FLATNEWEST', -1);
  36. define('FORUM_MODE_THREADED', 2);
  37. define('FORUM_MODE_NESTED', 3);
  38.  
  39. define('FORUM_CHOOSESUBSCRIBE', 0);
  40. define('FORUM_FORCESUBSCRIBE', 1);
  41. define('FORUM_INITIALSUBSCRIBE', 2);
  42. define('FORUM_DISALLOWSUBSCRIBE',3);
  43.  
  44. define('FORUM_TRACKING_OFF', 0);
  45. define('FORUM_TRACKING_OPTIONAL', 1);
  46. define('FORUM_TRACKING_ON', 2);
  47.  
  48. define('FORUM_MAILED_PENDING', 0);
  49. define('FORUM_MAILED_SUCCESS', 1);
  50. define('FORUM_MAILED_ERROR', 2);
  51.  
  52. if (!defined('FORUM_CRON_USER_CACHE')) {
  53.     /** Defines how many full user records are cached in forum cron. */
  54.     define('FORUM_CRON_USER_CACHE', 5000);
  55. }
  56.  
  57. /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
  58.  
  59. /**
  60.  * Given an object containing all the necessary data,
  61.  * (defined by the form in mod_form.php) this function
  62.  * will create a new instance and return the id number
  63.  * of the new instance.
  64.  *
  65.  * @param stdClass $forum add forum instance
  66.  * @param mod_forum_mod_form $mform
  67.  * @return int intance id
  68.  */
  69. function forum_add_instance($forum, $mform = null) {
  70.     global $CFG, $DB;
  71.  
  72.     $forum->timemodified = time();
  73.  
  74.     if (empty($forum->assessed)) {
  75.         $forum->assessed = 0;
  76.     }
  77.  
  78.     if (empty($forum->ratingtime) or empty($forum->assessed)) {
  79.         $forum->assesstimestart  = 0;
  80.         $forum->assesstimefinish = 0;
  81.     }
  82.  
  83.     $forum->id = $DB->insert_record('forum', $forum);
  84.     $modcontext = context_module::instance($forum->coursemodule);
  85.  
  86.     if ($forum->type == 'single') {  // Create related discussion.
  87.         $discussion = new stdClass();
  88.         $discussion->course        = $forum->course;
  89.         $discussion->forum         = $forum->id;
  90.         $discussion->name          = $forum->name;
  91.         $discussion->assessed      = $forum->assessed;
  92.         $discussion->message       = $forum->intro;
  93.         $discussion->messageformat = $forum->introformat;
  94.         $discussion->messagetrust  = trusttext_trusted(context_course::instance($forum->course));
  95.         $discussion->mailnow       = false;
  96.         $discussion->groupid       = -1;
  97.  
  98.         $message = '';
  99.  
  100.         $discussion->id = forum_add_discussion($discussion, null, $message);
  101.  
  102.         if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) {
  103.             // Ugly hack - we need to copy the files somehow.
  104.             $discussion = $DB->get_record('forum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST);
  105.             $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST);
  106.  
  107.             $options = array('subdirs'=>true); // Use the same options as intro field!
  108.             $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, $options, $post->message);
  109.             $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
  110.         }
  111.     }
  112.  
  113.     if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) {
  114.         $users = forum_get_potential_subscribers($modcontext, 0, 'u.id, u.email');
  115.         foreach ($users as $user) {
  116.             forum_subscribe($user->id, $forum->id);
  117.         }
  118.     }
  119.  
  120.     forum_grade_item_update($forum);
  121.  
  122.     return $forum->id;
  123. }
  124.  
  125.  
  126. /**
  127.  * Given an object containing all the necessary data,
  128.  * (defined by the form in mod_form.php) this function
  129.  * will update an existing instance with new data.
  130.  *
  131.  * @global object
  132.  * @param object $forum forum instance (with magic quotes)
  133.  * @return bool success
  134.  */
  135. function forum_update_instance($forum, $mform) {
  136.     global $DB, $OUTPUT, $USER;
  137.  
  138.     $forum->timemodified = time();
  139.     $forum->id           = $forum->instance;
  140.  
  141.     if (empty($forum->assessed)) {
  142.         $forum->assessed = 0;
  143.     }
  144.  
  145.     if (empty($forum->ratingtime) or empty($forum->assessed)) {
  146.         $forum->assesstimestart  = 0;
  147.         $forum->assesstimefinish = 0;
  148.     }
  149.  
  150.     $oldforum = $DB->get_record('forum', array('id'=>$forum->id));
  151.  
  152.     // MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum
  153.     // if  scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond?
  154.     // 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
  155.     if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) {
  156.         forum_update_grades($forum); // recalculate grades for the forum
  157.     }
  158.  
  159.     if ($forum->type == 'single') {  // Update related discussion and post.
  160.         $discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id), 'timemodified ASC');
  161.         if (!empty($discussions)) {
  162.             if (count($discussions) > 1) {
  163.                 echo $OUTPUT->notification(get_string('warnformorepost', 'forum'));
  164.             }
  165.             $discussion = array_pop($discussions);
  166.         } else {
  167.             // try to recover by creating initial discussion - MDL-16262
  168.             $discussion = new stdClass();
  169.             $discussion->course          = $forum->course;
  170.             $discussion->forum           = $forum->id;
  171.             $discussion->name            = $forum->name;
  172.             $discussion->assessed        = $forum->assessed;
  173.             $discussion->message         = $forum->intro;
  174.             $discussion->messageformat   = $forum->introformat;
  175.             $discussion->messagetrust    = true;
  176.             $discussion->mailnow         = false;
  177.             $discussion->groupid         = -1;
  178.  
  179.             $message = '';
  180.  
  181.             forum_add_discussion($discussion, null, $message);
  182.  
  183.             if (! $discussion = $DB->get_record('forum_discussions', array('forum'=>$forum->id))) {
  184.                 print_error('cannotadd', 'forum');
  185.             }
  186.         }
  187.         if (! $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost))) {
  188.             print_error('cannotfindfirstpost', 'forum');
  189.         }
  190.  
  191.         $cm         = get_coursemodule_from_instance('forum', $forum->id);
  192.         $modcontext = context_module::instance($cm->id, MUST_EXIST);
  193.  
  194.         $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST);
  195.         $post->subject       = $forum->name;
  196.         $post->message       = $forum->intro;
  197.         $post->messageformat = $forum->introformat;
  198.         $post->messagetrust  = trusttext_trusted($modcontext);
  199.         $post->modified      = $forum->timemodified;
  200.         $post->userid        = $USER->id;    // MDL-18599, so that current teacher can take ownership of activities.
  201.  
  202.         if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) {
  203.             // Ugly hack - we need to copy the files somehow.
  204.             $options = array('subdirs'=>true); // Use the same options as intro field!
  205.             $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, $options, $post->message);
  206.         }
  207.  
  208.         $DB->update_record('forum_posts', $post);
  209.         $discussion->name = $forum->name;
  210.         $DB->update_record('forum_discussions', $discussion);
  211.     }
  212.  
  213.     $DB->update_record('forum', $forum);
  214.  
  215.     $modcontext = context_module::instance($forum->coursemodule);
  216.     if (($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) && ($oldforum->forcesubscribe <> $forum->forcesubscribe)) {
  217.         $users = forum_get_potential_subscribers($modcontext, 0, 'u.id, u.email', '');
  218.         foreach ($users as $user) {
  219.             forum_subscribe($user->id, $forum->id);
  220.         }
  221.     }
  222.  
  223.     forum_grade_item_update($forum);
  224.  
  225.     return true;
  226. }
  227.  
  228.  
  229. /**
  230.  * Given an ID of an instance of this module,
  231.  * this function will permanently delete the instance
  232.  * and any data that depends on it.
  233.  *
  234.  * @global object
  235.  * @param int $id forum instance id
  236.  * @return bool success
  237.  */
  238. function forum_delete_instance($id) {
  239.     global $DB;
  240.  
  241.     if (!$forum = $DB->get_record('forum', array('id'=>$id))) {
  242.         return false;
  243.     }
  244.     if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
  245.         return false;
  246.     }
  247.     if (!$course = $DB->get_record('course', array('id'=>$cm->course))) {
  248.         return false;
  249.     }
  250.  
  251.     $context = context_module::instance($cm->id);
  252.  
  253.     // now get rid of all files
  254.     $fs = get_file_storage();
  255.     $fs->delete_area_files($context->id);
  256.  
  257.     $result = true;
  258.  
  259.     if ($discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id))) {
  260.         foreach ($discussions as $discussion) {
  261.             if (!forum_delete_discussion($discussion, true, $course, $cm, $forum)) {
  262.                 $result = false;
  263.             }
  264.         }
  265.     }
  266.  
  267.     if (!$DB->delete_records('forum_subscriptions', array('forum'=>$forum->id))) {
  268.         $result = false;
  269.     }
  270.  
  271.     forum_tp_delete_read_records(-1, -1, -1, $forum->id);
  272.  
  273.     if (!$DB->delete_records('forum', array('id'=>$forum->id))) {
  274.         $result = false;
  275.     }
  276.  
  277.     forum_grade_item_delete($forum);
  278.  
  279.     return $result;
  280. }
  281.  
  282.  
  283. /**
  284.  * Indicates API features that the forum supports.
  285.  *
  286.  * @uses FEATURE_GROUPS
  287.  * @uses FEATURE_GROUPINGS
  288.  * @uses FEATURE_GROUPMEMBERSONLY
  289.  * @uses FEATURE_MOD_INTRO
  290.  * @uses FEATURE_COMPLETION_TRACKS_VIEWS
  291.  * @uses FEATURE_COMPLETION_HAS_RULES
  292.  * @uses FEATURE_GRADE_HAS_GRADE
  293.  * @uses FEATURE_GRADE_OUTCOMES
  294.  * @param string $feature
  295.  * @return mixed True if yes (some features may use other values)
  296.  */
  297. function forum_supports($feature) {
  298.     switch($feature) {
  299.         case FEATURE_GROUPS:                  return true;
  300.         case FEATURE_GROUPINGS:               return true;
  301.         case FEATURE_GROUPMEMBERSONLY:        return true;
  302.         case FEATURE_MOD_INTRO:               return true;
  303.         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
  304.         case FEATURE_COMPLETION_HAS_RULES:    return true;
  305.         case FEATURE_GRADE_HAS_GRADE:         return true;
  306.         case FEATURE_GRADE_OUTCOMES:          return true;
  307.         case FEATURE_RATE:                    return true;
  308.         case FEATURE_BACKUP_MOODLE2:          return true;
  309.         case FEATURE_SHOW_DESCRIPTION:        return true;
  310.         case FEATURE_PLAGIARISM:              return true;
  311.  
  312.         default: return null;
  313.     }
  314. }
  315.  
  316.  
  317. /**
  318.  * Obtains the automatic completion state for this forum based on any conditions
  319.  * in forum settings.
  320.  *
  321.  * @global object
  322.  * @global object
  323.  * @param object $course Course
  324.  * @param object $cm Course-module
  325.  * @param int $userid User ID
  326.  * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
  327.  * @return bool True if completed, false if not. (If no conditions, then return
  328.  *   value depends on comparison type)
  329.  */
  330. function forum_get_completion_state($course,$cm,$userid,$type) {
  331.     global $CFG,$DB;
  332.  
  333.     // Get forum details
  334.     if (!($forum=$DB->get_record('forum',array('id'=>$cm->instance)))) {
  335.         throw new Exception("Can't find forum {$cm->instance}");
  336.     }
  337.  
  338.     $result=$type; // Default return value
  339.  
  340.     $postcountparams=array('userid'=>$userid,'forumid'=>$forum->id);
  341.     $postcountsql="
  342. SELECT
  343.    COUNT(1)
  344. FROM
  345.    {forum_posts} fp
  346.    INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id
  347. WHERE
  348.    fp.userid=:userid AND fd.forum=:forumid";
  349.  
  350.     if ($forum->completiondiscussions) {
  351.         $value = $forum->completiondiscussions <=
  352.                  $DB->count_records('forum_discussions',array('forum'=>$forum->id,'userid'=>$userid));
  353.         if ($type == COMPLETION_AND) {
  354.             $result = $result && $value;
  355.         } else {
  356.             $result = $result || $value;
  357.         }
  358.     }
  359.     if ($forum->completionreplies) {
  360.         $value = $forum->completionreplies <=
  361.                  $DB->get_field_sql( $postcountsql.' AND fp.parent<>0',$postcountparams);
  362.         if ($type==COMPLETION_AND) {
  363.             $result = $result && $value;
  364.         } else {
  365.             $result = $result || $value;
  366.         }
  367.     }
  368.     if ($forum->completionposts) {
  369.         $value = $forum->completionposts <= $DB->get_field_sql($postcountsql,$postcountparams);
  370.         if ($type == COMPLETION_AND) {
  371.             $result = $result && $value;
  372.         } else {
  373.             $result = $result || $value;
  374.         }
  375.     }
  376.  
  377.     return $result;
  378. }
  379.  
  380. /**
  381.  * Create a message-id string to use in the custom headers of forum notification emails
  382.  *
  383.  * message-id is used by email clients to identify emails and to nest conversations
  384.  *
  385.  * @param int $postid The ID of the forum post we are notifying the user about
  386.  * @param int $usertoid The ID of the user being notified
  387.  * @param string $hostname The server's hostname
  388.  * @return string A unique message-id
  389.  */
  390. function forum_get_email_message_id($postid, $usertoid, $hostname) {
  391.     return '<'.hash('sha256',$postid.'to'.$usertoid).'@'.$hostname.'>';
  392. }
  393.  
  394. /**
  395.  * Removes properties from user record that are not necessary
  396.  * for sending post notifications.
  397.  * @param stdClass $user
  398.  * @return void, $user parameter is modified
  399.  */
  400. function forum_cron_minimise_user_record(stdClass $user) {
  401.  
  402.     // We store large amount of users in one huge array,
  403.     // make sure we do not store info there we do not actually need
  404.     // in mail generation code or messaging.
  405.  
  406.     unset($user->institution);
  407.     unset($user->department);
  408.     unset($user->address);
  409.     unset($user->city);
  410.     unset($user->url);
  411.     unset($user->currentlogin);
  412.     unset($user->description);
  413.     unset($user->descriptionformat);
  414. }
  415.  
  416. /**
  417.  * Function to be run periodically according to the moodle cron
  418.  * Finds all posts that have yet to be mailed out, and mails them
  419.  * out to all subscribers
  420.  *
  421.  * @global object
  422.  * @global object
  423.  * @global object
  424.  * @uses CONTEXT_MODULE
  425.  * @uses CONTEXT_COURSE
  426.  * @uses SITEID
  427.  * @uses FORMAT_PLAIN
  428.  * @return void
  429.  */
  430. function forum_cron() {
  431.     global $CFG, $USER, $DB;
  432.  
  433.     $site = get_site();
  434.  
  435.     // All users that are subscribed to any post that needs sending,
  436.     // please increase $CFG->extramemorylimit on large sites that
  437.     // send notifications to a large number of users.
  438.     $users = array();
  439.     $userscount = 0; // Cached user counter - count($users) in PHP is horribly slow!!!
  440.  
  441.     // status arrays
  442.     $mailcount  = array();
  443.     $errorcount = array();
  444.  
  445.     // caches
  446.     $discussions     = array();
  447.     $forums          = array();
  448.     $courses         = array();
  449.     $coursemodules   = array();
  450.     $subscribedusers = array();
  451.  
  452.  
  453.     // Posts older than 2 days will not be mailed.  This is to avoid the problem where
  454.     // cron has not been running for a long time, and then suddenly people are flooded
  455.     // with mail from the past few weeks or months
  456.     $timenow   = time();
  457.     $endtime   = $timenow - $CFG->maxeditingtime;
  458.     $starttime = $endtime - 48 * 3600;   // Two days earlier
  459.  
  460.     if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
  461.         // Mark them all now as being mailed.  It's unlikely but possible there
  462.         // might be an error later so that a post is NOT actually mailed out,
  463.         // but since mail isn't crucial, we can accept this risk.  Doing it now
  464.         // prevents the risk of duplicated mails, which is a worse problem.
  465.  
  466.         if (!forum_mark_old_posts_as_mailed($endtime)) {
  467.             mtrace('Errors occurred while trying to mark some posts as being mailed.');
  468.             return false;  // Don't continue trying to mail them, in case we are in a cron loop
  469.         }
  470.  
  471.         // checking post validity, and adding users to loop through later
  472.         foreach ($posts as $pid => $post) {
  473.  
  474.             $discussionid = $post->discussion;
  475.             if (!isset($discussions[$discussionid])) {
  476.                 if ($discussion = $DB->get_record('forum_discussions', array('id'=> $post->discussion))) {
  477.                     $discussions[$discussionid] = $discussion;
  478.                 } else {
  479.                     mtrace('Could not find discussion '.$discussionid);
  480.                     unset($posts[$pid]);
  481.                     continue;
  482.                 }
  483.             }
  484.             $forumid = $discussions[$discussionid]->forum;
  485.             if (!isset($forums[$forumid])) {
  486.                 if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
  487.                     $forums[$forumid] = $forum;
  488.                 } else {
  489.                     mtrace('Could not find forum '.$forumid);
  490.                     unset($posts[$pid]);
  491.                     continue;
  492.                 }
  493.             }
  494.             $courseid = $forums[$forumid]->course;
  495.             if (!isset($courses[$courseid])) {
  496.                 if ($course = $DB->get_record('course', array('id' => $courseid))) {
  497.                     $courses[$courseid] = $course;
  498.                 } else {
  499.                     mtrace('Could not find course '.$courseid);
  500.                     unset($posts[$pid]);
  501.                     continue;
  502.                 }
  503.             }
  504.             if (!isset($coursemodules[$forumid])) {
  505.                 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
  506.                     $coursemodules[$forumid] = $cm;
  507.                 } else {
  508.                     mtrace('Could not find course module for forum '.$forumid);
  509.                     unset($posts[$pid]);
  510.                     continue;
  511.                 }
  512.             }
  513.  
  514.  
  515.             // caching subscribed users of each forum
  516.             if (!isset($subscribedusers[$forumid])) {
  517.                 $modcontext = context_module::instance($coursemodules[$forumid]->id);
  518.                 if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext, "u.*")) {
  519.                     foreach ($subusers as $postuser) {
  520.                         // this user is subscribed to this forum
  521.                         $subscribedusers[$forumid][$postuser->id] = $postuser->id;
  522.                         $userscount++;
  523.                         if ($userscount > FORUM_CRON_USER_CACHE) {
  524.                             // Store minimal user info.
  525.                             $minuser = new stdClass();
  526.                             $minuser->id = $postuser->id;
  527.                             $users[$postuser->id] = $minuser;
  528.                         } else {
  529.                             // Cache full user record.
  530.                             forum_cron_minimise_user_record($postuser);
  531.                             $users[$postuser->id] = $postuser;
  532.                         }
  533.                     }
  534.                     // Release memory.
  535.                     unset($subusers);
  536.                     unset($postuser);
  537.                 }
  538.             }
  539.  
  540.             $mailcount[$pid] = 0;
  541.             $errorcount[$pid] = 0;
  542.         }
  543.     }
  544.  
  545.     if ($users && $posts) {
  546.  
  547.         $urlinfo = parse_url($CFG->wwwroot);
  548.         $hostname = $urlinfo['host'];
  549.  
  550.         foreach ($users as $userto) {
  551.  
  552.             @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
  553.  
  554.             mtrace('Processing user '.$userto->id);
  555.  
  556.             // Init user caches - we keep the cache for one cycle only,
  557.             // otherwise it could consume too much memory.
  558.             if (isset($userto->username)) {
  559.                 $userto = clone($userto);
  560.             } else {
  561.                 $userto = $DB->get_record('user', array('id' => $userto->id));
  562.                 forum_cron_minimise_user_record($userto);
  563.             }
  564.             $userto->viewfullnames = array();
  565.             $userto->canpost       = array();
  566.             $userto->markposts     = array();
  567.  
  568.             // set this so that the capabilities are cached, and environment matches receiving user
  569.             cron_setup_user($userto);
  570.  
  571.             // reset the caches
  572.             foreach ($coursemodules as $forumid=>$unused) {
  573.                 $coursemodules[$forumid]->cache       = new stdClass();
  574.                 $coursemodules[$forumid]->cache->caps = array();
  575.                 unset($coursemodules[$forumid]->uservisible);
  576.             }
  577.  
  578.             foreach ($posts as $pid => $post) {
  579.  
  580.                 // Set up the environment for the post, discussion, forum, course
  581.                 $discussion = $discussions[$post->discussion];
  582.                 $forum      = $forums[$discussion->forum];
  583.                 $course     = $courses[$forum->course];
  584.                 $cm         =& $coursemodules[$forum->id];
  585.  
  586.                 // Do some checks  to see if we can bail out now
  587.                 // Only active enrolled users are in the list of subscribers
  588.                 if (!isset($subscribedusers[$forum->id][$userto->id])) {
  589.                     continue; // user does not subscribe to this forum
  590.                 }
  591.  
  592.                 // Don't send email if the forum is Q&A and the user has not posted
  593.                 // Initial topics are still mailed
  594.                 if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) {
  595.                     mtrace('Did not email '.$userto->id.' because user has not posted in discussion');
  596.                     continue;
  597.                 }
  598.  
  599.                 // Get info about the sending user
  600.                 if (array_key_exists($post->userid, $users)) { // we might know him/her already
  601.                     $userfrom = $users[$post->userid];
  602.                     if (!isset($userfrom->idnumber)) {
  603.                         // Minimalised user info, fetch full record.
  604.                         $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
  605.                         forum_cron_minimise_user_record($userfrom);
  606.                     }
  607.  
  608.                 } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
  609.                     forum_cron_minimise_user_record($userfrom);
  610.                     // Fetch only once if possible, we can add it to user list, it will be skipped anyway.
  611.                     if ($userscount <= FORUM_CRON_USER_CACHE) {
  612.                         $userscount++;
  613.                         $users[$userfrom->id] = $userfrom;
  614.                     }
  615.  
  616.                 } else {
  617.                     mtrace('Could not find user '.$post->userid);
  618.                     continue;
  619.                 }
  620.  
  621.                 //if we want to check that userto and userfrom are not the same person this is probably the spot to do it
  622.  
  623.                 // setup global $COURSE properly - needed for roles and languages
  624.                 cron_setup_user($userto, $course);
  625.  
  626.                 // Fill caches
  627.                 if (!isset($userto->viewfullnames[$forum->id])) {
  628.                     $modcontext = context_module::instance($cm->id);
  629.                     $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
  630.                 }
  631.                 if (!isset($userto->canpost[$discussion->id])) {
  632.                     $modcontext = context_module::instance($cm->id);
  633.                     $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  634.                 }
  635.                 if (!isset($userfrom->groups[$forum->id])) {
  636.                     if (!isset($userfrom->groups)) {
  637.                         $userfrom->groups = array();
  638.                         if (isset($users[$userfrom->id])) {
  639.                             $users[$userfrom->id]->groups = array();
  640.                         }
  641.                     }
  642.                     $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  643.                     if (isset($users[$userfrom->id])) {
  644.                         $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
  645.                     }
  646.                 }
  647.  
  648.                 // Make sure groups allow this user to see this email
  649.                 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
  650.                     if (!groups_group_exists($discussion->groupid)) { // Can't find group
  651.                         continue;                           // Be safe and don't send it to anyone
  652.                     }
  653.  
  654.                     if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
  655.                         // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
  656.                         continue;
  657.                     }
  658.                 }
  659.  
  660.                 // Make sure we're allowed to see it...
  661.                 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  662.                     mtrace('user '.$userto->id. ' can not see '.$post->id);
  663.                     continue;
  664.                 }
  665.  
  666.                 // OK so we need to send the email.
  667.  
  668.                 // Does the user want this post in a digest?  If so postpone it for now.
  669.                 if ($userto->maildigest > 0) {
  670.                     // This user wants the mails to be in digest form
  671.                     $queue = new stdClass();
  672.                     $queue->userid       = $userto->id;
  673.                     $queue->discussionid = $discussion->id;
  674.                     $queue->postid       = $post->id;
  675.                     $queue->timemodified = $post->created;
  676.                     $DB->insert_record('forum_queue', $queue);
  677.                     continue;
  678.                 }
  679.  
  680.  
  681.                 // Prepare to actually send the post now, and build up the content
  682.  
  683.                 $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
  684.  
  685.                 $userfrom->customheaders = array (  // Headers to make emails easier to track
  686.                            'Precedence: Bulk',
  687.                            'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
  688.                            'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
  689.                            'Message-ID: '.forum_get_email_message_id($post->id, $userto->id, $hostname),
  690.                            'X-Course-Id: '.$course->id,
  691.                            'X-Course-Name: '.format_string($course->fullname, true)
  692.                 );
  693.  
  694.                 if ($post->parent) {  // This post is a reply, so add headers for threading (see MDL-22551)
  695.                     $userfrom->customheaders[] = 'In-Reply-To: '.forum_get_email_message_id($post->parent, $userto->id, $hostname);
  696.                     $userfrom->customheaders[] = 'References: '.forum_get_email_message_id($post->parent, $userto->id, $hostname);
  697.                 }
  698.  
  699.                 $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
  700.  
  701.                 $postsubject = html_to_text("$shortname: ".format_string($post->subject, true));
  702.                 $posttext = forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto);
  703.                 $posthtml = forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto);
  704.  
  705.                 // Send the post now!
  706.  
  707.                 mtrace('Sending ', '');
  708.  
  709.                 $eventdata = new stdClass();
  710.                 $eventdata->component        = 'mod_forum';
  711.                 $eventdata->name             = 'posts';
  712.                 $eventdata->userfrom         = $userfrom;
  713.                 $eventdata->userto           = $userto;
  714.                 $eventdata->subject          = $postsubject;
  715.                 $eventdata->fullmessage      = $posttext;
  716.                 $eventdata->fullmessageformat = FORMAT_PLAIN;
  717.                 $eventdata->fullmessagehtml  = $posthtml;
  718.                 $eventdata->notification = 1;
  719.  
  720.                 // If forum_replytouser is not set then send mail using the noreplyaddress.
  721.                 if (empty($CFG->forum_replytouser)) {
  722.                     // Clone userfrom as it is referenced by $users.
  723.                     $cloneduserfrom = clone($userfrom);
  724.                     $cloneduserfrom->email = $CFG->noreplyaddress;
  725.                     $eventdata->userfrom = $cloneduserfrom;
  726.                 }
  727.  
  728.                 $smallmessagestrings = new stdClass();
  729.                 $smallmessagestrings->user = fullname($userfrom);
  730.                 $smallmessagestrings->forumname = "$shortname: ".format_string($forum->name,true).": ".$discussion->name;
  731.                 $smallmessagestrings->message = $post->message;
  732.                 //make sure strings are in message recipients language
  733.                 $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'forum', $smallmessagestrings, $userto->lang);
  734.  
  735.                 $eventdata->contexturl = "{$CFG->wwwroot}/mod/forum/discuss.php?d={$discussion->id}#p{$post->id}";
  736.                 $eventdata->contexturlname = $discussion->name;
  737.  
  738.                 $mailresult = message_send($eventdata);
  739.                 if (!$mailresult){
  740.                     mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id $post->id to user $userto->id".
  741.                          " ($userto->email) .. not trying again.");
  742.                     add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
  743.                                substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
  744.                     $errorcount[$post->id]++;
  745.                 } else {
  746.                     $mailcount[$post->id]++;
  747.  
  748.                 // Mark post as read if forum_usermarksread is set off
  749.                     if (!$CFG->forum_usermarksread) {
  750.                         $userto->markposts[$post->id] = $post->id;
  751.                     }
  752.                 }
  753.  
  754.                 mtrace('post '.$post->id. ': '.$post->subject);
  755.             }
  756.  
  757.             // mark processed posts as read
  758.             forum_tp_mark_posts_read($userto, $userto->markposts);
  759.             unset($userto);
  760.         }
  761.     }
  762.  
  763.     if ($posts) {
  764.         foreach ($posts as $post) {
  765.             mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
  766.             if ($errorcount[$post->id]) {
  767.                 $DB->set_field('forum_posts', 'mailed', FORUM_MAILED_ERROR, array('id' => $post->id));
  768.             }
  769.         }
  770.     }
  771.  
  772.     // release some memory
  773.     unset($subscribedusers);
  774.     unset($mailcount);
  775.     unset($errorcount);
  776.  
  777.     cron_setup_user();
  778.  
  779.     $sitetimezone = $CFG->timezone;
  780.  
  781.     // Now see if there are any digest mails waiting to be sent, and if we should send them
  782.  
  783.     mtrace('Starting digest processing...');
  784.  
  785.     @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes
  786.  
  787.     if (!isset($CFG->digestmailtimelast)) {    // To catch the first time
  788.         set_config('digestmailtimelast', 0);
  789.     }
  790.  
  791.     $timenow = time();
  792.     $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
  793.  
  794.     // Delete any really old ones (normally there shouldn't be any)
  795.     $weekago = $timenow - (7 * 24 * 3600);
  796.     $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago));
  797.     mtrace ('Cleaned old digest records');
  798.  
  799.     if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
  800.  
  801.         mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
  802.  
  803.         $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime));
  804.  
  805.         if ($digestposts_rs->valid()) {
  806.  
  807.             // We have work to do
  808.             $usermailcount = 0;
  809.  
  810.             //caches - reuse the those filled before too
  811.             $discussionposts = array();
  812.             $userdiscussions = array();
  813.  
  814.             foreach ($digestposts_rs as $digestpost) {
  815.                 if (!isset($posts[$digestpost->postid])) {
  816.                     if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
  817.                         $posts[$digestpost->postid] = $post;
  818.                     } else {
  819.                         continue;
  820.                     }
  821.                 }
  822.                 $discussionid = $digestpost->discussionid;
  823.                 if (!isset($discussions[$discussionid])) {
  824.                     if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) {
  825.                         $discussions[$discussionid] = $discussion;
  826.                     } else {
  827.                         continue;
  828.                     }
  829.                 }
  830.                 $forumid = $discussions[$discussionid]->forum;
  831.                 if (!isset($forums[$forumid])) {
  832.                     if ($forum = $DB->get_record('forum', array('id' => $forumid))) {
  833.                         $forums[$forumid] = $forum;
  834.                     } else {
  835.                         continue;
  836.                     }
  837.                 }
  838.  
  839.                 $courseid = $forums[$forumid]->course;
  840.                 if (!isset($courses[$courseid])) {
  841.                     if ($course = $DB->get_record('course', array('id' => $courseid))) {
  842.                         $courses[$courseid] = $course;
  843.                     } else {
  844.                         continue;
  845.                     }
  846.                 }
  847.  
  848.                 if (!isset($coursemodules[$forumid])) {
  849.                     if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
  850.                         $coursemodules[$forumid] = $cm;
  851.                     } else {
  852.                         continue;
  853.                     }
  854.                 }
  855.                 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
  856.                 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
  857.             }
  858.             $digestposts_rs->close(); /// Finished iteration, let's close the resultset
  859.  
  860.             // Data collected, start sending out emails to each user
  861.             foreach ($userdiscussions as $userid => $thesediscussions) {
  862.  
  863.                 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
  864.  
  865.                 cron_setup_user();
  866.  
  867.                 mtrace(get_string('processingdigest', 'forum', $userid), '... ');
  868.  
  869.                 // First of all delete all the queue entries for this user
  870.                 $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));
  871.  
  872.                 // Init user caches - we keep the cache for one cycle only,
  873.                 // otherwise it would unnecessarily consume memory.
  874.                 if (array_key_exists($userid, $users) and isset($users[$userid]->username)) {
  875.                     $userto = clone($users[$userid]);
  876.                 } else {
  877.                     $userto = $DB->get_record('user', array('id' => $userid));
  878.                     forum_cron_minimise_user_record($userto);
  879.                 }
  880.                 $userto->viewfullnames = array();
  881.                 $userto->canpost       = array();
  882.                 $userto->markposts     = array();
  883.  
  884.                 // Override the language and timezone of the "current" user, so that
  885.                 // mail is customised for the receiver.
  886.                 cron_setup_user($userto);
  887.  
  888.                 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
  889.  
  890.                 $headerdata = new stdClass();
  891.                 $headerdata->sitename = format_string($site->fullname, true);
  892.                 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
  893.  
  894.                 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
  895.                 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
  896.  
  897.                 $posthtml = "<head>";
  898. /*                foreach ($CFG->stylesheets as $stylesheet) {
  899.                     //TODO: MDL-21120
  900.                     $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
  901.                 }*/
  902.                 $posthtml .= "</head>\n<body id=\"email\">\n";
  903.                 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
  904.  
  905.                 foreach ($thesediscussions as $discussionid) {
  906.  
  907.                     @set_time_limit(120);   // to be reset for each post
  908.  
  909.                     $discussion = $discussions[$discussionid];
  910.                     $forum      = $forums[$discussion->forum];
  911.                     $course     = $courses[$forum->course];
  912.                     $cm         = $coursemodules[$forum->id];
  913.  
  914.                     //override language
  915.                     cron_setup_user($userto, $course);
  916.  
  917.                     // Fill caches
  918.                     if (!isset($userto->viewfullnames[$forum->id])) {
  919.                         $modcontext = context_module::instance($cm->id);
  920.                         $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
  921.                     }
  922.                     if (!isset($userto->canpost[$discussion->id])) {
  923.                         $modcontext = context_module::instance($cm->id);
  924.                         $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  925.                     }
  926.  
  927.                     $strforums      = get_string('forums', 'forum');
  928.                     $canunsubscribe = ! forum_is_forcesubscribed($forum);
  929.                     $canreply       = $userto->canpost[$discussion->id];
  930.                     $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
  931.  
  932.                     $posttext .= "\n \n";
  933.                     $posttext .= '=====================================================================';
  934.                     $posttext .= "\n \n";
  935.                     $posttext .= "$shortname -> $strforums -> ".format_string($forum->name,true);
  936.                     if ($discussion->name != $forum->name) {
  937.                         $posttext  .= " -> ".format_string($discussion->name,true);
  938.                     }
  939.                     $posttext .= "\n";
  940.  
  941.                     $posthtml .= "<p><font face=\"sans-serif\">".
  942.                     "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$shortname</a> -> ".
  943.                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
  944.                     "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
  945.                     if ($discussion->name == $forum->name) {
  946.                         $posthtml .= "</font></p>";
  947.                     } else {
  948.                         $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
  949.                     }
  950.                     $posthtml .= '<p>';
  951.  
  952.                     $postsarray = $discussionposts[$discussionid];
  953.                     sort($postsarray);
  954.  
  955.                     foreach ($postsarray as $postid) {
  956.                         $post = $posts[$postid];
  957.  
  958.                         if (array_key_exists($post->userid, $users)) { // we might know him/her already
  959.                             $userfrom = $users[$post->userid];
  960.                             if (!isset($userfrom->idnumber)) {
  961.                                 $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
  962.                                 forum_cron_minimise_user_record($userfrom);
  963.                             }
  964.  
  965.                         } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
  966.                             forum_cron_minimise_user_record($userfrom);
  967.                             if ($userscount <= FORUM_CRON_USER_CACHE) {
  968.                                 $userscount++;
  969.                                 $users[$userfrom->id] = $userfrom;
  970.                             }
  971.  
  972.                         } else {
  973.                             mtrace('Could not find user '.$post->userid);
  974.                             continue;
  975.                         }
  976.  
  977.                         if (!isset($userfrom->groups[$forum->id])) {
  978.                             if (!isset($userfrom->groups)) {
  979.                                 $userfrom->groups = array();
  980.                                 if (isset($users[$userfrom->id])) {
  981.                                     $users[$userfrom->id]->groups = array();
  982.                                 }
  983.                             }
  984.                             $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  985.                             if (isset($users[$userfrom->id])) {
  986.                                 $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
  987.                             }
  988.                         }
  989.  
  990.                         $userfrom->customheaders = array ("Precedence: Bulk");
  991.  
  992.                         if ($userto->maildigest == 2) {
  993.                             // Subjects only
  994.                             $by = new stdClass();
  995.                             $by->name = fullname($userfrom);
  996.                             $by->date = userdate($post->modified);
  997.                             $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
  998.                             $posttext .= "\n---------------------------------------------------------------------";
  999.  
  1000.                             $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
  1001.                             $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>';
  1002.  
  1003.                         } else {
  1004.                             // The full treatment
  1005.                             $posttext .= forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, true);
  1006.                             $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
  1007.  
  1008.                         // Create an array of postid's for this user to mark as read.
  1009.                             if (!$CFG->forum_usermarksread) {
  1010.                                 $userto->markposts[$post->id] = $post->id;
  1011.                             }
  1012.                         }
  1013.                     }
  1014.                     if ($canunsubscribe) {
  1015.                         $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>";
  1016.                     } else {
  1017.                         $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
  1018.                     }
  1019.                     $posthtml .= '<hr size="1" noshade="noshade" /></p>';
  1020.                 }
  1021.                 $posthtml .= '</body>';
  1022.  
  1023.                 if (empty($userto->mailformat) || $userto->mailformat != 1) {
  1024.                     // This user DOESN'T want to receive HTML
  1025.                     $posthtml = '';
  1026.                 }
  1027.  
  1028.                 $attachment = $attachname='';
  1029.                 // Directly email forum digests rather than sending them via messaging, use the
  1030.                 // site shortname as 'from name', the noreply address will be used by email_to_user.
  1031.                 $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname);
  1032.  
  1033.                 if (!$mailresult) {
  1034.                     mtrace("ERROR!");
  1035.                     echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
  1036.                     add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
  1037.                 } else {
  1038.                     mtrace("success.");
  1039.                     $usermailcount++;
  1040.  
  1041.                     // Mark post as read if forum_usermarksread is set off
  1042.                     forum_tp_mark_posts_read($userto, $userto->markposts);
  1043.                 }
  1044.             }
  1045.         }
  1046.     /// We have finishied all digest emails, update $CFG->digestmailtimelast
  1047.         set_config('digestmailtimelast', $timenow);
  1048.     }
  1049.  
  1050.     cron_setup_user();
  1051.  
  1052.     if (!empty($usermailcount)) {
  1053.         mtrace(get_string('digestsentusers', 'forum', $usermailcount));
  1054.     }
  1055.  
  1056.     if (!empty($CFG->forum_lastreadclean)) {
  1057.         $timenow = time();
  1058.         if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
  1059.             set_config('forum_lastreadclean', $timenow);
  1060.             mtrace('Removing old forum read tracking info...');
  1061.             forum_tp_clean_read_records();
  1062.         }
  1063.     } else {
  1064.         set_config('forum_lastreadclean', time());
  1065.     }
  1066.  
  1067.  
  1068.     return true;
  1069. }
  1070.  
  1071. /**
  1072.  * Builds and returns the body of the email notification in plain text.
  1073.  *
  1074.  * @global object
  1075.  * @global object
  1076.  * @uses CONTEXT_MODULE
  1077.  * @param object $course
  1078.  * @param object $cm
  1079.  * @param object $forum
  1080.  * @param object $discussion
  1081.  * @param object $post
  1082.  * @param object $userfrom
  1083.  * @param object $userto
  1084.  * @param boolean $bare
  1085.  * @return string The email body in plain text format.
  1086.  */
  1087. function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
  1088.     global $CFG, $USER;
  1089.  
  1090.     $modcontext = context_module::instance($cm->id);
  1091.  
  1092.     if (!isset($userto->viewfullnames[$forum->id])) {
  1093.         $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
  1094.     } else {
  1095.         $viewfullnames = $userto->viewfullnames[$forum->id];
  1096.     }
  1097.  
  1098.     if (!isset($userto->canpost[$discussion->id])) {
  1099.         $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  1100.     } else {
  1101.         $canreply = $userto->canpost[$discussion->id];
  1102.     }
  1103.  
  1104.     $by = New stdClass;
  1105.     $by->name = fullname($userfrom, $viewfullnames);
  1106.     $by->date = userdate($post->modified, "", $userto->timezone);
  1107.  
  1108.     $strbynameondate = get_string('bynameondate', 'forum', $by);
  1109.  
  1110.     $strforums = get_string('forums', 'forum');
  1111.  
  1112.     $canunsubscribe = ! forum_is_forcesubscribed($forum);
  1113.  
  1114.     $posttext = '';
  1115.  
  1116.     if (!$bare) {
  1117.         $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
  1118.         $posttext  = "$shortname -> $strforums -> ".format_string($forum->name,true);
  1119.  
  1120.         if ($discussion->name != $forum->name) {
  1121.             $posttext  .= " -> ".format_string($discussion->name,true);
  1122.         }
  1123.     }
  1124.  
  1125.     // add absolute file links
  1126.     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
  1127.  
  1128.     $posttext .= "\n---------------------------------------------------------------------\n";
  1129.     $posttext .= format_string($post->subject,true);
  1130.     if ($bare) {
  1131.         $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
  1132.     }
  1133.     $posttext .= "\n".$strbynameondate."\n";
  1134.     $posttext .= "---------------------------------------------------------------------\n";
  1135.     $posttext .= format_text_email($post->message, $post->messageformat);
  1136.     $posttext .= "\n\n";
  1137.     $posttext .= forum_print_attachments($post, $cm, "text");
  1138.  
  1139.     if (!$bare && $canreply) {
  1140.         $posttext .= "---------------------------------------------------------------------\n";
  1141.         $posttext .= get_string("postmailinfo", "forum", $shortname)."\n";
  1142.         $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
  1143.     }
  1144.     if (!$bare && $canunsubscribe) {
  1145.         $posttext .= "\n---------------------------------------------------------------------\n";
  1146.         $posttext .= get_string("unsubscribe", "forum");
  1147.         $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
  1148.     }
  1149.  
  1150.     return $posttext;
  1151. }
  1152.  
  1153. /**
  1154.  * Builds and returns the body of the email notification in html format.
  1155.  *
  1156.  * @global object
  1157.  * @param object $course
  1158.  * @param object $cm
  1159.  * @param object $forum
  1160.  * @param object $discussion
  1161.  * @param object $post
  1162.  * @param object $userfrom
  1163.  * @param object $userto
  1164.  * @return string The email text in HTML format
  1165.  */
  1166. function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto) {
  1167.     global $CFG;
  1168.  
  1169.     if ($userto->mailformat != 1) {  // Needs to be HTML
  1170.         return '';
  1171.     }
  1172.  
  1173.     if (!isset($userto->canpost[$discussion->id])) {
  1174.         $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course);
  1175.     } else {
  1176.         $canreply = $userto->canpost[$discussion->id];
  1177.     }
  1178.  
  1179.     $strforums = get_string('forums', 'forum');
  1180.     $canunsubscribe = ! forum_is_forcesubscribed($forum);
  1181.     $shortname = format_string($course->shortname, true, array('context' => context_course::instance($course->id)));
  1182.  
  1183.     $posthtml = '<head>';
  1184. /*    foreach ($CFG->stylesheets as $stylesheet) {
  1185.         //TODO: MDL-21120
  1186.         $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
  1187.     }*/
  1188.     $posthtml .= '</head>';
  1189.     $posthtml .= "\n<body id=\"email\">\n\n";
  1190.  
  1191.     $posthtml .= '<div class="navbar">'.
  1192.     '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$shortname.'</a> &raquo; '.
  1193.     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
  1194.     '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
  1195.     if ($discussion->name == $forum->name) {
  1196.         $posthtml .= '</div>';
  1197.     } else {
  1198.         $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
  1199.                      format_string($discussion->name,true).'</a></div>';
  1200.     }
  1201.     $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
  1202.  
  1203.     if ($canunsubscribe) {
  1204.         $posthtml .= '<hr /><div class="mdl-align unsubscribelink">
  1205.                      <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
  1206.                      <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
  1207.     }
  1208.  
  1209.     $posthtml .= '</body>';
  1210.  
  1211.     return $posthtml;
  1212. }
  1213.  
  1214.  
  1215. /**
  1216.  *
  1217.  * @param object $course
  1218.  * @param object $user
  1219.  * @param object $mod TODO this is not used in this function, refactor
  1220.  * @param object $forum
  1221.  * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
  1222.  */
  1223. function forum_user_outline($course, $user, $mod, $forum) {
  1224.     global $CFG;
  1225.     require_once("$CFG->libdir/gradelib.php");
  1226.     $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
  1227.     if (empty($grades->items[0]->grades)) {
  1228.         $grade = false;
  1229.     } else {
  1230.         $grade = reset($grades->items[0]->grades);
  1231.     }
  1232.  
  1233.     $count = forum_count_user_posts($forum->id, $user->id);
  1234.  
  1235.     if ($count && $count->postcount > 0) {
  1236.         $result = new stdClass();
  1237.         $result->info = get_string("numposts", "forum", $count->postcount);
  1238.         $result->time = $count->lastpost;
  1239.         if ($grade) {
  1240.             $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
  1241.         }
  1242.         return $result;
  1243.     } else if ($grade) {
  1244.         $result = new stdClass();
  1245.         $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
  1246.  
  1247.         //datesubmitted == time created. dategraded == time modified or time overridden
  1248.         //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
  1249.         //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
  1250.         if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
  1251.             $result->time = $grade->dategraded;
  1252.         } else {
  1253.             $result->time = $grade->datesubmitted;
  1254.         }
  1255.  
  1256.         return $result;
  1257.     }
  1258.     return NULL;
  1259. }
  1260.  
  1261.  
  1262. /**
  1263.  * @global object
  1264.  * @global object
  1265.  * @param object $coure
  1266.  * @param object $user
  1267.  * @param object $mod
  1268.  * @param object $forum
  1269.  */
  1270. function forum_user_complete($course, $user, $mod, $forum) {
  1271.     global $CFG,$USER, $OUTPUT;
  1272.     require_once("$CFG->libdir/gradelib.php");
  1273.  
  1274.     $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
  1275.     if (!empty($grades->items[0]->grades)) {
  1276.         $grade = reset($grades->items[0]->grades);
  1277.         echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
  1278.         if ($grade->str_feedback) {
  1279.             echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
  1280.         }
  1281.     }
  1282.  
  1283.     if ($posts = forum_get_user_posts($forum->id, $user->id)) {
  1284.  
  1285.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
  1286.             print_error('invalidcoursemodule');
  1287.         }
  1288.         $discussions = forum_get_user_involved_discussions($forum->id, $user->id);
  1289.  
  1290.         foreach ($posts as $post) {
  1291.             if (!isset($discussions[$post->discussion])) {
  1292.                 continue;
  1293.             }
  1294.             $discussion = $discussions[$post->discussion];
  1295.  
  1296.             forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false);
  1297.         }
  1298.     } else {
  1299.         echo "<p>".get_string("noposts", "forum")."</p>";
  1300.     }
  1301. }
  1302.  
  1303.  
  1304.  
  1305.  
  1306.  
  1307.  
  1308. /**
  1309.  * @global object
  1310.  * @global object
  1311.  * @global object
  1312.  * @param array $courses
  1313.  * @param array $htmlarray
  1314.  */
  1315. function forum_print_overview($courses,&$htmlarray) {
  1316.     global $USER, $CFG, $DB, $SESSION;
  1317.  
  1318.     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
  1319.         return array();
  1320.     }
  1321.  
  1322.     if (!$forums = get_all_instances_in_courses('forum',$courses)) {
  1323.         return;
  1324.     }
  1325.  
  1326.     // Courses to search for new posts
  1327.     $coursessqls = array();
  1328.     $params = array();
  1329.     foreach ($courses as $course) {
  1330.  
  1331.         // If the user has never entered into the course all posts are pending
  1332.         if ($course->lastaccess == 0) {
  1333.             $coursessqls[] = '(f.course = ?)';
  1334.             $params[] = $course->id;
  1335.  
  1336.         // Only posts created after the course last access
  1337.         } else {
  1338.             $coursessqls[] = '(f.course = ? AND p.created > ?)';
  1339.             $params[] = $course->id;
  1340.             $params[] = $course->lastaccess;
  1341.         }
  1342.     }
  1343.     $params[] = $USER->id;
  1344.     $coursessql = implode(' OR ', $coursessqls);
  1345.  
  1346.     $sql = "SELECT f.id, COUNT(*) as count "
  1347.                 .'FROM {forum} f '
  1348.                 .'JOIN {forum_discussions} d ON d.forum  = f.id '
  1349.                 .'JOIN {forum_posts} p ON p.discussion = d.id '
  1350.                 ."WHERE ($coursessql) "
  1351.                 .'AND p.userid != ? '
  1352.                 .'GROUP BY f.id';
  1353.  
  1354.     if (!$new = $DB->get_records_sql($sql, $params)) {
  1355.         $new = array(); // avoid warnings
  1356.     }
  1357.  
  1358.     // also get all forum tracking stuff ONCE.
  1359.     $trackingforums = array();
  1360.     foreach ($forums as $forum) {
  1361.         if (forum_tp_can_track_forums($forum)) {
  1362.             $trackingforums[$forum->id] = $forum;
  1363.         }
  1364.     }
  1365.  
  1366.     if (count($trackingforums) > 0) {
  1367.         $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  1368.         $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
  1369.             ' FROM {forum_posts} p '.
  1370.             ' JOIN {forum_discussions} d ON p.discussion = d.id '.
  1371.             ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE (';
  1372.         $params = array($USER->id);
  1373.  
  1374.         foreach ($trackingforums as $track) {
  1375.             $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR ';
  1376.             $params[] = $track->id;
  1377.             if (isset($SESSION->currentgroup[$track->course])) {
  1378.                 $groupid =  $SESSION->currentgroup[$track->course];
  1379.             } else {
  1380.                 // get first groupid
  1381.                 $groupids = groups_get_all_groups($track->course, $USER->id);
  1382.                 if ($groupids) {
  1383.                     reset($groupids);
  1384.                     $groupid = key($groupids);
  1385.                     $SESSION->currentgroup[$track->course] = $groupid;
  1386.                 } else {
  1387.                     $groupid = 0;
  1388.                 }
  1389.                 unset($groupids);
  1390.             }
  1391.             $params[] = $groupid;
  1392.         }
  1393.         $sql = substr($sql,0,-3); // take off the last OR
  1394.         $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course';
  1395.         $params[] = $cutoffdate;
  1396.  
  1397.         if (!$unread = $DB->get_records_sql($sql, $params)) {
  1398.             $unread = array();
  1399.         }
  1400.     } else {
  1401.         $unread = array();
  1402.     }
  1403.  
  1404.     if (empty($unread) and empty($new)) {
  1405.         return;
  1406.     }
  1407.  
  1408.     $strforum = get_string('modulename','forum');
  1409.  
  1410.     foreach ($forums as $forum) {
  1411.         $str = '';
  1412.         $count = 0;
  1413.         $thisunread = 0;
  1414.         $showunread = false;
  1415.         // either we have something from logs, or trackposts, or nothing.
  1416.         if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
  1417.             $count = $new[$forum->id]->count;
  1418.         }
  1419.         if (array_key_exists($forum->id,$unread)) {
  1420.             $thisunread = $unread[$forum->id]->count;
  1421.             $showunread = true;
  1422.         }
  1423.         if ($count > 0 || $thisunread > 0) {
  1424.             $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
  1425.                 $forum->name.'</a></div>';
  1426.             $str .= '<div class="info"><span class="postsincelogin">';
  1427.             $str .= get_string('overviewnumpostssince', 'forum', $count)."</span>";
  1428.             if (!empty($showunread)) {
  1429.                 $str .= '<div class="unreadposts">'.get_string('overviewnumunread', 'forum', $thisunread).'</div>';
  1430.             }
  1431.             $str .= '</div></div>';
  1432.         }
  1433.         if (!empty($str)) {
  1434.             if (!array_key_exists($forum->course,$htmlarray)) {
  1435.                 $htmlarray[$forum->course] = array();
  1436.             }
  1437.             if (!array_key_exists('forum',$htmlarray[$forum->course])) {
  1438.                 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
  1439.             }
  1440.             $htmlarray[$forum->course]['forum'] .= $str;
  1441.         }
  1442.     }
  1443. }
  1444.  
  1445. /**
  1446.  * Given a course and a date, prints a summary of all the new
  1447.  * messages posted in the course since that date
  1448.  *
  1449.  * @global object
  1450.  * @global object
  1451.  * @global object
  1452.  * @uses CONTEXT_MODULE
  1453.  * @uses VISIBLEGROUPS
  1454.  * @param object $course
  1455.  * @param bool $viewfullnames capability
  1456.  * @param int $timestart
  1457.  * @return bool success
  1458.  */
  1459. function forum_print_recent_activity($course, $viewfullnames, $timestart) {
  1460.     global $CFG, $USER, $DB, $OUTPUT;
  1461.  
  1462.     // do not use log table if possible, it may be huge and is expensive to join with other tables
  1463.  
  1464.     if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  1465.                                              d.timestart, d.timeend, d.userid AS duserid,
  1466.                                              u.firstname, u.lastname, u.email, u.picture
  1467.                                         FROM {forum_posts} p
  1468.                                              JOIN {forum_discussions} d ON d.id = p.discussion
  1469.                                              JOIN {forum} f             ON f.id = d.forum
  1470.                                              JOIN {user} u              ON u.id = p.userid
  1471.                                        WHERE p.created > ? AND f.course = ?
  1472.                                     ORDER BY p.id ASC", array($timestart, $course->id))) { // order by initial posting date
  1473.          return false;
  1474.     }
  1475.  
  1476.     $modinfo = get_fast_modinfo($course);
  1477.  
  1478.     $groupmodes = array();
  1479.     $cms    = array();
  1480.  
  1481.     $strftimerecent = get_string('strftimerecent');
  1482.  
  1483.     $printposts = array();
  1484.     foreach ($posts as $post) {
  1485.         if (!isset($modinfo->instances['forum'][$post->forum])) {
  1486.             // not visible
  1487.             continue;
  1488.         }
  1489.         $cm = $modinfo->instances['forum'][$post->forum];
  1490.         if (!$cm->uservisible) {
  1491.             continue;
  1492.         }
  1493.         $context = context_module::instance($cm->id);
  1494.  
  1495.         if (!has_capability('mod/forum:viewdiscussion', $context)) {
  1496.             continue;
  1497.         }
  1498.  
  1499.         if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
  1500.           and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
  1501.             if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
  1502.                 continue;
  1503.             }
  1504.         }
  1505.  
  1506.         $groupmode = groups_get_activity_groupmode($cm, $course);
  1507.  
  1508.         if ($groupmode) {
  1509.             if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
  1510.                 // oki (Open discussions have groupid -1)
  1511.             } else {
  1512.                 // separate mode
  1513.                 if (isguestuser()) {
  1514.                     // shortcut
  1515.                     continue;
  1516.                 }
  1517.  
  1518.                 if (is_null($modinfo->groups)) {
  1519.                     $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
  1520.                 }
  1521.  
  1522.                 if (!in_array($post->groupid, $modinfo->get_groups($cm->groupingid))) {
  1523.                     continue;
  1524.                 }
  1525.             }
  1526.         }
  1527.  
  1528.         $printposts[] = $post;
  1529.     }
  1530.     unset($posts);
  1531.  
  1532.     if (!$printposts) {
  1533.         return false;
  1534.     }
  1535.  
  1536.     echo $OUTPUT->heading(get_string('newforumposts', 'forum').':', 3);
  1537.     echo "\n<ul class='unlist'>\n";
  1538.  
  1539.     foreach ($printposts as $post) {
  1540.         $subjectclass = empty($post->parent) ? ' bold' : '';
  1541.  
  1542.         echo '<li><div class="head">'.
  1543.                '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
  1544.                '<div class="name">'.fullname($post, $viewfullnames).'</div>'.
  1545.              '</div>';
  1546.         echo '<div class="info'.$subjectclass.'">';
  1547.         if (empty($post->parent)) {
  1548.             echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
  1549.         } else {
  1550.             echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
  1551.         }
  1552.         $post->subject = break_up_long_words(format_string($post->subject, true));
  1553.         echo $post->subject;
  1554.         echo "</a>\"</div></li>\n";
  1555.     }
  1556.  
  1557.     echo "</ul>\n";
  1558.  
  1559.     return true;
  1560. }
  1561.  
  1562. /**
  1563.  * Return grade for given user or all users.
  1564.  *
  1565.  * @global object
  1566.  * @global object
  1567.  * @param object $forum
  1568.  * @param int $userid optional user id, 0 means all users
  1569.  * @return array array of grades, false if none
  1570.  */
  1571. function forum_get_user_grades($forum, $userid = 0) {
  1572.     global $CFG;
  1573.  
  1574.     require_once($CFG->dirroot.'/rating/lib.php');
  1575.  
  1576.     $ratingoptions = new stdClass;
  1577.     $ratingoptions->component = 'mod_forum';
  1578.     $ratingoptions->ratingarea = 'post';
  1579.  
  1580.     //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
  1581.     $ratingoptions->modulename = 'forum';
  1582.     $ratingoptions->moduleid   = $forum->id;
  1583.     $ratingoptions->userid = $userid;
  1584.     $ratingoptions->aggregationmethod = $forum->assessed;
  1585.     $ratingoptions->scaleid = $forum->scale;
  1586.     $ratingoptions->itemtable = 'forum_posts';
  1587.     $ratingoptions->itemtableusercolumn = 'userid';
  1588.  
  1589.     $rm = new rating_manager();
  1590.     return $rm->get_user_grades($ratingoptions);
  1591. }
  1592.  
  1593. /**
  1594.  * Update activity grades
  1595.  *
  1596.  * @category grade
  1597.  * @param object $forum
  1598.  * @param int $userid specific user only, 0 means all
  1599.  * @param boolean $nullifnone return null if grade does not exist
  1600.  * @return void
  1601.  */
  1602. function forum_update_grades($forum, $userid=0, $nullifnone=true) {
  1603.     global $CFG, $DB;
  1604.     require_once($CFG->libdir.'/gradelib.php');
  1605.  
  1606.     if (!$forum->assessed) {
  1607.         forum_grade_item_update($forum);
  1608.  
  1609.     } else if ($grades = forum_get_user_grades($forum, $userid)) {
  1610.         forum_grade_item_update($forum, $grades);
  1611.  
  1612.     } else if ($userid and $nullifnone) {
  1613.         $grade = new stdClass();
  1614.         $grade->userid   = $userid;
  1615.         $grade->rawgrade = NULL;
  1616.         forum_grade_item_update($forum, $grade);
  1617.  
  1618.     } else {
  1619.         forum_grade_item_update($forum);
  1620.     }
  1621. }
  1622.  
  1623. /**
  1624.  * Update all grades in gradebook.
  1625.  * @global object
  1626.  */
  1627. function forum_upgrade_grades() {
  1628.     global $DB;
  1629.  
  1630.     $sql = "SELECT COUNT('x')
  1631.              FROM {forum} f, {course_modules} cm, {modules} m
  1632.             WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
  1633.     $count = $DB->count_records_sql($sql);
  1634.  
  1635.     $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid
  1636.              FROM {forum} f, {course_modules} cm, {modules} m
  1637.             WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
  1638.     $rs = $DB->get_recordset_sql($sql);
  1639.     if ($rs->valid()) {
  1640.         $pbar = new progress_bar('forumupgradegrades', 500, true);
  1641.         $i=0;
  1642.         foreach ($rs as $forum) {
  1643.             $i++;
  1644.             upgrade_set_timeout(60*5); // set up timeout, may also abort execution
  1645.             forum_update_grades($forum, 0, false);
  1646.             $pbar->update($i, $count, "Updating Forum grades ($i/$count).");
  1647.         }
  1648.     }
  1649.     $rs->close();
  1650. }
  1651.  
  1652. /**
  1653.  * Create/update grade item for given forum
  1654.  *
  1655.  * @category grade
  1656.  * @uses GRADE_TYPE_NONE
  1657.  * @uses GRADE_TYPE_VALUE
  1658.  * @uses GRADE_TYPE_SCALE
  1659.  * @param stdClass $forum Forum object with extra cmidnumber
  1660.  * @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook
  1661.  * @return int 0 if ok
  1662.  */
  1663. function forum_grade_item_update($forum, $grades=NULL) {
  1664.     global $CFG;
  1665.     if (!function_exists('grade_update')) { //workaround for buggy PHP versions
  1666.         require_once($CFG->libdir.'/gradelib.php');
  1667.     }
  1668.  
  1669.     $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
  1670.  
  1671.     if (!$forum->assessed or $forum->scale == 0) {
  1672.         $params['gradetype'] = GRADE_TYPE_NONE;
  1673.  
  1674.     } else if ($forum->scale > 0) {
  1675.         $params['gradetype'] = GRADE_TYPE_VALUE;
  1676.         $params['grademax']  = $forum->scale;
  1677.         $params['grademin']  = 0;
  1678.  
  1679.     } else if ($forum->scale < 0) {
  1680.         $params['gradetype'] = GRADE_TYPE_SCALE;
  1681.         $params['scaleid']   = -$forum->scale;
  1682.     }
  1683.  
  1684.     if ($grades  === 'reset') {
  1685.         $params['reset'] = true;
  1686.         $grades = NULL;
  1687.     }
  1688.  
  1689.     return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
  1690. }
  1691.  
  1692. /**
  1693.  * Delete grade item for given forum
  1694.  *
  1695.  * @category grade
  1696.  * @param stdClass $forum Forum object
  1697.  * @return grade_item
  1698.  */
  1699. function forum_grade_item_delete($forum) {
  1700.     global $CFG;
  1701.     require_once($CFG->libdir.'/gradelib.php');
  1702.  
  1703.     return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
  1704. }
  1705.  
  1706.  
  1707. /**
  1708.  * This function returns if a scale is being used by one forum
  1709.  *
  1710.  * @global object
  1711.  * @param int $forumid
  1712.  * @param int $scaleid negative number
  1713.  * @return bool
  1714.  */
  1715. function forum_scale_used ($forumid,$scaleid) {
  1716.     global $DB;
  1717.     $return = false;
  1718.  
  1719.     $rec = $DB->get_record("forum",array("id" => "$forumid","scale" => "-$scaleid"));
  1720.  
  1721.     if (!empty($rec) && !empty($scaleid)) {
  1722.         $return = true;
  1723.     }
  1724.  
  1725.     return $return;
  1726. }
  1727.  
  1728. /**
  1729.  * Checks if scale is being used by any instance of forum
  1730.  *
  1731.  * This is used to find out if scale used anywhere
  1732.  *
  1733.  * @global object
  1734.  * @param $scaleid int
  1735.  * @return boolean True if the scale is used by any forum
  1736.  */
  1737. function forum_scale_used_anywhere($scaleid) {
  1738.     global $DB;
  1739.     if ($scaleid and $DB->record_exists('forum', array('scale' => -$scaleid))) {
  1740.         return true;
  1741.     } else {
  1742.         return false;
  1743.     }
  1744. }
  1745.  
  1746. // SQL FUNCTIONS ///////////////////////////////////////////////////////////
  1747.  
  1748. /**
  1749.  * Gets a post with all info ready for forum_print_post
  1750.  * Most of these joins are just to get the forum id
  1751.  *
  1752.  * @global object
  1753.  * @global object
  1754.  * @param int $postid
  1755.  * @return mixed array of posts or false
  1756.  */
  1757. function forum_get_post_full($postid) {
  1758.     global $CFG, $DB;
  1759.  
  1760.     return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1761.                             FROM {forum_posts} p
  1762.                                  JOIN {forum_discussions} d ON p.discussion = d.id
  1763.                                  LEFT JOIN {user} u ON p.userid = u.id
  1764.                            WHERE p.id = ?", array($postid));
  1765. }
  1766.  
  1767. /**
  1768.  * Gets posts with all info ready for forum_print_post
  1769.  * We pass forumid in because we always know it so no need to make a
  1770.  * complicated join to find it out.
  1771.  *
  1772.  * @global object
  1773.  * @global object
  1774.  * @return mixed array of posts or false
  1775.  */
  1776. function forum_get_discussion_posts($discussion, $sort, $forumid) {
  1777.     global $CFG, $DB;
  1778.  
  1779.     return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1780.                              FROM {forum_posts} p
  1781.                         LEFT JOIN {user} u ON p.userid = u.id
  1782.                             WHERE p.discussion = ?
  1783.                               AND p.parent > 0 $sort", array($discussion));
  1784. }
  1785.  
  1786. /**
  1787.  * Gets all posts in discussion including top parent.
  1788.  *
  1789.  * @global object
  1790.  * @global object
  1791.  * @global object
  1792.  * @param int $discussionid
  1793.  * @param string $sort
  1794.  * @param bool $tracking does user track the forum?
  1795.  * @return array of posts
  1796.  */
  1797. function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
  1798.     global $CFG, $DB, $USER;
  1799.  
  1800.     $tr_sel  = "";
  1801.     $tr_join = "";
  1802.     $params = array();
  1803.  
  1804.     if ($tracking) {
  1805.         $now = time();
  1806.         $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  1807.         $tr_sel  = ", fr.id AS postread";
  1808.         $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)";
  1809.         $params[] = $USER->id;
  1810.     }
  1811.  
  1812.     $params[] = $discussionid;
  1813.     if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
  1814.                                     FROM {forum_posts} p
  1815.                                          LEFT JOIN {user} u ON p.userid = u.id
  1816.                                          $tr_join
  1817.                                    WHERE p.discussion = ?
  1818.                                 ORDER BY $sort", $params)) {
  1819.         return array();
  1820.     }
  1821.  
  1822.     foreach ($posts as $pid=>$p) {
  1823.         if ($tracking) {
  1824.             if (forum_tp_is_post_old($p)) {
  1825.                  $posts[$pid]->postread = true;
  1826.             }
  1827.         }
  1828.         if (!$p->parent) {
  1829.             continue;
  1830.         }
  1831.         if (!isset($posts[$p->parent])) {
  1832.             continue; // parent does not exist??
  1833.         }
  1834.         if (!isset($posts[$p->parent]->children)) {
  1835.             $posts[$p->parent]->children = array();
  1836.         }
  1837.         $posts[$p->parent]->children[$pid] =& $posts[$pid];
  1838.     }
  1839.  
  1840.     return $posts;
  1841. }
  1842.  
  1843. /**
  1844.  * Gets posts with all info ready for forum_print_post
  1845.  * We pass forumid in because we always know it so no need to make a
  1846.  * complicated join to find it out.
  1847.  *
  1848.  * @global object
  1849.  * @global object
  1850.  * @param int $parent
  1851.  * @param int $forumid
  1852.  * @return array
  1853.  */
  1854. function forum_get_child_posts($parent, $forumid) {
  1855.     global $CFG, $DB;
  1856.  
  1857.     return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1858.                              FROM {forum_posts} p
  1859.                         LEFT JOIN {user} u ON p.userid = u.id
  1860.                             WHERE p.parent = ?
  1861.                          ORDER BY p.created ASC", array($parent));
  1862. }
  1863.  
  1864. /**
  1865.  * An array of forum objects that the user is allowed to read/search through.
  1866.  *
  1867.  * @global object
  1868.  * @global object
  1869.  * @global object
  1870.  * @param int $userid
  1871.  * @param int $courseid if 0, we look for forums throughout the whole site.
  1872.  * @return array of forum objects, or false if no matches
  1873.  *         Forum objects have the following attributes:
  1874.  *         id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
  1875.  *         viewhiddentimedposts
  1876.  */
  1877. function forum_get_readable_forums($userid, $courseid=0) {
  1878.  
  1879.     global $CFG, $DB, $USER;
  1880.     require_once($CFG->dirroot.'/course/lib.php');
  1881.  
  1882.     if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) {
  1883.         print_error('notinstalled', 'forum');
  1884.     }
  1885.  
  1886.     if ($courseid) {
  1887.         $courses = $DB->get_records('course', array('id' => $courseid));
  1888.     } else {
  1889.         // If no course is specified, then the user can see SITE + his courses.
  1890.         $courses1 = $DB->get_records('course', array('id' => SITEID));
  1891.         $courses2 = enrol_get_users_courses($userid, true, array('modinfo'));
  1892.         $courses = array_merge($courses1, $courses2);
  1893.     }
  1894.     if (!$courses) {
  1895.         return array();
  1896.     }
  1897.  
  1898.     $readableforums = array();
  1899.  
  1900.     foreach ($courses as $course) {
  1901.  
  1902.         $modinfo = get_fast_modinfo($course);
  1903.         if (is_null($modinfo->groups)) {
  1904.             $modinfo->groups = groups_get_user_groups($course->id, $userid);
  1905.         }
  1906.  
  1907.         if (empty($modinfo->instances['forum'])) {
  1908.             // hmm, no forums?
  1909.             continue;
  1910.         }
  1911.  
  1912.         $courseforums = $DB->get_records('forum', array('course' => $course->id));
  1913.  
  1914.         foreach ($modinfo->instances['forum'] as $forumid => $cm) {
  1915.             if (!$cm->uservisible or !isset($courseforums[$forumid])) {
  1916.                 continue;
  1917.             }
  1918.             $context = context_module::instance($cm->id);
  1919.             $forum = $courseforums[$forumid];
  1920.             $forum->context = $context;
  1921.             $forum->cm = $cm;
  1922.  
  1923.             if (!has_capability('mod/forum:viewdiscussion', $context)) {
  1924.                 continue;
  1925.             }
  1926.  
  1927.          /// group access
  1928.             if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  1929.                 if (is_null($modinfo->groups)) {
  1930.                     $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  1931.                 }
  1932.                 if (isset($modinfo->groups[$cm->groupingid])) {
  1933.                     $forum->onlygroups = $modinfo->groups[$cm->groupingid];
  1934.                     $forum->onlygroups[] = -1;
  1935.                 } else {
  1936.                     $forum->onlygroups = array(-1);
  1937.                 }
  1938.             }
  1939.  
  1940.         /// hidden timed discussions
  1941.             $forum->viewhiddentimedposts = true;
  1942.             if (!empty($CFG->forum_enabletimedposts)) {
  1943.                 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
  1944.                     $forum->viewhiddentimedposts = false;
  1945.                 }
  1946.             }
  1947.  
  1948.         /// qanda access
  1949.             if ($forum->type == 'qanda'
  1950.                     && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  1951.  
  1952.                 // We need to check whether the user has posted in the qanda forum.
  1953.                 $forum->onlydiscussions = array();  // Holds discussion ids for the discussions
  1954.                                                     // the user is allowed to see in this forum.
  1955.                 if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
  1956.                     foreach ($discussionspostedin as $d) {
  1957.                         $forum->onlydiscussions[] = $d->id;
  1958.                     }
  1959.                 }
  1960.             }
  1961.  
  1962.             $readableforums[$forum->id] = $forum;
  1963.         }
  1964.  
  1965.         unset($modinfo);
  1966.  
  1967.     } // End foreach $courses
  1968.  
  1969.     return $readableforums;
  1970. }
  1971.  
  1972. /**
  1973.  * Returns a list of posts found using an array of search terms.
  1974.  *
  1975.  * @global object
  1976.  * @global object
  1977.  * @global object
  1978.  * @param array $searchterms array of search terms, e.g. word +word -word
  1979.  * @param int $courseid if 0, we search through the whole site
  1980.  * @param int $limitfrom
  1981.  * @param int $limitnum
  1982.  * @param int &$totalcount
  1983.  * @param string $extrasql
  1984.  * @return array|bool Array of posts found or false
  1985.  */
  1986. function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
  1987.                             &$totalcount, $extrasql='') {
  1988.     global $CFG, $DB, $USER;
  1989.     require_once($CFG->libdir.'/searchlib.php');
  1990.  
  1991.     $forums = forum_get_readable_forums($USER->id, $courseid);
  1992.  
  1993.     if (count($forums) == 0) {
  1994.         $totalcount = 0;
  1995.         return false;
  1996.     }
  1997.  
  1998.     $now = round(time(), -2); // db friendly
  1999.  
  2000.     $fullaccess = array();
  2001.     $where = array();
  2002.     $params = array();
  2003.  
  2004.     foreach ($forums as $forumid => $forum) {
  2005.         $select = array();
  2006.  
  2007.         if (!$forum->viewhiddentimedposts) {
  2008.             $select[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
  2009.             $params = array_merge($params, array('userid'.$forumid=>$USER->id, 'timestart'.$forumid=>$now, 'timeend'.$forumid=>$now));
  2010.         }
  2011.  
  2012.         $cm = $forum->cm;
  2013.         $context = $forum->context;
  2014.  
  2015.         if ($forum->type == 'qanda'
  2016.             && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  2017.             if (!empty($forum->onlydiscussions)) {
  2018.                 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_');
  2019.                 $params = array_merge($params, $discussionid_params);
  2020.                 $select[] = "(d.id $discussionid_sql OR p.parent = 0)";
  2021.             } else {
  2022.                 $select[] = "p.parent = 0";
  2023.             }
  2024.         }
  2025.  
  2026.         if (!empty($forum->onlygroups)) {
  2027.             list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_');
  2028.             $params = array_merge($params, $groupid_params);
  2029.             $select[] = "d.groupid $groupid_sql";
  2030.         }
  2031.  
  2032.         if ($select) {
  2033.             $selects = implode(" AND ", $select);
  2034.             $where[] = "(d.forum = :forum{$forumid} AND $selects)";
  2035.             $params['forum'.$forumid] = $forumid;
  2036.         } else {
  2037.             $fullaccess[] = $forumid;
  2038.         }
  2039.     }
  2040.  
  2041.     if ($fullaccess) {
  2042.         list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula');
  2043.         $params = array_merge($params, $fullid_params);
  2044.         $where[] = "(d.forum $fullid_sql)";
  2045.     }
  2046.  
  2047.     $selectdiscussion = "(".implode(" OR ", $where).")";
  2048.  
  2049.     $messagesearch = '';
  2050.     $searchstring = '';
  2051.  
  2052.     // Need to concat these back together for parser to work.
  2053.     foreach($searchterms as $searchterm){
  2054.         if ($searchstring != '') {
  2055.             $searchstring .= ' ';
  2056.         }
  2057.         $searchstring .= $searchterm;
  2058.     }
  2059.  
  2060.     // We need to allow quoted strings for the search. The quotes *should* be stripped
  2061.     // by the parser, but this should be examined carefully for security implications.
  2062.     $searchstring = str_replace("\\\"","\"",$searchstring);
  2063.     $parser = new search_parser();
  2064.     $lexer = new search_lexer($parser);
  2065.  
  2066.     if ($lexer->parse($searchstring)) {
  2067.         $parsearray = $parser->get_parsed_array();
  2068.     // Experimental feature under 1.8! MDL-8830
  2069.     // Use alternative text searches if defined
  2070.     // This feature only works under mysql until properly implemented for other DBs
  2071.     // Requires manual creation of text index for forum_posts before enabling it:
  2072.     // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
  2073.     // Experimental feature under 1.8! MDL-8830
  2074.         if (!empty($CFG->forum_usetextsearches)) {
  2075.             list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
  2076.                                                  'p.userid', 'u.id', 'u.firstname',
  2077.                                                  'u.lastname', 'p.modified', 'd.forum');
  2078.         } else {
  2079.             list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject',
  2080.                                                  'p.userid', 'u.id', 'u.firstname',
  2081.                                                  'u.lastname', 'p.modified', 'd.forum');
  2082.         }
  2083.         $params = array_merge($params, $msparams);
  2084.     }
  2085.  
  2086.     $fromsql = "{forum_posts} p,
  2087.                  {forum_discussions} d,
  2088.                  {user} u";
  2089.  
  2090.     $selectsql = " $messagesearch
  2091.               AND p.discussion = d.id
  2092.               AND p.userid = u.id
  2093.               AND $selectdiscussion
  2094.                   $extrasql";
  2095.  
  2096.     $countsql = "SELECT COUNT(*)
  2097.                   FROM $fromsql
  2098.                  WHERE $selectsql";
  2099.  
  2100.     $searchsql = "SELECT p.*,
  2101.                         d.forum,
  2102.                         u.firstname,
  2103.                         u.lastname,
  2104.                         u.email,
  2105.                         u.picture,
  2106.                         u.imagealt
  2107.                    FROM $fromsql
  2108.                   WHERE $selectsql
  2109.                ORDER BY p.modified DESC";
  2110.  
  2111.     $totalcount = $DB->count_records_sql($countsql, $params);
  2112.  
  2113.     return $DB->get_records_sql($searchsql, $params, $limitfrom, $limitnum);
  2114. }
  2115.  
  2116. /**
  2117.  * Returns a list of ratings for a particular post - sorted.
  2118.  *
  2119.  * TODO: Check if this function is actually used anywhere.
  2120.  * Up until the fix for MDL-27471 this function wasn't even returning.
  2121.  *
  2122.  * @param stdClass $context
  2123.  * @param int $postid
  2124.  * @param string $sort
  2125.  * @return array Array of ratings or false
  2126.  */
  2127. function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") {
  2128.     $options = new stdClass;
  2129.     $options->context = $context;
  2130.     $options->component = 'mod_forum';
  2131.     $options->ratingarea = 'post';
  2132.     $options->itemid = $postid;
  2133.     $options->sort = "ORDER BY $sort";
  2134.  
  2135.     $rm = new rating_manager();
  2136.     return $rm->get_all_ratings_for_item($options);
  2137. }
  2138.  
  2139. /**
  2140.  * Returns a list of all new posts that have not been mailed yet
  2141.  *
  2142.  * @param int $starttime posts created after this time
  2143.  * @param int $endtime posts created before this
  2144.  * @param int $now used for timed discussions only
  2145.  * @return array
  2146.  */
  2147. function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
  2148.     global $CFG, $DB;
  2149.  
  2150.     $params = array();
  2151.     $params['mailed'] = FORUM_MAILED_PENDING;
  2152.     $params['ptimestart'] = $starttime;
  2153.     $params['ptimeend'] = $endtime;
  2154.     $params['mailnow'] = 1;
  2155.  
  2156.     if (!empty($CFG->forum_enabletimedposts)) {
  2157.         if (empty($now)) {
  2158.             $now = time();
  2159.         }
  2160.         $timedsql = "AND (d.timestart < :dtimestart AND (d.timeend = 0 OR d.timeend > :dtimeend))";
  2161.         $params['dtimestart'] = $now;
  2162.         $params['dtimeend'] = $now;
  2163.     } else {
  2164.         $timedsql = "";
  2165.     }
  2166.  
  2167.     return $DB->get_records_sql("SELECT p.*, d.course, d.forum
  2168.                                 FROM {forum_posts} p
  2169.                                 JOIN {forum_discussions} d ON d.id = p.discussion
  2170.                                 WHERE p.mailed = :mailed
  2171.                                 AND p.created >= :ptimestart
  2172.                                 AND (p.created < :ptimeend OR p.mailnow = :mailnow)
  2173.                                 $timedsql
  2174.                                 ORDER BY p.modified ASC", $params);
  2175. }
  2176.  
  2177. /**
  2178.  * Marks posts before a certain time as being mailed already
  2179.  *
  2180.  * @global object
  2181.  * @global object
  2182.  * @param int $endtime
  2183.  * @param int $now Defaults to time()
  2184.  * @return bool
  2185.  */
  2186. function forum_mark_old_posts_as_mailed($endtime, $now=null) {
  2187.     global $CFG, $DB;
  2188.  
  2189.     if (empty($now)) {
  2190.         $now = time();
  2191.     }
  2192.  
  2193.     $params = array();
  2194.     $params['mailedsuccess'] = FORUM_MAILED_SUCCESS;
  2195.     $params['now'] = $now;
  2196.     $params['endtime'] = $endtime;
  2197.     $params['mailnow'] = 1;
  2198.     $params['mailedpending'] = FORUM_MAILED_PENDING;
  2199.  
  2200.     if (empty($CFG->forum_enabletimedposts)) {
  2201.         return $DB->execute("UPDATE {forum_posts}
  2202.                             SET mailed = :mailedsuccess
  2203.                             WHERE (created < :endtime OR mailnow = :mailnow)
  2204.                             AND mailed = :mailedpending", $params);
  2205.     } else {
  2206.         return $DB->execute("UPDATE {forum_posts}
  2207.                             SET mailed = :mailedsuccess
  2208.                             WHERE discussion NOT IN (SELECT d.id
  2209.                                                      FROM {forum_discussions} d
  2210.                                                      WHERE d.timestart > :now)
  2211.                             AND (created < :endtime OR mailnow = :mailnow)
  2212.                             AND mailed = :mailedpending", $params);
  2213.     }
  2214. }
  2215.  
  2216. /**
  2217.  * Get all the posts for a user in a forum suitable for forum_print_post
  2218.  *
  2219.  * @global object
  2220.  * @global object
  2221.  * @uses CONTEXT_MODULE
  2222.  * @return array
  2223.  */
  2224. function forum_get_user_posts($forumid, $userid) {
  2225.     global $CFG, $DB;
  2226.  
  2227.     $timedsql = "";
  2228.     $params = array($forumid, $userid);
  2229.  
  2230.     if (!empty($CFG->forum_enabletimedposts)) {
  2231.         $cm = get_coursemodule_from_instance('forum', $forumid);
  2232.         if (!has_capability('mod/forum:viewhiddentimedposts' , context_module::instance($cm->id))) {
  2233.             $now = time();
  2234.             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
  2235.             $params[] = $now;
  2236.             $params[] = $now;
  2237.         }
  2238.     }
  2239.  
  2240.     return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  2241.                              FROM {forum} f
  2242.                                   JOIN {forum_discussions} d ON d.forum = f.id
  2243.                                   JOIN {forum_posts} p       ON p.discussion = d.id
  2244.                                   JOIN {user} u              ON u.id = p.userid
  2245.                             WHERE f.id = ?
  2246.                                   AND p.userid = ?
  2247.                                   $timedsql
  2248.                          ORDER BY p.modified ASC", $params);
  2249. }
  2250.  
  2251. /**
  2252.  * Get all the discussions user participated in
  2253.  *
  2254.  * @global object
  2255.  * @global object
  2256.  * @uses CONTEXT_MODULE
  2257.  * @param int $forumid
  2258.  * @param int $userid
  2259.  * @return array Array or false
  2260.  */
  2261. function forum_get_user_involved_discussions($forumid, $userid) {
  2262.     global $CFG, $DB;
  2263.  
  2264.     $timedsql = "";
  2265.     $params = array($forumid, $userid);
  2266.     if (!empty($CFG->forum_enabletimedposts)) {
  2267.         $cm = get_coursemodule_from_instance('forum', $forumid);
  2268.         if (!has_capability('mod/forum:viewhiddentimedposts' , context_module::instance($cm->id))) {
  2269.             $now = time();
  2270.             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
  2271.             $params[] = $now;
  2272.             $params[] = $now;
  2273.         }
  2274.     }
  2275.  
  2276.     return $DB->get_records_sql("SELECT DISTINCT d.*
  2277.                              FROM {forum} f
  2278.                                   JOIN {forum_discussions} d ON d.forum = f.id
  2279.                                   JOIN {forum_posts} p       ON p.discussion = d.id
  2280.                             WHERE f.id = ?
  2281.                                   AND p.userid = ?
  2282.                                   $timedsql", $params);
  2283. }
  2284.  
  2285. /**
  2286.  * Get all the posts for a user in a forum suitable for forum_print_post
  2287.  *
  2288.  * @global object
  2289.  * @global object
  2290.  * @param int $forumid
  2291.  * @param int $userid
  2292.  * @return array of counts or false
  2293.  */
  2294. function forum_count_user_posts($forumid, $userid) {
  2295.     global $CFG, $DB;
  2296.  
  2297.     $timedsql = "";
  2298.     $params = array($forumid, $userid);
  2299.     if (!empty($CFG->forum_enabletimedposts)) {
  2300.         $cm = get_coursemodule_from_instance('forum', $forumid);
  2301.         if (!has_capability('mod/forum:viewhiddentimedposts' , context_module::instance($cm->id))) {
  2302.             $now = time();
  2303.             $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))";
  2304.             $params[] = $now;
  2305.             $params[] = $now;
  2306.         }
  2307.     }
  2308.  
  2309.     return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
  2310.                             FROM {forum} f
  2311.                                  JOIN {forum_discussions} d ON d.forum = f.id
  2312.                                  JOIN {forum_posts} p       ON p.discussion = d.id
  2313.                                  JOIN {user} u              ON u.id = p.userid
  2314.                            WHERE f.id = ?
  2315.                                  AND p.userid = ?
  2316.                                  $timedsql", $params);
  2317. }
  2318.  
  2319. /**
  2320.  * Given a log entry, return the forum post details for it.
  2321.  *
  2322.  * @global object
  2323.  * @global object
  2324.  * @param object $log
  2325.  * @return array|null
  2326.  */
  2327. function forum_get_post_from_log($log) {
  2328.     global $CFG, $DB;
  2329.  
  2330.     if ($log->action == "add post") {
  2331.  
  2332.         return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  2333.                                           u.firstname, u.lastname, u.email, u.picture
  2334.                                 FROM {forum_discussions} d,
  2335.                                      {forum_posts} p,
  2336.                                      {forum} f,
  2337.                                      {user} u
  2338.                                WHERE p.id = ?
  2339.                                  AND d.id = p.discussion
  2340.                                  AND p.userid = u.id
  2341.                                  AND u.deleted <> '1'
  2342.                                  AND f.id = d.forum", array($log->info));
  2343.  
  2344.  
  2345.     } else if ($log->action == "add discussion") {
  2346.  
  2347.         return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  2348.                                           u.firstname, u.lastname, u.email, u.picture
  2349.                                 FROM {forum_discussions} d,
  2350.                                      {forum_posts} p,
  2351.                                      {forum} f,
  2352.                                      {user} u
  2353.                                WHERE d.id = ?
  2354.                                  AND d.firstpost = p.id
  2355.                                  AND p.userid = u.id
  2356.                                  AND u.deleted <> '1'
  2357.                                  AND f.id = d.forum", array($log->info));
  2358.     }
  2359.     return NULL;
  2360. }
  2361.  
  2362. /**
  2363.  * Given a discussion id, return the first post from the discussion
  2364.  *
  2365.  * @global object
  2366.  * @global object
  2367.  * @param int $dicsussionid
  2368.  * @return array
  2369.  */
  2370. function forum_get_firstpost_from_discussion($discussionid) {
  2371.     global $CFG, $DB;
  2372.  
  2373.     return $DB->get_record_sql("SELECT p.*
  2374.                             FROM {forum_discussions} d,
  2375.                                  {forum_posts} p
  2376.                            WHERE d.id = ?
  2377.                              AND d.firstpost = p.id ", array($discussionid));
  2378. }
  2379.  
  2380. /**
  2381.  * Returns an array of counts of replies to each discussion
  2382.  *
  2383.  * @global object
  2384.  * @global object
  2385.  * @param int $forumid
  2386.  * @param string $forumsort
  2387.  * @param int $limit
  2388.  * @param int $page
  2389.  * @param int $perpage
  2390.  * @return array
  2391.  */
  2392. function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
  2393.     global $CFG, $DB;
  2394.  
  2395.     if ($limit > 0) {
  2396.         $limitfrom = 0;
  2397.         $limitnum  = $limit;
  2398.     } else if ($page != -1) {
  2399.         $limitfrom = $page*$perpage;
  2400.         $limitnum  = $perpage;
  2401.     } else {
  2402.         $limitfrom = 0;
  2403.         $limitnum  = 0;
  2404.     }
  2405.  
  2406.     if ($forumsort == "") {
  2407.         $orderby = "";
  2408.         $groupby = "";
  2409.  
  2410.     } else {
  2411.         $orderby = "ORDER BY $forumsort";
  2412.         $groupby = ", ".strtolower($forumsort);
  2413.         $groupby = str_replace('desc', '', $groupby);
  2414.         $groupby = str_replace('asc', '', $groupby);
  2415.     }
  2416.  
  2417.     if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
  2418.         $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
  2419.                  FROM {forum_posts} p
  2420.                       JOIN {forum_discussions} d ON p.discussion = d.id
  2421.                 WHERE p.parent > 0 AND d.forum = ?
  2422.              GROUP BY p.discussion";
  2423.         return $DB->get_records_sql($sql, array($forumid));
  2424.  
  2425.     } else {
  2426.         $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
  2427.                  FROM {forum_posts} p
  2428.                       JOIN {forum_discussions} d ON p.discussion = d.id
  2429.                 WHERE d.forum = ?
  2430.              GROUP BY p.discussion $groupby
  2431.              $orderby";
  2432.         return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum);
  2433.     }
  2434. }
  2435.  
  2436. /**
  2437.  * @global object
  2438.  * @global object
  2439.  * @global object
  2440.  * @staticvar array $cache
  2441.  * @param object $forum
  2442.  * @param object $cm
  2443.  * @param object $course
  2444.  * @return mixed
  2445.  */
  2446. function forum_count_discussions($forum, $cm, $course) {
  2447.     global $CFG, $DB, $USER;
  2448.  
  2449.     static $cache = array();
  2450.  
  2451.     $now = round(time(), -2); // db cache friendliness
  2452.  
  2453.     $params = array($course->id);
  2454.  
  2455.     if (!isset($cache[$course->id])) {
  2456.         if (!empty($CFG->forum_enabletimedposts)) {
  2457.             $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
  2458.             $params[] = $now;
  2459.             $params[] = $now;
  2460.         } else {
  2461.             $timedsql = "";
  2462.         }
  2463.  
  2464.         $sql = "SELECT f.id, COUNT(d.id) as dcount
  2465.                  FROM {forum} f
  2466.                       JOIN {forum_discussions} d ON d.forum = f.id
  2467.                 WHERE f.course = ?
  2468.                       $timedsql
  2469.              GROUP BY f.id";
  2470.  
  2471.         if ($counts = $DB->get_records_sql($sql, $params)) {
  2472.             foreach ($counts as $count) {
  2473.                 $counts[$count->id] = $count->dcount;
  2474.             }
  2475.             $cache[$course->id] = $counts;
  2476.         } else {
  2477.             $cache[$course->id] = array();
  2478.         }
  2479.     }
  2480.  
  2481.     if (empty($cache[$course->id][$forum->id])) {
  2482.         return 0;
  2483.     }
  2484.  
  2485.     $groupmode = groups_get_activity_groupmode($cm, $course);
  2486.  
  2487.     if ($groupmode != SEPARATEGROUPS) {
  2488.         return $cache[$course->id][$forum->id];
  2489.     }
  2490.  
  2491.     if (has_capability('moodle/site:accessallgroups', context_module::instance($cm->id))) {
  2492.         return $cache[$course->id][$forum->id];
  2493.     }
  2494.  
  2495.     require_once($CFG->dirroot.'/course/lib.php');
  2496.  
  2497.     $modinfo = get_fast_modinfo($course);
  2498.     if (is_null($modinfo->groups)) {
  2499.         $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  2500.     }
  2501.  
  2502.     if (array_key_exists($cm->groupingid, $modinfo->groups)) {
  2503.         $mygroups = $modinfo->groups[$cm->groupingid];
  2504.     } else {
  2505.         $mygroups = false; // Will be set below
  2506.     }
  2507.  
  2508.     // add all groups posts
  2509.     if (empty($mygroups)) {
  2510.         $mygroups = array(-1=>-1);
  2511.     } else {
  2512.         $mygroups[-1] = -1;
  2513.     }
  2514.  
  2515.     list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups);
  2516.     $params[] = $forum->id;
  2517.  
  2518.     if (!empty($CFG->forum_enabletimedposts)) {
  2519.         $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  2520.         $params[] = $now;
  2521.         $params[] = $now;
  2522.     } else {
  2523.         $timedsql = "";
  2524.     }
  2525.  
  2526.     $sql = "SELECT COUNT(d.id)
  2527.              FROM {forum_discussions} d
  2528.             WHERE d.groupid $mygroups_sql AND d.forum = ?
  2529.                   $timedsql";
  2530.  
  2531.     return $DB->get_field_sql($sql, $params);
  2532. }
  2533.  
  2534. /**
  2535.  * How many posts by other users are unrated by a given user in the given discussion?
  2536.  *
  2537.  * TODO: Is this function still used anywhere?
  2538.  *
  2539.  * @param int $discussionid
  2540.  * @param int $userid
  2541.  * @return mixed
  2542.  */
  2543. function forum_count_unrated_posts($discussionid, $userid) {
  2544.     global $CFG, $DB;
  2545.  
  2546.     $sql = "SELECT COUNT(*) as num
  2547.              FROM {forum_posts}
  2548.             WHERE parent > 0
  2549.               AND discussion = :discussionid
  2550.               AND userid <> :userid";
  2551.     $params = array('discussionid' => $discussionid, 'userid' => $userid);
  2552.     $posts = $DB->get_record_sql($sql, $params);
  2553.     if ($posts) {
  2554.         $sql = "SELECT count(*) as num
  2555.                  FROM {forum_posts} p,
  2556.                       {rating} r
  2557.                 WHERE p.discussion = :discussionid AND
  2558.                       p.id = r.itemid AND
  2559.                       r.userid = userid AND
  2560.                       r.component = 'mod_forum' AND
  2561.                       r.ratingarea = 'post'";
  2562.         $rated = $DB->get_record_sql($sql, $params);
  2563.         if ($rated) {
  2564.             if ($posts->num > $rated->num) {
  2565.                 return $posts->num - $rated->num;
  2566.             } else {
  2567.                 return 0;    // Just in case there was a counting error
  2568.             }
  2569.         } else {
  2570.             return $posts->num;
  2571.         }
  2572.     } else {
  2573.         return 0;
  2574.     }
  2575. }
  2576.  
  2577. /**
  2578.  * Get all discussions in a forum
  2579.  *
  2580.  * @global object
  2581.  * @global object
  2582.  * @global object
  2583.  * @uses CONTEXT_MODULE
  2584.  * @uses VISIBLEGROUPS
  2585.  * @param object $cm
  2586.  * @param string $forumsort
  2587.  * @param bool $fullpost
  2588.  * @param int $unused
  2589.  * @param int $limit
  2590.  * @param bool $userlastmodified
  2591.  * @param int $page
  2592.  * @param int $perpage
  2593.  * @return array
  2594.  */
  2595. function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
  2596.     global $CFG, $DB, $USER;
  2597.  
  2598.     $timelimit = '';
  2599.  
  2600.     $now = round(time(), -2);
  2601.     $params = array($cm->instance);
  2602.  
  2603.     $modcontext = context_module::instance($cm->id);
  2604.  
  2605.     if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { /// User must have perms to view discussions
  2606.         return array();
  2607.     }
  2608.  
  2609.     if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
  2610.  
  2611.         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
  2612.             $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
  2613.             $params[] = $now;
  2614.             $params[] = $now;
  2615.             if (isloggedin()) {
  2616.                 $timelimit .= " OR d.userid = ?";
  2617.                 $params[] = $USER->id;
  2618.             }
  2619.             $timelimit .= ")";
  2620.         }
  2621.     }
  2622.  
  2623.     if ($limit > 0) {
  2624.         $limitfrom = 0;
  2625.         $limitnum  = $limit;
  2626.     } else if ($page != -1) {
  2627.         $limitfrom = $page*$perpage;
  2628.         $limitnum  = $perpage;
  2629.     } else {
  2630.         $limitfrom = 0;
  2631.         $limitnum  = 0;
  2632.     }
  2633.  
  2634.     $groupmode    = groups_get_activity_groupmode($cm);
  2635.     $currentgroup = groups_get_activity_group($cm);
  2636.  
  2637.     if ($groupmode) {
  2638.         if (empty($modcontext)) {
  2639.             $modcontext = context_module::instance($cm->id);
  2640.         }
  2641.  
  2642.         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  2643.             if ($currentgroup) {
  2644.                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
  2645.                 $params[] = $currentgroup;
  2646.             } else {
  2647.                 $groupselect = "";
  2648.             }
  2649.  
  2650.         } else {
  2651.             //seprate groups without access all
  2652.             if ($currentgroup) {
  2653.                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
  2654.                 $params[] = $currentgroup;
  2655.             } else {
  2656.                 $groupselect = "AND d.groupid = -1";
  2657.             }
  2658.         }
  2659.     } else {
  2660.         $groupselect = "";
  2661.     }
  2662.  
  2663.  
  2664.     if (empty($forumsort)) {
  2665.         $forumsort = "d.timemodified DESC";
  2666.     }
  2667.     if (empty($fullpost)) {
  2668.         $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
  2669.     } else {
  2670.         $postdata = "p.*";
  2671.     }
  2672.  
  2673.     if (empty($userlastmodified)) {  // We don't need to know this
  2674.         $umfields = "";
  2675.         $umtable  = "";
  2676.     } else {
  2677.         $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
  2678.         $umtable  = " LEFT JOIN {user} um ON (d.usermodified = um.id)";
  2679.     }
  2680.  
  2681.     $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
  2682.                   u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
  2683.              FROM {forum_discussions} d
  2684.                   JOIN {forum_posts} p ON p.discussion = d.id
  2685.                   JOIN {user} u ON p.userid = u.id
  2686.                   $umtable
  2687.             WHERE d.forum = ? AND p.parent = 0
  2688.                   $timelimit $groupselect
  2689.          ORDER BY $forumsort";
  2690.     return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
  2691. }
  2692.  
  2693. /**
  2694.  *
  2695.  * @global object
  2696.  * @global object
  2697.  * @global object
  2698.  * @uses CONTEXT_MODULE
  2699.  * @uses VISIBLEGROUPS
  2700.  * @param object $cm
  2701.  * @return array
  2702.  */
  2703. function forum_get_discussions_unread($cm) {
  2704.     global $CFG, $DB, $USER;
  2705.  
  2706.     $now = round(time(), -2);
  2707.     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  2708.  
  2709.     $params = array();
  2710.     $groupmode    = groups_get_activity_groupmode($cm);
  2711.     $currentgroup = groups_get_activity_group($cm);
  2712.  
  2713.     if ($groupmode) {
  2714.         $modcontext = context_module::instance($cm->id);
  2715.  
  2716.         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  2717.             if ($currentgroup) {
  2718.                 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
  2719.                 $params['currentgroup'] = $currentgroup;
  2720.             } else {
  2721.                 $groupselect = "";
  2722.             }
  2723.  
  2724.         } else {
  2725.             //separate groups without access all
  2726.             if ($currentgroup) {
  2727.                 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)";
  2728.                 $params['currentgroup'] = $currentgroup;
  2729.             } else {
  2730.                 $groupselect = "AND d.groupid = -1";
  2731.             }
  2732.         }
  2733.     } else {
  2734.         $groupselect = "";
  2735.     }
  2736.  
  2737.     if (!empty($CFG->forum_enabletimedposts)) {
  2738.         $timedsql = "AND d.timestart < :now1 AND (d.timeend = 0 OR d.timeend > :now2)";
  2739.         $params['now1'] = $now;
  2740.         $params['now2'] = $now;
  2741.     } else {
  2742.         $timedsql = "";
  2743.     }
  2744.  
  2745.     $sql = "SELECT d.id, COUNT(p.id) AS unread
  2746.              FROM {forum_discussions} d
  2747.                   JOIN {forum_posts} p     ON p.discussion = d.id
  2748.                   LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id)
  2749.             WHERE d.forum = {$cm->instance}
  2750.                   AND p.modified >= :cutoffdate AND r.id is NULL
  2751.                   $groupselect
  2752.                   $timedsql
  2753.          GROUP BY d.id";
  2754.     $params['cutoffdate'] = $cutoffdate;
  2755.  
  2756.     if ($unreads = $DB->get_records_sql($sql, $params)) {
  2757.         foreach ($unreads as $unread) {
  2758.             $unreads[$unread->id] = $unread->unread;
  2759.         }
  2760.         return $unreads;
  2761.     } else {
  2762.         return array();
  2763.     }
  2764. }
  2765.  
  2766. /**
  2767.  * @global object
  2768.  * @global object
  2769.  * @global object
  2770.  * @uses CONEXT_MODULE
  2771.  * @uses VISIBLEGROUPS
  2772.  * @param object $cm
  2773.  * @return array
  2774.  */
  2775. function forum_get_discussions_count($cm) {
  2776.     global $CFG, $DB, $USER;
  2777.  
  2778.     $now = round(time(), -2);
  2779.     $params = array($cm->instance);
  2780.     $groupmode    = groups_get_activity_groupmode($cm);
  2781.     $currentgroup = groups_get_activity_group($cm);
  2782.  
  2783.     if ($groupmode) {
  2784.         $modcontext = context_module::instance($cm->id);
  2785.  
  2786.         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  2787.             if ($currentgroup) {
  2788.                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
  2789.                 $params[] = $currentgroup;
  2790.             } else {
  2791.                 $groupselect = "";
  2792.             }
  2793.  
  2794.         } else {
  2795.             //seprate groups without access all
  2796.             if ($currentgroup) {
  2797.                 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)";
  2798.                 $params[] = $currentgroup;
  2799.             } else {
  2800.                 $groupselect = "AND d.groupid = -1";
  2801.             }
  2802.         }
  2803.     } else {
  2804.         $groupselect = "";
  2805.     }
  2806.  
  2807.     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  2808.  
  2809.     $timelimit = "";
  2810.  
  2811.     if (!empty($CFG->forum_enabletimedposts)) {
  2812.  
  2813.         $modcontext = context_module::instance($cm->id);
  2814.  
  2815.         if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
  2816.             $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))";
  2817.             $params[] = $now;
  2818.             $params[] = $now;
  2819.             if (isloggedin()) {
  2820.                 $timelimit .= " OR d.userid = ?";
  2821.                 $params[] = $USER->id;
  2822.             }
  2823.             $timelimit .= ")";
  2824.         }
  2825.     }
  2826.  
  2827.     $sql = "SELECT COUNT(d.id)
  2828.              FROM {forum_discussions} d
  2829.                   JOIN {forum_posts} p ON p.discussion = d.id
  2830.             WHERE d.forum = ? AND p.parent = 0
  2831.                   $groupselect $timelimit";
  2832.  
  2833.     return $DB->get_field_sql($sql, $params);
  2834. }
  2835.  
  2836.  
  2837. /**
  2838.  * Get all discussions started by a particular user in a course (or group)
  2839.  * This function no longer used ...
  2840.  *
  2841.  * @todo Remove this function if no longer used
  2842.  * @global object
  2843.  * @global object
  2844.  * @param int $courseid
  2845.  * @param int $userid
  2846.  * @param int $groupid
  2847.  * @return array
  2848.  */
  2849. function forum_get_user_discussions($courseid, $userid, $groupid=0) {
  2850.     global $CFG, $DB;
  2851.     $params = array($courseid, $userid);
  2852.     if ($groupid) {
  2853.         $groupselect = " AND d.groupid = ? ";
  2854.         $params[] = $groupid;
  2855.     } else  {
  2856.         $groupselect = "";
  2857.     }
  2858.  
  2859.     return $DB->get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
  2860.                                   f.type as forumtype, f.name as forumname, f.id as forumid
  2861.                              FROM {forum_discussions} d,
  2862.                                   {forum_posts} p,
  2863.                                   {user} u,
  2864.                                   {forum} f
  2865.                             WHERE d.course = ?
  2866.                               AND p.discussion = d.id
  2867.                               AND p.parent = 0
  2868.                               AND p.userid = u.id
  2869.                               AND u.id = ?
  2870.                               AND d.forum = f.id $groupselect
  2871.                          ORDER BY p.created DESC", $params);
  2872. }
  2873.  
  2874. /**
  2875.  * Get the list of potential subscribers to a forum.
  2876.  *
  2877.  * @param object $forumcontext the forum context.
  2878.  * @param integer $groupid the id of a group, or 0 for all groups.
  2879.  * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
  2880.  * @param string $sort sort order. As for get_users_by_capability.
  2881.  * @return array list of users.
  2882.  */
  2883. function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort = '') {
  2884.     global $DB;
  2885.  
  2886.     // only active enrolled users or everybody on the frontpage
  2887.     list($esql, $params) = get_enrolled_sql($forumcontext, 'mod/forum:allowforcesubscribe', $groupid, true);
  2888.     if (!$sort) {
  2889.         list($sort, $sortparams) = users_order_by_sql('u');
  2890.         $params = array_merge($params, $sortparams);
  2891.     }
  2892.  
  2893.     $sql = "SELECT $fields
  2894.              FROM {user} u
  2895.              JOIN ($esql) je ON je.id = u.id
  2896.          ORDER BY $sort";
  2897.  
  2898.     return $DB->get_records_sql($sql, $params);
  2899. }
  2900.  
  2901. /**
  2902.  * Returns list of user objects that are subscribed to this forum
  2903.  *
  2904.  * @global object
  2905.  * @global object
  2906.  * @param object $course the course
  2907.  * @param forum $forum the forum
  2908.  * @param integer $groupid group id, or 0 for all.
  2909.  * @param object $context the forum context, to save re-fetching it where possible.
  2910.  * @param string $fields requested user fields (with "u." table prefix)
  2911.  * @return array list of users.
  2912.  */
  2913. function forum_subscribed_users($course, $forum, $groupid=0, $context = null, $fields = null) {
  2914.     global $CFG, $DB;
  2915.  
  2916.     if (empty($fields)) {
  2917.         $fields ="u.id,
  2918.                  u.username,
  2919.                  u.firstname,
  2920.                  u.lastname,
  2921.                  u.maildisplay,
  2922.                  u.mailformat,
  2923.                  u.maildigest,
  2924.                  u.imagealt,
  2925.                  u.email,
  2926.                  u.emailstop,
  2927.                  u.city,
  2928.                  u.country,
  2929.                  u.lastaccess,
  2930.                  u.lastlogin,
  2931.                  u.picture,
  2932.                  u.timezone,
  2933.                  u.theme,
  2934.                  u.lang,
  2935.                  u.trackforums,
  2936.                  u.mnethostid";
  2937.     }
  2938.  
  2939.     if (empty($context)) {
  2940.         $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
  2941.         $context = context_module::instance($cm->id);
  2942.     }
  2943.  
  2944.     if (forum_is_forcesubscribed($forum)) {
  2945.         $results = forum_get_potential_subscribers($context, $groupid, $fields, "u.email ASC");
  2946.  
  2947.     } else {
  2948.         // only active enrolled users or everybody on the frontpage
  2949.         list($esql, $params) = get_enrolled_sql($context, '', $groupid, true);
  2950.         $params['forumid'] = $forum->id;
  2951.         $results = $DB->get_records_sql("SELECT $fields
  2952.                                           FROM {user} u
  2953.                                           JOIN ($esql) je ON je.id = u.id
  2954.                                           JOIN {forum_subscriptions} s ON s.userid = u.id
  2955.                                          WHERE s.forum = :forumid
  2956.                                       ORDER BY u.email ASC", $params);
  2957.     }
  2958.  
  2959.     // Guest user should never be subscribed to a forum.
  2960.     unset($results[$CFG->siteguest]);
  2961.  
  2962.     return $results;
  2963. }
  2964.  
  2965.  
  2966.  
  2967. // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
  2968.  
  2969.  
  2970. /**
  2971.  * @global object
  2972.  * @global object
  2973.  * @param int $courseid
  2974.  * @param string $type
  2975.  */
  2976. function forum_get_course_forum($courseid, $type) {
  2977. // How to set up special 1-per-course forums
  2978.     global $CFG, $DB, $OUTPUT, $USER;
  2979.  
  2980.     if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $type), "id ASC")) {
  2981.         // There should always only be ONE, but with the right combination of
  2982.         // errors there might be more.  In this case, just return the oldest one (lowest ID).
  2983.         foreach ($forums as $forum) {
  2984.             return $forum;   // ie the first one
  2985.         }
  2986.     }
  2987.  
  2988.     // Doesn't exist, so create one now.
  2989.     $forum = new stdClass();
  2990.     $forum->course = $courseid;
  2991.     $forum->type = "$type";
  2992.     if (!empty($USER->htmleditor)) {
  2993.         $forum->introformat = $USER->htmleditor;
  2994.     }
  2995.     switch ($forum->type) {
  2996.         case "news":
  2997.             $forum->name  = get_string("namenews", "forum");
  2998.             $forum->intro = get_string("intronews", "forum");
  2999.             $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
  3000.             $forum->assessed = 0;
  3001.             if ($courseid == SITEID) {
  3002.                 $forum->name  = get_string("sitenews");
  3003.                 $forum->forcesubscribe = 0;
  3004.             }
  3005.             break;
  3006.         case "social":
  3007.             $forum->name  = get_string("namesocial", "forum");
  3008.             $forum->intro = get_string("introsocial", "forum");
  3009.             $forum->assessed = 0;
  3010.             $forum->forcesubscribe = 0;
  3011.             break;
  3012.         case "blog":
  3013.             $forum->name = get_string('blogforum', 'forum');
  3014.             $forum->intro = get_string('introblog', 'forum');
  3015.             $forum->assessed = 0;
  3016.             $forum->forcesubscribe = 0;
  3017.             break;
  3018.         default:
  3019.             echo $OUTPUT->notification("That forum type doesn't exist!");
  3020.             return false;
  3021.             break;
  3022.     }
  3023.  
  3024.     $forum->timemodified = time();
  3025.     $forum->id = $DB->insert_record("forum", $forum);
  3026.  
  3027.     if (! $module = $DB->get_record("modules", array("name" => "forum"))) {
  3028.         echo $OUTPUT->notification("Could not find forum module!!");
  3029.         return false;
  3030.     }
  3031.     $mod = new stdClass();
  3032.     $mod->course = $courseid;
  3033.     $mod->module = $module->id;
  3034.     $mod->instance = $forum->id;
  3035.     $mod->section = 0;
  3036.     include_once("$CFG->dirroot/course/lib.php");
  3037.     if (! $mod->coursemodule = add_course_module($mod) ) {
  3038.         echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'");
  3039.         return false;
  3040.     }
  3041.     $sectionid = course_add_cm_to_section($courseid, $mod->coursemodule, 0);
  3042.     return $DB->get_record("forum", array("id" => "$forum->id"));
  3043. }
  3044.  
  3045.  
  3046. /**
  3047.  * Given the data about a posting, builds up the HTML to display it and
  3048.  * returns the HTML in a string.  This is designed for sending via HTML email.
  3049.  *
  3050.  * @global object
  3051.  * @param object $course
  3052.  * @param object $cm
  3053.  * @param object $forum
  3054.  * @param object $discussion
  3055.  * @param object $post
  3056.  * @param object $userform
  3057.  * @param object $userto
  3058.  * @param bool $ownpost
  3059.  * @param bool $reply
  3060.  * @param bool $link
  3061.  * @param bool $rate
  3062.  * @param string $footer
  3063.  * @return string
  3064.  */
  3065. function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto,
  3066.                               $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
  3067.  
  3068.     global $CFG, $OUTPUT;
  3069.  
  3070.     $modcontext = context_module::instance($cm->id);
  3071.  
  3072.     if (!isset($userto->viewfullnames[$forum->id])) {
  3073.         $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
  3074.     } else {
  3075.         $viewfullnames = $userto->viewfullnames[$forum->id];
  3076.     }
  3077.  
  3078.     // add absolute file links
  3079.     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
  3080.  
  3081.     // format the post body
  3082.     $options = new stdClass();
  3083.     $options->para = true;
  3084.     $formattedtext = format_text($post->message, $post->messageformat, $options, $course->id);
  3085.  
  3086.     $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
  3087.  
  3088.     $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
  3089.     $output .= $OUTPUT->user_picture($userfrom, array('courseid'=>$course->id));
  3090.     $output .= '</td>';
  3091.  
  3092.     if ($post->parent) {
  3093.         $output .= '<td class="topic">';
  3094.     } else {
  3095.         $output .= '<td class="topic starter">';
  3096.     }
  3097.     $output .= '<div class="subject">'.format_string($post->subject).'</div>';
  3098.  
  3099.     $fullname = fullname($userfrom, $viewfullnames);
  3100.     $by = new stdClass();
  3101.     $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
  3102.     $by->date = userdate($post->modified, '', $userto->timezone);
  3103.     $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
  3104.  
  3105.     $output .= '</td></tr>';
  3106.  
  3107.     $output .= '<tr><td class="left side" valign="top">';
  3108.  
  3109.     if (isset($userfrom->groups)) {
  3110.         $groups = $userfrom->groups[$forum->id];
  3111.     } else {
  3112.         $groups = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  3113.     }
  3114.  
  3115.     if ($groups) {
  3116.         $output .= print_group_picture($groups, $course->id, false, true, true);
  3117.     } else {
  3118.         $output .= '&nbsp;';
  3119.     }
  3120.  
  3121.     $output .= '</td><td class="content">';
  3122.  
  3123.     $attachments = forum_print_attachments($post, $cm, 'html');
  3124.     if ($attachments !== '') {
  3125.         $output .= '<div class="attachments">';
  3126.         $output .= $attachments;
  3127.         $output .= '</div>';
  3128.     }
  3129.  
  3130.     $output .= $formattedtext;
  3131.  
  3132. // Commands
  3133.     $commands = array();
  3134.  
  3135.     if ($post->parent) {
  3136.         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  3137.                       $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
  3138.     }
  3139.  
  3140.     if ($reply) {
  3141.         $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
  3142.                       get_string('reply', 'forum').'</a>';
  3143.     }
  3144.  
  3145.     $output .= '<div class="commands">';
  3146.     $output .= implode(' | ', $commands);
  3147.     $output .= '</div>';
  3148.  
  3149. // Context link to post if required
  3150.     if ($link) {
  3151.         $output .= '<div class="link">';
  3152.         $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
  3153.                      get_string('postincontext', 'forum').'</a>';
  3154.         $output .= '</div>';
  3155.     }
  3156.  
  3157.     if ($footer) {
  3158.         $output .= '<div class="footer">'.$footer.'</div>';
  3159.     }
  3160.     $output .= '</td></tr></table>'."\n\n";
  3161.  
  3162.     return $output;
  3163. }
  3164.  
  3165. /**
  3166.  * Print a forum post
  3167.  *
  3168.  * @global object
  3169.  * @global object
  3170.  * @uses FORUM_MODE_THREADED
  3171.  * @uses PORTFOLIO_FORMAT_PLAINHTML
  3172.  * @uses PORTFOLIO_FORMAT_FILE
  3173.  * @uses PORTFOLIO_FORMAT_RICHHTML
  3174.  * @uses PORTFOLIO_ADD_TEXT_LINK
  3175.  * @uses CONTEXT_MODULE
  3176.  * @param object $post The post to print.
  3177.  * @param object $discussion
  3178.  * @param object $forum
  3179.  * @param object $cm
  3180.  * @param object $course
  3181.  * @param boolean $ownpost Whether this post belongs to the current user.
  3182.  * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
  3183.  * @param boolean $link Just print a shortened version of the post as a link to the full post.
  3184.  * @param string $footer Extra stuff to print after the message.
  3185.  * @param string $highlight Space-separated list of terms to highlight.
  3186.  * @param int $post_read true, false or -99. If we already know whether this user
  3187.  *          has read this post, pass that in, otherwise, pass in -99, and this
  3188.  *          function will work it out.
  3189.  * @param boolean $dummyifcantsee When forum_user_can_see_post says that
  3190.  *          the current user can't see this post, if this argument is true
  3191.  *          (the default) then print a dummy 'you can't see this post' post.
  3192.  *          If false, don't output anything at all.
  3193.  * @param bool|null $istracked
  3194.  * @return void
  3195.  */
  3196. function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
  3197.                           $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) {
  3198.     global $USER, $CFG, $OUTPUT;
  3199.  
  3200.     require_once($CFG->libdir . '/filelib.php');
  3201.  
  3202.     // String cache
  3203.     static $str;
  3204.  
  3205.     $modcontext = context_module::instance($cm->id);
  3206.  
  3207.     $post->course = $course->id;
  3208.     $post->forum  = $forum->id;
  3209.     $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id);
  3210.     if (!empty($CFG->enableplagiarism)) {
  3211.         require_once($CFG->libdir.'/plagiarismlib.php');
  3212.         $post->message .= plagiarism_get_links(array('userid' => $post->userid,
  3213.             'content' => $post->message,
  3214.             'cmid' => $cm->id,
  3215.             'course' => $post->course,
  3216.             'forum' => $post->forum));
  3217.     }
  3218.  
  3219.     // caching
  3220.     if (!isset($cm->cache)) {
  3221.         $cm->cache = new stdClass;
  3222.     }
  3223.  
  3224.     if (!isset($cm->cache->caps)) {
  3225.         $cm->cache->caps = array();
  3226.         $cm->cache->caps['mod/forum:viewdiscussion']   = has_capability('mod/forum:viewdiscussion', $modcontext);
  3227.         $cm->cache->caps['moodle/site:viewfullnames']  = has_capability('moodle/site:viewfullnames', $modcontext);
  3228.         $cm->cache->caps['mod/forum:editanypost']      = has_capability('mod/forum:editanypost', $modcontext);
  3229.         $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext);
  3230.         $cm->cache->caps['mod/forum:deleteownpost']    = has_capability('mod/forum:deleteownpost', $modcontext);
  3231.         $cm->cache->caps['mod/forum:deleteanypost']    = has_capability('mod/forum:deleteanypost', $modcontext);
  3232.         $cm->cache->caps['mod/forum:viewanyrating']    = has_capability('mod/forum:viewanyrating', $modcontext);
  3233.         $cm->cache->caps['mod/forum:exportpost']       = has_capability('mod/forum:exportpost', $modcontext);
  3234.         $cm->cache->caps['mod/forum:exportownpost']    = has_capability('mod/forum:exportownpost', $modcontext);
  3235.     }
  3236.  
  3237.     if (!isset($cm->uservisible)) {
  3238.         $cm->uservisible = coursemodule_visible_for_user($cm);
  3239.     }
  3240.  
  3241.     if ($istracked && is_null($postisread)) {
  3242.         $postisread = forum_tp_is_post_read($USER->id, $post);
  3243.     }
  3244.  
  3245.     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  3246.         $output = '';
  3247.         if (!$dummyifcantsee) {
  3248.             if ($return) {
  3249.                 return $output;
  3250.             }
  3251.             echo $output;
  3252.             return;
  3253.         }
  3254.         $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id));
  3255.         $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix'));
  3256.         $output .= html_writer::start_tag('div', array('class'=>'row header'));
  3257.         $output .= html_writer::tag('div', '', array('class'=>'left picture')); // Picture
  3258.         if ($post->parent) {
  3259.             $output .= html_writer::start_tag('div', array('class'=>'topic'));
  3260.         } else {
  3261.             $output .= html_writer::start_tag('div', array('class'=>'topic starter'));
  3262.         }
  3263.         $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class'=>'subject')); // Subject
  3264.         $output .= html_writer::tag('div', get_string('forumauthorhidden','forum'), array('class'=>'author')); // author
  3265.         $output .= html_writer::end_tag('div');
  3266.         $output .= html_writer::end_tag('div'); // row
  3267.         $output .= html_writer::start_tag('div', array('class'=>'row'));
  3268.         $output .= html_writer::tag('div', '&nbsp;', array('class'=>'left side')); // Groups
  3269.         $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content
  3270.         $output .= html_writer::end_tag('div'); // row
  3271.         $output .= html_writer::end_tag('div'); // forumpost
  3272.  
  3273.         if ($return) {
  3274.             return $output;
  3275.         }
  3276.         echo $output;
  3277.         return;
  3278.     }
  3279.  
  3280.     if (empty($str)) {
  3281.         $str = new stdClass;
  3282.         $str->edit         = get_string('edit', 'forum');
  3283.         $str->delete       = get_string('delete', 'forum');
  3284.         $str->reply        = get_string('reply', 'forum');
  3285.         $str->parent       = get_string('parent', 'forum');
  3286.         $str->pruneheading = get_string('pruneheading', 'forum');
  3287.         $str->prune        = get_string('prune', 'forum');
  3288.         $str->displaymode     = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
  3289.         $str->markread     = get_string('markread', 'forum');
  3290.         $str->markunread   = get_string('markunread', 'forum');
  3291.     }
  3292.  
  3293.     $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion));
  3294.  
  3295.     // Build an object that represents the posting user
  3296.     $postuser = new stdClass;
  3297.     $postuser->id        = $post->userid;
  3298.     $postuser->firstname = $post->firstname;
  3299.     $postuser->lastname  = $post->lastname;
  3300.     $postuser->imagealt  = $post->imagealt;
  3301.     $postuser->picture   = $post->picture;
  3302.     $postuser->email     = $post->email;
  3303.     // Some handy things for later on
  3304.     $postuser->fullname    = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']);
  3305.     $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id));
  3306.  
  3307.     // Prepare the groups the posting user belongs to
  3308.     if (isset($cm->cache->usersgroups)) {
  3309.         $groups = array();
  3310.         if (isset($cm->cache->usersgroups[$post->userid])) {
  3311.             foreach ($cm->cache->usersgroups[$post->userid] as $gid) {
  3312.                 $groups[$gid] = $cm->cache->groups[$gid];
  3313.             }
  3314.         }
  3315.     } else {
  3316.         $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid);
  3317.     }
  3318.  
  3319.     // Prepare the attachements for the post, files then images
  3320.     list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages');
  3321.  
  3322.     // Determine if we need to shorten this post
  3323.     $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost));
  3324.  
  3325.  
  3326.     // Prepare an array of commands
  3327.     $commands = array();
  3328.  
  3329.     // SPECIAL CASE: The front page can display a news item post to non-logged in users.
  3330.     // Don't display the mark read / unread controls in this case.
  3331.     if ($istracked && $CFG->forum_usermarksread && isloggedin()) {
  3332.         $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread'));
  3333.         $text = $str->markunread;
  3334.         if (!$postisread) {
  3335.             $url->param('mark', 'read');
  3336.             $text = $str->markread;
  3337.         }
  3338.         if ($str->displaymode == FORUM_MODE_THREADED) {
  3339.             $url->param('parent', $post->parent);
  3340.         } else {
  3341.             $url->set_anchor('p'.$post->id);
  3342.         }
  3343.         $commands[] = array('url'=>$url, 'text'=>$text);
  3344.     }
  3345.  
  3346.     // Zoom in to the parent specifically
  3347.     if ($post->parent) {
  3348.         $url = new moodle_url($discussionlink);
  3349.         if ($str->displaymode == FORUM_MODE_THREADED) {
  3350.             $url->param('parent', $post->parent);
  3351.         } else {
  3352.             $url->set_anchor('p'.$post->parent);
  3353.         }
  3354.         $commands[] = array('url'=>$url, 'text'=>$str->parent);
  3355.     }
  3356.  
  3357.     // Hack for allow to edit news posts those are not displayed yet until they are displayed
  3358.     $age = time() - $post->created;
  3359.     if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) {
  3360.         $age = 0;
  3361.     }
  3362.  
  3363.     if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
  3364.         if (has_capability('moodle/course:manageactivities', $modcontext)) {
  3365.             // The first post in single simple is the forum description.
  3366.             $commands[] = array('url'=>new moodle_url('/course/modedit.php', array('update'=>$cm->id, 'sesskey'=>sesskey(), 'return'=>1)), 'text'=>$str->edit);
  3367.         }
  3368.     } else if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) {
  3369.         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit);
  3370.     }
  3371.  
  3372.     // Added by Girish N J
  3373.    
  3374.  
  3375.      $commands[] = array('url'=>new moodle_url('#'), 'text'=>'LIKE','attributes'=>array('id'=>'demolike'));
  3376. // attempt 1
  3377.     $commands[]= array ('sub'=>$post->subject);
  3378. // attempt 2
  3379.   global $content;
  3380.  
  3381.   $content=$post->subject;
  3382.  
  3383.  
  3384.   if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') {
  3385.         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading);
  3386.     }
  3387.  
  3388.     if ($forum->type == 'single' and $discussion->firstpost == $post->id) {
  3389.         // Do not allow deleting of first post in single simple type.
  3390.     } else if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) {
  3391.         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete);
  3392.     }
  3393.  
  3394.     if ($reply) {
  3395.         $commands[] = array('url'=>new moodle_url('/mod/forum/post.php#mformforum', array('reply'=>$post->id)), 'text'=>$str->reply);
  3396.     }
  3397.  
  3398.     if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) {
  3399.         $p = array('postid' => $post->id);
  3400.         require_once($CFG->libdir.'/portfoliolib.php');
  3401.         $button = new portfolio_add_button();
  3402.         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), 'mod_forum');
  3403.         if (empty($attachments)) {
  3404.             $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
  3405.         } else {
  3406.             $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
  3407.         }
  3408.  
  3409.         $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
  3410.         if (!empty($porfoliohtml)) {
  3411.             $commands[] = $porfoliohtml;
  3412.         }
  3413.     }
  3414.     // Finished building commands
  3415.  
  3416.  
  3417.     // Begin output
  3418.  
  3419.     $output  = '';
  3420.  
  3421.     if ($istracked) {
  3422.         if ($postisread) {
  3423.             $forumpostclass = ' read';
  3424.         } else {
  3425.             $forumpostclass = ' unread';
  3426.             $output .= html_writer::tag('a', '', array('name'=>'unread'));
  3427.         }
  3428.     } else {
  3429.         // ignore trackign status if not tracked or tracked param missing
  3430.         $forumpostclass = '';
  3431.     }
  3432.  
  3433.     $topicclass = '';
  3434.     if (empty($post->parent)) {
  3435.         $topicclass = ' firstpost starter';
  3436.     }
  3437.  
  3438.     $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id));
  3439.     $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix'.$forumpostclass.$topicclass));
  3440.     $output .= html_writer::start_tag('div', array('class'=>'row header clearfix'));
  3441.     $output .= html_writer::start_tag('div', array('class'=>'left picture'));
  3442.     $output .= $OUTPUT->user_picture($postuser, array('courseid'=>$course->id));
  3443.     $output .= html_writer::end_tag('div');
  3444.  
  3445.  
  3446.     $output .= html_writer::start_tag('div', array('class'=>'topic'.$topicclass));
  3447.  
  3448.     $postsubject = $post->subject;
  3449.     if (empty($post->subjectnoformat)) {
  3450.         $postsubject = format_string($postsubject);
  3451.     }
  3452.     $output .= html_writer::tag('div', $postsubject, array('class'=>'subject'));
  3453.  
  3454.     $by = new stdClass();
  3455.     $by->name = html_writer::link($postuser->profilelink, $postuser->fullname);
  3456.     $by->date = userdate($post->modified);
  3457.     $output .= html_writer::tag('div', get_string('bynameondate', 'forum', $by), array('class'=>'author'));
  3458.  
  3459.     $output .= html_writer::end_tag('div'); //topic
  3460.     $output .= html_writer::end_tag('div'); //row
  3461.  
  3462.     $output .= html_writer::start_tag('div', array('class'=>'row maincontent clearfix'));
  3463.     $output .= html_writer::start_tag('div', array('class'=>'left'));
  3464.  
  3465.     $groupoutput = '';
  3466.     if ($groups) {
  3467.         $groupoutput = print_group_picture($groups, $course->id, false, true, true);
  3468.     }
  3469.     if (empty($groupoutput)) {
  3470.         $groupoutput = '&nbsp;';
  3471.     }
  3472.     $output .= html_writer::tag('div', $groupoutput, array('class'=>'grouppictures'));
  3473.  
  3474.     $output .= html_writer::end_tag('div'); //left side
  3475.     $output .= html_writer::start_tag('div', array('class'=>'no-overflow'));
  3476.     $output .= html_writer::start_tag('div', array('class'=>'content'));
  3477.     if (!empty($attachments)) {
  3478.         $output .= html_writer::tag('div', $attachments, array('class'=>'attachments'));
  3479.     }
  3480.  
  3481.     $options = new stdClass;
  3482.     $options->para    = false;
  3483.     $options->trusted = $post->messagetrust;
  3484.     $options->context = $modcontext;
  3485.     if ($shortenpost) {
  3486.         // Prepare shortened version by filtering the text then shortening it.
  3487.         $postclass    = 'shortenedpost';
  3488.         $postcontent  = format_text($post->message, $post->messageformat, $options);
  3489.         $postcontent  = forum_shorten_post($postcontent);
  3490.         $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum'));
  3491.         $postcontent .= html_writer::tag('div', '('.get_string('numwords', 'moodle', count_words($post->message)).')',
  3492.             array('class'=>'post-word-count'));
  3493.     } else {
  3494.         // Prepare whole post
  3495.         $postclass    = 'fullpost';
  3496.         $postcontent  = format_text($post->message, $post->messageformat, $options, $course->id);
  3497.         if (!empty($highlight)) {
  3498.             $postcontent = highlight($highlight, $postcontent);
  3499.         }
  3500.         if (!empty($forum->displaywordcount)) {
  3501.             $postcontent .= html_writer::tag('div', get_string('numwords', 'moodle', count_words($post->message)),
  3502.                 array('class'=>'post-word-count'));
  3503.         }
  3504.         $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages'));
  3505.     }
  3506.  
  3507.     // Output the post content
  3508.     $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass));
  3509.     $output .= html_writer::end_tag('div'); // Content
  3510.     $output .= html_writer::end_tag('div'); // Content mask
  3511.     $output .= html_writer::end_tag('div'); // Row
  3512.  
  3513.     $output .= html_writer::start_tag('div', array('class'=>'row side'));
  3514.     $output .= html_writer::tag('div','&nbsp;', array('class'=>'left'));
  3515.     $output .= html_writer::start_tag('div', array('class'=>'options clearfix'));
  3516.  
  3517.     // Output ratings
  3518.     if (!empty($post->rating)) {
  3519.         $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating'));
  3520.     }
  3521.  
  3522.     // Output the commands
  3523.     $commandhtml = array();
  3524.     foreach ($commands as $command) {
  3525.         if (is_array($command)) {
  3526.             $commandhtml[] = html_writer::link($command['url'], $command['text'],$command['attributes']);
  3527.         } else {
  3528.             $commandhtml[] = $command;
  3529.         }
  3530.     }
  3531.     $output .= html_writer::tag('div', implode(' | ', $commandhtml), array('class'=>'commands'));
  3532.  
  3533.     // Output link to post if required
  3534.     if ($link) {
  3535.         if ($post->replies == 1) {
  3536.             $replystring = get_string('repliesone', 'forum', $post->replies);
  3537.         } else {
  3538.             $replystring = get_string('repliesmany', 'forum', $post->replies);
  3539.         }
  3540.  
  3541.         $output .= html_writer::start_tag('div', array('class'=>'link'));
  3542.         $output .= html_writer::link($discussionlink, get_string('discussthistopic', 'forum'));
  3543.         $output .= '&nbsp;('.$replystring.')';
  3544.         $output .= html_writer::end_tag('div'); // link
  3545.     }
  3546.  
  3547.     // Output footer if required
  3548.     if ($footer) {
  3549.         $output .= html_writer::tag('div', $footer, array('class'=>'footer'));
  3550.     }
  3551.  
  3552.     // Close remaining open divs
  3553.     $output .= html_writer::end_tag('div'); // content
  3554.     $output .= html_writer::end_tag('div'); // row
  3555.     $output .= html_writer::end_tag('div'); // forumpost
  3556.  
  3557.     // Mark the forum post as read if required
  3558.     if ($istracked && !$CFG->forum_usermarksread && !$postisread) {
  3559.         forum_tp_mark_post_read($USER->id, $post, $forum->id);
  3560.     }
  3561.  
  3562.     if ($return) {
  3563.         return $output;
  3564.     }
  3565.     echo $output;
  3566.     return;
  3567. }
  3568.  
  3569. /**
  3570.  * Return rating related permissions
  3571.  *
  3572.  * @param string $options the context id
  3573.  * @return array an associative array of the user's rating permissions
  3574.  */
  3575. function forum_rating_permissions($contextid, $component, $ratingarea) {
  3576.     $context = context::instance_by_id($contextid, MUST_EXIST);
  3577.     if ($component != 'mod_forum' || $ratingarea != 'post') {
  3578.         // We don't know about this component/ratingarea so just return null to get the
  3579.         // default restrictive permissions.
  3580.         return null;
  3581.     }
  3582.     return array(
  3583.         'view'    => has_capability('mod/forum:viewrating', $context),
  3584.         'viewany' => has_capability('mod/forum:viewanyrating', $context),
  3585.         'viewall' => has_capability('mod/forum:viewallratings', $context),
  3586.         'rate'    => has_capability('mod/forum:rate', $context)
  3587.     );
  3588. }
  3589.  
  3590. /**
  3591.  * Validates a submitted rating
  3592.  * @param array $params submitted data
  3593.  *            context => object the context in which the rated items exists [required]
  3594.  *            component => The component for this module - should always be mod_forum [required]
  3595.  *            ratingarea => object the context in which the rated items exists [required]
  3596.  *            itemid => int the ID of the object being rated [required]
  3597.  *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
  3598.  *            rating => int the submitted rating [required]
  3599.  *            rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
  3600.  *            aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
  3601.  * @return boolean true if the rating is valid. Will throw rating_exception if not
  3602.  */
  3603. function forum_rating_validate($params) {
  3604.     global $DB, $USER;
  3605.  
  3606.     // Check the component is mod_forum
  3607.     if ($params['component'] != 'mod_forum') {
  3608.         throw new rating_exception('invalidcomponent');
  3609.     }
  3610.  
  3611.     // Check the ratingarea is post (the only rating area in forum)
  3612.     if ($params['ratingarea'] != 'post') {
  3613.         throw new rating_exception('invalidratingarea');
  3614.     }
  3615.  
  3616.     // Check the rateduserid is not the current user .. you can't rate your own posts
  3617.     if ($params['rateduserid'] == $USER->id) {
  3618.         throw new rating_exception('nopermissiontorate');
  3619.     }
  3620.  
  3621.     // Fetch all the related records ... we need to do this anyway to call forum_user_can_see_post
  3622.     $post = $DB->get_record('forum_posts', array('id' => $params['itemid'], 'userid' => $params['rateduserid']), '*', MUST_EXIST);
  3623.     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion), '*', MUST_EXIST);
  3624.     $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
  3625.     $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
  3626.     $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id , false, MUST_EXIST);
  3627.     $context = context_module::instance($cm->id);
  3628.  
  3629.     // Make sure the context provided is the context of the forum
  3630.     if ($context->id != $params['context']->id) {
  3631.         throw new rating_exception('invalidcontext');
  3632.     }
  3633.  
  3634.     if ($forum->scale != $params['scaleid']) {
  3635.         //the scale being submitted doesnt match the one in the database
  3636.         throw new rating_exception('invalidscaleid');
  3637.     }
  3638.  
  3639.     // check the item we're rating was created in the assessable time window
  3640.     if (!empty($forum->assesstimestart) && !empty($forum->assesstimefinish)) {
  3641.         if ($post->created < $forum->assesstimestart || $post->created > $forum->assesstimefinish) {
  3642.             throw new rating_exception('notavailable');
  3643.         }
  3644.     }
  3645.  
  3646.     //check that the submitted rating is valid for the scale
  3647.  
  3648.     // lower limit
  3649.     if ($params['rating'] < 0  && $params['rating'] != RATING_UNSET_RATING) {
  3650.         throw new rating_exception('invalidnum');
  3651.     }
  3652.  
  3653.     // upper limit
  3654.     if ($forum->scale < 0) {
  3655.         //its a custom scale
  3656.         $scalerecord = $DB->get_record('scale', array('id' => -$forum->scale));
  3657.         if ($scalerecord) {
  3658.             $scalearray = explode(',', $scalerecord->scale);
  3659.             if ($params['rating'] > count($scalearray)) {
  3660.                 throw new rating_exception('invalidnum');
  3661.             }
  3662.         } else {
  3663.             throw new rating_exception('invalidscaleid');
  3664.         }
  3665.     } else if ($params['rating'] > $forum->scale) {
  3666.         //if its numeric and submitted rating is above maximum
  3667.         throw new rating_exception('invalidnum');
  3668.     }
  3669.  
  3670.     // Make sure groups allow this user to see the item they're rating
  3671.     if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
  3672.         if (!groups_group_exists($discussion->groupid)) { // Can't find group
  3673.             throw new rating_exception('cannotfindgroup');//something is wrong
  3674.         }
  3675.  
  3676.         if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
  3677.             // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
  3678.             throw new rating_exception('notmemberofgroup');
  3679.         }
  3680.     }
  3681.  
  3682.     // perform some final capability checks
  3683.     if (!forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
  3684.         throw new rating_exception('nopermissiontorate');
  3685.     }
  3686.  
  3687.     return true;
  3688. }
  3689.  
  3690.  
  3691. /**
  3692.  * This function prints the overview of a discussion in the forum listing.
  3693.  * It needs some discussion information and some post information, these
  3694.  * happen to be combined for efficiency in the $post parameter by the function
  3695.  * that calls this one: forum_print_latest_discussions()
  3696.  *
  3697.  * @global object
  3698.  * @global object
  3699.  * @param object $post The post object (passed by reference for speed).
  3700.  * @param object $forum The forum object.
  3701.  * @param int $group Current group.
  3702.  * @param string $datestring Format to use for the dates.
  3703.  * @param boolean $cantrack Is tracking enabled for this forum.
  3704.  * @param boolean $forumtracked Is the user tracking this forum.
  3705.  * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
  3706.  */
  3707. function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
  3708.                                         $cantrack=true, $forumtracked=true, $canviewparticipants=true, $modcontext=NULL) {
  3709.  
  3710.     global $USER, $CFG, $OUTPUT;
  3711.  
  3712.     static $rowcount;
  3713.     static $strmarkalldread;
  3714.  
  3715.     if (empty($modcontext)) {
  3716.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  3717.             print_error('invalidcoursemodule');
  3718.         }
  3719.         $modcontext = context_module::instance($cm->id);
  3720.     }
  3721.  
  3722.     if (!isset($rowcount)) {
  3723.         $rowcount = 0;
  3724.         $strmarkalldread = get_string('markalldread', 'forum');
  3725.     } else {
  3726.         $rowcount = ($rowcount + 1) % 2;
  3727.     }
  3728.  
  3729.     $post->subject = format_string($post->subject,true);
  3730.  
  3731.     echo "\n\n";
  3732.     echo '<tr class="discussion r'.$rowcount.'">';
  3733.  
  3734.     // Topic
  3735.     echo '<td class="topic starter">';
  3736.     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
  3737.     echo "</td>\n";
  3738.  
  3739.     // Picture
  3740.     $postuser = new stdClass();
  3741.     $postuser->id = $post->userid;
  3742.     $postuser->firstname = $post->firstname;
  3743.     $postuser->lastname = $post->lastname;
  3744.     $postuser->imagealt = $post->imagealt;
  3745.     $postuser->picture = $post->picture;
  3746.     $postuser->email = $post->email;
  3747.  
  3748.     echo '<td class="picture">';
  3749.     echo $OUTPUT->user_picture($postuser, array('courseid'=>$forum->course));
  3750.     echo "</td>\n";
  3751.  
  3752.     // User name
  3753.     $fullname = fullname($postuser, has_capability('moodle/site:viewfullnames', $modcontext));
  3754.     echo '<td class="author">';
  3755.     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
  3756.     echo "</td>\n";
  3757.  
  3758.     // Group picture
  3759.     if ($group !== -1) {  // Groups are active - group is a group data object or NULL
  3760.         echo '<td class="picture group">';
  3761.         if (!empty($group->picture) and empty($group->hidepicture)) {
  3762.             print_group_picture($group, $forum->course, false, false, true);
  3763.         } else if (isset($group->id)) {
  3764.             if($canviewparticipants) {
  3765.                 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
  3766.             } else {
  3767.                 echo $group->name;
  3768.             }
  3769.         }
  3770.         echo "</td>\n";
  3771.     }
  3772.  
  3773.     if (has_capability('mod/forum:viewdiscussion', $modcontext)) {   // Show the column with replies
  3774.         echo '<td class="replies">';
  3775.         echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
  3776.         echo $post->replies.'</a>';
  3777.         echo "</td>\n";
  3778.  
  3779.         if ($cantrack) {
  3780.             echo '<td class="replies">';
  3781.             if ($forumtracked) {
  3782.                 if ($post->unread > 0) {
  3783.                     echo '<span class="unread">';
  3784.                     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
  3785.                     echo $post->unread;
  3786.                     echo '</a>';
  3787.                     echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
  3788.                          $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
  3789.                          '<img src="'.$OUTPUT->pix_url('t/markasread') . '" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
  3790.                     echo '</span>';
  3791.                 } else {
  3792.                     echo '<span class="read">';
  3793.                     echo $post->unread;
  3794.                     echo '</span>';
  3795.                 }
  3796.             } else {
  3797.                 echo '<span class="read">';
  3798.                 echo '-';
  3799.                 echo '</span>';
  3800.             }
  3801.             echo "</td>\n";
  3802.         }
  3803.     }
  3804.  
  3805.     echo '<td class="lastpost">';
  3806.     $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified;  // Just in case
  3807.     $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
  3808.     $usermodified = new stdClass();
  3809.     $usermodified->id        = $post->usermodified;
  3810.     $usermodified->firstname = $post->umfirstname;
  3811.     $usermodified->lastname  = $post->umlastname;
  3812.     echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
  3813.          fullname($usermodified).'</a><br />';
  3814.     echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
  3815.           userdate($usedate, $datestring).'</a>';
  3816.     echo "</td>\n";
  3817.  
  3818.     echo "</tr>\n\n";
  3819.  
  3820. }
  3821.  
  3822.  
  3823. /**
  3824.  * Given a post object that we already know has a long message
  3825.  * this function truncates the message nicely to the first
  3826.  * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
  3827.  *
  3828.  * @global object
  3829.  * @param string $message
  3830.  * @return string
  3831.  */
  3832. function forum_shorten_post($message) {
  3833.     global $CFG;
  3834.  
  3835.     return shorten_text($message, $CFG->forum_shortpost);
  3836. }
  3837.  
  3838. /**
  3839.  * Print the drop down that allows the user to select how they want to have
  3840.  * the discussion displayed.
  3841.  *
  3842.  * @param int $id forum id if $forumtype is 'single',
  3843.  *              discussion id for any other forum type
  3844.  * @param mixed $mode forum layout mode
  3845.  * @param string $forumtype optional
  3846.  */
  3847. function forum_print_mode_form($id, $mode, $forumtype='') {
  3848.     global $OUTPUT;
  3849.     if ($forumtype == 'single') {
  3850.         $select = new single_select(new moodle_url("/mod/forum/view.php", array('f'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode");
  3851.         $select->set_label(get_string('displaymode', 'forum'), array('class' => 'accesshide'));
  3852.         $select->class = "forummode";
  3853.     } else {
  3854.         $select = new single_select(new moodle_url("/mod/forum/discuss.php", array('d'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode");
  3855.         $select->set_label(get_string('displaymode', 'forum'), array('class' => 'accesshide'));
  3856.     }
  3857.     echo $OUTPUT->render($select);
  3858. }
  3859.  
  3860. /**
  3861.  * @global object
  3862.  * @param object $course
  3863.  * @param string $search
  3864.  * @return string
  3865.  */
  3866. function forum_search_form($course, $search='') {
  3867.     global $CFG, $OUTPUT;
  3868.  
  3869.     $output  = '<div class="forumsearch">';
  3870.     $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
  3871.     $output .= '<fieldset class="invisiblefieldset">';
  3872.     $output .= $OUTPUT->help_icon('search');
  3873.     $output .= '<label class="accesshide" for="search" >'.get_string('search', 'forum').'</label>';
  3874.     $output .= '<input id="search" name="search" type="text" size="18" value="'.s($search, true).'" alt="search" />';
  3875.     $output .= '<label class="accesshide" for="searchforums" >'.get_string('searchforums', 'forum').'</label>';
  3876.     $output .= '<input id="searchforums" value="'.get_string('searchforums', 'forum').'" type="submit" />';
  3877.     $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
  3878.     $output .= '</fieldset>';
  3879.     $output .= '</form>';
  3880.     $output .= '</div>';
  3881.  
  3882.     return $output;
  3883. }
  3884.  
  3885.  
  3886. /**
  3887.  * @global object
  3888.  * @global object
  3889.  */
  3890. function forum_set_return() {
  3891.     global $CFG, $SESSION;
  3892.  
  3893.     if (! isset($SESSION->fromdiscussion)) {
  3894.         if (!empty($_SERVER['HTTP_REFERER'])) {
  3895.             $referer = $_SERVER['HTTP_REFERER'];
  3896.         } else {
  3897.             $referer = "";
  3898.         }
  3899.         // If the referer is NOT a login screen then save it.
  3900.         if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
  3901.             $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
  3902.         }
  3903.     }
  3904. }
  3905.  
  3906.  
  3907. /**
  3908.  * @global object
  3909.  * @param string $default
  3910.  * @return string
  3911.  */
  3912. function forum_go_back_to($default) {
  3913.     global $SESSION;
  3914.  
  3915.     if (!empty($SESSION->fromdiscussion)) {
  3916.         $returnto = $SESSION->fromdiscussion;
  3917.         unset($SESSION->fromdiscussion);
  3918.         return $returnto;
  3919.     } else {
  3920.         return $default;
  3921.     }
  3922. }
  3923.  
  3924. /**
  3925.  * Given a discussion object that is being moved to $forumto,
  3926.  * this function checks all posts in that discussion
  3927.  * for attachments, and if any are found, these are
  3928.  * moved to the new forum directory.
  3929.  *
  3930.  * @global object
  3931.  * @param object $discussion
  3932.  * @param int $forumfrom source forum id
  3933.  * @param int $forumto target forum id
  3934.  * @return bool success
  3935.  */
  3936. function forum_move_attachments($discussion, $forumfrom, $forumto) {
  3937.     global $DB;
  3938.  
  3939.     $fs = get_file_storage();
  3940.  
  3941.     $newcm = get_coursemodule_from_instance('forum', $forumto);
  3942.     $oldcm = get_coursemodule_from_instance('forum', $forumfrom);
  3943.  
  3944.     $newcontext = context_module::instance($newcm->id);
  3945.     $oldcontext = context_module::instance($oldcm->id);
  3946.  
  3947.     // loop through all posts, better not use attachment flag ;-)
  3948.     if ($posts = $DB->get_records('forum_posts', array('discussion'=>$discussion->id), '', 'id, attachment')) {
  3949.         foreach ($posts as $post) {
  3950.             $fs->move_area_files_to_new_context($oldcontext->id,
  3951.                     $newcontext->id, 'mod_forum', 'post', $post->id);
  3952.             $attachmentsmoved = $fs->move_area_files_to_new_context($oldcontext->id,
  3953.                     $newcontext->id, 'mod_forum', 'attachment', $post->id);
  3954.             if ($attachmentsmoved > 0 && $post->attachment != '1') {
  3955.                 // Weird - let's fix it
  3956.                 $post->attachment = '1';
  3957.                 $DB->update_record('forum_posts', $post);
  3958.             } else if ($attachmentsmoved == 0 && $post->attachment != '') {
  3959.                 // Weird - let's fix it
  3960.                 $post->attachment = '';
  3961.                 $DB->update_record('forum_posts', $post);
  3962.             }
  3963.         }
  3964.     }
  3965.  
  3966.     return true;
  3967. }
  3968.  
  3969. /**
  3970.  * Returns attachments as formated text/html optionally with separate images
  3971.  *
  3972.  * @global object
  3973.  * @global object
  3974.  * @global object
  3975.  * @param object $post
  3976.  * @param object $cm
  3977.  * @param string $type html/text/separateimages
  3978.  * @return mixed string or array of (html text withouth images and image HTML)
  3979.  */
  3980. function forum_print_attachments($post, $cm, $type) {
  3981.     global $CFG, $DB, $USER, $OUTPUT;
  3982.  
  3983.     if (empty($post->attachment)) {
  3984.         return $type !== 'separateimages' ? '' : array('', '');
  3985.     }
  3986.  
  3987.     if (!in_array($type, array('separateimages', 'html', 'text'))) {
  3988.         return $type !== 'separateimages' ? '' : array('', '');
  3989.     }
  3990.  
  3991.     if (!$context = context_module::instance($cm->id)) {
  3992.         return $type !== 'separateimages' ? '' : array('', '');
  3993.     }
  3994.     $strattachment = get_string('attachment', 'forum');
  3995.  
  3996.     $fs = get_file_storage();
  3997.  
  3998.     $imagereturn = '';
  3999.     $output = '';
  4000.  
  4001.     $canexport = !empty($CFG->enableportfolios) && (has_capability('mod/forum:exportpost', $context) || ($post->userid == $USER->id && has_capability('mod/forum:exportownpost', $context)));
  4002.  
  4003.     if ($canexport) {
  4004.         require_once($CFG->libdir.'/portfoliolib.php');
  4005.     }
  4006.  
  4007.     $files = $fs->get_area_files($context->id, 'mod_forum', 'attachment', $post->id, "timemodified", false);
  4008.     if ($files) {
  4009.         if ($canexport) {
  4010.             $button = new portfolio_add_button();
  4011.         }
  4012.         foreach ($files as $file) {
  4013.             $filename = $file->get_filename();
  4014.             $mimetype = $file->get_mimetype();
  4015.             $iconimage = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon'));
  4016.             $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context->id.'/mod_forum/attachment/'.$post->id.'/'.$filename);
  4017.  
  4018.             if ($type == 'html') {
  4019.                 $output .= "<a href=\"$path\">$iconimage</a> ";
  4020.                 $output .= "<a href=\"$path\">".s($filename)."</a>";
  4021.                 if ($canexport) {
  4022.                     $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), 'mod_forum');
  4023.                     $button->set_format_by_file($file);
  4024.                     $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
  4025.                 }
  4026.                 $output .= "<br />";
  4027.  
  4028.             } else if ($type == 'text') {
  4029.                 $output .= "$strattachment ".s($filename).":\n$path\n";
  4030.  
  4031.             } else { //'returnimages'
  4032.                 if (in_array($mimetype, array('image/gif', 'image/jpeg', 'image/png'))) {
  4033.                     // Image attachments don't get printed as links
  4034.                     $imagereturn .= "<br /><img src=\"$path\" alt=\"\" />";
  4035.                     if ($canexport) {
  4036.                         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), 'mod_forum');
  4037.                         $button->set_format_by_file($file);
  4038.                         $imagereturn .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
  4039.                     }
  4040.                 } else {
  4041.                     $output .= "<a href=\"$path\">$iconimage</a> ";
  4042.                     $output .= format_text("<a href=\"$path\">".s($filename)."</a>", FORMAT_HTML, array('context'=>$context));
  4043.                     if ($canexport) {
  4044.                         $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), 'mod_forum');
  4045.                         $button->set_format_by_file($file);
  4046.                         $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
  4047.                     }
  4048.                     $output .= '<br />';
  4049.                 }
  4050.             }
  4051.  
  4052.             if (!empty($CFG->enableplagiarism)) {
  4053.                 require_once($CFG->libdir.'/plagiarismlib.php');
  4054.                 $output .= plagiarism_get_links(array('userid' => $post->userid,
  4055.                     'file' => $file,
  4056.                     'cmid' => $cm->id,
  4057.                     'course' => $post->course,
  4058.                     'forum' => $post->forum));
  4059.                 $output .= '<br />';
  4060.             }
  4061.         }
  4062.     }
  4063.  
  4064.     if ($type !== 'separateimages') {
  4065.         return $output;
  4066.  
  4067.     } else {
  4068.         return array($output, $imagereturn);
  4069.     }
  4070. }
  4071.  
  4072. ////////////////////////////////////////////////////////////////////////////////
  4073. // File API                                                                   //
  4074. ////////////////////////////////////////////////////////////////////////////////
  4075.  
  4076. /**
  4077.  * Lists all browsable file areas
  4078.  *
  4079.  * @package  mod_forum
  4080.  * @category files
  4081.  * @param stdClass $course course object
  4082.  * @param stdClass $cm course module object
  4083.  * @param stdClass $context context object
  4084.  * @return array
  4085.  */
  4086. function forum_get_file_areas($course, $cm, $context) {
  4087.     return array(
  4088.         'attachment' => get_string('areaattachment', 'mod_forum'),
  4089.         'post' => get_string('areapost', 'mod_forum'),
  4090.     );
  4091. }
  4092.  
  4093. /**
  4094.  * File browsing support for forum module.
  4095.  *
  4096.  * @package  mod_forum
  4097.  * @category files
  4098.  * @param stdClass $browser file browser object
  4099.  * @param stdClass $areas file areas
  4100.  * @param stdClass $course course object
  4101.  * @param stdClass $cm course module
  4102.  * @param stdClass $context context module
  4103.  * @param string $filearea file area
  4104.  * @param int $itemid item ID
  4105.  * @param string $filepath file path
  4106.  * @param string $filename file name
  4107.  * @return file_info instance or null if not found
  4108.  */
  4109. function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
  4110.     global $CFG, $DB, $USER;
  4111.  
  4112.     if ($context->contextlevel != CONTEXT_MODULE) {
  4113.         return null;
  4114.     }
  4115.  
  4116.     // filearea must contain a real area
  4117.     if (!isset($areas[$filearea])) {
  4118.         return null;
  4119.     }
  4120.  
  4121.     // Note that forum_user_can_see_post() additionally allows access for parent roles
  4122.     // and it explicitly checks qanda forum type, too. One day, when we stop requiring
  4123.     // course:managefiles, we will need to extend this.
  4124.     if (!has_capability('mod/forum:viewdiscussion', $context)) {
  4125.         return null;
  4126.     }
  4127.  
  4128.     if (is_null($itemid)) {
  4129.         require_once($CFG->dirroot.'/mod/forum/locallib.php');
  4130.         return new forum_file_info_container($browser, $course, $cm, $context, $areas, $filearea);
  4131.     }
  4132.  
  4133.     static $cached = array();
  4134.     // $cached will store last retrieved post, discussion and forum. To make sure that the cache
  4135.     // is cleared between unit tests we check if this is the same session
  4136.     if (!isset($cached['sesskey']) || $cached['sesskey'] != sesskey()) {
  4137.         $cached = array('sesskey' => sesskey());
  4138.     }
  4139.  
  4140.     if (isset($cached['post']) && $cached['post']->id == $itemid) {
  4141.         $post = $cached['post'];
  4142.     } else if ($post = $DB->get_record('forum_posts', array('id' => $itemid))) {
  4143.         $cached['post'] = $post;
  4144.     } else {
  4145.         return null;
  4146.     }
  4147.  
  4148.     if (isset($cached['discussion']) && $cached['discussion']->id == $post->discussion) {
  4149.         $discussion = $cached['discussion'];
  4150.     } else if ($discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion))) {
  4151.         $cached['discussion'] = $discussion;
  4152.     } else {
  4153.         return null;
  4154.     }
  4155.  
  4156.     if (isset($cached['forum']) && $cached['forum']->id == $cm->instance) {
  4157.         $forum = $cached['forum'];
  4158.     } else if ($forum = $DB->get_record('forum', array('id' => $cm->instance))) {
  4159.         $cached['forum'] = $forum;
  4160.     } else {
  4161.         return null;
  4162.     }
  4163.  
  4164.     $fs = get_file_storage();
  4165.     $filepath = is_null($filepath) ? '/' : $filepath;
  4166.     $filename = is_null($filename) ? '.' : $filename;
  4167.     if (!($storedfile = $fs->get_file($context->id, 'mod_forum', $filearea, $itemid, $filepath, $filename))) {
  4168.         return null;
  4169.     }
  4170.  
  4171.     // Checks to see if the user can manage files or is the owner.
  4172.     // TODO MDL-33805 - Do not use userid here and move the capability check above.
  4173.     if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
  4174.         return null;
  4175.     }
  4176.     // Make sure groups allow this user to see this file
  4177.     if ($discussion->groupid > 0 && !has_capability('moodle/site:accessallgroups', $context)) {
  4178.         $groupmode = groups_get_activity_groupmode($cm, $course);
  4179.         if ($groupmode == SEPARATEGROUPS && !groups_is_member($discussion->groupid)) {
  4180.             return null;
  4181.         }
  4182.     }
  4183.  
  4184.     // Make sure we're allowed to see it...
  4185.     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  4186.         return null;
  4187.     }
  4188.  
  4189.     $urlbase = $CFG->wwwroot.'/pluginfile.php';
  4190.     return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false);
  4191. }
  4192.  
  4193. /**
  4194.  * Serves the forum attachments. Implements needed access control ;-)
  4195.  *
  4196.  * @package  mod_forum
  4197.  * @category files
  4198.  * @param stdClass $course course object
  4199.  * @param stdClass $cm course module object
  4200.  * @param stdClass $context context object
  4201.  * @param string $filearea file area
  4202.  * @param array $args extra arguments
  4203.  * @param bool $forcedownload whether or not force download
  4204.  * @param array $options additional options affecting the file serving
  4205.  * @return bool false if file not found, does not return if found - justsend the file
  4206.  */
  4207. function forum_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
  4208.     global $CFG, $DB;
  4209.  
  4210.     if ($context->contextlevel != CONTEXT_MODULE) {
  4211.         return false;
  4212.     }
  4213.  
  4214.     require_course_login($course, true, $cm);
  4215.  
  4216.     $areas = forum_get_file_areas($course, $cm, $context);
  4217.  
  4218.     // filearea must contain a real area
  4219.     if (!isset($areas[$filearea])) {
  4220.         return false;
  4221.     }
  4222.  
  4223.     $postid = (int)array_shift($args);
  4224.  
  4225.     if (!$post = $DB->get_record('forum_posts', array('id'=>$postid))) {
  4226.         return false;
  4227.     }
  4228.  
  4229.     if (!$discussion = $DB->get_record('forum_discussions', array('id'=>$post->discussion))) {
  4230.         return false;
  4231.     }
  4232.  
  4233.     if (!$forum = $DB->get_record('forum', array('id'=>$cm->instance))) {
  4234.         return false;
  4235.     }
  4236.  
  4237.     $fs = get_file_storage();
  4238.     $relativepath = implode('/', $args);
  4239.     $fullpath = "/$context->id/mod_forum/$filearea/$postid/$relativepath";
  4240.     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
  4241.         return false;
  4242.     }
  4243.  
  4244.     // Make sure groups allow this user to see this file
  4245.     if ($discussion->groupid > 0) {
  4246.         $groupmode = groups_get_activity_groupmode($cm, $course);
  4247.         if ($groupmode == SEPARATEGROUPS) {
  4248.             if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
  4249.                 return false;
  4250.             }
  4251.         }
  4252.     }
  4253.  
  4254.     // Make sure we're allowed to see it...
  4255.     if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  4256.         return false;
  4257.     }
  4258.  
  4259.     // finally send the file
  4260.     send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
  4261. }
  4262.  
  4263. /**
  4264.  * If successful, this function returns the name of the file
  4265.  *
  4266.  * @global object
  4267.  * @param object $post is a full post record, including course and forum
  4268.  * @param object $forum
  4269.  * @param object $cm
  4270.  * @param mixed $mform
  4271.  * @param string $unused
  4272.  * @return bool
  4273.  */
  4274. function forum_add_attachment($post, $forum, $cm, $mform=null, $unused=null) {
  4275.     global $DB;
  4276.  
  4277.     if (empty($mform)) {
  4278.         return false;
  4279.     }
  4280.  
  4281.     if (empty($post->attachments)) {
  4282.         return true;   // Nothing to do
  4283.     }
  4284.  
  4285.     $context = context_module::instance($cm->id);
  4286.  
  4287.     $info = file_get_draft_area_info($post->attachments);
  4288.     $present = ($info['filecount']>0) ? '1' : '';
  4289.     file_save_draft_area_files($post->attachments, $context->id, 'mod_forum', 'attachment', $post->id,
  4290.             mod_forum_post_form::attachment_options($forum));
  4291.  
  4292.     $DB->set_field('forum_posts', 'attachment', $present, array('id'=>$post->id));
  4293.  
  4294.     return true;
  4295. }
  4296.  
  4297. /**
  4298.  * Add a new post in an existing discussion.
  4299.  *
  4300.  * @global object
  4301.  * @global object
  4302.  * @global object
  4303.  * @param object $post
  4304.  * @param mixed $mform
  4305.  * @param string $message
  4306.  * @return int
  4307.  */
  4308. function forum_add_new_post($post, $mform, &$message) {
  4309.     global $USER, $CFG, $DB;
  4310.  
  4311.     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion));
  4312.     $forum      = $DB->get_record('forum', array('id' => $discussion->forum));
  4313.     $cm         = get_coursemodule_from_instance('forum', $forum->id);
  4314.     $context    = context_module::instance($cm->id);
  4315.  
  4316.     $post->created    = $post->modified = time();
  4317.     $post->mailed     = FORUM_MAILED_PENDING;
  4318.     $post->userid     = $USER->id;
  4319.     $post->attachment = "";
  4320.  
  4321.     $post->id = $DB->insert_record("forum_posts", $post);
  4322.     $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id,
  4323.             mod_forum_post_form::editor_options(), $post->message);
  4324.     $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
  4325.     forum_add_attachment($post, $forum, $cm, $mform, $message);
  4326.  
  4327.     // Update discussion modified date
  4328.     $DB->set_field("forum_discussions", "timemodified", $post->modified, array("id" => $post->discussion));
  4329.     $DB->set_field("forum_discussions", "usermodified", $post->userid, array("id" => $post->discussion));
  4330.  
  4331.     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  4332.         forum_tp_mark_post_read($post->userid, $post, $post->forum);
  4333.     }
  4334.  
  4335.     // Let Moodle know that assessable content is uploaded (eg for plagiarism detection)
  4336.     forum_trigger_content_uploaded_event($post, $cm, 'forum_add_new_post');
  4337.  
  4338.     return $post->id;
  4339. }
  4340.  
  4341. /**
  4342.  * Update a post
  4343.  *
  4344.  * @global object
  4345.  * @global object
  4346.  * @global object
  4347.  * @param object $post
  4348.  * @param mixed $mform
  4349.  * @param string $message
  4350.  * @return bool
  4351.  */
  4352. function forum_update_post($post, $mform, &$message) {
  4353.     global $USER, $CFG, $DB;
  4354.  
  4355.     $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion));
  4356.     $forum      = $DB->get_record('forum', array('id' => $discussion->forum));
  4357.     $cm         = get_coursemodule_from_instance('forum', $forum->id);
  4358.     $context    = context_module::instance($cm->id);
  4359.  
  4360.     $post->modified = time();
  4361.  
  4362.     $DB->update_record('forum_posts', $post);
  4363.  
  4364.     $discussion->timemodified = $post->modified; // last modified tracking
  4365.     $discussion->usermodified = $post->userid;   // last modified tracking
  4366.  
  4367.     if (!$post->parent) {   // Post is a discussion starter - update discussion title and times too
  4368.         $discussion->name      = $post->subject;
  4369.         $discussion->timestart = $post->timestart;
  4370.         $discussion->timeend   = $post->timeend;
  4371.     }
  4372.     $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id,
  4373.             mod_forum_post_form::editor_options(), $post->message);
  4374.     $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id));
  4375.  
  4376.     $DB->update_record('forum_discussions', $discussion);
  4377.  
  4378.     forum_add_attachment($post, $forum, $cm, $mform, $message);
  4379.  
  4380.     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  4381.         forum_tp_mark_post_read($post->userid, $post, $post->forum);
  4382.     }
  4383.  
  4384.     // Let Moodle know that assessable content is uploaded (eg for plagiarism detection)
  4385.     forum_trigger_content_uploaded_event($post, $cm, 'forum_update_post');
  4386.  
  4387.     return true;
  4388. }
  4389.  
  4390. /**
  4391.  * Given an object containing all the necessary data,
  4392.  * create a new discussion and return the id
  4393.  *
  4394.  * @param object $post
  4395.  * @param mixed $mform
  4396.  * @param string $unused
  4397.  * @param int $userid
  4398.  * @return object
  4399.  */
  4400. function forum_add_discussion($discussion, $mform=null, $unused=null, $userid=null) {
  4401.     global $USER, $CFG, $DB;
  4402.  
  4403.     $timenow = time();
  4404.  
  4405.     if (is_null($userid)) {
  4406.         $userid = $USER->id;
  4407.     }
  4408.  
  4409.     // The first post is stored as a real post, and linked
  4410.     // to from the discuss entry.
  4411.  
  4412.     $forum = $DB->get_record('forum', array('id'=>$discussion->forum));
  4413.     $cm    = get_coursemodule_from_instance('forum', $forum->id);
  4414.  
  4415.     $post = new stdClass();
  4416.     $post->discussion    = 0;
  4417.     $post->parent        = 0;
  4418.     $post->userid        = $userid;
  4419.     $post->created       = $timenow;
  4420.     $post->modified      = $timenow;
  4421.     $post->mailed        = FORUM_MAILED_PENDING;
  4422.     $post->subject       = $discussion->name;
  4423.     $post->message       = $discussion->message;
  4424.     $post->messageformat = $discussion->messageformat;
  4425.     $post->messagetrust  = $discussion->messagetrust;
  4426.     $post->attachments   = isset($discussion->attachments) ? $discussion->attachments : null;
  4427.     $post->forum         = $forum->id;     // speedup
  4428.     $post->course        = $forum->course; // speedup
  4429.     $post->mailnow       = $discussion->mailnow;
  4430.  
  4431.     $post->id = $DB->insert_record("forum_posts", $post);
  4432.  
  4433.     // TODO: Fix the calling code so that there always is a $cm when this function is called
  4434.     if (!empty($cm->id) && !empty($discussion->itemid)) {   // In "single simple discussions" this may not exist yet
  4435.         $context = context_module::instance($cm->id);
  4436.         $text = file_save_draft_area_files($discussion->itemid, $context->id, 'mod_forum', 'post', $post->id,
  4437.                 mod_forum_post_form::editor_options(), $post->message);
  4438.         $DB->set_field('forum_posts', 'message', $text, array('id'=>$post->id));
  4439.     }
  4440.  
  4441.     // Now do the main entry for the discussion, linking to this first post
  4442.  
  4443.     $discussion->firstpost    = $post->id;
  4444.     $discussion->timemodified = $timenow;
  4445.     $discussion->usermodified = $post->userid;
  4446.     $discussion->userid       = $userid;
  4447.  
  4448.     $post->discussion = $DB->insert_record("forum_discussions", $discussion);
  4449.  
  4450.     // Finally, set the pointer on the post.
  4451.     $DB->set_field("forum_posts", "discussion", $post->discussion, array("id"=>$post->id));
  4452.  
  4453.     if (!empty($cm->id)) {
  4454.         forum_add_attachment($post, $forum, $cm, $mform, $unused);
  4455.     }
  4456.  
  4457.     if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  4458.         forum_tp_mark_post_read($post->userid, $post, $post->forum);
  4459.     }
  4460.  
  4461.     // Let Moodle know that assessable content is uploaded (eg for plagiarism detection)
  4462.     if (!empty($cm->id)) {
  4463.         forum_trigger_content_uploaded_event($post, $cm, 'forum_add_discussion');
  4464.     }
  4465.  
  4466.     return $post->discussion;
  4467. }
  4468.  
  4469.  
  4470. /**
  4471.  * Deletes a discussion and handles all associated cleanup.
  4472.  *
  4473.  * @global object
  4474.  * @param object $discussion Discussion to delete
  4475.  * @param bool $fulldelete True when deleting entire forum
  4476.  * @param object $course Course
  4477.  * @param object $cm Course-module
  4478.  * @param object $forum Forum
  4479.  * @return bool
  4480.  */
  4481. function forum_delete_discussion($discussion, $fulldelete, $course, $cm, $forum) {
  4482.     global $DB, $CFG;
  4483.     require_once($CFG->libdir.'/completionlib.php');
  4484.  
  4485.     $result = true;
  4486.  
  4487.     if ($posts = $DB->get_records("forum_posts", array("discussion" => $discussion->id))) {
  4488.         foreach ($posts as $post) {
  4489.             $post->course = $discussion->course;
  4490.             $post->forum  = $discussion->forum;
  4491.             if (!forum_delete_post($post, 'ignore', $course, $cm, $forum, $fulldelete)) {
  4492.                 $result = false;
  4493.             }
  4494.         }
  4495.     }
  4496.  
  4497.     forum_tp_delete_read_records(-1, -1, $discussion->id);
  4498.  
  4499.     if (!$DB->delete_records("forum_discussions", array("id"=>$discussion->id))) {
  4500.         $result = false;
  4501.     }
  4502.  
  4503.     // Update completion state if we are tracking completion based on number of posts
  4504.     // But don't bother when deleting whole thing
  4505.     if (!$fulldelete) {
  4506.         $completion = new completion_info($course);
  4507.         if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC &&
  4508.            ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) {
  4509.             $completion->update_state($cm, COMPLETION_INCOMPLETE, $discussion->userid);
  4510.         }
  4511.     }
  4512.  
  4513.     return $result;
  4514. }
  4515.  
  4516.  
  4517. /**
  4518.  * Deletes a single forum post.
  4519.  *
  4520.  * @global object
  4521.  * @param object $post Forum post object
  4522.  * @param mixed $children Whether to delete children. If false, returns false
  4523.  *   if there are any children (without deleting the post). If true,
  4524.  *   recursively deletes all children. If set to special value 'ignore', deletes
  4525.  *   post regardless of children (this is for use only when deleting all posts
  4526.  *   in a disussion).
  4527.  * @param object $course Course
  4528.  * @param object $cm Course-module
  4529.  * @param object $forum Forum
  4530.  * @param bool $skipcompletion True to skip updating completion state if it
  4531.  *   would otherwise be updated, i.e. when deleting entire forum anyway.
  4532.  * @return bool
  4533.  */
  4534. function forum_delete_post($post, $children, $course, $cm, $forum, $skipcompletion=false) {
  4535.     global $DB, $CFG;
  4536.     require_once($CFG->libdir.'/completionlib.php');
  4537.  
  4538.     $context = context_module::instance($cm->id);
  4539.  
  4540.     if ($children !== 'ignore' && ($childposts = $DB->get_records('forum_posts', array('parent'=>$post->id)))) {
  4541.        if ($children) {
  4542.            foreach ($childposts as $childpost) {
  4543.                forum_delete_post($childpost, true, $course, $cm, $forum, $skipcompletion);
  4544.            }
  4545.        } else {
  4546.            return false;
  4547.        }
  4548.     }
  4549.  
  4550.     // Delete ratings.
  4551.     require_once($CFG->dirroot.'/rating/lib.php');
  4552.     $delopt = new stdClass;
  4553.     $delopt->contextid = $context->id;
  4554.     $delopt->component = 'mod_forum';
  4555.     $delopt->ratingarea = 'post';
  4556.     $delopt->itemid = $post->id;
  4557.     $rm = new rating_manager();
  4558.     $rm->delete_ratings($delopt);
  4559.  
  4560.     // Delete attachments.
  4561.     $fs = get_file_storage();
  4562.     $fs->delete_area_files($context->id, 'mod_forum', 'attachment', $post->id);
  4563.     $fs->delete_area_files($context->id, 'mod_forum', 'post', $post->id);
  4564.  
  4565.     // Delete cached RSS feeds.
  4566.     if (!empty($CFG->enablerssfeeds)) {
  4567.         require_once($CFG->dirroot.'/mod/forum/rsslib.php');
  4568.         forum_rss_delete_file($forum);
  4569.     }
  4570.  
  4571.     if ($DB->delete_records("forum_posts", array("id" => $post->id))) {
  4572.  
  4573.         forum_tp_delete_read_records(-1, $post->id);
  4574.  
  4575.     // Just in case we are deleting the last post
  4576.         forum_discussion_update_last_post($post->discussion);
  4577.  
  4578.         // Update completion state if we are tracking completion based on number of posts
  4579.         // But don't bother when deleting whole thing
  4580.  
  4581.         if (!$skipcompletion) {
  4582.             $completion = new completion_info($course);
  4583.             if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC &&
  4584.                ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) {
  4585.                 $completion->update_state($cm, COMPLETION_INCOMPLETE, $post->userid);
  4586.             }
  4587.         }
  4588.  
  4589.         return true;
  4590.     }
  4591.     return false;
  4592. }
  4593.  
  4594. /**
  4595.  * Sends post content to plagiarism plugin
  4596.  * @param object $post Forum post object
  4597.  * @param object $cm Course-module
  4598.  * @param string $name
  4599.  * @return bool
  4600. */
  4601. function forum_trigger_content_uploaded_event($post, $cm, $name) {
  4602.     $context = context_module::instance($cm->id);
  4603.     $fs = get_file_storage();
  4604.     $files = $fs->get_area_files($context->id, 'mod_forum', 'attachment', $post->id, "timemodified", false);
  4605.     $eventdata = new stdClass();
  4606.     $eventdata->modulename   = 'forum';
  4607.     $eventdata->name         = $name;
  4608.     $eventdata->cmid         = $cm->id;
  4609.     $eventdata->itemid       = $post->id;
  4610.     $eventdata->courseid     = $post->course;
  4611.     $eventdata->userid       = $post->userid;
  4612.     $eventdata->content      = $post->message;
  4613.     if ($files) {
  4614.         $eventdata->pathnamehashes = array_keys($files);
  4615.     }
  4616.     events_trigger('assessable_content_uploaded', $eventdata);
  4617.  
  4618.     return true;
  4619. }
  4620.  
  4621. /**
  4622.  * @global object
  4623.  * @param object $post
  4624.  * @param bool $children
  4625.  * @return int
  4626.  */
  4627. function forum_count_replies($post, $children=true) {
  4628.     global $DB;
  4629.     $count = 0;
  4630.  
  4631.     if ($children) {
  4632.         if ($childposts = $DB->get_records('forum_posts', array('parent' => $post->id))) {
  4633.            foreach ($childposts as $childpost) {
  4634.                $count ++;                   // For this child
  4635.                $count += forum_count_replies($childpost, true);
  4636.            }
  4637.         }
  4638.     } else {
  4639.         $count += $DB->count_records('forum_posts', array('parent' => $post->id));
  4640.     }
  4641.  
  4642.     return $count;
  4643. }
  4644.  
  4645.  
  4646. /**
  4647.  * @global object
  4648.  * @param int $forumid
  4649.  * @param mixed $value
  4650.  * @return bool
  4651.  */
  4652. function forum_forcesubscribe($forumid, $value=1) {
  4653.     global $DB;
  4654.     return $DB->set_field("forum", "forcesubscribe", $value, array("id" => $forumid));
  4655. }
  4656.  
  4657. /**
  4658.  * @global object
  4659.  * @param object $forum
  4660.  * @return bool
  4661.  */
  4662. function forum_is_forcesubscribed($forum) {
  4663.     global $DB;
  4664.     if (isset($forum->forcesubscribe)) {    // then we use that
  4665.         return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE);
  4666.     } else {   // Check the database
  4667.        return ($DB->get_field('forum', 'forcesubscribe', array('id' => $forum)) == FORUM_FORCESUBSCRIBE);
  4668.     }
  4669. }
  4670.  
  4671. function forum_get_forcesubscribed($forum) {
  4672.     global $DB;
  4673.     if (isset($forum->forcesubscribe)) {    // then we use that
  4674.         return $forum->forcesubscribe;
  4675.     } else {   // Check the database
  4676.         return $DB->get_field('forum', 'forcesubscribe', array('id' => $forum));
  4677.     }
  4678. }
  4679.  
  4680. /**
  4681.  * @global object
  4682.  * @param int $userid
  4683.  * @param object $forum
  4684.  * @return bool
  4685.  */
  4686. function forum_is_subscribed($userid, $forum) {
  4687.     global $DB;
  4688.     if (is_numeric($forum)) {
  4689.         $forum = $DB->get_record('forum', array('id' => $forum));
  4690.     }
  4691.     // If forum is force subscribed and has allowforcesubscribe, then user is subscribed.
  4692.     $cm = get_coursemodule_from_instance('forum', $forum->id);
  4693.     if (forum_is_forcesubscribed($forum) && $cm &&
  4694.             has_capability('mod/forum:allowforcesubscribe', context_module::instance($cm->id), $userid)) {
  4695.         return true;
  4696.     }
  4697.     return $DB->record_exists("forum_subscriptions", array("userid" => $userid, "forum" => $forum->id));
  4698. }
  4699.  
  4700. function forum_get_subscribed_forums($course) {
  4701.     global $USER, $CFG, $DB;
  4702.     $sql = "SELECT f.id
  4703.              FROM {forum} f
  4704.                   LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?)
  4705.             WHERE f.course = ?
  4706.                   AND f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
  4707.                   AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)";
  4708.     if ($subscribed = $DB->get_records_sql($sql, array($USER->id, $course->id))) {
  4709.         foreach ($subscribed as $s) {
  4710.             $subscribed[$s->id] = $s->id;
  4711.         }
  4712.         return $subscribed;
  4713.     } else {
  4714.         return array();
  4715.     }
  4716. }
  4717.  
  4718. /**
  4719.  * Returns an array of forums that the current user is subscribed to and is allowed to unsubscribe from
  4720.  *
  4721.  * @return array An array of unsubscribable forums
  4722.  */
  4723. function forum_get_optional_subscribed_forums() {
  4724.     global $USER, $DB;
  4725.  
  4726.     // Get courses that $USER is enrolled in and can see
  4727.     $courses = enrol_get_my_courses();
  4728.     if (empty($courses)) {
  4729.         return array();
  4730.     }
  4731.  
  4732.     $courseids = array();
  4733.     foreach($courses as $course) {
  4734.         $courseids[] = $course->id;
  4735.     }
  4736.     list($coursesql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'c');
  4737.  
  4738.     // get all forums from the user's courses that they are subscribed to and which are not set to forced
  4739.     $sql = "SELECT f.id, cm.id as cm, cm.visible
  4740.              FROM {forum} f
  4741.                   JOIN {course_modules} cm ON cm.instance = f.id
  4742.                   JOIN {modules} m ON m.name = :modulename AND m.id = cm.module
  4743.                   LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
  4744.             WHERE f.forcesubscribe <> :forcesubscribe AND fs.id IS NOT NULL
  4745.                   AND cm.course $coursesql";
  4746.     $params = array_merge($courseparams, array('modulename'=>'forum', 'userid'=>$USER->id, 'forcesubscribe'=>FORUM_FORCESUBSCRIBE));
  4747.     if (!$forums = $DB->get_records_sql($sql, $params)) {
  4748.         return array();
  4749.     }
  4750.  
  4751.     $unsubscribableforums = array(); // Array to return
  4752.  
  4753.     foreach($forums as $forum) {
  4754.  
  4755.         if (empty($forum->visible)) {
  4756.             // the forum is hidden
  4757.             $context = context_module::instance($forum->cm);
  4758.             if (!has_capability('moodle/course:viewhiddenactivities', $context)) {
  4759.                 // the user can't see the hidden forum
  4760.                 continue;
  4761.             }
  4762.         }
  4763.  
  4764.         // subscribe.php only requires 'mod/forum:managesubscriptions' when
  4765.         // unsubscribing a user other than yourself so we don't require it here either
  4766.  
  4767.         // A check for whether the forum has subscription set to forced is built into the SQL above
  4768.  
  4769.         $unsubscribableforums[] = $forum;
  4770.     }
  4771.  
  4772.     return $unsubscribableforums;
  4773. }
  4774.  
  4775. /**
  4776.  * Adds user to the subscriber list
  4777.  *
  4778.  * @global object
  4779.  * @param int $userid
  4780.  * @param int $forumid
  4781.  */
  4782. function forum_subscribe($userid, $forumid) {
  4783.     global $DB;
  4784.  
  4785.     if ($DB->record_exists("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid))) {
  4786.         return true;
  4787.     }
  4788.  
  4789.     $sub = new stdClass();
  4790.     $sub->userid  = $userid;
  4791.     $sub->forum = $forumid;
  4792.  
  4793.     return $DB->insert_record("forum_subscriptions", $sub);
  4794. }
  4795.  
  4796. /**
  4797.  * Removes user from the subscriber list
  4798.  *
  4799.  * @global object
  4800.  * @param int $userid
  4801.  * @param int $forumid
  4802.  */
  4803. function forum_unsubscribe($userid, $forumid) {
  4804.     global $DB;
  4805.     return $DB->delete_records("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid));
  4806. }
  4807.  
  4808. /**
  4809.  * Given a new post, subscribes or unsubscribes as appropriate.
  4810.  * Returns some text which describes what happened.
  4811.  *
  4812.  * @global objec
  4813.  * @param object $post
  4814.  * @param object $forum
  4815.  */
  4816. function forum_post_subscription($post, $forum) {
  4817.  
  4818.     global $USER;
  4819.  
  4820.     $action = '';
  4821.     $subscribed = forum_is_subscribed($USER->id, $forum);
  4822.  
  4823.     if ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE) { // database ignored
  4824.         return "";
  4825.  
  4826.     } elseif (($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE)
  4827.         && !has_capability('moodle/course:manageactivities', context_course::instance($forum->course), $USER->id)) {
  4828.         if ($subscribed) {
  4829.             $action = 'unsubscribe'; // sanity check, following MDL-14558
  4830.         } else {
  4831.             return "";
  4832.         }
  4833.  
  4834.     } else { // go with the user's choice
  4835.         if (isset($post->subscribe)) {
  4836.             // no change
  4837.             if ((!empty($post->subscribe) && $subscribed)
  4838.                 || (empty($post->subscribe) && !$subscribed)) {
  4839.                 return "";
  4840.  
  4841.             } elseif (!empty($post->subscribe) && !$subscribed) {
  4842.                 $action = 'subscribe';
  4843.  
  4844.             } elseif (empty($post->subscribe) && $subscribed) {
  4845.                 $action = 'unsubscribe';
  4846.             }
  4847.         }
  4848.     }
  4849.  
  4850.     $info = new stdClass();
  4851.     $info->name  = fullname($USER);
  4852.     $info->forum = format_string($forum->name);
  4853.  
  4854.     switch ($action) {
  4855.         case 'subscribe':
  4856.             forum_subscribe($USER->id, $post->forum);
  4857.             return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
  4858.         case 'unsubscribe':
  4859.             forum_unsubscribe($USER->id, $post->forum);
  4860.             return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
  4861.     }
  4862. }
  4863.  
  4864. /**
  4865.  * Generate and return the subscribe or unsubscribe link for a forum.
  4866.  *
  4867.  * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
  4868.  * @param object $context the context object for this forum.
  4869.  * @param array $messages text used for the link in its various states
  4870.  *      (subscribed, unsubscribed, forcesubscribed or cantsubscribe).
  4871.  *      Any strings not passed in are taken from the $defaultmessages array
  4872.  *      at the top of the function.
  4873.  * @param bool $cantaccessagroup
  4874.  * @param bool $fakelink
  4875.  * @param bool $backtoindex
  4876.  * @param array $subscribed_forums
  4877.  * @return string
  4878.  */
  4879. function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false, $fakelink=true, $backtoindex=false, $subscribed_forums=null) {
  4880.     global $CFG, $USER, $PAGE, $OUTPUT;
  4881.     $defaultmessages = array(
  4882.         'subscribed' => get_string('unsubscribe', 'forum'),
  4883.         'unsubscribed' => get_string('subscribe', 'forum'),
  4884.         'cantaccessgroup' => get_string('no'),
  4885.         'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
  4886.         'cantsubscribe' => get_string('disallowsubscribe','forum')
  4887.     );
  4888.     $messages = $messages + $defaultmessages;
  4889.  
  4890.     if (forum_is_forcesubscribed($forum)) {
  4891.         return $messages['forcesubscribed'];
  4892.     } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) {
  4893.         return $messages['cantsubscribe'];
  4894.     } else if ($cantaccessagroup) {
  4895.         return $messages['cantaccessgroup'];
  4896.     } else {
  4897.         if (!is_enrolled($context, $USER, '', true)) {
  4898.             return '';
  4899.         }
  4900.         if (is_null($subscribed_forums)) {
  4901.             $subscribed = forum_is_subscribed($USER->id, $forum);
  4902.         } else {
  4903.             $subscribed = !empty($subscribed_forums[$forum->id]);
  4904.         }
  4905.         if ($subscribed) {
  4906.             $linktext = $messages['subscribed'];
  4907.             $linktitle = get_string('subscribestop', 'forum');
  4908.         } else {
  4909.             $linktext = $messages['unsubscribed'];
  4910.             $linktitle = get_string('subscribestart', 'forum');
  4911.         }
  4912.  
  4913.         $options = array();
  4914.         if ($backtoindex) {
  4915.             $backtoindexlink = '&amp;backtoindex=1';
  4916.             $options['backtoindex'] = 1;
  4917.         } else {
  4918.             $backtoindexlink = '';
  4919.         }
  4920.         $link = '';
  4921.  
  4922.         if ($fakelink) {
  4923.             $PAGE->requires->js('/mod/forum/forum.js');
  4924.             $PAGE->requires->js_function_call('forum_produce_subscribe_link', array($forum->id, $backtoindexlink, $linktext, $linktitle));
  4925.             $link = "<noscript>";
  4926.         }
  4927.         $options['id'] = $forum->id;
  4928.         $options['sesskey'] = sesskey();
  4929.         $url = new moodle_url('/mod/forum/subscribe.php', $options);
  4930.         $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle));
  4931.         if ($fakelink) {
  4932.             $link .= '</noscript>';
  4933.         }
  4934.  
  4935.         return $link;
  4936.     }
  4937. }
  4938.  
  4939.  
  4940. /**
  4941.  * Generate and return the track or no track link for a forum.
  4942.  *
  4943.  * @global object
  4944.  * @global object
  4945.  * @global object
  4946.  * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
  4947.  * @param array $messages
  4948.  * @param bool $fakelink
  4949.  * @return string
  4950.  */
  4951. function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) {
  4952.     global $CFG, $USER, $PAGE, $OUTPUT;
  4953.  
  4954.     static $strnotrackforum, $strtrackforum;
  4955.  
  4956.     if (isset($messages['trackforum'])) {
  4957.          $strtrackforum = $messages['trackforum'];
  4958.     }
  4959.     if (isset($messages['notrackforum'])) {
  4960.          $strnotrackforum = $messages['notrackforum'];
  4961.     }
  4962.     if (empty($strtrackforum)) {
  4963.         $strtrackforum = get_string('trackforum', 'forum');
  4964.     }
  4965.     if (empty($strnotrackforum)) {
  4966.         $strnotrackforum = get_string('notrackforum', 'forum');
  4967.     }
  4968.  
  4969.     if (forum_tp_is_tracked($forum)) {
  4970.         $linktitle = $strnotrackforum;
  4971.         $linktext = $strnotrackforum;
  4972.     } else {
  4973.         $linktitle = $strtrackforum;
  4974.         $linktext = $strtrackforum;
  4975.     }
  4976.  
  4977.     $link = '';
  4978.     if ($fakelink) {
  4979.         $PAGE->requires->js('/mod/forum/forum.js');
  4980.         $PAGE->requires->js_function_call('forum_produce_tracking_link', Array($forum->id, $linktext, $linktitle));
  4981.         // use <noscript> to print button in case javascript is not enabled
  4982.         $link .= '<noscript>';
  4983.     }
  4984.     $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forum->id));
  4985.     $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle));
  4986.  
  4987.     if ($fakelink) {
  4988.         $link .= '</noscript>';
  4989.     }
  4990.  
  4991.     return $link;
  4992. }
  4993.  
  4994.  
  4995.  
  4996. /**
  4997.  * Returns true if user created new discussion already
  4998.  *
  4999.  * @global object
  5000.  * @global object
  5001.  * @param int $forumid
  5002.  * @param int $userid
  5003.  * @return bool
  5004.  */
  5005. function forum_user_has_posted_discussion($forumid, $userid) {
  5006.     global $CFG, $DB;
  5007.  
  5008.     $sql = "SELECT 'x'
  5009.              FROM {forum_discussions} d, {forum_posts} p
  5010.             WHERE d.forum = ? AND p.discussion = d.id AND p.parent = 0 and p.userid = ?";
  5011.  
  5012.     return $DB->record_exists_sql($sql, array($forumid, $userid));
  5013. }
  5014.  
  5015. /**
  5016.  * @global object
  5017.  * @global object
  5018.  * @param int $forumid
  5019.  * @param int $userid
  5020.  * @return array
  5021.  */
  5022. function forum_discussions_user_has_posted_in($forumid, $userid) {
  5023.     global $CFG, $DB;
  5024.  
  5025.     $haspostedsql = "SELECT d.id AS id,
  5026.                            d.*
  5027.                       FROM {forum_posts} p,
  5028.                            {forum_discussions} d
  5029.                      WHERE p.discussion = d.id
  5030.                        AND d.forum = ?
  5031.                        AND p.userid = ?";
  5032.  
  5033.     return $DB->get_records_sql($haspostedsql, array($forumid, $userid));
  5034. }
  5035.  
  5036. /**
  5037.  * @global object
  5038.  * @global object
  5039.  * @param int $forumid
  5040.  * @param int $did
  5041.  * @param int $userid
  5042.  * @return bool
  5043.  */
  5044. function forum_user_has_posted($forumid, $did, $userid) {
  5045.     global $DB;
  5046.  
  5047.     if (empty($did)) {
  5048.         // posted in any forum discussion?
  5049.         $sql = "SELECT 'x'
  5050.                  FROM {forum_posts} p
  5051.                  JOIN {forum_discussions} d ON d.id = p.discussion
  5052.                 WHERE p.userid = :userid AND d.forum = :forumid";
  5053.         return $DB->record_exists_sql($sql, array('forumid'=>$forumid,'userid'=>$userid));
  5054.     } else {
  5055.         return $DB->record_exists('forum_posts', array('discussion'=>$did,'userid'=>$userid));
  5056.     }
  5057. }
  5058.  
  5059. /**
  5060.  * Returns creation time of the first user's post in given discussion
  5061.  * @global object $DB
  5062.  * @param int $did Discussion id
  5063.  * @param int $userid User id
  5064.  * @return int|bool post creation time stamp or return false
  5065.  */
  5066. function forum_get_user_posted_time($did, $userid) {
  5067.     global $DB;
  5068.  
  5069.     $posttime = $DB->get_field('forum_posts', 'MIN(created)', array('userid'=>$userid, 'discussion'=>$did));
  5070.     if (empty($posttime)) {
  5071.         return false;
  5072.     }
  5073.     return $posttime;
  5074. }
  5075.  
  5076. /**
  5077.  * @global object
  5078.  * @param object $forum
  5079.  * @param object $currentgroup
  5080.  * @param int $unused
  5081.  * @param object $cm
  5082.  * @param object $context
  5083.  * @return bool
  5084.  */
  5085. function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1, $cm=NULL, $context=NULL) {
  5086. // $forum is an object
  5087.     global $USER;
  5088.  
  5089.     // shortcut - guest and not-logged-in users can not post
  5090.     if (isguestuser() or !isloggedin()) {
  5091.         return false;
  5092.     }
  5093.  
  5094.     if (!$cm) {
  5095.         debugging('missing cm', DEBUG_DEVELOPER);
  5096.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5097.             print_error('invalidcoursemodule');
  5098.         }
  5099.     }
  5100.  
  5101.     if (!$context) {
  5102.         $context = context_module::instance($cm->id);
  5103.     }
  5104.  
  5105.     if ($currentgroup === null) {
  5106.         $currentgroup = groups_get_activity_group($cm);
  5107.     }
  5108.  
  5109.     $groupmode = groups_get_activity_groupmode($cm);
  5110.  
  5111.     if ($forum->type == 'news') {
  5112.         $capname = 'mod/forum:addnews';
  5113.     } else if ($forum->type == 'qanda') {
  5114.         $capname = 'mod/forum:addquestion';
  5115.     } else {
  5116.         $capname = 'mod/forum:startdiscussion';
  5117.     }
  5118.  
  5119.     if (!has_capability($capname, $context)) {
  5120.         return false;
  5121.     }
  5122.  
  5123.     if ($forum->type == 'single') {
  5124.         return false;
  5125.     }
  5126.  
  5127.     if ($forum->type == 'eachuser') {
  5128.         if (forum_user_has_posted_discussion($forum->id, $USER->id)) {
  5129.             return false;
  5130.         }
  5131.     }
  5132.  
  5133.     if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
  5134.         return true;
  5135.     }
  5136.  
  5137.     if ($currentgroup) {
  5138.         return groups_is_member($currentgroup);
  5139.     } else {
  5140.         // no group membership and no accessallgroups means no new discussions
  5141.         // reverted to 1.7 behaviour in 1.9+,  buggy in 1.8.0-1.9.0
  5142.         return false;
  5143.     }
  5144. }
  5145.  
  5146. /**
  5147.  * This function checks whether the user can reply to posts in a forum
  5148.  * discussion. Use forum_user_can_post_discussion() to check whether the user
  5149.  * can start discussions.
  5150.  *
  5151.  * @global object
  5152.  * @global object
  5153.  * @uses DEBUG_DEVELOPER
  5154.  * @uses CONTEXT_MODULE
  5155.  * @uses VISIBLEGROUPS
  5156.  * @param object $forum forum object
  5157.  * @param object $discussion
  5158.  * @param object $user
  5159.  * @param object $cm
  5160.  * @param object $course
  5161.  * @param object $context
  5162.  * @return bool
  5163.  */
  5164. function forum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $course=NULL, $context=NULL) {
  5165.     global $USER, $DB;
  5166.     if (empty($user)) {
  5167.         $user = $USER;
  5168.     }
  5169.  
  5170.     // shortcut - guest and not-logged-in users can not post
  5171.     if (isguestuser($user) or empty($user->id)) {
  5172.         return false;
  5173.     }
  5174.  
  5175.     if (!isset($discussion->groupid)) {
  5176.         debugging('incorrect discussion parameter', DEBUG_DEVELOPER);
  5177.         return false;
  5178.     }
  5179.  
  5180.     if (!$cm) {
  5181.         debugging('missing cm', DEBUG_DEVELOPER);
  5182.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5183.             print_error('invalidcoursemodule');
  5184.         }
  5185.     }
  5186.  
  5187.     if (!$course) {
  5188.         debugging('missing course', DEBUG_DEVELOPER);
  5189.         if (!$course = $DB->get_record('course', array('id' => $forum->course))) {
  5190.             print_error('invalidcourseid');
  5191.         }
  5192.     }
  5193.  
  5194.     if (!$context) {
  5195.         $context = context_module::instance($cm->id);
  5196.     }
  5197.  
  5198.     // normal users with temporary guest access can not post, suspended users can not post either
  5199.     if (!is_viewing($context, $user->id) and !is_enrolled($context, $user->id, '', true)) {
  5200.         return false;
  5201.     }
  5202.  
  5203.     if ($forum->type == 'news') {
  5204.         $capname = 'mod/forum:replynews';
  5205.     } else {
  5206.         $capname = 'mod/forum:replypost';
  5207.     }
  5208.  
  5209.     if (!has_capability($capname, $context, $user->id)) {
  5210.         return false;
  5211.     }
  5212.  
  5213.     if (!$groupmode = groups_get_activity_groupmode($cm, $course)) {
  5214.         return true;
  5215.     }
  5216.  
  5217.     if (has_capability('moodle/site:accessallgroups', $context)) {
  5218.         return true;
  5219.     }
  5220.  
  5221.     if ($groupmode == VISIBLEGROUPS) {
  5222.         if ($discussion->groupid == -1) {
  5223.             // allow students to reply to all participants discussions - this was not possible in Moodle <1.8
  5224.             return true;
  5225.         }
  5226.         return groups_is_member($discussion->groupid);
  5227.  
  5228.     } else {
  5229.         //separate groups
  5230.         if ($discussion->groupid == -1) {
  5231.             return false;
  5232.         }
  5233.         return groups_is_member($discussion->groupid);
  5234.     }
  5235. }
  5236.  
  5237. /**
  5238.  * Checks to see if a user can view a particular post.
  5239.  *
  5240.  * @deprecated since Moodle 2.4 use forum_user_can_see_post() instead
  5241.  *
  5242.  * @param object $post
  5243.  * @param object $course
  5244.  * @param object $cm
  5245.  * @param object $forum
  5246.  * @param object $discussion
  5247.  * @param object $user
  5248.  * @return boolean
  5249.  */
  5250. function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=null){
  5251.     debugging('forum_user_can_view_post() is deprecated. Please use forum_user_can_see_post() instead.', DEBUG_DEVELOPER);
  5252.     return forum_user_can_see_post($forum, $discussion, $post, $user, $cm);
  5253. }
  5254.  
  5255. /**
  5256. * Check to ensure a user can view a timed discussion.
  5257. *
  5258. * @param object $discussion
  5259. * @param object $user
  5260. * @param object $context
  5261. * @return boolean returns true if they can view post, false otherwise
  5262. */
  5263. function forum_user_can_see_timed_discussion($discussion, $user, $context) {
  5264.     global $CFG;
  5265.  
  5266.     // Check that the user can view a discussion that is normally hidden due to access times.
  5267.     if (!empty($CFG->forum_enabletimedposts)) {
  5268.         $time = time();
  5269.         if (($discussion->timestart != 0 && $discussion->timestart > $time)
  5270.             || ($discussion->timeend != 0 && $discussion->timeend < $time)) {
  5271.             if (!has_capability('mod/forum:viewhiddentimedposts', $context, $user->id)) {
  5272.                 return false;
  5273.             }
  5274.         }
  5275.     }
  5276.  
  5277.     return true;
  5278. }
  5279.  
  5280. /**
  5281. * Check to ensure a user can view a group discussion.
  5282. *
  5283. * @param object $discussion
  5284. * @param object $cm
  5285. * @param object $context
  5286. * @return boolean returns true if they can view post, false otherwise
  5287. */
  5288. function forum_user_can_see_group_discussion($discussion, $cm, $context) {
  5289.  
  5290.     // If it's a grouped discussion, make sure the user is a member.
  5291.     if ($discussion->groupid > 0) {
  5292.         $groupmode = groups_get_activity_groupmode($cm);
  5293.         if ($groupmode == SEPARATEGROUPS) {
  5294.             return groups_is_member($discussion->groupid) || has_capability('moodle/site:accessallgroups', $context);
  5295.         }
  5296.     }
  5297.  
  5298.     return true;
  5299. }
  5300.  
  5301. /**
  5302.  * @global object
  5303.  * @global object
  5304.  * @uses DEBUG_DEVELOPER
  5305.  * @param object $forum
  5306.  * @param object $discussion
  5307.  * @param object $context
  5308.  * @param object $user
  5309.  * @return bool
  5310.  */
  5311. function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
  5312.     global $USER, $DB;
  5313.  
  5314.     if (empty($user) || empty($user->id)) {
  5315.         $user = $USER;
  5316.     }
  5317.  
  5318.     // retrieve objects (yuk)
  5319.     if (is_numeric($forum)) {
  5320.         debugging('missing full forum', DEBUG_DEVELOPER);
  5321.         if (!$forum = $DB->get_record('forum',array('id'=>$forum))) {
  5322.             return false;
  5323.         }
  5324.     }
  5325.     if (is_numeric($discussion)) {
  5326.         debugging('missing full discussion', DEBUG_DEVELOPER);
  5327.         if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) {
  5328.             return false;
  5329.         }
  5330.     }
  5331.     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5332.         print_error('invalidcoursemodule');
  5333.     }
  5334.  
  5335.     if (!has_capability('mod/forum:viewdiscussion', $context)) {
  5336.         return false;
  5337.     }
  5338.  
  5339.     if (!forum_user_can_see_timed_discussion($discussion, $user, $context)) {
  5340.         return false;
  5341.     }
  5342.  
  5343.     if (!forum_user_can_see_group_discussion($discussion, $cm, $context)) {
  5344.         return false;
  5345.     }
  5346.  
  5347.     if ($forum->type == 'qanda' &&
  5348.             !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
  5349.             !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  5350.         return false;
  5351.     }
  5352.     return true;
  5353. }
  5354.  
  5355. /**
  5356.  * @global object
  5357.  * @global object
  5358.  * @param object $forum
  5359.  * @param object $discussion
  5360.  * @param object $post
  5361.  * @param object $user
  5362.  * @param object $cm
  5363.  * @return bool
  5364.  */
  5365. function forum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm=NULL) {
  5366.     global $CFG, $USER, $DB;
  5367.  
  5368.     // Context used throughout function.
  5369.     $modcontext = context_module::instance($cm->id);
  5370.  
  5371.     // retrieve objects (yuk)
  5372.     if (is_numeric($forum)) {
  5373.         debugging('missing full forum', DEBUG_DEVELOPER);
  5374.         if (!$forum = $DB->get_record('forum',array('id'=>$forum))) {
  5375.             return false;
  5376.         }
  5377.     }
  5378.  
  5379.     if (is_numeric($discussion)) {
  5380.         debugging('missing full discussion', DEBUG_DEVELOPER);
  5381.         if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) {
  5382.             return false;
  5383.         }
  5384.     }
  5385.     if (is_numeric($post)) {
  5386.         debugging('missing full post', DEBUG_DEVELOPER);
  5387.         if (!$post = $DB->get_record('forum_posts',array('id'=>$post))) {
  5388.             return false;
  5389.         }
  5390.     }
  5391.  
  5392.     if (!isset($post->id) && isset($post->parent)) {
  5393.         $post->id = $post->parent;
  5394.     }
  5395.  
  5396.     if (!$cm) {
  5397.         debugging('missing cm', DEBUG_DEVELOPER);
  5398.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5399.             print_error('invalidcoursemodule');
  5400.         }
  5401.     }
  5402.  
  5403.     if (empty($user) || empty($user->id)) {
  5404.         $user = $USER;
  5405.     }
  5406.  
  5407.     $canviewdiscussion = !empty($cm->cache->caps['mod/forum:viewdiscussion']) || has_capability('mod/forum:viewdiscussion', $modcontext, $user->id);
  5408.     if (!$canviewdiscussion && !has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), context_user::instance($post->userid))) {
  5409.         return false;
  5410.     }
  5411.  
  5412.     if (isset($cm->uservisible)) {
  5413.         if (!$cm->uservisible) {
  5414.             return false;
  5415.         }
  5416.     } else {
  5417.         if (!coursemodule_visible_for_user($cm, $user->id)) {
  5418.             return false;
  5419.         }
  5420.     }
  5421.  
  5422.     if (!forum_user_can_see_timed_discussion($discussion, $user, $modcontext)) {
  5423.         return false;
  5424.     }
  5425.  
  5426.     if (!forum_user_can_see_group_discussion($discussion, $cm, $modcontext)) {
  5427.         return false;
  5428.     }
  5429.  
  5430.     if ($forum->type == 'qanda') {
  5431.         $firstpost = forum_get_firstpost_from_discussion($discussion->id);
  5432.         $userfirstpost = forum_get_user_posted_time($discussion->id, $user->id);
  5433.  
  5434.         return (($userfirstpost !== false && (time() - $userfirstpost >= $CFG->maxeditingtime)) ||
  5435.                 $firstpost->id == $post->id || $post->userid == $user->id || $firstpost->userid == $user->id ||
  5436.                 has_capability('mod/forum:viewqandawithoutposting', $modcontext, $user->id));
  5437.     }
  5438.     return true;
  5439. }
  5440.  
  5441.  
  5442. /**
  5443.  * Prints the discussion view screen for a forum.
  5444.  *
  5445.  * @global object
  5446.  * @global object
  5447.  * @param object $course The current course object.
  5448.  * @param object $forum Forum to be printed.
  5449.  * @param int $maxdiscussions .
  5450.  * @param string $displayformat The display format to use (optional).
  5451.  * @param string $sort Sort arguments for database query (optional).
  5452.  * @param int $groupmode Group mode of the forum (optional).
  5453.  * @param void $unused (originally current group)
  5454.  * @param int $page Page mode, page to display (optional).
  5455.  * @param int $perpage The maximum number of discussions per page(optional)
  5456.  *
  5457.  */
  5458. function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $displayformat='plain', $sort='',
  5459.                                         $currentgroup=-1, $groupmode=-1, $page=-1, $perpage=100, $cm=NULL) {
  5460.     global $CFG, $USER, $OUTPUT;
  5461.  
  5462.     if (!$cm) {
  5463.         if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5464.             print_error('invalidcoursemodule');
  5465.         }
  5466.     }
  5467.     $context = context_module::instance($cm->id);
  5468.  
  5469.     if (empty($sort)) {
  5470.         $sort = "d.timemodified DESC";
  5471.     }
  5472.  
  5473.     $olddiscussionlink = false;
  5474.  
  5475.  // Sort out some defaults
  5476.     if ($perpage <= 0) {
  5477.         $perpage = 0;
  5478.         $page    = -1;
  5479.     }
  5480.  
  5481.     if ($maxdiscussions == 0) {
  5482.         // all discussions - backwards compatibility
  5483.         $page    = -1;
  5484.         $perpage = 0;
  5485.         if ($displayformat == 'plain') {
  5486.             $displayformat = 'header';  // Abbreviate display by default
  5487.         }
  5488.  
  5489.     } else if ($maxdiscussions > 0) {
  5490.         $page    = -1;
  5491.         $perpage = $maxdiscussions;
  5492.     }
  5493.  
  5494.     $fullpost = false;
  5495.     if ($displayformat == 'plain') {
  5496.         $fullpost = true;
  5497.     }
  5498.  
  5499.  
  5500. // Decide if current user is allowed to see ALL the current discussions or not
  5501.  
  5502. // First check the group stuff
  5503.     if ($currentgroup == -1 or $groupmode == -1) {
  5504.         $groupmode    = groups_get_activity_groupmode($cm, $course);
  5505.         $currentgroup = groups_get_activity_group($cm);
  5506.     }
  5507.  
  5508.     $groups = array(); //cache
  5509.  
  5510. // If the user can post discussions, then this is a good place to put the
  5511. // button for it. We do not show the button if we are showing site news
  5512. // and the current user is a guest.
  5513.  
  5514.     $canstart = forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context);
  5515.     if (!$canstart and $forum->type !== 'news') {
  5516.         if (isguestuser() or !isloggedin()) {
  5517.             $canstart = true;
  5518.         }
  5519.         if (!is_enrolled($context) and !is_viewing($context)) {
  5520.             // allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link
  5521.             // normal users with temporary guest access see this button too, they are asked to enrol instead
  5522.             // do not show the button to users with suspended enrolments here
  5523.             $canstart = enrol_selfenrol_available($course->id);
  5524.         }
  5525.     }
  5526.  
  5527.     if ($canstart) {
  5528.         echo '<div class="singlebutton forumaddnew">';
  5529.         echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
  5530.         echo '<div>';
  5531.         echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
  5532.         switch ($forum->type) {
  5533.             case 'news':
  5534.             case 'blog':
  5535.                 $buttonadd = get_string('addanewtopic', 'forum');
  5536.                 break;
  5537.             case 'qanda':
  5538.                 $buttonadd = get_string('addanewquestion', 'forum');
  5539.                 break;
  5540.             default:
  5541.                 $buttonadd = get_string('addanewdiscussion', 'forum');
  5542.                 break;
  5543.         }
  5544.         echo '<input type="submit" value="'.$buttonadd.'" />';
  5545.         echo '</div>';
  5546.         echo '</form>';
  5547.         echo "</div>\n";
  5548.  
  5549.     } else if (isguestuser() or !isloggedin() or $forum->type == 'news') {
  5550.         // no button and no info
  5551.  
  5552.     } else if ($groupmode and has_capability('mod/forum:startdiscussion', $context)) {
  5553.         // inform users why they can not post new discussion
  5554.         if ($currentgroup) {
  5555.             echo $OUTPUT->notification(get_string('cannotadddiscussion', 'forum'));
  5556.         } else {
  5557.             echo $OUTPUT->notification(get_string('cannotadddiscussionall', 'forum'));
  5558.         }
  5559.     }
  5560.  
  5561. // Get all the recent discussions we're allowed to see
  5562.  
  5563.     $getuserlastmodified = ($displayformat == 'header');
  5564.  
  5565.     if (! $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage) ) {
  5566.         echo '<div class="forumnodiscuss">';
  5567.         if ($forum->type == 'news') {
  5568.             echo '('.get_string('nonews', 'forum').')';
  5569.         } else if ($forum->type == 'qanda') {
  5570.             echo '('.get_string('noquestions','forum').')';
  5571.         } else {
  5572.             echo '('.get_string('nodiscussions', 'forum').')';
  5573.         }
  5574.         echo "</div>\n";
  5575.         return;
  5576.     }
  5577.  
  5578. // If we want paging
  5579.     if ($page != -1) {
  5580.         ///Get the number of discussions found
  5581.         $numdiscussions = forum_get_discussions_count($cm);
  5582.  
  5583.         ///Show the paging bar
  5584.         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
  5585.         if ($numdiscussions > 1000) {
  5586.             // saves some memory on sites with very large forums
  5587.             $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage);
  5588.         } else {
  5589.             $replies = forum_count_discussion_replies($forum->id);
  5590.         }
  5591.  
  5592.     } else {
  5593.         $replies = forum_count_discussion_replies($forum->id);
  5594.  
  5595.         if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) {
  5596.             $olddiscussionlink = true;
  5597.         }
  5598.     }
  5599.  
  5600.     $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
  5601.  
  5602.     $strdatestring = get_string('strftimerecentfull');
  5603.  
  5604.     // Check if the forum is tracked.
  5605.     if ($cantrack = forum_tp_can_track_forums($forum)) {
  5606.         $forumtracked = forum_tp_is_tracked($forum);
  5607.     } else {
  5608.         $forumtracked = false;
  5609.     }
  5610.  
  5611.     if ($forumtracked) {
  5612.         $unreads = forum_get_discussions_unread($cm);
  5613.     } else {
  5614.         $unreads = array();
  5615.     }
  5616.  
  5617.     if ($displayformat == 'header') {
  5618.         echo '<table cellspacing="0" class="forumheaderlist">';
  5619.         echo '<thead>';
  5620.         echo '<tr>';
  5621.         echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
  5622.         echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
  5623.         if ($groupmode > 0) {
  5624.             echo '<th class="header group" scope="col">'.get_string('group').'</th>';
  5625.         }
  5626.         if (has_capability('mod/forum:viewdiscussion', $context)) {
  5627.             echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
  5628.             // If the forum can be tracked, display the unread column.
  5629.             if ($cantrack) {
  5630.                 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
  5631.                 if ($forumtracked) {
  5632.                     echo '<a title="'.get_string('markallread', 'forum').
  5633.                          '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
  5634.                          $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
  5635.                          '<img src="'.$OUTPUT->pix_url('t/markasread') . '" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
  5636.                 }
  5637.                 echo '</th>';
  5638.             }
  5639.         }
  5640.         echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
  5641.         echo '</tr>';
  5642.         echo '</thead>';
  5643.         echo '<tbody>';
  5644.     }
  5645.  
  5646.     foreach ($discussions as $discussion) {
  5647.         if (!empty($replies[$discussion->discussion])) {
  5648.             $discussion->replies = $replies[$discussion->discussion]->replies;
  5649.             $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
  5650.         } else {
  5651.             $discussion->replies = 0;
  5652.         }
  5653.  
  5654.         // SPECIAL CASE: The front page can display a news item post to non-logged in users.
  5655.         // All posts are read in this case.
  5656.         if (!$forumtracked) {
  5657.             $discussion->unread = '-';
  5658.         } else if (empty($USER)) {
  5659.             $discussion->unread = 0;
  5660.         } else {
  5661.             if (empty($unreads[$discussion->discussion])) {
  5662.                 $discussion->unread = 0;
  5663.             } else {
  5664.                 $discussion->unread = $unreads[$discussion->discussion];
  5665.             }
  5666.         }
  5667.  
  5668.         if (isloggedin()) {
  5669.             $ownpost = ($discussion->userid == $USER->id);
  5670.         } else {
  5671.             $ownpost=false;
  5672.         }
  5673.         // Use discussion name instead of subject of first post
  5674.         $discussion->subject = $discussion->name;
  5675.  
  5676.         switch ($displayformat) {
  5677.             case 'header':
  5678.                 if ($groupmode > 0) {
  5679.                     if (isset($groups[$discussion->groupid])) {
  5680.                         $group = $groups[$discussion->groupid];
  5681.                     } else {
  5682.                         $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid);
  5683.                     }
  5684.                 } else {
  5685.                     $group = -1;
  5686.                 }
  5687.                 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
  5688.                     $canviewparticipants, $context);
  5689.             break;
  5690.             default:
  5691.                 $link = false;
  5692.  
  5693.                 if ($discussion->replies) {
  5694.                     $link = true;
  5695.                 } else {
  5696.                     $modcontext = context_module::instance($cm->id);
  5697.                     $link = forum_user_can_see_discussion($forum, $discussion, $modcontext, $USER);
  5698.                 }
  5699.  
  5700.                 $discussion->forum = $forum->id;
  5701.  
  5702.                 forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false,
  5703.                         '', null, true, $forumtracked);
  5704.             break;
  5705.         }
  5706.     }
  5707.  
  5708.     if ($displayformat == "header") {
  5709.         echo '</tbody>';
  5710.         echo '</table>';
  5711.     }
  5712.  
  5713.     if ($olddiscussionlink) {
  5714.         if ($forum->type == 'news') {
  5715.             $strolder = get_string('oldertopics', 'forum');
  5716.         } else {
  5717.             $strolder = get_string('olderdiscussions', 'forum');
  5718.         }
  5719.         echo '<div class="forumolddiscuss">';
  5720.         echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
  5721.         echo $strolder.'</a> ...</div>';
  5722.     }
  5723.  
  5724.     if ($page != -1) { ///Show the paging bar
  5725.         echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id");
  5726.     }
  5727. }
  5728.  
  5729.  
  5730. /**
  5731.  * Prints a forum discussion
  5732.  *
  5733.  * @uses CONTEXT_MODULE
  5734.  * @uses FORUM_MODE_FLATNEWEST
  5735.  * @uses FORUM_MODE_FLATOLDEST
  5736.  * @uses FORUM_MODE_THREADED
  5737.  * @uses FORUM_MODE_NESTED
  5738.  * @param stdClass $course
  5739.  * @param stdClass $cm
  5740.  * @param stdClass $forum
  5741.  * @param stdClass $discussion
  5742.  * @param stdClass $post
  5743.  * @param int $mode
  5744.  * @param mixed $canreply
  5745.  * @param bool $canrate
  5746.  */
  5747. function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
  5748.     global $USER, $CFG;
  5749.  
  5750.     require_once($CFG->dirroot.'/rating/lib.php');
  5751.  
  5752.     $ownpost = (isloggedin() && $USER->id == $post->userid);
  5753.  
  5754.     $modcontext = context_module::instance($cm->id);
  5755.     if ($canreply === NULL) {
  5756.         $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
  5757.     } else {
  5758.         $reply = $canreply;
  5759.     }
  5760.  
  5761.     // $cm holds general cache for forum functions
  5762.     $cm->cache = new stdClass;
  5763.     $cm->cache->groups      = groups_get_all_groups($course->id, 0, $cm->groupingid);
  5764.     $cm->cache->usersgroups = array();
  5765.  
  5766.     $posters = array();
  5767.  
  5768.     // preload all posts - TODO: improve...
  5769.     if ($mode == FORUM_MODE_FLATNEWEST) {
  5770.         $sort = "p.created DESC";
  5771.     } else {
  5772.         $sort = "p.created ASC";
  5773.     }
  5774.  
  5775.     $forumtracked = forum_tp_is_tracked($forum);
  5776.     $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
  5777.     $post = $posts[$post->id];
  5778.  
  5779.     foreach ($posts as $pid=>$p) {
  5780.         $posters[$p->userid] = $p->userid;
  5781.     }
  5782.  
  5783.     // preload all groups of ppl that posted in this discussion
  5784.     if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) {
  5785.         foreach($postersgroups as $pg) {
  5786.             if (!isset($cm->cache->usersgroups[$pg->userid])) {
  5787.                 $cm->cache->usersgroups[$pg->userid] = array();
  5788.             }
  5789.             $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid;
  5790.         }
  5791.         unset($postersgroups);
  5792.     }
  5793.  
  5794.     //load ratings
  5795.     if ($forum->assessed != RATING_AGGREGATE_NONE) {
  5796.         $ratingoptions = new stdClass;
  5797.         $ratingoptions->context = $modcontext;
  5798.         $ratingoptions->component = 'mod_forum';
  5799.         $ratingoptions->ratingarea = 'post';
  5800.         $ratingoptions->items = $posts;
  5801.         $ratingoptions->aggregate = $forum->assessed;//the aggregation method
  5802.         $ratingoptions->scaleid = $forum->scale;
  5803.         $ratingoptions->userid = $USER->id;
  5804.         if ($forum->type == 'single' or !$discussion->id) {
  5805.             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id";
  5806.         } else {
  5807.             $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
  5808.         }
  5809.         $ratingoptions->assesstimestart = $forum->assesstimestart;
  5810.         $ratingoptions->assesstimefinish = $forum->assesstimefinish;
  5811.  
  5812.         $rm = new rating_manager();
  5813.         $posts = $rm->get_ratings($ratingoptions);
  5814.     }
  5815.  
  5816.  
  5817.     $post->forum = $forum->id;   // Add the forum id to the post object, later used by forum_print_post
  5818.     $post->forumtype = $forum->type;
  5819.  
  5820.     $post->subject = format_string($post->subject);
  5821.  
  5822.     $postread = !empty($post->postread);
  5823.  
  5824.     forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false,
  5825.                          '', '', $postread, true, $forumtracked);
  5826.  
  5827.     switch ($mode) {
  5828.         case FORUM_MODE_FLATOLDEST :
  5829.         case FORUM_MODE_FLATNEWEST :
  5830.         default:
  5831.             forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts);
  5832.             break;
  5833.  
  5834.         case FORUM_MODE_THREADED :
  5835.             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts);
  5836.             break;
  5837.  
  5838.         case FORUM_MODE_NESTED :
  5839.             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
  5840.             break;
  5841.     }
  5842. }
  5843.  
  5844.  
  5845. /**
  5846.  * @global object
  5847.  * @global object
  5848.  * @uses FORUM_MODE_FLATNEWEST
  5849.  * @param object $course
  5850.  * @param object $cm
  5851.  * @param object $forum
  5852.  * @param object $discussion
  5853.  * @param object $post
  5854.  * @param object $mode
  5855.  * @param bool $reply
  5856.  * @param bool $forumtracked
  5857.  * @param array $posts
  5858.  * @return void
  5859.  */
  5860. function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) {
  5861.     global $USER, $CFG;
  5862.  
  5863.     $link  = false;
  5864.  
  5865.     if ($mode == FORUM_MODE_FLATNEWEST) {
  5866.         $sort = "ORDER BY created DESC";
  5867.     } else {
  5868.         $sort = "ORDER BY created ASC";
  5869.     }
  5870.  
  5871.     foreach ($posts as $post) {
  5872.         if (!$post->parent) {
  5873.             continue;
  5874.         }
  5875.         $post->subject = format_string($post->subject);
  5876.         $ownpost = ($USER->id == $post->userid);
  5877.  
  5878.         $postread = !empty($post->postread);
  5879.  
  5880.         forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
  5881.                              '', '', $postread, true, $forumtracked);
  5882.     }
  5883. }
  5884.  
  5885. /**
  5886.  * @todo Document this function
  5887.  *
  5888.  * @global object
  5889.  * @global object
  5890.  * @uses CONTEXT_MODULE
  5891.  * @return void
  5892.  */
  5893. function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) {
  5894.     global $USER, $CFG;
  5895.  
  5896.     $link  = false;
  5897.  
  5898.     if (!empty($posts[$parent->id]->children)) {
  5899.         $posts = $posts[$parent->id]->children;
  5900.  
  5901.         $modcontext       = context_module::instance($cm->id);
  5902.         $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
  5903.  
  5904.         foreach ($posts as $post) {
  5905.  
  5906.             echo '<div class="indent">';
  5907.             if ($depth > 0) {
  5908.                 $ownpost = ($USER->id == $post->userid);
  5909.                 $post->subject = format_string($post->subject);
  5910.  
  5911.                 $postread = !empty($post->postread);
  5912.  
  5913.                 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
  5914.                                      '', '', $postread, true, $forumtracked);
  5915.             } else {
  5916.                 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  5917.                     echo "</div>\n";
  5918.                     continue;
  5919.                 }
  5920.                 $by = new stdClass();
  5921.                 $by->name = fullname($post, $canviewfullnames);
  5922.                 $by->date = userdate($post->modified);
  5923.  
  5924.                 if ($forumtracked) {
  5925.                     if (!empty($post->postread)) {
  5926.                         $style = '<span class="forumthread read">';
  5927.                     } else {
  5928.                         $style = '<span class="forumthread unread">';
  5929.                     }
  5930.                 } else {
  5931.                     $style = '<span class="forumthread">';
  5932.                 }
  5933.                 echo $style."<a name=\"$post->id\"></a>".
  5934.                      "<a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a> ";
  5935.                 print_string("bynameondate", "forum", $by);
  5936.                 echo "</span>";
  5937.             }
  5938.  
  5939.             forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts);
  5940.             echo "</div>\n";
  5941.         }
  5942.     }
  5943. }
  5944.  
  5945. /**
  5946.  * @todo Document this function
  5947.  * @global object
  5948.  * @global object
  5949.  * @return void
  5950.  */
  5951. function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) {
  5952.     global $USER, $CFG;
  5953.  
  5954.     $link  = false;
  5955.  
  5956.     if (!empty($posts[$parent->id]->children)) {
  5957.         $posts = $posts[$parent->id]->children;
  5958.  
  5959.         foreach ($posts as $post) {
  5960.  
  5961.             echo '<div class="indent">';
  5962.             if (!isloggedin()) {
  5963.                 $ownpost = false;
  5964.             } else {
  5965.                 $ownpost = ($USER->id == $post->userid);
  5966.             }
  5967.  
  5968.             $post->subject = format_string($post->subject);
  5969.             $postread = !empty($post->postread);
  5970.  
  5971.             forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link,
  5972.                                  '', '', $postread, true, $forumtracked);
  5973.             forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts);
  5974.             echo "</div>\n";
  5975.         }
  5976.     }
  5977. }
  5978.  
  5979. /**
  5980.  * Returns all forum posts since a given time in specified forum.
  5981.  *
  5982.  * @todo Document this functions args
  5983.  * @global object
  5984.  * @global object
  5985.  * @global object
  5986.  * @global object
  5987.  */
  5988. function forum_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0)  {
  5989.     global $CFG, $COURSE, $USER, $DB;
  5990.  
  5991.     if ($COURSE->id == $courseid) {
  5992.         $course = $COURSE;
  5993.     } else {
  5994.         $course = $DB->get_record('course', array('id' => $courseid));
  5995.     }
  5996.  
  5997.     $modinfo = get_fast_modinfo($course);
  5998.  
  5999.     $cm = $modinfo->cms[$cmid];
  6000.     $params = array($timestart, $cm->instance);
  6001.  
  6002.     if ($userid) {
  6003.         $userselect = "AND u.id = ?";
  6004.         $params[] = $userid;
  6005.     } else {
  6006.         $userselect = "";
  6007.     }
  6008.  
  6009.     if ($groupid) {
  6010.         $groupselect = "AND d.groupid = ?";
  6011.         $params[] = $groupid;
  6012.     } else {
  6013.         $groupselect = "";
  6014.     }
  6015.  
  6016.     if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  6017.                                              d.timestart, d.timeend, d.userid AS duserid,
  6018.                                              u.firstname, u.lastname, u.email, u.picture, u.imagealt, u.email
  6019.                                         FROM {forum_posts} p
  6020.                                              JOIN {forum_discussions} d ON d.id = p.discussion
  6021.                                              JOIN {forum} f             ON f.id = d.forum
  6022.                                              JOIN {user} u              ON u.id = p.userid
  6023.                                        WHERE p.created > ? AND f.id = ?
  6024.                                              $userselect $groupselect
  6025.                                     ORDER BY p.id ASC", $params)) { // order by initial posting date
  6026.          return;
  6027.     }
  6028.  
  6029.     $groupmode       = groups_get_activity_groupmode($cm, $course);
  6030.     $cm_context      = context_module::instance($cm->id);
  6031.     $viewhiddentimed = has_capability('mod/forum:viewhiddentimedposts', $cm_context);
  6032.     $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
  6033.  
  6034.     if (is_null($modinfo->groups)) {
  6035.         $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
  6036.     }
  6037.  
  6038.     $printposts = array();
  6039.     foreach ($posts as $post) {
  6040.  
  6041.         if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
  6042.           and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
  6043.             if (!$viewhiddentimed) {
  6044.                 continue;
  6045.             }
  6046.         }
  6047.  
  6048.         if ($groupmode) {
  6049.             if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or $accessallgroups) {
  6050.                 // oki (Open discussions have groupid -1)
  6051.             } else {
  6052.                 // separate mode
  6053.                 if (isguestuser()) {
  6054.                     // shortcut
  6055.                     continue;
  6056.                 }
  6057.  
  6058.                 if (!in_array($post->groupid, $modinfo->get_groups($cm->groupingid))) {
  6059.                     continue;
  6060.                 }
  6061.             }
  6062.         }
  6063.  
  6064.         $printposts[] = $post;
  6065.     }
  6066.  
  6067.     if (!$printposts) {
  6068.         return;
  6069.     }
  6070.  
  6071.     $aname = format_string($cm->name,true);
  6072.  
  6073.     foreach ($printposts as $post) {
  6074.         $tmpactivity = new stdClass();
  6075.  
  6076.         $tmpactivity->type         = 'forum';
  6077.         $tmpactivity->cmid         = $cm->id;
  6078.         $tmpactivity->name         = $aname;
  6079.         $tmpactivity->sectionnum   = $cm->sectionnum;
  6080.         $tmpactivity->timestamp    = $post->modified;
  6081.  
  6082.         $tmpactivity->content = new stdClass();
  6083.         $tmpactivity->content->id         = $post->id;
  6084.         $tmpactivity->content->discussion = $post->discussion;
  6085.         $tmpactivity->content->subject    = format_string($post->subject);
  6086.         $tmpactivity->content->parent     = $post->parent;
  6087.  
  6088.         $tmpactivity->user = new stdClass();
  6089.         $tmpactivity->user->id        = $post->userid;
  6090.         $tmpactivity->user->firstname = $post->firstname;
  6091.         $tmpactivity->user->lastname  = $post->lastname;
  6092.         $tmpactivity->user->picture   = $post->picture;
  6093.         $tmpactivity->user->imagealt  = $post->imagealt;
  6094.         $tmpactivity->user->email     = $post->email;
  6095.  
  6096.         $activities[$index++] = $tmpactivity;
  6097.     }
  6098.  
  6099.     return;
  6100. }
  6101.  
  6102. /**
  6103.  * @todo Document this function
  6104.  * @global object
  6105.  */
  6106. function forum_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
  6107.     global $CFG, $OUTPUT;
  6108.  
  6109.     if ($activity->content->parent) {
  6110.         $class = 'reply';
  6111.     } else {
  6112.         $class = 'discussion';
  6113.     }
  6114.  
  6115.     echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
  6116.  
  6117.     echo "<tr><td class=\"userpicture\" valign=\"top\">";
  6118.     echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid));
  6119.     echo "</td><td class=\"$class\">";
  6120.  
  6121.     echo '<div class="title">';
  6122.     if ($detail) {
  6123.         $aname = s($activity->name);
  6124.         echo "<img src=\"" . $OUTPUT->pix_url('icon', $activity->type) . "\" ".
  6125.              "class=\"icon\" alt=\"{$aname}\" />";
  6126.     }
  6127.     echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d={$activity->content->discussion}"
  6128.          ."#p{$activity->content->id}\">{$activity->content->subject}</a>";
  6129.     echo '</div>';
  6130.  
  6131.     echo '<div class="user">';
  6132.     $fullname = fullname($activity->user, $viewfullnames);
  6133.     echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">"
  6134.          ."{$fullname}</a> - ".userdate($activity->timestamp);
  6135.     echo '</div>';
  6136.       echo "</td></tr></table>";
  6137.  
  6138.     return;
  6139. }
  6140.  
  6141. /**
  6142.  * recursively sets the discussion field to $discussionid on $postid and all its children
  6143.  * used when pruning a post
  6144.  *
  6145.  * @global object
  6146.  * @param int $postid
  6147.  * @param int $discussionid
  6148.  * @return bool
  6149.  */
  6150. function forum_change_discussionid($postid, $discussionid) {
  6151.     global $DB;
  6152.     $DB->set_field('forum_posts', 'discussion', $discussionid, array('id' => $postid));
  6153.     if ($posts = $DB->get_records('forum_posts', array('parent' => $postid))) {
  6154.         foreach ($posts as $post) {
  6155.             forum_change_discussionid($post->id, $discussionid);
  6156.         }
  6157.     }
  6158.     return true;
  6159. }
  6160.  
  6161. /**
  6162.  * Prints the editing button on subscribers page
  6163.  *
  6164.  * @global object
  6165.  * @global object
  6166.  * @param int $courseid
  6167.  * @param int $forumid
  6168.  * @return string
  6169.  */
  6170. function forum_update_subscriptions_button($courseid, $forumid) {
  6171.     global $CFG, $USER;
  6172.  
  6173.     if (!empty($USER->subscriptionsediting)) {
  6174.         $string = get_string('turneditingoff');
  6175.         $edit = "off";
  6176.     } else {
  6177.         $string = get_string('turneditingon');
  6178.         $edit = "on";
  6179.     }
  6180.  
  6181.     return "<form method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
  6182.            "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
  6183.            "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
  6184.            "<input type=\"submit\" value=\"$string\" /></form>";
  6185. }
  6186.  
  6187. /**
  6188.  * This function gets run whenever user is enrolled into course
  6189.  *
  6190.  * @deprecated deprecating this function as we will be using forum_user_role_assigned
  6191.  * @param stdClass $cp
  6192.  * @return void
  6193.  */
  6194. function forum_user_enrolled($cp) {
  6195.     global $DB;
  6196.  
  6197.     // NOTE: this has to be as fast as possible - we do not want to slow down enrolments!
  6198.     //       Originally there used to be 'mod/forum:initialsubscriptions' which was
  6199.     //       introduced because we did not have enrolment information in earlier versions...
  6200.  
  6201.     $sql = "SELECT f.id
  6202.              FROM {forum} f
  6203.         LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
  6204.             WHERE f.course = :courseid AND f.forcesubscribe = :initial AND fs.id IS NULL";
  6205.     $params = array('courseid'=>$cp->courseid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE);
  6206.  
  6207.     $forums = $DB->get_records_sql($sql, $params);
  6208.     foreach ($forums as $forum) {
  6209.         forum_subscribe($cp->userid, $forum->id);
  6210.     }
  6211. }
  6212.  
  6213. /**
  6214.  * This function gets run whenever user is assigned role in course
  6215.  *
  6216.  * @param stdClass $cp
  6217.  * @return void
  6218.  */
  6219. function forum_user_role_assigned($cp) {
  6220.     global $DB;
  6221.  
  6222.     $context = context::instance_by_id($cp->contextid, MUST_EXIST);
  6223.  
  6224.     // If contextlevel is course then only subscribe user. Role assignment
  6225.     // at course level means user is enroled in course and can subscribe to forum.
  6226.     if ($context->contextlevel != CONTEXT_COURSE) {
  6227.         return;
  6228.     }
  6229.  
  6230.     $sql = "SELECT f.id, cm.id AS cmid
  6231.              FROM {forum} f
  6232.              JOIN {course_modules} cm ON (cm.instance = f.id)
  6233.              JOIN {modules} m ON (m.id = cm.module)
  6234.         LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid)
  6235.             WHERE f.course = :courseid
  6236.               AND f.forcesubscribe = :initial
  6237.               AND m.name = 'forum'
  6238.               AND fs.id IS NULL";
  6239.     $params = array('courseid'=>$context->instanceid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE);
  6240.  
  6241.     $forums = $DB->get_records_sql($sql, $params);
  6242.     foreach ($forums as $forum) {
  6243.         // If user doesn't have allowforcesubscribe capability then don't subscribe.
  6244.         if (has_capability('mod/forum:allowforcesubscribe', context_module::instance($forum->cmid), $cp->userid)) {
  6245.             forum_subscribe($cp->userid, $forum->id);
  6246.         }
  6247.     }
  6248. }
  6249.  
  6250. /**
  6251.  * This function gets run whenever user is unenrolled from course
  6252.  *
  6253.  * @param stdClass $cp
  6254.  * @return void
  6255.  */
  6256. function forum_user_unenrolled($cp) {
  6257.     global $DB;
  6258.  
  6259.     // NOTE: this has to be as fast as possible!
  6260.  
  6261.     if ($cp->lastenrol) {
  6262.         $params = array('userid'=>$cp->userid, 'courseid'=>$cp->courseid);
  6263.         $forumselect = "IN (SELECT f.id FROM {forum} f WHERE f.course = :courseid)";
  6264.  
  6265.         $DB->delete_records_select('forum_subscriptions', "userid = :userid AND forum $forumselect", $params);
  6266.         $DB->delete_records_select('forum_track_prefs',   "userid = :userid AND forumid $forumselect", $params);
  6267.         $DB->delete_records_select('forum_read',          "userid = :userid AND forumid $forumselect", $params);
  6268.     }
  6269. }
  6270.  
  6271. // Functions to do with read tracking.
  6272.  
  6273. /**
  6274.  * Mark posts as read.
  6275.  *
  6276.  * @global object
  6277.  * @global object
  6278.  * @param object $user object
  6279.  * @param array $postids array of post ids
  6280.  * @return boolean success
  6281.  */
  6282. function forum_tp_mark_posts_read($user, $postids) {
  6283.     global $CFG, $DB;
  6284.  
  6285.     if (!forum_tp_can_track_forums(false, $user)) {
  6286.         return true;
  6287.     }
  6288.  
  6289.     $status = true;
  6290.  
  6291.     $now = time();
  6292.     $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  6293.  
  6294.     if (empty($postids)) {
  6295.         return true;
  6296.  
  6297.     } else if (count($postids) > 200) {
  6298.         while ($part = array_splice($postids, 0, 200)) {
  6299.             $status = forum_tp_mark_posts_read($user, $part) && $status;
  6300.         }
  6301.         return $status;
  6302.     }
  6303.  
  6304.     list($usql, $params) = $DB->get_in_or_equal($postids);
  6305.     $params[] = $user->id;
  6306.  
  6307.     $sql = "SELECT id
  6308.              FROM {forum_read}
  6309.             WHERE postid $usql AND userid = ?";
  6310.     if ($existing = $DB->get_records_sql($sql, $params)) {
  6311.         $existing = array_keys($existing);
  6312.     } else {
  6313.         $existing = array();
  6314.     }
  6315.  
  6316.     $new = array_diff($postids, $existing);
  6317.  
  6318.     if ($new) {
  6319.         list($usql, $new_params) = $DB->get_in_or_equal($new);
  6320.         $params = array($user->id, $now, $now, $user->id);
  6321.         $params = array_merge($params, $new_params);
  6322.         $params[] = $cutoffdate;
  6323.  
  6324.         $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
  6325.  
  6326.                SELECT ?, p.id, p.discussion, d.forum, ?, ?
  6327.                  FROM {forum_posts} p
  6328.                       JOIN {forum_discussions} d       ON d.id = p.discussion
  6329.                       JOIN {forum} f                   ON f.id = d.forum
  6330.                       LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id)
  6331.                 WHERE p.id $usql
  6332.                       AND p.modified >= ?
  6333.                       AND (f.trackingtype = ".FORUM_TRACKING_ON."
  6334.                            OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))";
  6335.         $status = $DB->execute($sql, $params) && $status;
  6336.     }
  6337.  
  6338.     if ($existing) {
  6339.         list($usql, $new_params) = $DB->get_in_or_equal($existing);
  6340.         $params = array($now, $user->id);
  6341.         $params = array_merge($params, $new_params);
  6342.  
  6343.         $sql = "UPDATE {forum_read}
  6344.                   SET lastread = ?
  6345.                 WHERE userid = ? AND postid $usql";
  6346.         $status = $DB->execute($sql, $params) && $status;
  6347.     }
  6348.  
  6349.     return $status;
  6350. }
  6351.  
  6352. /**
  6353.  * Mark post as read.
  6354.  * @global object
  6355.  * @global object
  6356.  * @param int $userid
  6357.  * @param int $postid
  6358.  */
  6359. function forum_tp_add_read_record($userid, $postid) {
  6360.     global $CFG, $DB;
  6361.  
  6362.     $now = time();
  6363.     $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  6364.  
  6365.     if (!$DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $postid))) {
  6366.         $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread)
  6367.  
  6368.                SELECT ?, p.id, p.discussion, d.forum, ?, ?
  6369.                  FROM {forum_posts} p
  6370.                       JOIN {forum_discussions} d ON d.id = p.discussion
  6371.                 WHERE p.id = ? AND p.modified >= ?";
  6372.         return $DB->execute($sql, array($userid, $now, $now, $postid, $cutoffdate));
  6373.  
  6374.     } else {
  6375.         $sql = "UPDATE {forum_read}
  6376.                   SET lastread = ?
  6377.                 WHERE userid = ? AND postid = ?";
  6378.         return $DB->execute($sql, array($now, $userid, $userid));
  6379.     }
  6380. }
  6381.  
  6382. /**
  6383.  * Returns all records in the 'forum_read' table matching the passed keys, indexed
  6384.  * by userid.
  6385.  *
  6386.  * @global object
  6387.  * @param int $userid
  6388.  * @param int $postid
  6389.  * @param int $discussionid
  6390.  * @param int $forumid
  6391.  * @return array
  6392.  */
  6393. function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
  6394.     global $DB;
  6395.     $select = '';
  6396.     $params = array();
  6397.  
  6398.     if ($userid > -1) {
  6399.         if ($select != '') $select .= ' AND ';
  6400.         $select .= 'userid = ?';
  6401.         $params[] = $userid;
  6402.     }
  6403.     if ($postid > -1) {
  6404.         if ($select != '') $select .= ' AND ';
  6405.         $select .= 'postid = ?';
  6406.         $params[] = $postid;
  6407.     }
  6408.     if ($discussionid > -1) {
  6409.         if ($select != '') $select .= ' AND ';
  6410.         $select .= 'discussionid = ?';
  6411.         $params[] = $discussionid;
  6412.     }
  6413.     if ($forumid > -1) {
  6414.         if ($select != '') $select .= ' AND ';
  6415.         $select .= 'forumid = ?';
  6416.         $params[] = $forumid;
  6417.     }
  6418.  
  6419.     return $DB->get_records_select('forum_read', $select, $params);
  6420. }
  6421.  
  6422. /**
  6423.  * Returns all read records for the provided user and discussion, indexed by postid.
  6424.  *
  6425.  * @global object
  6426.  * @param inti $userid
  6427.  * @param int $discussionid
  6428.  */
  6429. function forum_tp_get_discussion_read_records($userid, $discussionid) {
  6430.     global $DB;
  6431.     $select = 'userid = ? AND discussionid = ?';
  6432.     $fields = 'postid, firstread, lastread';
  6433.     return $DB->get_records_select('forum_read', $select, array($userid, $discussionid), '', $fields);
  6434. }
  6435.  
  6436. /**
  6437.  * If its an old post, do nothing. If the record exists, the maintenance will clear it up later.
  6438.  *
  6439.  * @return bool
  6440.  */
  6441. function forum_tp_mark_post_read($userid, $post, $forumid) {
  6442.     if (!forum_tp_is_post_old($post)) {
  6443.         return forum_tp_add_read_record($userid, $post->id);
  6444.     } else {
  6445.         return true;
  6446.     }
  6447. }
  6448.  
  6449. /**
  6450.  * Marks a whole forum as read, for a given user
  6451.  *
  6452.  * @global object
  6453.  * @global object
  6454.  * @param object $user
  6455.  * @param int $forumid
  6456.  * @param int|bool $groupid
  6457.  * @return bool
  6458.  */
  6459. function forum_tp_mark_forum_read($user, $forumid, $groupid=false) {
  6460.     global $CFG, $DB;
  6461.  
  6462.     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  6463.  
  6464.     $groupsel = "";
  6465.     $params = array($user->id, $forumid, $cutoffdate);
  6466.  
  6467.     if ($groupid !== false) {
  6468.         $groupsel = " AND (d.groupid = ? OR d.groupid = -1)";
  6469.         $params[] = $groupid;
  6470.     }
  6471.  
  6472.     $sql = "SELECT p.id
  6473.              FROM {forum_posts} p
  6474.                   LEFT JOIN {forum_discussions} d ON d.id = p.discussion
  6475.                   LEFT JOIN {forum_read} r        ON (r.postid = p.id AND r.userid = ?)
  6476.             WHERE d.forum = ?
  6477.                   AND p.modified >= ? AND r.id is NULL
  6478.                   $groupsel";
  6479.  
  6480.     if ($posts = $DB->get_records_sql($sql, $params)) {
  6481.         $postids = array_keys($posts);
  6482.         return forum_tp_mark_posts_read($user, $postids);
  6483.     }
  6484.  
  6485.     return true;
  6486. }
  6487.  
  6488. /**
  6489.  * Marks a whole discussion as read, for a given user
  6490.  *
  6491.  * @global object
  6492.  * @global object
  6493.  * @param object $user
  6494.  * @param int $discussionid
  6495.  * @return bool
  6496.  */
  6497. function forum_tp_mark_discussion_read($user, $discussionid) {
  6498.     global $CFG, $DB;
  6499.  
  6500.     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  6501.  
  6502.     $sql = "SELECT p.id
  6503.              FROM {forum_posts} p
  6504.                   LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?)
  6505.             WHERE p.discussion = ?
  6506.                   AND p.modified >= ? AND r.id is NULL";
  6507.  
  6508.     if ($posts = $DB->get_records_sql($sql, array($user->id, $discussionid, $cutoffdate))) {
  6509.         $postids = array_keys($posts);
  6510.         return forum_tp_mark_posts_read($user, $postids);
  6511.     }
  6512.  
  6513.     return true;
  6514. }
  6515.  
  6516. /**
  6517.  * @global object
  6518.  * @param int $userid
  6519.  * @param object $post
  6520.  */
  6521. function forum_tp_is_post_read($userid, $post) {
  6522.     global $DB;
  6523.     return (forum_tp_is_post_old($post) ||
  6524.             $DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $post->id)));
  6525. }
  6526.  
  6527. /**
  6528.  * @global object
  6529.  * @param object $post
  6530.  * @param int $time Defautls to time()
  6531.  */
  6532. function forum_tp_is_post_old($post, $time=null) {
  6533.     global $CFG;
  6534.  
  6535.     if (is_null($time)) {
  6536.         $time = time();
  6537.     }
  6538.     return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600)));
  6539. }
  6540.  
  6541. /**
  6542.  * Returns the count of records for the provided user and discussion.
  6543.  *
  6544.  * @global object
  6545.  * @global object
  6546.  * @param int $userid
  6547.  * @param int $discussionid
  6548.  * @return bool
  6549.  */
  6550. function forum_tp_count_discussion_read_records($userid, $discussionid) {
  6551.     global $CFG, $DB;
  6552.  
  6553.     $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  6554.  
  6555.     $sql = 'SELECT COUNT(DISTINCT p.id) '.
  6556.            'FROM {forum_discussions} d '.
  6557.            'LEFT JOIN {forum_read} r ON d.id = r.discussionid AND r.userid = ? '.
  6558.            'LEFT JOIN {forum_posts} p ON p.discussion = d.id '.
  6559.                 'AND (p.modified < ? OR p.id = r.postid) '.
  6560.            'WHERE d.id = ? ';
  6561.  
  6562.     return ($DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid)));
  6563. }
  6564.  
  6565. /**
  6566.  * Returns the count of records for the provided user and discussion.
  6567.  *
  6568.  * @global object
  6569.  * @global object
  6570.  * @param int $userid
  6571.  * @param int $discussionid
  6572.  * @return int
  6573.  */
  6574. function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
  6575.     global $CFG, $DB;
  6576.  
  6577.     $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  6578.  
  6579.     $sql = 'SELECT COUNT(p.id) '.
  6580.            'FROM {forum_posts} p '.
  6581.            'LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? '.
  6582.            'WHERE p.discussion = ? '.
  6583.                 'AND p.modified >= ? AND r.id is NULL';
  6584.  
  6585.     return $DB->count_records_sql($sql, array($userid, $discussionid, $cutoffdate));
  6586. }
  6587.  
  6588. /**
  6589.  * Returns the count of posts for the provided forum and [optionally] group.
  6590.  * @global object
  6591.  * @global object
  6592.  * @param int $forumid
  6593.  * @param int|bool $groupid
  6594.  * @return int
  6595.  */
  6596. function forum_tp_count_forum_posts($forumid, $groupid=false) {
  6597.     global $CFG, $DB;
  6598.     $params = array($forumid);
  6599.     $sql = 'SELECT COUNT(*) '.
  6600.            'FROM {forum_posts} fp,{forum_discussions} fd '.
  6601.            'WHERE fd.forum = ? AND fp.discussion = fd.id';
  6602.     if ($groupid !== false) {
  6603.         $sql .= ' AND (fd.groupid = ? OR fd.groupid = -1)';
  6604.         $params[] = $groupid;
  6605.     }
  6606.     $count = $DB->count_records_sql($sql, $params);
  6607.  
  6608.  
  6609.     return $count;
  6610. }
  6611.  
  6612. /**
  6613.  * Returns the count of records for the provided user and forum and [optionally] group.
  6614.  * @global object
  6615.  * @global object
  6616.  * @param int $userid
  6617.  * @param int $forumid
  6618.  * @param int|bool $groupid
  6619.  * @return int
  6620.  */
  6621. function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
  6622.     global $CFG, $DB;
  6623.  
  6624.     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  6625.  
  6626.     $groupsel = '';
  6627.     $params = array($userid, $forumid, $cutoffdate);
  6628.     if ($groupid !== false) {
  6629.         $groupsel = "AND (d.groupid = ? OR d.groupid = -1)";
  6630.         $params[] = $groupid;
  6631.     }
  6632.  
  6633.     $sql = "SELECT COUNT(p.id)
  6634.              FROM  {forum_posts} p
  6635.                    JOIN {forum_discussions} d ON d.id = p.discussion
  6636.                    LEFT JOIN {forum_read} r   ON (r.postid = p.id AND r.userid= ?)
  6637.              WHERE d.forum = ?
  6638.                    AND (p.modified < $cutoffdate OR (p.modified >= ? AND r.id IS NOT NULL))
  6639.                    $groupsel";
  6640.  
  6641.     return $DB->get_field_sql($sql, $params);
  6642. }
  6643.  
  6644. /**
  6645.  * Returns the count of records for the provided user and course.
  6646.  * Please note that group access is ignored!
  6647.  *
  6648.  * @global object
  6649.  * @global object
  6650.  * @param int $userid
  6651.  * @param int $courseid
  6652.  * @return array
  6653.  */
  6654. function forum_tp_get_course_unread_posts($userid, $courseid) {
  6655.     global $CFG, $DB;
  6656.  
  6657.     $now = round(time(), -2); // db cache friendliness
  6658.     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  6659.     $params = array($userid, $userid, $courseid, $cutoffdate);
  6660.  
  6661.     if (!empty($CFG->forum_enabletimedposts)) {
  6662.         $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
  6663.         $params[] = $now;
  6664.         $params[] = $now;
  6665.     } else {
  6666.         $timedsql = "";
  6667.     }
  6668.  
  6669.     $sql = "SELECT f.id, COUNT(p.id) AS unread
  6670.              FROM {forum_posts} p
  6671.                   JOIN {forum_discussions} d       ON d.id = p.discussion
  6672.                   JOIN {forum} f                   ON f.id = d.forum
  6673.                   JOIN {course} c                  ON c.id = f.course
  6674.                   LEFT JOIN {forum_read} r         ON (r.postid = p.id AND r.userid = ?)
  6675.                   LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id)
  6676.             WHERE f.course = ?
  6677.                   AND p.modified >= ? AND r.id is NULL
  6678.                   AND (f.trackingtype = ".FORUM_TRACKING_ON."
  6679.                        OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))
  6680.                   $timedsql
  6681.          GROUP BY f.id";
  6682.  
  6683.     if ($return = $DB->get_records_sql($sql, $params)) {
  6684.         return $return;
  6685.     }
  6686.  
  6687.     return array();
  6688. }
  6689.  
  6690. /**
  6691.  * Returns the count of records for the provided user and forum and [optionally] group.
  6692.  *
  6693.  * @global object
  6694.  * @global object
  6695.  * @global object
  6696.  * @param object $cm
  6697.  * @param object $course
  6698.  * @return int
  6699.  */
  6700. function forum_tp_count_forum_unread_posts($cm, $course) {
  6701.     global $CFG, $USER, $DB;
  6702.  
  6703.     static $readcache = array();
  6704.  
  6705.     $forumid = $cm->instance;
  6706.  
  6707.     if (!isset($readcache[$course->id])) {
  6708.         $readcache[$course->id] = array();
  6709.         if ($counts = forum_tp_get_course_unread_posts($USER->id, $course->id)) {
  6710.             foreach ($counts as $count) {
  6711.                 $readcache[$course->id][$count->id] = $count->unread;
  6712.             }
  6713.         }
  6714.     }
  6715.  
  6716.     if (empty($readcache[$course->id][$forumid])) {
  6717.         // no need to check group mode ;-)
  6718.         return 0;
  6719.     }
  6720.  
  6721.     $groupmode = groups_get_activity_groupmode($cm, $course);
  6722.  
  6723.     if ($groupmode != SEPARATEGROUPS) {
  6724.         return $readcache[$course->id][$forumid];
  6725.     }
  6726.  
  6727.     if (has_capability('moodle/site:accessallgroups', context_module::instance($cm->id))) {
  6728.         return $readcache[$course->id][$forumid];
  6729.     }
  6730.  
  6731.     require_once($CFG->dirroot.'/course/lib.php');
  6732.  
  6733.     $modinfo = get_fast_modinfo($course);
  6734.     if (is_null($modinfo->groups)) {
  6735.         $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  6736.     }
  6737.  
  6738.     if (array_key_exists($cm->groupingid, $modinfo->groups)) {
  6739.         $mygroups = $modinfo->groups[$cm->groupingid];
  6740.     } else {
  6741.         $mygroups = false; // Will be set below
  6742.     }
  6743.  
  6744.     // add all groups posts
  6745.     if (empty($mygroups)) {
  6746.         $mygroups = array(-1=>-1);
  6747.     } else {
  6748.         $mygroups[-1] = -1;
  6749.     }
  6750.  
  6751.     list ($groups_sql, $groups_params) = $DB->get_in_or_equal($mygroups);
  6752.  
  6753.     $now = round(time(), -2); // db cache friendliness
  6754.     $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  6755.     $params = array($USER->id, $forumid, $cutoffdate);
  6756.  
  6757.     if (!empty($CFG->forum_enabletimedposts)) {
  6758.         $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)";
  6759.         $params[] = $now;
  6760.         $params[] = $now;
  6761.     } else {
  6762.         $timedsql = "";
  6763.     }
  6764.  
  6765.     $params = array_merge($params, $groups_params);
  6766.  
  6767.     $sql = "SELECT COUNT(p.id)
  6768.              FROM {forum_posts} p
  6769.                   JOIN {forum_discussions} d ON p.discussion = d.id
  6770.                   LEFT JOIN {forum_read} r   ON (r.postid = p.id AND r.userid = ?)
  6771.             WHERE d.forum = ?
  6772.                   AND p.modified >= ? AND r.id is NULL
  6773.                   $timedsql
  6774.                   AND d.groupid $groups_sql";
  6775.  
  6776.     return $DB->get_field_sql($sql, $params);
  6777. }
  6778.  
  6779. /**
  6780.  * Deletes read records for the specified index. At least one parameter must be specified.
  6781.  *
  6782.  * @global object
  6783.  * @param int $userid
  6784.  * @param int $postid
  6785.  * @param int $discussionid
  6786.  * @param int $forumid
  6787.  * @return bool
  6788.  */
  6789. function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
  6790.     global $DB;
  6791.     $params = array();
  6792.  
  6793.     $select = '';
  6794.     if ($userid > -1) {
  6795.         if ($select != '') $select .= ' AND ';
  6796.         $select .= 'userid = ?';
  6797.         $params[] = $userid;
  6798.     }
  6799.     if ($postid > -1) {
  6800.         if ($select != '') $select .= ' AND ';
  6801.         $select .= 'postid = ?';
  6802.         $params[] = $postid;
  6803.     }
  6804.     if ($discussionid > -1) {
  6805.         if ($select != '') $select .= ' AND ';
  6806.         $select .= 'discussionid = ?';
  6807.         $params[] = $discussionid;
  6808.     }
  6809.     if ($forumid > -1) {
  6810.         if ($select != '') $select .= ' AND ';
  6811.         $select .= 'forumid = ?';
  6812.         $params[] = $forumid;
  6813.     }
  6814.     if ($select == '') {
  6815.         return false;
  6816.     }
  6817.     else {
  6818.         return $DB->delete_records_select('forum_read', $select, $params);
  6819.     }
  6820. }
  6821. /**
  6822.  * Get a list of forums not tracked by the user.
  6823.  *
  6824.  * @global object
  6825.  * @global object
  6826.  * @param int $userid The id of the user to use.
  6827.  * @param int $courseid The id of the course being checked.
  6828.  * @return mixed An array indexed by forum id, or false.
  6829.  */
  6830. function forum_tp_get_untracked_forums($userid, $courseid) {
  6831.     global $CFG, $DB;
  6832.  
  6833.     $sql = "SELECT f.id
  6834.              FROM {forum} f
  6835.                   LEFT JOIN {forum_track_prefs} ft ON (ft.forumid = f.id AND ft.userid = ?)
  6836.             WHERE f.course = ?
  6837.                   AND (f.trackingtype = ".FORUM_TRACKING_OFF."
  6838.                        OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND ft.id IS NOT NULL))";
  6839.  
  6840.     if ($forums = $DB->get_records_sql($sql, array($userid, $courseid))) {
  6841.         foreach ($forums as $forum) {
  6842.             $forums[$forum->id] = $forum;
  6843.         }
  6844.         return $forums;
  6845.  
  6846.     } else {
  6847.         return array();
  6848.     }
  6849. }
  6850.  
  6851. /**
  6852.  * Determine if a user can track forums and optionally a particular forum.
  6853.  * Checks the site settings, the user settings and the forum settings (if
  6854.  * requested).
  6855.  *
  6856.  * @global object
  6857.  * @global object
  6858.  * @global object
  6859.  * @param mixed $forum The forum object to test, or the int id (optional).
  6860.  * @param mixed $userid The user object to check for (optional).
  6861.  * @return boolean
  6862.  */
  6863. function forum_tp_can_track_forums($forum=false, $user=false) {
  6864.     global $USER, $CFG, $DB;
  6865.  
  6866.     // if possible, avoid expensive
  6867.     // queries
  6868.     if (empty($CFG->forum_trackreadposts)) {
  6869.         return false;
  6870.     }
  6871.  
  6872.     if ($user === false) {
  6873.         $user = $USER;
  6874.     }
  6875.  
  6876.     if (isguestuser($user) or empty($user->id)) {
  6877.         return false;
  6878.     }
  6879.  
  6880.     if ($forum === false) {
  6881.         // general abitily to track forums
  6882.         return (bool)$user->trackforums;
  6883.     }
  6884.  
  6885.  
  6886.     // Work toward always passing an object...
  6887.     if (is_numeric($forum)) {
  6888.         debugging('Better use proper forum object.', DEBUG_DEVELOPER);
  6889.         $forum = $DB->get_record('forum', array('id' => $forum), '', 'id,trackingtype');
  6890.     }
  6891.  
  6892.     $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
  6893.     $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
  6894.  
  6895.     return ($forumforced || $forumallows)  && !empty($user->trackforums);
  6896. }
  6897.  
  6898. /**
  6899.  * Tells whether a specific forum is tracked by the user. A user can optionally
  6900.  * be specified. If not specified, the current user is assumed.
  6901.  *
  6902.  * @global object
  6903.  * @global object
  6904.  * @global object
  6905.  * @param mixed $forum If int, the id of the forum being checked; if object, the forum object
  6906.  * @param int $userid The id of the user being checked (optional).
  6907.  * @return boolean
  6908.  */
  6909. function forum_tp_is_tracked($forum, $user=false) {
  6910.     global $USER, $CFG, $DB;
  6911.  
  6912.     if ($user === false) {
  6913.         $user = $USER;
  6914.     }
  6915.  
  6916.     if (isguestuser($user) or empty($user->id)) {
  6917.         return false;
  6918.     }
  6919.  
  6920.     // Work toward always passing an object...
  6921.     if (is_numeric($forum)) {
  6922.         debugging('Better use proper forum object.', DEBUG_DEVELOPER);
  6923.         $forum = $DB->get_record('forum', array('id' => $forum));
  6924.     }
  6925.  
  6926.     if (!forum_tp_can_track_forums($forum, $user)) {
  6927.         return false;
  6928.     }
  6929.  
  6930.     $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
  6931.     $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
  6932.  
  6933.     return $forumforced ||
  6934.            ($forumallows && $DB->get_record('forum_track_prefs', array('userid' => $user->id, 'forumid' => $forum->id)) === false);
  6935. }
  6936.  
  6937. /**
  6938.  * @global object
  6939.  * @global object
  6940.  * @param int $forumid
  6941.  * @param int $userid
  6942.  */
  6943. function forum_tp_start_tracking($forumid, $userid=false) {
  6944.     global $USER, $DB;
  6945.  
  6946.     if ($userid === false) {
  6947.         $userid = $USER->id;
  6948.     }
  6949.  
  6950.     return $DB->delete_records('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid));
  6951. }
  6952.  
  6953. /**
  6954.  * @global object
  6955.  * @global object
  6956.  * @param int $forumid
  6957.  * @param int $userid
  6958.  */
  6959. function forum_tp_stop_tracking($forumid, $userid=false) {
  6960.     global $USER, $DB;
  6961.  
  6962.     if ($userid === false) {
  6963.         $userid = $USER->id;
  6964.     }
  6965.  
  6966.     if (!$DB->record_exists('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid))) {
  6967.         $track_prefs = new stdClass();
  6968.         $track_prefs->userid = $userid;
  6969.         $track_prefs->forumid = $forumid;
  6970.         $DB->insert_record('forum_track_prefs', $track_prefs);
  6971.     }
  6972.  
  6973.     return forum_tp_delete_read_records($userid, -1, -1, $forumid);
  6974. }
  6975.  
  6976.  
  6977. /**
  6978.  * Clean old records from the forum_read table.
  6979.  * @global object
  6980.  * @global object
  6981.  * @return void
  6982.  */
  6983. function forum_tp_clean_read_records() {
  6984.     global $CFG, $DB;
  6985.  
  6986.     if (!isset($CFG->forum_oldpostdays)) {
  6987.         return;
  6988.     }
  6989. // Look for records older than the cutoffdate that are still in the forum_read table.
  6990.     $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  6991.  
  6992.     //first get the oldest tracking present - we need tis to speedup the next delete query
  6993.     $sql = "SELECT MIN(fp.modified) AS first
  6994.              FROM {forum_posts} fp
  6995.                   JOIN {forum_read} fr ON fr.postid=fp.id";
  6996.     if (!$first = $DB->get_field_sql($sql)) {
  6997.         // nothing to delete;
  6998.         return;
  6999.     }
  7000.  
  7001.     // now delete old tracking info
  7002.     $sql = "DELETE
  7003.              FROM {forum_read}
  7004.             WHERE postid IN (SELECT fp.id
  7005.                                FROM {forum_posts} fp
  7006.                               WHERE fp.modified >= ? AND fp.modified < ?)";
  7007.     $DB->execute($sql, array($first, $cutoffdate));
  7008. }
  7009.  
  7010. /**
  7011.  * Sets the last post for a given discussion
  7012.  *
  7013.  * @global object
  7014.  * @global object
  7015.  * @param into $discussionid
  7016.  * @return bool|int
  7017.  **/
  7018. function forum_discussion_update_last_post($discussionid) {
  7019.     global $CFG, $DB;
  7020.  
  7021. // Check the given discussion exists
  7022.     if (!$DB->record_exists('forum_discussions', array('id' => $discussionid))) {
  7023.         return false;
  7024.     }
  7025.  
  7026. // Use SQL to find the last post for this discussion
  7027.     $sql = "SELECT id, userid, modified
  7028.              FROM {forum_posts}
  7029.             WHERE discussion=?
  7030.             ORDER BY modified DESC";
  7031.  
  7032. // Lets go find the last post
  7033.     if (($lastposts = $DB->get_records_sql($sql, array($discussionid), 0, 1))) {
  7034.         $lastpost = reset($lastposts);
  7035.         $discussionobject = new stdClass();
  7036.         $discussionobject->id           = $discussionid;
  7037.         $discussionobject->usermodified = $lastpost->userid;
  7038.         $discussionobject->timemodified = $lastpost->modified;
  7039.         $DB->update_record('forum_discussions', $discussionobject);
  7040.         return $lastpost->id;
  7041.     }
  7042.  
  7043. // To get here either we couldn't find a post for the discussion (weird)
  7044. // or we couldn't update the discussion record (weird x2)
  7045.     return false;
  7046. }
  7047.  
  7048.  
  7049. /**
  7050.  * @return array
  7051.  */
  7052. function forum_get_view_actions() {
  7053.     return array('view discussion', 'search', 'forum', 'forums', 'subscribers', 'view forum');
  7054. }
  7055.  
  7056. /**
  7057.  * @return array
  7058.  */
  7059. function forum_get_post_actions() {
  7060.     return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
  7061. }
  7062.  
  7063. /**
  7064.  * Returns a warning object if a user has reached the number of posts equal to
  7065.  * the warning/blocking setting, or false if there is no warning to show.
  7066.  *
  7067.  * @param int|stdClass $forum the forum id or the forum object
  7068.  * @param stdClass $cm the course module
  7069.  * @return stdClass|bool returns an object with the warning information, else
  7070.  *         returns false if no warning is required.
  7071.  */
  7072. function forum_check_throttling($forum, $cm = null) {
  7073.     global $CFG, $DB, $USER;
  7074.  
  7075.     if (is_numeric($forum)) {
  7076.         $forum = $DB->get_record('forum', array('id' => $forum), '*', MUST_EXIST);
  7077.     }
  7078.  
  7079.     if (!is_object($forum)) {
  7080.         return false; // This is broken.
  7081.     }
  7082.  
  7083.     if (!$cm) {
  7084.         $cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course, false, MUST_EXIST);
  7085.     }
  7086.  
  7087.     if (empty($forum->blockafter)) {
  7088.         return false;
  7089.     }
  7090.  
  7091.     if (empty($forum->blockperiod)) {
  7092.         return false;
  7093.     }
  7094.  
  7095.     $modcontext = context_module::instance($cm->id);
  7096.     if (has_capability('mod/forum:postwithoutthrottling', $modcontext)) {
  7097.         return false;
  7098.     }
  7099.  
  7100.     // Get the number of posts in the last period we care about.
  7101.     $timenow = time();
  7102.     $timeafter = $timenow - $forum->blockperiod;
  7103.     $numposts = $DB->count_records_sql('SELECT COUNT(p.id) FROM {forum_posts} p
  7104.                                        JOIN {forum_discussions} d
  7105.                                        ON p.discussion = d.id WHERE d.forum = ?
  7106.                                        AND p.userid = ? AND p.created > ?', array($forum->id, $USER->id, $timeafter));
  7107.  
  7108.     $a = new stdClass();
  7109.     $a->blockafter = $forum->blockafter;
  7110.     $a->numposts = $numposts;
  7111.     $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
  7112.  
  7113.     if ($forum->blockafter <= $numposts) {
  7114.         $warning = new stdClass();
  7115.         $warning->canpost = false;
  7116.         $warning->errorcode = 'forumblockingtoomanyposts';
  7117.         $warning->module = 'error';
  7118.         $warning->additional = $a;
  7119.         $warning->link = $CFG->wwwroot . '/mod/forum/view.php?f=' . $forum->id;
  7120.  
  7121.         return $warning;
  7122.     }
  7123.  
  7124.     if ($forum->warnafter <= $numposts) {
  7125.         $warning = new stdClass();
  7126.         $warning->canpost = true;
  7127.         $warning->errorcode = 'forumblockingalmosttoomanyposts';
  7128.         $warning->module = 'forum';
  7129.         $warning->additional = $a;
  7130.         $warning->link = null;
  7131.  
  7132.         return $warning;
  7133.     }
  7134. }
  7135.  
  7136. /**
  7137.  * Throws an error if the user is no longer allowed to post due to having reached
  7138.  * or exceeded the number of posts specified in 'Post threshold for blocking'
  7139.  * setting.
  7140.  *
  7141.  * @since Moodle 2.5
  7142.  * @param stdClass $thresholdwarning the warning information returned
  7143.  *        from the function forum_check_throttling.
  7144.  */
  7145. function forum_check_blocking_threshold($thresholdwarning) {
  7146.     if (!empty($thresholdwarning) && !$thresholdwarning->canpost) {
  7147.         print_error($thresholdwarning->errorcode,
  7148.                     $thresholdwarning->module,
  7149.                     $thresholdwarning->link,
  7150.                     $thresholdwarning->additional);
  7151.     }
  7152. }
  7153.  
  7154.  
  7155. /**
  7156.  * Removes all grades from gradebook
  7157.  *
  7158.  * @global object
  7159.  * @global object
  7160.  * @param int $courseid
  7161.  * @param string $type optional
  7162.  */
  7163. function forum_reset_gradebook($courseid, $type='') {
  7164.     global $CFG, $DB;
  7165.  
  7166.     $wheresql = '';
  7167.     $params = array($courseid);
  7168.     if ($type) {
  7169.         $wheresql = "AND f.type=?";
  7170.         $params[] = $type;
  7171.     }
  7172.  
  7173.     $sql = "SELECT f.*, cm.idnumber as cmidnumber, f.course as courseid
  7174.              FROM {forum} f, {course_modules} cm, {modules} m
  7175.             WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id AND f.course=? $wheresql";
  7176.  
  7177.     if ($forums = $DB->get_records_sql($sql, $params)) {
  7178.         foreach ($forums as $forum) {
  7179.             forum_grade_item_update($forum, 'reset');
  7180.         }
  7181.     }
  7182. }
  7183.  
  7184. /**
  7185.  * This function is used by the reset_course_userdata function in moodlelib.
  7186.  * This function will remove all posts from the specified forum
  7187.  * and clean up any related data.
  7188.  *
  7189.  * @global object
  7190.  * @global object
  7191.  * @param $data the data submitted from the reset course.
  7192.  * @return array status array
  7193.  */
  7194. function forum_reset_userdata($data) {
  7195.     global $CFG, $DB;
  7196.     require_once($CFG->dirroot.'/rating/lib.php');
  7197.  
  7198.     $componentstr = get_string('modulenameplural', 'forum');
  7199.     $status = array();
  7200.  
  7201.     $params = array($data->courseid);
  7202.  
  7203.     $removeposts = false;
  7204.     $typesql     = "";
  7205.     if (!empty($data->reset_forum_all)) {
  7206.         $removeposts = true;
  7207.         $typesstr    = get_string('resetforumsall', 'forum');
  7208.         $types       = array();
  7209.     } else if (!empty($data->reset_forum_types)){
  7210.         $removeposts = true;
  7211.         $typesql     = "";
  7212.         $types       = array();
  7213.         $forum_types_all = forum_get_forum_types_all();
  7214.         foreach ($data->reset_forum_types as $type) {
  7215.             if (!array_key_exists($type, $forum_types_all)) {
  7216.                 continue;
  7217.             }
  7218.             $typesql .= " AND f.type=?";
  7219.             $types[] = $forum_types_all[$type];
  7220.             $params[] = $type;
  7221.         }
  7222.         $typesstr = get_string('resetforums', 'forum').': '.implode(', ', $types);
  7223.     }
  7224.     $alldiscussionssql = "SELECT fd.id
  7225.                            FROM {forum_discussions} fd, {forum} f
  7226.                           WHERE f.course=? AND f.id=fd.forum";
  7227.  
  7228.     $allforumssql      = "SELECT f.id
  7229.                            FROM {forum} f
  7230.                           WHERE f.course=?";
  7231.  
  7232.     $allpostssql       = "SELECT fp.id
  7233.                            FROM {forum_posts} fp, {forum_discussions} fd, {forum} f
  7234.                           WHERE f.course=? AND f.id=fd.forum AND fd.id=fp.discussion";
  7235.  
  7236.     $forumssql = $forums = $rm = null;
  7237.  
  7238.     if( $removeposts || !empty($data->reset_forum_ratings) ) {
  7239.         $forumssql      = "$allforumssql $typesql";
  7240.         $forums = $forums = $DB->get_records_sql($forumssql, $params);
  7241.         $rm = new rating_manager();
  7242.         $ratingdeloptions = new stdClass;
  7243.         $ratingdeloptions->component = 'mod_forum';
  7244.         $ratingdeloptions->ratingarea = 'post';
  7245.     }
  7246.  
  7247.     if ($removeposts) {
  7248.         $discussionssql = "$alldiscussionssql $typesql";
  7249.         $postssql       = "$allpostssql $typesql";
  7250.  
  7251.         // now get rid of all attachments
  7252.         $fs = get_file_storage();
  7253.         if ($forums) {
  7254.             foreach ($forums as $forumid=>$unused) {
  7255.                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
  7256.                     continue;
  7257.                 }
  7258.                 $context = context_module::instance($cm->id);
  7259.                 $fs->delete_area_files($context->id, 'mod_forum', 'attachment');
  7260.                 $fs->delete_area_files($context->id, 'mod_forum', 'post');
  7261.  
  7262.                 //remove ratings
  7263.                 $ratingdeloptions->contextid = $context->id;
  7264.                 $rm->delete_ratings($ratingdeloptions);
  7265.             }
  7266.         }
  7267.  
  7268.         // first delete all read flags
  7269.         $DB->delete_records_select('forum_read', "forumid IN ($forumssql)", $params);
  7270.  
  7271.         // remove tracking prefs
  7272.         $DB->delete_records_select('forum_track_prefs', "forumid IN ($forumssql)", $params);
  7273.  
  7274.         // remove posts from queue
  7275.         $DB->delete_records_select('forum_queue', "discussionid IN ($discussionssql)", $params);
  7276.  
  7277.         // all posts - initial posts must be kept in single simple discussion forums
  7278.         $DB->delete_records_select('forum_posts', "discussion IN ($discussionssql) AND parent <> 0", $params); // first all children
  7279.         $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
  7280.  
  7281.         // finally all discussions except single simple forums
  7282.         $DB->delete_records_select('forum_discussions', "forum IN ($forumssql AND f.type <> 'single')", $params);
  7283.  
  7284.         // remove all grades from gradebook
  7285.         if (empty($data->reset_gradebook_grades)) {
  7286.             if (empty($types)) {
  7287.                 forum_reset_gradebook($data->courseid);
  7288.             } else {
  7289.                 foreach ($types as $type) {
  7290.                     forum_reset_gradebook($data->courseid, $type);
  7291.                 }
  7292.             }
  7293.         }
  7294.  
  7295.         $status[] = array('component'=>$componentstr, 'item'=>$typesstr, 'error'=>false);
  7296.     }
  7297.  
  7298.     // remove all ratings in this course's forums
  7299.     if (!empty($data->reset_forum_ratings)) {
  7300.         if ($forums) {
  7301.             foreach ($forums as $forumid=>$unused) {
  7302.                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
  7303.                     continue;
  7304.                 }
  7305.                 $context = context_module::instance($cm->id);
  7306.  
  7307.                 //remove ratings
  7308.                 $ratingdeloptions->contextid = $context->id;
  7309.                 $rm->delete_ratings($ratingdeloptions);
  7310.             }
  7311.         }
  7312.  
  7313.         // remove all grades from gradebook
  7314.         if (empty($data->reset_gradebook_grades)) {
  7315.             forum_reset_gradebook($data->courseid);
  7316.         }
  7317.     }
  7318.  
  7319.     // remove all subscriptions unconditionally - even for users still enrolled in course
  7320.     if (!empty($data->reset_forum_subscriptions)) {
  7321.         $DB->delete_records_select('forum_subscriptions', "forum IN ($allforumssql)", $params);
  7322.         $status[] = array('component'=>$componentstr, 'item'=>get_string('resetsubscriptions','forum'), 'error'=>false);
  7323.     }
  7324.  
  7325.     // remove all tracking prefs unconditionally - even for users still enrolled in course
  7326.     if (!empty($data->reset_forum_track_prefs)) {
  7327.         $DB->delete_records_select('forum_track_prefs', "forumid IN ($allforumssql)", $params);
  7328.         $status[] = array('component'=>$componentstr, 'item'=>get_string('resettrackprefs','forum'), 'error'=>false);
  7329.     }
  7330.  
  7331.     /// updating dates - shift may be negative too
  7332.     if ($data->timeshift) {
  7333.         shift_course_mod_dates('forum', array('assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
  7334.         $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
  7335.     }
  7336.  
  7337.     return $status;
  7338. }
  7339.  
  7340. /**
  7341.  * Called by course/reset.php
  7342.  *
  7343.  * @param $mform form passed by reference
  7344.  */
  7345. function forum_reset_course_form_definition(&$mform) {
  7346.     $mform->addElement('header', 'forumheader', get_string('modulenameplural', 'forum'));
  7347.  
  7348.     $mform->addElement('checkbox', 'reset_forum_all', get_string('resetforumsall','forum'));
  7349.  
  7350.     $mform->addElement('select', 'reset_forum_types', get_string('resetforums', 'forum'), forum_get_forum_types_all(), array('multiple' => 'multiple'));
  7351.     $mform->setAdvanced('reset_forum_types');
  7352.     $mform->disabledIf('reset_forum_types', 'reset_forum_all', 'checked');
  7353.  
  7354.     $mform->addElement('checkbox', 'reset_forum_subscriptions', get_string('resetsubscriptions','forum'));
  7355.     $mform->setAdvanced('reset_forum_subscriptions');
  7356.  
  7357.     $mform->addElement('checkbox', 'reset_forum_track_prefs', get_string('resettrackprefs','forum'));
  7358.     $mform->setAdvanced('reset_forum_track_prefs');
  7359.     $mform->disabledIf('reset_forum_track_prefs', 'reset_forum_all', 'checked');
  7360.  
  7361.     $mform->addElement('checkbox', 'reset_forum_ratings', get_string('deleteallratings'));
  7362.     $mform->disabledIf('reset_forum_ratings', 'reset_forum_all', 'checked');
  7363. }
  7364.  
  7365. /**
  7366.  * Course reset form defaults.
  7367.  * @return array
  7368.  */
  7369. function forum_reset_course_form_defaults($course) {
  7370.     return array('reset_forum_all'=>1, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1);
  7371. }
  7372.  
  7373. /**
  7374.  * Converts a forum to use the Roles System
  7375.  *
  7376.  * @global object
  7377.  * @global object
  7378.  * @param object $forum        a forum object with the same attributes as a record
  7379.  *                        from the forum database table
  7380.  * @param int $forummodid   the id of the forum module, from the modules table
  7381.  * @param array $teacherroles array of roles that have archetype teacher
  7382.  * @param array $studentroles array of roles that have archetype student
  7383.  * @param array $guestroles   array of roles that have archetype guest
  7384.  * @param int $cmid         the course_module id for this forum instance
  7385.  * @return boolean      forum was converted or not
  7386.  */
  7387. function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
  7388.                                 $studentroles=array(), $guestroles=array(), $cmid=NULL) {
  7389.  
  7390.     global $CFG, $DB, $OUTPUT;
  7391.  
  7392.     if (!isset($forum->open) && !isset($forum->assesspublic)) {
  7393.         // We assume that this forum has already been converted to use the
  7394.         // Roles System. Columns forum.open and forum.assesspublic get dropped
  7395.         // once the forum module has been upgraded to use Roles.
  7396.         return false;
  7397.     }
  7398.  
  7399.     if ($forum->type == 'teacher') {
  7400.  
  7401.         // Teacher forums should be converted to normal forums that
  7402.         // use the Roles System to implement the old behavior.
  7403.         // Note:
  7404.         //   Seems that teacher forums were never backed up in 1.6 since they
  7405.         //   didn't have an entry in the course_modules table.
  7406.         require_once($CFG->dirroot.'/course/lib.php');
  7407.  
  7408.         if ($DB->count_records('forum_discussions', array('forum' => $forum->id)) == 0) {
  7409.             // Delete empty teacher forums.
  7410.             $DB->delete_records('forum', array('id' => $forum->id));
  7411.         } else {
  7412.             // Create a course module for the forum and assign it to
  7413.             // section 0 in the course.
  7414.             $mod = new stdClass();
  7415.             $mod->course = $forum->course;
  7416.             $mod->module = $forummodid;
  7417.             $mod->instance = $forum->id;
  7418.             $mod->section = 0;
  7419.             $mod->visible = 0;     // Hide the forum
  7420.             $mod->visibleold = 0;  // Hide the forum
  7421.             $mod->groupmode = 0;
  7422.  
  7423.             if (!$cmid = add_course_module($mod)) {
  7424.                 print_error('cannotcreateinstanceforteacher', 'forum');
  7425.             } else {
  7426.                 $sectionid = course_add_cm_to_section($forum->course, $mod->coursemodule, 0);
  7427.             }
  7428.  
  7429.             // Change the forum type to general.
  7430.             $forum->type = 'general';
  7431.             $DB->update_record('forum', $forum);
  7432.  
  7433.             $context = context_module::instance($cmid);
  7434.  
  7435.             // Create overrides for default student and guest roles (prevent).
  7436.             foreach ($studentroles as $studentrole) {
  7437.                 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  7438.                 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id);
  7439.                 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  7440.                 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
  7441.                 assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
  7442.                 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
  7443.                 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
  7444.                 assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id);
  7445.                 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id);
  7446.                 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id);
  7447.                 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id);
  7448.                 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id);
  7449.                 assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id);
  7450.                 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id);
  7451.                 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id);
  7452.                 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id);
  7453.                 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $studentrole->id, $context->id);
  7454.             }
  7455.             foreach ($guestroles as $guestrole) {
  7456.                 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
  7457.                 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id);
  7458.                 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
  7459.                 assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id);
  7460.                 assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id);
  7461.                 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id);
  7462.                 assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id);
  7463.                 assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id);
  7464.                 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id);
  7465.                 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id);
  7466.                 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id);
  7467.                 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id);
  7468.                 assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id);
  7469.                 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id);
  7470.                 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id);
  7471.                 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id);
  7472.                 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $guestrole->id, $context->id);
  7473.             }
  7474.         }
  7475.     } else {
  7476.         // Non-teacher forum.
  7477.  
  7478.         if (empty($cmid)) {
  7479.             // We were not given the course_module id. Try to find it.
  7480.             if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
  7481.                 echo $OUTPUT->notification('Could not get the course module for the forum');
  7482.                 return false;
  7483.             } else {
  7484.                 $cmid = $cm->id;
  7485.             }
  7486.         }
  7487.         $context = context_module::instance($cmid);
  7488.  
  7489.         // $forum->open defines what students can do:
  7490.         //   0 = No discussions, no replies
  7491.         //   1 = No discussions, but replies are allowed
  7492.         //   2 = Discussions and replies are allowed
  7493.         switch ($forum->open) {
  7494.             case 0:
  7495.                 foreach ($studentroles as $studentrole) {
  7496.                     assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  7497.                     assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
  7498.                 }
  7499.                 break;
  7500.             case 1:
  7501.                 foreach ($studentroles as $studentrole) {
  7502.                     assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  7503.                     assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
  7504.                 }
  7505.                 break;
  7506.             case 2:
  7507.                 foreach ($studentroles as $studentrole) {
  7508.                     assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id);
  7509.                     assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
  7510.                 }
  7511.                 break;
  7512.         }
  7513.  
  7514.         // $forum->assessed defines whether forum rating is turned
  7515.         // on (1 or 2) and who can rate posts:
  7516.         //   1 = Everyone can rate posts
  7517.         //   2 = Only teachers can rate posts
  7518.         switch ($forum->assessed) {
  7519.             case 1:
  7520.                 foreach ($studentroles as $studentrole) {
  7521.                     assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id);
  7522.                 }
  7523.                 foreach ($teacherroles as $teacherrole) {
  7524.                     assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
  7525.                 }
  7526.                 break;
  7527.             case 2:
  7528.                 foreach ($studentroles as $studentrole) {
  7529.                     assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
  7530.                 }
  7531.                 foreach ($teacherroles as $teacherrole) {
  7532.                     assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
  7533.                 }
  7534.                 break;
  7535.         }
  7536.  
  7537.         // $forum->assesspublic defines whether students can see
  7538.         // everybody's ratings:
  7539.         //   0 = Students can only see their own ratings
  7540.         //   1 = Students can see everyone's ratings
  7541.         switch ($forum->assesspublic) {
  7542.             case 0:
  7543.                 foreach ($studentroles as $studentrole) {
  7544.                     assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
  7545.                 }
  7546.                 foreach ($teacherroles as $teacherrole) {
  7547.                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
  7548.                 }
  7549.                 break;
  7550.             case 1:
  7551.                 foreach ($studentroles as $studentrole) {
  7552.                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id);
  7553.                 }
  7554.                 foreach ($teacherroles as $teacherrole) {
  7555.                     assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
  7556.                 }
  7557.                 break;
  7558.         }
  7559.  
  7560.         if (empty($cm)) {
  7561.             $cm = $DB->get_record('course_modules', array('id' => $cmid));
  7562.         }
  7563.  
  7564.         // $cm->groupmode:
  7565.         // 0 - No groups
  7566.         // 1 - Separate groups
  7567.         // 2 - Visible groups
  7568.         switch ($cm->groupmode) {
  7569.             case 0:
  7570.                 break;
  7571.             case 1:
  7572.                 foreach ($studentroles as $studentrole) {
  7573.                     assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
  7574.                 }
  7575.                 foreach ($teacherroles as $teacherrole) {
  7576.                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
  7577.                 }
  7578.                 break;
  7579.             case 2:
  7580.                 foreach ($studentroles as $studentrole) {
  7581.                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
  7582.                 }
  7583.                 foreach ($teacherroles as $teacherrole) {
  7584.                     assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
  7585.                 }
  7586.                 break;
  7587.         }
  7588.     }
  7589.     return true;
  7590. }
  7591.  
  7592. /**
  7593.  * Returns array of forum layout modes
  7594.  *
  7595.  * @return array
  7596.  */
  7597. function forum_get_layout_modes() {
  7598.     return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
  7599.                   FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
  7600.                   FORUM_MODE_THREADED   => get_string('modethreaded', 'forum'),
  7601.                   FORUM_MODE_NESTED     => get_string('modenested', 'forum'));
  7602. }
  7603.  
  7604. /**
  7605.  * Returns array of forum types chooseable on the forum editing form
  7606.  *
  7607.  * @return array
  7608.  */
  7609. function forum_get_forum_types() {
  7610.     return array ('general'  => get_string('generalforum', 'forum'),
  7611.                   'eachuser' => get_string('eachuserforum', 'forum'),
  7612.                   'single'   => get_string('singleforum', 'forum'),
  7613.                   'qanda'    => get_string('qandaforum', 'forum'),
  7614.                   'blog'     => get_string('blogforum', 'forum'));
  7615. }
  7616.  
  7617. /**
  7618.  * Returns array of all forum layout modes
  7619.  *
  7620.  * @return array
  7621.  */
  7622. function forum_get_forum_types_all() {
  7623.     return array ('news'     => get_string('namenews','forum'),
  7624.                   'social'   => get_string('namesocial','forum'),
  7625.                   'general'  => get_string('generalforum', 'forum'),
  7626.                   'eachuser' => get_string('eachuserforum', 'forum'),
  7627.                   'single'   => get_string('singleforum', 'forum'),
  7628.                   'qanda'    => get_string('qandaforum', 'forum'),
  7629.                   'blog'     => get_string('blogforum', 'forum'));
  7630. }
  7631.  
  7632. /**
  7633.  * Returns array of forum open modes
  7634.  *
  7635.  * @return array
  7636.  */
  7637. function forum_get_open_modes() {
  7638.     return array ('2' => get_string('openmode2', 'forum'),
  7639.                   '1' => get_string('openmode1', 'forum'),
  7640.                   '0' => get_string('openmode0', 'forum') );
  7641. }
  7642.  
  7643. /**
  7644.  * Returns all other caps used in module
  7645.  *
  7646.  * @return array
  7647.  */
  7648. function forum_get_extra_capabilities() {
  7649.     return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/site:trustcontent', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate');
  7650. }
  7651.  
  7652.  
  7653. /**
  7654.  * This function is used to extend the global navigation by add forum nodes if there
  7655.  * is relevant content.
  7656.  *
  7657.  * @param navigation_node $navref
  7658.  * @param stdClass $course
  7659.  * @param stdClass $module
  7660.  * @param stdClass $cm
  7661.  */
  7662. /*************************************************
  7663. function forum_extend_navigation($navref, $course, $module, $cm) {
  7664.     global $CFG, $OUTPUT, $USER;
  7665.  
  7666.     $limit = 5;
  7667.  
  7668.     $discussions = forum_get_discussions($cm,"d.timemodified DESC", false, -1, $limit);
  7669.     $discussioncount = forum_get_discussions_count($cm);
  7670.     if (!is_array($discussions) || count($discussions)==0) {
  7671.         return;
  7672.     }
  7673.     $discussionnode = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')');
  7674.     $discussionnode->mainnavonly = true;
  7675.     $discussionnode->display = false; // Do not display on navigation (only on navbar)
  7676.  
  7677.     foreach ($discussions as $discussion) {
  7678.         $icon = new pix_icon('i/feedback', '');
  7679.         $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$discussion->discussion));
  7680.         $discussionnode->add($discussion->subject, $url, navigation_node::TYPE_SETTING, null, null, $icon);
  7681.     }
  7682.  
  7683.     if ($discussioncount > count($discussions)) {
  7684.         if (!empty($navref->action)) {
  7685.             $url = $navref->action;
  7686.         } else {
  7687.             $url = new moodle_url('/mod/forum/view.php', array('id'=>$cm->id));
  7688.         }
  7689.         $discussionnode->add(get_string('viewalldiscussions', 'forum'), $url, navigation_node::TYPE_SETTING, null, null, $icon);
  7690.     }
  7691.  
  7692.     $index = 0;
  7693.     $recentposts = array();
  7694.     $lastlogin = time() - COURSE_MAX_RECENT_PERIOD;
  7695.     if (!isguestuser() and !empty($USER->lastcourseaccess[$course->id])) {
  7696.         if ($USER->lastcourseaccess[$course->id] > $lastlogin) {
  7697.             $lastlogin = $USER->lastcourseaccess[$course->id];
  7698.         }
  7699.     }
  7700.     forum_get_recent_mod_activity($recentposts, $index, $lastlogin, $course->id, $cm->id);
  7701.  
  7702.     if (is_array($recentposts) && count($recentposts)>0) {
  7703.         $recentnode = $navref->add(get_string('recentactivity').' ('.count($recentposts).')');
  7704.         $recentnode->mainnavonly = true;
  7705.         $recentnode->display = false;
  7706.         foreach ($recentposts as $post) {
  7707.             $icon = new pix_icon('i/feedback', '');
  7708.             $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->content->discussion));
  7709.             $title = $post->content->subject."\n".userdate($post->timestamp, get_string('strftimerecent', 'langconfig'))."\n".$post->user->firstname.' '.$post->user->lastname;
  7710.             $recentnode->add($title, $url, navigation_node::TYPE_SETTING, null, null, $icon);
  7711.         }
  7712.     }
  7713. }
  7714. *************************/
  7715.  
  7716. /**
  7717.  * Adds module specific settings to the settings block
  7718.  *
  7719.  * @param settings_navigation $settings The settings navigation object
  7720.  * @param navigation_node $forumnode The node to add module settings to
  7721.  */
  7722. function forum_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $forumnode) {
  7723.     global $USER, $PAGE, $CFG, $DB, $OUTPUT;
  7724.  
  7725.     $forumobject = $DB->get_record("forum", array("id" => $PAGE->cm->instance));
  7726.     if (empty($PAGE->cm->context)) {
  7727.         $PAGE->cm->context = context_module::instance($PAGE->cm->instance);
  7728.     }
  7729.  
  7730.     // for some actions you need to be enrolled, beiing admin is not enough sometimes here
  7731.     $enrolled = is_enrolled($PAGE->cm->context, $USER, '', false);
  7732.     $activeenrolled = is_enrolled($PAGE->cm->context, $USER, '', true);
  7733.  
  7734.     $canmanage  = has_capability('mod/forum:managesubscriptions', $PAGE->cm->context);
  7735.     $subscriptionmode = forum_get_forcesubscribed($forumobject);
  7736.     $cansubscribe = ($activeenrolled && $subscriptionmode != FORUM_FORCESUBSCRIBE && ($subscriptionmode != FORUM_DISALLOWSUBSCRIBE || $canmanage));
  7737.  
  7738.     if ($canmanage) {
  7739.         $mode = $forumnode->add(get_string('subscriptionmode', 'forum'), null, navigation_node::TYPE_CONTAINER);
  7740.  
  7741.         $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);
  7742.         $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);
  7743.         $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);
  7744.         $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);
  7745.  
  7746.         switch ($subscriptionmode) {
  7747.             case FORUM_CHOOSESUBSCRIBE : // 0
  7748.                 $allowchoice->action = null;
  7749.                 $allowchoice->add_class('activesetting');
  7750.                 break;
  7751.             case FORUM_FORCESUBSCRIBE : // 1
  7752.                 $forceforever->action = null;
  7753.                 $forceforever->add_class('activesetting');
  7754.                 break;
  7755.             case FORUM_INITIALSUBSCRIBE : // 2
  7756.                 $forceinitially->action = null;
  7757.                 $forceinitially->add_class('activesetting');
  7758.                 break;
  7759.             case FORUM_DISALLOWSUBSCRIBE : // 3
  7760.                 $disallowchoice->action = null;
  7761.                 $disallowchoice->add_class('activesetting');
  7762.                 break;
  7763.         }
  7764.  
  7765.     } else if ($activeenrolled) {
  7766.  
  7767.         switch ($subscriptionmode) {
  7768.             case FORUM_CHOOSESUBSCRIBE : // 0
  7769.                 $notenode = $forumnode->add(get_string('subscriptionoptional', 'forum'));
  7770.                 break;
  7771.             case FORUM_FORCESUBSCRIBE : // 1
  7772.                 $notenode = $forumnode->add(get_string('subscriptionforced', 'forum'));
  7773.                 break;
  7774.             case FORUM_INITIALSUBSCRIBE : // 2
  7775.                 $notenode = $forumnode->add(get_string('subscriptionauto', 'forum'));
  7776.                 break;
  7777.             case FORUM_DISALLOWSUBSCRIBE : // 3
  7778.                 $notenode = $forumnode->add(get_string('subscriptiondisabled', 'forum'));
  7779.                 break;
  7780.         }
  7781.     }
  7782.  
  7783.     if ($cansubscribe) {
  7784.         if (forum_is_subscribed($USER->id, $forumobject)) {
  7785.             $linktext = get_string('unsubscribe', 'forum');
  7786.         } else {
  7787.             $linktext = get_string('subscribe', 'forum');
  7788.         }
  7789.         $url = new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'sesskey'=>sesskey()));
  7790.         $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING);
  7791.     }
  7792.  
  7793.     if (has_capability('mod/forum:viewsubscribers', $PAGE->cm->context)){
  7794.         $url = new moodle_url('/mod/forum/subscribers.php', array('id'=>$forumobject->id));
  7795.         $forumnode->add(get_string('showsubscribers', 'forum'), $url, navigation_node::TYPE_SETTING);
  7796.     }
  7797.  
  7798.     if ($enrolled && forum_tp_can_track_forums($forumobject)) { // keep tracking info for users with suspended enrolments
  7799.         if ($forumobject->trackingtype != FORUM_TRACKING_OPTIONAL) {
  7800.             //tracking forced on or off in forum settings so dont provide a link here to change it
  7801.             //could add unclickable text like for forced subscription but not sure this justifies adding another menu item
  7802.         } else {
  7803.             if (forum_tp_is_tracked($forumobject)) {
  7804.                 $linktext = get_string('notrackforum', 'forum');
  7805.             } else {
  7806.                 $linktext = get_string('trackforum', 'forum');
  7807.             }
  7808.             $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forumobject->id));
  7809.             $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING);
  7810.         }
  7811.     }
  7812.  
  7813.     if (!isloggedin() && $PAGE->course->id == SITEID) {
  7814.         $userid = guest_user()->id;
  7815.     } else {
  7816.         $userid = $USER->id;
  7817.     }
  7818.  
  7819.     $hascourseaccess = ($PAGE->course->id == SITEID) || can_access_course($PAGE->course, $userid);
  7820.     $enablerssfeeds = !empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds);
  7821.  
  7822.     if ($enablerssfeeds && $forumobject->rsstype && $forumobject->rssarticles && $hascourseaccess) {
  7823.  
  7824.         if (!function_exists('rss_get_url')) {
  7825.             require_once("$CFG->libdir/rsslib.php");
  7826.         }
  7827.  
  7828.         if ($forumobject->rsstype == 1) {
  7829.             $string = get_string('rsssubscriberssdiscussions','forum');
  7830.         } else {
  7831.             $string = get_string('rsssubscriberssposts','forum');
  7832.         }
  7833.  
  7834.         $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $userid, "mod_forum", $forumobject->id));
  7835.         $forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
  7836.     }
  7837. }
  7838.  
  7839. /**
  7840.  * Abstract class used by forum subscriber selection controls
  7841.  * @package mod-forum
  7842.  * @copyright 2009 Sam Hemelryk
  7843.  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7844.  */
  7845. abstract class forum_subscriber_selector_base extends user_selector_base {
  7846.  
  7847.     /**
  7848.      * The id of the forum this selector is being used for
  7849.      * @var int
  7850.      */
  7851.     protected $forumid = null;
  7852.     /**
  7853.      * The context of the forum this selector is being used for
  7854.      * @var object
  7855.      */
  7856.     protected $context = null;
  7857.     /**
  7858.      * The id of the current group
  7859.      * @var int
  7860.      */
  7861.     protected $currentgroup = null;
  7862.  
  7863.     /**
  7864.      * Constructor method
  7865.      * @param string $name
  7866.      * @param array $options
  7867.      */
  7868.     public function __construct($name, $options) {
  7869.         $options['accesscontext'] = $options['context'];
  7870.         parent::__construct($name, $options);
  7871.         if (isset($options['context'])) {
  7872.             $this->context = $options['context'];
  7873.         }
  7874.         if (isset($options['currentgroup'])) {
  7875.             $this->currentgroup = $options['currentgroup'];
  7876.         }
  7877.         if (isset($options['forumid'])) {
  7878.             $this->forumid = $options['forumid'];
  7879.         }
  7880.     }
  7881.  
  7882.     /**
  7883.      * Returns an array of options to seralise and store for searches
  7884.      *
  7885.      * @return array
  7886.      */
  7887.     protected function get_options() {
  7888.         global $CFG;
  7889.         $options = parent::get_options();
  7890.         $options['file'] =  substr(__FILE__, strlen($CFG->dirroot.'/'));
  7891.         $options['context'] = $this->context;
  7892.         $options['currentgroup'] = $this->currentgroup;
  7893.         $options['forumid'] = $this->forumid;
  7894.         return $options;
  7895.     }
  7896.  
  7897. }
  7898.  
  7899. /**
  7900.  * A user selector control for potential subscribers to the selected forum
  7901.  * @package mod-forum
  7902.  * @copyright 2009 Sam Hemelryk
  7903.  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7904.  */
  7905. class forum_potential_subscriber_selector extends forum_subscriber_selector_base {
  7906.     /**
  7907.      * If set to true EVERYONE in this course is force subscribed to this forum
  7908.      * @var bool
  7909.      */
  7910.     protected $forcesubscribed = false;
  7911.     /**
  7912.      * Can be used to store existing subscribers so that they can be removed from
  7913.      * the potential subscribers list
  7914.      */
  7915.     protected $existingsubscribers = array();
  7916.  
  7917.     /**
  7918.      * Constructor method
  7919.      * @param string $name
  7920.      * @param array $options
  7921.      */
  7922.     public function __construct($name, $options) {
  7923.         parent::__construct($name, $options);
  7924.         if (isset($options['forcesubscribed'])) {
  7925.             $this->forcesubscribed=true;
  7926.         }
  7927.     }
  7928.  
  7929.     /**
  7930.      * Returns an arary of options for this control
  7931.      * @return array
  7932.      */
  7933.     protected function get_options() {
  7934.         $options = parent::get_options();
  7935.         if ($this->forcesubscribed===true) {
  7936.             $options['forcesubscribed']=1;
  7937.         }
  7938.         return $options;
  7939.     }
  7940.  
  7941.     /**
  7942.      * Finds all potential users
  7943.      *
  7944.      * Potential subscribers are all enroled users who are not already subscribed.
  7945.      *
  7946.      * @param string $search
  7947.      * @return array
  7948.      */
  7949.     public function find_users($search) {
  7950.         global $DB;
  7951.  
  7952.         $whereconditions = array();
  7953.         list($wherecondition, $params) = $this->search_sql($search, 'u');
  7954.         if ($wherecondition) {
  7955.             $whereconditions[] = $wherecondition;
  7956.         }
  7957.  
  7958.         if (!$this->forcesubscribed) {
  7959.             $existingids = array();
  7960.             foreach ($this->existingsubscribers as $group) {
  7961.                 foreach ($group as $user) {
  7962.                     $existingids[$user->id] = 1;
  7963.                 }
  7964.             }
  7965.             if ($existingids) {
  7966.                 list($usertest, $userparams) = $DB->get_in_or_equal(
  7967.                         array_keys($existingids), SQL_PARAMS_NAMED, 'existing', false);
  7968.                 $whereconditions[] = 'u.id ' . $usertest;
  7969.                 $params = array_merge($params, $userparams);
  7970.             }
  7971.         }
  7972.  
  7973.         if ($whereconditions) {
  7974.             $wherecondition = 'WHERE ' . implode(' AND ', $whereconditions);
  7975.         }
  7976.  
  7977.         list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true);
  7978.         $params = array_merge($params, $eparams);
  7979.  
  7980.         $fields      = 'SELECT ' . $this->required_fields_sql('u');
  7981.         $countfields = 'SELECT COUNT(u.id)';
  7982.  
  7983.         $sql = " FROM {user} u
  7984.                 JOIN ($esql) je ON je.id = u.id
  7985.                      $wherecondition";
  7986.  
  7987.         list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext);
  7988.         $order = ' ORDER BY ' . $sort;
  7989.  
  7990.         // Check to see if there are too many to show sensibly.
  7991.         if (!$this->is_validating()) {
  7992.             $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
  7993.             if ($potentialmemberscount > $this->maxusersperpage) {
  7994.                 return $this->too_many_results($search, $potentialmemberscount);
  7995.             }
  7996.         }
  7997.  
  7998.         // If not, show them.
  7999.         $availableusers = $DB->get_records_sql($fields . $sql . $order, array_merge($params, $sortparams));
  8000.  
  8001.         if (empty($availableusers)) {
  8002.             return array();
  8003.         }
  8004.  
  8005.         if ($this->forcesubscribed) {
  8006.             return array(get_string("existingsubscribers", 'forum') => $availableusers);
  8007.         } else {
  8008.             return array(get_string("potentialsubscribers", 'forum') => $availableusers);
  8009.         }
  8010.     }
  8011.  
  8012.     /**
  8013.      * Sets the existing subscribers
  8014.      * @param array $users
  8015.      */
  8016.     public function set_existing_subscribers(array $users) {
  8017.         $this->existingsubscribers = $users;
  8018.     }
  8019.  
  8020.     /**
  8021.      * Sets this forum as force subscribed or not
  8022.      */
  8023.     public function set_force_subscribed($setting=true) {
  8024.         $this->forcesubscribed = true;
  8025.     }
  8026. }
  8027.  
  8028. /**
  8029.  * User selector control for removing subscribed users
  8030.  * @package mod-forum
  8031.  * @copyright 2009 Sam Hemelryk
  8032.  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  8033.  */
  8034. class forum_existing_subscriber_selector extends forum_subscriber_selector_base {
  8035.  
  8036.     /**
  8037.      * Finds all subscribed users
  8038.      *
  8039.      * @param string $search
  8040.      * @return array
  8041.      */
  8042.     public function find_users($search) {
  8043.         global $DB;
  8044.         list($wherecondition, $params) = $this->search_sql($search, 'u');
  8045.         $params['forumid'] = $this->forumid;
  8046.  
  8047.         // only active enrolled or everybody on the frontpage
  8048.         list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true);
  8049.         $fields = $this->required_fields_sql('u');
  8050.         list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext);
  8051.         $params = array_merge($params, $eparams, $sortparams);
  8052.  
  8053.         $subscribers = $DB->get_records_sql("SELECT $fields
  8054.                                               FROM {user} u
  8055.                                               JOIN ($esql) je ON je.id = u.id
  8056.                                               JOIN {forum_subscriptions} s ON s.userid = u.id
  8057.                                              WHERE $wherecondition AND s.forum = :forumid
  8058.                                           ORDER BY $sort", $params);
  8059.  
  8060.         return array(get_string("existingsubscribers", 'forum') => $subscribers);
  8061.     }
  8062.  
  8063. }
  8064.  
  8065. /**
  8066.  * Adds information about unread messages, that is only required for the course view page (and
  8067.  * similar), to the course-module object.
  8068.  * @param cm_info $cm Course-module object
  8069.  */
  8070. function forum_cm_info_view(cm_info $cm) {
  8071.     global $CFG;
  8072.  
  8073.     // Get tracking status (once per request)
  8074.     static $initialised;
  8075.     static $usetracking, $strunreadpostsone;
  8076.     if (!isset($initialised)) {
  8077.         if ($usetracking = forum_tp_can_track_forums()) {
  8078.             $strunreadpostsone = get_string('unreadpostsone', 'forum');
  8079.         }
  8080.         $initialised = true;
  8081.     }
  8082.  
  8083.     if ($usetracking) {
  8084.         if ($unread = forum_tp_count_forum_unread_posts($cm, $cm->get_course())) {
  8085.             $out = '<span class="unread"> <a href="' . $cm->get_url() . '">';
  8086.             if ($unread == 1) {
  8087.                 $out .= $strunreadpostsone;
  8088.             } else {
  8089.                 $out .= get_string('unreadpostsnumber', 'forum', $unread);
  8090.             }
  8091.             $out .= '</a></span>';
  8092.             $cm->set_after_link($out);
  8093.         }
  8094.     }
  8095. }
  8096.  
  8097. /**
  8098.  * Return a list of page types
  8099.  * @param string $pagetype current page type
  8100.  * @param stdClass $parentcontext Block's parent context
  8101.  * @param stdClass $currentcontext Current context of block
  8102.  */
  8103. function forum_page_type_list($pagetype, $parentcontext, $currentcontext) {
  8104.     $forum_pagetype = array(
  8105.         'mod-forum-*'=>get_string('page-mod-forum-x', 'forum'),
  8106.         'mod-forum-view'=>get_string('page-mod-forum-view', 'forum'),
  8107.         'mod-forum-discuss'=>get_string('page-mod-forum-discuss', 'forum')
  8108.     );
  8109.     return $forum_pagetype;
  8110. }
  8111.  
  8112. /**
  8113.  * Gets all of the courses where the provided user has posted in a forum.
  8114.  *
  8115.  * @global moodle_database $DB The database connection
  8116.  * @param stdClass $user The user who's posts we are looking for
  8117.  * @param bool $discussionsonly If true only look for discussions started by the user
  8118.  * @param bool $includecontexts If set to trye contexts for the courses will be preloaded
  8119.  * @param int $limitfrom The offset of records to return
  8120.  * @param int $limitnum The number of records to return
  8121.  * @return array An array of courses
  8122.  */
  8123. function forum_get_courses_user_posted_in($user, $discussionsonly = false, $includecontexts = true, $limitfrom = null, $limitnum = null) {
  8124.     global $DB;
  8125.  
  8126.     // If we are only after discussions we need only look at the forum_discussions
  8127.     // table and join to the userid there. If we are looking for posts then we need
  8128.     // to join to the forum_posts table.
  8129.     if (!$discussionsonly) {
  8130.         $subquery = "(SELECT DISTINCT fd.course
  8131.                         FROM {forum_discussions} fd
  8132.                         JOIN {forum_posts} fp ON fp.discussion = fd.id
  8133.                        WHERE fp.userid = :userid )";
  8134.     } else {
  8135.         $subquery= "(SELECT DISTINCT fd.course
  8136.                         FROM {forum_discussions} fd
  8137.                        WHERE fd.userid = :userid )";
  8138.     }
  8139.  
  8140.     $params = array('userid' => $user->id);
  8141.  
  8142.     // Join to the context table so that we can preload contexts if required.
  8143.     if ($includecontexts) {
  8144.         list($ctxselect, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
  8145.     } else {
  8146.         $ctxselect = '';
  8147.         $ctxjoin = '';
  8148.     }
  8149.  
  8150.     // Now we need to get all of the courses to search.
  8151.     // All courses where the user has posted within a forum will be returned.
  8152.     $sql = "SELECT c.* $ctxselect
  8153.            FROM {course} c
  8154.            $ctxjoin
  8155.            WHERE c.id IN ($subquery)";
  8156.     $courses = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
  8157.     if ($includecontexts) {
  8158.         array_map('context_instance_preload', $courses);
  8159.     }
  8160.     return $courses;
  8161. }
  8162.  
  8163. /**
  8164.  * Gets all of the forums a user has posted in for one or more courses.
  8165.  *
  8166.  * @global moodle_database $DB
  8167.  * @param stdClass $user
  8168.  * @param array $courseids An array of courseids to search or if not provided
  8169.  *                       all courses the user has posted within
  8170.  * @param bool $discussionsonly If true then only forums where the user has started
  8171.  *                       a discussion will be returned.
  8172.  * @param int $limitfrom The offset of records to return
  8173.  * @param int $limitnum The number of records to return
  8174.  * @return array An array of forums the user has posted within in the provided courses
  8175.  */
  8176. function forum_get_forums_user_posted_in($user, array $courseids = null, $discussionsonly = false, $limitfrom = null, $limitnum = null) {
  8177.     global $DB;
  8178.  
  8179.     if (!is_null($courseids)) {
  8180.         list($coursewhere, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'courseid');
  8181.         $coursewhere = ' AND f.course '.$coursewhere;
  8182.     } else {
  8183.         $coursewhere = '';
  8184.         $params = array();
  8185.     }
  8186.     $params['userid'] = $user->id;
  8187.     $params['forum'] = 'forum';
  8188.  
  8189.     if ($discussionsonly) {
  8190.         $join = 'JOIN {forum_discussions} ff ON ff.forum = f.id';
  8191.     } else {
  8192.         $join = 'JOIN {forum_discussions} fd ON fd.forum = f.id
  8193.                 JOIN {forum_posts} ff ON ff.discussion = fd.id';
  8194.     }
  8195.  
  8196.     $sql = "SELECT f.*, cm.id AS cmid
  8197.              FROM {forum} f
  8198.              JOIN {course_modules} cm ON cm.instance = f.id
  8199.              JOIN {modules} m ON m.id = cm.module
  8200.              JOIN (
  8201.                  SELECT f.id
  8202.                    FROM {forum} f
  8203.                    {$join}
  8204.                   WHERE ff.userid = :userid
  8205.                GROUP BY f.id
  8206.                   ) j ON j.id = f.id
  8207.             WHERE m.name = :forum
  8208.                 {$coursewhere}";
  8209.  
  8210.     $courseforums = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
  8211.     return $courseforums;
  8212. }
  8213.  
  8214. /**
  8215.  * Returns posts made by the selected user in the requested courses.
  8216.  *
  8217.  * This method can be used to return all of the posts made by the requested user
  8218.  * within the given courses.
  8219.  * For each course the access of the current user and requested user is checked
  8220.  * and then for each post access to the post and forum is checked as well.
  8221.  *
  8222.  * This function is safe to use with usercapabilities.
  8223.  *
  8224.  * @global moodle_database $DB
  8225.  * @param stdClass $user The user whose posts we want to get
  8226.  * @param array $courses The courses to search
  8227.  * @param bool $musthaveaccess If set to true errors will be thrown if the user
  8228.  *                             cannot access one or more of the courses to search
  8229.  * @param bool $discussionsonly If set to true only discussion starting posts
  8230.  *                              will be returned.
  8231.  * @param int $limitfrom The offset of records to return
  8232.  * @param int $limitnum The number of records to return
  8233.  * @return stdClass An object the following properties
  8234.  *               ->totalcount: the total number of posts made by the requested user
  8235.  *                             that the current user can see.
  8236.  *               ->courses: An array of courses the current user can see that the
  8237.  *                          requested user has posted in.
  8238.  *               ->forums: An array of forums relating to the posts returned in the
  8239.  *                         property below.
  8240.  *               ->posts: An array containing the posts to show for this request.
  8241.  */
  8242. function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false, $discussionsonly = false, $limitfrom = 0, $limitnum = 50) {
  8243.     global $DB, $USER, $CFG;
  8244.  
  8245.     $return = new stdClass;
  8246.     $return->totalcount = 0;    // The total number of posts that the current user is able to view
  8247.     $return->courses = array(); // The courses the current user can access
  8248.     $return->forums = array();  // The forums that the current user can access that contain posts
  8249.     $return->posts = array();   // The posts to display
  8250.  
  8251.     // First up a small sanity check. If there are no courses to check we can
  8252.     // return immediately, there is obviously nothing to search.
  8253.     if (empty($courses)) {
  8254.         return $return;
  8255.     }
  8256.  
  8257.     // A couple of quick setups
  8258.     $isloggedin = isloggedin();
  8259.     $isguestuser = $isloggedin && isguestuser();
  8260.     $iscurrentuser = $isloggedin && $USER->id == $user->id;
  8261.  
  8262.     // Checkout whether or not the current user has capabilities over the requested
  8263.     // user and if so they have the capabilities required to view the requested
  8264.     // users content.
  8265.     $usercontext = context_user::instance($user->id, MUST_EXIST);
  8266.     $hascapsonuser = !$iscurrentuser && $DB->record_exists('role_assignments', array('userid' => $USER->id, 'contextid' => $usercontext->id));
  8267.     $hascapsonuser = $hascapsonuser && has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), $usercontext);
  8268.  
  8269.     // Before we actually search each course we need to check the user's access to the
  8270.     // course. If the user doesn't have the appropraite access then we either throw an
  8271.     // error if a particular course was requested or we just skip over the course.
  8272.     foreach ($courses as $course) {
  8273.         $coursecontext = context_course::instance($course->id, MUST_EXIST);
  8274.         if ($iscurrentuser || $hascapsonuser) {
  8275.             // If it is the current user, or the current user has capabilities to the
  8276.             // requested user then all we need to do is check the requested users
  8277.             // current access to the course.
  8278.             // Note: There is no need to check group access or anything of the like
  8279.             // as either the current user is the requested user, or has granted
  8280.             // capabilities on the requested user. Either way they can see what the
  8281.             // requested user posted, although its VERY unlikely in the `parent` situation
  8282.             // that the current user will be able to view the posts in context.
  8283.             if (!is_viewing($coursecontext, $user) && !is_enrolled($coursecontext, $user)) {
  8284.                 // Need to have full access to a course to see the rest of own info
  8285.                 if ($musthaveaccess) {
  8286.                     print_error('errorenrolmentrequired', 'forum');
  8287.                 }
  8288.                 continue;
  8289.             }
  8290.         } else {
  8291.             // Check whether the current user is enrolled or has access to view the course
  8292.             // if they don't we immediately have a problem.
  8293.             if (!can_access_course($course)) {
  8294.                 if ($musthaveaccess) {
  8295.                     print_error('errorenrolmentrequired', 'forum');
  8296.                 }
  8297.                 continue;
  8298.             }
  8299.  
  8300.             // Check whether the requested user is enrolled or has access to view the course
  8301.             // if they don't we immediately have a problem.
  8302.             if (!can_access_course($course, $user)) {
  8303.                 if ($musthaveaccess) {
  8304.                     print_error('notenrolled', 'forum');
  8305.                 }
  8306.                 continue;
  8307.             }
  8308.  
  8309.             // If groups are in use and enforced throughout the course then make sure
  8310.             // we can meet in at least one course level group.
  8311.             // Note that we check if either the current user or the requested user have
  8312.             // the capability to access all groups. This is because with that capability
  8313.             // a user in group A could post in the group B forum. Grrrr.
  8314.             if (groups_get_course_groupmode($course) == SEPARATEGROUPS && $course->groupmodeforce
  8315.               && !has_capability('moodle/site:accessallgroups', $coursecontext) && !has_capability('moodle/site:accessallgroups', $coursecontext, $user->id)) {
  8316.                 // If its the guest user to bad... the guest user cannot access groups
  8317.                 if (!$isloggedin or $isguestuser) {
  8318.                     // do not use require_login() here because we might have already used require_login($course)
  8319.                     if ($musthaveaccess) {
  8320.                         redirect(get_login_url());
  8321.                     }
  8322.                     continue;
  8323.                 }
  8324.                 // Get the groups of the current user
  8325.                 $mygroups = array_keys(groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid, 'g.id, g.name'));
  8326.                 // Get the groups the requested user is a member of
  8327.                 $usergroups = array_keys(groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name'));
  8328.                 // Check whether they are members of the same group. If they are great.
  8329.                 $intersect = array_intersect($mygroups, $usergroups);
  8330.                 if (empty($intersect)) {
  8331.                     // But they're not... if it was a specific course throw an error otherwise
  8332.                     // just skip this course so that it is not searched.
  8333.                     if ($musthaveaccess) {
  8334.                         print_error("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id");
  8335.                     }
  8336.                     continue;
  8337.                 }
  8338.             }
  8339.         }
  8340.         // Woo hoo we got this far which means the current user can search this
  8341.         // this course for the requested user. Although this is only the course accessibility
  8342.         // handling that is complete, the forum accessibility tests are yet to come.
  8343.         $return->courses[$course->id] = $course;
  8344.     }
  8345.     // No longer beed $courses array - lose it not it may be big
  8346.     unset($courses);
  8347.  
  8348.     // Make sure that we have some courses to search
  8349.     if (empty($return->courses)) {
  8350.         // If we don't have any courses to search then the reality is that the current
  8351.         // user doesn't have access to any courses is which the requested user has posted.
  8352.         // Although we do know at this point that the requested user has posts.
  8353.         if ($musthaveaccess) {
  8354.             print_error('permissiondenied');
  8355.         } else {
  8356.             return $return;
  8357.         }
  8358.     }
  8359.  
  8360.     // Next step: Collect all of the forums that we will want to search.
  8361.     // It is important to note that this step isn't actually about searching, it is
  8362.     // about determining which forums we can search by testing accessibility.
  8363.     $forums = forum_get_forums_user_posted_in($user, array_keys($return->courses), $discussionsonly);
  8364.  
  8365.     // Will be used to build the where conditions for the search
  8366.     $forumsearchwhere = array();
  8367.     // Will be used to store the where condition params for the search
  8368.     $forumsearchparams = array();
  8369.     // Will record forums where the user can freely access everything
  8370.     $forumsearchfullaccess = array();
  8371.     // DB caching friendly
  8372.     $now = round(time(), -2);
  8373.     // For each course to search we want to find the forums the user has posted in
  8374.     // and providing the current user can access the forum create a search condition
  8375.     // for the forum to get the requested users posts.
  8376.     foreach ($return->courses as $course) {
  8377.         // Now we need to get the forums
  8378.         $modinfo = get_fast_modinfo($course);
  8379.         if (empty($modinfo->instances['forum'])) {
  8380.             // hmmm, no forums? well at least its easy... skip!
  8381.             continue;
  8382.         }
  8383.         // Iterate
  8384.         foreach ($modinfo->get_instances_of('forum') as $forumid => $cm) {
  8385.             if (!$cm->uservisible or !isset($forums[$forumid])) {
  8386.                 continue;
  8387.             }
  8388.             // Get the forum in question
  8389.             $forum = $forums[$forumid];
  8390.             // This is needed for functionality later on in the forum code....
  8391.             $forum->cm = $cm;
  8392.  
  8393.             // Check that either the current user can view the forum, or that the
  8394.             // current user has capabilities over the requested user and the requested
  8395.             // user can view the discussion
  8396.             if (!has_capability('mod/forum:viewdiscussion', $cm->context) && !($hascapsonuser && has_capability('mod/forum:viewdiscussion', $cm->context, $user->id))) {
  8397.                 continue;
  8398.             }
  8399.  
  8400.             // This will contain forum specific where clauses
  8401.             $forumsearchselect = array();
  8402.             if (!$iscurrentuser && !$hascapsonuser) {
  8403.                 // Make sure we check group access
  8404.                 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $cm->context)) {
  8405.                     $groups = $modinfo->get_groups($cm->groupingid);
  8406.                     $groups[] = -1;
  8407.                     list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_');
  8408.                     $forumsearchparams = array_merge($forumsearchparams, $groupid_params);
  8409.                     $forumsearchselect[] = "d.groupid $groupid_sql";
  8410.                 }
  8411.  
  8412.                 // hidden timed discussions
  8413.                 if (!empty($CFG->forum_enabletimedposts) && !has_capability('mod/forum:viewhiddentimedposts', $cm->context)) {
  8414.                     $forumsearchselect[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
  8415.                     $forumsearchparams['userid'.$forumid] = $user->id;
  8416.                     $forumsearchparams['timestart'.$forumid] = $now;
  8417.                     $forumsearchparams['timeend'.$forumid] = $now;
  8418.                 }
  8419.  
  8420.                 // qanda access
  8421.                 if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $cm->context)) {
  8422.                     // We need to check whether the user has posted in the qanda forum.
  8423.                     $discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $user->id);
  8424.                     if (!empty($discussionspostedin)) {
  8425.                         $forumonlydiscussions = array();  // Holds discussion ids for the discussions the user is allowed to see in this forum.
  8426.                         foreach ($discussionspostedin as $d) {
  8427.                             $forumonlydiscussions[] = $d->id;
  8428.                         }
  8429.                         list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forumonlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_');
  8430.                         $forumsearchparams = array_merge($forumsearchparams, $discussionid_params);
  8431.                         $forumsearchselect[] = "(d.id $discussionid_sql OR p.parent = 0)";
  8432.                     } else {
  8433.                         $forumsearchselect[] = "p.parent = 0";
  8434.                     }
  8435.  
  8436.                 }
  8437.  
  8438.                 if (count($forumsearchselect) > 0) {
  8439.                     $forumsearchwhere[] = "(d.forum = :forum{$forumid} AND ".implode(" AND ", $forumsearchselect).")";
  8440.                     $forumsearchparams['forum'.$forumid] = $forumid;
  8441.                 } else {
  8442.                     $forumsearchfullaccess[] = $forumid;
  8443.                 }
  8444.             } else {
  8445.                 // The current user/parent can see all of their own posts
  8446.                 $forumsearchfullaccess[] = $forumid;
  8447.             }
  8448.         }
  8449.     }
  8450.  
  8451.     // If we dont have any search conditions, and we don't have any forums where
  8452.     // the user has full access then we just return the default.
  8453.     if (empty($forumsearchwhere) && empty($forumsearchfullaccess)) {
  8454.         return $return;
  8455.     }
  8456.  
  8457.     // Prepare a where condition for the full access forums.
  8458.     if (count($forumsearchfullaccess) > 0) {
  8459.         list($fullidsql, $fullidparams) = $DB->get_in_or_equal($forumsearchfullaccess, SQL_PARAMS_NAMED, 'fula');
  8460.         $forumsearchparams = array_merge($forumsearchparams, $fullidparams);
  8461.         $forumsearchwhere[] = "(d.forum $fullidsql)";
  8462.     }
  8463.  
  8464.     // Prepare SQL to both count and search.
  8465.     // We alias user.id to useridx because we forum_posts already has a userid field and not aliasing this would break
  8466.     // oracle and mssql.
  8467.     $userfields = user_picture::fields('u', null, 'useridx');
  8468.     $countsql = 'SELECT COUNT(*) ';
  8469.     $selectsql = 'SELECT p.*, d.forum, d.name AS discussionname, '.$userfields.' ';
  8470.     $wheresql = implode(" OR ", $forumsearchwhere);
  8471.  
  8472.     if ($discussionsonly) {
  8473.         if ($wheresql == '') {
  8474.             $wheresql = 'p.parent = 0';
  8475.         } else {
  8476.             $wheresql = 'p.parent = 0 AND ('.$wheresql.')';
  8477.         }
  8478.     }
  8479.  
  8480.     $sql = "FROM {forum_posts} p
  8481.            JOIN {forum_discussions} d ON d.id = p.discussion
  8482.            JOIN {user} u ON u.id = p.userid
  8483.           WHERE ($wheresql)
  8484.             AND p.userid = :userid ";
  8485.     $orderby = "ORDER BY p.modified DESC";
  8486.     $forumsearchparams['userid'] = $user->id;
  8487.  
  8488.     // Set the total number posts made by the requested user that the current user can see
  8489.     $return->totalcount = $DB->count_records_sql($countsql.$sql, $forumsearchparams);
  8490.     // Set the collection of posts that has been requested
  8491.     $return->posts = $DB->get_records_sql($selectsql.$sql.$orderby, $forumsearchparams, $limitfrom, $limitnum);
  8492.  
  8493.     // We need to build an array of forums for which posts will be displayed.
  8494.     // We do this here to save the caller needing to retrieve them themselves before
  8495.     // printing these forums posts. Given we have the forums already there is
  8496.     // practically no overhead here.
  8497.     foreach ($return->posts as $post) {
  8498.         if (!array_key_exists($post->forum, $return->forums)) {
  8499.             $return->forums[$post->forum] = $forums[$post->forum];
  8500.         }
  8501.     }
  8502.  
  8503.     return $return;
  8504. }
Add Comment
Please, Sign In to add comment