This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,569 @@
<?php
/**
* @phpstan-type DatabaseStatus array{
* status: string|false,
* inProgress: bool,
* current?: string,
* next?: string,
* time?: float,
* manual?: array<int, string>,
* result?: 'ok'|'error',
* reason?: string|false,
* debug?: array<int, string>,
* complete?: int|float
* }
*/
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';
/**
* Current upgrade stage
*
* @var string|false
*/
private $stage = false;
/**
* List of all upgrade stages
*
* @var array<int, string>
*/
private $stages = [];
/**
* Current database status
*
* @var string|false
*/
private $status = false;
/**
* Result of last operation
*
* @var string|false
*/
private $result = false;
/**
* Reason for current status
*
* @var string|false
*/
private $reason = false;
/**
* Debug information
*
* @var array<int, string>
*/
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;
}
}
/**
* Load current upgrade stage from options
*
* @return void
*/
public function load_stage(): void {
$settings = Red_Options::get();
if ( isset( $settings[ self::DB_UPGRADE_STAGE ] ) ) {
$stage_data = $settings[ self::DB_UPGRADE_STAGE ];
// Database stage can be set to false to clear it - only process if it's an array
// phpcs:ignore function.alreadyNarrowedType
// @phpstan-ignore function.alreadyNarrowedType
if ( ! is_array( $stage_data ) ) {
return;
}
$this->stage = isset( $stage_data['stage'] ) ? $stage_data['stage'] : false;
$this->stages = isset( $stage_data['stages'] ) ? $stage_data['stages'] : [];
// Only override status if we have a saved upgrade in progress
if ( isset( $stage_data['status'] ) && $stage_data['status'] !== false ) {
$this->status = $stage_data['status'];
}
}
}
/**
* Does the database need install
*
* @return bool true if needs installing, false otherwise
*/
public function needs_installing(): bool {
$settings = Red_Options::get();
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(): bool {
// 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() !== false && $this->status !== self::STATUS_NEED_INSTALL ) {
return true;
}
return false;
}
/**
* Get current database version
*
* @return string Current database version
*/
public function get_current_version(): string {
$settings = Red_Options::get();
if ( $settings['database'] !== '' ) {
if ( $settings['database'] === '+OK' ) {
return REDIRECTION_DB_VERSION;
}
return $settings['database'];
}
$version = $this->get_old_version();
if ( $version !== false && $version !== '' && $version !== '0' && $version !== 0 ) {
// Upgrade the old value
$version = (string) $version;
red_set_options( array( 'database' => $version ) );
delete_option( self::OLD_DB_VERSION );
$this->clear_cache();
return $version;
}
return '';
}
/**
* Get old database version from legacy option
*
* @return mixed
*/
private function get_old_version() {
return get_option( self::OLD_DB_VERSION );
}
/**
* Check if required database tables exist
*
* @return void
*/
public function check_tables_exist(): void {
$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( self::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( string $version ): bool {
return version_compare( $this->get_current_version(), $version, 'ge' );
}
/**
* Check if last operation resulted in error
*
* @return bool
*/
public function is_error(): bool {
return $this->result === self::RESULT_ERROR;
}
/**
* Set error status
*
* @param string $error Error message.
* @return void
*/
public function set_error( string $error ): void {
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();
}
/**
* Set success status
*
* @param string $reason Success message.
* @return void
*/
public function set_ok( string $reason ): void {
$this->reason = $reason;
$this->result = self::RESULT_OK;
$this->debug = [];
}
/**
* Stop current upgrade
*
* @return void
*/
public function stop_update(): void {
$this->stage = false;
$this->stages = [];
$this->debug = [];
red_set_options( [ self::DB_UPGRADE_STAGE => false ] );
$this->clear_cache();
}
/**
* Finish upgrade process
*
* @return void
*/
public function finish(): void {
$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|false Current stage name, or false if not upgrading
*/
public function get_current_stage() {
return $this->stage;
}
/**
* Move current stage on to the next
*
* @return void
*/
public function set_next_stage(): void {
$this->debug = [];
$stage = $this->get_current_stage();
if ( $stage !== false ) {
$stage = $this->get_next_stage( $stage );
// Save next position
if ( $stage !== false ) {
$this->set_stage( $stage );
} else {
$this->finish();
}
}
}
/**
* Get current upgrade status
*
* @return DatabaseStatus 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;
} elseif ( $this->status === self::STATUS_NEED_INSTALL || $this->status === self::STATUS_NEED_UPDATING ) {
// For fresh install/update that hasn't started yet, set initial progress state
$result['complete'] = 0;
$result['result'] = self::RESULT_OK;
$result['reason'] = false;
}
return $result;
}
/**
* Get error status information
*
* @phpstan-return array{reason: string|false, result: 'error', debug: array<int, string>}
* @return array<string, mixed>
*/
private function get_error_status(): array {
return [
'reason' => $this->reason,
'result' => self::RESULT_ERROR,
'debug' => $this->debug,
];
}
/**
* Get progress status information
*
* @return array{complete: int|float, result: 'ok', reason: string|false}
*/
private function get_progress_status() {
$complete = 0;
if ( $this->stage !== false ) {
$total = count( $this->stages );
$pos = array_search( $this->stage, $this->stages, true );
if ( $pos !== false && $total > 0 ) {
$complete = round( ( $pos / $total ) * 100, 1 );
}
}
return [
'complete' => $complete,
'result' => self::RESULT_OK,
'reason' => $this->reason,
];
}
/**
* Get version upgrade information
*
* @phpstan-return array{current: string, next: string, time: float}
* @return array<string, mixed>
*/
private function get_version_upgrade(): array {
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
*
* @param Red_Database_Upgrade[] $upgrades List of upgrade versions.
* @return void
*/
public function start_install( array $upgrades ) {
$this->set_stages( $upgrades );
$this->status = self::STATUS_NEED_INSTALL;
}
/**
* Start database upgrade process
*
* @param Red_Database_Upgrade[] $upgrades List of upgrade versions.
* @return void
*/
public function start_upgrade( array $upgrades ) {
$this->set_stages( $upgrades );
$this->status = self::STATUS_NEED_UPDATING;
}
/**
* Set upgrade stages
*
* @param Red_Database_Upgrade[] $upgrades List of upgrade versions.
* @return void
*/
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] );
}
}
/**
* @param string|false $stage
* @return void
*/
public function set_stage( $stage ): void {
$this->stage = $stage;
$this->save_details();
}
/**
* @return void
*/
private function save_details(): void {
$stages = [
self::DB_UPGRADE_STAGE => [
'stage' => $this->stage,
'stages' => $this->stages,
'status' => $this->status,
],
];
red_set_options( $stages );
$this->clear_cache();
}
/**
* @return array<int, string>
*/
private function get_manual_upgrade(): array {
$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;
}
/**
* @param string $stage
* @return string|false
*/
private function get_next_stage( string $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 );
}
if ( count( $upgraders ) === 0 ) {
return false;
}
$upgrader = Red_Database_Upgrader::get( $upgraders[0] );
// Where are we in this?
$pos = array_search( $stage, $this->stages, true );
if ( $pos === false ) {
return false;
}
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() );
$current_position = array_search( $stage, $current_stages, true );
if ( $current_position !== false && $current_position === count( $current_stages ) - 1 && isset( $upgraders[1] ) ) {
$this->save_db_version( $upgraders[1]->get_version() );
}
// Move on to next in current version
return $this->stages[ $pos + 1 ];
}
/**
* @param string $version
* @return void
*/
public function save_db_version( string $version ): void {
red_set_options( array( 'database' => $version ) );
delete_option( self::OLD_DB_VERSION );
$this->clear_cache();
}
/**
* @return void
*/
private function clear_cache(): void {
// Clear Red_Options in-memory cache
Red_Options::reset();
// Clear WordPress object cache if available
if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && function_exists( 'wp_cache_flush' ) ) {
wp_cache_flush();
}
}
}
@@ -0,0 +1,44 @@
<?php
class Red_Database_Upgrade {
/**
* @var string
*/
private $version;
/**
* @var string
*/
private $file;
/**
* @var class-string<Red_Database_Upgrader>
*/
private $class;
/**
* @param string $version
* @param string $file
* @param class-string<Red_Database_Upgrader> $class
*/
public function __construct( string $version, string $file, string $class ) {
$this->version = $version;
$this->file = $file;
$this->class = $class;
}
public function get_version(): string {
return $this->version;
}
public function get_file(): string {
return $this->file;
}
/**
* @return class-string<Red_Database_Upgrader>
*/
public function get_class(): string {
return $this->class;
}
}
@@ -0,0 +1,177 @@
<?php
require_once __DIR__ . '/database-upgrade.php';
abstract class Red_Database_Upgrader {
/**
* @var list<string>
*/
private $queries = [];
/**
* @var bool
*/
private $live = true;
/**
* Return an array of all the stages for an upgrade
*
* @return array<string, string> stage name => reason
*/
abstract public function get_stages();
/**
* @param string $stage
* @return string
*/
public function get_reason( string $stage ): string {
$stages = $this->get_stages();
if ( isset( $stages[ $stage ] ) ) {
return $stages[ $stage ];
}
return 'Unknown';
}
/**
* Run a particular stage on the current upgrader
*
* @param Red_Database_Status $status
* @return void
*/
public function perform_stage( Red_Database_Status $status ): void {
global $wpdb;
$stage = $status->get_current_stage();
if ( is_string( $stage ) && $this->has_stage( $stage ) && method_exists( $this, $stage ) ) {
try {
$this->invoke_stage( $stage, $wpdb, true );
$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 );
}
}
/**
* @param string $stage
* @return list<string>
*/
public function get_queries_for_stage( string $stage ): array {
global $wpdb;
$this->queries = [];
$this->live = false;
$this->invoke_stage( $stage, $wpdb, false );
$this->live = true;
return $this->queries;
}
/**
* Returns the current database charset
*
* @return string Database charset
*/
public function get_charset(): string {
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
*
* @param wpdb $wpdb WordPress database instance.
* @param string $sql SQL query.
* @return bool true if query is performed ok, otherwise an exception is thrown
*/
protected function do_query( wpdb $wpdb, string $sql ): bool {
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 ) ); // phpcs:ignore
}
return true;
}
/**
* Load a database upgrader class
*
* @param Red_Database_Upgrade $version
* @return Red_Database_Upgrader Database upgrader
*/
public static function get( Red_Database_Upgrade $version ): Red_Database_Upgrader {
include_once __DIR__ . '/schema/' . str_replace( [ '..', '/' ], '', $version->get_file() );
$class = $version->get_class();
return new $class();
}
/**
* @param string $stage
* @return bool
*/
private function has_stage( string $stage ): bool {
return in_array( $stage, array_keys( $this->get_stages() ), true );
}
/**
* @param string $stage
* @param wpdb $wpdb
* @param bool $live
* @return void
*/
private function invoke_stage( string $stage, wpdb $wpdb, bool $live ): void {
if ( ! method_exists( $this, $stage ) ) {
return;
}
// Methods that accept both $wpdb and $live parameters
// Most stage methods only accept $wpdb, but some (like create_groups) accept both
$two_param_methods = [ 'create_groups' ];
if ( in_array( $stage, $two_param_methods, true ) ) {
/** @var callable(wpdb, bool): void $callable */
$callable = [ $this, $stage ];
call_user_func( $callable, $wpdb, $live );
} else {
/** @var callable(wpdb): void $callable */
$callable = [ $this, $stage ];
call_user_func( $callable, $wpdb );
}
}
}
@@ -0,0 +1,138 @@
<?php
require_once __DIR__ . '/database-status.php';
require_once __DIR__ . '/database-upgrade.php';
require_once __DIR__ . '/database-upgrader.php';
class Red_Database {
/**
* Get all upgrades for a database version
*
* @param string $current_version
* @param string|false $current_stage
* @return list<Red_Database_Upgrade> Array of versions from self::get_upgrades()
*/
public function get_upgrades_for_version( $current_version, $current_stage ) {
if ( empty( $current_version ) ) {
return [
new Red_Database_Upgrade( REDIRECTION_DB_VERSION, 'latest.php', 'Red_Latest_Database' ),
];
}
$upgraders = [];
$found = false;
foreach ( $this->get_upgrades() as $upgrade ) {
if ( ! $found ) {
$upgrader = Red_Database_Upgrader::get( $upgrade );
$stage_present = is_string( $current_stage ) && in_array( $current_stage, array_keys( $upgrader->get_stages() ), true );
$same_version = $current_stage === false && version_compare( $upgrade->get_version(), $current_version, 'gt' );
if ( $stage_present || $same_version ) {
$found = true;
}
}
if ( $found ) {
$upgraders[] = $upgrade;
}
}
return $upgraders;
}
/**
* Apply a particular upgrade stage
*
* @return void
*/
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();
}
}
/**
* Apply a callback to all sites in a multisite network, or to the current site if not multisite.
*
* @param callable $callback Callback function to apply to each site.
* @return void
*/
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( (int) $site->blog_id );
$callback();
restore_current_blog();
},
get_sites( [ 'number' => $per_page, 'offset' => $offset ] )
);
}
return;
}
$callback();
}
/**
* Get latest database installer
*
* @return Red_Latest_Database Red_Latest_Database
*/
public static function get_latest_database() {
include_once __DIR__ . '/schema/latest.php';
return new Red_Latest_Database();
}
/**
* List of all upgrades and their associated file
*
* @return list<Red_Database_Upgrade> Database upgrade array
*/
public function get_upgrades() {
return [
new Red_Database_Upgrade( '2.0.1', '201.php', 'Red_Database_201' ),
new Red_Database_Upgrade( '2.1.16', '216.php', 'Red_Database_216' ),
new Red_Database_Upgrade( '2.2', '220.php', 'Red_Database_220' ),
new Red_Database_Upgrade( '2.3.1', '231.php', 'Red_Database_231' ),
new Red_Database_Upgrade( '2.3.2', '232.php', 'Red_Database_232' ),
new Red_Database_Upgrade( '2.3.3', '233.php', 'Red_Database_233' ),
new Red_Database_Upgrade( '2.4', '240.php', 'Red_Database_240' ),
new Red_Database_Upgrade( '4.0', '400.php', 'Red_Database_400' ),
new Red_Database_Upgrade( '4.1', '410.php', 'Red_Database_410' ),
new Red_Database_Upgrade( '4.2', '420.php', 'Red_Database_420' ),
];
}
}
@@ -0,0 +1,21 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_201 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'add_title_201' => 'Add titles to redirects',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
protected function add_title_201( $wpdb ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` ADD `title` varchar(50) NULL" );
}
}
@@ -0,0 +1,37 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_216 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'add_group_indices_216' => 'Add indices to groups',
'add_redirect_indices_216' => 'Add indices to redirects',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
}
@@ -0,0 +1,37 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_220 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'add_group_indices_220' => 'Add group indices to redirects',
'add_log_indices_220' => 'Add indices to logs',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
}
@@ -0,0 +1,53 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_231 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'remove_404_module_231' => 'Remove 404 module',
'create_404_table_231' => 'Create 404 table',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
protected function create_404_table_231( $wpdb ) {
$this->do_query( $wpdb, $this->get_404_table( $wpdb ) );
return true;
}
/**
* @param \wpdb $wpdb
* @return string
*/
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";
}
}
@@ -0,0 +1,22 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_232 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'remove_modules_232' => 'Remove module table',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
protected function remove_modules_232( $wpdb ) {
$this->do_query( $wpdb, "DROP TABLE IF EXISTS {$wpdb->prefix}redirection_modules" );
return true;
}
}
@@ -0,0 +1,24 @@
<?php
// Note: not localised as the messages aren't important enough
class Red_Database_233 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'fix_invalid_groups_233' => 'Migrate any groups with invalid module ID',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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 );
}
}
@@ -0,0 +1,119 @@
<?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 {
/**
* @return array<string, string>
*/
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',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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`)" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
protected function convert_title_to_text_240( $wpdb ) {
return $this->do_query( $wpdb, "ALTER TABLE `{$wpdb->prefix}redirection_items` CHANGE `title` `title` text" );
}
}
@@ -0,0 +1,115 @@
<?php
class Red_Database_400 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
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',
];
}
/**
* @param \wpdb $wpdb
* @param string $column
* @return bool
*/
private function has_column( $wpdb, $column ) {
$wpdb->hide_errors();
$wpdb->suppress_errors( true );
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N );
$wpdb->show_errors();
$wpdb->suppress_errors( false );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), strtolower( $column ) ) !== false ) {
return true;
}
return false;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
private function has_match_index( $wpdb ) {
$wpdb->hide_errors();
$wpdb->suppress_errors( true );
$existing = $wpdb->get_row( "SHOW CREATE TABLE `{$wpdb->prefix}redirection_items`", ARRAY_N );
$wpdb->show_errors();
$wpdb->suppress_errors( false );
if ( isset( $existing[1] ) && strpos( strtolower( $existing[1] ), 'key `match_url' ) !== false ) {
return true;
}
return false;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool|void
*/
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))" );
}
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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
$table_name = $wpdb->prefix . 'redirection_items';
// phpstan requires literal-string but table prefix must be dynamic for WordPress multisite
/** @phpstan-ignore argument.type */
$prepared = $wpdb->prepare( "UPDATE `{$table_name}` SET match_data=%s WHERE regex=0 AND match_url LIKE %s", '{"source":{"flag_query":"exactorder"}}', '%?%' ); // phpcs:ignore
if ( $prepared !== null ) {
$this->do_query( $wpdb, $prepared );
}
// 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=''" );
}
}
@@ -0,0 +1,24 @@
<?php
class Red_Database_410 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
public function get_stages() {
return [
'handle_double_slash' => 'Support double-slash URLs',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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=''" );
}
}
@@ -0,0 +1,134 @@
<?php
class Red_Database_420 extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
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',
];
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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`" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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`" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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;
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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" );
}
/**
* @param \wpdb $wpdb
* @return bool
*/
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" );
}
}
@@ -0,0 +1,313 @@
<?php
/**
* Latest database schema
*/
class Red_Latest_Database extends Red_Database_Upgrader {
/**
* @return array<string, string>
*/
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
*/
/**
* @return bool|\WP_Error
*/
public function install() {
global $wpdb;
foreach ( $this->get_stages() as $stage => $info ) {
// @phpstan-ignore method.dynamicName
$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)
*/
/**
* @return void
*/
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
*/
/**
* @return list<string>
*/
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
*/
/**
* @return list<string>
*/
public function get_table_schema() {
global $wpdb;
$tables = array_keys( $this->get_all_tables() );
$show = array();
foreach ( $tables as $table ) {
// Suppress errors when checking for table existence
$wpdb->hide_errors();
$wpdb->suppress_errors( true );
// These are known queries without user input
// phpcs:ignore
$row = $wpdb->get_row( 'SHOW CREATE TABLE ' . $table, ARRAY_N );
$wpdb->show_errors();
$wpdb->suppress_errors( false );
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
*/
/**
* @return array<string, string>
*/
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
*/
/**
* @param \wpdb $wpdb
* @param bool $is_live
* @return bool
*/
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 !== null ) {
red_set_options( array( 'last_group_id' => $group->id ) );
}
return true;
}
/**
* Creates all the tables
*/
/**
* @param \wpdb $wpdb
* @return bool
*/
public function create_tables( $wpdb ) {
global $wpdb;
foreach ( $this->get_all_tables() as $table => $sql ) {
$sql = (string) preg_replace( '/[ \t]{2,}/', '', $sql );
$this->do_query( $wpdb, $sql );
}
return true;
}
/**
* @param string $prefix
* @param string $charset_collate
* @return string
*/
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 DEFAULT NULL,
`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 DEFAULT NULL,
`match_type` VARCHAR(20) NOT NULL,
`title` TEXT DEFAULT NULL,
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";
}
/**
* @param string $prefix
* @param string $charset_collate
* @return string
*/
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";
}
/**
* @param string $prefix
* @param string $charset_collate
* @return string
*/
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 DEFAULT NULL,
`agent` MEDIUMTEXT DEFAULT NULL,
`referrer` MEDIUMTEXT DEFAULT NULL,
`http_code` INT(11) unsigned NOT NULL DEFAULT 0,
`request_method` VARCHAR(10) DEFAULT NULL,
`request_data` MEDIUMTEXT DEFAULT NULL,
`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";
}
/**
* @param string $prefix
* @param string $charset_collate
* @return string
*/
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 DEFAULT NULL,
`ip` VARCHAR(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `created` (`created`),
KEY `referrer` (`referrer`(191)),
KEY `ip` (`ip`)
) $charset_collate";
}
}