| [ Index ] |
WordPress Source Cross Reference |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 Plugin Name: WordPress Database Backup 4 Plugin URI: http://www.skippy.net/blog/plugins/ 5 Description: On-demand backup of your WordPress database. 6 Author: Scott Merrill 7 Version: 1.7 8 Author URI: http://www.skippy.net/ 9 10 Much of this was modified from Mark Ghosh's One Click Backup, which 11 in turn was derived from phpMyAdmin. 12 13 Many thanks to Owen (http://asymptomatic.net/wp/) for his patch 14 http://dev.wp-plugins.org/ticket/219 15 */ 16 17 // CHANGE THIS IF YOU WANT TO USE A 18 // DIFFERENT BACKUP LOCATION 19 20 $rand = substr( md5( md5( DB_PASSWORD ) ), -5 ); 21 22 define('WP_BACKUP_DIR', 'wp-content/backup-' . $rand); 23 24 define('ROWS_PER_SEGMENT', 100); 25 26 class wpdbBackup { 27 28 var $backup_complete = false; 29 var $backup_file = ''; 30 var $backup_dir = WP_BACKUP_DIR; 31 var $backup_errors = array(); 32 var $basename; 33 // Simple table name storage 34 var $wp_table_names = array('categories','comments','link2cat','links','options','post2cat','postmeta','posts','users','usermeta'); 35 36 function gzip() { 37 return function_exists('gzopen'); 38 } 39 40 function wpdbBackup() { 41 add_action('wp_cron_daily', array(&$this, 'wp_cron_daily')); 42 43 $this->backup_dir = trailingslashit($this->backup_dir); 44 $this->basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__); 45 46 if (isset($_POST['do_backup'])) { 47 if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.')); 48 switch($_POST['do_backup']) { 49 case 'backup': 50 $this->perform_backup(); 51 break; 52 case 'fragments': 53 add_action('admin_menu', array(&$this, 'fragment_menu')); 54 break; 55 } 56 } elseif (isset($_GET['fragment'] )) { 57 if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.')); 58 add_action('init', array(&$this, 'init')); 59 } elseif (isset($_GET['backup'] )) { 60 if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.')); 61 add_action('init', array(&$this, 'init')); 62 } else { 63 add_action('admin_menu', array(&$this, 'admin_menu')); 64 } 65 } 66 67 function init() { 68 if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.')); 69 70 if (isset($_GET['backup'])) { 71 $via = isset($_GET['via']) ? $_GET['via'] : 'http'; 72 73 $this->backup_file = $_GET['backup']; 74 75 switch($via) { 76 case 'smtp': 77 case 'email': 78 $this->deliver_backup ($this->backup_file, 'smtp', $_GET['recipient']); 79 echo ' 80 <!-- ' . $via . ' --> 81 <script type="text/javascript"><!--\\ 82 '; 83 if($this->backup_errors) { 84 foreach($this->backup_errors as $error) { 85 echo "window.parent.addError('$error');\n"; 86 } 87 } 88 echo ' 89 alert("' . __('Backup Complete!') . '"); 90 </script> 91 '; 92 break; 93 default: 94 $this->deliver_backup ($this->backup_file, $via); 95 } 96 die(); 97 } 98 if (isset($_GET['fragment'] )) { 99 list($table, $segment, $filename) = explode(':', $_GET['fragment']); 100 $this->backup_fragment($table, $segment, $filename); 101 } 102 103 die(); 104 } 105 106 function build_backup_script() { 107 global $wpdb; 108 109 $datum = date("Ymd_B"); 110 $backup_filename = DB_NAME . '_' . $wpdb->prefix . $datum . '.sql'; 111 if ($this->gzip()) $backup_filename .= '.gz'; 112 113 echo "<div class='wrap'>"; 114 //echo "<pre>" . print_r($_POST, 1) . "</pre>"; 115 echo '<h2>' . __('Backup') . '</h2> 116 <fieldset class="options"><legend>' . __('Progress') . '</legend> 117 <p><strong>' . 118 __('DO NOT DO THE FOLLOWING AS IT WILL CAUSE YOUR BACKUP TO FAIL:'). 119 '</strong></p> 120 <ol> 121 <li>'.__('Close this browser').'</li> 122 <li>'.__('Reload this page').'</li> 123 <li>'.__('Click the Stop or Back buttons in your browser').'</li> 124 </ol> 125 <p><strong>' . __('Progress:') . '</strong></p> 126 <div id="meterbox" style="height:11px;width:80%;padding:3px;border:1px solid #659fff;"><div id="meter" style="height:11px;background-color:#659fff;width:0%;text-align:center;font-size:6pt;"> </div></div> 127 <div id="progress_message"></div> 128 <div id="errors"></div> 129 </fieldset> 130 <iframe id="backuploader" src="about:blank" style="border:0px solid white;height:1em;width:1em;"></iframe> 131 <script type="text/javascript"><!--// 132 function setMeter(pct) { 133 var meter = document.getElementById("meter"); 134 meter.style.width = pct + "%"; 135 meter.innerHTML = Math.floor(pct) + "%"; 136 } 137 function setProgress(str) { 138 var progress = document.getElementById("progress_message"); 139 progress.innerHTML = str; 140 } 141 function addError(str) { 142 var errors = document.getElementById("errors"); 143 errors.innerHTML = errors.innerHTML + str + "<br />"; 144 } 145 146 function backup(table, segment) { 147 var fram = document.getElementById("backuploader"); 148 fram.src = "' . $_SERVER['REQUEST_URI'] . '&fragment=" + table + ":" + segment + ":' . $backup_filename . '"; 149 } 150 151 var curStep = 0; 152 153 function nextStep() { 154 backupStep(curStep); 155 curStep++; 156 } 157 158 function finishBackup() { 159 var fram = document.getElementById("backuploader"); 160 setMeter(100); 161 '; 162 163 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__); 164 $download_uri = get_settings('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$backup_filename}"; 165 switch($_POST['deliver']) { 166 case 'http': 167 echo ' 168 setProgress("' . sprintf(__("Backup complete, preparing <a href=\\\"%s\\\">backup</a> for download..."), $download_uri) . '"); 169 fram.src = "' . $download_uri . '"; 170 '; 171 break; 172 case 'smtp': 173 echo ' 174 setProgress("' . sprintf(__("Backup complete, sending <a href=\\\"%s\\\">backup</a> via email..."), $download_uri) . '"); 175 fram.src = "' . $download_uri . '&via=email&recipient=' . $_POST['backup_recipient'] . '"; 176 '; 177 break; 178 default: 179 echo ' 180 setProgress("' . sprintf(__("Backup complete, download <a href=\\\"%s\\\">here</a>."), $download_uri) . '"); 181 '; 182 } 183 184 echo ' 185 } 186 187 function backupStep(step) { 188 switch(step) { 189 case 0: backup("", 0); break; 190 '; 191 192 $also_backup = array(); 193 if (isset($_POST['other_tables'])) { 194 $also_backup = $_POST['other_tables']; 195 } else { 196 $also_backup = array(); 197 } 198 $core_tables = $_POST['core_tables']; 199 $tables = array_merge($core_tables, $also_backup); 200 $step_count = 1; 201 foreach ($tables as $table) { 202 $rec_count = $wpdb->get_var("SELECT count(*) FROM {$table}"); 203 $rec_segments = ceil($rec_count / ROWS_PER_SEGMENT); 204 $table_count = 0; 205 do { 206 echo "case {$step_count}: backup(\"{$table}\", {$table_count}); break;\n"; 207 $step_count++; 208 $table_count++; 209 } while($table_count < $rec_segments); 210 echo "case {$step_count}: backup(\"{$table}\", -1); break;\n"; 211 $step_count++; 212 } 213 echo "case {$step_count}: finishBackup(); break;"; 214 215 echo ' 216 } 217 if(step != 0) setMeter(100 * step / ' . $step_count . '); 218 } 219 220 nextStep(); 221 //--></script> 222 </div> 223 '; 224 } 225 226 function backup_fragment($table, $segment, $filename) { 227 global $wpdb; 228 229 echo "$table:$segment:$filename"; 230 231 if($table == '') { 232 $msg = __('Creating backup file...'); 233 } else { 234 if($segment == -1) { 235 $msg = sprintf(__('Finished backing up table \\"%s\\".'), $table); 236 } else { 237 $msg = sprintf(__('Backing up table \\"%s\\"...'), $table); 238 } 239 } 240 241 echo '<script type="text/javascript"><!--// 242 var msg = "' . $msg . '"; 243 window.parent.setProgress(msg); 244 '; 245 246 if (is_writable(ABSPATH . $this->backup_dir)) { 247 $this->fp = $this->open(ABSPATH . $this->backup_dir . $filename, 'a'); 248 if(!$this->fp) { 249 $this->backup_error(__('Could not open the backup file for writing!')); 250 $this->fatal_error = __('The backup file could not be saved. Please check the permissions for writing to your backup directory and try again.'); 251 } 252 else { 253 if($table == '') { 254 //Begin new backup of MySql 255 $this->stow("# WordPress MySQL database backup\n"); 256 $this->stow("#\n"); 257 $this->stow("# Generated: " . date("l j. F Y H:i T") . "\n"); 258 $this->stow("# Hostname: " . DB_HOST . "\n"); 259 $this->stow("# Database: " . $this->backquote(DB_NAME) . "\n"); 260 $this->stow("# --------------------------------------------------------\n"); 261 } else { 262 if($segment == 0) { 263 // Increase script execution time-limit to 15 min for every table. 264 if ( !ini_get('safe_mode')) @set_time_limit(15*60); 265 //ini_set('memory_limit', '16M'); 266 // Create the SQL statements 267 $this->stow("# --------------------------------------------------------\n"); 268 $this->stow("# Table: " . $this->backquote($table) . "\n"); 269 $this->stow("# --------------------------------------------------------\n"); 270 } 271 $this->backup_table($table, $segment); 272 } 273 } 274 } else { 275 $this->backup_error(__('The backup directory is not writeable!')); 276 $this->fatal_error = __('The backup directory is not writeable! Please check the permissions for writing to your backup directory and try again.'); 277 } 278 279 if($this->fp) $this->close($this->fp); 280 281 if($this->backup_errors) { 282 foreach($this->backup_errors as $error) { 283 echo "window.parent.addError('$error');\n"; 284 } 285 } 286 if($this->fatal_error) { 287 echo ' 288 alert("' . addslashes($this->fatal_error) . '"); 289 //--></script> 290 '; 291 } 292 else { 293 echo ' 294 window.parent.nextStep(); 295 //--></script> 296 '; 297 } 298 299 die(); 300 } 301 302 function perform_backup() { 303 // are we backing up any other tables? 304 $also_backup = array(); 305 if (isset($_POST['other_tables'])) { 306 $also_backup = $_POST['other_tables']; 307 } 308 309 $core_tables = $_POST['core_tables']; 310 $this->backup_file = $this->db_backup($core_tables, $also_backup); 311 if (FALSE !== $this->backup_file) { 312 if ('smtp' == $_POST['deliver']) { 313 $this->deliver_backup ($this->backup_file, $_POST['deliver'], $_POST['backup_recipient']); 314 } elseif ('http' == $_POST['deliver']) { 315 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__); 316 header('Refresh: 3; ' . get_settings('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$this->backup_file}"); 317 } 318 // we do this to say we're done. 319 $this->backup_complete = true; 320 } 321 } 322 323 /////////////////////////////// 324 function admin_menu() { 325 add_management_page(__('Backup'), __('Backup'), 9, basename(__FILE__), array(&$this, 'backup_menu')); 326 } 327 328 function fragment_menu() { 329 add_management_page(__('Backup'), __('Backup'), 9, basename(__FILE__), array(&$this, 'build_backup_script')); 330 } 331 332 ///////////////////////////////////////////////////////// 333 function sql_addslashes($a_string = '', $is_like = FALSE) 334 { 335 /* 336 Better addslashes for SQL queries. 337 Taken from phpMyAdmin. 338 */ 339 if ($is_like) { 340 $a_string = str_replace('\\', '\\\\\\\\', $a_string); 341 } else { 342 $a_string = str_replace('\\', '\\\\', $a_string); 343 } 344 $a_string = str_replace('\'', '\\\'', $a_string); 345 346 return $a_string; 347 } // function sql_addslashes($a_string = '', $is_like = FALSE) 348 349 /////////////////////////////////////////////////////////// 350 function backquote($a_name) 351 { 352 /* 353 Add backqouotes to tables and db-names in 354 SQL queries. Taken from phpMyAdmin. 355 */ 356 if (!empty($a_name) && $a_name != '*') { 357 if (is_array($a_name)) { 358 $result = array(); 359 reset($a_name); 360 while(list($key, $val) = each($a_name)) { 361 $result[$key] = '`' . $val . '`'; 362 } 363 return $result; 364 } else { 365 return '`' . $a_name . '`'; 366 } 367 } else { 368 return $a_name; 369 } 370 } // function backquote($a_name, $do_it = TRUE) 371 372 ///////////// 373 function open($filename = '', $mode = 'w') { 374 if ('' == $filename) return false; 375 if ($this->gzip()) { 376 $fp = @gzopen($filename, $mode); 377 } else { 378 $fp = @fopen($filename, $mode); 379 } 380 return $fp; 381 } 382 383 ////////////// 384 function close($fp) { 385 if ($this->gzip()) { 386 gzclose($fp); 387 } else { 388 fclose($fp); 389 } 390 } 391 392 ////////////// 393 function stow($query_line) { 394 if ($this->gzip()) { 395 if(@gzwrite($this->fp, $query_line) === FALSE) { 396 backup_error(__('There was an error writing a line to the backup script:')); 397 backup_error(' ' . $query_line); 398 } 399 } else { 400 if(@fwrite($this->fp, $query_line) === FALSE) { 401 backup_error(__('There was an error writing a line to the backup script:')); 402 backup_error(' ' . $query_line); 403 } 404 } 405 } 406 407 function backup_error($err) { 408 if(count($this->backup_errors) < 20) { 409 $this->backup_errors[] = $err; 410 } elseif(count($this->backup_errors) == 20) { 411 $this->backup_errors[] = __('Subsequent errors have been omitted from this log.'); 412 } 413 } 414 415 ///////////////////////////// 416 function backup_table($table, $segment = 'none') { 417 global $wpdb; 418 419 /* 420 Taken partially from phpMyAdmin and partially from 421 Alain Wolf, Zurich - Switzerland 422 Website: http://restkultur.ch/personal/wolf/scripts/db_backup/ 423 424 Modified by Scott Merril (http://www.skippy.net/) 425 to use the WordPress $wpdb object 426 */ 427 428 $table_structure = $wpdb->get_results("DESCRIBE $table"); 429 if (! $table_structure) { 430 backup_errors(__('Error getting table details') . ": $table"); 431 return FALSE; 432 } 433 434 if(($segment == 'none') || ($segment == 0)) { 435 // 436 // Add SQL statement to drop existing table 437 $this->stow("\n\n"); 438 $this->stow("#\n"); 439 $this->stow("# Delete any existing table " . $this->backquote($table) . "\n"); 440 $this->stow("#\n"); 441 $this->stow("\n"); 442 $this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n"); 443 444 // 445 //Table structure 446 // Comment in SQL-file 447 $this->stow("\n\n"); 448 $this->stow("#\n"); 449 $this->stow("# Table structure of table " . $this->backquote($table) . "\n"); 450 $this->stow("#\n"); 451 $this->stow("\n"); 452 453 $create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N); 454 if (FALSE === $create_table) { 455 $this->backup_error(sprintf(__("Error with SHOW CREATE TABLE for %s."), $table)); 456 $this->stow("#\n# Error with SHOW CREATE TABLE for $table!\n#\n"); 457 } 458 $this->stow($create_table[0][1] . ' ;'); 459 460 if (FALSE === $table_structure) { 461 $this->backup_error(sprintf(__("Error getting table structure of %s"), $table)); 462 $this->stow("#\n# Error getting table structure of $table!\n#\n"); 463 } 464 465 // 466 // Comment in SQL-file 467 $this->stow("\n\n"); 468 $this->stow("#\n"); 469 $this->stow('# Data contents of table ' . $this->backquote($table) . "\n"); 470 $this->stow("#\n"); 471 } 472 473 if(($segment == 'none') || ($segment >= 0)) { 474 $ints = array(); 475 foreach ($table_structure as $struct) { 476 if ( (0 === strpos($struct->Type, 'tinyint')) || 477 (0 === strpos(strtolower($struct->Type), 'smallint')) || 478 (0 === strpos(strtolower($struct->Type), 'mediumint')) || 479 (0 === strpos(strtolower($struct->Type), 'int')) || 480 (0 === strpos(strtolower($struct->Type), 'bigint')) || 481 (0 === strpos(strtolower($struct->Type), 'timestamp')) ) { 482 $ints[strtolower($struct->Field)] = "1"; 483 } 484 } 485 486 487 // Batch by $row_inc 488 489 if($segment == 'none') { 490 $row_start = 0; 491 $row_inc = ROWS_PER_SEGMENT; 492 } else { 493 $row_start = $segment * ROWS_PER_SEGMENT; 494 $row_inc = ROWS_PER_SEGMENT; 495 } 496 497 do { 498 if ( !ini_get('safe_mode')) @set_time_limit(15*60); 499 $table_data = $wpdb->get_results("SELECT * FROM $table LIMIT {$row_start}, {$row_inc}", ARRAY_A); 500 501 /* 502 if (FALSE === $table_data) { 503 $wp_backup_error .= "Error getting table contents from $table\r\n"; 504 fwrite($fp, "#\n# Error getting table contents fom $table!\n#\n"); 505 } 506 */ 507 508