2909 lines
196 KiB
PHP
2909 lines
196 KiB
PHP
|
<?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…" 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…" 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’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’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,iVBORw0KGgoAAAANSUhEUgAAASwAAAH0CAYAAACHEBA3AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMTAvMDkvMTMvbciPAAAgAElEQVR4nO2d7XHbSLO2b791/kunNgDx6QTEE4GwEZgbgeAIlhuB6QgsR2AogqUjWCiCJROYBQPYeqQI/P7oHmEIAiAAgh8j3VeVC6YAzAy+bvQMero/gBBC9vDz589zNwEA8D/nbsBbQ0QmACYACudccax63C8fpgAeACzl358Px6rnUgjOKwDAOZefqy3kfPy/czfgDZIC+MuWx67nDkBx5HouhRR6Xv0/8g6hYMVLYsv8jG0g5KRQsCLE/fJhAuAWwA/59+fzeVtDyOmgYMXJzJbLs7aCkBNDwYqT1JYULPKuoGBFhvvlwzW0O7hmd5C8NyhY8eG7g9k5G0HIOaBgxQfHr8i7pdVx1Jz1EqjD3jOA3Dm36lq4iExt/2v708rK6NWVEZEEwNTKKQCs+rSjobwEekzLNgdPEbm2bafBn1fWhsb9WuoEBpxL4LU7+BHaHexcd9CGGfQ4Cuhx770Odg2nKJ02C2jbe9d/Kir3LTDwfAfljXUPRHcuL41awbILnkEdE6vrNgAWzrmsqVB7MB4A3DSsfwQwb3tgrA0LqEVxVbP+ycqovQlFZAHgM4AvzrlFUGaG7eP6KiKv2wT7X9sx3Le0cQ0gbXsQTKgy1JyLfcdQQ2LLvKGuBfSYPb8653J7UDLo2Jfne91xV9r9UNmnc9tr2lLlBfYCg4rn4BdQUKf3/t+5b239Bnq98o7lTay8jy3bPAF4cM41WryHnktSstMltIu0gl70JwCfAPwK4DcAj1Ar57vdkDuIyAOAP6EP6AbANwBf7N8j9Ea9B7CyG6yujBTAP1CxyqAP3gfn3AcA/2fl3AHIm8qoKXNqxzUN2vRkqz+LyLKybYFSrH4Ex+CPYwO9Ab312HQcf9m5eAz2/2b79zoGDBi/suuZo/5h+WzXq7rPwtpd+4AZvu1p17ZUuLIyPgP4W0Rya+sgrB1/o0GsjBsAf3Vpc3APNoqVcQdg3lLOAsc/l++GOgsrg95Mfzjnqjfz0iyPRV1hIjIH8DtUlBY1+3vLJYPeCEsRmdZYWhOoSKTVdfYWSkXk2eqaY880mKDOvFqm3SQPts5vm0PPwQ/om69oKHeG5qkxE2vbk9VZ3W4uIhlUFJcI5sm1MAOwkX9/9nkTL6HHsrG2TrFtsW5dI7uGVcvIW0Oo7H8F4EFEDuqiG3fQl1gysKy08jtsc1XEHkSksUts98T3mlX+HNaVWVfOuc7lm2VLsOxhvQOwqRMbALCLvPNGsbfjV/vZeNPZ/jMRWUHfOg/YvdmWTV2VgAeoYM32bAeoWBXOuZ1tnXNZ5SbxD/ijc67aruq+bQPf9wCenHNJy/6pdRduRCRt62a7Xz74rnGfwfYF9BxvvXzsQVpAuzJF8PcJymsI6MM1r7bL9vfbXUGvRbKnLX+gfFABtUwT6PXz3eUrmMV5wLjOE/RlmQftnUDPm7dyrqD3XN0LdVLz99oXlwlbWteII5/Ld0u1S+i7JsWAsha2/NLxDeFF777aFeiyf3Dz7IxvVZhBrZe0pawV8DrWcAd9kzaa+R15QTcxXdhy37Z+fR/BukONpWy/k5qXQvj7xbbJqoXa/p/CeuzctbFyzuXBv6Vzbu6cm0C7y54rDHfZ+OacS6pjVHavLCrbNrV3ge176tE5N6sTUOdcZi+kunslrG/sc/luqQrWq6lq1lYf/HhP1mVju6nW9jPpWVcfblHTtWwgteWi75fMGh46luHP+WTPdgmAF/n3Zx/BarOUt14Kdr3DDwxZ24vDHr5N8Kcu4txUVopyPBHQh3bSs5i5c67xJVNjDTfd3+FxbPZZ2Vb2xZzLt85Wl9A592xfLPwgYO2bpUrwRtgAmPS42QqooHQddB7CY48xgSFWzEE451YiArQMylrsKz9w34esx7bVa9Bl3yW0W163f18W2A4bM0NNl62Jkb4yJti2rhYDizr3uXyz1A26z1B+VVrZ17PFHuHyJ/gGw2IV7b1AJoJTlH4sk45lF102srfiFYD1CNbV2KS2zHvu12f7pPL7wYS0jUnw/72D0G2Y+8ULSsFI0EOw2rCvsGmHTav3YT6wyqTy+6Tn8i2zI1hmZSUofZDuoeNMbf4m3rx+xLDxh6avNdfQG22OcmA2/MoyJv5mvTSxAs7j3X6Oh2YV1Nt3SGILe8Gl9q/WH7CGrTpHdOikAI1EreOoWRip+ZB4t4E76NhC02d6QL/E5WM0rOJ8+gQ1z7c+RYvIZQSaPiIW++oGp4999eWEdY1Gg/Poxv72tXan4xPlubxEWqfmmCjNoT5DKVQ0vL/MJBCPHO1ezb0wsfoTak19avvcPyKXaFkBp7OuqsefnWHKyOSQnWv8p7Z6BSLSS7DsHi8GNOUSzuWbpPPkZxONKfTLnvcX8RS2HOvrhi97x2/lWASDtpdmvqe2zI9cT7WbnRy5vi2sCxd23fKe+0+xLVZ/mItDH6Gv1pn0aUPAWc/lW6ZXtAbv9Gk/k+DvBWyqyqE+JN6REup0mR1S1gDW1ob0xPXWEoRCHjTZuVdd2pV/Cf60OGZ9NaSV33nP/RfB/5+a3Dn2UBWaRd1G+7iAc/lm6R1eJjBtqwOZC1v2vlEqPl+JLfO+5YzAq2V3hrrrSGyZnai+8Nrd2NShvYjItG5OYlfMOgrP+WbAWGg456/vvgBeX8ih60jnc1DDWc7lW6e3YAUTddfh380aWkOtrKxHeXNs36y+/9/6lWiAY+teAge+XsdwRLw1m5+ovgdsWwb3Nik5qdtYRGbm9vI39rumTBrKSFDO3fQc+sKorasji8rvxnMgItcikopIXlPOMc/lu6U6lzCBmuZZ3RsumEQM1L/1U+jNd+99X1rCjyQoB/HDryhL6NecVERqPc6t7GXlb9fQAda0rr4ezKA3zb2V2Tb5eQLg+hiTVYPYV30nOw+vU11aUugHD88dNMJB6E5yjfboA3X4CB9LlC+lWU05jz3HnTzroKx7EXkIplxN0GL5i8i1v8+cc4WIfML2eFjdOZigxV3iyOfy3VL3ldD7XW2g4lPY371PlHeu3LkBzGs7Qel4+rdozKjwBryGdnX8RXpCcDPZDfMN6vWbm2j5rzyJteEeOiH1xv6eQt/Kk05H3YIdw28oI0p8FJEf2B3f8A/bl5p1Y3AO3ys455b2wD5g2+rx4WCayDsUf4PSm7uOvRPOW1hgWxz8vfeM+nbfmWXk3SAWfoVNiAd2IzbsOwdbHPlcvku2uoRmVf0KFZEbqDB8tn+/Q0/0N7R89bC32gTlWMBtUIYv5xb6RvxkX3KeK2XMrZ5bAH+KyE/zufoLeoP9ZpEX/Pyz71BhnfQ6+uZjWFo9/hg+Vo7hs7Vtg+OIFXD68atXrGucYHt+XxOP0Hhliz3bbfas++0Q69iuWdXf6RZlXDd/X4fcwaKR1pSXAfgPuk2H+tHSrgzjn8t3y4emFbIbZrZAz3CuUh9atnM5lTasUAlJa13DGUaKWNnQhqZjOGrcIvfLhzmAa/n35+JYdXShMiXK8wyLvtCy3wKV6KfQ85Zg+54a9Txae2dQS/4ZldDIUoaJBjreN3YPTLH9oi7Q01F66Lm8BH7+vAwf7UbBIuQQ6gTr0h9K0sylCBaz5hBCooGCRQiJBgoWISQaKFiEkGigYBFCooGCRQiJBgoWISQaWgP4EXIAGbY9yE8yH5K8beg4SgjZCx1HCSGkJ6N1CX2cn7cy/cLmfU2g88WKszaGEAJgXAvrLwzLSXippNDjSc/bDEKIh11CQkg0ULAIIdEQlWBZDO3i3O0ghJyHqAQLGi64a9pxQsgbIzbBurQkp4SQE0JPd3I2LsUZkcRDbBYWIeQd09nCMsfQJPjTTlKIHuVMoUkCChw5mcPYddYkEsitvJ38i
|
||
|
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>“<span data-report="search"></span>”</strong>\
|
||
|
(to be replaced by <strong>“<span data-report="replace"></span>”</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">× 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’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… Can you check you’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();
|