/** * 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; }