🔧 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'));
|
|
}
|