4 Replies - 13750 Views - Last Post: 22 January 2012 - 08:56 AM

#1 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

WPMU Blog Clone Plugin Problems

Posted 23 June 2010 - 07:45 AM

I am in the process of writing a wordpress mu (Multi user) plugin which will clone a blog (posts, users, links, options, etc. And add it as a new blog, however am running into an issue where wordpress gets stuck in an infinite loop when trying to modify/ create a post or page, or when I attempt to upload media on it.

I had originally started a thread on the wordpress.org help forums for plugins and hacks (located here: http://wordpress.org...13583?replies=1 may have been the incorrect area) but it has been over 30 hours now and I have yet to receive a response of any sort so I thought I would post this here.

Here is the associated code (sorry it is commented heavily due to requirements of the people I am doing it for):
<?php
/*
Plugin Name: Clone Blog
Description: Allows you to easily clone a wpmu blog.
Author: amath
License: (Events: GNU General Public License 2.0 (GPL) http://www.gnu.org/licenses/gpl.html)
*/
// The above was required, DO NOT REMOVE!

// FOR DEBUG PURPOSES ONLY!
// SET TRUE TO HAVE ADDITIONAL OUTPUT WHEN CLONING
// SET TO FALSE WHEN NOT DEBUGGING!!!!!!!
// Default value: false
define("CLONE_DEBUG", false);

// IF USING THIS PLUGIN ON A LIVE SITE SET CLONE_LIVE TO true
// IF NOT ON A LIVE SITE (CLONE_LIVE = false) NO UPDATE/ INSERTION QUERIES WILL BE DONE
// Default value: true
define("CLONE_LIVE", true);

// To add the clone blog option to the site admin menu
add_action('admin_menu', 'cloneAddBlogMenuPage');
// The hook calls this function:
function cloneAddBlogMenuPage() {
  // Use workdpress's function to add a new menu item
  // parent, page_title, menu_title, access_level, file, function
	add_submenu_page('wpmu-admin.php', 'Clone Blog', 'Clone Blog', 10, 'cloneblog', 'clone_blog_init');
	// end function
}

// The function to be called when the menu item "Clone Blog" is clicked on.
function clone_blog_init(){
  // Set an initial content variable, we will be echoing this at the end of the function.
  $cloneContent = "Blog ID: " . $_POST['blogId'] . "<br/>";
  
  // If we have post data, and we have a blog ID set (something to clone), 
  // and we have a path set lets do it (clone the blog).
  if($_POST && isset($_POST['blogId']) && isset($_POST['path'])){
    // We want access to the database so tell the function that $wpdb is a global variable
    global $wpdb;

    // set the clone's ID equal to one more than the number of blogs
    $cloneId = 1+get_blog_count();
    // Get the path information from the POST data
    $path = $_POST['path'];
    // Split the path apart to get an array of directories
    $pathParts = explode("/", $path);
    // Take the last directory (in an array such as this: /amath/courses/1350/) and set it to the course number (ie: 1350)
    $courseNum = $pathParts[3];
    // Get the list of blogs (in a serialized format) from the database
    $unserializedBlogList = unserialize($wpdb->get_var("SELECT meta_value FROM wp_sitemeta WHERE meta_key = 'blog_list'"));
    // set the current blog id to what was sent in (from POST)
    $blogId = $_POST['blogId'];
    
    // set a variable aside for later use
    $cloneBlog;
    // loop through each of the (now) unserialized blogs, call them $blogList for simplicity
    foreach($unserializedBlogList AS $blogList){
      /// If the blog's id is equal to the one we are cloning
      if($blogList['blog_id'] == $blogId){
        // set the clone blog variable (set aside directory before the loop) to the current blog we are looking at
        $cloneBlog = $blogList;
        // Break out of the loop, we are done looking
        break;
      // end if
      }
    //end for
    }
    unset($blogList); // clean up
    // Update the clone blog's blog_id (set it equal to the clone id variable);
    // NOTE - The blog_id is supposed to be a string, hence the concat in front.
    $cloneBlog['blog_id'] = "" . $cloneId;
    // Update the clone's path (for the first time)
    $cloneBlog['path'] = $path;
    
    // push the new cloned blog to the end of our blog array
    $unserializedBlogList[] = $cloneBlog;
    // serialize the blogs array and set it to FinalBlogList - for use later
    $FinalBlogList = serialize($unserializedBlogList);
    
    // commented out from debug
    //die(var_dump($FinalBlogList));
    
    // Create an array for all the queries we are about to create (so we can loop through them a little later easily)
    $queryArray = array();
    // Create all the queries to be done later on in the code.
    // NOTE - All of these next few queries are done in the if(CLONE_LIVE) block below. 
    //        They are defined here so we can output them when DEBUGGING.
    // {
    // wp_#_commentmeta
    // Create the new table (for the clone) and copy EVERYTHING from the host's tables
    $queryArray['commentMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_". $cloneId . "_commentmeta SELECT * FROM wp_" . $blogId . "_commentmeta";
    
    //repeat previous for the next 8 tables
    // {
    // wp_#_comments
    $queryArray['commentsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_comments SELECT * FROM wp_" . $blogId . "_comments";
    
    // wp_#_links
    $queryArray['linksQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_links SELECT * FROM wp_" . $blogId . "_links";
  
    // wp_#_options
    $queryArray['optionsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_options SELECT * FROM wp_" . $blogId . "_options";
    
    // wp_#_postmeta
    $queryArray['postMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_postmeta SELECT * FROM wp_" . $blogId . "_postmeta";
    
    // wp_#_posts
    $queryArray['postsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_posts SELECT * FROM wp_" . $blogId . "_posts";
    
    // wp_#_term_relationships
    $queryArray['termRelationshipsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_relationships SELECT * FROM wp_" . $blogId . "_term_relationships";
    
    // wp_#_term_taxonomy
    $queryArray['termTaxonomyQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_taxonomy SELECT * FROM wp_" . $blogId . "_term_taxonomy";
    
    // wp_#_terms
    $queryArray['termsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_terms SELECT * FROM wp_" . $blogId . "_terms";
    // }
    // end repitition of the table cloning
    
    // insert the blog to the wp_blogs table:
    // NOTICE - The 'archived' value is the character zero ('0') and not the numeric value. 
    //   If you try to a number of boolean it will truncate the field and the blog won't show up in a blog list.
    $queryArray['blogQuery'] = $wpdb->prepare("INSERT INTO `wp_blogs` (`blog_id`, `site_id`, `domain`, `path`, `registered`, `last_updated`, `public`, `archived`, `mature`, `spam`, `deleted`, `lang_id`) VALUES(%d, 1, 'amath.colorado.edu', %s, '2010-05-28 22:17:59', '2010-06-08 20:34:48', 1, '0', 0, 0, 0, 0)", $cloneId, $path);
    
    // Update the options table for the clone. Set the siteurl equal to the path passed in
    $queryArray['updateQueryOne'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='siteurl'";
    
    // update the options table for the clone. Set the home url to the path passed in.
    $queryArray['updateQueryTwo'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='home'";
    
    // update, set upload url to the course number
    $queryArray['updateQueryThree'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu/amath/courses/" . $courseNum . "/files' WHERE option_name='fileupload_url'";
    
    // set upload path to the clone's id
    $queryArray['updateQueryFour'] = "UPDATE wp_" . $cloneId . "_options SET option_value='wp-content/blogs.dir/" . $cloneId . "/files' WHERE option_name='upload_path'";
    
    // set blog count the updated blog count (which coincides with clondId)
    $queryArray['updateQueryFive'] = "UPDATE wp_sitemeta SET meta_value = " . $cloneId . " WHERE meta_key = 'blog_count'";
  
    // update the list of blogs to the FinalBlogList from earlier.
    $queryArray['updateQuerySix'] = "UPDATE wp_sitemeta SET meta_value = '" . $FinalBlogList . "' WHERE meta_key = 'blog_list'";
    
    // change an option name to reflect the correct blog (the clone).
    $queryArray['updateQuerySeven'] = "UPDATE wp_" . $clondId . "_options SET option_name = 'wp_" . $cloneId . "_user_roles' WHERE option_name = 'wp_" . $blogId . "_user_roles'";
    // }
    // End creating queries to be done.
    
    foreach($queryArray AS $queryKey => $queryValue){
      // if on a live site
      if(CLONE_LIVE){
        // make it so
        $wpdb->query($queryValue);
        // end if on live site
      }
      // if debugging
      if(CLONE_DEBUG){
        // add the query to our output buffer
        $cloneContent .= $queryKey . ": " . $queryValue . "<br/>";
        // end if debugging
      }
    } // end if CLONE_LIVE
    unset($queryKey, $queryValue); // Clean up
    
    // if we are live (otherwise there is no table wp_<CLONE_ID>_posts so a warning would be thrown.
    if(CLONE_LIVE){
      $updateQueryEight = ""; // for use in just a second.
      $allPosts = $wpdb->get_results("SELECT guid, ID FROM wp_" . $cloneId . "_posts WHERE guid != ''", "ARRAY_A"); // get all the posts from our table
      foreach($allPosts AS $post){ // loop through each post
        $url = $post['guid'];
        $courseParts = explode("courses", $url);
        $urlParts = explode("/", $courseParts[1]);
        // If the first character after courses is a slash we know the ourse number will be directly after that
        if(substr($courseParts[1], 0, 1) == '/'){
          // update the course number
          $urlParts[1] = $courseNum;
          // end if the first character was a slash
        }
        // otherwise
        else{
          // update the course number
          $urlParts[0] = $courseNum;
          // end else
        }
        // remake the url:
        $urlConstructor = implode("/", $urlParts);
        $courseParts[1] = $urlConstructor;
        $finalURL = implode("courses", $courseParts);
        // Update every post's guid
        $wpdb->query("UPDATE wp_" . $cloneId . "_posts SET guid = '" . $finalURL . "' WHERER ID = " . $post['ID'] . "");
        
        // if we are in debug mode
        if(CLONE_DEBUG){
          // add difference between the two URLs to our output buffer
          $cloneContent .= $url . " -> " . $finalURL . "<br/>";
          // end if CLONE_DEBUG
        }
        // end foreach $allPosts
      }
      unset($post); // Clean up

      // Clear the clone's cache and recreate it.
      refresh_blog_details($cloneId);
      // Set the content to success.
      $cloneContent .= "The blog was successfully cloned!<br/>Clone ID: " . $cloneId;
      // end if CLONE_LIVE
    }
    // end if
  }
  // If we don't have any POST data
  else{
    // Get a list of the blogs, we want all of them
    $cloneBlogList = get_blog_list(0, 'all');
    // Start creating a form for the page
    $cloneContent .= "<form action='#' method='post'><select name='blogId'>";
    // loop through each of the blogs in the list, call each individual blog a "cloneBlog"
    foreach($cloneBlogList AS $cloneBlog){
      // for each of the cloneBlogs get their details;
      $cloneBlogDetails = get_blog_details($cloneBlog['blog_id']);
      // From their details and id we can create an option for the cloning process.
      $cloneContent .= "<option value='" . $cloneBlog['blog_id'] . "'>" . $cloneBlogDetails->blogname . "</option>";
      // end for
    }
    unset($cloneBlog); // clean up.
    // Finish off the form
    $cloneContent .= "</select><br/>New Blog Path: <input type='text' name='path'/><br/><input type='submit' value='Clone Blog'/></form>";
  // end else
  }
  // output our buffer to the page
  echo $cloneContent;
// end function
}
?>


Now, I feel it is important to state this is my first wordpress plugin of any sort, so there are probably API calls I could be using and am not currently.

At the moment it is set up using some blog-specific variables and options (given that I need it to work for my client first), but I hope to be able to get a generic build of it once it is completed.

Any help or insight would be greatly appreciated.

Is This A Good Question/Topic? 0
  • +

Replies To: WPMU Blog Clone Plugin Problems

#2 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

Re: WPMU Blog Clone Plugin Problems

Posted 24 June 2010 - 11:47 AM

Okay, I have been able to narrow down the problem a bit (to a single table in fact), but haven't found a solution. Here is the table (all the columns):
+--------------+---------------------+------+-----+---------+----------------+
| Field        | Type                | Null | Key | Default | Extra          |
+--------------+---------------------+------+-----+---------+----------------+
| option_id    | bigint(20) unsigned |      | PRI | NULL    | auto_increment |
| blog_id      | int(11)             |      |     | 0       |                |
| option_name  | varchar(64)         |      | UNI |         |                |
| option_value | longtext            |      |     |         |                |
| autoload     | varchar(20)         |      |     | yes     |                |
+--------------+---------------------+------+-----+---------+----------------+



And here is a list of all the option_names that I am dealing with:
+----------------------------------+
| option_name                      |
+----------------------------------+
| active_plugins                   |
| admin_email                      |
| advanced_edit                    |
| allowedthemes                    |
| avatar_default                   |
| avatar_rating                    |
| blacklist_keys                   |
| blogdescription                  |
| blogname                         |
| blog_charset                     |
| blog_public                      |
| blog_upload_space                |
| category_base                    |
| category_children                |
| close_comments_days_old          |
| close_comments_for_old_posts     |
| comments_notify                  |
| comments_per_page                |
| comment_max_links                |
| comment_moderation               |
| comment_order                    |
| comment_registration             |
| comment_whitelist                |
| cron                             |
| current_theme                    |
| dashboard_widget_options         |
| date_format                      |
| db_version                       |
| default_category                 |
| default_comments_page            |
| default_comment_status           |
| default_email_category           |
| default_link_category            |
| default_pingback_flag            |
| default_ping_status              |
| default_post_edit_rows           |
| default_role                     |
| embed_autourls                   |
| embed_size_h                     |
| embed_size_w                     |
| enable_app                       |
| enable_xmlrpc                    |
| ep_exclude_pages                 |
| fileupload_url                   |
| gmt_offset                       |
| gzipcompression                  |
| hack_file                        |
| home                             |
| html_type                        |
| image_default_align              |
| image_default_link_type          |
| image_default_size               |
| language                         |
| large_size_h                     |
| large_size_w                     |
| links_recently_updated_append    |
| links_recently_updated_prepend   |
| links_recently_updated_time      |
| links_updated_date_format        |
| mailserver_login                 |
| mailserver_pass                  |
| mailserver_port                  |
| mailserver_url                   |
| medium_size_h                    |
| medium_size_w                    |
| moderation_keys                  |
| moderation_notify                |
| new_admin_email                  |
| nonce_salt                       |
| page_comments                    |
| permalink_structure              |
| ping_sites                       |
| posts_per_page                   |
| posts_per_rss                    |
| post_count                       |
| recently_edited                  |
| require_name_email               |
| rewrite_rules                    |
| rss_language                     |
| rss_use_excerpt                  |
| secret                           |
| show_avatars                     |
| show_on_front                    |
| sidebars_widgets                 |
| siteurl                          |
| start_of_week                    |
| sticky_posts                     |
| stylesheet                       |
| suffusion_options                |
| tag_base                         |
| template                         |
| thread_comments                  |
| thread_comments_depth            |
| thumbnail_crop                   |
| thumbnail_size_h                 |
| thumbnail_size_w                 |
| timezone_string                  |
| time_format                      |
| uploads_use_yearmonth_folders    |
| upload_path                      |
| upload_url_path                  |
| users_can_register               |
| use_balanceTags                  |
| use_linksupdate                  |
| use_smilies                      |
| use_trackback                    |
| widget_archives                  |
| widget_calendar                  |
| widget_categories                |
| widget_links                     |
| widget_meta                      |
| widget_pages                     |
| widget_recent-comments           |
| widget_recent-posts              |
| widget_rss                       |
| widget_search                    |
| widget_suf-cat-posts             |
| widget_suf-featured-posts        |
| widget_suf-follow-twitter        |
| widget_suf-google-translator     |
| widget_suf-subscription          |
| widget_tag_cloud                 |
| widget_text                      |
| WPLANG                           |
| wp_2_user_roles                  |
| _transient_dirsize_cache         |
| _transient_doing_cron            |
| _transient_plugin_slugs          |
| _transient_random_seed           |
| _transient_timeout_dirsize_cache |
| _transient_timeout_plugin_slugs  |
+----------------------------------+



Originally I thought the problem was being caused by:
"nonce_salt", "secret", and "_transient_random_seed"
But upon changing each of them in a semi-cloned blog (everything except the options table) I found that the blog worked regardless the value in those fields (in fact I put "wasd" in each of them - they are normally all different - and it still worked). This tells me that it is some other value in the table causing the problem. I have tried selecting all the blog_id columns from each of my blogs (remember this is a mu site (multiple users - ie blogs)) and they always return '0', regardless of the blog's actual ID. I have also made sure that the "wp_2_user_roles" was renamed for the cloned blog (since '2' is the blog's id) and that had no effect.

At this point I have re-written some of the script, commented out some of what I used to be doing and added some more wordpress API calls into the mix (most of which I then undo...). For instance this code:
<?php
/*
Plugin Name: Clone Blog
Description: Allows you to easily clone a wpmu blog.
Author: amath
Version: 1.0
License: (Events: GNU General Public License 2.0 (GPL) http://www.gnu.org/licenses/gpl.html)
*/
// The above was required, DO NOT REMOVE!

// FOR DEBUG PURPOSES ONLY!
// SET TRUE TO HAVE ADDITIONAL OUTPUT WHEN CLONING
// SET TO FALSE WHEN NOT DEBUGGING!!!!!!!
// Default value: false
define("CLONE_DEBUG", true);

// IF USING THIS PLUGIN ON A LIVE SITE SET CLONE_LIVE TO true
// IF NOT ON A LIVE SITE (CLONE_LIVE = false) NO UPDATE/ INSERTION QUERIES WILL BE DONE
// Default value: true
define("CLONE_LIVE", true);

// To add the clone blog option to the site admin menu
add_action('admin_menu', 'clone_add_blog_menu_page');
/*
  {
    "function": "clone_add_blog_menu_page",
    "parameter": {
    },
    "return": "void",
    "description": "Adds a sub-menu item to the admin panel of the wp-admin pages",
    "since": "v1.0"
  }
*/
// The hook calls this function:
function clone_add_blog_menu_page() {
  // Use workdpress's function to add a new menu item
  // parent, page_title, menu_title, access_level, file, function
	add_submenu_page('wpmu-admin.php', 'Clone Blog', 'Clone Blog', 10, 'cloneblog', 'clone_blog_init');
	// end function
}

/*
  {
    "function": "clone_blog_init",
    "parameters": {
    },
    "return": "void",
    "description": "Responsible for creating the main page content and processing the posted data (ie cloning the blog).",
    "since": "v1.0"
  }
*/
// The function to be called when the menu item "Clone Blog" is clicked on.
function clone_blog_init(){
  // Set an initial content variable, we will be echoing this at the end of the function.
  $cloneContent = "Blog ID: " . $_POST['blogId'] . "<br/>";
  
  // If we have post data, and we have a blog ID set (something to clone), 
  // and we have a path set lets do it (clone the blog).
  if($_POST && isset($_POST['blogId']) && isset($_POST['path'])){
    // We want access to the database so tell the function that $wpdb is a global variable
    global $wpdb;
    
    
    $blogId = $_POST['blogId'];
    $domain = "amath.colorado.edu";
    $path = $_POST['path'];
    $title = "New Blog";
    $user_id = 1;
    $cloneId = wpmu_create_blog($domain, $path, $title, $user_id);
    
    /*
    // Table name that we need the status of (to get the clone's id)
    $tablename = "wp_blogs";
    // Initialize it to something, just to be safe (assuming no blogs were deleted - ever - this will be the same as the final id is.
    $cloneId = 1 + get_blog_count();
    // Query to get the table's status (including the increment value for the blog_id)
    $QueryStatus = "SHOW TABLE STATUS LIKE '$tablename'";
    // Get the results of the query (as an associative array)
    $QueryStatusResult = $wpdb->get_results($QueryStatus, "ARRAY_A");
    // get the clone's id from the results returned from the query
    $cloneId = $QueryStatusResult[0]['Auto_increment'];
    
    if(CLONE_DEBUG){
      $cloneContent .= "Clone ID: " . $cloneId . "<br/>";
    }
    
    
    
//    $cloneId = 1+get_blog_count();
    // Get the path information from the POST data
    $path = $_POST['path'];
    // Split the path apart to get an array of directories
    $pathParts = explode("/", $path);
    // Take the last directory (in an array such as this: /amath/courses/1350/) and set it to the course number (ie: 1350)
    $courseNum = $pathParts[3];
    // Get the list of blogs (in a serialized format) from the database
    $unserializedBlogList = unserialize($wpdb->get_var("SELECT meta_value FROM wp_sitemeta WHERE meta_key = 'blog_list'"));
    // set the current blog id to what was sent in (from POST)
    $blogId = $_POST['blogId'];
    
    // set a variable aside for later use
    $cloneBlog;
    // loop through each of the (now) unserialized blogs, call them $blogList for simplicity
    foreach($unserializedBlogList AS $blogList){
      /// If the blog's id is equal to the one we are cloning
      if($blogList['blog_id'] == $blogId){
        // set the clone blog variable (set aside directory before the loop) to the current blog we are looking at
        $cloneBlog = $blogList;
        // Break out of the loop, we are done looking
        break;
      // end if
      }
    //end for
    }
    unset($blogList); // clean up
    // Update the clone blog's blog_id (set it equal to the clone id variable);
    // NOTE - The blog_id is supposed to be a string, hence the concat in front.
    $cloneBlog['blog_id'] = "" . $cloneId;
    // Update the clone's path (for the first time)
    $cloneBlog['path'] = $path;
    
    // push the new cloned blog to the end of our blog array
    $unserializedBlogList[] = $cloneBlog;
    // serialize the blogs array and set it to FinalBlogList - for use later
    $FinalBlogList = serialize($unserializedBlogList);
    
    // commented out from debug
    //die(var_dump($FinalBlogList));
    
    */
    
    // Create an array for all the queries we are about to create (so we can loop through them a little later easily)
    $queryArray = array();
    
    $queryArray['dropAllCloneTables'] = "DROP TABLE wp_" . $cloneId . "_commentmeta, wp_" . $cloneId . "_comments, wp_" . $cloneId . "_links, wp_" . $cloneId . "_postmeta, wp_" . $cloneId . "_posts, wp_" . $cloneId . "_term_relationships, wp_" . $cloneId . "_term_taxonomy, wp_" . $cloneId . "_terms";
    // wp_" . $cloneId . "_options,
    
    // Create all the queries to be done later on in the code.
    // NOTE - All of these next few queries are done in the if(CLONE_LIVE) block below. 
    //        They are defined here so we can output them when DEBUGGING.
    // NOTICE - We are using "CREATE TABLE IF NOT EXISTS", this is because we don't want to accidentally overwrite 
    //        anything important (such as a pre-existing blog that has an ID greater than the number of blogs. This 
    //        may also cause problems as it is possible to have a clone which should have the same ID as another 
    //        blog, and for that reason won't be created.
    // FIXME - Update all the queries to use the wordpress database api better. At the moment we are just using the 
    //        query method, but that is likely not the best option for most of this.
    // {
    // wp_#_commentmeta
    // Create the new table (for the clone) and copy EVERYTHING from the host's tables
    $queryArray['commentMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_". $cloneId . "_commentmeta SELECT * FROM wp_" . $blogId . "_commentmeta";
    
    //repeat previous for the next 8 tables
    // {
    // wp_#_comments
    $queryArray['commentsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_comments SELECT * FROM wp_" . $blogId . "_comments";
    
    // wp_#_links
    $queryArray['linksQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_links SELECT * FROM wp_" . $blogId . "_links";
  
    // wp_#_options
    //$queryArray['optionsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_options SELECT * FROM wp_" . $blogId . "_options";
    
    // wp_#_postmeta
    $queryArray['postMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_postmeta SELECT * FROM wp_" . $blogId . "_postmeta";
    
    // wp_#_posts
    $queryArray['postsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_posts SELECT * FROM wp_" . $blogId . "_posts";
    
    // wp_#_term_relationships
    $queryArray['termRelationshipsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_relationships SELECT * FROM wp_" . $blogId . "_term_relationships";
    
    // wp_#_term_taxonomy
    $queryArray['termTaxonomyQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_taxonomy SELECT * FROM wp_" . $blogId . "_term_taxonomy";
    
    // wp_#_terms
    $queryArray['termsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_terms SELECT * FROM wp_" . $blogId . "_terms";
    // }
    // end repitition of the table cloning
    
    // insert the blog to the wp_blogs table:
    // NOTICE - The 'archived' value is the character zero ('0') and not the numeric value. 
    //   If you try to a number of boolean it will truncate the field and the blog won't show up in a blog list.
    // NOTICE - We don't provide the blog_id variable so that the database table will auto-increment as normal.
    // NOTE - This should no longer be useful.
    //$queryArray['blogQuery'] = $wpdb->prepare("INSERT INTO `wp_blogs` (`site_id`, `domain`, `path`, `registered`, `last_updated`, `public`, `archived`, `mature`, `spam`, `deleted`, `lang_id`) VALUES(1, 'amath.colorado.edu', %s, '2010-05-28 22:17:59', '2010-06-08 20:34:48', 1, '0', 0, 0, 0, 0)", $path);
    
    // Update the options table for the clone. Set the siteurl equal to the path passed in
    $queryArray['updateQueryOne'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='siteurl'";
    
    // update the options table for the clone. Set the home url to the path passed in.
    $queryArray['updateQueryTwo'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='home'";
    
    // update, set upload url to the course number
    $queryArray['updateQueryThree'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu/amath/courses/" . $courseNum . "/files' WHERE option_name='fileupload_url'";
    
    // set upload path to the clone's id
    $queryArray['updateQueryFour'] = "UPDATE wp_" . $cloneId . "_options SET option_value='wp-content/blogs.dir/" . $cloneId . "/files' WHERE option_name='upload_path'";
    
    // set blog count the updated blog count (which coincides with clondId)
    // NOTE - Should no longer be useful.
    //$queryArray['updateQueryFive'] = "UPDATE wp_sitemeta SET meta_value = " . $cloneId . " WHERE meta_key = 'blog_count'";
  
    // update the list of blogs to the FinalBlogList from earlier.
    // NOTE - This should no longer be useful.
    //$queryArray['updateQuerySix'] = "UPDATE wp_sitemeta SET meta_value = '" . $FinalBlogList . "' WHERE meta_key = 'blog_list'";
    
    // change an option name to reflect the correct blog (the clone).
    $queryArray['updateQuerySeven'] = "UPDATE wp_" . $clondId . "_options SET option_name = 'wp_" . $cloneId . "_user_roles' WHERE option_name = 'wp_" . $blogId . "_user_roles'";
    // }
    // End creating queries to be done.
    
    foreach($queryArray AS $queryKey => $queryValue){
      // if on a live site
      if(CLONE_LIVE){
        // make it so
        $wpdb->query($queryValue);
        // end if on live site
      }
      // if debugging
      if(CLONE_DEBUG){
        // add the query to our output buffer
        $cloneContent .= $queryKey . ": " . $queryValue . "<br/>";
        // end if debugging
      }
    } // end if CLONE_LIVE
    unset($queryKey, $queryValue); // Clean up
    
    // if we are live (otherwise there is no table wp_<CLONE_ID>_posts so a warning would be thrown.
    if(CLONE_LIVE){
      $updateQueryEight = ""; // for use in just a second.
      $allPosts = $wpdb->get_results("SELECT guid, ID FROM wp_" . $cloneId . "_posts WHERE guid != ''", "ARRAY_A"); // get all the posts from our table
      foreach($allPosts AS $post){ // loop through each post
        $url = $post['guid'];
        $courseParts = explode("courses", $url);
        $urlParts = explode("/", $courseParts[1]);
        // If the first character after courses is a slash we know the ourse number will be directly after that
        if(substr($courseParts[1], 0, 1) == '/'){
          // update the course number
          $urlParts[1] = $courseNum;
          // end if the first character was a slash
        }
        // otherwise
        else{
          // update the course number
          $urlParts[0] = $courseNum;
          // end else
        }
        // remake the url:
        $urlConstructor = implode("/", $urlParts);
        $courseParts[1] = $urlConstructor;
        $finalURL = implode("courses", $courseParts);
        // Update every post's guid
        $wpdb->query("UPDATE wp_" . $cloneId . "_posts SET guid = '" . $finalURL . "' WHERER ID = " . $post['ID'] . "");
        
        // if we are in debug mode
        if(CLONE_DEBUG){
          // add difference between the two URLs to our output buffer
          $cloneContent .= $url . " -> " . $finalURL . "<br/>";
          // end if CLONE_DEBUG
        }
        // end foreach $allPosts
      }
      unset($post); // Clean up

      // Clear the clone's cache and recreate it.
      refresh_blog_details($cloneId);
      // Set the content to success.
      $cloneContent .= "The blog was successfully cloned!<br/>Clone ID: " . $cloneId;
      // end if CLONE_LIVE
    }
    // end if
  }
  // If we don't have any POST data
  else{
    // Get a list of the blogs, we want all of them
    $cloneBlogList = get_blog_list(0, 'all');
    // Start creating a form for the page
    $cloneContent .= "<form action='#' method='post'><select name='blogId'>";
    // loop through each of the blogs in the list, call each individual blog a "cloneBlog"
    foreach($cloneBlogList AS $cloneBlog){
      // FIXME - Please say there is a more elegant way of doing this. Otherwise it is a bit of extra overhead 
      //        that isn't really necessary...
      // for each of the cloneBlogs get their details;
      $cloneBlogDetails = get_blog_details($cloneBlog['blog_id']);
      // From their details and id we can create an option for the cloning process.
      $cloneContent .= "<option value='" . $cloneBlog['blog_id'] . "'>" . $cloneBlogDetails->blogname . "</option>";
      // end for
    }
    unset($cloneBlog); // clean up.
    // Finish off the form
    $cloneContent .= "</select><br/>New Blog Path: <input type='text' name='path'/><br/><input type='submit' value='Clone Blog'/></form>";
  // end else
  }
  // output our buffer to the page
  echo $cloneContent;
// end function
}
?>


Allows the "clone" to work, but doesn't get all the options associated with it.

This code:
<?php
/*
Plugin Name: Clone Blog
Description: Allows you to easily clone a wpmu blog.
Author: amath
Version: 1.0
License: (Events: GNU General Public License 2.0 (GPL) http://www.gnu.org/licenses/gpl.html)
*/
// The above was required, DO NOT REMOVE!

// FOR DEBUG PURPOSES ONLY!
// SET TRUE TO HAVE ADDITIONAL OUTPUT WHEN CLONING
// SET TO FALSE WHEN NOT DEBUGGING!!!!!!!
// Default value: false
define("CLONE_DEBUG", true);

// IF USING THIS PLUGIN ON A LIVE SITE SET CLONE_LIVE TO true
// IF NOT ON A LIVE SITE (CLONE_LIVE = false) NO UPDATE/ INSERTION QUERIES WILL BE DONE
// Default value: true
define("CLONE_LIVE", true);

// To add the clone blog option to the site admin menu
add_action('admin_menu', 'clone_add_blog_menu_page');
/*
  {
    "function": "clone_add_blog_menu_page",
    "parameter": {
    },
    "return": "void",
    "description": "Adds a sub-menu item to the admin panel of the wp-admin pages",
    "since": "v1.0"
  }
*/
// The hook calls this function:
function clone_add_blog_menu_page() {
  // Use workdpress's function to add a new menu item
  // parent, page_title, menu_title, access_level, file, function
	add_submenu_page('wpmu-admin.php', 'Clone Blog', 'Clone Blog', 10, 'cloneblog', 'clone_blog_init');
	// end function
}

/*
  {
    "function": "clone_blog_init",
    "parameters": {
    },
    "return": "void",
    "description": "Responsible for creating the main page content and processing the posted data (ie cloning the blog).",
    "since": "v1.0"
  }
*/
// The function to be called when the menu item "Clone Blog" is clicked on.
function clone_blog_init(){
  // Set an initial content variable, we will be echoing this at the end of the function.
  $cloneContent = "Blog ID: " . $_POST['blogId'] . "<br/>";
  
  // If we have post data, and we have a blog ID set (something to clone), 
  // and we have a path set lets do it (clone the blog).
  if($_POST && isset($_POST['blogId']) && isset($_POST['path'])){
    // We want access to the database so tell the function that $wpdb is a global variable
    global $wpdb;
    
    
    $blogId = $_POST['blogId'];
    $domain = "amath.colorado.edu";
    $path = $_POST['path'];
    $title = "New Blog";
    $user_id = 1;
    $cloneId = wpmu_create_blog($domain, $path, $title, $user_id);
    
    /*
    // Table name that we need the status of (to get the clone's id)
    $tablename = "wp_blogs";
    // Initialize it to something, just to be safe (assuming no blogs were deleted - ever - this will be the same as the final id is.
    $cloneId = 1 + get_blog_count();
    // Query to get the table's status (including the increment value for the blog_id)
    $QueryStatus = "SHOW TABLE STATUS LIKE '$tablename'";
    // Get the results of the query (as an associative array)
    $QueryStatusResult = $wpdb->get_results($QueryStatus, "ARRAY_A");
    // get the clone's id from the results returned from the query
    $cloneId = $QueryStatusResult[0]['Auto_increment'];
    
    if(CLONE_DEBUG){
      $cloneContent .= "Clone ID: " . $cloneId . "<br/>";
    }
    
    
    
//    $cloneId = 1+get_blog_count();
    // Get the path information from the POST data
    $path = $_POST['path'];
    // Split the path apart to get an array of directories
    $pathParts = explode("/", $path);
    // Take the last directory (in an array such as this: /amath/courses/1350/) and set it to the course number (ie: 1350)
    $courseNum = $pathParts[3];
    // Get the list of blogs (in a serialized format) from the database
    $unserializedBlogList = unserialize($wpdb->get_var("SELECT meta_value FROM wp_sitemeta WHERE meta_key = 'blog_list'"));
    // set the current blog id to what was sent in (from POST)
    $blogId = $_POST['blogId'];
    
    // set a variable aside for later use
    $cloneBlog;
    // loop through each of the (now) unserialized blogs, call them $blogList for simplicity
    foreach($unserializedBlogList AS $blogList){
      /// If the blog's id is equal to the one we are cloning
      if($blogList['blog_id'] == $blogId){
        // set the clone blog variable (set aside directory before the loop) to the current blog we are looking at
        $cloneBlog = $blogList;
        // Break out of the loop, we are done looking
        break;
      // end if
      }
    //end for
    }
    unset($blogList); // clean up
    // Update the clone blog's blog_id (set it equal to the clone id variable);
    // NOTE - The blog_id is supposed to be a string, hence the concat in front.
    $cloneBlog['blog_id'] = "" . $cloneId;
    // Update the clone's path (for the first time)
    $cloneBlog['path'] = $path;
    
    // push the new cloned blog to the end of our blog array
    $unserializedBlogList[] = $cloneBlog;
    // serialize the blogs array and set it to FinalBlogList - for use later
    $FinalBlogList = serialize($unserializedBlogList);
    
    // commented out from debug
    //die(var_dump($FinalBlogList));
    
    */
    
    // Create an array for all the queries we are about to create (so we can loop through them a little later easily)
    $queryArray = array();
    
    $queryArray['dropAllCloneTables'] = "DROP TABLE wp_" . $cloneId . "_commentmeta, wp_" . $cloneId . "_comments, wp_" . $cloneId . "_links, wp_" . $cloneId . "_options, wp_" . $cloneId . "_postmeta, wp_" . $cloneId . "_posts, wp_" . $cloneId . "_term_relationships, wp_" . $cloneId . "_term_taxonomy, wp_" . $cloneId . "_terms";
    
    
    // Create all the queries to be done later on in the code.
    // NOTE - All of these next few queries are done in the if(CLONE_LIVE) block below. 
    //        They are defined here so we can output them when DEBUGGING.
    // NOTICE - We are using "CREATE TABLE IF NOT EXISTS", this is because we don't want to accidentally overwrite 
    //        anything important (such as a pre-existing blog that has an ID greater than the number of blogs. This 
    //        may also cause problems as it is possible to have a clone which should have the same ID as another 
    //        blog, and for that reason won't be created.
    // FIXME - Update all the queries to use the wordpress database api better. At the moment we are just using the 
    //        query method, but that is likely not the best option for most of this.
    // {
    // wp_#_commentmeta
    // Create the new table (for the clone) and copy EVERYTHING from the host's tables
    $queryArray['commentMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_". $cloneId . "_commentmeta SELECT * FROM wp_" . $blogId . "_commentmeta";
    
    //repeat previous for the next 8 tables
    // {
    // wp_#_comments
    $queryArray['commentsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_comments SELECT * FROM wp_" . $blogId . "_comments";
    
    // wp_#_links
    $queryArray['linksQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_links SELECT * FROM wp_" . $blogId . "_links";
  
    // wp_#_options
    $queryArray['optionsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_options SELECT * FROM wp_" . $blogId . "_options";
    
    // wp_#_postmeta
    $queryArray['postMetaQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_postmeta SELECT * FROM wp_" . $blogId . "_postmeta";
    
    // wp_#_posts
    $queryArray['postsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_posts SELECT * FROM wp_" . $blogId . "_posts";
    
    // wp_#_term_relationships
    $queryArray['termRelationshipsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_relationships SELECT * FROM wp_" . $blogId . "_term_relationships";
    
    // wp_#_term_taxonomy
    $queryArray['termTaxonomyQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_term_taxonomy SELECT * FROM wp_" . $blogId . "_term_taxonomy";
    
    // wp_#_terms
    $queryArray['termsQuery'] = "CREATE TABLE IF NOT EXISTS wp_" . $cloneId . "_terms SELECT * FROM wp_" . $blogId . "_terms";
    // }
    // end repitition of the table cloning
    
    // insert the blog to the wp_blogs table:
    // NOTICE - The 'archived' value is the character zero ('0') and not the numeric value. 
    //   If you try to a number of boolean it will truncate the field and the blog won't show up in a blog list.
    // NOTICE - We don't provide the blog_id variable so that the database table will auto-increment as normal.
    // NOTE - This should no longer be useful.
    //$queryArray['blogQuery'] = $wpdb->prepare("INSERT INTO `wp_blogs` (`site_id`, `domain`, `path`, `registered`, `last_updated`, `public`, `archived`, `mature`, `spam`, `deleted`, `lang_id`) VALUES(1, 'amath.colorado.edu', %s, '2010-05-28 22:17:59', '2010-06-08 20:34:48', 1, '0', 0, 0, 0, 0)", $path);
    
    // Update the options table for the clone. Set the siteurl equal to the path passed in
    $queryArray['updateQueryOne'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='siteurl'";
    
    // update the options table for the clone. Set the home url to the path passed in.
    $queryArray['updateQueryTwo'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu" . $path . "' WHERE option_name='home'";
    
    // update, set upload url to the course number
    $queryArray['updateQueryThree'] = "UPDATE wp_" . $cloneId . "_options SET option_value='http://amath.colorado.edu/amath/courses/" . $courseNum . "/files' WHERE option_name='fileupload_url'";
    
    // set upload path to the clone's id
    $queryArray['updateQueryFour'] = "UPDATE wp_" . $cloneId . "_options SET option_value='wp-content/blogs.dir/" . $cloneId . "/files' WHERE option_name='upload_path'";
    
    // set blog count the updated blog count (which coincides with clondId)
    // NOTE - Should no longer be useful.
    //$queryArray['updateQueryFive'] = "UPDATE wp_sitemeta SET meta_value = " . $cloneId . " WHERE meta_key = 'blog_count'";
  
    // update the list of blogs to the FinalBlogList from earlier.
    // NOTE - This should no longer be useful.
    //$queryArray['updateQuerySix'] = "UPDATE wp_sitemeta SET meta_value = '" . $FinalBlogList . "' WHERE meta_key = 'blog_list'";
    
    // change an option name to reflect the correct blog (the clone).
    $queryArray['updateQuerySeven'] = "UPDATE wp_" . $clondId . "_options SET option_name = 'wp_" . $cloneId . "_user_roles' WHERE option_name = 'wp_" . $blogId . "_user_roles'";
    // }
    // End creating queries to be done.
    
    foreach($queryArray AS $queryKey => $queryValue){
      // if on a live site
      if(CLONE_LIVE){
        // make it so
        $wpdb->query($queryValue);
        // end if on live site
      }
      // if debugging
      if(CLONE_DEBUG){
        // add the query to our output buffer
        $cloneContent .= $queryKey . ": " . $queryValue . "<br/>";
        // end if debugging
      }
    } // end if CLONE_LIVE
    unset($queryKey, $queryValue); // Clean up
    
    // if we are live (otherwise there is no table wp_<CLONE_ID>_posts so a warning would be thrown.
    if(CLONE_LIVE){
      $updateQueryEight = ""; // for use in just a second.
      $allPosts = $wpdb->get_results("SELECT guid, ID FROM wp_" . $cloneId . "_posts WHERE guid != ''", "ARRAY_A"); // get all the posts from our table
      foreach($allPosts AS $post){ // loop through each post
        $url = $post['guid'];
        $courseParts = explode("courses", $url);
        $urlParts = explode("/", $courseParts[1]);
        // If the first character after courses is a slash we know the ourse number will be directly after that
        if(substr($courseParts[1], 0, 1) == '/'){
          // update the course number
          $urlParts[1] = $courseNum;
          // end if the first character was a slash
        }
        // otherwise
        else{
          // update the course number
          $urlParts[0] = $courseNum;
          // end else
        }
        // remake the url:
        $urlConstructor = implode("/", $urlParts);
        $courseParts[1] = $urlConstructor;
        $finalURL = implode("courses", $courseParts);
        // Update every post's guid
        $wpdb->query("UPDATE wp_" . $cloneId . "_posts SET guid = '" . $finalURL . "' WHERER ID = " . $post['ID'] . "");
        
        // if we are in debug mode
        if(CLONE_DEBUG){
          // add difference between the two URLs to our output buffer
          $cloneContent .= $url . " -> " . $finalURL . "<br/>";
          // end if CLONE_DEBUG
        }
        // end foreach $allPosts
      }
      unset($post); // Clean up

      // Clear the clone's cache and recreate it.
      refresh_blog_details($cloneId);
      // Set the content to success.
      $cloneContent .= "The blog was successfully cloned!<br/>Clone ID: " . $cloneId;
      // end if CLONE_LIVE
    }
    // end if
  }
  // If we don't have any POST data
  else{
    // Get a list of the blogs, we want all of them
    $cloneBlogList = get_blog_list(0, 'all');
    // Start creating a form for the page
    $cloneContent .= "<form action='#' method='post'><select name='blogId'>";
    // loop through each of the blogs in the list, call each individual blog a "cloneBlog"
    foreach($cloneBlogList AS $cloneBlog){
      // FIXME - Please say there is a more elegant way of doing this. Otherwise it is a bit of extra overhead 
      //        that isn't really necessary...
      // for each of the cloneBlogs get their details;
      $cloneBlogDetails = get_blog_details($cloneBlog['blog_id']);
      // From their details and id we can create an option for the cloning process.
      $cloneContent .= "<option value='" . $cloneBlog['blog_id'] . "'>" . $cloneBlogDetails->blogname . "</option>";
      // end for
    }
    unset($cloneBlog); // clean up.
    // Finish off the form
    $cloneContent .= "</select><br/>New Blog Path: <input type='text' name='path'/><br/><input type='submit' value='Clone Blog'/></form>";
  // end else
  }
  // output our buffer to the page
  echo $cloneContent;
// end function
}
?>


Gets the clone to have all the same options as the original, but doesn't allow for any posting or post modifications (and as a result doesn't work).

Any ideas?
Was This Post Helpful? 0
  • +
  • -

#3 pdmtg  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 14-January 11

Re: WPMU Blog Clone Plugin Problems

Posted 14 January 2011 - 10:33 AM

Have you been able to make this work?
Was This Post Helpful? 0
  • +
  • -

#4 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1107
  • View blog
  • Posts: 6,924
  • Joined: 07-September 06

Re: WPMU Blog Clone Plugin Problems

Posted 14 January 2011 - 10:37 AM

For my requirements I believe I have.
Was This Post Helpful? 0
  • +
  • -

#5 seo tools  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 17-January 12

Re: WPMU Blog Clone Plugin Problems

Posted 22 January 2012 - 08:56 AM

Matchless topic, it is very interesting to me )))) tHANKS www.dreamincode.net tEAM
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1