Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,391 @@
<?php
class Red_Database_Status {
// Used in < 3.7 versions of Redirection, but since migrated to general settings
const OLD_DB_VERSION = 'redirection_version';
const DB_UPGRADE_STAGE = 'database_stage';
const RESULT_OK = 'ok';
const RESULT_ERROR = 'error';
const STATUS_OK = 'ok';
const STATUS_NEED_INSTALL = 'need-install';
const STATUS_NEED_UPDATING = 'need-update';
const STATUS_FINISHED_INSTALL = 'finish-install';
const STATUS_FINISHED_UPDATING = 'finish-update';
private $stage = false;
private $stages = [];
private $status = false;
private $result = false;
private $reason = false;
private $debug = [];
public function __construct() {
$this->status = self::STATUS_OK;
if ( $this->needs_installing() ) {
$this->status = self::STATUS_NEED_INSTALL;
}
$this->load_stage();
if ( $this->needs_updating() ) {
$this->status = self::STATUS_NEED_UPDATING;
}
}
public function load_stage() {
$settings = red_get_options();
if ( isset( $settings[ self::DB_UPGRADE_STAGE ] ) ) {
$this->stage = isset( $settings[ self::DB_UPGRADE_STAGE ]['stage'] ) ? $settings[ self::DB_UPGRADE_STAGE ]['stage'] : false;
$this->stages = isset( $settings[ self::DB_UPGRADE_STAGE ]['stages'] ) ? $settings[ self::DB_UPGRADE_STAGE ]['stages'] : [];
$this->status = isset( $settings[ self::DB_UPGRADE_STAGE ]['status'] ) ? $settings[ self::DB_UPGRADE_STAGE ]['status'] : false;
}
}
/**
* Does the database need install
*
* @return bool true if needs installing, false otherwise
*/
public function needs_installing() {
$settings = red_get_options();
if ( $settings['database'] === '' && $this->get_old_version() === false ) {
return true;
}
return false;
}
/**
* Does the current database need updating to the target
*
* @return bool true if needs updating, false otherwise
*/
public function needs_updating() {
// We need updating if we don't need to install, and the current version is less than target version
if ( $this->needs_installing() === false && version_compare( $this->get_current_version(), REDIRECTION_DB_VERSION, '<' ) ) {
return true;
}
// Also if we're still in the process of upgrading
if ( $this->get_current_stage() && $this->status !== self::STATUS_NEED_INSTALL ) {
return true;
}
return false;
}
/**
* Get current database version
*
* @return string Current database version
*/
public function get_current_version() {
$settings = red_get_options();
if ( $settings['database'] !== '' && is_string( $settings['database'] ) ) {
if ( $settings['database'] === '+OK' ) {
return REDIRECTION_DB_VERSION;
}
return $settings['database'];
}
if ( $this->get_old_version() !== false ) {
$version = $this->get_old_version();
// Upgrade the old value
if ( $version ) {
red_set_options( array( 'database' => $version ) );
delete_option( self::OLD_DB_VERSION );
$this->clear_cache();
return $version;
}
}
return '';
}
private function get_old_version() {
return get_option( self::OLD_DB_VERSION );
}
public function check_tables_exist() {
$latest = Red_Database::get_latest_database();
$missing = $latest->get_missing_tables();
// No tables installed - do a fresh install
if ( count( $missing ) === count( $latest->get_all_tables() ) ) {
delete_option( Red_Database_Status::OLD_DB_VERSION );
red_set_options( [ 'database' => '' ] );
$this->clear_cache();
$this->status = self::STATUS_NEED_INSTALL;
$this->stop_update();
} elseif ( count( $missing ) > 0 && version_compare( $this->get_current_version(), '2.3.3', 'ge' ) ) {
// Some tables are missing - try and fill them in
$latest->install();
}
}
/**
* Does the current database support a particular version
*
* @param string $version Target version
* @return bool true if supported, false otherwise
*/
public function does_support( $version ) {
return version_compare( $this->get_current_version(), $version, 'ge' );
}
public function is_error() {
return $this->result === self::RESULT_ERROR;
}
public function set_error( $error ) {
global $wpdb;
$this->result = self::RESULT_ERROR;
$this->reason = str_replace( "\t", ' ', $error );
if ( $wpdb->last_error ) {
$this->debug[] = $wpdb->last_error;
if ( strpos( $wpdb->last_error, 'command denied to user' ) !== false ) {
$this->reason .= ' - ' . __( 'Insufficient database permissions detected. Please give your database user appropriate permissions.', 'redirection' );
}
}
$latest = Red_Database::get_latest_database();
$this->debug = array_merge( $this->debug, $latest->get_table_schema() );
$this->debug[] = 'Stage: ' . $this->get_current_stage();
}
public function set_ok( $reason ) {
$this->reason = $reason;
$this->result = self::RESULT_OK;
$this->debug = [];
}
/**
* Stop current upgrade
*/
public function stop_update() {
$this->stage = false;
$this->stages = [];
$this->debug = [];
red_set_options( [ self::DB_UPGRADE_STAGE => false ] );
$this->clear_cache();
}
public function finish() {
$this->stop_update();
if ( $this->status === self::STATUS_NEED_INSTALL ) {
$this->status = self::STATUS_FINISHED_INSTALL;
} elseif ( $this->status === self::STATUS_NEED_UPDATING ) {
$this->status = self::STATUS_FINISHED_UPDATING;
}
}
/**
* Get current upgrade stage
* @return string|bool Current stage name, or false if not upgrading
*/
public function get_current_stage() {
return $this->stage;
}
/**
* Move current stage on to the next
*/
public function set_next_stage() {
$this->debug = [];
$stage = $this->get_current_stage();
if ( $stage ) {
$stage = $this->get_next_stage( $stage );
// Save next position
if ( $stage ) {
$this->set_stage( $stage );
} else {
$this->finish();
}
}
}
/**
* Get current upgrade status
*
* @return array Database status array
*/
public function get_json() {
// Base information
$result = [
'status' => $this->status,
'inProgress' => $this->stage !== false,
];
// Add on version status
if ( $this->status === self::STATUS_NEED_INSTALL || $this->status === self::STATUS_NEED_UPDATING ) {
$result = array_merge(
$result,
$this->get_version_upgrade(),
[ 'manual' => $this->get_manual_upgrade() ]
);
}
// Add on upgrade status
if ( $this->is_error() ) {
$result = array_merge( $result, $this->get_version_upgrade(), $this->get_progress_status(), $this->get_error_status() );
} elseif ( $result['inProgress'] ) {
$result = array_merge( $result, $this->get_progress_status() );
} elseif ( $this->status === self::STATUS_FINISHED_INSTALL || $this->status === self::STATUS_FINISHED_UPDATING ) {
$result['complete'] = 100;
$result['reason'] = $this->reason;
}
return $result;
}
private function get_error_status() {
return [
'reason' => $this->reason,
'result' => self::RESULT_ERROR,
'debug' => $this->debug,
];
}
private function get_progress_status() {
$complete = 0;
if ( $this->stage ) {
$complete = round( ( array_search( $this->stage, $this->stages, true ) / count( $this->stages ) ) * 100, 1 );
}
return [
'complete' => $complete,
'result' => self::RESULT_OK,
'reason' => $this->reason,
];
}
private function get_version_upgrade() {
return [
'current' => $this->get_current_version() ? $this->get_current_version() : '-',
'next' => REDIRECTION_DB_VERSION,
'time' => microtime( true ),
];
}
/**
* Set the status information for a database upgrade
*/
public function start_install( array $upgrades ) {
$this->set_stages( $upgrades );
$this->status = self::STATUS_NEED_INSTALL;
}
public function start_upgrade( array $upgrades ) {
$this->set_stages( $upgrades );
$this->status = self::STATUS_NEED_UPDATING;
}
private function set_stages( array $upgrades ) {
$this->stages = [];
foreach ( $upgrades as $upgrade ) {
$upgrader = Red_Database_Upgrader::get( $upgrade );
$this->stages = array_merge( $this->stages, array_keys( $upgrader->get_stages() ) );
}
if ( count( $this->stages ) > 0 ) {
$this->set_stage( $this->stages[0] );
}
}
public function set_stage( $stage ) {
$this->stage = $stage;
$this->save_details();
}
private function save_details() {
$stages = [
self::DB_UPGRADE_STAGE => [
'stage' => $this->stage,
'stages' => $this->stages,
'status' => $this->status,
],
];
red_set_options( $stages );
$this->clear_cache();
}
private function get_manual_upgrade() {
$queries = [];
$database = new Red_Database();
$upgraders = $database->get_upgrades_for_version( $this->get_current_version(), false );
foreach ( $upgraders as $upgrade ) {
$upgrade = Red_Database_Upgrader::get( $upgrade );
$stages = $upgrade->get_stages();
foreach ( array_keys( $stages ) as $stage ) {
$queries = array_merge( $queries, $upgrade->get_queries_for_stage( $stage ) );
}
}
return $queries;
}
private function get_next_stage( $stage ) {
$database = new Red_Database();
$upgraders = $database->get_upgrades_for_version( $this->get_current_version(), $this->get_current_stage() );
if ( count( $upgraders ) === 0 ) {
$upgraders = $database->get_upgrades_for_version( $this->get_current_version(), false );
}
$upgrader = Red_Database_Upgrader::get( $upgraders[0] );
// Where are we in this?
$pos = array_search( $this->stage, $this->stages, true );
if ( $pos === count( $this->stages ) - 1 ) {
$this->save_db_version( REDIRECTION_DB_VERSION );
return false;
}
// Set current DB version
$current_stages = array_keys( $upgrader->get_stages() );
if ( array_search( $this->stage, $current_stages, true ) === count( $current_stages ) - 1 ) {
$this->save_db_version( $upgraders[1]['version'] );
}
// Move on to next in current version
return $this->stages[ $pos + 1 ];
}
public function save_db_version( $version ) {
red_set_options( array( 'database' => $version ) );
delete_option( self::OLD_DB_VERSION );
$this->clear_cache();
}
private function clear_cache() {
if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && function_exists( 'wp_cache_flush' ) ) {
wp_cache_flush();
}
}
}

View File

@@ -0,0 +1,124 @@
<?php
abstract class Red_Database_Upgrader {
private $queries = [];
private $live = true;
/**
* Return an array of all the stages for an upgrade
*
* @return array stage name => reason
*/
abstract public function get_stages();
public function get_reason( $stage ) {
$stages = $this->get_stages();
if ( isset( $stages[ $stage ] ) ) {
return $stages[ $stage ];
}
return 'Unknown';
}
/**
* Run a particular stage on the current upgrader
*
* @return Red_Database_Status
*/
public function perform_stage( Red_Database_Status $status ) {
global $wpdb;
$stage = $status->get_current_stage();
if ( $this->has_stage( $stage ) && method_exists( $this, $stage ) ) {
try {
$this->$stage( $wpdb );
$status->set_ok( $this->get_reason( $stage ) );
} catch ( Exception $e ) {
$status->set_error( $e->getMessage() );
}
} else {
$status->set_error( 'No stage found for upgrade ' . $stage );
}
}
public function get_queries_for_stage( $stage ) {
global $wpdb;
$this->queries = [];
$this->live = false;
$this->$stage( $wpdb, false );
$this->live = true;
return $this->queries;
}
/**
* Returns the current database charset
*
* @return string Database charset
*/
public function get_charset() {
global $wpdb;
$charset_collate = '';
if ( ! empty( $wpdb->charset ) ) {
// Fix some common invalid charset values
$fixes = [
'utf-8',
'utf',
];
$charset = $wpdb->charset;
if ( in_array( strtolower( $charset ), $fixes, true ) ) {
$charset = 'utf8';
}
$charset_collate = "DEFAULT CHARACTER SET $charset";
}
if ( ! empty( $wpdb->collate ) ) {
$charset_collate .= " COLLATE=$wpdb->collate";
}
return $charset_collate;
}
/**
* Performs a $wpdb->query, and throws an exception if an error occurs
*
* @return bool true if query is performed ok, otherwise an exception is thrown
*/
protected function do_query( $wpdb, $sql ) {
if ( ! $this->live ) {
$this->queries[] = $sql;
return true;
}
// These are known queries without user input
// phpcs:ignore
$result = $wpdb->query( $sql );
if ( $result === false ) {
/* translators: 1: SQL string */
throw new Exception( sprintf( 'Failed to perform query "%s"', $sql ) );
}
return true;
}
/**
* Load a database upgrader class
*
* @return object Database upgrader
*/
public static function get( $version ) {
include_once dirname( __FILE__ ) . '/schema/' . str_replace( [ '..', '/' ], '', $version['file'] );
return new $version['class'];
}
private function has_stage( $stage ) {
return in_array( $stage, array_keys( $this->get_stages() ), true );
}
}

View File

@@ -0,0 +1,170 @@
<?php
require_once __DIR__ . '/database-status.php';
require_once __DIR__ . '/database-upgrader.php';
class Red_Database {
/**
* Get all upgrades for a database version
*
* @return array Array of versions from self::get_upgrades()
*/
public function get_upgrades_for_version( $current_version, $current_stage ) {
if ( empty( $current_version ) ) {
return [
[
'version' => REDIRECTION_DB_VERSION,
'file' => 'latest.php',
'class' => 'Red_Latest_Database',
],
];
}
$upgraders = [];
$found = false;
foreach ( $this->get_upgrades() as $upgrade ) {
if ( ! $found ) {
$upgrader = Red_Database_Upgrader::get( $upgrade );
$stage_present = in_array( $current_stage, array_keys( $upgrader->get_stages() ), true );
$same_version = $current_stage === false && version_compare( $upgrade['version'], $current_version, 'gt' );
if ( $stage_present || $same_version ) {
$found = true;
}
}
if ( $found ) {
$upgraders[] = $upgrade;
}
}
return $upgraders;
}
/**
* Apply a particular upgrade stage
*
* @return mixed Result for upgrade
*/
public function apply_upgrade( Red_Database_Status $status ) {
$upgraders = $this->get_upgrades_for_version( $status->get_current_version(), $status->get_current_stage() );
if ( count( $upgraders ) === 0 ) {
$status->set_error( 'No upgrades found for version ' . $status->get_current_version() );
return;
}
if ( $status->get_current_stage() === false ) {
if ( $status->needs_installing() ) {
$status->start_install( $upgraders );
} else {
$status->start_upgrade( $upgraders );
}
}
// Look at first upgrade
$upgrader = Red_Database_Upgrader::get( $upgraders[0] );
// Perform the upgrade
$upgrader->perform_stage( $status );
if ( ! $status->is_error() ) {
$status->set_next_stage();
}
}
public static function apply_to_sites( $callback ) {
if ( is_multisite() && ( is_network_admin() || defined( 'WP_CLI' ) && WP_CLI ) ) {
$total = get_sites( [ 'count' => true ] );
$per_page = 100;
// Paginate through all sites and apply the callback
for ( $offset = 0; $offset < $total; $offset += $per_page ) {
array_map( function( $site ) use ( $callback ) {
switch_to_blog( $site->blog_id );
$callback();
restore_current_blog();
}, get_sites( [ 'number' => $per_page, 'offset' => $offset ] ) );
}
return;
}
$callback();
}
/**
* Get latest database installer
*
* @return object Red_Latest_Database
*/
public static function get_latest_database() {
include_once dirname( __FILE__ ) . '/schema/latest.php';
return new Red_Latest_Database();
}
/**
* List of all upgrades and their associated file
*
* @return array Database upgrade array
*/
public function get_upgrades() {
return [
[
'version' => '2.0.1',
'file' => '201.php',
'class' => 'Red_Database_201',
],
[
'version' => '2.1.16',
'file' => '216.php',
'class' => 'Red_Database_216',
],
[
'version' => '2.2',
'file' => '220.php',
'class' => 'Red_Database_220',
],
[
'version' => '2.3.1',
'file' => '231.php',
'class' => 'Red_Database_231',
],
[
'version' => '2.3.2',
'file' => '232.php',
'class' => 'Red_Database_232',
],
[
'version' => '2.3.3',
'file' => '233.php',
'class' => 'Red_Database_233',
],
[
'version' => '2.4',
'file' => '240.php',
'class' => 'Red_Database_240',
],
[
'version' => '4.0',
'file' => '400.php',
'class' => 'Red_Database_400',
],
[
'version' => '4.1',
'file' => '410.php',
'class' => 'Red_Database_410',
],
[
'version' => '4.2',
'file' => '420.php',
'class' => 'Red_Database_420',
],
];
}
}

View File

@@ -0,0 +1,14 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_201 extends Red_Database_Upgrader {
public function get_stages() {
return [
'add_title_201' => 'Add titles to redirects',
];
}
protected function add_title_201( $wpdb ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `title` varchar(50) NULL" );
}
}

View File

@@ -0,0 +1,26 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_216 extends Red_Database_Upgrader {
public function get_stages() {
return [
'add_group_indices_216' => 'Add indices to groups',
'add_redirect_indices_216' => 'Add indices to redirects',
];
}
protected function add_group_indices_216( $wpdb ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_groups` ADD INDEX(module_id)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_groups` ADD INDEX(status)" );
return true;
}
protected function add_redirect_indices_216( $wpdb ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(url(191))" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(status)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX(regex)" );
return true;
}
}

View File

@@ -0,0 +1,26 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_220 extends Red_Database_Upgrader {
public function get_stages() {
return [
'add_group_indices_220' => 'Add group indices to redirects',
'add_log_indices_220' => 'Add indices to logs',
];
}
protected function add_group_indices_220( $wpdb ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `group_idpos` (`group_id`,`position`)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `group` (`group_id`)" );
return true;
}
protected function add_log_indices_220( $wpdb ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `created` (`created`)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `redirection_id` (`redirection_id`)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `ip` (`ip`)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `group_id` (`group_id`)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD INDEX `module_id` (`module_id`)" );
return true;
}
}

View File

@@ -0,0 +1,37 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_231 extends Red_Database_Upgrader {
public function get_stages() {
return [
'remove_404_module_231' => 'Remove 404 module',
'create_404_table_231' => 'Create 404 table',
];
}
protected function remove_404_module_231( $wpdb ) {
return $this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_groups SET module_id=1 WHERE module_id=3" );
}
protected function create_404_table_231( $wpdb ) {
$this->do_query( $wpdb, $this->get_404_table( $wpdb ) );
}
private function get_404_table( $wpdb ) {
$charset_collate = $this->get_charset();
return "CREATE TABLE `{$wpdb->prefix}redirection_404` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`url` varchar(255) NOT NULL DEFAULT '',
`agent` varchar(255) DEFAULT NULL,
`referrer` varchar(255) DEFAULT NULL,
`ip` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `created` (`created`),
KEY `url` (`url`(191)),
KEY `ip` (`ip`),
KEY `referrer` (`referrer`(191))
) $charset_collate";
}
}

View File

@@ -0,0 +1,15 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_232 extends Red_Database_Upgrader {
public function get_stages() {
return [
'remove_modules_232' => 'Remove module table',
];
}
protected function remove_modules_232( $wpdb ) {
$this->do_query( $wpdb, "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_modules" );
return true;
}
}

View File

@@ -0,0 +1,17 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_233 extends Red_Database_Upgrader {
public function get_stages() {
return [
'fix_invalid_groups_233' => 'Migrate any groups with invalid module ID',
];
}
protected function fix_invalid_groups_233( $wpdb ) {
$this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_groups SET module_id=1 WHERE module_id > 2" );
$latest = Red_Database::get_latest_database();
return $latest->create_groups( $wpdb );
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* There are several problems with 2.3.3 => 2.4 that this attempts to cope with:
* - some sites have a misconfigured IP column
* - some sites don't have any IP column
*/
class Red_Database_240 extends Red_Database_Upgrader {
public function get_stages() {
return [
'convert_int_ip_to_varchar_240' => 'Convert integer IP values to support IPv6',
'expand_log_ip_column_240' => 'Expand IP size in logs to support IPv6',
'convert_title_to_text_240' => 'Expand size of redirect titles',
'add_missing_index_240' => 'Add missing IP index to 404 logs',
];
}
private function has_ip_index( $wpdb ) {
$wpdb->hide_errors();
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N );
$wpdb->show_errors();
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'key `ip` (' ) !== false ) {
return true;
}
return false;
}
protected function has_varchar_ip( $wpdb ) {
$wpdb->hide_errors();
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N );
$wpdb->show_errors();
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), '`ip` varchar(45)' ) !== false ) {
return true;
}
return false;
}
protected function has_int_ip( $wpdb ) {
$wpdb->hide_errors();
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N );
$wpdb->show_errors();
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), '`ip` int' ) !== false ) {
return true;
}
return false;
}
protected function convert_int_ip_to_varchar_240( $wpdb ) {
if ( $this->has_int_ip( $wpdb ) ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `ipaddress` VARCHAR(45) DEFAULT NULL AFTER `ip`" );
$this->do_query( $wpdb, "UPDATE {$wpdb->prefix}redirection_404 SET ipaddress=INET_NTOA(ip)" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP `ip`" );
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` CHANGE `ipaddress` `ip` VARCHAR(45) DEFAULT NULL" );
}
return true;
}
protected function expand_log_ip_column_240( $wpdb ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` CHANGE `ip` `ip` VARCHAR(45) DEFAULT NULL" );
}
protected function add_missing_index_240( $wpdb ) {
if ( $this->has_ip_index( $wpdb ) ) {
// Remove index
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP INDEX ip" );
}
// Ensure we have an IP column
$this->convert_int_ip_to_varchar_240( $wpdb );
if ( ! $this->has_varchar_ip( $wpdb ) ) {
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `ip` VARCHAR(45) DEFAULT NULL" );
}
// Finally add the index
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD INDEX `ip` (`ip`)" );
}
protected function convert_title_to_text_240( $wpdb ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` CHANGE `title` `title` text" );
}
}

View File

@@ -0,0 +1,72 @@
<?php
class Red_Database_400 extends Red_Database_Upgrader {
public function get_stages() {
return [
'add_match_url_400' => 'Add a matched URL column',
'add_match_url_index' => 'Add match URL index',
'add_redirect_data_400' => 'Add column to store new flags',
'convert_existing_urls_400' => 'Convert existing URLs to new format',
];
}
private function has_column( $wpdb, $column ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), strtolower( $column ) ) !== false ) {
return true;
}
return false;
}
private function has_match_index( $wpdb ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'key `match_url' ) !== false ) {
return true;
}
return false;
}
protected function add_match_url_400( $wpdb ) {
if ( ! $this->has_column( $wpdb, '`match_url` varchar(2000)' ) ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `match_url` VARCHAR(2000) NULL DEFAULT NULL AFTER `url`" );
}
return true;
}
protected function add_match_url_index( $wpdb ) {
if ( ! $this->has_match_index( $wpdb ) ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD INDEX `match_url` (`match_url`(191))" );
}
}
protected function add_redirect_data_400( $wpdb ) {
if ( ! $this->has_column( $wpdb, '`match_data` TEXT' ) ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `match_data` TEXT NULL DEFAULT NULL AFTER `match_url`" );
}
return true;
}
protected function convert_existing_urls_400( $wpdb ) {
// All regex get match_url=regex
$this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='regex' WHERE regex=1" );
// Remove query part from all URLs and lowercase
$this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LOWER(url) WHERE regex=0" );
// Set exact match if query param present
$this->do_query( $wpdb, $wpdb->prepare( "UPDATE `{$wpdb->prefix}redirection_items` SET match_data=%s WHERE regex=0 AND match_url LIKE '%?%'", '{"source":{"flag_query":"exactorder"}}' ) );
// Trim the last / from a URL
$this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LEFT(match_url,LENGTH(match_url)-1) WHERE regex=0 AND match_url != '/' AND RIGHT(match_url, 1) = '/'" );
$this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=REPLACE(match_url, '/?', '?') WHERE regex=0" );
// Any URL that is now empty becomes /
return $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='/' WHERE match_url=''" );
}
}

View File

@@ -0,0 +1,17 @@
<?php
class Red_Database_410 extends Red_Database_Upgrader {
public function get_stages() {
return [
'handle_double_slash' => 'Support double-slash URLs',
];
}
protected function handle_double_slash( $wpdb ) {
// Update any URL with a double slash at the end
$this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url=LOWER(LEFT(SUBSTRING_INDEX(url, '?', 1),LENGTH(SUBSTRING_INDEX(url, '?', 1)) - 1)) WHERE RIGHT(SUBSTRING_INDEX(url, '?', 1), 2) = '//' AND regex=0" );
// Any URL that is now empty becomes /
return $this->do_query( $wpdb, "UPDATE `{$wpdb->prefix}redirection_items` SET match_url='/' WHERE match_url=''" );
}
}

View File

@@ -0,0 +1,99 @@
<?php
class Red_Database_420 extends Red_Database_Upgrader {
public function get_stages() {
return [
'add_extra_logging' => 'Add extra logging support',
'remove_module_id' => 'Remove module ID from logs',
'remove_group_id' => 'Remove group ID from logs',
'add_extra_404' => 'Add extra 404 logging support',
];
}
protected function remove_module_id( $wpdb ) {
if ( ! $this->has_module_id( $wpdb ) ) {
return true;
}
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` DROP `module_id`" );
}
protected function remove_group_id( $wpdb ) {
if ( ! $this->has_group_id( $wpdb ) ) {
return true;
}
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` DROP `group_id`" );
}
private function has_module_id( $wpdb ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'module_id' ) !== false ) {
return true;
}
return false;
}
private function has_group_id( $wpdb ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'group_id' ) !== false ) {
return true;
}
return false;
}
private function has_log_domain( $wpdb ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_logs`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'domain` varchar' ) !== false ) {
return true;
}
return false;
}
private function has_404_domain( $wpdb ) {
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_404`", ARRAY_N );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'domain` varchar' ) !== false ) {
return true;
}
return false;
}
protected function add_extra_logging( $wpdb ) {
if ( $this->has_log_domain( $wpdb ) ) {
return true;
}
// Update any URL with a double slash at the end
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `domain` VARCHAR(255) NULL DEFAULT NULL AFTER `url`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `http_code` INT(11) unsigned NOT NULL DEFAULT 0 AFTER `referrer`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `request_method` VARCHAR(10) NULL DEFAULT NULL AFTER `http_code`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `redirect_by` VARCHAR(50) NULL DEFAULT NULL AFTER `request_method`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` ADD `request_data` MEDIUMTEXT NULL DEFAULT NULL AFTER `request_method`" );
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_logs` CHANGE COLUMN `agent` `agent` MEDIUMTEXT NULL" );
}
protected function add_extra_404( $wpdb ) {
if ( $this->has_404_domain( $wpdb ) ) {
return true;
}
// Update any URL with a double slash at the end
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `domain` VARCHAR(255) NULL DEFAULT NULL AFTER `url`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `http_code` INT(11) unsigned NOT NULL DEFAULT 0 AFTER `referrer`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `request_method` VARCHAR(10) NULL DEFAULT NULL AFTER `http_code`" );
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` ADD `request_data` MEDIUMTEXT NULL DEFAULT NULL AFTER `request_method`" );
// Same as log table
$this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` DROP INDEX `url`" );
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_404` CHANGE COLUMN `url` `url` MEDIUMTEXT NOT NULL" );
}
}

View File

@@ -0,0 +1,260 @@
<?php
/**
* Latest database schema
*/
class Red_Latest_Database extends Red_Database_Upgrader {
public function get_stages() {
return [
/* translators: displayed when installing the plugin */
'create_tables' => __( 'Install Redirection tables', 'redirection' ),
/* translators: displayed when installing the plugin */
'create_groups' => __( 'Create basic data', 'redirection' ),
];
}
/**
* Install the latest database
*
* @return bool|WP_Error true if installed, WP_Error otherwise
*/
public function install() {
global $wpdb;
foreach ( $this->get_stages() as $stage => $info ) {
$result = $this->$stage( $wpdb );
if ( is_wp_error( $result ) ) {
if ( $wpdb->last_error ) {
$result->add_data( $wpdb->last_error );
}
return $result;
}
}
red_set_options( array( 'database' => REDIRECTION_DB_VERSION ) );
return true;
}
/**
* Remove the database and any options (including unused ones)
*/
public function remove() {
global $wpdb;
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_items" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_logs" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_groups" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_modules" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_404" );
delete_option( 'redirection_lookup' );
delete_option( 'redirection_post' );
delete_option( 'redirection_root' );
delete_option( 'redirection_index' );
delete_option( 'redirection_options' );
delete_option( Red_Database_Status::OLD_DB_VERSION );
}
/**
* Return any tables that are missing from the database
*
* @return array Array of missing table names
*/
public function get_missing_tables() {
global $wpdb;
$tables = array_keys( $this->get_all_tables() );
$missing = [];
foreach ( $tables as $table ) {
$result = $wpdb->query( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
if ( intval( $result, 10 ) !== 1 ) {
$missing[] = $table;
}
}
return $missing;
}
/**
* Get table schema for latest database tables
*
* @return array Database schema array
*/
public function get_table_schema() {
global $wpdb;
$tables = array_keys( $this->get_all_tables() );
$show = array();
foreach ( $tables as $table ) {
// These are known queries without user input
// phpcs:ignore
$row = $wpdb->get_row( 'SHOW CREATE TABLE ' . $table, ARRAY_N );
if ( $row ) {
$show = array_merge( $show, explode( "\n", $row[1] ) );
$show[] = '';
} else {
/* translators: 1: table name */
$show[] = sprintf( __( 'Table "%s" is missing', 'redirection' ), $table );
}
}
return $show;
}
/**
* Return array of table names and table schema
*
* @return array
*/
public function get_all_tables() {
global $wpdb;
$charset_collate = $this->get_charset();
return array(
"{$wpdb->prefix}redirection_items" => $this->create_items_sql( $wpdb->prefix, $charset_collate ),
"{$wpdb->prefix}redirection_groups" => $this->create_groups_sql( $wpdb->prefix, $charset_collate ),
"{$wpdb->prefix}redirection_logs" => $this->create_log_sql( $wpdb->prefix, $charset_collate ),
"{$wpdb->prefix}redirection_404" => $this->create_404_sql( $wpdb->prefix, $charset_collate ),
);
}
/**
* Creates default group information
*/
public function create_groups( $wpdb, $is_live = true ) {
if ( ! $is_live ) {
return true;
}
$defaults = [
[
'name' => __( 'Redirections', 'redirection' ),
'module_id' => 1,
'position' => 0,
],
[
'name' => __( 'Modified Posts', 'redirection' ),
'module_id' => 1,
'position' => 1,
],
];
$existing_groups = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_groups" );
// Default groups
if ( intval( $existing_groups, 10 ) === 0 ) {
$wpdb->insert( $wpdb->prefix . 'redirection_groups', $defaults[0] );
$wpdb->insert( $wpdb->prefix . 'redirection_groups', $defaults[1] );
}
$group = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}redirection_groups LIMIT 1" );
if ( $group ) {
red_set_options( array( 'last_group_id' => $group->id ) );
}
return true;
}
/**
* Creates all the tables
*/
public function create_tables( $wpdb ) {
global $wpdb;
foreach ( $this->get_all_tables() as $table => $sql ) {
$sql = preg_replace( '/[ \t]{2,}/', '', $sql );
$this->do_query( $wpdb, $sql );
}
return true;
}
private function create_items_sql( $prefix, $charset_collate ) {
return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`url` mediumtext NOT NULL,
`match_url` VARCHAR(2000) DEFAULT NULL,
`match_data` TEXT,
`regex` INT(11) unsigned NOT NULL DEFAULT '0',
`position` INT(11) unsigned NOT NULL DEFAULT '0',
`last_count` INT(10) unsigned NOT NULL DEFAULT '0',
`last_access` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
`group_id` INT(11) NOT NULL DEFAULT '0',
`status` enum('enabled','disabled') NOT NULL DEFAULT 'enabled',
`action_type` VARCHAR(20) NOT NULL,
`action_code` INT(11) unsigned NOT NULL,
`action_data` MEDIUMTEXT,
`match_type` VARCHAR(20) NOT NULL,
`title` TEXT,
PRIMARY KEY (`id`),
KEY `url` (`url`(191)),
KEY `status` (`status`),
KEY `regex` (`regex`),
KEY `group_idpos` (`group_id`,`position`),
KEY `group` (`group_id`),
KEY `match_url` (`match_url`(191))
) $charset_collate";
}
private function create_groups_sql( $prefix, $charset_collate ) {
return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_groups` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`tracking` INT(11) NOT NULL DEFAULT '1',
`module_id` INT(11) unsigned NOT NULL DEFAULT '0',
`status` enum('enabled','disabled') NOT NULL DEFAULT 'enabled',
`position` INT(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `module_id` (`module_id`),
KEY `status` (`status`)
) $charset_collate";
}
private function create_log_sql( $prefix, $charset_collate ) {
return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_logs` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`url` MEDIUMTEXT NOT NULL,
`domain` VARCHAR(255) DEFAULT NULL,
`sent_to` MEDIUMTEXT,
`agent` MEDIUMTEXT,
`referrer` MEDIUMTEXT,
`http_code` INT(11) unsigned NOT NULL DEFAULT '0',
`request_method` VARCHAR(10) DEFAULT NULL,
`request_data` MEDIUMTEXT,
`redirect_by` VARCHAR(50) DEFAULT NULL,
`redirection_id` INT(11) unsigned DEFAULT NULL,
`ip` VARCHAR(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `created` (`created`),
KEY `redirection_id` (`redirection_id`),
KEY `ip` (`ip`)
) $charset_collate";
}
private function create_404_sql( $prefix, $charset_collate ) {
return "CREATE TABLE IF NOT EXISTS `{$prefix}redirection_404` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`url` MEDIUMTEXT NOT NULL,
`domain` VARCHAR(255) DEFAULT NULL,
`agent` VARCHAR(255) DEFAULT NULL,
`referrer` VARCHAR(255) DEFAULT NULL,
`http_code` INT(11) unsigned NOT NULL DEFAULT '0',
`request_method` VARCHAR(10) DEFAULT NULL,
`request_data` MEDIUMTEXT,
`ip` VARCHAR(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `created` (`created`),
KEY `referrer` (`referrer`(191)),
KEY `ip` (`ip`)
) $charset_collate";
}
}