🔧 Bug Fixes: - Fixed download limits defaulting to 5 instead of 0 for unlimited downloads - Fixed software license filename sanitization (spaces→dashes, dots→underscores, proper .zip extension) - Software downloads now show as "Test-Plugin-v2-2-0.zip" instead of "Test Plugin v2.2.0" ✨ UI/UX Enhancements: - Redesigned license key display to span full table width with FontAwesome copy icons - Added responsive CSS styling for license key rows - Integrated FontAwesome CDN for modern copy icons 🏗️ Architecture Improvements: - Added comprehensive filename sanitization in both download handler and API paths - Enhanced software license product handling for local package files - Improved error handling and logging throughout download processes 📦 Infrastructure: - Added Gitea workflows for automated releases on push to main - Created comprehensive .gitignore excluding test files and browser automation - Updated documentation with all recent improvements and technical insights 🔍 Technical Details: - Software license products served from wp-content/uploads/wpdd-packages/ - Download flow: token → process_download_by_token() → process_download() → deliver_file() - Dual path coverage for both API downloads and regular file delivery - Version placeholder system for automated deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			756 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * Database Check and Update Script for WP Digital Download
 | 
						|
 * This script checks and creates missing database tables
 | 
						|
 */
 | 
						|
 | 
						|
if (!defined('ABSPATH')) {
 | 
						|
    exit;
 | 
						|
}
 | 
						|
 | 
						|
function wpdd_check_and_create_tables() {
 | 
						|
    global $wpdb;
 | 
						|
    
 | 
						|
    $charset_collate = $wpdb->get_charset_collate();
 | 
						|
    $results = array();
 | 
						|
    
 | 
						|
    // Check if creator_earnings table exists and has correct structure
 | 
						|
    $table_name = $wpdb->prefix . 'wpdd_creator_earnings';
 | 
						|
    $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name;
 | 
						|
    
 | 
						|
    if (!$table_exists) {
 | 
						|
        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
 | 
						|
            id bigint(20) NOT NULL AUTO_INCREMENT,
 | 
						|
            creator_id bigint(20) NOT NULL,
 | 
						|
            order_id bigint(20) NOT NULL,
 | 
						|
            product_id bigint(20) NOT NULL,
 | 
						|
            sale_amount decimal(10,2) NOT NULL,
 | 
						|
            commission_rate decimal(5,2) NOT NULL,
 | 
						|
            creator_earning decimal(10,2) NOT NULL,
 | 
						|
            payout_id bigint(20) DEFAULT NULL,
 | 
						|
            payout_status varchar(20) DEFAULT 'pending',
 | 
						|
            available_at datetime DEFAULT NULL,
 | 
						|
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
 | 
						|
            PRIMARY KEY (id),
 | 
						|
            KEY creator_id (creator_id),
 | 
						|
            KEY order_id (order_id),
 | 
						|
            KEY product_id (product_id),
 | 
						|
            KEY payout_id (payout_id),
 | 
						|
            KEY payout_status (payout_status)
 | 
						|
        ) $charset_collate;";
 | 
						|
        
 | 
						|
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
 | 
						|
        dbDelta($sql);
 | 
						|
        $results[] = "Created table: $table_name";
 | 
						|
    } else {
 | 
						|
        // Check if payout_id and payout_status columns exist
 | 
						|
        $columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
 | 
						|
        $has_payout_id = false;
 | 
						|
        $has_payout_status = false;
 | 
						|
        
 | 
						|
        $has_available_at = false;
 | 
						|
        
 | 
						|
        foreach ($columns as $column) {
 | 
						|
            if ($column->Field == 'payout_id') $has_payout_id = true;
 | 
						|
            if ($column->Field == 'payout_status') $has_payout_status = true;
 | 
						|
            if ($column->Field == 'available_at') $has_available_at = true;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (!$has_payout_id) {
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN payout_id bigint(20) DEFAULT NULL");
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD KEY payout_id (payout_id)");
 | 
						|
            $results[] = "Added payout_id column to $table_name";
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (!$has_payout_status) {
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN payout_status varchar(20) DEFAULT 'pending'");
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD KEY payout_status (payout_status)");
 | 
						|
            $results[] = "Added payout_status column to $table_name";
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (!$has_available_at) {
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD COLUMN available_at datetime DEFAULT NULL");
 | 
						|
            $wpdb->query("ALTER TABLE $table_name ADD KEY available_at (available_at)");
 | 
						|
            $results[] = "Added available_at column to $table_name";
 | 
						|
            
 | 
						|
            // Update existing pending earnings with available_at dates
 | 
						|
            $holding_days = intval(get_option('wpdd_earnings_holding_days', 15));
 | 
						|
            $wpdb->query(
 | 
						|
                $wpdb->prepare(
 | 
						|
                    "UPDATE $table_name 
 | 
						|
                     SET available_at = DATE_ADD(created_at, INTERVAL %d DAY)
 | 
						|
                     WHERE payout_status = 'pending' AND available_at IS NULL",
 | 
						|
                    $holding_days
 | 
						|
                )
 | 
						|
            );
 | 
						|
            $results[] = "Updated pending earnings with available_at dates";
 | 
						|
        }
 | 
						|
        
 | 
						|
        $results[] = "Table exists: $table_name";
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Check if balance_adjustments table exists
 | 
						|
    $table_name = $wpdb->prefix . 'wpdd_balance_adjustments';
 | 
						|
    if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
 | 
						|
        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
 | 
						|
            id bigint(20) NOT NULL AUTO_INCREMENT,
 | 
						|
            creator_id bigint(20) NOT NULL,
 | 
						|
            adjustment_type varchar(20) NOT NULL,
 | 
						|
            amount decimal(10,2) NOT NULL,
 | 
						|
            previous_balance decimal(10,2) NOT NULL,
 | 
						|
            new_balance decimal(10,2) NOT NULL,
 | 
						|
            reason text NOT NULL,
 | 
						|
            adjusted_by bigint(20) NOT NULL,
 | 
						|
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
 | 
						|
            PRIMARY KEY (id),
 | 
						|
            KEY creator_id (creator_id),
 | 
						|
            KEY adjusted_by (adjusted_by)
 | 
						|
        ) $charset_collate;";
 | 
						|
        
 | 
						|
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
 | 
						|
        dbDelta($sql);
 | 
						|
        $results[] = "Created table: $table_name";
 | 
						|
    } else {
 | 
						|
        $results[] = "Table exists: $table_name";
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Check existing orders and add missing earnings records
 | 
						|
    $completed_orders = $wpdb->get_results(
 | 
						|
        "SELECT o.*, p.post_author as creator_id 
 | 
						|
         FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
         INNER JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
         WHERE o.status = 'completed'"
 | 
						|
    );
 | 
						|
    
 | 
						|
    $results[] = "Found " . count($completed_orders) . " completed orders";
 | 
						|
    
 | 
						|
    // Process each order to ensure earnings are recorded
 | 
						|
    $added_earnings = 0;
 | 
						|
    foreach ($completed_orders as $order) {
 | 
						|
        // Check if earning already recorded
 | 
						|
        $existing = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT id FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE order_id = %d",
 | 
						|
            $order->id
 | 
						|
        ));
 | 
						|
        
 | 
						|
        if (!$existing && $order->amount > 0) {
 | 
						|
            $commission_rate = floatval(get_option('wpdd_commission_rate', 0));
 | 
						|
            $creator_share = $order->amount * (1 - ($commission_rate / 100));
 | 
						|
            
 | 
						|
            // Insert earning record
 | 
						|
            $wpdb->insert(
 | 
						|
                $wpdb->prefix . 'wpdd_creator_earnings',
 | 
						|
                array(
 | 
						|
                    'creator_id' => $order->creator_id,
 | 
						|
                    'order_id' => $order->id,
 | 
						|
                    'product_id' => $order->product_id,
 | 
						|
                    'sale_amount' => $order->amount,
 | 
						|
                    'commission_rate' => $commission_rate,
 | 
						|
                    'creator_earning' => $creator_share,
 | 
						|
                    'payout_status' => 'pending',
 | 
						|
                    'created_at' => $order->purchase_date
 | 
						|
                ),
 | 
						|
                array('%d', '%d', '%d', '%f', '%f', '%f', '%s', '%s')
 | 
						|
            );
 | 
						|
            
 | 
						|
            $added_earnings++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    if ($added_earnings > 0) {
 | 
						|
        $results[] = "Added $added_earnings missing earning records";
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Recalculate all creator balances based on UNPAID earnings only
 | 
						|
    $creators = get_users(array('role' => 'wpdd_creator'));
 | 
						|
    
 | 
						|
    foreach ($creators as $creator) {
 | 
						|
        // Calculate available earnings only (not pending or already paid)
 | 
						|
        $available_earnings = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(creator_earning) 
 | 
						|
             FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE creator_id = %d 
 | 
						|
             AND payout_status = 'available'",
 | 
						|
            $creator->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        // Calculate balance adjustments
 | 
						|
        $total_adjustments = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(CASE 
 | 
						|
                WHEN adjustment_type = 'add' THEN amount 
 | 
						|
                WHEN adjustment_type = 'subtract' THEN -amount 
 | 
						|
                ELSE 0 
 | 
						|
             END) 
 | 
						|
             FROM {$wpdb->prefix}wpdd_balance_adjustments 
 | 
						|
             WHERE creator_id = %d",
 | 
						|
            $creator->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        $available_earnings = floatval($available_earnings);
 | 
						|
        $total_adjustments = floatval($total_adjustments);
 | 
						|
        
 | 
						|
        $balance = $available_earnings + $total_adjustments;
 | 
						|
        
 | 
						|
        // Update creator balance
 | 
						|
        update_user_meta($creator->ID, 'wpdd_creator_balance', $balance);
 | 
						|
        
 | 
						|
        // Get total earnings for display
 | 
						|
        $total_earnings = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(creator_earning) 
 | 
						|
             FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE creator_id = %d",
 | 
						|
            $creator->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        // Get paid out amount
 | 
						|
        $paid_out = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(creator_earning) 
 | 
						|
             FROM {$wpdb->prefix}wpdd_creator_earnings 
 | 
						|
             WHERE creator_id = %d 
 | 
						|
             AND payout_status = 'paid'",
 | 
						|
            $creator->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        $results[] = sprintf(
 | 
						|
            "Updated balance for %s: Total Earnings: $%.2f, Paid Out: $%.2f, Adjustments: $%.2f, Current Balance: $%.2f",
 | 
						|
            $creator->display_name,
 | 
						|
            floatval($total_earnings),
 | 
						|
            floatval($paid_out),
 | 
						|
            $total_adjustments,
 | 
						|
            $balance
 | 
						|
        );
 | 
						|
    }
 | 
						|
    
 | 
						|
    return $results;
 | 
						|
}
 | 
						|
 | 
						|
// Add admin page to run this check
 | 
						|
add_action('admin_menu', function() {
 | 
						|
    add_submenu_page(
 | 
						|
        'edit.php?post_type=wpdd_product',
 | 
						|
        'Tools',
 | 
						|
        'Tools',
 | 
						|
        'manage_options',
 | 
						|
        'wpdd-tools',
 | 
						|
        function() {
 | 
						|
            $active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'system';
 | 
						|
            ?>
 | 
						|
            <div class="wrap">
 | 
						|
                <h1>WP Digital Download - Tools</h1>
 | 
						|
                
 | 
						|
                <h2 class="nav-tab-wrapper">
 | 
						|
                    <a href="?post_type=wpdd_product&page=wpdd-tools&tab=system" 
 | 
						|
                       class="nav-tab <?php echo $active_tab == 'system' ? 'nav-tab-active' : ''; ?>">System Tools</a>
 | 
						|
                    <a href="?post_type=wpdd_product&page=wpdd-tools&tab=licenses" 
 | 
						|
                       class="nav-tab <?php echo $active_tab == 'licenses' ? 'nav-tab-active' : ''; ?>">License Tools</a>
 | 
						|
                    <a href="?post_type=wpdd_product&page=wpdd-tools&tab=email-test" 
 | 
						|
                       class="nav-tab <?php echo $active_tab == 'email-test' ? 'nav-tab-active' : ''; ?>">Email Test</a>
 | 
						|
                    <a href="?post_type=wpdd_product&page=wpdd-tools&tab=email-logs" 
 | 
						|
                       class="nav-tab <?php echo $active_tab == 'email-logs' ? 'nav-tab-active' : ''; ?>">Email Logs</a>
 | 
						|
                </h2>
 | 
						|
                
 | 
						|
                <?php if ($active_tab == 'system') : ?>
 | 
						|
                <div class="wpdd-tools-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
 | 
						|
                    
 | 
						|
                    <!-- Database Check Tool -->
 | 
						|
                    <div class="wpdd-tool-box" style="background: white; border: 1px solid #ccd0d4; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
 | 
						|
                        <h2 style="margin-top: 0;">🔧 Database Check & Repair</h2>
 | 
						|
                        
 | 
						|
                        <?php
 | 
						|
                        if (isset($_POST['run_check']) && wp_verify_nonce($_POST['_wpnonce'], 'wpdd_db_check')) {
 | 
						|
                            $results = wpdd_check_and_create_tables();
 | 
						|
                            echo '<div class="notice notice-success"><p>Database check completed!</p></div>';
 | 
						|
                            echo '<h3>Results:</h3>';
 | 
						|
                            echo '<ul style="max-height: 200px; overflow-y: auto; background: #f9f9f9; padding: 10px; border: 1px solid #ddd;">';
 | 
						|
                            foreach ($results as $result) {
 | 
						|
                                echo '<li>' . esc_html($result) . '</li>';
 | 
						|
                            }
 | 
						|
                            echo '</ul>';
 | 
						|
                        }
 | 
						|
                        ?>
 | 
						|
                        
 | 
						|
                        <p>This tool will:</p>
 | 
						|
                        <ul style="font-size: 13px;">
 | 
						|
                            <li>Check and create missing database tables</li>
 | 
						|
                            <li>Add payout tracking columns if missing</li>
 | 
						|
                            <li>Add earning records for completed orders</li>
 | 
						|
                            <li>Recalculate creator balances</li>
 | 
						|
                            <li>Update pending earnings with proper dates</li>
 | 
						|
                        </ul>
 | 
						|
                        
 | 
						|
                        <form method="post">
 | 
						|
                            <?php wp_nonce_field('wpdd_db_check'); ?>
 | 
						|
                            <p class="submit" style="margin: 10px 0 0 0;">
 | 
						|
                                <input type="submit" name="run_check" class="button button-primary" value="Run Database Check">
 | 
						|
                            </p>
 | 
						|
                        </form>
 | 
						|
                    </div>
 | 
						|
                    
 | 
						|
                    <!-- System Status Tool -->
 | 
						|
                    <div class="wpdd-tool-box" style="background: white; border: 1px solid #ccd0d4; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
 | 
						|
                        <h2 style="margin-top: 0;">📊 System Status</h2>
 | 
						|
                        <p>Quick overview of your plugin status:</p>
 | 
						|
                        
 | 
						|
                        <?php
 | 
						|
                        // PayPal Status
 | 
						|
                        $paypal_client_id = get_option('wpdd_paypal_client_id');
 | 
						|
                        $paypal_secret = get_option('wpdd_paypal_secret');
 | 
						|
                        $paypal_configured = !empty($paypal_client_id) && !empty($paypal_secret);
 | 
						|
                        
 | 
						|
                        echo '<p><strong>PayPal:</strong> <span style="color: ' . ($paypal_configured ? '#46b450' : '#dc3232') . ';">' . ($paypal_configured ? '✓ Configured' : '✗ Not Configured') . '</span></p>';
 | 
						|
                        
 | 
						|
                        if ($paypal_configured) {
 | 
						|
                            echo '<p style="font-size: 11px; color: #666;"><strong>Client ID:</strong> ' . substr($paypal_client_id, 0, 10) . '...</p>';
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        // PayPal Mode
 | 
						|
                        $paypal_mode = get_option('wpdd_paypal_mode', 'sandbox');
 | 
						|
                        echo '<p><strong>PayPal Mode:</strong> ' . ucfirst(esc_html($paypal_mode)) . '</p>';
 | 
						|
                        
 | 
						|
                        // Currency
 | 
						|
                        $currency = get_option('wpdd_currency', 'USD');
 | 
						|
                        echo '<p><strong>Currency:</strong> ' . esc_html($currency) . '</p>';
 | 
						|
                        
 | 
						|
                        // Commission Rate
 | 
						|
                        $commission = get_option('wpdd_commission_rate', 0);
 | 
						|
                        echo '<p><strong>Platform Commission:</strong> ' . floatval($commission) . '%</p>';
 | 
						|
                        
 | 
						|
                        // Holding Period
 | 
						|
                        $holding = get_option('wpdd_earnings_holding_days', 15);
 | 
						|
                        echo '<p><strong>Earnings Hold:</strong> ' . intval($holding) . ' days</p>';
 | 
						|
                        
 | 
						|
                        // Product Count
 | 
						|
                        $products = wp_count_posts('wpdd_product');
 | 
						|
                        echo '<p><strong>Products:</strong> ' . intval($products->publish) . ' published</p>';
 | 
						|
                        
 | 
						|
                        // Creator Count
 | 
						|
                        $creators = count(get_users(array('role' => 'wpdd_creator')));
 | 
						|
                        echo '<p><strong>Creators:</strong> ' . $creators . '</p>';
 | 
						|
                        ?>
 | 
						|
                    </div>
 | 
						|
                    
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <!-- Settings Debug Section -->
 | 
						|
                <div style="margin-top: 20px; padding: 15px; background: #f9f9f9; border: 1px solid #ddd;">
 | 
						|
                    <h3>🔍 Settings Debug Info</h3>
 | 
						|
                    <p style="font-size: 12px;">If settings are resetting, check these values:</p>
 | 
						|
                    <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; font-family: monospace; font-size: 11px;">
 | 
						|
                        <?php
 | 
						|
                        $debug_settings = array(
 | 
						|
                            'wpdd_paypal_mode',
 | 
						|
                            'wpdd_paypal_client_id', 
 | 
						|
                            'wpdd_currency',
 | 
						|
                            'wpdd_commission_rate',
 | 
						|
                            'wpdd_payout_threshold',
 | 
						|
                            'wpdd_earnings_holding_days'
 | 
						|
                        );
 | 
						|
                        
 | 
						|
                        foreach ($debug_settings as $setting) {
 | 
						|
                            $value = get_option($setting);
 | 
						|
                            $display_value = $setting === 'wpdd_paypal_client_id' && !empty($value) ? substr($value, 0, 8) . '...' : $value;
 | 
						|
                            echo '<div><strong>' . esc_html($setting) . ':</strong><br>' . esc_html($display_value ?: '(empty)') . '</div>';
 | 
						|
                        }
 | 
						|
                        ?>
 | 
						|
                    </div>
 | 
						|
                    
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <?php elseif ($active_tab == 'licenses') : ?>
 | 
						|
                <!-- License Tools Tab -->
 | 
						|
                <div style="background: white; padding: 20px; margin-top: 20px; border: 1px solid #ccd0d4;">
 | 
						|
                    <h2>🔑 License Tools</h2>
 | 
						|
                    <p>Manage and regenerate license keys for completed orders.</p>
 | 
						|
                    
 | 
						|
                    <?php
 | 
						|
                    global $wpdb;
 | 
						|
                    
 | 
						|
                    // Load license manager if not already loaded
 | 
						|
                    if (!class_exists('WPDD_License_Manager')) {
 | 
						|
                        require_once(WPDD_PLUGIN_PATH . 'includes/class-wpdd-license-manager.php');
 | 
						|
                    }
 | 
						|
                    
 | 
						|
                    // Handle license generation
 | 
						|
                    if (isset($_POST['generate_license']) && isset($_POST['order_id']) && wp_verify_nonce($_POST['_wpnonce'], 'wpdd_generate_license')) {
 | 
						|
                        $order_id = intval($_POST['order_id']);
 | 
						|
                        $result = WPDD_License_Manager::generate_license_for_order($order_id);
 | 
						|
                        
 | 
						|
                        if ($result) {
 | 
						|
                            echo '<div class="notice notice-success"><p>✅ License generated successfully for order #' . $order_id . ': <code>' . esc_html($result) . '</code></p></div>';
 | 
						|
                        } else {
 | 
						|
                            echo '<div class="notice notice-warning"><p>⚠️ License generation completed for order #' . $order_id . '. Check error logs if no license was created.</p></div>';
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    
 | 
						|
                    // Handle bulk license generation
 | 
						|
                    if (isset($_POST['generate_all_missing']) && wp_verify_nonce($_POST['_wpnonce'], 'wpdd_generate_all_licenses')) {
 | 
						|
                        $orders_without_licenses = $wpdb->get_results("
 | 
						|
                            SELECT o.id
 | 
						|
                            FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
                            LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
                            LEFT JOIN {$wpdb->prefix}wpdd_licenses l ON o.id = l.order_id
 | 
						|
                            WHERE o.status = 'completed' 
 | 
						|
                            AND l.id IS NULL
 | 
						|
                            AND p.post_type = 'wpdd_product'
 | 
						|
                        ");
 | 
						|
                        
 | 
						|
                        $generated = 0;
 | 
						|
                        foreach ($orders_without_licenses as $order) {
 | 
						|
                            $result = WPDD_License_Manager::generate_license_for_order($order->id);
 | 
						|
                            if ($result) {
 | 
						|
                                $generated++;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        echo '<div class="notice notice-success"><p>✅ Generated ' . $generated . ' license keys out of ' . count($orders_without_licenses) . ' eligible orders.</p></div>';
 | 
						|
                    }
 | 
						|
                    
 | 
						|
                    // Get orders without license keys for software products
 | 
						|
                    $orders_without_licenses = $wpdb->get_results("
 | 
						|
                        SELECT o.*, p.post_title as product_name,
 | 
						|
                               pm_type.meta_value as product_type,
 | 
						|
                               pm_git.meta_value as git_repository
 | 
						|
                        FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
                        LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
                        LEFT JOIN {$wpdb->prefix}wpdd_licenses l ON o.id = l.order_id
 | 
						|
                        LEFT JOIN {$wpdb->postmeta} pm_type ON (p.ID = pm_type.post_id AND pm_type.meta_key = '_wpdd_product_type')
 | 
						|
                        LEFT JOIN {$wpdb->postmeta} pm_git ON (p.ID = pm_git.post_id AND pm_git.meta_key = '_wpdd_git_repository')
 | 
						|
                        WHERE o.status = 'completed' 
 | 
						|
                        AND l.id IS NULL
 | 
						|
                        AND p.post_type = 'wpdd_product'
 | 
						|
                        AND (pm_type.meta_value = 'software_license' OR pm_git.meta_value IS NOT NULL)
 | 
						|
                        ORDER BY o.id DESC
 | 
						|
                    ");
 | 
						|
                    ?>
 | 
						|
                    
 | 
						|
                    <?php if (!empty($orders_without_licenses)): ?>
 | 
						|
                        <div style="margin-bottom: 20px;">
 | 
						|
                            <form method="post" style="display: inline;">
 | 
						|
                                <?php wp_nonce_field('wpdd_generate_all_licenses'); ?>
 | 
						|
                                <input type="submit" name="generate_all_missing" class="button button-secondary" 
 | 
						|
                                       value="Generate All Missing Licenses (<?php echo count($orders_without_licenses); ?> orders)"
 | 
						|
                                       onclick="return confirm('Generate license keys for all <?php echo count($orders_without_licenses); ?> orders? This action cannot be undone.');">
 | 
						|
                            </form>
 | 
						|
                        </div>
 | 
						|
                        
 | 
						|
                        <h3>Orders Missing License Keys</h3>
 | 
						|
                        <table class="wp-list-table widefat fixed striped">
 | 
						|
                            <thead>
 | 
						|
                                <tr>
 | 
						|
                                    <th style="width: 80px;">Order ID</th>
 | 
						|
                                    <th style="width: 140px;">Order Number</th>
 | 
						|
                                    <th>Product</th>
 | 
						|
                                    <th style="width: 100px;">Product Type</th>
 | 
						|
                                    <th style="width: 200px;">Customer Email</th>
 | 
						|
                                    <th style="width: 100px;">Date</th>
 | 
						|
                                    <th style="width: 100px;">Action</th>
 | 
						|
                                </tr>
 | 
						|
                            </thead>
 | 
						|
                            <tbody>
 | 
						|
                                <?php foreach ($orders_without_licenses as $order): ?>
 | 
						|
                                <tr>
 | 
						|
                                    <td><?php echo $order->id; ?></td>
 | 
						|
                                    <td><?php echo esc_html($order->order_number); ?></td>
 | 
						|
                                    <td>
 | 
						|
                                        <strong><?php echo esc_html($order->product_name); ?></strong>
 | 
						|
                                        <?php if (!empty($order->git_repository)): ?>
 | 
						|
                                            <br><small style="color: #666;">Git: <?php echo esc_html($order->git_repository); ?></small>
 | 
						|
                                        <?php endif; ?>
 | 
						|
                                    </td>
 | 
						|
                                    <td><?php echo esc_html($order->product_type ?: 'digital_download'); ?></td>
 | 
						|
                                    <td><?php echo esc_html($order->customer_email); ?></td>
 | 
						|
                                    <td><?php echo date('Y-m-d', strtotime($order->purchase_date)); ?></td>
 | 
						|
                                    <td>
 | 
						|
                                        <form method="post" style="display: inline;">
 | 
						|
                                            <?php wp_nonce_field('wpdd_generate_license'); ?>
 | 
						|
                                            <input type="hidden" name="order_id" value="<?php echo $order->id; ?>">
 | 
						|
                                            <input type="submit" name="generate_license" class="button button-primary button-small" 
 | 
						|
                                                   value="Generate" 
 | 
						|
                                                   onclick="return confirm('Generate license for order #<?php echo $order->id; ?>?');">
 | 
						|
                                        </form>
 | 
						|
                                    </td>
 | 
						|
                                </tr>
 | 
						|
                                <?php endforeach; ?>
 | 
						|
                            </tbody>
 | 
						|
                        </table>
 | 
						|
                    <?php else: ?>
 | 
						|
                        <div class="notice notice-info">
 | 
						|
                            <p>✅ All eligible software orders have license keys assigned.</p>
 | 
						|
                        </div>
 | 
						|
                    <?php endif; ?>
 | 
						|
                    
 | 
						|
                    <hr style="margin: 30px 0;">
 | 
						|
                    
 | 
						|
                    <h3>🔍 License Statistics</h3>
 | 
						|
                    <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px;">
 | 
						|
                        <?php
 | 
						|
                        // Get license statistics
 | 
						|
                        $total_licenses = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_licenses");
 | 
						|
                        $active_licenses = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_licenses WHERE status = 'active'");
 | 
						|
                        $expired_licenses = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_licenses WHERE expires_at IS NOT NULL AND expires_at < NOW()");
 | 
						|
                        
 | 
						|
                        $software_orders = $wpdb->get_var("
 | 
						|
                            SELECT COUNT(*)
 | 
						|
                            FROM {$wpdb->prefix}wpdd_orders o
 | 
						|
                            LEFT JOIN {$wpdb->posts} p ON o.product_id = p.ID
 | 
						|
                            LEFT JOIN {$wpdb->postmeta} pm_type ON (p.ID = pm_type.post_id AND pm_type.meta_key = '_wpdd_product_type')
 | 
						|
                            LEFT JOIN {$wpdb->postmeta} pm_git ON (p.ID = pm_git.post_id AND pm_git.meta_key = '_wpdd_git_repository')
 | 
						|
                            WHERE o.status = 'completed' 
 | 
						|
                            AND p.post_type = 'wpdd_product'
 | 
						|
                            AND (pm_type.meta_value = 'software_license' OR pm_git.meta_value IS NOT NULL)
 | 
						|
                        ");
 | 
						|
                        
 | 
						|
                        $missing_licenses = count($orders_without_licenses);
 | 
						|
                        ?>
 | 
						|
                        
 | 
						|
                        <div style="background: #f9f9f9; padding: 15px; border: 1px solid #ddd; text-align: center;">
 | 
						|
                            <h4 style="margin: 0 0 10px 0;">Total Licenses</h4>
 | 
						|
                            <div style="font-size: 24px; font-weight: bold; color: #0073aa;"><?php echo $total_licenses; ?></div>
 | 
						|
                        </div>
 | 
						|
                        
 | 
						|
                        <div style="background: #f9f9f9; padding: 15px; border: 1px solid #ddd; text-align: center;">
 | 
						|
                            <h4 style="margin: 0 0 10px 0;">Active Licenses</h4>
 | 
						|
                            <div style="font-size: 24px; font-weight: bold; color: #46b450;"><?php echo $active_licenses; ?></div>
 | 
						|
                        </div>
 | 
						|
                        
 | 
						|
                        <div style="background: #f9f9f9; padding: 15px; border: 1px solid #ddd; text-align: center;">
 | 
						|
                            <h4 style="margin: 0 0 10px 0;">Missing Licenses</h4>
 | 
						|
                            <div style="font-size: 24px; font-weight: bold; color: <?php echo $missing_licenses > 0 ? '#dc3232' : '#46b450'; ?>;"><?php echo $missing_licenses; ?></div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                    
 | 
						|
                    <?php if ($expired_licenses > 0): ?>
 | 
						|
                    <div style="margin-top: 15px; padding: 10px; background: #fff3cd; border: 1px solid #ffeaa7; border-left: 4px solid #f39c12;">
 | 
						|
                        <strong>⚠️ Notice:</strong> <?php echo $expired_licenses; ?> license(s) have expired.
 | 
						|
                    </div>
 | 
						|
                    <?php endif; ?>
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <?php elseif ($active_tab == 'email-test') : ?>
 | 
						|
                <!-- Email Testing Tab -->
 | 
						|
                <div style="background: white; padding: 20px; margin-top: 20px; border: 1px solid #ccd0d4;">
 | 
						|
                    <h2>📧 Email Test</h2>
 | 
						|
                    <p>Test your email configuration by sending a test email.</p>
 | 
						|
                    
 | 
						|
                    <?php
 | 
						|
                    // Handle email test submission
 | 
						|
                    if (isset($_POST['send_test_email']) && wp_verify_nonce($_POST['_wpnonce'], 'wpdd_test_email')) {
 | 
						|
                        $to_email = sanitize_email($_POST['test_email']);
 | 
						|
                        $subject = sanitize_text_field($_POST['test_subject']);
 | 
						|
                        $message = sanitize_textarea_field($_POST['test_message']);
 | 
						|
                        
 | 
						|
                        if (!empty($to_email) && is_email($to_email)) {
 | 
						|
                            // Get SMTP settings
 | 
						|
                            $smtp_enabled = get_option('wpdd_smtp_enabled');
 | 
						|
                            $from_email = get_option('wpdd_from_email', get_option('admin_email'));
 | 
						|
                            $from_name = get_option('wpdd_from_name', get_bloginfo('name'));
 | 
						|
                            
 | 
						|
                            // Set headers
 | 
						|
                            $headers = array(
 | 
						|
                                'From: ' . $from_name . ' <' . $from_email . '>',
 | 
						|
                                'Content-Type: text/html; charset=UTF-8'
 | 
						|
                            );
 | 
						|
                            
 | 
						|
                            // Configure SMTP if enabled
 | 
						|
                            if ($smtp_enabled) {
 | 
						|
                                add_action('phpmailer_init', 'wpdd_configure_smtp');
 | 
						|
                            }
 | 
						|
                            
 | 
						|
                            // Send email
 | 
						|
                            $sent = wp_mail($to_email, $subject, nl2br($message), $headers);
 | 
						|
                            
 | 
						|
                            if ($sent) {
 | 
						|
                                echo '<div class="notice notice-success"><p>✅ Test email sent successfully to ' . esc_html($to_email) . '</p></div>';
 | 
						|
                            } else {
 | 
						|
                                global $phpmailer;
 | 
						|
                                $error_info = '';
 | 
						|
                                if (isset($phpmailer) && is_object($phpmailer) && !empty($phpmailer->ErrorInfo)) {
 | 
						|
                                    $error_info = $phpmailer->ErrorInfo;
 | 
						|
                                }
 | 
						|
                                echo '<div class="notice notice-error"><p>❌ Failed to send test email. ' . esc_html($error_info) . '</p></div>';
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            echo '<div class="notice notice-error"><p>Please enter a valid email address.</p></div>';
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    ?>
 | 
						|
                    
 | 
						|
                    <form method="post" style="max-width: 600px;">
 | 
						|
                        <?php wp_nonce_field('wpdd_test_email'); ?>
 | 
						|
                        
 | 
						|
                        <table class="form-table">
 | 
						|
                            <tr>
 | 
						|
                                <th scope="row"><label for="test_email">To Email</label></th>
 | 
						|
                                <td>
 | 
						|
                                    <input type="email" id="test_email" name="test_email" class="regular-text" 
 | 
						|
                                           value="<?php echo esc_attr(get_option('admin_email')); ?>" required />
 | 
						|
                                    <p class="description">Email address to send the test email to.</p>
 | 
						|
                                </td>
 | 
						|
                            </tr>
 | 
						|
                            <tr>
 | 
						|
                                <th scope="row"><label for="test_subject">Subject</label></th>
 | 
						|
                                <td>
 | 
						|
                                    <input type="text" id="test_subject" name="test_subject" class="regular-text" 
 | 
						|
                                           value="Test Email from <?php echo esc_attr(get_bloginfo('name')); ?>" required />
 | 
						|
                                </td>
 | 
						|
                            </tr>
 | 
						|
                            <tr>
 | 
						|
                                <th scope="row"><label for="test_message">Message</label></th>
 | 
						|
                                <td>
 | 
						|
                                    <textarea id="test_message" name="test_message" rows="5" cols="50" class="large-text">This is a test email from WP Digital Download plugin.
 | 
						|
 | 
						|
If you received this email, your email configuration is working correctly!
 | 
						|
 | 
						|
Site: <?php echo esc_html(get_bloginfo('name')); ?>
 | 
						|
URL: <?php echo esc_html(get_bloginfo('url')); ?>
 | 
						|
Time: <?php echo current_time('mysql'); ?></textarea>
 | 
						|
                                </td>
 | 
						|
                            </tr>
 | 
						|
                        </table>
 | 
						|
                        
 | 
						|
                        <p class="submit">
 | 
						|
                            <input type="submit" name="send_test_email" class="button button-primary" value="Send Test Email" />
 | 
						|
                        </p>
 | 
						|
                    </form>
 | 
						|
                    
 | 
						|
                    <hr style="margin: 30px 0;">
 | 
						|
                    
 | 
						|
                    <h3>📋 Current Email Configuration</h3>
 | 
						|
                    <table class="widefat" style="max-width: 600px;">
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>SMTP Enabled:</strong></td>
 | 
						|
                            <td><?php echo get_option('wpdd_smtp_enabled') ? '✅ Yes' : '❌ No (using default mail)'; ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>From Email:</strong></td>
 | 
						|
                            <td><?php echo esc_html(get_option('wpdd_from_email', get_option('admin_email'))); ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>From Name:</strong></td>
 | 
						|
                            <td><?php echo esc_html(get_option('wpdd_from_name', get_bloginfo('name'))); ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <?php if (get_option('wpdd_smtp_enabled')) : ?>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>SMTP Host:</strong></td>
 | 
						|
                            <td><?php echo esc_html(get_option('wpdd_smtp_host')); ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>SMTP Port:</strong></td>
 | 
						|
                            <td><?php echo esc_html(get_option('wpdd_smtp_port')); ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>SMTP Encryption:</strong></td>
 | 
						|
                            <td><?php echo esc_html(get_option('wpdd_smtp_encryption')); ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <tr>
 | 
						|
                            <td><strong>SMTP Username:</strong></td>
 | 
						|
                            <td><?php echo get_option('wpdd_smtp_username') ? '✅ Set' : '❌ Not set'; ?></td>
 | 
						|
                        </tr>
 | 
						|
                        <?php endif; ?>
 | 
						|
                    </table>
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <?php elseif ($active_tab == 'email-logs') : ?>
 | 
						|
                <!-- Email Logs Tab -->
 | 
						|
                <div style="background: white; padding: 20px; margin-top: 20px; border: 1px solid #ccd0d4;">
 | 
						|
                    <h2>📨 Email Logs</h2>
 | 
						|
                    <p>View the last 100 emails sent by the plugin.</p>
 | 
						|
                    
 | 
						|
                    <?php
 | 
						|
                    global $wpdb;
 | 
						|
                    $table_name = $wpdb->prefix . 'wpdd_email_logs';
 | 
						|
                    
 | 
						|
                    // Check if table exists
 | 
						|
                    if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
 | 
						|
                        echo '<div class="notice notice-warning"><p>Email logs table not found. It will be created when the first email is sent.</p></div>';
 | 
						|
                    } else {
 | 
						|
                        // Get email logs
 | 
						|
                        $logs = $wpdb->get_results(
 | 
						|
                            "SELECT * FROM $table_name ORDER BY sent_at DESC LIMIT 100"
 | 
						|
                        );
 | 
						|
                        
 | 
						|
                        if (empty($logs)) {
 | 
						|
                            echo '<p>No email logs found.</p>';
 | 
						|
                        } else {
 | 
						|
                            ?>
 | 
						|
                            <table class="wp-list-table widefat fixed striped">
 | 
						|
                                <thead>
 | 
						|
                                    <tr>
 | 
						|
                                        <th style="width: 150px;">Date/Time</th>
 | 
						|
                                        <th style="width: 200px;">To</th>
 | 
						|
                                        <th>Subject</th>
 | 
						|
                                        <th style="width: 100px;">Status</th>
 | 
						|
                                        <th style="width: 100px;">Type</th>
 | 
						|
                                        <th style="width: 80px;">Actions</th>
 | 
						|
                                    </tr>
 | 
						|
                                </thead>
 | 
						|
                                <tbody>
 | 
						|
                                    <?php foreach ($logs as $log) : ?>
 | 
						|
                                    <tr>
 | 
						|
                                        <td><?php echo esc_html($log->sent_at); ?></td>
 | 
						|
                                        <td><?php echo esc_html($log->to_email); ?></td>
 | 
						|
                                        <td><?php echo esc_html($log->subject); ?></td>
 | 
						|
                                        <td>
 | 
						|
                                            <?php if ($log->status == 'sent') : ?>
 | 
						|
                                                <span style="color: #46b450;">✅ Sent</span>
 | 
						|
                                            <?php else : ?>
 | 
						|
                                                <span style="color: #dc3232;">❌ Failed</span>
 | 
						|
                                            <?php endif; ?>
 | 
						|
                                        </td>
 | 
						|
                                        <td><?php echo esc_html($log->email_type); ?></td>
 | 
						|
                                        <td>
 | 
						|
                                            <button class="button button-small view-email-details" 
 | 
						|
                                                    data-id="<?php echo $log->id; ?>">View</button>
 | 
						|
                                        </td>
 | 
						|
                                    </tr>
 | 
						|
                                    <tr id="email-details-<?php echo $log->id; ?>" style="display: none;">
 | 
						|
                                        <td colspan="6" style="background: #f9f9f9; padding: 10px;">
 | 
						|
                                            <strong>Message:</strong><br>
 | 
						|
                                            <div style="border: 1px solid #ddd; padding: 10px; background: white; margin-top: 5px;">
 | 
						|
                                                <?php echo wp_kses_post($log->message); ?>
 | 
						|
                                            </div>
 | 
						|
                                            <?php if (!empty($log->error_message)) : ?>
 | 
						|
                                            <br><strong>Error:</strong> <?php echo esc_html($log->error_message); ?>
 | 
						|
                                            <?php endif; ?>
 | 
						|
                                        </td>
 | 
						|
                                    </tr>
 | 
						|
                                    <?php endforeach; ?>
 | 
						|
                                </tbody>
 | 
						|
                            </table>
 | 
						|
                            
 | 
						|
                            <script>
 | 
						|
                            jQuery(document).ready(function($) {
 | 
						|
                                $('.view-email-details').on('click', function() {
 | 
						|
                                    var id = $(this).data('id');
 | 
						|
                                    $('#email-details-' + id).toggle();
 | 
						|
                                    $(this).text($(this).text() == 'View' ? 'Hide' : 'View');
 | 
						|
                                });
 | 
						|
                            });
 | 
						|
                            </script>
 | 
						|
                            <?php
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    ?>
 | 
						|
                </div>
 | 
						|
                
 | 
						|
                <?php endif; ?>
 | 
						|
                
 | 
						|
            </div>
 | 
						|
            <?php
 | 
						|
        }
 | 
						|
    );
 | 
						|
});
 | 
						|
 | 
						|
// SMTP configuration function
 | 
						|
function wpdd_configure_smtp($phpmailer) {
 | 
						|
    $phpmailer->isSMTP();
 | 
						|
    $phpmailer->Host = get_option('wpdd_smtp_host');
 | 
						|
    $phpmailer->Port = get_option('wpdd_smtp_port', 587);
 | 
						|
    $phpmailer->SMTPAuth = true;
 | 
						|
    $phpmailer->Username = get_option('wpdd_smtp_username');
 | 
						|
    $phpmailer->Password = get_option('wpdd_smtp_password');
 | 
						|
    $phpmailer->SMTPSecure = get_option('wpdd_smtp_encryption', 'tls');
 | 
						|
    $phpmailer->From = get_option('wpdd_from_email', get_option('admin_email'));
 | 
						|
    $phpmailer->FromName = get_option('wpdd_from_name', get_bloginfo('name'));
 | 
						|
}
 |