783 lines
28 KiB
PHP
783 lines
28 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: Ticket Tailor MAX
|
|
* Plugin URI: https://www.tickettailor.com/
|
|
* Description: Integrate Ticket Tailor events and ticketing with WordPress. Display events, sell tickets, and manage orders seamlessly.
|
|
* Version: 4.0.0
|
|
* Author: SSP Media
|
|
* Author URI: https://sspmedia.ca/wordpress/
|
|
* License: GPL v2 or later
|
|
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
|
* Text Domain: ticket-tailor
|
|
* Domain Path: /languages
|
|
* Requires at least: 5.0
|
|
* Requires PHP: 7.2
|
|
* WC tested up to: 8.0
|
|
* WC requires at least: 3.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
// Define plugin constants
|
|
define('TICKET_TAILOR_VERSION', '4.0');
|
|
define('TICKET_TAILOR_PLUGIN_FILE', __FILE__);
|
|
define('TICKET_TAILOR_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
|
define('TICKET_TAILOR_PLUGIN_URL', plugin_dir_url(__FILE__));
|
|
define('TICKET_TAILOR_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
|
|
|
/**
|
|
* Main Ticket Tailor Plugin Class
|
|
*/
|
|
class Ticket_Tailor_Plugin {
|
|
|
|
/**
|
|
* Single instance of the class
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* API Client
|
|
*/
|
|
public $api;
|
|
|
|
/**
|
|
* Event Manager
|
|
*/
|
|
public $events;
|
|
|
|
/**
|
|
* Order Manager
|
|
*/
|
|
public $orders;
|
|
|
|
/**
|
|
* Webhook Handler
|
|
*/
|
|
public $webhooks;
|
|
|
|
/**
|
|
* Admin Handler
|
|
*/
|
|
public $admin;
|
|
|
|
/**
|
|
* Get single instance
|
|
*/
|
|
public static function get_instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->load_dependencies();
|
|
$this->init_components();
|
|
$this->init_hooks();
|
|
}
|
|
|
|
/**
|
|
* Load required files
|
|
*/
|
|
private function load_dependencies() {
|
|
// Security classes - SECURITY ENHANCEMENT
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-security-logger.php';
|
|
|
|
// Core classes
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-api-client.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-event-manager.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-order-manager.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-webhook-handler.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-admin.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-blocks.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-shortcodes.php';
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-template-loader.php';
|
|
|
|
// ENTERPRISE: Health check endpoint
|
|
require_once TICKET_TAILOR_PLUGIN_DIR . 'includes/class-health-check.php';
|
|
}
|
|
|
|
/**
|
|
* Initialize components
|
|
*/
|
|
private function init_components() {
|
|
$this->api = new Ticket_Tailor_API_Client();
|
|
$this->events = new Ticket_Tailor_Event_Manager($this->api);
|
|
$this->orders = new Ticket_Tailor_Order_Manager($this->api);
|
|
$this->webhooks = new Ticket_Tailor_Webhook_Handler($this->events, $this->orders);
|
|
$this->admin = new Ticket_Tailor_Admin($this->api, $this->events, $this->orders);
|
|
|
|
// ENTERPRISE: Health check for monitoring
|
|
new Ticket_Tailor_Health_Check($this->api, $this->events, $this->orders);
|
|
|
|
new Ticket_Tailor_Blocks($this->events);
|
|
new Ticket_Tailor_Shortcodes($this->events, $this->orders);
|
|
}
|
|
|
|
/**
|
|
* Initialize hooks
|
|
*/
|
|
private function init_hooks() {
|
|
register_activation_hook(TICKET_TAILOR_PLUGIN_FILE, array($this, 'activate'));
|
|
register_deactivation_hook(TICKET_TAILOR_PLUGIN_FILE, array($this, 'deactivate'));
|
|
|
|
add_action('plugins_loaded', array($this, 'load_textdomain'));
|
|
add_action('init', array($this, 'register_post_types'));
|
|
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
|
|
|
|
// Cron jobs for syncing
|
|
add_action('ticket_tailor_sync_events', array($this->events, 'sync_all_events'));
|
|
add_action('ticket_tailor_sync_orders', array($this->orders, 'sync_all_orders'));
|
|
|
|
// PERFORMANCE FIX: Cron jobs for cleanup
|
|
add_action('ticket_tailor_cleanup_security_logs', array($this, 'cleanup_security_logs'));
|
|
add_action('ticket_tailor_cleanup_rate_limits', array($this, 'cleanup_rate_limits'));
|
|
}
|
|
|
|
/**
|
|
* Enqueue frontend assets
|
|
*/
|
|
public function enqueue_frontend_assets() {
|
|
// Enqueue frontend CSS
|
|
wp_enqueue_style(
|
|
'ticket-tailor-frontend',
|
|
TICKET_TAILOR_PLUGIN_URL . 'assets/css/frontend.css',
|
|
array(),
|
|
TICKET_TAILOR_VERSION
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Plugin activation - SECURITY ENHANCEMENT: Added custom capabilities
|
|
*/
|
|
public function activate() {
|
|
// Create custom tables
|
|
$this->create_tables();
|
|
|
|
// SECURITY ENHANCEMENT: Add custom capabilities
|
|
$this->add_custom_capabilities();
|
|
|
|
// Schedule cron jobs
|
|
if (!wp_next_scheduled('ticket_tailor_sync_events')) {
|
|
wp_schedule_event(time(), 'hourly', 'ticket_tailor_sync_events');
|
|
}
|
|
|
|
if (!wp_next_scheduled('ticket_tailor_sync_orders')) {
|
|
wp_schedule_event(time(), 'hourly', 'ticket_tailor_sync_orders');
|
|
}
|
|
|
|
// PERFORMANCE FIX: Schedule cleanup cron jobs
|
|
if (!wp_next_scheduled('ticket_tailor_cleanup_security_logs')) {
|
|
wp_schedule_event(time(), 'daily', 'ticket_tailor_cleanup_security_logs');
|
|
}
|
|
|
|
if (!wp_next_scheduled('ticket_tailor_cleanup_rate_limits')) {
|
|
wp_schedule_event(time(), 'hourly', 'ticket_tailor_cleanup_rate_limits');
|
|
}
|
|
|
|
// Set activation time
|
|
if (!get_option('ticket_tailor_activated_time')) {
|
|
add_option('ticket_tailor_activated_time', time());
|
|
}
|
|
|
|
// Set welcome transient
|
|
set_transient('ticket_tailor_welcome_notice', true, 30);
|
|
|
|
// Flush rewrite rules for custom post types
|
|
flush_rewrite_rules();
|
|
}
|
|
|
|
/**
|
|
* Add custom capabilities - SECURITY ENHANCEMENT
|
|
*/
|
|
private function add_custom_capabilities() {
|
|
// Administrator gets all capabilities
|
|
$admin_role = get_role('administrator');
|
|
if ($admin_role) {
|
|
$admin_role->add_cap('manage_ticket_tailor');
|
|
$admin_role->add_cap('view_ticket_tailor_dashboard');
|
|
$admin_role->add_cap('view_ticket_tailor_events');
|
|
$admin_role->add_cap('view_ticket_tailor_orders');
|
|
$admin_role->add_cap('sync_ticket_tailor_events');
|
|
$admin_role->add_cap('configure_ticket_tailor_settings');
|
|
$admin_role->add_cap('configure_ticket_tailor_webhooks');
|
|
}
|
|
|
|
// Editor can view but not configure
|
|
$editor_role = get_role('editor');
|
|
if ($editor_role) {
|
|
$editor_role->add_cap('view_ticket_tailor_dashboard');
|
|
$editor_role->add_cap('view_ticket_tailor_events');
|
|
$editor_role->add_cap('view_ticket_tailor_orders');
|
|
}
|
|
|
|
// Shop Manager (WooCommerce) gets order access
|
|
$shop_manager_role = get_role('shop_manager');
|
|
if ($shop_manager_role) {
|
|
$shop_manager_role->add_cap('view_ticket_tailor_dashboard');
|
|
$shop_manager_role->add_cap('view_ticket_tailor_events');
|
|
$shop_manager_role->add_cap('view_ticket_tailor_orders');
|
|
$shop_manager_role->add_cap('sync_ticket_tailor_events');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Plugin deactivation - FIXED VERSION (No security errors)
|
|
*/
|
|
public function deactivate() {
|
|
// Prevent any admin actions from running during deactivation
|
|
remove_all_actions('admin_init');
|
|
|
|
// Clear scheduled events safely
|
|
$timestamp_events = wp_next_scheduled('ticket_tailor_sync_events');
|
|
if ($timestamp_events) {
|
|
wp_unschedule_event($timestamp_events, 'ticket_tailor_sync_events');
|
|
}
|
|
|
|
$timestamp_orders = wp_next_scheduled('ticket_tailor_sync_orders');
|
|
if ($timestamp_orders) {
|
|
wp_unschedule_event($timestamp_orders, 'ticket_tailor_sync_orders');
|
|
}
|
|
|
|
// Clear all scheduled hooks
|
|
wp_clear_scheduled_hook('ticket_tailor_sync_events');
|
|
wp_clear_scheduled_hook('ticket_tailor_sync_orders');
|
|
wp_clear_scheduled_hook('ticket_tailor_cleanup_security_logs');
|
|
wp_clear_scheduled_hook('ticket_tailor_cleanup_rate_limits');
|
|
|
|
// Clear transients
|
|
delete_transient('ticket_tailor_events_cache');
|
|
delete_transient('ticket_tailor_orders_cache');
|
|
delete_transient('ticket_tailor_welcome_notice');
|
|
|
|
// Clear any user notices - SECURITY FIX: Use prepared statements
|
|
global $wpdb;
|
|
$wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
|
$wpdb->esc_like('_transient_ticket_tailor_notice_') . '%'
|
|
));
|
|
$wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
|
$wpdb->esc_like('_transient_timeout_ticket_tailor_notice_') . '%'
|
|
));
|
|
|
|
// Flush rewrite rules
|
|
flush_rewrite_rules();
|
|
}
|
|
|
|
/**
|
|
* Create custom database tables - ENTERPRISE: Optimized indexes
|
|
*/
|
|
private function create_tables() {
|
|
global $wpdb;
|
|
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
|
|
// Events cache table - ENTERPRISE: Added composite index
|
|
$table_events = $wpdb->prefix . 'ticket_tailor_events';
|
|
$sql_events = "CREATE TABLE IF NOT EXISTS $table_events (
|
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
event_id varchar(255) NOT NULL,
|
|
event_data longtext NOT NULL,
|
|
last_synced datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY event_id (event_id),
|
|
KEY last_synced (last_synced),
|
|
KEY event_id_synced (event_id, last_synced)
|
|
) $charset_collate;";
|
|
|
|
// Orders cache table - ENTERPRISE: Added composite indexes
|
|
$table_orders = $wpdb->prefix . 'ticket_tailor_orders';
|
|
$sql_orders = "CREATE TABLE IF NOT EXISTS $table_orders (
|
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
order_id varchar(255) NOT NULL,
|
|
event_id varchar(255) NOT NULL,
|
|
order_data longtext NOT NULL,
|
|
last_synced datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY order_id (order_id),
|
|
KEY event_id (event_id),
|
|
KEY last_synced (last_synced),
|
|
KEY event_synced (event_id, last_synced),
|
|
KEY order_synced (order_id, last_synced)
|
|
) $charset_collate;";
|
|
|
|
// Webhook idempotency table - ENTERPRISE: Prevent duplicate webhook processing
|
|
$table_webhooks = $wpdb->prefix . 'ticket_tailor_webhook_log';
|
|
$sql_webhooks = "CREATE TABLE IF NOT EXISTS $table_webhooks (
|
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
webhook_id varchar(255) NOT NULL,
|
|
event_type varchar(100) NOT NULL,
|
|
processed_at datetime NOT NULL,
|
|
ip_address varchar(45) NOT NULL,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY webhook_id (webhook_id),
|
|
KEY processed_at (processed_at),
|
|
KEY event_type (event_type)
|
|
) $charset_collate;";
|
|
|
|
// Security log table
|
|
$table_security = $wpdb->prefix . 'ticket_tailor_security_log';
|
|
$sql_security = "CREATE TABLE IF NOT EXISTS $table_security (
|
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
event_type varchar(50) NOT NULL,
|
|
user_id bigint(20) DEFAULT 0,
|
|
ip_address varchar(45) NOT NULL,
|
|
details longtext,
|
|
timestamp datetime NOT NULL,
|
|
PRIMARY KEY (id),
|
|
KEY event_type (event_type),
|
|
KEY timestamp (timestamp),
|
|
KEY ip_address (ip_address)
|
|
) $charset_collate;";
|
|
|
|
// Rate limits table
|
|
$table_rate_limits = $wpdb->prefix . 'ticket_tailor_rate_limits';
|
|
$sql_rate_limits = "CREATE TABLE IF NOT EXISTS $table_rate_limits (
|
|
id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
ip_address varchar(45) NOT NULL,
|
|
endpoint varchar(255) NOT NULL,
|
|
request_count int(11) NOT NULL DEFAULT 1,
|
|
timestamp datetime NOT NULL,
|
|
PRIMARY KEY (id),
|
|
KEY ip_endpoint (ip_address, endpoint),
|
|
KEY timestamp (timestamp)
|
|
) $charset_collate;";
|
|
|
|
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
|
dbDelta($sql_events);
|
|
dbDelta($sql_orders);
|
|
dbDelta($sql_webhooks);
|
|
dbDelta($sql_security);
|
|
dbDelta($sql_rate_limits);
|
|
|
|
// Store database version
|
|
update_option('ticket_tailor_db_version', '2.0');
|
|
}
|
|
|
|
/**
|
|
* Register custom post types
|
|
* FIXED: Added show_in_menu => false to prevent duplicate menu
|
|
*/
|
|
public function register_post_types() {
|
|
// Register Event post type for better WordPress integration
|
|
register_post_type('tt_event', array(
|
|
'labels' => array(
|
|
'name' => __('Events', 'ticket-tailor'),
|
|
'singular_name' => __('Event', 'ticket-tailor'),
|
|
'add_new' => __('Add New Event', 'ticket-tailor'),
|
|
'add_new_item' => __('Add New Event', 'ticket-tailor'),
|
|
'edit_item' => __('Edit Event', 'ticket-tailor'),
|
|
'new_item' => __('New Event', 'ticket-tailor'),
|
|
'view_item' => __('View Event', 'ticket-tailor'),
|
|
'search_items' => __('Search Events', 'ticket-tailor'),
|
|
'not_found' => __('No events found', 'ticket-tailor'),
|
|
'not_found_in_trash' => __('No events found in trash', 'ticket-tailor'),
|
|
'all_items' => __('All Events', 'ticket-tailor'),
|
|
'menu_name' => __('Events', 'ticket-tailor'),
|
|
),
|
|
'public' => true,
|
|
'has_archive' => true,
|
|
'show_in_rest' => true,
|
|
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
|
|
'menu_icon' => 'dashicons-tickets-alt',
|
|
'rewrite' => array(
|
|
'slug' => 'events',
|
|
'with_front' => false,
|
|
),
|
|
// FIX: Hide the automatic menu item since we have our own custom menu structure
|
|
'show_in_menu' => false, // This removes the duplicate "Events" menu
|
|
// The events are still accessible through our custom Ticket Tailor menu
|
|
'capability_type' => 'post',
|
|
'capabilities' => array(
|
|
'publish_posts' => 'publish_tt_events',
|
|
'edit_posts' => 'edit_tt_events',
|
|
'edit_others_posts' => 'edit_others_tt_events',
|
|
'delete_posts' => 'delete_tt_events',
|
|
'delete_others_posts' => 'delete_others_tt_events',
|
|
'read_private_posts' => 'read_private_tt_events',
|
|
'edit_post' => 'edit_tt_event',
|
|
'delete_post' => 'delete_tt_event',
|
|
'read_post' => 'read_tt_event',
|
|
),
|
|
'map_meta_cap' => true,
|
|
'taxonomies' => array('tt_event_category', 'tt_event_tag'),
|
|
'show_ui' => true,
|
|
'show_in_nav_menus' => true,
|
|
'can_export' => true,
|
|
'exclude_from_search' => false,
|
|
'publicly_queryable' => true,
|
|
));
|
|
|
|
// Register Event Category taxonomy
|
|
register_taxonomy('tt_event_category', 'tt_event', array(
|
|
'labels' => array(
|
|
'name' => __('Event Categories', 'ticket-tailor'),
|
|
'singular_name' => __('Event Category', 'ticket-tailor'),
|
|
'search_items' => __('Search Event Categories', 'ticket-tailor'),
|
|
'all_items' => __('All Event Categories', 'ticket-tailor'),
|
|
'parent_item' => __('Parent Event Category', 'ticket-tailor'),
|
|
'parent_item_colon' => __('Parent Event Category:', 'ticket-tailor'),
|
|
'edit_item' => __('Edit Event Category', 'ticket-tailor'),
|
|
'update_item' => __('Update Event Category', 'ticket-tailor'),
|
|
'add_new_item' => __('Add New Event Category', 'ticket-tailor'),
|
|
'new_item_name' => __('New Event Category Name', 'ticket-tailor'),
|
|
'menu_name' => __('Categories', 'ticket-tailor'),
|
|
),
|
|
'hierarchical' => true,
|
|
'public' => true,
|
|
'show_ui' => true,
|
|
'show_admin_column' => true,
|
|
'show_in_nav_menus' => true,
|
|
'show_tagcloud' => true,
|
|
'show_in_rest' => true,
|
|
'rewrite' => array(
|
|
'slug' => 'event-category',
|
|
'with_front' => false,
|
|
'hierarchical' => true,
|
|
),
|
|
));
|
|
|
|
// Register Event Tag taxonomy
|
|
register_taxonomy('tt_event_tag', 'tt_event', array(
|
|
'labels' => array(
|
|
'name' => __('Event Tags', 'ticket-tailor'),
|
|
'singular_name' => __('Event Tag', 'ticket-tailor'),
|
|
'search_items' => __('Search Event Tags', 'ticket-tailor'),
|
|
'popular_items' => __('Popular Event Tags', 'ticket-tailor'),
|
|
'all_items' => __('All Event Tags', 'ticket-tailor'),
|
|
'edit_item' => __('Edit Event Tag', 'ticket-tailor'),
|
|
'update_item' => __('Update Event Tag', 'ticket-tailor'),
|
|
'add_new_item' => __('Add New Event Tag', 'ticket-tailor'),
|
|
'new_item_name' => __('New Event Tag Name', 'ticket-tailor'),
|
|
'separate_items_with_commas' => __('Separate event tags with commas', 'ticket-tailor'),
|
|
'add_or_remove_items' => __('Add or remove event tags', 'ticket-tailor'),
|
|
'choose_from_most_used' => __('Choose from the most used event tags', 'ticket-tailor'),
|
|
'menu_name' => __('Tags', 'ticket-tailor'),
|
|
),
|
|
'hierarchical' => false,
|
|
'public' => true,
|
|
'show_ui' => true,
|
|
'show_admin_column' => true,
|
|
'show_in_nav_menus' => true,
|
|
'show_tagcloud' => true,
|
|
'show_in_rest' => true,
|
|
'rewrite' => array(
|
|
'slug' => 'event-tag',
|
|
'with_front' => false,
|
|
),
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Load text domain for translations
|
|
*/
|
|
public function load_textdomain() {
|
|
load_plugin_textdomain(
|
|
'ticket-tailor',
|
|
false,
|
|
dirname(plugin_basename(TICKET_TAILOR_PLUGIN_FILE)) . '/languages/'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check WooCommerce and HPOS status
|
|
*/
|
|
public function get_woocommerce_status() {
|
|
if (!class_exists('WooCommerce')) {
|
|
return array(
|
|
'active' => false,
|
|
'version' => null,
|
|
'hpos_enabled' => false,
|
|
);
|
|
}
|
|
|
|
$hpos_enabled = false;
|
|
if (class_exists('\Automattic\WooCommerce\Utilities\OrderUtil')) {
|
|
$hpos_enabled = \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
|
|
}
|
|
|
|
return array(
|
|
'active' => true,
|
|
'version' => defined('WC_VERSION') ? WC_VERSION : WC()->version,
|
|
'hpos_enabled' => $hpos_enabled,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the plugin is properly configured
|
|
*/
|
|
public function is_configured() {
|
|
$api_key = get_option('ticket_tailor_api_key', '');
|
|
return !empty($api_key);
|
|
}
|
|
|
|
/**
|
|
* Get plugin version
|
|
*/
|
|
public function get_version() {
|
|
return TICKET_TAILOR_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Get plugin URL
|
|
*/
|
|
public function get_plugin_url() {
|
|
return TICKET_TAILOR_PLUGIN_URL;
|
|
}
|
|
|
|
/**
|
|
* Get plugin path
|
|
*/
|
|
public function get_plugin_path() {
|
|
return TICKET_TAILOR_PLUGIN_DIR;
|
|
}
|
|
|
|
/**
|
|
* Log debug messages - SECURITY FIX: Store logs outside web root or with protection
|
|
*/
|
|
public function log($message, $level = 'info') {
|
|
if (!get_option('ticket_tailor_debug_mode', false)) {
|
|
return;
|
|
}
|
|
|
|
if (is_array($message) || is_object($message)) {
|
|
$message = print_r($message, true);
|
|
}
|
|
|
|
// Sanitize message to prevent log injection
|
|
$message = str_replace(array("\r", "\n"), ' ', $message);
|
|
|
|
$log_entry = sprintf(
|
|
'[%s] [%s] %s',
|
|
date('Y-m-d H:i:s'),
|
|
strtoupper(sanitize_key($level)),
|
|
$message
|
|
);
|
|
|
|
// Use WordPress uploads directory with .htaccess protection
|
|
$upload_dir = wp_upload_dir();
|
|
$log_dir = $upload_dir['basedir'] . '/ticket-tailor-logs';
|
|
|
|
// Create log directory if it doesn't exist
|
|
if (!file_exists($log_dir)) {
|
|
wp_mkdir_p($log_dir);
|
|
|
|
// Create .htaccess to deny web access
|
|
$htaccess_content = "deny from all\n";
|
|
file_put_contents($log_dir . '/.htaccess', $htaccess_content);
|
|
|
|
// Create index.php for additional protection
|
|
file_put_contents($log_dir . '/index.php', '<?php // Silence is golden');
|
|
}
|
|
|
|
$log_file = $log_dir . '/debug.log';
|
|
|
|
// Implement log rotation (max 5MB)
|
|
if (file_exists($log_file) && filesize($log_file) > 5242880) {
|
|
rename($log_file, $log_dir . '/debug-' . date('Y-m-d-His') . '.log');
|
|
}
|
|
|
|
error_log($log_entry . PHP_EOL, 3, $log_file);
|
|
}
|
|
|
|
/**
|
|
* Cleanup security logs - PERFORMANCE FIX
|
|
* Called by daily cron job
|
|
*/
|
|
public function cleanup_security_logs() {
|
|
$security_logger = new Ticket_Tailor_Security_Logger();
|
|
$security_logger->cleanup_old_logs();
|
|
|
|
$this->log('Security logs cleanup completed', 'info');
|
|
}
|
|
|
|
/**
|
|
* Cleanup rate limit table - PERFORMANCE FIX
|
|
* Called by hourly cron job
|
|
*/
|
|
public function cleanup_rate_limits() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'ticket_tailor_rate_limits';
|
|
|
|
// Delete entries older than 24 hours
|
|
$threshold = gmdate('Y-m-d H:i:s', time() - DAY_IN_SECONDS);
|
|
|
|
$deleted = $wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$table_name} WHERE timestamp < %s",
|
|
$threshold
|
|
));
|
|
|
|
if ($deleted !== false) {
|
|
$this->log('Rate limit cleanup completed: ' . $deleted . ' entries removed', 'info');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle plugin upgrade
|
|
*/
|
|
public function maybe_upgrade() {
|
|
$current_version = get_option('ticket_tailor_version', '0.0.0');
|
|
|
|
if (version_compare($current_version, TICKET_TAILOR_VERSION, '<')) {
|
|
// Perform upgrade tasks here if needed
|
|
|
|
// Update version
|
|
update_option('ticket_tailor_version', TICKET_TAILOR_VERSION);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the plugin
|
|
*/
|
|
function ticket_tailor() {
|
|
return Ticket_Tailor_Plugin::get_instance();
|
|
}
|
|
|
|
// Declare WooCommerce HPOS compatibility
|
|
add_action('before_woocommerce_init', function() {
|
|
if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) {
|
|
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
|
|
'custom_order_tables',
|
|
TICKET_TAILOR_PLUGIN_FILE,
|
|
true
|
|
);
|
|
}
|
|
});
|
|
|
|
// Check plugin dependencies
|
|
add_action('admin_init', function() {
|
|
// Skip checks during plugin activation/deactivation
|
|
if (defined('WP_UNINSTALL_PLUGIN') ||
|
|
(isset($_REQUEST['action']) && in_array($_REQUEST['action'], array('activate', 'deactivate', 'delete')))) {
|
|
return;
|
|
}
|
|
|
|
if (!is_plugin_active('ticket-tailor/ticket-tailor.php')) {
|
|
return;
|
|
}
|
|
|
|
// Check PHP version
|
|
if (version_compare(PHP_VERSION, '7.2', '<')) {
|
|
deactivate_plugins(plugin_basename(TICKET_TAILOR_PLUGIN_FILE));
|
|
wp_die(
|
|
esc_html__('Ticket Tailor requires PHP version 7.2 or higher. Please upgrade your PHP version.', 'ticket-tailor'),
|
|
esc_html__('Plugin Activation Error', 'ticket-tailor'),
|
|
array('back_link' => true)
|
|
);
|
|
}
|
|
|
|
// Check WordPress version
|
|
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
|
|
deactivate_plugins(plugin_basename(TICKET_TAILOR_PLUGIN_FILE));
|
|
wp_die(
|
|
esc_html__('Ticket Tailor requires WordPress version 5.0 or higher. Please upgrade WordPress.', 'ticket-tailor'),
|
|
esc_html__('Plugin Activation Error', 'ticket-tailor'),
|
|
array('back_link' => true)
|
|
);
|
|
}
|
|
});
|
|
|
|
// Handle uninstall
|
|
register_uninstall_hook(TICKET_TAILOR_PLUGIN_FILE, 'ticket_tailor_uninstall');
|
|
|
|
function ticket_tailor_uninstall() {
|
|
// Only run if explicitly uninstalling
|
|
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
|
return;
|
|
}
|
|
|
|
// Remove options
|
|
delete_option('ticket_tailor_api_key');
|
|
delete_option('ticket_tailor_cache_duration');
|
|
delete_option('ticket_tailor_currency');
|
|
delete_option('ticket_tailor_debug_mode');
|
|
delete_option('ticket_tailor_webhook_secret');
|
|
delete_option('ticket_tailor_version');
|
|
delete_option('ticket_tailor_db_version');
|
|
delete_option('ticket_tailor_activated_time');
|
|
|
|
// Remove style options
|
|
delete_option('ticket_tailor_text_color');
|
|
delete_option('ticket_tailor_border_color');
|
|
delete_option('ticket_tailor_border_radius');
|
|
delete_option('ticket_tailor_button_bg');
|
|
delete_option('ticket_tailor_button_hover');
|
|
delete_option('ticket_tailor_button_text');
|
|
|
|
// Remove all transients - SECURITY FIX: Use prepared statements
|
|
global $wpdb;
|
|
$wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
|
$wpdb->esc_like('_transient_ticket_tailor_') . '%'
|
|
));
|
|
$wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
|
|
$wpdb->esc_like('_transient_timeout_ticket_tailor_') . '%'
|
|
));
|
|
|
|
// Remove custom tables - SECURITY FIX: Validate table names
|
|
$events_table = $wpdb->prefix . 'ticket_tailor_events';
|
|
$orders_table = $wpdb->prefix . 'ticket_tailor_orders';
|
|
$webhooks_table = $wpdb->prefix . 'ticket_tailor_webhook_log';
|
|
$security_table = $wpdb->prefix . 'ticket_tailor_security_log';
|
|
$rate_limits_table = $wpdb->prefix . 'ticket_tailor_rate_limits';
|
|
|
|
// Validate table names match expected pattern
|
|
if (preg_match('/^[a-zA-Z0-9_]+$/', $events_table)) {
|
|
$wpdb->query("DROP TABLE IF EXISTS `{$events_table}`");
|
|
}
|
|
if (preg_match('/^[a-zA-Z0-9_]+$/', $orders_table)) {
|
|
$wpdb->query("DROP TABLE IF EXISTS `{$orders_table}`");
|
|
}
|
|
if (preg_match('/^[a-zA-Z0-9_]+$/', $webhooks_table)) {
|
|
$wpdb->query("DROP TABLE IF EXISTS `{$webhooks_table}`");
|
|
}
|
|
if (preg_match('/^[a-zA-Z0-9_]+$/', $security_table)) {
|
|
$wpdb->query("DROP TABLE IF EXISTS `{$security_table}`");
|
|
}
|
|
if (preg_match('/^[a-zA-Z0-9_]+$/', $rate_limits_table)) {
|
|
$wpdb->query("DROP TABLE IF EXISTS `{$rate_limits_table}`");
|
|
}
|
|
|
|
// Clear scheduled hooks
|
|
wp_clear_scheduled_hook('ticket_tailor_sync_events');
|
|
wp_clear_scheduled_hook('ticket_tailor_sync_orders');
|
|
wp_clear_scheduled_hook('ticket_tailor_cleanup_security_logs');
|
|
wp_clear_scheduled_hook('ticket_tailor_cleanup_rate_limits');
|
|
|
|
// Remove all posts of custom post type
|
|
$posts = get_posts(array(
|
|
'post_type' => 'tt_event',
|
|
'numberposts' => -1,
|
|
'post_status' => 'any'
|
|
));
|
|
|
|
foreach ($posts as $post) {
|
|
wp_delete_post($post->ID, true);
|
|
}
|
|
|
|
// Remove all terms from custom taxonomies
|
|
$terms = get_terms(array(
|
|
'taxonomy' => array('tt_event_category', 'tt_event_tag'),
|
|
'hide_empty' => false,
|
|
));
|
|
|
|
foreach ($terms as $term) {
|
|
wp_delete_term($term->term_id, $term->taxonomy);
|
|
}
|
|
|
|
// Flush rewrite rules
|
|
flush_rewrite_rules();
|
|
}
|
|
|
|
// Start the plugin - FIXED: Delay initialization until WordPress is fully loaded
|
|
add_action('plugins_loaded', 'ticket_tailor');
|