wp/wp-content/plugins/blog-copier/blog-copier.php
changeset 4 346c88efed21
child 5 5e2f62d02dcd
equal deleted inserted replaced
3:6d22aaa62d12 4:346c88efed21
       
     1 <?php
       
     2 /*
       
     3 Plugin Name: Blog Copier
       
     4 Plugin URI: http://wordpress.org/extend/plugins/blog-copier/
       
     5 Description: Enables superusers to copy existing sub blogs to new sub blogs.
       
     6 Version: 1.0.5
       
     7 Author: Modern Tribe, Inc.
       
     8 Network: true
       
     9 Author URI: http://tri.be
       
    10 
       
    11 Copyright: (C) 2012 Modern Tribe derived from (C) 2010 Ron Rennick, All rights reserved.
       
    12 
       
    13 See http://wpebooks.com/replicator/ for original code.
       
    14 
       
    15 This program is free software; you can redistribute it and/or modify
       
    16 it under the terms of the GNU General Public License as published by
       
    17 the Free Software Foundation; either version 2 of the License, or
       
    18 (at your option) any later version.
       
    19 
       
    20 This program is distributed in the hope that it will be useful,
       
    21 but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    23 GNU General Public License for more details.
       
    24 
       
    25 You should have received a copy of the GNU General Public License
       
    26 along with this program; if not, write to the Free Software
       
    27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    28 */
       
    29 
       
    30 // Block direct requests
       
    31 if ( !defined('ABSPATH') )
       
    32 	die('-1');
       
    33 
       
    34 if ( !class_exists('BlogCopier') ) {
       
    35 
       
    36 	/**
       
    37 	 * Blog Copier
       
    38 	 *
       
    39 	 * @package BlogCopier
       
    40 	 */
       
    41 	class BlogCopier {
       
    42 
       
    43 		private $_name;
       
    44 		private $_domain = 'blog-copier';
       
    45 
       
    46 		/**
       
    47 		 * Main constructor function
       
    48 		 */
       
    49 		public function __construct() {
       
    50 			add_action( 'network_admin_menu', array( $this, 'ms_add_page' ) );
       
    51 			add_filter( 'manage_sites_action_links', array( $this, 'add_site_action' ), 10, 2 );
       
    52 		}
       
    53 
       
    54 		/**
       
    55 		 * Add admin page to network admin menu
       
    56 		 */
       
    57 		public function ms_add_page() {
       
    58 			$this->setup_localization();
       
    59 			add_submenu_page( 'sites.php', $this->_name, $this->_name, 'manage_sites', $this->_domain, array( $this, 'admin_page' ) );
       
    60 		}
       
    61 
       
    62 		/**
       
    63 		 * Add "Copy Blog" link under each site in the sites list view.
       
    64 		 *
       
    65 		 * @param array $actions
       
    66 		 * @param int $blog_id
       
    67 		 * @return array $actions
       
    68 		 */
       
    69 		public function add_site_action( $actions, $blog_id ) {
       
    70 			if( !is_main_site( $blog_id ) ) {
       
    71 				$this->setup_localization();
       
    72 				$url = add_query_arg( array(
       
    73 					'page' => $this->_domain,
       
    74 					'blog' => $blog_id
       
    75 				), network_admin_url( 'sites.php' ) );
       
    76 				$nonce_string = sprintf( '%s-%s', $this->_domain, $blog_id );
       
    77 				$actions[$this->_domain] = '<a href="' . esc_url( wp_nonce_url( $url, $nonce_string ) ) . '">' . __( 'Copy', $this->_domain ) . '</a>';
       
    78 			}
       
    79 
       
    80 			return $actions;
       
    81 		}
       
    82 
       
    83 		/**
       
    84 		 * Admin page
       
    85 		 */
       
    86 		public function admin_page() {
       
    87 			global $wpdb, $current_site;
       
    88 
       
    89 			if( !current_user_can( 'manage_sites' ) )
       
    90 				wp_die( __( "Sorry, you don't have permissions to use this page.", $this->_domain ) );
       
    91 
       
    92 			$from_blog = false;
       
    93 			$copy_id = 0;
       
    94 			$nonce_string = sprintf( '%s-%s', $this->_domain, $copy_id );
       
    95 			if( isset($_GET['blog']) && wp_verify_nonce( $_GET['_wpnonce'], $nonce_string ) ) {
       
    96 				$copy_id = (int)$_GET['blog'];
       
    97 				$from_blog = get_blog_details( $copy_id );
       
    98 				if( $from_blog->site_id != $current_site->id ) {
       
    99 					$from_blog = false;
       
   100 				}
       
   101 			}
       
   102 			$from_blog_id = ( isset( $_POST['source_blog'] ) ) ? (int) $_POST['source_blog'] : -1;
       
   103 
       
   104 			if( isset($_POST[ 'action' ]) && $_POST[ 'action' ] == $this->_domain ) {
       
   105 				check_admin_referer( $this->_domain );
       
   106 				$blog = $_POST['blog'];
       
   107 				$domain = sanitize_user( str_replace( '/', '', $blog[ 'domain' ] ) );
       
   108 				$title = $blog[ 'title' ];
       
   109 				$copy_files = (isset($_POST['copy_files']) && $_POST['copy_files'] == '1') ? true : false;
       
   110 
       
   111 				if ( !$from_blog_id ) {
       
   112 					$msg = __( 'Please select a source blog.', $this->_domain );
       
   113 				} elseif ( empty( $domain ) ) {
       
   114 					$msg = __( 'Please enter a "New Blog Address".', $this->_domain );
       
   115 				} elseif ( empty( $title ) ) {
       
   116 					$msg = __( 'Please enter a "New Blog Title".', $this->_domain );
       
   117 				} else {
       
   118 					$msg = $this->copy_blog( $domain, $title, $from_blog_id, $copy_files );
       
   119 				}
       
   120 			} else {
       
   121 				$copy_files = true; // set the default for first page load
       
   122 			} ?>
       
   123 			<div class='wrap'><h2><?php echo $this->_name; ?></h2><?php
       
   124 
       
   125 			if( isset( $msg ) ) { ?>
       
   126 			<div id="message" class="updated fade"><p><strong><?php echo $msg; ?>
       
   127 			</strong></p></div><?php
       
   128 			}
       
   129 			if( !$from_blog ) {
       
   130 				$query = "SELECT b.blog_id, CONCAT(b.domain, b.path) as domain_path FROM {$wpdb->blogs} b " .
       
   131 					"WHERE b.site_id = {$current_site->id} && b.blog_id > 1 ORDER BY domain_path ASC LIMIT 10000";
       
   132 
       
   133 				$blogs = $wpdb->get_results( $query );
       
   134 			}
       
   135 			if( $from_blog || $blogs ) { ?>
       
   136 			<div class="wrap">
       
   137 			<h3><?php _e( 'Blog Copy Settings', $this->_domain ); ?></h3>
       
   138 				<form method="POST">
       
   139 					<input type="hidden" name="action" value="<?php echo $this->_domain; ?>" />
       
   140 						<table class="form-table">
       
   141 
       
   142 							<?php if( $from_blog ) { ?>
       
   143 							<tr>
       
   144 								<th scope='row'><?php _e( 'Source Blog to Copy', $this->_domain ); ?></th>
       
   145 								<td><strong><?php printf( '<a href="%s" target="_blank">%s</a>', $from_blog->siteurl, $from_blog->blogname ); ?></strong>
       
   146 								<input type="hidden" name="source_blog" value="<?php echo $copy_id; ?>" />
       
   147 								</td>
       
   148 							</tr>
       
   149 							<?php } else { ?>
       
   150 							<tr class="form-required">
       
   151 								<th scope='row'><?php _e( 'Choose Source Blog to Copy', $this->_domain ); ?></th>
       
   152 								<td>
       
   153 									<select name="source_blog">
       
   154 									<?php foreach( $blogs as $blog ) { ?>
       
   155 										<option value="<?php echo $blog->blog_id; ?>" <?php selected( $blog->blog_id, $from_blog_id ); ?>><?php echo substr($blog->domain_path, 0, -1); ?></option>
       
   156 									<?php } ?>
       
   157 									</select>
       
   158 								</td>
       
   159 							</tr>
       
   160 							<?php } ?>
       
   161 
       
   162 							<tr class="form-required">
       
   163 								<th scope='row'><?php _e( 'New Blog Address', $this->_domain ); ?></th>
       
   164 								<td>
       
   165 								<?php if( is_subdomain_install() ) { ?>
       
   166 									<input name="blog[domain]" type="text" title="<?php _e( 'Subdomain', $this->_domain ); ?>" class="regular-text"/>.<?php echo $current_site->domain;?>
       
   167 								<?php } else {
       
   168 									echo $current_site->domain . $current_site->path ?><input name="blog[domain]" type="text" title="<?php _e( 'Domain', $this->_domain ); ?>" class="regular-text"/>
       
   169 								<?php } ?>
       
   170 								</td>
       
   171 							</tr>
       
   172 
       
   173 							<tr class="form-required">
       
   174 								<th scope='row'><?php _e( 'New Blog Title', $this->_domain ); ?></th>
       
   175 								<td><input name="blog[title]" type="text" title="<?php _e( 'Title', $this->_domain ); ?>" class="regular-text"/></td>
       
   176 							</tr>
       
   177 
       
   178 							<tr class="form-required">
       
   179 								<th scope='row'><?php _e( 'Copy Files?', $this->_domain ); ?></th>
       
   180 								<td><input type="checkbox" name="copy_files" value="1" <?php checked( $copy_files ); ?>/></td>
       
   181 							</tr>
       
   182 
       
   183 						</table>
       
   184 						<?php wp_nonce_field( $this->_domain ); ?>
       
   185 					<p class="submit"><input class='button' type='submit' value='<?php _e( 'Copy Now', $this->_domain ); ?>' /></p>
       
   186 				</form></div>
       
   187 			<?php } else { ?>
       
   188 				<div class="wrap">
       
   189 					<h3><?php _e( 'Oops!', $this->_domain ); ?></h3>
       
   190 					<p><?php
       
   191 					printf( __( 'This plugin only works on subblogs. To use this you\'ll need to <a href="%s">create at least one subblog</a>.', $this->_domain ), network_admin_url( 'site-new.php' ) );
       
   192 					?></p>
       
   193 				</div>
       
   194 			<?php }
       
   195 		}
       
   196 
       
   197 		/**
       
   198 		 * Copy the blog
       
   199 		 *
       
   200 		 * @param string $domain url of the new blog
       
   201 		 * @param string $title title of the new blog
       
   202 		 * @param int $from_blog_id ID of the blog being copied from.
       
   203 		 * @param bool $copy_files true if files should be copied
       
   204 		 * @return string status message
       
   205 		 */
       
   206 		public function copy_blog($domain, $title, $from_blog_id = 0, $copy_files = true) {
       
   207 			global $wpdb, $current_site, $base;
       
   208 
       
   209 			$email = get_blog_option( $from_blog_id, 'admin_email' );
       
   210 			$user_id = email_exists( sanitize_email( $email ) );
       
   211 			if( !$user_id ) {
       
   212 				// Use current user instead
       
   213 				$user_id = get_current_user_id();
       
   214 			}
       
   215 			// The user id of the user that will become the blog admin of the new blog.
       
   216 			$user_id = apply_filters('copy_blog_user_id', $user_id, $from_blog_id);
       
   217 
       
   218 			if( is_subdomain_install() ) {
       
   219 				$newdomain = $domain.".".$current_site->domain;
       
   220 				$path = $base;
       
   221 			} else {
       
   222 				$newdomain = $current_site->domain;
       
   223 				$path = trailingslashit( $base ) . trailingslashit( $domain );
       
   224 			}
       
   225 
       
   226 			// The new domain that will be created for the destination blog.
       
   227 			$newdomain = apply_filters('copy_blog_domain', $newdomain, $domain);
       
   228 
       
   229 			// The new path that will be created for the destination blog.
       
   230 			$path = apply_filters('copy_blog_path', $path, $domain);
       
   231 
       
   232 			$wpdb->hide_errors();
       
   233 			$to_blog_id = wpmu_create_blog( $newdomain, $path, $title, $user_id , array( "public" => 1 ), $current_site->id );
       
   234 			$wpdb->show_errors();
       
   235 
       
   236 			if( !is_wp_error( $to_blog_id ) ) {
       
   237 				$dashboard_blog = get_dashboard_blog();
       
   238 				if( !is_super_admin() && get_user_option( 'primary_blog', $user_id ) == $dashboard_blog->blog_id )
       
   239 					update_user_option( $user_id, 'primary_blog', $to_blog_id, true );
       
   240 
       
   241 				// now copy
       
   242 				if( $from_blog_id ) {
       
   243 
       
   244 					$this->copy_blog_data( $from_blog_id, $to_blog_id );
       
   245 
       
   246 					if ($copy_files) {
       
   247 
       
   248 						$this->copy_blog_files( $from_blog_id, $to_blog_id );
       
   249 						$this->replace_content_urls( $from_blog_id, $to_blog_id );
       
   250 
       
   251 					}
       
   252 					$msg = sprintf(__( 'Copied: %s in %s seconds', $this->_domain ),'<a href="http://'.$newdomain.'" target="_blank">'.$title.'</a>', number_format_i18n(timer_stop()));
       
   253 					do_action( 'log', __( 'Copy Complete!', $this->_domain ), $this->_domain, $msg );
       
   254 				}
       
   255 			} else {
       
   256 				$msg = $to_blog_id->get_error_message();
       
   257 			}
       
   258 			return $msg;
       
   259 		}
       
   260 
       
   261 		/**
       
   262 		 * Copy blog data from one blog to another
       
   263 		 *
       
   264 		 * @param int $from_blog_id ID of the blog being copied from.
       
   265 		 * @param int $to_blog_id ID of the blog being copied to.
       
   266 		 */
       
   267 		private function copy_blog_data( $from_blog_id, $to_blog_id ) {
       
   268 			global $wpdb, $wp_version;
       
   269 			if( $from_blog_id ) {
       
   270 				$from_blog_prefix = $this->get_blog_prefix( $from_blog_id );
       
   271 				$to_blog_prefix = $this->get_blog_prefix( $to_blog_id );
       
   272 				$from_blog_prefix_length = strlen($from_blog_prefix);
       
   273 				$to_blog_prefix_length = strlen($to_blog_prefix);
       
   274 				$from_blog_escaped_prefix = str_replace( '_', '\_', $from_blog_prefix );
       
   275 
       
   276 				// Grab key options from new blog.
       
   277 				$saved_options = array(
       
   278 					'siteurl'=>'',
       
   279 					'home'=>'',
       
   280 					'upload_path'=>'',
       
   281 					'fileupload_url'=>'',
       
   282 					'upload_url_path'=>'',
       
   283 					'admin_email'=>'',
       
   284 					'blogname'=>''
       
   285 				);
       
   286 				// Options that should be preserved in the new blog.
       
   287 				$saved_options = apply_filters('copy_blog_data_saved_options', $saved_options);
       
   288 				foreach($saved_options as $option_name => $option_value) {
       
   289 					$saved_options[$option_name] = get_blog_option( $to_blog_id, $option_name );
       
   290 				}
       
   291 
       
   292 				// Copy over ALL the tables.
       
   293 				$query = $wpdb->prepare('SHOW TABLES LIKE %s',$from_blog_escaped_prefix.'%');
       
   294 				do_action( 'log', $query, $this->_domain);
       
   295 				$old_tables = $wpdb->get_col($query);
       
   296 
       
   297 				foreach ($old_tables as $k => $table) {
       
   298 					$raw_table_name = substr( $table, $from_blog_prefix_length );
       
   299 					$newtable = $to_blog_prefix . $raw_table_name;
       
   300 
       
   301 					$query = "DROP TABLE IF EXISTS {$newtable}";
       
   302 					do_action( 'log', $query, $this->_domain);
       
   303 					$wpdb->get_results($query);
       
   304 
       
   305 					$query = "CREATE TABLE IF NOT EXISTS {$newtable} LIKE {$table}";
       
   306 					do_action( 'log', $query, $this->_domain);
       
   307 					$wpdb->get_results($query);
       
   308 
       
   309 					$query = "INSERT {$newtable} SELECT * FROM {$table}";
       
   310 					do_action( 'log', $query, $this->_domain);
       
   311 					$wpdb->get_results($query);
       
   312 				}
       
   313 
       
   314 				// apply key opptions from new blog.
       
   315 				switch_to_blog( $to_blog_id );
       
   316 				foreach( $saved_options as $option_name => $option_value ) {
       
   317 					update_option( $option_name, $option_value );
       
   318 				}
       
   319 
       
   320 				/// fix all options with the wrong prefix...
       
   321 				$query = $wpdb->prepare("SELECT * FROM {$wpdb->options} WHERE option_name LIKE %s",$from_blog_escaped_prefix.'%');
       
   322 				$options = $wpdb->get_results( $query );
       
   323 				do_action( 'log', $query, $this->_domain, count($options).' results found.');
       
   324 				if( $options ) {
       
   325 					foreach( $options as $option ) {
       
   326 						$raw_option_name = substr($option->option_name,$from_blog_prefix_length);
       
   327 						$wpdb->update( $wpdb->options, array( 'option_name' => $to_blog_prefix . $raw_option_name ), array( 'option_id' => $option->option_id ) );
       
   328 					}
       
   329 					wp_cache_flush();
       
   330 				}
       
   331 
       
   332 				// Fix GUIDs on copied posts
       
   333 				$this->replace_guid_urls( $from_blog_id, $to_blog_id );
       
   334 
       
   335 				restore_current_blog();
       
   336 			}
       
   337 		}
       
   338 
       
   339 		/**
       
   340 		 * Copy files from one blog to another.
       
   341 		 *
       
   342 		 * @param int $from_blog_id ID of the blog being copied from.
       
   343 		 * @param int $to_blog_id ID of the blog being copied to.
       
   344 		 */
       
   345 		private function copy_blog_files( $from_blog_id, $to_blog_id ) {
       
   346 			set_time_limit( 2400 ); // 60 seconds x 10 minutes
       
   347 			@ini_set('memory_limit','2048M');
       
   348 
       
   349 			// Path to source blog files.
       
   350 			switch_to_blog($from_blog_id);
       
   351 			$dir_info = wp_upload_dir();
       
   352 			$from = str_replace(' ', "\\ ", trailingslashit($dir_info['basedir']).'*'); // * necessary with GNU cp, doesn't hurt anything with BSD cp
       
   353 			restore_current_blog();
       
   354 			$from = apply_filters('copy_blog_files_from', $from, $from_blog_id);
       
   355 
       
   356 			// Path to destination blog files.
       
   357 			switch_to_blog($to_blog_id);
       
   358 			$dir_info = wp_upload_dir();
       
   359 			$to = str_replace(' ', "\\ ", trailingslashit($dir_info['basedir']));
       
   360 			restore_current_blog();
       
   361 			$to = apply_filters('copy_blog_files_to', $to, $to_blog_id);
       
   362 
       
   363 			// Shell command used to copy files.
       
   364 			$command = apply_filters('copy_blog_files_command', sprintf("cp -Rfp %s %s", $from, $to), $from, $to );
       
   365 			exec($command);
       
   366 		}
       
   367 
       
   368 		/**
       
   369 		 * Replace URLs in post content
       
   370 		 *
       
   371 		 * @param int $from_blog_id ID of the blog being copied from.
       
   372 		 * @param int $to_blog_id ID of the blog being copied to.
       
   373 		 */
       
   374 		private function replace_content_urls( $from_blog_id, $to_blog_id ) {
       
   375 			global $wpdb;
       
   376 			$to_blog_prefix = $this->get_blog_prefix( $to_blog_id );
       
   377 			$from_blog_url = get_blog_option( $from_blog_id, 'siteurl' );
       
   378 			$to_blog_url = get_blog_option( $to_blog_id, 'siteurl' );
       
   379 			$query = $wpdb->prepare( "UPDATE {$to_blog_prefix}posts SET post_content = REPLACE(post_content, '%s', '%s')", $from_blog_url, $to_blog_url );
       
   380 			do_action( 'log', $query, $this->_domain);
       
   381 			$wpdb->query( $query );
       
   382 		}
       
   383 
       
   384 		/**
       
   385 		 * Replace URLs in post GUIDs
       
   386 		 *
       
   387 		 * @param int $from_blog_id ID of the blog being copied from.
       
   388 		 * @param int $to_blog_id ID of the blog being copied to.
       
   389 		 */
       
   390 		private function replace_guid_urls( $from_blog_id, $to_blog_id ) {
       
   391 			global $wpdb;
       
   392 			$to_blog_prefix = $this->get_blog_prefix( $to_blog_id );
       
   393 			$from_blog_url = get_blog_option( $from_blog_id, 'siteurl' );
       
   394 			$to_blog_url = get_blog_option( $to_blog_id, 'siteurl' );
       
   395 			$query = $wpdb->prepare( "UPDATE {$to_blog_prefix}posts SET guid = REPLACE(guid, '%s', '%s')", $from_blog_url, $to_blog_url );
       
   396 			do_action( 'log', $query, $this->_domain);
       
   397 			$wpdb->query( $query );
       
   398 		}
       
   399 
       
   400 		/**
       
   401 		 * Get the database prefix for a blog
       
   402 		 *
       
   403 		 * @param int $blog_id ID of the blog.
       
   404 		 * @return string prefix
       
   405 		 */
       
   406 		private function get_blog_prefix( $blog_id ) {
       
   407 			global $wpdb;
       
   408 			if( is_callable( array( &$wpdb, 'get_blog_prefix' ) ) ) {
       
   409 				$prefix = $wpdb->get_blog_prefix( $blog_id );
       
   410 			} else {
       
   411 				$prefix = $wpdb->base_prefix . $blog_id . '_';
       
   412 			}
       
   413 			return $prefix;
       
   414 		}
       
   415 
       
   416 		/**
       
   417 		 * Load the localization file
       
   418 		 */
       
   419 		private function setup_localization() {
       
   420 			if ( !isset( $this->_name ) ) {
       
   421 				load_plugin_textdomain( $this->_domain, false, trailingslashit(dirname(__FILE__)) . 'lang/');
       
   422 				$this->_name = __( 'Blog Copier', $this->_domain );
       
   423 			}
       
   424 		}
       
   425 
       
   426 	}
       
   427 
       
   428 	global $BlogCopier;
       
   429 	$BlogCopier = new BlogCopier();
       
   430 }
       
   431 ?>