/**
 * Return a list of groups the user belongs to that apply to this forum (same grouping)
 * @param int $userid
 * @param int $forumid
 * @return an array of group lists or an empty array
 */
function get_group_list($userid, $forumid)
{
    global $CFG;
    //$courseid = forum::get_from_id($forumng->id, forum::CLONE_DIRECT)->get_course_id();
    $sql_group = "\nSELECT\n    g.id AS groupid\nFROM\n    {$CFG->prefix}forumng f\n    INNER JOIN {$CFG->prefix}course_modules cm on f.id = cm.instance \n    INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n    INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = {$userid} \n    INNER JOIN {$CFG->prefix}groups g ON gm.groupid = g.id AND g.courseid = cm.course \n    LEFT JOIN {$CFG->prefix}groupings_groups gg ON gg.groupid = g.id AND cm.groupingid = gg.groupingid \nWHERE\n    f.id = {$forumid}\n    AND m.name = 'forumng'\n    AND (cm.groupingid = 0 or gg.id IS NOT NULL)";
    $rs = forum_utils::get_recordset_sql($sql_group);
    $results = array();
    while ($rec = rs_fetch_next_record($rs)) {
        $results[] = $rec->groupid;
    }
    rs_close($rs);
    return $results;
}
 /**
  * Either delete or archive old discussions based on the forum setting
  */
 static function archive_old_discussions()
 {
     global $CFG;
     $now = time();
     $housekeepingquery = " \nFROM \n    {$CFG->prefix}forumng_discussions fd\n    INNER JOIN {$CFG->prefix}forumng_posts fp ON fd.lastpostid = fp.id\n    INNER JOIN {$CFG->prefix}forumng f ON fd.forumid = f.id\nWHERE\n    f.removeafter<>0  AND fd.sticky<>1 AND fp.modified<{$now} - f.removeafter \n";
     $count = forum_utils::count_records_sql("SELECT COUNT(1) {$housekeepingquery}");
     if ($count) {
         mtrace("\nBeginning processing {$count} discussion archiving/deleting requests");
         $housekeepingrs = forum_utils::get_recordset_sql("\nSELECT \n    fd.id AS discussionid, f.id AS forumid, f.removeafter, f.removeto {$housekeepingquery} ORDER BY f.removeto\n            ");
         $targetforum = null;
         $targetcourseid = null;
         $cron_log = '';
         $discussionmovecount = 0;
         $discussiondeletecount = 0;
         while ($rec = rs_fetch_next_record($housekeepingrs)) {
             $discussion = forum_discussion::get_from_id($rec->discussionid, forum::CLONE_DIRECT);
             if ($rec->removeto) {
                 //moving to a different forum
                 $forum = $discussion->get_forum();
                 $course = $forum->get_course();
                 $modinfo = get_fast_modinfo($course);
                 if ($forum->can_archive_forum($modinfo, $cron_log)) {
                     //Do not get the target forum and course id again if the target forum is the same
                     if (!$targetforum || $targetforum->get_id() != $rec->removeto) {
                         $targetforum = forum::get_from_id($rec->removeto, forum::CLONE_DIRECT);
                         $targetforum = $targetforum->get_real_forum();
                     }
                     //target discussion groupid must be the same as the original groupid
                     $targetgroupmode = $targetforum->get_group_mode();
                     $targetgroupid = $targetgroupmode ? $discussion->get_group_id() : null;
                     $discussion->move($targetforum, $targetgroupid);
                     $discussionmovecount++;
                 }
             } else {
                 //Delete all discussions and relevant data permanently
                 $discussion->permanently_delete();
                 $discussiondeletecount++;
             }
         }
         rs_close($housekeepingrs);
         mtrace("\n {$discussionmovecount} discussions have been archived and {$discussiondeletecount} discussions have been deleted permanently.");
     }
 }
 /**
  * Executes a database update in such a way that it will work in MySQL,
  * when the update uses a subquery that refers to the table being updated.
  * @param string $update Update query with the special string %'IN'% at the
  *   point where the IN clause should go, i.e. replacing 'IN (SELECT id ...)' 
  * @param string $inids Query that selects a column (which must be named
  *   id), i.e. 'SELECT id ...'
  */
 public static function update_with_subquery_grrr_mysql($update, $inids)
 {
     global $CFG;
     if (preg_match('~^mysql~', $CFG->dbtype)) {
         // MySQL is a PoS so the update can't directly run (you can't update
         // a table based on a subquery that refers to the table). Instead,
         // we do the same thing but with a separate update using an IN clause.
         // This might theoretically run into problems if you had a really huge
         // set of forums with frequent posts (so that the IN size exceeds
         // MySQL query limit) however the limits appear to be generous enough
         // that this is unlikely.
         $ids = array();
         $rs = forum_utils::get_recordset_sql($inids);
         while ($rec = rs_fetch_next_record($rs)) {
             $ids[] = $rec->id;
         }
         rs_close($rs);
         if (count($ids) > 0) {
             $update = str_replace("%'IN'%", forum_utils::in_or_equals($ids), $update);
             forum_utils::execute_sql($update);
         }
     } else {
         // With a decent database we can do the update and query in one,
         // avoiding the need to transfer an ID list around.
         forum_utils::execute_sql(str_replace("%'IN'%", "IN ({$inids})", $update));
     }
 }
 /**
  * Obtains group info for a user in this discussion. Group info may be
  * cached in the discussion object in order to reduce DB queries.
  * @param int $userid User ID (must be a user who has posts in this discussion)
  *   May be 0 to pre-cache the data without returning anything
  * @param bool $cacheall If true, obtains data for all users in the
  *   discussion and caches it; set false if only one user's information
  *   is likely to be required, to do a single query
  * @return array Array of group objects containing id, name, picture
  *   (empty if none). False if $userid was 0.
  * @throws forum_exception If user is not in this discussion
  */
 public function get_user_groups($userid, $cacheall = true)
 {
     global $CFG;
     // If there is no cached data yet, and we are supposed to cache it,
     // then cache it now
     if (!$this->groupscache && $cacheall) {
         $this->groupscache = array();
         // Get list of users in discussion and initialise empty cache
         $userids = array();
         $this->get_root_post()->list_all_user_ids($userids);
         $userids = array_keys($userids);
         $inorequals = forum_utils::in_or_equals($userids);
         foreach ($userids as $auserid) {
             $this->groupscache[$auserid] = array();
         }
         // Basic IDs
         $courseid = $this->get_forum()->get_course_id();
         $discussionid = $this->get_id();
         // Grouping restriction
         if ($groupingid = $this->get_forum()->get_grouping()) {
             $groupingjoin = "INNER JOIN {$CFG->prefix}groupings_groups gg ON gg.groupid=g.id";
             $groupingcheck = "AND gg.groupingid = {$groupingid}";
         } else {
             $groupingjoin = $groupingcheck = '';
         }
         // Do query
         $rs = forum_utils::get_recordset_sql("\nSELECT\n    gm.userid, g.id, g.name, g.picture, g.hidepicture\nFROM\n    {$CFG->prefix}groups_members gm\n    INNER JOIN {$CFG->prefix}groups g ON g.id=gm.groupid\n    {$groupingjoin}\nWHERE\n    g.courseid={$courseid}\n    {$groupingcheck}\n    AND gm.userid {$inorequals}\n ");
         while ($rec = rs_fetch_next_record($rs)) {
             $auserid = $rec->userid;
             unset($rec->userid);
             $this->groupscache[$auserid][] = $rec;
         }
         rs_close($rs);
         // Update cached version to include this data
         if ($this->incache) {
             $this->cache($this->incache->userid);
         }
     }
     // If caller only wants to cache data, return false
     if (!$userid) {
         return false;
     }
     // If there is cached data, use it
     if ($this->groupscache) {
         if (!array_key_exists($userid, $this->groupscache)) {
             throw new forum_exception("Unknown discussion user");
         }
         return $this->groupscache[$userid];
     }
     // Otherwise make a query just for this user
     $groups = groups_get_all_groups($this->get_forum()->get_course_id(), $userid, $this->get_course_module()->groupingid);
     return $groups ? $groups : array();
 }
 /**
  * Update the forumng_subscription table to incorporate the group subscription feature.
  * @param bool $moodleupdate If this is true, the function is running as part of the 
  *   moodle upgrade.php for Sep 2010 release. In this case, the database queries must
  *   not be changed and other code must work the same way (avoid calls to functions
  *   except Moodle standard ones)
  */
 public function group_subscription_update($moodleupdate = false, $cmid = 0)
 {
     global $CFG;
     forum_utils::start_transaction();
     if ($cmid) {
         //only update one forum
         $optionalquery = "AND cm.id = {$cmid}";
     } else {
         $optionalquery = '';
     }
     // Query get the distinct forums
     $sql_count = "\nSELECT\n    COUNT(DISTINCT cm.id) AS totalnumberforum\nFROM \n    {$CFG->prefix}forumng_subscriptions fs\n    INNER JOIN {$CFG->prefix}course_modules cm on fs.forumid = cm.instance \n    INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n    INNER JOIN {$CFG->prefix}course c on c.id = cm.course \nWHERE \n    discussionid IS NULL AND m.name='forumng' {$optionalquery}\n    AND (CASE WHEN c.groupmodeforce=1 THEN c.groupmode ELSE cm.groupmode END ) = 1";
     //Query lists all subscriptions to forums that have separate groups
     $sql_sub = "\nSELECT\n    cm.id AS cmid, fs.id AS subid, fs.userid, fs.forumid, c.id AS courseid, cm.groupingid \nFROM\n    {$CFG->prefix}forumng_subscriptions fs\n    INNER JOIN {$CFG->prefix}course_modules cm on fs.forumid = cm.instance \n    INNER JOIN {$CFG->prefix}modules m on cm.module = m.id \n    INNER JOIN {$CFG->prefix}course c on c.id = cm.course \nWHERE \n    discussionid IS NULL and m.name='forumng' {$optionalquery}\n    AND (CASE WHEN c.groupmodeforce=1 THEN c.groupmode ELSE cm.groupmode END ) = 1 \nORDER BY cm.id, fs.id";
     //Query lists all groups that the user belongs to from the above query
     $sql_group = "\nSELECT\n    subs.subid, g.id AS groupid\nFROM\n    ({$sql_sub}) subs \n    INNER JOIN {$CFG->prefix}groups_members gm ON gm.userid = subs.userid \n    INNER JOIN {$CFG->prefix}groups g ON gm.groupid = g.id AND g.courseid = subs.courseid \n    LEFT JOIN {$CFG->prefix}groupings_groups gg ON gg.groupid = g.id AND subs.groupingid = gg.groupingid \nWHERE\n    (subs.groupingid = 0 or gg.id IS NOT NULL)\nORDER BY\n    subs.cmid, subs.subid";
     $rs = forum_utils::get_recordset_sql($sql_group);
     $results = array();
     while ($rec = rs_fetch_next_record($rs)) {
         if (!array_key_exists($rec->subid, $results)) {
             $results[$rec->subid] = array();
         }
         $results[$rec->subid][] = $rec->groupid;
     }
     rs_close($rs);
     $rs = forum_utils::get_recordset_sql($sql_sub);
     $lastcmid = 0;
     $forumcount = 1;
     $totalforumcount = 0;
     $totalforumcount = count_records_sql($sql_count);
     while ($rec = rs_fetch_next_record($rs)) {
         if ($lastcmid != $rec->cmid) {
             if ($moodleupdate) {
                 print "Updating the subscriptions {$forumcount}/{$totalforumcount} (current cmid:{$rec->cmid}) <br />";
             }
             $context = get_context_instance(CONTEXT_MODULE, $rec->cmid);
             $aagusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id');
             $aagusers = $aagusers ? $aagusers : array();
             $lastcmid = $rec->cmid;
             $forumcount++;
         }
         if (!array_key_exists($rec->userid, $aagusers)) {
             //Delete the whole forum subscription
             forum_utils::delete_records('forumng_subscriptions', 'id', $rec->subid);
             //check if the subid exists in the results array
             if (array_key_exists($rec->subid, $results)) {
                 foreach ($results[$rec->subid] as $groupid) {
                     $subrecord = new StdClass();
                     $subrecord->userid = $rec->userid;
                     $subrecord->forumid = $rec->forumid;
                     $subrecord->subscribed = 1;
                     $subrecord->groupid = $groupid;
                     forum_utils::insert_record('forumng_subscriptions', $subrecord);
                 }
             }
         }
     }
     forum_utils::finish_transaction();
 }
 /**
  * Splits this post to become a new discussion
  * @param $newsubject
  * @param bool $log True to log action
  * @return int ID of new discussion
  */
 function split($newsubject, $log = true)
 {
     global $CFG;
     $this->require_children();
     // Begin a transaction
     forum_utils::start_transaction();
     $olddiscussion = $this->get_discussion();
     // Create new discussion
     $newest = null;
     $this->find_newest_child($newest);
     $newdiscussionid = $olddiscussion->clone_for_split($this->get_id(), $newest->get_id());
     // Update all child posts
     $list = array();
     $this->list_child_ids($list);
     unset($list[0]);
     // Don't include this post itself
     if (count($list) > 0) {
         $inorequals = forum_utils::in_or_equals($list);
         forum_utils::execute_sql("\nUPDATE\n    {$CFG->prefix}forumng_posts\nSET\n    discussionid = {$newdiscussionid}\nWHERE\n    id {$inorequals}");
     }
     // Update this post
     $changes = new stdClass();
     $changes->id = $this->get_id();
     $changes->subject = addslashes($newsubject);
     $changes->parentpostid = null;
     //When split the post, reset the important to 0 so that it is not highlighted.
     $changes->important = 0;
     // Note don't update modified time, or it makes this post unread,
     // which isn't very helpful
     $changes->discussionid = $newdiscussionid;
     forum_utils::update_record('forumng_posts', $changes);
     // Update read data if relevant
     if (forum::enabled_read_tracking() && $newest->get_modified() >= forum::get_read_tracking_deadline()) {
         $rs = forum_utils::get_recordset_sql("\nSELECT\n    userid, time\nFROM\n    {$CFG->prefix}forumng_read\nWHERE\n    discussionid = " . $olddiscussion->get_id() . "\n    AND time >= " . $this->get_created());
         while ($rec = rs_fetch_next_record($rs)) {
             $rec->discussionid = $newdiscussionid;
             forum_utils::insert_record('forumng_read', $rec);
         }
         rs_close($rs);
     }
     $olddiscussion->possible_lastpost_change();
     // Move attachments
     $olddiscussionfolder = $olddiscussion->get_attachment_folder();
     $newdiscussionfolder = $olddiscussion->get_attachment_folder(0, 0, $newdiscussionid);
     if (is_dir($olddiscussionfolder)) {
         // Put this post back on the list
         $list[0] = $this->get_id();
         // Loop through all posts; move attachments if present
         $madenewfolder = false;
         foreach ($list as $id) {
             $oldfolder = $olddiscussionfolder . '/' . $id;
             $newfolder = $newdiscussionfolder . '/' . $id;
             if (is_dir($oldfolder)) {
                 if (!$madenewfolder) {
                     check_dir_exists($newfolder, true, true);
                     $madenewfolder = true;
                 }
                 forum_utils::rename($oldfolder, $newfolder);
             }
         }
     }
     if ($log) {
         $this->log('split post');
     }
     forum_utils::finish_transaction();
     $this->get_discussion()->uncache();
     // If discussion-based completion is turned on, this may enable someone
     // to complete
     if ($this->get_forum()->get_completion_discussions()) {
         $this->update_completion(true);
     }
     return $newdiscussionid;
 }