static function delete_old_posts() { global $CFG; // Check if deletion is turned off if (empty($CFG->forumng_permanentdeletion)) { return; } mtrace('Beginning forum deleted/edit message cleanup...'); // Work out how long ago things have to have been 'deleted' before we // permanently delete them $deletebefore = time() - $CFG->forumng_permanentdeletion; // Handle all posts which were deleted (that long ago) or which are in // discussions which were deleted (that long ago) $mainquery = "\nFROM\n {$CFG->prefix}forumng_posts fp\n INNER JOIN {$CFG->prefix}forumng_discussions fd ON fd.id = fp.discussionid\n INNER JOIN {$CFG->prefix}forumng f ON fd.forumid = f.id\nWHERE\n (fp.deleted<>0 AND fp.deleted<{$deletebefore})\n OR (fp.oldversion<>0 AND fp.modified<{$deletebefore})\n OR (fd.deleted<>0 AND fd.deleted<{$deletebefore})"; $idquery = "SELECT fp.id {$mainquery} "; $before = microtime(true); mtrace('Message search: ', ''); $count = count_records_sql("SELECT COUNT(1) {$mainquery}"); mtrace(round(microtime(true) - $before, 1) . 's'); if ($count == 0) { mtrace("No old deleted / edited messages to clean up."); } else { mtrace("Permanently deleting {$count} old deleted / edited messages."); } if ($count) { $before = microtime(true); mtrace('Database post deletion: ', ''); forum_utils::start_transaction(); // Delete all ratings forum_utils::execute_sql("DELETE FROM {$CFG->prefix}forumng_ratings WHERE postid IN ({$idquery})"); // Find all attachments $attachmentrecords = forum_utils::get_records_sql("\nSELECT\n fp.id AS postid, fd.id AS discussionid, f.id AS forumid, f.course AS courseid\n{$mainquery}\n AND fp.attachments<>0"); // Delete all posts forum_utils::update_with_subquery_grrr_mysql("DELETE FROM {$CFG->prefix}forumng_posts WHERE id %'IN'%", $idquery); // Now delete all discussions forum_utils::execute_sql("DELETE FROM {$CFG->prefix}forumng_discussions " . "WHERE deleted<>0 AND deleted<{$deletebefore}"); mtrace(round(microtime(true) - $before, 1) . 's'); $before = microtime(true); mtrace('Filesystem attachment deletion: ', ''); // OK, now delete attachments (this is done last in case the db update // failed and gets rolled back) foreach ($attachmentrecords as $attachmentrecord) { $folder = forum_post::get_any_attachment_folder($attachmentrecord->courseid, $attachmentrecord->forumid, $attachmentrecord->discussionid, $attachmentrecord->postid); // Delete post folder if (!remove_dir($folder)) { mtrace("\nError deleting post folder: {$folder}"); // But don't stop because we can't undo earlier changes } } // Get list of all discussions that might need deleting $discussions = array(); foreach ($attachmentrecords as $attachmentrecord) { // This will ensure we have only one entry per discussion $discussions[$attachmentrecord->discussionid] = $attachmentrecord; } // Delete discussion folder only if empty (there might be other // not-yet-deleted posts) foreach ($discussions as $attachmentrecord) { $folder = forum_discussion::get_attachment_folder($attachmentrecord->courseid, $attachmentrecord->forumid, $attachmentrecord->discussionid); $handle = @opendir($folder); if (!$handle) { mtrace("\nError opening discussion folder: {$folder}"); continue; } $gotfiles = false; while (($file = readdir($handle)) !== false) { if ($file != '.' && $file != '..') { $gotfiles = true; break; } } closedir($handle); if (!$gotfiles) { if (!rmdir($folder)) { mtrace("\nError deleting discussion folder: {$folder}"); } } } forum_utils::finish_transaction(); mtrace(round(microtime(true) - $before, 1) . 's'); } }
/** * This special function is required only because of the OU shared * activities system. On the shared activities course, modinfo is not * available. We can provide a fake version, but only if specific IDs * are given * @param object $course Moodle course object * @param array $specificids List of course-module IDs; empty array = all * @return object Moodle modinfo object * @throws forum_exception If this is shared activities course and you're * trying to list all forums on it */ private static function get_modinfo_special($course, $specificids = array()) { global $CFG, $FORUMNG_CACHE; $modinfo = get_fast_modinfo($course); if (class_exists('ouflags') && !count($modinfo->cms)) { // OU shared activities system requires a hack here so that this // can work on the shared activities course, which doesn't have // modinfo. It can only work if specific IDs are listed. if (count($specificids)) { if (!empty($FORUMNG_CACHE->modinfo_special) && $FORUMNG_CACHE->modinfo_special->specificids == $specificids) { return $FORUMNG_CACHE->modinfo_special->modinfo; } $inorequals = forum_utils::in_or_equals($specificids); $modinfo->cms = forum_utils::get_records_sql("\nSELECT\n cm.*, m.name AS modname, f.type AS forumtype\nFROM\n {$CFG->prefix}course_modules cm\n INNER JOIN {$CFG->prefix}modules m ON m.id = cm.module\n INNER JOIN {$CFG->prefix}forumng f ON f.id = cm.instance\nWHERE\n cm.id {$inorequals} AND m.name='forumng'"); $modinfo->instances['forumng'] = $modinfo->cms; $FORUMNG_CACHE->modinfo_special->modinfo = $modinfo; $FORUMNG_CACHE->modinfo_special->specificids = $specificids; } else { throw new forum_exception('Cannot get_course_forums on ' . 'shared activities course without specific ID list'); } } return $modinfo; }
/** * Obtains a list of forums on the given course which can be converted. * The requirements for this are that they must have a supported forum * type and there must not be an existing ForumNG with the same name. * @param object $course * @return array Array of id=>name of convertable forums */ public static function get_convertible_forums($course) { global $CFG; return forum_utils::get_records_sql("\nSELECT cm.id, f.name \nFROM\n {$CFG->prefix}forum f\n INNER JOIN {$CFG->prefix}course_modules cm ON cm.instance=f.id \n AND cm.module = (SELECT id FROM {$CFG->prefix}modules WHERE name='forum')\n LEFT JOIN {$CFG->prefix}forumng fng ON fng.name=f.name AND fng.course=f.course\nWHERE\n cm.course={$course->id} AND f.course={$course->id} \n AND f.type='general'\n AND fng.id IS NULL"); }
/** * Internal function. Queries for posts. * @param string $where Where clause (fp is alias for post table) * @param string $order Sort order; the default is fp.id - note this is preferable * to fp.timecreated because it works correctly if there are two posts in * the same second * @param bool $ratings True if ratings should be included in the query * @param bool $flags True if flags should be included in the query * @param bool $effectivesubjects True if the query should include the * (complicated!) logic to obtain the 'effective subject'. This may result * in additional queries afterward for posts which are very deeply nested. * @param int $userid 0 = current user (at present this is only used for * flags) * @return array Resulting posts as array of Moodle records, empty array * if none */ static function query_posts($where, $order = 'fp.id', $ratings = true, $flags = false, $effectivesubjects = false, $userid = 0, $joindiscussion = false, $discussionsubject = false, $limitfrom = '', $limitnum = '') { global $CFG, $USER; $userid = forum_utils::get_real_userid($userid); // We include ratings if these are enabled, otherwise save the database // some effort and don't bother if ($ratings) { $ratingsquery = ",\n(SELECT AVG(rating) FROM {$CFG->prefix}forumng_ratings\n WHERE postid=fp.id) AS averagerating,\n(SELECT COUNT(1) FROM {$CFG->prefix}forumng_ratings\n WHERE postid=fp.id) AS numratings,\n(SELECT rating FROM {$CFG->prefix}forumng_ratings\n WHERE postid=fp.id AND userid={$USER->id}) AS ownrating"; } else { $ratingsquery = ''; } if ($flags) { $flagsjoin = "\n LEFT JOIN {$CFG->prefix}forumng_flags ff ON ff.postid = fp.id AND ff.userid = {$userid}"; $flagsquery = ",ff.flagged"; } else { $flagsjoin = ''; $flagsquery = ''; } if ($joindiscussion) { $discussionjoin = "\n INNER JOIN {$CFG->prefix}forumng_discussions fd ON fp.discussionid = fd.id"; $discussionquery = ',' . forum_utils::select_discussion_fields('fd'); if ($discussionsubject) { $discussionjoin .= "\n INNER JOIN {$CFG->prefix}forumng_posts fdfp ON fd.postid = fdfp.id"; $discussionquery .= ', fdfp.subject AS fd_subject'; } } else { $discussionjoin = ''; $discussionquery = ''; } if ($effectivesubjects) { $maxdepth = self::PARENTPOST_DEPTH_PER_QUERY; $subjectsjoin = ''; $subjectsquery = ", p{$maxdepth}.parentpostid AS nextparent "; for ($depth = 2; $depth <= $maxdepth; $depth++) { $subjectsquery .= ", p{$depth}.subject AS s{$depth}, p{$depth}.deleted AS d{$depth}"; $prev = 'p' . ($depth - 1); if ($prev == 'p1') { $prev = 'fp'; } $subjectsjoin .= "LEFT JOIN {$CFG->prefix}forumng_posts p{$depth}\n ON p{$depth}.id = {$prev}.parentpostid "; } } else { $subjectsjoin = ''; $subjectsquery = ''; } // Retrieve posts from discussion with incorporated user information // and ratings info if specified $results = forum_utils::get_records_sql("\nSELECT\n fp.*,\n " . forum_utils::select_username_fields('u') . ",\n " . forum_utils::select_username_fields('eu') . ",\n " . forum_utils::select_username_fields('du') . "\n {$ratingsquery}\n {$flagsquery}\n {$subjectsquery}\n {$discussionquery}\nFROM\n {$CFG->prefix}forumng_posts fp\n INNER JOIN {$CFG->prefix}user u ON fp.userid=u.id\n LEFT JOIN {$CFG->prefix}user eu ON fp.edituserid=eu.id\n LEFT JOIN {$CFG->prefix}user du ON fp.deleteuserid=du.id \n {$discussionjoin}\n {$flagsjoin}\n {$subjectsjoin}\nWHERE\n {$where}\nORDER BY\n {$order}\n", $limitfrom, $limitnum); if ($effectivesubjects) { // Figure out the effective subject for each result foreach ($results as $result) { $got = false; if ($result->subject !== null) { $result->effectivesubject = $result->subject; $got = true; continue; } for ($depth = 2; $depth <= $maxdepth; $depth++) { $var = "s{$depth}"; $var2 = "d{$depth}"; if (!$got && $result->{$var} !== null && $result->{$var2} == 0) { $result->effectivesubject = get_string('re', 'forumng', $result->{$var}); $got = true; } unset($result->{$var}); unset($result->{$var2}); } if (!$got) { // Do extra queries to pick up subjects for posts where it // was unknown within the default depth. We can use the // 'nextparent' to get the ID of the parent post of the last // one that we checked already $result->effectivesubject = self::inner_get_recursive_subject($result->nextparent); } } } return $results; }