4
|
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 |
?> |