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