function make_forumng($courseid, $starttime, $discussions, $posts, $readpercent, $readusers, &$userids, $subscribepercent, $ratingpercent)
{
    $section = get_record('course_sections', 'course', $courseid, 'section', 0);
    forum_utils::start_transaction();
    // Create course modules record
    $mod = new StdClass();
    $mod->course = $courseid;
    $mod->module = get_field('modules', 'id', 'name', 'forumng');
    $mod->section = $section->section;
    // was $section->id; logical but incorrect!
    $mod->added = $starttime;
    $mod->visible = 1;
    // course_modules and course_sections each contain a reference
    // to each other, so we have to update one of them twice.
    // Note: This is unbelievable!!! $mod->section MUST BE section number (not id)
    //       Adds course_module with section number, add_mod_to_section uses
    //       section number (& course id) to get section id, which is returned
    //       course module record then updated to replace section number by id!!!
    if (!($mod->coursemodule = add_course_module($mod))) {
        throw new Exception("Could not add a new course module");
    }
    if (!($sectionid = add_mod_to_section($mod))) {
        throw new Exception("Could not add the new course module to that section");
    }
    // Create forum object
    $forumng = new StdClass();
    static $index = 0;
    $index++;
    $forumng->name = 'Perf test ' . date('Ymd H:j', $starttime) . ' ' . $index;
    $forumng->course = $courseid;
    $forumng->section = $section;
    $forumng->cmidnumber = $mod->coursemodule;
    if (!($forumng->id = forumng_add_instance($forumng))) {
        throw new forum_exception('Failed to add forum');
    }
    // Mark cm object as owning it
    $updatemod = new stdClass();
    $updatemod->id = $mod->coursemodule;
    $updatemod->instance = $forumng->id;
    $updatemod->section = $sectionid;
    forum_utils::update_record('course_modules', $updatemod);
    // Make it be random users included in the forum
    shuffle($userids);
    // OK, forum is created. Let's make discussions
    $forum = forum::get_from_id($forumng->id, forum::CLONE_DIRECT);
    $count = my_random($discussions);
    for ($i = 0; $i < $count; $i++) {
        make_discussion($forum, $posts, my_random_percentage($readpercent) ? $readusers : 0, $userids, $ratingpercent);
    }
    // Add subscribe users
    set_time_limit(200);
    for ($i = 0; $i < $readusers; $i++) {
        if (my_random_percentage($subscribepercent)) {
            $forum->subscribe($userids[$i]);
        }
    }
    forum_utils::finish_transaction();
}
 /**
  * Creates a new ForumNG by copying data (including all messages etc) from
  * an old forum. The old forum will be hidden.
  *
  * Behaviour is undefined if the old forum wasn't eligible for conversion
  * (mod_forumng_utils::get_convertible_forums).
  * @param object $course Moodle course object
  * @param int $forumcmid Old forum to convert
  * @param bool $progress If true, print progress to output
  * @param bool $hide If true, newly-created forum is also hidden
  * @param bool $nodata If true, no user data (posts, subscriptions, etc)
  *   is copied; you only get a forum with same configuration
  * @param bool $insection If true, remeber to create the new forumNG in the same section.
  * @throws mod_forumng_exception If any error occurs
  */
 public static function create_from_old_forum($course, $forumcmid, $progress, $hide, $nodata, $insection = true)
 {
     global $CFG, $DB, $OUTPUT;
     // Start the clock and a database transaction
     $starttime = microtime(true);
     $transaction = $DB->start_delegated_transaction();
     // Note we do not use get_fast_modinfo because it doesn't contain the
     // complete $cm object.
     $cm = $DB->get_record('course_modules', array('id' => $forumcmid), '*', MUST_EXIST);
     $forum = $DB->get_record('forum', array('id' => $cm->instance), '*', MUST_EXIST);
     if ($progress) {
         echo $OUTPUT->heading(s($forum->name), 3);
         print '<ul><li>' . get_string('convert_process_init', 'forumng');
         flush();
     }
     // Hide forum
     $DB->update_record('course_modules', (object) array('id' => $cm->id, 'visible' => 0));
     // Table for changed subscription constants
     $subscriptiontranslate = array(0 => 1, 1 => 3, 2 => 2, 3 => 0);
     // Get, convert, and create forum table data
     $forumng = (object) array('course' => $course->id, 'name' => $forum->name, 'type' => 'general', 'intro' => $forum->intro, 'ratingscale' => $forum->scale, 'ratingfrom' => $forum->assesstimestart, 'ratinguntil' => $forum->assesstimefinish, 'ratingthreshold' => 1, 'grading' => $forum->assessed, 'attachmentmaxbytes' => $forum->maxbytes, 'subscription' => $subscriptiontranslate[$forum->forcesubscribe], 'feedtype' => $forum->rsstype, 'feeditems' => $forum->rssarticles, 'maxpostsperiod' => $forum->blockperiod, 'maxpostsblock' => $forum->blockafter, 'postingfrom' => 0, 'postinguntil' => 0, 'typedata' => null);
     require_once $CFG->dirroot . '/mod/forumng/lib.php';
     // Note: The idnumber is required. We cannot copy it because then there
     // would be a duplicate idnumber. Let's just leave blank, people will
     // have to configure this manually.
     $forumng->cmidnumber = '';
     if (!($newforumngid = forumng_add_instance($forumng))) {
         throw new coding_exception("Failed to add forumng instance");
     }
     $forumng->id = $newforumngid;
     // Create and add course-modules entry
     $newcm = new stdClass();
     $newcm->course = $course->id;
     $newcm->module = $DB->get_field('modules', 'id', array('name' => 'forumng'));
     if (!$newcm->module) {
         throw new coding_exception("Cannot find forumng module id");
     }
     $newcm->instance = $newforumngid;
     $newcm->section = $cm->section;
     $newcm->added = time();
     $newcm->score = $cm->score;
     $newcm->indent = $cm->indent;
     $newcm->visible = 0;
     // Forums are always hidden until finished
     $newcm->groupmode = $cm->groupmode;
     $newcm->groupingid = $cm->groupingid;
     $newcm->idnumber = $cm->idnumber;
     $newcm->completion = $cm->completion;
     $newcm->completiongradeitemnumber = $cm->completiongradeitemnumber;
     $newcm->completionview = $cm->completionview;
     $newcm->availability = $cm->availability;
     // Add
     $newcm->id = $DB->insert_record('course_modules', $newcm);
     $oldcontext = context_module::instance($cm->id);
     $newcontext = context_module::instance($newcm->id);
     // Update section.
     if ($insection) {
         $section = $DB->get_record('course_sections', array('id' => $newcm->section), '*', MUST_EXIST);
         $updatesection = (object) array('id' => $section->id, 'sequence' => str_replace($cm->id, $cm->id . ',' . $newcm->id, $section->sequence));
         if ($updatesection->sequence == $section->sequence) {
             throw new coding_exception("Unable to update sequence");
         }
         $DB->update_record('course_sections', $updatesection);
     }
     // Construct forum object for new forum
     $newforum = self::get_from_id($forumng->id, self::CLONE_DIRECT);
     if ($progress) {
         print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
     }
     if (!$nodata) {
         // Convert subscriptions
         switch ($newforum->get_effective_subscription_option()) {
             case self::SUBSCRIPTION_PERMITTED:
                 if ($progress) {
                     print '<li>' . get_string('convert_process_subscriptions_normal', 'forumng');
                     flush();
                 }
                 // Standard subscription - just copy subscriptions.
                 $rs = $DB->get_recordset('forum_subscriptions', array('forum' => $forum->id));
                 foreach ($rs as $rec) {
                     $DB->insert_record('forumng_subscriptions', (object) array('forumngid' => $forumng->id, 'userid' => $rec->userid, 'subscribed' => 1));
                 }
                 $rs->close();
                 if ($progress) {
                     print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
                 }
                 break;
             case self::SUBSCRIPTION_INITIALLY_SUBSCRIBED:
                 // Initial subscription is handled differently; the old forum
                 // stores all the subscriptions in the database, while in this
                 // forum we only store people who chose to unsubscribe
                 if ($progress) {
                     print '<li>' . get_string('convert_process_subscriptions_initial', 'forumng');
                     flush();
                 }
                 // Get list of those subscribed on old forum.
                 $rs = $DB->get_recordset('forum_subscriptions', array('forum' => $forum->id));
                 $subscribedbefore = array();
                 foreach ($rs as $rec) {
                     $subscribedbefore[$rec->userid] = true;
                 }
                 $rs->close();
                 // Get list of those subscribed on new forum
                 $new = $newforum->get_subscribers();
                 // For anyone in the new list but not the old list, add an
                 // unsubscribe
                 foreach ($new as $user) {
                     if (!array_key_exists($user->id, $subscribedbefore)) {
                         $DB->insert_record('forumng_subscriptions', (object) array('forumngid' => $forumng->id, 'userid' => $user->id, 'subscribed' => 0));
                     }
                 }
                 if ($progress) {
                     print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
                 }
                 break;
         }
         // Convert discussions
         if ($progress) {
             print '<li>' . get_string('convert_process_discussions', 'forumng');
             flush();
         }
         $rsd = $DB->get_recordset('forum_discussions', array('forum' => $forum->id));
         $count = 0;
         foreach ($rsd as $recd) {
             // Convert discussion options
             $newd = (object) array('forumngid' => $forumng->id, 'timestart' => $recd->timestart, 'timeend' => $recd->timeend, 'deleted' => 0, 'locked' => 0, 'sticky' => 0);
             if ($recd->groupid == -1 || !$newcm->groupmode) {
                 $newd->groupid = null;
             } else {
                 $newd->groupid = $recd->groupid;
             }
             // Save discussion
             $newd->id = $DB->insert_record('forumng_discussions', $newd);
             // Convert posts
             $lastposttime = -1;
             $discussionupdate = (object) array('id' => $newd->id);
             $postids = array();
             // From old post id to new post id.
             $parentposts = array();
             // From new post id to old parent id.
             $subjects = array();
             // From new id to subject text (no slashes).
             $rsp = $DB->get_recordset('forum_posts', array('discussion' => $recd->id));
             foreach ($rsp as $recp) {
                 // Convert post
                 $newp = (object) array('discussionid' => $newd->id, 'userid' => $recp->userid, 'created' => $recp->created, 'modified' => $recp->modified, 'deleted' => 0, 'deleteuserid' => null, 'mailstate' => self::MAILSTATE_DIGESTED, 'oldversion' => 0, 'edituserid' => null, 'subject' => $recp->subject, 'message' => $recp->message, 'messageformat' => $recp->messageformat, 'important' => 0);
                 // Are there any attachments?
                 $attachments = array();
                 // Standard forum uses attachment field for filename
                 if ($recp->attachment) {
                     $attachments[] = $recp->attachment;
                 }
                 $newp->attachments = count($attachments) ? 1 : 0;
                 // Add record
                 $newp->id = $DB->insert_record('forumng_posts', $newp);
                 // Remember details for later parent update
                 $postids[$recp->id] = $newp->id;
                 if ($recp->parent) {
                     $parentposts[$newp->id] = $recp->parent;
                 } else {
                     $discussionupdate->postid = $newp->id;
                 }
                 if ($newp->created > $lastposttime) {
                     $discussionupdate->lastpostid = $newp->id;
                 }
                 $subjects[$newp->id] = $recp->subject;
                 // Copy attachments.
                 $fo = get_file_storage();
                 $filesok = 0;
                 $filesfailed = 0;
                 $oldattachs = $fo->get_area_files($oldcontext->id, 'mod_forum', 'attachment', $recp->id, 'itemid', false);
                 foreach ($oldattachs as $attachment) {
                     $filerecord = array('component' => 'mod_forumng', 'itemid' => $newp->id, 'contextid' => $newcontext->id);
                     $fo->create_file_from_storedfile($filerecord, $attachment);
                 }
                 $oldimgs = $fo->get_area_files($oldcontext->id, 'mod_forum', 'message', $recp->id, 'itemid', false);
                 foreach ($oldimgs as $attachment) {
                     $filerecord = array('component' => 'mod_forumng', 'itemid' => $newp->id, 'contextid' => $newcontext->id);
                     $fo->create_file_from_storedfile($filerecord, $attachment);
                 }
                 // Convert ratings.
                 /*if ($forumng->ratingscale) {
                       // TODO: Support grades -> ratings.
                       $rsr = $DB->get_recordset('forum_ratings',
                               array('post' => $recp->id));
                       foreach ($rsr as $recr) {
                           $DB->insert_record('forumng_ratings', (object)array(
                               'postid' =>  $newp->id,
                               'userid' => $recr->userid,
                               'time' => $recr->time,
                               'rating' => $recr->rating));
                       }
                       $rsr->close();
                   }*/
             }
             $rsp->close();
             // Update parent numbers
             $newparentids = array();
             foreach ($parentposts as $newid => $oldparentid) {
                 if (!array_key_exists($oldparentid, $postids)) {
                     throw new coding_exception("Unknown parent post {$oldparentid}");
                 }
                 $newparentid = $postids[$oldparentid];
                 $DB->update_record('forumng_posts', (object) array('id' => $newid, 'parentpostid' => $newparentid));
                 $newparentids[$newid] = $newparentid;
             }
             // Update subjects
             $removesubjects = array();
             // Array of ints to cancel subjects
             foreach ($newparentids as $newid => $newparentid) {
                 $subject = $subjects[$newid];
                 $parentsubject = $subjects[$newparentid];
                 if ($subject && ($subject == get_string('re', 'forum') . ' ' . $parentsubject || $subject == $parentsubject)) {
                     $removesubjects[] = $newid;
                 }
             }
             if (count($removesubjects)) {
                 list($in, $inparams) = mod_forumng_utils::get_in_array_sql('id', $removesubjects);
                 $DB->execute("UPDATE {forumng_posts} SET subject = NULL WHERE {$in}", $inparams);
             }
             // Update first/last post numbers
             $DB->update_record('forumng_discussions', $discussionupdate);
             // Convert read data
             $rsr = $DB->get_recordset_sql("\nSELECT\n    userid, MAX(lastread) AS lastread\nFROM\n    {forum_read}\nWHERE\n    discussionid = ?\nGROUP BY\n    userid", array($recd->id));
             foreach ($rsr as $recr) {
                 $DB->insert_record('forumng_read', (object) array('discussionid' => $newd->id, 'userid' => $recr->userid, 'time' => $recr->lastread));
             }
             $rsr->close();
             // Display dot for each discussion
             if ($progress) {
                 print '.';
                 $count++;
                 if ($count % 10 == 0) {
                     print $count;
                 }
                 flush();
             }
         }
         $rsd->close();
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Show forum
     if (!$hide && $cm->visible) {
         if ($progress) {
             print '<li>' . get_string('convert_process_show', 'forumng');
             flush();
         }
         $updatecm = (object) array('id' => $newcm->id, 'visible' => 1);
         $DB->update_record('course_modules', $updatecm);
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Transfer role assignments
     $roles = $DB->get_records('role_assignments', array('contextid' => $oldcontext->id));
     if ($roles) {
         if ($progress) {
             print '<li>' . get_string('convert_process_assignments', 'forumng');
             flush();
         }
         foreach ($roles as $role) {
             $newrole = $role;
             $newrole->contextid = $newcontext->id;
             $newrole->enrol = $newrole->enrol;
             $DB->insert_record('role_assignments', $newrole);
         }
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Transfer capabilities
     $capabilities = array('moodle/course:viewhiddenactivities' => 'moodle/course:viewhiddenactivities', 'moodle/site:accessallgroups' => 'moodle/site:accessallgroups', 'moodle/site:trustcontent' => 'moodle/site:trustcontent', 'moodle/site:viewfullnames' => 'moodle/site:viewfullnames', 'mod/forum:viewdiscussion' => 'mod/forumng:viewdiscussion', 'mod/forum:startdiscussion' => 'mod/forumng:startdiscussion', 'mod/forum:replypost' => 'mod/forumng:replypost', 'mod/forum:viewrating' => 'mod/forumng:viewrating', 'mod/forum:viewanyrating' => 'mod/forumng:viewanyrating', 'mod/forum:rate' => 'mod/forumng:rate', 'mod/forum:createattachment' => 'mod/forumng:createattachment', 'mod/forum:deleteanypost' => 'mod/forumng:deleteanypost', 'mod/forum:splitdiscussions' => 'mod/forumng:splitdiscussions', 'mod/forum:movediscussions' => 'mod/forumng:movediscussions', 'mod/forum:editanypost' => 'mod/forumng:editanypost', 'mod/forum:viewsubscribers' => 'mod/forumng:viewsubscribers', 'mod/forum:managesubscriptions' => 'mod/forumng:managesubscriptions', 'mod/forum:viewhiddentimedposts' => 'mod/forumng:viewallposts');
     $caps = $DB->get_records('role_capabilities', array('contextid' => $oldcontext->id));
     if ($caps) {
         if ($progress) {
             print '<li>' . get_string('convert_process_overrides', 'forumng');
             flush();
         }
         foreach ($caps as $cap) {
             foreach ($capabilities as $key => $capability) {
                 if ($cap->capability != $key) {
                     continue;
                 }
                 $newcap = $cap;
                 $newcap->contextid = $newcontext->id;
                 $newcap->capability = $capability;
                 $newcap->capability = $newcap->capability;
                 $DB->insert_record('role_capabilities', $newcap);
             }
         }
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Do course cache
     rebuild_course_cache($course->id, true);
     // Update search data
     if (self::search_installed()) {
         if ($progress) {
             print '<li>' . get_string('convert_process_search', 'forumng') . '</li>';
             flush();
         }
         self::search_update_all($progress, $course->id, $newcm->id);
     }
     if ($progress) {
         print '<li>' . get_string('convert_process_update_subscriptions', 'forumng');
         flush();
     }
     self::group_subscription_update(false, $newcm->id);
     if ($progress) {
         print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
     }
     $transaction->allow_commit();
     if ($progress) {
         $a = (object) array('seconds' => round(microtime(true) - $starttime, 1), 'link' => '<a href="view.php?id=' . $newcm->id . '">' . get_string('convert_newforum', 'forumng') . '</a>');
         print '</ul><p>' . get_string('convert_process_complete', 'forumng', $a) . '</p>';
     }
 }
 /**
  * Creates a new ForumNG by copying data (including all messages etc) from
  * an old forum. The old forum will be hidden.
  *
  * Behaviour is undefined if the old forum wasn't eligible for conversion
  * (forum_utils::get_convertible_forums).
  * @param object $course Moodle course object
  * @param int $forumcmid Old forum to convert
  * @param bool $progress If true, print progress to output
  * @param bool $hide If true, newly-created forum is also hidden
  * @param bool $nodata If true, no user data (posts, subscriptions, etc)
  *   is copied; you only get a forum with same configuration
  * @param bool $insection If true, remeber to create the new forumNG in the same section.
  * @throws forum_exception If any error occurs
  */
 public static function create_from_old_forum($course, $forumcmid, $progress, $hide, $nodata, $insection = true)
 {
     global $CFG;
     // Start the clock and a database transaction
     $starttime = microtime(true);
     forum_utils::start_transaction();
     // Note we do not use get_fast_modinfo because it doesn't contain the
     // complete $cm object.
     $cm = forum_utils::get_record('course_modules', 'id', $forumcmid);
     $forum = forum_utils::get_record('forum', 'id', $cm->instance);
     if ($progress) {
         print_heading(s($forum->name), '', 3);
         print '<ul><li>' . get_string('convert_process_init', 'forumng');
         flush();
     }
     // Hide forum
     forum_utils::update_record('course_modules', (object) array('id' => $cm->id, 'visible' => 0));
     // Table for changed subscription constants
     $subscriptiontranslate = array(0 => 1, 1 => 3, 2 => 2, 3 => 0);
     // Get, convert, and create forum table data
     $forumng = (object) array('course' => $course->id, 'name' => addslashes($forum->name), 'type' => 'general', 'intro' => addslashes($forum->intro), 'ratingscale' => $forum->scale, 'ratingfrom' => $forum->assesstimestart, 'ratinguntil' => $forum->assesstimefinish, 'ratingthreshold' => 1, 'grading' => $forum->assessed, 'attachmentmaxbytes' => $forum->maxbytes, 'subscription' => $subscriptiontranslate[$forum->forcesubscribe], 'feedtype' => $forum->rsstype, 'feeditems' => $forum->rssarticles, 'maxpostsperiod' => $forum->blockperiod, 'maxpostsblock' => $forum->blockafter, 'postingfrom' => 0, 'postinguntil' => 0, 'typedata' => null);
     require_once $CFG->dirroot . '/mod/forumng/lib.php';
     // Note: The idnumber is required. We cannot copy it because then there
     // would be a duplicate idnumber. Let's just leave blank, people will
     // have to configure this manually.
     $forumng->cmidnumber = '';
     if (!($newforumid = forumng_add_instance($forumng))) {
         throw new forum_exception("Failed to add forumng instance");
     }
     // Create and add course-modules entry
     $newcm = new stdClass();
     $newcm->course = $course->id;
     $newcm->module = get_field('modules', 'id', 'name', 'forumng');
     if (!$newcm->module) {
         throw new forum_exception("Cannot find forumng module id");
     }
     $newcm->instance = $newforumid;
     $newcm->section = $cm->section;
     $newcm->added = time();
     $newcm->score = $cm->score;
     $newcm->indent = $cm->indent;
     $newcm->visible = 0;
     // Forums are always hidden until finished
     $newcm->groupmode = $cm->groupmode;
     $newcm->groupingid = $cm->groupingid;
     $newcm->idnumber = $cm->idnumber;
     $newcm->groupmembersonly = $cm->groupmembersonly;
     // Include extra OU-specific data
     if (class_exists('ouflags')) {
         $newcm->showto = $cm->showto;
         $newcm->stealth = $cm->stealth;
         $newcm->parentcmid = $cm->parentcmid;
         $newcm->completion = $cm->completion;
         $newcm->completiongradeitemnumber = $cm->completiongradeitemnumber;
         $newcm->completionview = $cm->completionview;
         $newcm->availablefrom = $cm->availablefrom;
         $newcm->availableuntil = $cm->availableuntil;
         $newcm->showavailability = $cm->showavailability;
         $newcm->parentpagename = $cm->parentpagename;
     }
     // Add
     $newcm->id = forum_utils::insert_record('course_modules', $newcm);
     // Update section
     if ($insection) {
         $section = forum_utils::get_record('course_sections', 'id', $newcm->section);
         $updatesection = (object) array('id' => $section->id, 'sequence' => str_replace($cm->id, $cm->id . ',' . $newcm->id, $section->sequence));
         if ($updatesection->sequence == $section->sequence) {
             throw new forum_exception("Unable to update sequence");
         }
         forum_utils::update_record('course_sections', $updatesection);
     }
     // Construct forum object for new forum
     $newforum = self::get_from_id($forumng->id, forum::CLONE_DIRECT);
     if ($progress) {
         print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
     }
     if (!$nodata) {
         // Convert subscriptions
         switch ($newforum->get_effective_subscription_option()) {
             case self::SUBSCRIPTION_PERMITTED:
                 if ($progress) {
                     print '<li>' . get_string('convert_process_subscriptions_normal', 'forumng');
                     flush();
                 }
                 // Standard subscription - just copy subscriptions
                 $rs = forum_utils::get_recordset('forum_subscriptions', 'forum', $forum->id);
                 while ($rec = rs_fetch_next_record($rs)) {
                     forum_utils::insert_record('forumng_subscriptions', (object) array('forumid' => $forumng->id, 'userid' => $rec->userid, 'subscribed' => 1));
                 }
                 rs_close($rs);
                 if ($progress) {
                     print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
                 }
                 break;
             case self::SUBSCRIPTION_INITIALLY_SUBSCRIBED:
                 // Initial subscription is handled differently; the old forum
                 // stores all the subscriptions in the database, while in this
                 // forum we only store people who chose to unsubscribe
                 if ($progress) {
                     print '<li>' . get_string('convert_process_subscriptions_initial', 'forumng');
                     flush();
                 }
                 // Get list of those subscribed on old forum
                 $rs = forum_utils::get_recordset('forum_subscriptions', 'forum', $forum->id);
                 $subscribedbefore = array();
                 while ($rec = rs_fetch_next_record($rs)) {
                     $subscribedbefore[$rec->userid] = true;
                 }
                 rs_close();
                 // Get list of those subscribed on new forum
                 $new = $newforum->get_subscribers();
                 // For anyone in the new list but not the old list, add an
                 // unsubscribe
                 foreach ($new as $user) {
                     if (!array_key_exists($user->id, $subscribedbefore)) {
                         forum_utils::insert_record('forumng_subscriptions', (object) array('forumid' => $forumng->id, 'userid' => $user->id, 'subscribed' => 0));
                     }
                 }
                 if ($progress) {
                     print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
                 }
                 break;
         }
         // Convert discussions
         if ($progress) {
             print '<li>' . get_string('convert_process_discussions', 'forumng');
             flush();
         }
         $rsd = forum_utils::get_recordset('forum_discussions', 'forum', $forum->id);
         $count = 0;
         while ($recd = rs_fetch_next_record($rsd)) {
             // Convert discussion options
             $newd = (object) array('forumid' => $forumng->id, 'timestart' => $recd->timestart, 'timeend' => $recd->timeend, 'deleted' => 0, 'locked' => 0, 'sticky' => 0);
             if ($recd->groupid == -1 || !$newcm->groupmode) {
                 $newd->groupid = null;
             } else {
                 $newd->groupid = $recd->groupid;
             }
             // Save discussion
             $newd->id = forum_utils::insert_record('forumng_discussions', $newd);
             // Convert posts
             $lastposttime = -1;
             $discussionupdate = (object) array('id' => $newd->id);
             $postids = array();
             // From old post id to new post id
             $parentposts = array();
             // From new post id to old parent id
             $subjects = array();
             // From new id to subject text (no slashes)
             $rsp = forum_utils::get_recordset('forum_posts', 'discussion', $recd->id);
             while ($recp = rs_fetch_next_record($rsp)) {
                 // Convert post
                 $newp = (object) array('discussionid' => $newd->id, 'userid' => $recp->userid, 'created' => $recp->created, 'modified' => $recp->modified, 'deleted' => 0, 'deleteuserid' => null, 'mailstate' => self::MAILSTATE_DIGESTED, 'oldversion' => 0, 'edituserid' => null, 'subject' => addslashes($recp->subject), 'message' => addslashes($recp->message), 'format' => $recp->format, 'important' => 0);
                 // Are there any attachments?
                 $attachments = array();
                 if (class_exists('ouflags')) {
                     // OU has customisation for existing forum that supports
                     // multiple attachments
                     $attachmentrecords = forum_utils::get_records('forum_attachments', 'postid', $recp->id);
                     foreach ($attachmentrecords as $reca) {
                         $attachments[] = $reca->attachment;
                     }
                 } else {
                     // Standard forum uses attachment field for filename
                     if ($recp->attachment) {
                         $attachments[] = $recp->attachment;
                     }
                 }
                 $newp->attachments = count($attachments) ? 1 : 0;
                 // Add record
                 $newp->id = forum_utils::insert_record('forumng_posts', $newp);
                 // Remember details for later parent update
                 $postids[$recp->id] = $newp->id;
                 if ($recp->parent) {
                     $parentposts[$newp->id] = $recp->parent;
                 } else {
                     $discussionupdate->postid = $newp->id;
                 }
                 if ($newp->created > $lastposttime) {
                     $discussionupdate->lastpostid = $newp->id;
                 }
                 $subjects[$newp->id] = $recp->subject;
                 // Copy attachments
                 $oldfolder = $CFG->dataroot . "/{$course->id}/{$CFG->moddata}/forum/{$forum->id}/{$recp->id}";
                 $newfolder = forum_post::get_any_attachment_folder($course->id, $forumng->id, $newd->id, $newp->id);
                 $filesok = 0;
                 $filesfailed = 0;
                 foreach ($attachments as $attachment) {
                     // Create folder if it isn't there
                     $attachment = clean_filename($attachment);
                     check_dir_exists($newfolder, true, true);
                     // Copy file
                     try {
                         forum_utils::copy("{$oldfolder}/{$attachment}", "{$newfolder}/{$attachment}");
                         $filesok++;
                     } catch (forum_exception $e) {
                         if ($progress) {
                             print "[<strong>Warning</strong>: file copy failed for post " . $recp->id . " => " . $newp->id . ", file " . s($attachment) . "]";
                         }
                         $filesfailed++;
                     }
                 }
                 // If all files failed, clean up
                 if ($filesfailed && !$filesok) {
                     rmdir($newfolder);
                     $noattachments = (object) array('id' => $newp->id, 'attachments' => 0);
                     forum_utils::update_record('forumng_posts', $noattachments);
                 }
                 // Convert ratings
                 if ($forumng->ratingscale) {
                     $rsr = get_recordset('forum_ratings', 'post', $recp->id);
                     while ($recr = rs_fetch_next_record($rsr)) {
                         forum_utils::insert_record('forumng_ratings', (object) array('postid' => $newp->id, 'userid' => $recr->userid, 'time' => $recr->time, 'rating' => $recr->rating));
                     }
                     rs_close($rsr);
                 }
             }
             rs_close($rsp);
             // Update parent numbers
             $newparentids = array();
             foreach ($parentposts as $newid => $oldparentid) {
                 if (!array_key_exists($oldparentid, $postids)) {
                     throw new forum_exception("Unknown parent post {$oldparentid}");
                 }
                 $newparentid = $postids[$oldparentid];
                 forum_utils::update_record('forumng_posts', (object) array('id' => $newid, 'parentpostid' => $newparentid));
                 $newparentids[$newid] = $newparentid;
             }
             // Update subjects
             $removesubjects = array();
             // Array of ints to cancel subjects
             foreach ($newparentids as $newid => $newparentid) {
                 $subject = $subjects[$newid];
                 $parentsubject = $subjects[$newparentid];
                 if ($subject && ($subject == get_string('re', 'forum') . ' ' . $parentsubject || $subject == $parentsubject)) {
                     $removesubjects[] = $newid;
                 }
             }
             if (count($removesubjects)) {
                 $in = forum_utils::in_or_equals($removesubjects);
                 forum_utils::execute_sql("UPDATE {$CFG->prefix}forumng_posts SET subject=NULL WHERE id {$in}");
             }
             // Update first/last post numbers
             forum_utils::update_record('forumng_discussions', $discussionupdate);
             // Convert read data
             $rsr = forum_utils::get_recordset_sql("\nSELECT\n    userid, MAX(lastread) AS lastread\nFROM\n    {$CFG->prefix}forum_read\nWHERE\n    discussionid = {$recd->id}\nGROUP BY\n    userid");
             while ($recr = rs_fetch_next_record($rsr)) {
                 forum_utils::insert_record('forumng_read', (object) array('discussionid' => $newd->id, 'userid' => $recr->userid, 'time' => $recr->lastread));
             }
             rs_close($rsr);
             // Display dot for each discussion
             if ($progress) {
                 print '.';
                 $count++;
                 if ($count % 10 == 0) {
                     print $count;
                 }
                 flush();
             }
         }
         rs_close($rsd);
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Show forum
     if (!$hide && $cm->visible) {
         if ($progress) {
             print '<li>' . get_string('convert_process_show', 'forumng');
             flush();
         }
         $updatecm = (object) array('id' => $newcm->id, 'visible' => 1);
         forum_utils::update_record('course_modules', $updatecm);
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Transfer role assignments
     $oldcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
     $newcontext = get_context_instance(CONTEXT_MODULE, $newcm->id);
     $roles = get_records('role_assignments', 'contextid', $oldcontext->id);
     if ($roles) {
         if ($progress) {
             print '<li>' . get_string('convert_process_assignments', 'forumng');
             flush();
         }
         foreach ($roles as $role) {
             $newrole = $role;
             $newrole->contextid = $newcontext->id;
             $newrole->enrol = addslashes($newrole->enrol);
             forum_utils::insert_record('role_assignments', $newrole);
         }
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Transfer capabilities
     $capabilities = array('moodle/course:viewhiddenactivities' => 'moodle/course:viewhiddenactivities', 'moodle/site:accessallgroups' => 'moodle/site:accessallgroups', 'moodle/site:trustcontent' => 'moodle/site:trustcontent', 'moodle/site:viewfullnames' => 'moodle/site:viewfullnames', 'mod/forum:viewdiscussion' => 'mod/forumng:viewdiscussion', 'mod/forum:startdiscussion' => 'mod/forumng:startdiscussion', 'mod/forum:replypost' => 'mod/forumng:replypost', 'mod/forum:viewrating' => 'mod/forumng:viewrating', 'mod/forum:viewanyrating' => 'mod/forumng:viewanyrating', 'mod/forum:rate' => 'mod/forumng:rate', 'mod/forum:createattachment' => 'mod/forumng:createattachment', 'mod/forum:deleteanypost' => 'mod/forumng:deleteanypost', 'mod/forum:splitdiscussions' => 'mod/forumng:splitdiscussions', 'mod/forum:movediscussions' => 'mod/forumng:movediscussions', 'mod/forum:editanypost' => 'mod/forumng:editanypost', 'mod/forum:viewsubscribers' => 'mod/forumng:viewsubscribers', 'mod/forum:managesubscriptions' => 'mod/forumng:managesubscriptions', 'mod/forum:viewhiddentimedposts' => 'mod/forumng:viewallposts');
     $caps = get_records('role_capabilities', 'contextid', $oldcontext->id);
     if ($caps) {
         if ($progress) {
             print '<li>' . get_string('convert_process_overrides', 'forumng');
             flush();
         }
         foreach ($caps as $cap) {
             foreach ($capabilities as $key => $capability) {
                 if ($cap->capability != $key) {
                     continue;
                 }
                 $newcap = $cap;
                 $newcap->contextid = $newcontext->id;
                 $newcap->capability = $capability;
                 $newcap->capability = addslashes($newcap->capability);
                 forum_utils::insert_record('role_capabilities', $newcap);
             }
         }
         if ($progress) {
             print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
         }
     }
     // Do course cache
     rebuild_course_cache($course->id, true);
     // Update search data
     if (self::search_installed()) {
         if ($progress) {
             print '<li>' . get_string('convert_process_search', 'forumng') . '</li>';
             flush();
         }
         self::search_update_all($progress, $course->id, $newcm->id);
     }
     // OU only: Transfer external dashboard details to new forum
     if (class_exists('ouflags')) {
         if ($progress) {
             print '<li>' . get_string('convert_process_dashboard', 'forumng');
             flush();
         }
         require_once $CFG->dirroot . '/local/externaldashboard/external_dashboard.php';
         $a = new stdClass();
         list($a->yay, $a->nay) = external_dashboard::transfer_favourites($forumcmid, $newcm->id);
         if ($progress) {
             print ' ' . get_string('convert_process_dashboard_done', 'forumng', $a) . '</li>';
         }
     }
     if ($progress) {
         print '<li>' . get_string('convert_process_update_subscriptions', 'forumng');
         flush();
     }
     self::group_subscription_update(false, $newcm->id);
     if ($progress) {
         print ' ' . get_string('convert_process_state_done', 'forumng') . '</li>';
     }
     forum_utils::finish_transaction();
     if ($progress) {
         $a = (object) array('seconds' => round(microtime(true) - $starttime, 1), 'link' => '<a href="view.php?id=' . $newcm->id . '">' . get_string('convert_newforum', 'forumng') . '</a>');
         print '</ul><p>' . get_string('convert_process_complete', 'forumng', $a) . '</p>';
     }
 }