srdb/index.php

2909 lines
196 KiB
PHP
Raw Permalink Normal View History

2017-01-16 16:05:08 +00:00
<?php
/**
*
* Safe Search and Replace on Database with Serialized Data v3.1.0
*
* This script is to solve the problem of doing database search and replace when
* some data is stored within PHP serialized arrays or objects.
*
* For more information, see
* http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
*
* To contribute go to
* http://github.com/interconnectit/search-replace-db
*
* To use, load the script on your server and point your web browser to it.
* In some situations, consider using the command line interface version.
*
* BIG WARNING! Take a backup first, and carefully test the results of this
* code. If you don't, and you vape your data then you only have yourself to
* blame. Seriously. And if your English is bad and you don't fully
* understand the instructions then STOP. Right there. Yes. Before you do any
* damage.
*
* USE OF THIS SCRIPT IS ENTIRELY AT YOUR OWN RISK. I/We accept no liability
* from its use.
*
* First Written 2009-05-25 by David Coveney of Interconnect IT Ltd (UK)
* http://www.davidcoveney.com or http://interconnectit.com
* and released under the GPL v3
* ie, do what ever you want with the code, and we take no responsibility for it
* OK? If you don't wish to take responsibility, hire us at Interconnect IT Ltd
* on +44 (0)151 331 5140 and we will do the work for you at our hourly rate,
* minimum 1hr
*
* License: GPL v3
* License URL: http://www.gnu.org/copyleft/gpl.html
*
*
* Version 3.1.0:
* * Added port number option to both web and CLI interfaces.
* * More reliable fallback on non-PDO systems.
* * Confirmation on 'Delete me'
* * Comprehensive check to prevent accidental deletion of web projects
* * Removed mysql functions and replaced with mysqli
*
* Version 3.0.0:
* * Major overhaul
* * Multibyte string replacements
* * UI completely redesigned
* * Removed all links from script until 'delete' has been clicked to avoid
* security risk from our access logs
* * Search replace functionality moved to it's own separate class
* * Replacements done table by table to avoid timeouts
* * Convert tables to InnoDB
* * Convert tables to utf8_unicode_ci
* * Use PDO if available
* * Preview/view changes
* * Optionally use preg_replace()
* * Scripts bootstraps WordPress/Drupal to avoid issues with unknown
* serialised objects/classes
* * Added marketing stuff to deleted screen (sorry but we're running a
* business!)
*
* Version 2.2.0:
* * Added remove script patch from David Anderson (wordshell.net)
* * Added ability to replace strings with nothing
* * Copy changes
* * Added code to recursive_unserialize_replace to deal with objects not
* just arrays. This was submitted by Tina Matter.
* ToDo: Test object handling. Not sure how it will cope with object in the
* db created with classes that don't exist in anything but the base PHP.
*
* Version 2.1.0:
* - Changed to version 2.1.0
* * Following change by Sergei Biryukov - merged in and tested by Dave Coveney
* - Added Charset Support (tested with UTF-8, not tested on other charsets)
* * Following changes implemented by James Whitehead with thanks to all the commenters and feedback given!
* - Removed PHP warnings if you go to step 3+ without DB details.
* - Added options to skip changing the guid column. If there are other
* columns that need excluding you can add them to the $exclude_cols global
* array. May choose to add another option to the table select page to let
* you add to this array from the front end.
* - Minor tweak to label styling.
* - Added comments to each of the functions.
* - Removed a dead param from icit_srdb_replacer
* Version 2.0.0:
* - returned to using unserialize function to check if string is
* serialized or not
* - marked is_serialized_string function as deprecated
* - changed form order to improve usability and make use on multisites a
* bit less scary
* - changed to version 2, as really should have done when the UI was
* introduced
* - added a recursive array walker to deal with serialized strings being
* stored in serialized strings. Yes, really.
* - changes by James R Whitehead (kudos for recursive walker) and David
* Coveney 2011-08-26
* Version 1.0.2:
* - typos corrected, button text tweak - David Coveney / Robert O'Rourke
* Version 1.0.1
* - styling and form added by James R Whitehead.
*
* Credits: moz667 at gmail dot com for his recursive_array_replace posted at
* uk.php.net which saved me a little time - a perfect sample for me
* and seems to work in all cases.
*
*/
// always good here
header( 'HTTP/1.1 200 OK' );
header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
header('Pragma: no-cache'); // HTTP 1.0.
header('Expires: 0'); // Proxies.
require_once( 'srdb.class.php' );
class icit_srdb_ui extends icit_srdb {
/**
* @var string Root path of the CMS
*/
public $path;
public $is_wordpress = false;
public $is_drupal = false;
public function __construct() {
// php 5.4 date timezone requirement, shouldn't affect anything
date_default_timezone_set( 'Europe/London' );
// prevent fatals from hiding the UI
register_shutdown_function( array( $this, 'fatal_handler' ) );
// flag to bootstrap WP or Drupal
$bootstrap = true; // isset( $_GET[ 'bootstrap' ] );
// discover environment
if ( $bootstrap && $this->is_wordpress() ) {
// prevent warnings if the charset and collate aren't defined
if ( !defined( 'DB_CHARSET') ) {
define( 'DB_CHARSET', 'utf8' );
}
if ( !defined( 'DB_COLLATE') ) {
define( 'DB_COLLATE', '' );
}
// populate db details
$name = DB_NAME;
$user = DB_USER;
$pass = DB_PASSWORD;
// Port and host need to be split apart.
if ( strstr( DB_HOST, ':' ) !== false ) {
$parts = explode( ':', DB_HOST );
$host = $parts[0];
$port_input = $parts[1];
$port = abs( (int)$port_input );
} else {
$host = DB_HOST;
$port = 3306;
}
$charset = DB_CHARSET;
$collate = DB_COLLATE;
$this->response( $name, $user, $pass, $host, $port, $charset, $collate );
} elseif( $bootstrap && $this->is_drupal() ) {
$database = Database::getConnection();
$database_opts = $database->getConnectionOptions();
// populate db details
$name = $database_opts[ 'database' ];
$user = $database_opts[ 'username' ];
$pass = $database_opts[ 'password' ];
$host = $database_opts[ 'host' ];
$port = $database_opts[ 'port' ];
$charset = 'utf8';
$collate = '';
$port_as_string = (string)$port ? (string)$port : "0";
if ( (string)abs( (int)$port ) !== $port_as_string ) {
$port = 3306;
} else {
$port = (string)abs( (int)$port );
}
$this->response( $name, $user, $pass, $host, $port, $charset, $collate );
} else {
$this->response();
}
}
public function response( $name = '', $user = '', $pass = '', $host = '127.0.0.1', $port = 3306, $charset = 'utf8', $collate = '' ) {
// always override with post data
if ( isset( $_POST[ 'name' ] ) ) {
$name = $_POST[ 'name' ]; // your database
$user = $_POST[ 'user' ]; // your db userid
$pass = $_POST[ 'pass' ]; // your db password
$host = $_POST[ 'host' ]; // normally localhost, but not necessarily.
$port_input = $_POST[ 'port' ];
// Make sure that the string version of absint(port) is identical to the string input.
// This prevents expressions, decimals, spaces, etc.
$port_as_string = (string)$port_input ? (string)$port_input : "0";
if ( (string)abs( (int)$port_input ) !== $port_as_string ) {
// Mangled port number: non numeric.
$this->add_error('Port number must be a positive integer. If you are unsure, try the default port 3306.', 'db');
// Force a bad run by supplying nonsense.
$port = "nonsense";
} else {
$port = abs( (int)$port_input );
}
$charset = 'utf8'; // isset( $_POST[ 'char' ] ) ? stripcslashes( $_POST[ 'char' ] ) : ''; // your db charset
$collate = '';
}
// Search replace details
$search = isset( $_POST[ 'search' ] ) ? $_POST[ 'search' ] : '';
$replace = isset( $_POST[ 'replace' ] ) ? $_POST[ 'replace' ] : '';
// regex options
$regex = isset( $_POST[ 'regex' ] );
$regex_i = isset( $_POST[ 'regex_i' ] );
$regex_m = isset( $_POST[ 'regex_m' ] );
$regex_s = isset( $_POST[ 'regex_s' ] );
$regex_x = isset( $_POST[ 'regex_x' ] );
// Tables to scanned
$tables = isset( $_POST[ 'tables' ] ) && is_array( $_POST[ 'tables' ] ) ? $_POST[ 'tables' ] : array( );
if ( isset( $_POST[ 'use_tables' ] ) && $_POST[ 'use_tables' ] == 'all' )
$tables = array();
// exclude / include columns
$exclude_cols = isset( $_POST[ 'exclude_cols' ] ) ? $_POST[ 'exclude_cols' ] : array();
$include_cols = isset( $_POST[ 'include_cols' ] ) ? $_POST[ 'include_cols' ] : array();
foreach( array( 'exclude_cols', 'include_cols' ) as $maybe_string_arg ) {
if ( is_string( $$maybe_string_arg ) )
$$maybe_string_arg = array_filter( array_map( 'trim', explode( ',', $$maybe_string_arg ) ) );
}
// update class vars
$vars = array(
'name', 'user', 'pass', 'host', 'port',
'charset', 'collate', 'tables',
'search', 'replace',
'exclude_cols', 'include_cols',
'regex', 'regex_i', 'regex_m', 'regex_s', 'regex_x'
);
foreach( $vars as $var ) {
if ( isset( $$var ) )
$this->set( $var, $$var );
}
// are doing something?
$show = '';
if ( isset( $_POST[ 'submit' ] ) ) {
if ( is_array( $_POST[ 'submit' ] ) )
$show = key( $_POST[ 'submit' ] );
if ( is_string( $_POST[ 'submit' ] ) )
$show = preg_replace( '/submit\[([a-z0-9]+)\]/', '$1', $_POST[ 'submit' ] );
}
// is it an AJAX call
$ajax = isset( $_POST[ 'ajax' ] );
// body callback
$html = 'ui';
switch( $show ) {
// remove search replace
case 'delete':
// determine if it's the folder of compiled version
if ( basename( __FILE__ ) == 'index.php' )
$path = str_replace( basename( __FILE__ ), '', __FILE__ );
else
$path = __FILE__;
$delete_script_success = $this->delete_script( $path );
if ( self::DELETE_SCRIPT_FAIL_UNSAFE === $delete_script_success) {
$this->add_error( 'Delete aborted! You seem to have placed Search/Replace into your WordPress or Drupal root. Please remove Search/Replace manually.', 'delete' );
} else {
if ( ( self::DELETE_SCRIPT_SUCCESS === $delete_script_success ) && !( is_file( __FILE__ ) && file_exists( __FILE__ ) ) ) {
$this->add_error( 'Search/Replace has been successfully removed from your server', 'delete' );
} else {
$this->add_error( 'Could not fully delete Search/Replace. You will have to delete it manually', 'delete' );
}
}
$html = 'deleted';
break;
case 'liverun':
// bsy-web, 20130621: Check live run was explicitly clicked and only set false then
$this->set( 'dry_run', false );
case 'dryrun':
// build regex string
// non UI implements can just pass in complete regex string
if ( $this->regex ) {
$mods = '';
if ( $this->regex_i ) $mods .= 'i';
if ( $this->regex_s ) $mods .= 's';
if ( $this->regex_m ) $mods .= 'm';
if ( $this->regex_x ) $mods .= 'x';
$this->search = '/' . $this->search . '/' . $mods;
}
// call search replace class
$parent = parent::__construct( array(
'name' => $this->get( 'name' ),
'user' => $this->get( 'user' ),
'pass' => $this->get( 'pass' ),
'host' => $this->get( 'host' ),
'port' => $this->get( 'port' ),
'search' => $this->get( 'search' ),
'replace' => $this->get( 'replace' ),
'tables' => $this->get( 'tables' ),
'dry_run' => $this->get( 'dry_run' ),
'regex' => $this->get( 'regex' ),
'exclude_cols' => $this->get( 'exclude_cols' ),
'include_cols' => $this->get( 'include_cols' )
) );
break;
case 'innodb':
// call search replace class to alter engine
$parent = parent::__construct( array(
'name' => $this->get( 'name' ),
'user' => $this->get( 'user' ),
'pass' => $this->get( 'pass' ),
'host' => $this->get( 'host' ),
'port' => $this->get( 'port' ),
'tables' => $this->get( 'tables' ),
'alter_engine' => 'InnoDB',
) );
break;
case 'utf8':
// call search replace class to alter engine
$parent = parent::__construct( array(
'name' => $this->get( 'name' ),
'user' => $this->get( 'user' ),
'pass' => $this->get( 'pass' ),
'host' => $this->get( 'host' ),
'port' => $this->get( 'port' ),
'tables' => $this->get( 'tables' ),
'alter_collation' => 'utf8_unicode_ci',
) );
break;
case 'utf8mb4':
// call search replace class to alter engine
$parent = parent::__construct( array(
'name' => $this->get( 'name' ),
'user' => $this->get( 'user' ),
'pass' => $this->get( 'pass' ),
'host' => $this->get( 'host' ),
'port' => $this->get( 'port' ),
'tables' => $this->get( 'tables' ),
'alter_collation' => 'utf8mb4_unicode_ci',
) );
break;
case 'update':
default:
// get tables or error messages
$this->db_setup();
if ( $this->db_valid() ) {
// get engines
$this->set( 'engines', $this->get_engines() );
// get tables
$this->set( 'all_tables', $this->get_tables() );
}
break;
}
$info = array(
'table_select' => $this->table_select( false ),
'engines' => $this->get( 'engines' )
);
// set header again before output in case WP does it's thing
header( 'HTTP/1.1 200 OK' );
if ( ! $ajax ) {
$this->html( $html );
} else {
// return json version of results
header( 'Content-Type: application/json' );
echo json_encode( array(
'errors' => $this->get( 'errors' ),
'report' => $this->get( 'report' ),
'info' => $info
) );
exit;
}
}
public function exceptions( $exception ) {
$this->add_error( '<p class="exception">' . $exception->getMessage() . '</p>' );
}
public function errors( $no, $message, $file, $line ) {
$this->add_error( '<p class="error">' . "<strong>{$no}:</strong> {$message} in {$file} on line {$line}" . '</p>', 'results' );
}
public function fatal_handler() {
$error = error_get_last();
if( $error !== NULL ) {
$errno = $error["type"];
$errfile = $error["file"];
$errline = $error["line"];
$errstr = $error["message"];
if ( $errno == 1 ) {
header( 'HTTP/1.1 200 OK' );
$this->add_error( '<p class="error">Could not bootstrap environment.<br /> ' . "Fatal error in {$errfile}, line {$errline}. {$errstr}" . '</p>', 'environment' );
$this->response();
}
}
}
/**
* Return an array of all files and directories recursively below $path.
*
* If $path is a file, returns an array containing just that filename.
*
* @param string $path directory/file path.
*
* @return array
*/
public function determine_all_files_below_path( $path ) {
// A file contains only 'itself'.
if ( is_file( $path ) ) {
return array( $path );
}
$directory_contents = glob( $path . '/*' );
$full_recursive_contents = array();
// Every directory contains all of its files, plus 'itself'.
foreach ( $directory_contents as $item_filename ) {
$full_recursive_contents = array_merge($full_recursive_contents, $this->determine_all_files_below_path( $item_filename ) );
}
$full_recursive_contents = array_merge($full_recursive_contents, array( $path ) );
return $full_recursive_contents;
}
/**
* @param $path Filename to inspect
*
* @return boolean true if it is most likely nothing to do with WordPress or Drupal.
*/
public function safe_to_delete_filename( $path ) {
// You'll have to edit this list if
// more files are included in SRDB.
// Using an untargeted deletion operation is
// entirely unacceptable.
$srdb_filename_whitelist = array(
'composer.json',
'index.php',
'package.json',
'README.md',
'srdb.class.php',
'srdb.cli.php',
'srdb-tests',
'charset-test.php',
'DataSet.xml',
'DataSetGenerator.php',
'db.sql',
'SrdbTest.php'
);
foreach ( $srdb_filename_whitelist as $whitelist_item ) {
if ( false !== stripos( $path, $whitelist_item ) ) {
return true;
}
}
return false;
}
/**
* Checks an array of fully qualified filenames to see if they are all
* SRDB filenames.
*
* @param array $array_of_paths
*
* @return boolean true if all paths are most likely SRDB files.
*/
public function safe_to_delete_all_filenames( $array_of_paths ) {
foreach ( $array_of_paths as $path ) {
if ( !$this->safe_to_delete_filename( $path ) ) {
return false;
}
}
return true;
}
const DELETE_SCRIPT_SUCCESS = 0;
const DELETE_SCRIPT_FAIL_CANT_DELETE = -1;
const DELETE_SCRIPT_FAIL_UNSAFE = -2;
/**
* http://stackoverflow.com/questions/3349753/delete-directory-with-files-in-it
*
* @param string $path directory/file path
*
* @return integer DELETE_SCRIPT_SUCCESS for success, DELETE_SCRIPT_FAIL_CANT_DELETE for physical failure, DELETE_SCRIPT_FAIL_UNSAFE for 'Shouldn't delete wordpress' failure.
*/
public function delete_script( $path ) {
$all_targets = $this->determine_all_files_below_path( $path );
$all_targets_minus_containing_directory = $all_targets;
// Proceed if all files identified (except the current directory)
// match a list of whitelisted deletable filenames.
array_pop( $all_targets_minus_containing_directory );
$can_proceed = $this->safe_to_delete_all_filenames( $all_targets_minus_containing_directory );
if ( !$can_proceed ) return self::DELETE_SCRIPT_FAIL_UNSAFE;
foreach ( $all_targets as $target_filename ) {
if ( is_file( $target_filename ) ) {
if ( false === @unlink( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE;
} else {
if ( false === @rmdir( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE;
}
}
return self::DELETE_SCRIPT_SUCCESS;
}
/**
* Attempts to detect a WordPress installation and bootstraps the environment with it
*
* @return bool Whether it is a WP install and we have database credentials
*/
public function is_wordpress() {
$path_mod = '';
$depth = 0;
$max_depth = 4;
$bootstrap_file = 'wp-blog-header.php';
while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) {
$path_mod .= '/..';
if ( $depth++ >= $max_depth )
break;
}
if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) {
// store WP path
$this->path = dirname( __FILE__ ) . $path_mod;
// just in case we're white screening
try {
// need to make as many of the globals available as possible or things can break
// (globals suck)
global $wp, $wpdb, $wp_query, $wp_the_query, $wp_version,
$wp_db_version, $tinymce_version, $manifest_version,
$required_php_version, $required_mysql_version,
$post, $posts, $wp_locale, $authordata, $more, $numpages,
$currentday, $currentmonth, $page, $pages, $multipage,
$wp_rewrite, $wp_filesystem, $blog_id, $request,
$wp_styles, $wp_taxonomies, $wp_post_types, $wp_filter,
$wp_object_cache, $query_string, $single, $post_type,
$is_iphone, $is_chrome, $is_safari, $is_NS4, $is_opera,
$is_macIE, $is_winIE, $is_gecko, $is_lynx, $is_IE,
$is_apache, $is_iis7, $is_IIS;
// prevent multisite redirect
define( 'WP_INSTALLING', true );
// prevent super/total cache
define( 'DONOTCACHEDB', true );
define( 'DONOTCACHEPAGE', true );
define( 'DONOTCACHEOBJECT', true );
define( 'DONOTCDN', true );
define( 'DONOTMINIFY', true );
// cancel batcache
if ( function_exists( 'batcache_cancel' ) )
batcache_cancel();
// bootstrap WordPress
require( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" );
$this->set( 'path', ABSPATH );
$this->set( 'is_wordpress', true );
return true;
} catch( Exception $error ) {
// try and get database values using regex approach
$db_details = $this->define_find( $this->path . '/wp-config.php' );
if ( $db_details ) {
define( 'DB_NAME', $db_details[ 'name' ] );
define( 'DB_USER', $db_details[ 'user' ] );
define( 'DB_PASSWORD', $db_details[ 'pass' ] );
define( 'DB_HOST', $db_details[ 'host' ] );
define( 'DB_CHARSET', $db_details[ 'char' ] );
define( 'DB_COLLATE', $db_details[ 'coll' ] );
// additional error message
$this->add_error( 'WordPress detected but could not bootstrap environment. There might be a PHP error, possibly caused by changes to the database', 'db' );
}
if ( $db_details )
return true;
}
}
return false;
}
public function is_drupal() {
$path_mod = '';
$depth = 0;
$max_depth = 4;
$bootstrap_file = 'includes/bootstrap.inc';
while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) {
$path_mod .= '/..';
if ( $depth++ >= $max_depth )
break;
}
if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) {
try {
// require the bootstrap include
require_once( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" );
// define drupal root
if ( ! defined( 'DRUPAL_ROOT' ) )
define( 'DRUPAL_ROOT', dirname( __FILE__ ) . $path_mod );
// load drupal
drupal_bootstrap( DRUPAL_BOOTSTRAP_FULL );
// confirm environment
$this->set( 'is_drupal', true );
return true;
} catch( Exception $error ) {
// We can't add_error here as 'db' because if the db errors array is not empty, the interface doesn't activate!
// This is a consequence of the 'complete' method in JavaScript
$this->add_error( 'Drupal detected but could not bootstrap to retrieve configuration. There might be a PHP error, possibly caused by changes to the database', 'recoverable_db' );
}
}
return false;
}
/**
* Search through the file name passed for a set of defines used to set up
* WordPress db access.
*
* @param string $filename The file name we need to scan for the defines.
*
* @return array List of db connection details.
*/
public function define_find( $filename = 'wp-config.php' ) {
if ( $filename == 'wp-config.php' ) {
$filename = dirname( __FILE__ ) . '/' . basename( $filename );
// look up one directory if config file doesn't exist in current directory
if ( ! file_exists( $filename ) )
$filename = dirname( __FILE__ ) . '/../' . basename( $filename );
}
if ( file_exists( $filename ) && is_file( $filename ) && is_readable( $filename ) ) {
$file = @fopen( $filename, 'r' );
$file_content = fread( $file, filesize( $filename ) );
@fclose( $file );
}
preg_match_all( '/define\s*?\(\s*?([\'"])(DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|DB_CHARSET|DB_COLLATE)\1\s*?,\s*?([\'"])([^\3]*?)\3\s*?\)\s*?;/si', $file_content, $defines );
if ( ( isset( $defines[ 2 ] ) && ! empty( $defines[ 2 ] ) ) && ( isset( $defines[ 4 ] ) && ! empty( $defines[ 4 ] ) ) ) {
foreach( $defines[ 2 ] as $key => $define ) {
switch( $define ) {
case 'DB_NAME':
$name = $defines[ 4 ][ $key ];
break;
case 'DB_USER':
$user = $defines[ 4 ][ $key ];
break;
case 'DB_PASSWORD':
$pass = $defines[ 4 ][ $key ];
break;
case 'DB_HOST':
$host = $defines[ 4 ][ $key ];
break;
case 'DB_CHARSET':
$char = $defines[ 4 ][ $key ];
break;
case 'DB_COLLATE':
$coll = $defines[ 4 ][ $key ];
break;
}
}
}
return array(
'host' => $host,
'name' => $name,
'user' => $user,
'pass' => $pass,
'char' => $char,
'coll' => $coll
);
}
/**
* Display the current url
*
*/
public function self_link() {
return 'http://' . $_SERVER[ 'HTTP_HOST' ] . rtrim( $_SERVER[ 'REQUEST_URI' ], '/' );
}
/**
* Simple html escaping
*
* @param string $string Thing that needs escaping
* @param bool $echo Do we echo or return?
*
* @return string Escaped string.
*/
public function esc_html_attr( $string = '', $echo = false ) {
$output = htmlentities( $string, ENT_QUOTES, 'UTF-8' );
if ( $echo )
echo $output;
else
return $output;
}
public function checked( $value, $value2, $echo = true ) {
$output = $value == $value2 ? ' checked="checked"' : '';
if ( $echo )
echo $output;
return $output;
}
public function selected( $value, $value2, $echo = true ) {
$output = $value == $value2 ? ' selected="selected"' : '';
if ( $echo )
echo $output;
return $output;
}
public function get_errors( $type ) {
if ( ! isset( $this->errors[ $type ] ) || ! count( $this->errors[ $type ] ) )
return;
echo '<div class="errors">';
foreach( $this->errors[ $type ] as $error ) {
if ( $error instanceof Exception )
echo '<p class="exception">' . $error->getMessage() . '</p>';
elseif ( is_string( $error ) )
echo $error;
}
echo '</div>';
}
public function get_report( $table = null ) {
$report = $this->get( 'report' );
if ( empty( $report ) )
return;
$dry_run = $this->get( 'dry_run' );
$search = $this->get( 'search' );
$replace = $this->get( 'replace' );
// Calc the time taken.
$time = array_sum( explode( ' ', $report[ 'end' ] ) ) - array_sum( explode( ' ', $report[ 'start' ] ) );
$srch_rplc_input_phrase = $dry_run ?
'searching for <strong>"' . $search . '"</strong> (to be replaced by <strong>"' . $replace . '"</strong>)' :
'replacing <strong>"' . $search . '"</strong> with <strong>"' . $replace . '"</strong>';
echo '
<div class="report">';
echo '
<h2>Report</h2>';
echo '
<p>';
printf(
'In the process of %s we scanned <strong>%d</strong> tables with a total of
<strong>%d</strong> rows, <strong>%d</strong> cells %s changed.
<strong>%d</strong> db updates were actually performed.
It all took <strong>%f</strong> seconds.',
$srch_rplc_input_phrase,
$report[ 'tables' ],
$report[ 'rows' ],
$report[ 'change' ],
$dry_run ? 'would have been' : 'were',
$report[ 'updates' ],
$time
);
echo '
</p>';
echo '
<table class="table-reports">
<thead>
<tr>
<th>Table</th>
<th>Rows</th>
<th>Cells changed</th>
<th>Updates</th>
<th>Seconds</th>
</tr>
</thead>
<tbody>';
foreach( $report[ 'table_reports' ] as $table => $t_report ) {
$t_time = array_sum( explode( ' ', $t_report[ 'end' ] ) ) - array_sum( explode( ' ', $t_report[ 'start' ] ) );
echo '
<tr>';
printf( '
<th>%s:</th>
<td>%d</td>
<td>%d</td>
<td>%d</td>
<td>%f</td>',
$table,
$t_report[ 'rows' ],
$t_report[ 'change' ],
$t_report[ 'updates' ],
$t_time
);
echo '
</tr>';
}
echo '
</tbody>
</table>';
echo '
</div>';
}
public function table_select( $echo = true ) {
$table_select = '';
if ( ! empty( $this->all_tables ) ) {
$table_select .= '<select name="tables[]" multiple="multiple">';
foreach( $this->all_tables as $table ) {
$size = $table[ 'Data_length' ] / 1000;
$size_unit = 'kb';
if ( $size > 1000 ) {
$size = $size / 1000;
$size_unit = 'Mb';
}
if ( $size > 1000 ) {
$size = $size / 1000;
$size_unit = 'Gb';
}
$size = number_format( $size, 2 ) . $size_unit;
$rows = $table[ 'Rows' ] > 1 ? 'rows' : 'row';
$table_select .= sprintf( '<option value="%s" %s>%s</option>',
$this->esc_html_attr( $table[ 0 ], false ),
$this->selected( true, in_array( $table[ 0 ], $this->tables ), false ),
"{$table[0]}: {$table['Engine']}, rows: {$table['Rows']}, size: {$size}, collation: {$table['Collation']}, character_set: {$table['Character_set']}"
);
}
$table_select .= '</select>';
}
if ( $echo )
echo $table_select;
return $table_select;
}
public function ui() {
// Warn if we're running in safe mode as we'll probably time out.
if ( ini_get( 'safe_mode' ) ) {
?>
<div class="special-errors">
<h4>Warning</h4>
<?php echo printf( '<p>Safe mode is on so you may run into problems if it takes longer than %s seconds to process your request.</p>', ini_get( 'max_execution_time' ) ); ?>
</div>
<?php
}
?>
<form action="" method="post">
<!-- 1. search/replace -->
<fieldset class="row row-search">
<h1>search<span>/</span>replace</h1>
<?php $this->get_errors( 'search' ); ?>
<div class="fields fields-large">
<label for="search"><span class="label-text">replace</span> <span class="hide-if-regex-off regex-left">/</span><input id="search" type="text" placeholder="search for&hellip;" value="<?php $this->esc_html_attr( $this->search, true ); ?>" name="search" /><span class="hide-if-regex-off regex-right">/</span></label>
<label for="replace"><span class="label-text">with</span> <input id="replace" type="text" placeholder="replace with&hellip;" value="<?php $this->esc_html_attr( $this->replace, true ); ?>" name="replace" /></label>
<label for="regex" class="field-advanced"><input id="regex" type="checkbox" name="regex" value="1" <?php $this->checked( true, $this->regex ); ?> /> use regex</label>
</div>
<div class="fields field-advanced hide-if-regex-off">
<label for="regex_i" class="field field-advanced"><input type="checkbox" name="regex_i" id="regex_i" value="1" <?php $this->checked( true, $this->regex_i ); ?> /> <abbr title="case insensitive">i</abbr></abbr></label>
<label for="regex_m" class="field field-advanced"><input type="checkbox" name="regex_m" id="regex_m" value="1" <?php $this->checked( true, $this->regex_m ); ?> /> <abbr title="multiline">m</abbr></label>
<label for="regex_s" class="field field-advanced"><input type="checkbox" name="regex_s" id="regex_s" value="1" <?php $this->checked( true, $this->regex_s ); ?> /> <abbr title="dot also matches newlines">s</abbr></label>
<label for="regex_x" class="field field-advanced"><input type="checkbox" name="regex_x" id="regex_x" value="1" <?php $this->checked( true, $this->regex_x ); ?> /> <abbr title="extended mode">x</abbr></label>
</div>
</fieldset>
<!-- 2. db details -->
<fieldset class="row row-db">
<h1>db details</h1>
<?php $this->get_errors( 'environment' ); ?>
<?php $this->get_errors( 'recoverable_db' ); ?>
<?php $this->get_errors( 'db' ); ?>
<div class="fields fields-small">
<div class="field field-short">
<label for="name">name</label>
<input id="name" name="name" type="text" value="<?php $this->esc_html_attr( $this->name, true ); ?>" />
</div>
<div class="field field-short">
<label for="user">user</label>
<input id="user" name="user" type="text" value="<?php $this->esc_html_attr( $this->user, true ); ?>" />
</div>
<div class="field field-short">
<label for="pass">pass</label>
<input id="pass" name="pass" type="text" value="<?php $this->esc_html_attr( $this->pass, true ); ?>" />
</div>
<div class="field field-short">
<label for="host">host</label>
<input id="host" name="host" type="text" value="<?php $this->esc_html_attr( $this->host, true ); ?>" />
</div>
<div class="field field-short">
<label for="port">port</label>
<input id="port" name="port" type="text" value="<?php $this->esc_html_attr( $this->port, true ); ?>" />
</div>
</div>
</fieldset>
<!-- 3. tables -->
<fieldset class="row row-tables">
<h1>tables</h1>
<?php $this->get_errors( 'tables' ); ?>
<div class="fields">
<div class="field radio">
<label for="all_tables">
<input id="all_tables" name="use_tables" value="all" type="radio" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> <?php $this->checked( true, empty( $this->tables ) ); ?> />
all tables
</label>
</div>
<div class="field radio">
<label for="subset_tables">
<input id="subset_tables" name="use_tables" value="subset" type="radio" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> <?php $this->checked( false, empty( $this->tables ) ); ?> />
select tables
</label>
</div>
<div class="field table-select hide-if-js"><?php $this->table_select(); ?></div>
</div>
<div class="fields field-advanced">
<div class="field field-advanced field-medium">
<label for="exclude_cols">columns to exclude (optional, comma separated)</label>
<input id="exclude_cols" type="text" name="exclude_cols" value="<?php $this->esc_html_attr( implode( ',', $this->get( 'exclude_cols' ) ) ) ?>" placeholder="eg. guid" />
</div>
<div class="field field-advanced field-medium">
<label for="include_cols">columns to include only (optional, comma separated)</label>
<input id="include_cols" type="text" name="include_cols" value="<?php $this->esc_html_attr( implode( ',', $this->get( 'include_cols' ) ) ) ?>" placeholder="eg. post_content, post_excerpt" />
</div>
</div>
</fieldset>
<!-- 4. results -->
<fieldset class="row row-results">
<h1>actions</h1>
<?php $this->get_errors( 'results' ); ?>
<div class="fields">
<span class="submit-group">
<input type="submit" name="submit[update]" value="update details" />
<input type="submit" name="submit[dryrun]" value="dry run" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" />
<input type="submit" name="submit[liverun]" value="live run" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" />
<span class="separator">/</span>
</span>
<span class="submit-group">
<?php if ( in_array( 'InnoDB', $this->get( 'engines' ) ) ) { ?>
<input type="submit" name="submit[innodb]" value="convert to innodb" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" />
<?php } ?>
<input type="submit" name="submit[utf8]" value="convert to utf8 unicode" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" />
<input type="submit" name="submit[utf8mb4]" value="convert to utf8mb4 unicode" <?php if ( ! $this->db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" />
</span>
</div>
<?php $this->get_report(); ?>
</fieldset>
<!-- 5. branding -->
<section class="row row-delete">
<h1>delete</h1>
<div class="fields">
<p>
<input type="submit" name="submit[delete]" value="delete me" />
Once you&rsquo;re done click the <strong>delete me</strong> button to secure your server
</p>
</div>
</section>
</form>
<section class="help">
<h1 class="branding">interconnect/it</h1>
<h2>Safe Search and Replace on Database with Serialized Data v3.1.0</h2>
<p>This developer/sysadmin tool carries out search/replace functions on MySQL DBs and can handle serialised PHP Arrays and Objects.</p>
<p><strong class="red">WARNINGS!</strong>
Ensure data is backed up.
We take no responsibility for any damage caused by this script or its misuse.
DB Connection Settings are auto-filled when WordPress or Drupal is detected but can be confused by commented out settings so CHECK!
There is NO UNDO!
Be careful running this script on a production server.</p>
<h3>Don't Forget to Remove Me!</h3>
<p>Delete this utility from your
server after use by clicking the 'delete me' button. It represents a major security threat to your database if
maliciously used.</p>
<p>If you have feedback or want to contribute to this script click the delete button to find out how.</p>
<p><em>We don't put links on the search replace UI itself to avoid seeing URLs for the script in our access logs.</em></p>
<h3>Again, use Of This Script Is Entirely At Your Own Risk</h3>
<p>The easiest and safest way to use this script is to copy your site's files and DB to a new location.
You then, if required, fix up your .htaccess and wp-config.php appropriately. Once
done, run this script, select your tables (in most cases all of them) and then
enter the search replace strings. You can press back in your browser to do
this several times, as may be required in some cases.</p>
</section>
<?php
}
public function deleted() {
// obligatory marketing!
// seriously though it's good stuff
?>
<!-- 1. branding -->
<section class="row row-branding">
<h1><a href="http://interconnectit.com/" target="_blank">interconnect<span>/</span><strong>it</strong></a></h1>
<?php $this->get_errors( 'delete' ); ?>
<div class="content">
<p>Thanks for using our search/replace tool! We&rsquo;d really appreciate it if you took a
minute to join our mailing list and check out some of our other products.</p>
</div>
</section>
<!-- 2. subscribe -->
<section class="row row-subscribe">
<h1>newsletter</h1>
<form action="http://interconnectit.us2.list-manage.com/subscribe/post" method="POST" class="fields fields-small">
<input type="hidden" name="u" value="08ec797202866aded7b2619b2">
<input type="hidden" name="id" value="538abe0a97">
<div id="mergeTable" class="mergeTable">
<div class="mergeRow dojoDndItem mergeRow-email field field-short" id="mergeRow-0">
<label for="MERGE0"><strong>email address</strong> <span class="asterisk">*</span></label>
<input type="email" autocapitalize="off" autocorrect="off" name="MERGE0" id="MERGE0" size="25" value="">
</div>
<div class="mergeRow dojoDndItem mergeRow-text field field-short" id="mergeRow-1">
<label for="MERGE1">first name</label>
<input type="text" name="MERGE1" id="MERGE1" size="25" value="">
</div>
<div class="mergeRow dojoDndItem mergeRow-text field field-short" id="mergeRow-2">
<label for="MERGE2">last name</label>
<input type="text" name="MERGE2" id="MERGE2" size="25" value="">
</div>
<div class="submit_container field field-short">
<br />
<input type="submit" name="submit" value="subscribe">
</div>
</div>
</form>
</section>
<!-- 3. contribute -->
<section class="row row-contribute">
<h1>contribute</h1>
<div class="content">
<p>Got suggestions? Found a bug? Want to contribute code? <a href="https://github.com/interconnectit/search-replace-db">Join us on Github!</a></p>
</div>
</section>
<section class="row row-blog">
<h1>blogs</h1>
<div class="content">
<p><a href="http://interconnectit.com/blog/" target="_blank">We couldn't load our blog feed for some reason so here's a link instead!</a></p>
</div>
</section>
<!-- 5. products -->
<section class="row row-products">
<h1>products</h1>
<div class="content">
<p><a href="http://interconnectit.com/products/" target="_blank">We couldn't load our product feed for some reason so here's a link instead!</a></p>
</div>
</section>
<?php
}
public function html( $body ) {
// html classes
$classes = array( 'no-js' );
$classes[] = $this->regex ? 'regex-on' : 'regex-off';
?><!DOCTYPE html>
<html class="<?php echo implode( ' ', $classes ); ?>">
<head>
<script>var h = document.getElementsByTagName('html')[0];h.className = h.className.replace('no-js', 'js');</script>
<title>interconnect/it : search replace db</title>
<?php $this->meta(); ?>
<?php $this->css(); ?>
<?php $this->js(); ?>
</head>
<body>
<?php $this->$body(); ?>
</body>
</html>
<?php
}
public function meta() {
?>
<meta charset="utf-8" />
<?php
}
public function css() {
?>
<style type="text/css">
* { margin: 0; padding: 0; }
::-webkit-input-placeholder { /* WebKit browsers */
color: #999;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: #999;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: #999;
}
:-ms-input-placeholder { /* Internet Explorer 10+ */
color: #999;
}
.js .hide-if-js {
display: none;
}
.no-js .hide-if-nojs {
display: none;
}
.regex-off .hide-if-regex-off {
display: none;
}
.regex-on .hide-if-regex-on {
display: none;
}
html {
background: #fff;
font-size: 10px;
border-top: 20px solid #de1301;
}
body {
font-family: 'Gill Sans MT', 'Gill Sans', Calibri, sans-serif;
font-size: 1.6rem;
}
h2,
h3 {
text-transform: uppercase;
font-weight: normal;
margin: 2.0rem 0 1.0rem;
}
label {
cursor: pointer;
}
/*.row {
background-color: rgba( 210, 0, 0, 1 );
padding: 20px 40px;
border: 0;
overflow: hidden;
}
.row + .row {
background-color: rgba( 210, 0, 0, .8 );
}
.row + .row + .row {
background-color: rgba( 210, 0, 0, .6 );
}
.row + .row + .row + .row {
background-color: rgba( 210, 0, 0, .4 );
}
.row + .row + .row + .row + .row {
background-color: rgba( 210, 0, 0, .2 );
}*/
.row {
background-color: rgba( 210, 210, 210, 1 );
padding: 20px 40px;
border: 0;
overflow: hidden;
}
.row + .row {
background-color: rgba( 210, 210, 210, .8 );
}
.row + .row + .row {
background-color: rgba( 210, 210, 210, .6 );
}
.row + .row + .row + .row {
background-color: rgba( 210, 210, 210, .4 );
}
.row + .row + .row + .row + .row {
background-color: rgba( 210, 210, 210, .2 );
}
.row h1 {
display: block;
font-size: 4.0rem;
font-weight: normal;
margin: 15px 0 20px;
float: left;
}
.row h1,
.branding {
width: 260px;
background:
url(data:image/png;base64,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
no-repeat
0 0;
height: 40px;
overflow: hidden;
text-indent: -9999px;
}
h1 span {
color: #de1301;
}
.row-db h1 {
background-position: 0 -40px;
}
.row-tables h1 {
background-position: 0 -80px;
}
.row-results h1 {
background-position: 0 -120px;
}
.row-delete h1 {
background-position: 0 -160px;
}
.row-branding h1,
.branding {
background-position: 0 -200px;
}
.row-subscribe h1 {
background-position: 0 -240px;
}
.row-contribute h1 {
background-position: 0 -280px;
}
.row-blog h1 {
background-position: 0 -320px;
}
.row-products h1 {
background-position: 0 -360px;
}
legend, fieldset {
/*color: #fff;*/
}
.fields, .content, .errors {
clear: both;
margin-top: 15px;
margin-bottom: 15px;
overflow: hidden;
}
.content {
margin-top: 20px;
margin-bottom: 20px;
}
.errors, .special-errors {
color: #fff;
background: #671301;
padding: 10px;
font-weight: bold;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
border: 2px solid rgba(0,0,0,.3);
}
@media only screen and (min-width: 1000px) {
legend, h1 {
min-width: 250px;
font-size: 4.0rem;
margin-bottom: 10px;
}
.fields, .content, .errors {
margin-left: 300px;
clear: right;
}
}
.fields {
margin-right: -20px;
margin-bottom: 5px;
}
.fields-small {
margin-top: 0;
}
.fields-large {
font-size: 2.0rem;
margin-right: 0;
margin-bottom: 15px;
}
.fields-large label {
white-space: nowrap;
margin: 10px 0;
display: block;
}
.fields-large input[type="text"] {
width: 100%;
}
.label-text {
display: block;
}
@media only screen and (min-width: 1110px) {
.label-text {
display: inline;
}
.fields-large label {
margin: 0;
text-align: left;
display: inline-block;
}
.fields-large input[type="text"] {
width: 15em;
}
.regex-on .fields-large .regex-left + input[type="text"] {
width: 12.7em;
}
}
.field {
float: left;
padding-right: 20px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
.field label {
display: block;
}
.table-select {
clear: both;
}
.field-long,
.field-medium,
.field-short {
width: 100%;
}
.field-long input[type="text"],
.field-medium input[type="text"],
.field-short input[type="text"],
.field-long input[type="email"],
.field-medium input[type="email"],
.field-short input[type="email"] {
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 10px;
}
@media only screen and (min-width: 400px) {
.field-short {
width: 50%;
}
}
@media only screen and (min-width: 700px) {
.field-medium {
width: 50%;
}
.field-short {
width: 20%;
}
}
.description {
font-size: 1.8rem;
font-style: italic;
color: #eee;
margin-top: 10px;
}
input[type="text"],
input[type="email"],
.regex-left,
.regex-right {
background: rgba(255,255,255,.7);
border: 2px solid rgba(0,0,0,.15);
padding: 10px 10px 10px;
font-family: Monaco, Consolas, monospace;
font-weight: bold;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
.regex-on .regex-left + input[type="text"] {
padding-left: 0;
padding-right: 0;
border-left: 0;
border-right: 0;
width: 80%;
}
.regex-left {
color: #000;
padding-right: 0;
border-right: 0;
width: 1em;
}
.regex-right {
color: #000;
padding-left: 0;
border-left: 0;
width: 1em;
}
[type="submit"] {
padding: 5px 10px 8px;
color: #fff;
background: #de1301 left center;
cursor: pointer;
border: 2px solid rgba(0,0,0,.15);
margin-right: 20px;
margin-bottom: 10px;
display: inline-block;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: background-color 0.2s ease-in, color 0.2s ease-in, padding-left 0.05s ease-in;
-moz-transition: background-color 0.2s ease-in, color 0.2s ease-in, padding-left 0.05s ease-in;
-ms-transition: background-color 0.2s ease-in, color 0.2s ease-in, padding-left 0.05s ease-in;
transition: background-color 0.2s ease-in, color 0.2s ease-in, padding-left 0.05s ease-in;
}
.separator {
margin-right: 20px;
}
[type="submit"]:focus,
[type="submit"]:active {
outline: 2px solid #ab1301;
}
[type="submit"][disabled],
[type="submit"][disabled]:hover,
[type="submit"][disabled]:active,
[type="submit"][disabled]:focus,
[type="submit"][disabled]:active:hover {
background: #999;
color: #ccc;
cursor: default;
outline: none;
padding-left: 10px;
}
[type="submit"].active,
[type="submit"].active:hover,
[type="submit"].active:active,
[type="submit"][disabled].active,
[type="submit"][disabled].active:hover,
[type="submit"][disabled].active:active,
[type="submit"][disabled].active:active:hover {
outline: none;
color: #fff;
background:
#900
url(data:image/gif;base64,R0lGODlhEAAQAPQAAJkAAP///5sGBufGxsl6evv4+O7Y2KgoKLtWVvXo6M+IiNWYmKMaGsFmZq84OOG2ttuoqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==)
no-repeat
8px
center;
padding-left: 30px;
}
.submit-group {
display: block;
margin: 0 0;
}
.field input[type="submit"] {
margin-top: 2px;
}
@media only screen and (min-width: 500px) {
.submit-group {
white-space: nowrap;
display: inline-block;
}
}
input, select, textarea, button {
font-family: inherit;
font-size: inherit;
}
.checkboxes {
float: none;
}
select[multiple] {
height: 16em;
font-family: Monaco, Consolas, monospace;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
margin: 20px 0 0;
}
select[multiple] option {
padding: 5px;
}
.checkboxes ul {
list-style: none;
max-height: 260px;
-webkit-columns: 16em 3;
-webkit-column-gap: 2em;
overflow: auto;
padding-bottom: 1em;
}
.checkboxes li {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
input[type="checkbox"],
input[type="radio"] {
vertical-align: middle;
}
.report {
color: #000;
background: #fff;
margin-top: 20px;
margin-bottom: 20px;
padding: 10px;
}
.report ul {
list-style: none;
margin: 10px 0;
display: table;
}
.report li {
margin: 0;
display: table-row;
padding: 10px 0;
}
.report li>strong {
display: table-cell;
padding-right: 20px;
}
.report li>span {
display: table-cell;
padding-right: 20px;
white-space: nowrap;
}
.report tbody tr:nth-child(2n-1) {
background: rgba(0,0,0,.1);
}
.report table {
width: 100%;
border-collapse: collapse;
}
.report th,
.report td {
text-align: left;
padding: 5px;
}
.changes-overlay {
background: #fff;
position: fixed;
left: 0; top: 0;
right: 0; bottom: 0;
overflow: auto;
}
.changes-overlay .overlay-header {
overflow: hidden;
}
.changes-overlay .close {
float: right;
margin: 40px 20px;
color: #c00;
text-decoration: none;
font-weight: bold;
font-size: 2.4rem;
}
.changes-overlay h1 {
margin: 20px;
float: none;
font-weight: normal;
}
.changes-overlay h1 small {
vertical-align: baseline;
font-size: 1.4rem;
color: #999;
}
.changes-overlay .changes {
margin: 0 0;
overflow: auto;
position: absolute;
top: 100px; right: 0;
left: 0; bottom: 0;
clear: both;
width: 100%;
}
.highlight {
background: #ffa;
}
.diff-wrap {
margin: 20px;
background: #f3f3f3;
overflow: hidden;
}
.diff-wrap h3 {
font-size: 1.6rem;
font-weight: bold;
margin: 10px 10px 10px;
}
.diff {
overflow: auto;
margin: 5px;
}
.diff pre {
float: left;
width: 50%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
padding: 5px;
background: #fff;
white-space: normal;
word-break: break-all;
font-size: 1.3rem;
}
.diff .from {
border-right: 5px solid #f3f3f3;
}
.diff .to {
border-left: 5px solid #f3f3f3;
}
a {
color: #d00;
}
h1 a {
text-decoration: none;
color: inherit;
}
.help {
margin: 40px;
color: #999;
max-width: 640px;
}
.help a {
color: inherit;
}
.help p {
margin: 10px 0;
}
.red {
color: #c00;
}
.row-branding .content {
font-size: 2.4rem;
}
.row-blogs {
}
.blog {
margin-bottom: 15px;
}
.blog a {
display: block;
text-decoration: none;
}
.blog a:hover h2 {
text-decoration: underline;
}
.blog h2 {
margin: 0;
line-height: 1.2;
}
.blog div {
display: inline-block;
color: #333;
font-size: 1.4rem;
}
.blog .categories {
font-style: italic;
margin-left: 20px;
}
.row-products .content {
overflow: hidden;
margin-right: -20px;
}
.product {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
padding-right: 20px;
margin-bottom: 20px;
}
@media only screen and (min-width: 600px) {
.product {
float: left;
width: 100%;
width: 50%;
}
}
@media only screen and (min-width: 1200px) {
.product {
width: 33.33333%;
}
}
.product-thumb {
height: 100px;
overflow: hidden;
}
.product img {
max-width: 100%;
height: auto;
}
.product a {
display: block;
text-decoration: none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
padding: 20px 20px 40px 20px;
border: 2px solid rgba(0,0,0,.1);
background: #fff;
min-height: 380px;
}
.product a:hover {
border: 2px solid rgba(0,0,0,.3);
}
.product-description {
color: #000;
line-height: 1.2;
}
.product-description p {
margin-bottom: 10px;
}
.product-description li {
list-style: none;
font-style: italic;
margin: 5px 0;
}
</style>
<?php
}
public function js() {
?>
<script>
/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
//# sourceMappingURL=jquery-1.10.2.min.map
*/
(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error
}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){r
u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentSty
jQuery.noConflict();
function containsSerialisedString( text )
{
// we can't display the highlight on objects with strings (manifest as "s:digit") because this might change the length
return ( ( /s:\d/.exec( text ) ) ? true : false );
}
// patch console free browsers
window.console = window.console || { log: function(){} };
;(function($){
var srdb;
srdb = function() {
var t = this,
dom = $( 'html' );
$.extend( t, {
errors: {},
report: {},
info: {},
prev_data: {},
tables: 0,
rows: 0,
changes: 0,
updates: 0,
time: 0.0,
button: false,
running: false,
countdown: null,
escape: false,
// constructor
init: function() {
// search replace ui
if ( $( '.row-db' ).length ) {
// show/hide tables
dom.on( 'click', '[name="use_tables"]', t.toggle_tables );
dom.find( '[name="use_tables"][checked]' ).click();
// toggle regex mode
dom.on( 'click', '[name="regex"]', t.toggle_regex );
dom.find( '[name="regex"][checked]' ).click();
// ajax form
dom.on( 'submit', 'form', t.submit_proxy );
dom.on( 'click', '[type="submit"]', t.submit );
// prevent accidental browsing away
window.onbeforeunload = function() {
return t.running ? t.confirm_strings.unload_running : t.confirm_strings.unload_default;
};
// deleted ui
} else {
// mailchimp
dom.on( 'submit', 'form[action*="list-manage.com"]', t.mailchimp );
// fetch blog feed
t.fetch_blogs();
// fetch product feed
t.fetch_products();
}
},
report_tpl: '\
<p class="main-report">\
In the process of <span data-report="search_replace"></span> we scanned\
<strong data-report="tables"></strong> tables with a total of\
<strong data-report="rows"></strong> rows,\
<strong data-report="changes"></strong> cells\
<span data-report="dry_run"></span> changed.\
<strong data-report="updates"></strong> db updates were performed.\
It all took <strong data-report="time"></strong> seconds.\
</p>',
table_report_tpl: '\
<th data-report="table"></th>\
<td data-report="rows"></td>\
<td data-report="changes"></td>\
<td data-report="updates"></td>\
<td data-report="time"></td>',
table_report_head_tpl: '',
strings_dry: {
search_replace: 'searching for <strong>&ldquo;<span data-report="search"></span>&rdquo;</strong>\
(to be replaced by <strong>&ldquo;<span data-report="replace"></span>&rdquo;</strong>)',
updates: 'would have been'
},
strings_live: {
search_replace: 'replacing <strong data-report="search"></strong> with\
<strong data-report="replace"></strong>',
updates: 'were'
},
confirm_strings: {
live_run: 'Are you absolutely ready to run search/replace? Make sure you have backed up your database!',
modify: 'Are you absolutely ready to modify the tables? Make sure you have backed up your database!',
unload_default: 'DON\'T FORGET TO DELETE THIS SCRIPT!!!\n\nClick the delete button at the bottom to remove it.',
unload_running: 'The script is still in progress, do you definitely want to leave this page?'
},
toggle_tables: function() {
if ( this.id == 'all_tables' ) {
dom.find( '.table-select' ).slideUp( 400 );
} else {
dom.find( '.table-select' ).slideDown( 400 );
}
},
toggle_regex: function() {
if ( $( this ).is( ':checked' ) )
dom.removeClass( 'regex-off' ).addClass( 'regex-on' );
else
dom.removeClass( 'regex-on' ).addClass( 'regex-off' );
},
reset: function() {
t.errors = {};
t.report = {};
t.tables = 0;
t.rows = 0;
t.changes = 0;
t.updates = 0;
t.time = 0.0;
},
map_form_data: function( $form ) {
var data_temp = $form.serializeArray(),
data = {};
$.map( data_temp, function( field, i ) {
if ( data[ field.name ] ) {
if ( ! $.isArray( data[ field.name ] ) )
data[ field.name ] = [ data[ field.name ] ];
data[ field.name ].push( field.value );
}
else {
if ( field.value === '1' )
field.value = true;
data[ field.name ] = field.value;
}
} );
return data;
},
submit_proxy: function( e ) {
if ( t.button !== 'submit[delete]' )
return false;
return true;
},
submit: function( e ) {
// workaround for submission not coming from a button click
var $button = $( this ),
$form = $( this ).parents( 'form' ),
submit = $button.attr( 'name' ),
button_text = $button.val(),
seconds = 5;
// track button clicked
t.button = submit;
// reset escape parameter
t.escape = false;
// add spinner
$button.addClass( 'active' );
if ( submit == 'submit[delete]' && ! t.running ) {
if ( ! confirm( 'Do you really want to delete the Search/Replace script directory and -all its contents-?' ) ) {
t.complete();
return false;
}
window.onbeforeunload = null;
$( '[type="submit"]' ).not( $button ).attr( 'disabled', 'disabled' );
return true;
}
if ( submit == 'submit[liverun]' && ! window.confirm( t.confirm_strings.live_run ) ) {
t.complete();
return false;
}
if ( ( submit == 'submit[innodb]' || submit == 'submit[utf8]' || submit == 'submit[utf8mb4]' ) && ! window.confirm( t.confirm_strings.modify ) ) {
t.complete();
return false;
}
// disable buttons & add spinner
$( '[type="submit"]' ).attr( 'disabled', 'disabled' );
// stop normal submission
e.preventDefault();
// reset reports
t.reset();
// get form data as an object
data = t.map_form_data( $form );
// use all tables if none selected
if ( dom.find( '#all_tables' ).is( ':checked' ) || ! data[ 'tables[]' ] || ! data[ 'tables[]' ].length )
data[ 'tables[]' ] = $.map( $( 'select[name^="tables"] option' ), function( el, i ) { return $( el ).attr( 'value' ); } );
// check we don't just have one table selected as we get a string not array
if ( ! $.isArray( data[ 'tables[]' ] ) )
data[ 'tables[]' ] = [ data[ 'tables[]' ] ];
// add in ajax and submit params
data = $.extend( {
ajax: true,
submit: submit
}, data );
// count down & stop button
if ( submit.match( /dryrun|liverun|innodb|utf8|utf8mb4/ ) ) {
// insert stop button
$( '<input type="submit" name="submit[stop]" value="stop" class="stop-button" />' )
.click( function() {
clearInterval( t.countdown );
t.escape = true;
t.complete();
$( '[type="submit"].db-required' ).removeAttr( 'disabled' );
$button.val( button_text );
} )
.insertAfter( $button );
if ( submit.match( /liverun|innodb|utf8|utf8mb4/ ) ) {
$button.val( button_text + ' in ... ' + seconds );
t.countdown = setInterval( function() {
if ( seconds == 0 ) {
clearInterval( t.countdown );
$button.val( button_text );
t.run( data );
return;
}
$button.val( button_text + ' in ... ' + --seconds );
}, 1000 );
} else {
t.run( data );
}
} else {
t.run( data );
}
return false;
},
// trigger ajax
run: function( data ) {
var $feedback = $( '.errors, .report' ),
feedback_length = $feedback.length;
// set running flag
t.running = true;
// clear previous errors
if ( feedback_length ) {
$feedback.each( function( i ) {
$( this ).fadeOut( 200, function() {
$( this ).remove();
// start recursive table post
if ( i+1 == feedback_length )
t.recursive_fetch_json( data, 0 );
} );
} );
} else {
// start recursive table post
t.recursive_fetch_json( data, 0 );
}
return false;
},
complete: function() {
// remove spinner
$( '[type="submit"]' )
.removeClass( 'active' )
.not( '.db-required' )
.removeAttr( 'disabled' );
if ( typeof t.errors.db != 'undefined' && ! t.errors.db.length )
$( '[type="submit"].db-required' ).removeAttr( 'disabled' );
t.running = false;
$( '.stop-button' ).remove();
},
recursive_fetch_json: function( data, i ) {
// break from loop
if ( t.escape ) {
return false;
}
if ( data[ 'tables[]' ].length && typeof data[ 'tables[]' ][ i ] == 'undefined' ) {
t.complete();
return false;
}
// clone data
var post_data = $.extend( true, {}, data ),
dry_run = data.submit != 'submit[liverun]',
strings = dry_run ? t.strings_dry : t.strings_live,
result = true,
start = Date.now() / 1000,
end = start;
// remap values so we just do one table at a time
post_data[ 'tables[]' ] = [ data[ 'tables[]' ][ i ] ];
post_data.use_tables = 'subset';
// processing function
function process_response( response ) {
if ( response ) {
var errors = response.errors,
report = response.report,
info = response.info;
// append errors
$.each( errors, function( type, error_list ) {
if ( ! error_list.length ) {
if ( type == 'db' ) {
$( '[name="use_tables"]' ).removeAttr( 'disabled' );
// update the table dropdown if we're changing db
if ( $( '.table-select' ).html() == '' || ( t.prev_data.name && t.prev_data.name !== data.name ) )
$( '.table-select' ).html( info.table_select );
// add/remove innodb button if innodb is available or not
if ( $.inArray( 'InnoDB', info.engines ) >= 0 && ! $( '[name="submit\[innodb\]"]' ).length )
$( '[name="submit\[utf8\]"]' ).before( '<input type="submit" name="submit[innodb]" value="convert to innodb" class="db-required secondary field-advanced" />' );
}
return;
}
var $row = $( '.row-' + type ),
$errors = $row.find( '.errors' );
if ( ! $errors.length ) {
$errors = $( '<div class="errors"></div>' ).hide().insertAfter( $( 'legend,h1', $row ) );
$errors.fadeIn( 200 );
}
$.each( error_list, function( i, error ) {
if ( ! t.errors[ type ] || $.inArray( error, t.errors[ type ] ) < 0 )
$( '<p>' + error + '</p>' ).hide().appendTo( $errors ).fadeIn( 200 );
} );
if ( type == 'db' ) {
$( '[name="use_tables"]' ).eq(0).click().end().attr( 'disabled', 'disabled' );
$( '.table-select' ).html( '' );
$( '[name="submit\[innodb\]"]' ).remove();
}
} );
// scroll back to top most errors block
//if ( t.errors !== errors && $( '.errors' ).length && $( '.errors' ).eq( 0 ).offset().top < $( 'body' ).scrollTop() )
// $( 'html,body' ).animate( { scrollTop: $( '.errors' ).eq(0).offset().top }, 300 );
// track errors
$.extend( true, t.errors, errors );
// track info
$.extend( true, t.info, info );
// append reports
if ( report.tables ) {
var $row = $( '.row-results' ),
$report = $row.find( '.report' ),
$table_reports = $row.find( '.table-reports' );
if ( ! $report.length )
$report = $( '<div class="report"></div>' ).appendTo( $row );
end = Date.now() / 1000;
t.tables += report.tables;
t.rows += report.rows;
t.changes += report.change;
t.updates += report.updates;
t.time += t.get_time( start, end );
if ( ! $report.find( '.main-report' ).length ) {
$( t.report_tpl )
.find( '[data-report="search_replace"]' ).html( strings.search_replace ).end()
.find( '[data-report="search"]' ).text( data.search ).end()
.find( '[data-report="replace"]' ).text( data.replace ).end()
.find( '[data-report="dry_run"]' ).html( strings.updates ).end()
.prependTo( $report );
}
$( '.main-report' )
.find( '[data-report="tables"]' ).html( t.tables ).end()
.find( '[data-report="rows"]' ).html( t.rows ).end()
.find( '[data-report="changes"]' ).html( t.changes ).end()
.find( '[data-report="updates"]' ).html( t.updates ).end()
.find( '[data-report="time"]' ).html( t.time.toFixed( 7 ) ).end();
if ( ! $table_reports.length )
$table_reports = $( '\
<table class="table-reports">\
<thead>\
<tr>\
<th>Table</th>\
<th>Rows</th>\
<th>Cells changed</th>\
<th>Updates</th>\
<th>Seconds</th>\
</tr>\
</thead>\
<tbody></tbody>\
</table>' ).appendTo( $report );
$.each( report.table_reports, function( table, table_report ) {
var $view_changes = '',
changes_length = table_report.changes.length;
if ( changes_length ) {
$view_changes = $( '<a href="#" title="View the first ' + changes_length + ' modifications">view changes</a>' )
.data( 'report', table_report )
.data( 'table', table )
.click( t.changes_overlay );
}
$( '<tr class="' + table + '">' + t.table_report_tpl + '</tr>' )
.hide()
.find( '[data-report="table"]' ).html( table ).end()
.find( '[data-report="rows"]' ).html( table_report.rows ).end()
.find( '[data-report="changes"]' ).html( table_report.change + ' ' ).append( $view_changes ).end()
.find( '[data-report="updates"]' ).html( table_report.updates ).end()
.find( '[data-report="time"]' ).html( t.get_time( start, end ).toFixed( 7 ) ).end()
.appendTo( $table_reports.find( 'tbody' ) )
.fadeIn( 150 );
} );
$.extend( true, t.report, report );
// fetch next table
t.recursive_fetch_json( data, ++i );
} else if ( report.engine ) {
var $row = $( '.row-results' ),
$report = $row.find( '.report' ),
$table_reports = $row.find( '.table-reports' );
if ( ! $report.length )
$report = $( '<div class="report"></div>' ).appendTo( $row );
if ( ! $table_reports.length )
$table_reports = $( '\
<table class="table-reports">\
<thead>\
<tr>\
<th>Table</th>\
<th>Engine</th>\
</tr>\
</thead>\
<tbody></tbody>\
</table>' ).appendTo( $report );
$.each( report.converted, function( table, converted ) {
$( '<tr class="' + table + '"><td>' + table + '</td><td>' + report.engine + '</td></tr>' )
.hide()
.prependTo( $table_reports.find( 'tbody' ) )
.fadeIn( 150 );
$( '.table-select option[value="' + table + '"]' ).html( function(){
return $( this ).html().replace( new RegExp( table + ': [^,]+' ), table + ': ' + report.engine );
} );
} );
// fetch next table
t.recursive_fetch_json( data, ++i );
} else if ( report.collation ) {
var $row = $( '.row-results' ),
$report = $row.find( '.report' ),
$table_reports = $row.find( '.table-reports' );
if ( ! $report.length )
$report = $( '<div class="report"></div>' ).appendTo( $row );
if ( ! $table_reports.length )
$table_reports = $( '\
<table class="table-reports">\
<thead>\
<tr>\
<th>Table</th>\
<th>Charset</th>\
<th>Collation</th>\
</tr>\
</thead>\
<tbody></tbody>\
</table>' ).appendTo( $report );
$.each( report.converted, function( table, converted ) {
$( '\
<tr class="' + table + '">\
<td>' + table + '</td>\
<td>' + report.collation.replace( /^([^_]+).*$/, '$1' ) + '</td>\
<td>' + report.collation + '</td>\
</tr>' )
.hide()
.appendTo( $table_reports.find( 'tbody' ) )
.fadeIn( 150 );
$( '.table-select option[value="' + table + '"]' ).html( function(){
return $( this ).html().replace( new RegExp( 'collation: .*?$' ), 'collation: ' + report.collation );
} );
} );
// fetch next table
t.recursive_fetch_json( data, ++i );
} else {
console.log( 'no report' );
t.complete();
}
} else {
console.log( 'no response' );
t.complete();
}
// remember previous request
t.prev_data = $.extend( {}, data );
return true;
}
return $.ajax( {
url: window.location.href,
data: post_data,
type: 'POST',
dataType: 'json',
// sometimes WordPress forces a 404, we can still get responseJSON in some cases though
error: function( xhr ) {
if ( xhr.responseJSON )
process_response( xhr.responseJSON );
else {
// handle error
alert(
'The script encountered an error while running an AJAX request.\
\
If you are using your hosts file to map a domain try browsing via the IP address directly.\
\
If you are still running into problems we recommend trying the CLI script bundled with this package.\
See the README for details.'
);
try {
process_response({errors:{db:['The script encountered an error while running an AJAX request.']}});
} catch (e) {
// We're not interested in the nuts and bolts.
// Squelch exceptions and just use process_response to print a generic error.
}
// Reactivate the interface.
t.complete();
}
},
success: function( data ) {
process_response( data );
}
} );
},
get_time: function( start, end ) {
start = start || 0.0;
end = end || 0.0;
start = parseFloat( start );
end = parseFloat( end );
var diff = end - start;
return parseFloat( diff < 0.0 ? 0.0 : diff );
},
changes_overlay: function( e ) {
e.preventDefault();
var $overlay = $( '.changes-overlay' ),
table = $( this ).data( 'table' ),
report = $( this ).data( 'report' )
changes = report.changes,
search = $( '[name="search"]' ).val(),
replace = $( '[name="replace"]' ).val(),
regex = $( '[name="regex"]' ).is( ':checked' ),
regex_i = $( '[name="regex_i"]' ).is( ':checked' ),
regex_m = $( '[name="regex_m"]' ).is( ':checked' ),
regex_search_iter = new RegExp( search, 'g' + ( regex_i ? 'i' : '' ) + ( regex_m ? 'm' : '' ) ),
regex_search = new RegExp( search, 'g' + ( regex_i ? 'i' : '' ) + ( regex_m ? 'm' : '' ) );
if ( ! $overlay.length ) {
$overlay = $( '<div class="changes-overlay"><div class="overlay-header"><a class="close" href="#close">&times; Close</a><h1></h1></div><div class="changes"></div></div>' )
.hide()
.find( '.close' )
.click( function( e ) {
e.preventDefault();
$overlay.fadeOut( 300 );
$( 'body' ).css( { overflow: 'auto' } );
} )
.end()
.appendTo( $( 'body' ) );
$( document ).on( 'keyup', function( e ) {
// escape key
if ( $overlay.is( ':visible' ) && e.which == 27 ) {
$overlay.find( '.close' ).click();
}
} );
}
$( 'body' ).css( { overflow: 'hidden' } );
$overlay
.find( 'h1' ).html( table + ' <small>Showing first 20 changes</small>' ).end()
.find( '.changes' ).html( '' ).end()
.fadeIn( 300 )
.find( '.changes' ).html( function() {
var $changes = $( this );
$.each( changes, function( i, item ) {
if ( i >= 20 )
return false;
var match_search,
match_replace,
text,
$change = $( '\
<div class="diff-wrap">\
<h3>row ' + item.row + ', column `' + item.column + '`</h3>\
<div class="diff">\
<pre class="from"></pre>\
<pre class="to"></pre>\
</div>\
</div>' )
.find( '.from' ).text( item.from ).end()
.find( '.to' ).text( item.to ).end()
.appendTo( $changes );
var from_div = $change.find('.from');
var to_div = $change.find('.to');
var original_text = from_div.html();
// Only display highlights if this isn't a serialised object.
// We CANNOT show highlights properly without writing a FULL COMPLETE
// php compatible serialize unserialize pair.
// Any attempt to work around the above restriction will not work,
// if you try it, you will find you are -writing such functions yourself-!
if ( !containsSerialisedString( original_text ) )
{
if ( regex ) {
var result_of_regex;
var copied_char_from_source = 0;
var output_search_panel = '';
var output_replace_panel = '';
while ( result_of_regex = regex_search_iter.exec( original_text ) ) {
var search_match_start = result_of_regex.index;
var search_match_end = regex_search_iter.lastIndex;
output_search_panel = output_search_panel + original_text.slice(copied_char_from_source, search_match_start);
output_replace_panel = output_replace_panel + original_text.slice(copied_char_from_source, search_match_start);
output_search_panel = output_search_panel + '<span class="highlight">';
output_search_panel = output_search_panel + original_text.slice(search_match_start, search_match_end);
output_search_panel = output_search_panel + '</span>';
output_replace_panel = output_replace_panel + '<span class="highlight">';
output_replace_panel = output_replace_panel + original_text.slice(search_match_start, search_match_end).replace( regex_search, replace );
output_replace_panel = output_replace_panel + '</span>';
copied_char_from_source = search_match_end;
}
output_search_panel = output_search_panel + original_text.slice(copied_char_from_source);
output_replace_panel = output_replace_panel + original_text.slice(copied_char_from_source);
from_div.html( output_search_panel );
to_div.html( output_replace_panel );
} else {
// Do a multiple straight up search replace on search with the highlight string we want to put in.
var original_chunks = original_text.split(search);
from_div.html( original_chunks.join('<span class="highlight">' + search + '</span>') );
if (replace)
{
// only display highlights if this isn't a serialised object
if ( !containsSerialisedString( to_div.html() ) )
{
to_div.html( original_chunks.join('<span class="highlight">' + replace + '</span>') );
}
}
}
}
return true;
} );
$( this ).scrollTop( 0 );
} ).end();
},
onunload: function() {
return window.confirm( t.running ? t.confirm_strings.unload_running : t.confirm_strings.unload_default );
},
fetch_products: function() {
// fetch products feed from interconnectit.com
var $products,
tpl = '\
<div class="product">\
<a href="{{custom_fields.link}}" title="Link opens in new tab" target="_blank">\
<div class="product-thumb"><img src="{{attachments[0].url}}" alt="{{title_plain}}" /></div>\
<h2>{{title}}</h2>\
<div class="product-description">{{content}}</div>\
</a>\
</div>';
// get products as jsonp
$.ajax( {
type: 'GET',
url: 'http://products.network.interconnectit.com/api/core/get_posts/',
data: { order: 'ASC', orderby: 'menu_order title' },
dataType: 'jsonp',
jsonpCallback: 'show_products',
contentType: 'application/json',
success: function( products ) {
$products = $( '.row-products .content' ).html( '' );
$.each( products.posts, function( i, product ) {
// run template replacement
$products.append( tpl.replace( /{{([a-z\.\[\]0-9_]+)}}/g, function( match, p1, offset, search ) {
return typeof eval( 'product.' + p1 ) != 'undefined' ? eval( 'product.' + p1 ) : '';
} ) );
} );
},
error: function(e) {
}
} );
},
fetch_blogs: function() {
// fetch products feed from interconnectit.com
var $blogs,
tpl = '\
<div class="blog">\
<a href="{{url}}" title="Link opens in new tab" target="_blank">\
<h2>{{title}}</h2>\
<div class="date">{{date}}</div>\
<div class="categories">Filed under: {{categories}}</div>\
</a>\
</div>';
// get products as jsonp
$.ajax( {
type: 'GET',
url: 'http://interconnectit.com/api/core/get_posts/',
data: { count: 3, category__not_in: [ 216 ] },
dataType: 'jsonp',
jsonpCallback: 'show_blogs',
contentType: 'application/json',
success: function( blogs ) {
$blogs = $( '.row-blog .content' ).html( '' );
$.each( blogs.posts, function( i, blog ) {
// run template replacement
$blogs.append( tpl.replace( /{{([a-z\.\[\]0-9_]+)}}/g, function( match, p1, offset, search ) {
var value = typeof eval( 'blog.' + p1 ) != 'undefined' ? eval( 'blog.' + p1 ) : '';
if ( p1 == 'date' )
value = new Date( value ).toDateString();
if ( p1 == 'categories' )
value = $.map( value, function( category, i ){ return category.title; } ).join( ', ' );
return value;
} ) );
} );
},
error: function(e) {
}
} );
},
mailchimp: function( e ) {
e.preventDefault();
var $this = $( this ),
$form = $this.is( 'form' ) ? $this : $this.parents( 'form' ),
$button = $form.find( 'input[type="submit"]' ).addClass( 'active' ),
action = $form.attr( 'action' ).replace( /subscribe\/post$/, 'subscribe/post-json' );
// remove errors
$( '.row-subscribe .errors' ).remove();
// get response from mailchimp
$.ajax( {
type: 'GET',
url: action,
data: $form.serialize() + '&c=?',
dataType: 'json',
success: function( response ) {
if ( response && response.result == 'success' ) {
$form.find( '>*' ).fadeOut( 150, function() {
$form.html( '' );
$( '<div class="content"><p class="thanks">Success! We didn&rsquo;t think it was possible but now we like you even more!</p></div>' )
.hide()
.insertAfter( $form )
.fadeIn( 300 );
$form.remove();
} );
}
if ( response && response.result != 'success' ) {
$( '<div class="errors"><p>Computer says no&hellip; Can you check you&rsquo;ve filled in the email address field correctly?</p></div>' )
.hide()
.insertAfter( '.row-subscribe h1' )
.fadeIn( 200 );
}
},
complete: function() {
$button.removeClass( 'active' );
}
} );
}
} );
// constructor
t.init();
return t;
}
// load on ready
$( document ).ready( srdb );
})(jQuery);
</script>
<?php
}
}
// initialise
new icit_srdb_ui();