Files
wp-digital-download/includes/class-wpdd-install.php
jknapp a160fe3964 Major improvements: Fix download limits, enhance license display, fix software filenames
🔧 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>
2025-09-09 19:16:57 -07:00

340 lines
13 KiB
PHP

<?php
if (!defined('ABSPATH')) {
exit;
}
class WPDD_Install {
public static function activate() {
// Load required files if not already loaded
if (!class_exists('WPDD_Post_Types')) {
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-post-types.php';
}
if (!class_exists('WPDD_Roles')) {
require_once WPDD_PLUGIN_PATH . 'includes/class-wpdd-roles.php';
}
// Register post types and taxonomies before flushing rules
WPDD_Post_Types::register_post_types();
WPDD_Post_Types::register_taxonomies();
self::create_tables();
self::create_upload_protection();
// Set flag to show setup notice instead of creating pages automatically
if (!get_option('wpdd_setup_completed')) {
add_option('wpdd_show_setup_notice', true);
}
WPDD_Roles::create_roles();
// Flush rewrite rules after post types are registered
flush_rewrite_rules();
}
public static function deactivate() {
flush_rewrite_rules();
}
private static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$sql = array();
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_orders (
id bigint(20) NOT NULL AUTO_INCREMENT,
order_number varchar(50) NOT NULL,
product_id bigint(20) NOT NULL,
customer_id bigint(20) NOT NULL,
creator_id bigint(20) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'pending',
payment_method varchar(50) DEFAULT NULL,
transaction_id varchar(100) DEFAULT NULL,
amount decimal(10,2) NOT NULL,
currency varchar(10) NOT NULL DEFAULT 'USD',
customer_email varchar(100) NOT NULL,
customer_name varchar(100) DEFAULT NULL,
purchase_date datetime DEFAULT CURRENT_TIMESTAMP,
download_count int(11) DEFAULT 0,
notes text DEFAULT NULL,
PRIMARY KEY (id),
KEY order_number (order_number),
KEY product_id (product_id),
KEY customer_id (customer_id),
KEY creator_id (creator_id),
KEY status (status)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_downloads (
id bigint(20) NOT NULL AUTO_INCREMENT,
order_id bigint(20) NOT NULL,
product_id bigint(20) NOT NULL,
customer_id bigint(20) NOT NULL,
file_id varchar(100) NOT NULL,
download_date datetime DEFAULT CURRENT_TIMESTAMP,
ip_address varchar(45) DEFAULT NULL,
user_agent text DEFAULT NULL,
PRIMARY KEY (id),
KEY order_id (order_id),
KEY product_id (product_id),
KEY customer_id (customer_id)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_download_links (
id bigint(20) NOT NULL AUTO_INCREMENT,
order_id bigint(20) NOT NULL,
token varchar(64) NOT NULL,
expires_at datetime NOT NULL,
download_count int(11) DEFAULT 0,
max_downloads int(11) DEFAULT 5,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY token (token),
KEY order_id (order_id)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_creator_earnings (
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),
KEY available_at (available_at)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_payouts (
id bigint(20) NOT NULL AUTO_INCREMENT,
creator_id bigint(20) NOT NULL,
amount decimal(10,2) NOT NULL,
currency varchar(10) NOT NULL,
paypal_email varchar(100) NOT NULL,
transaction_id varchar(100) DEFAULT NULL,
status varchar(20) NOT NULL DEFAULT 'pending',
payout_method varchar(20) NOT NULL DEFAULT 'manual',
notes text DEFAULT NULL,
processed_by bigint(20) DEFAULT NULL,
processed_at datetime DEFAULT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY creator_id (creator_id),
KEY status (status),
KEY transaction_id (transaction_id)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_balance_adjustments (
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;";
// Software Licensing Tables
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_email_logs (
id bigint(20) NOT NULL AUTO_INCREMENT,
to_email varchar(100) NOT NULL,
subject varchar(255) NOT NULL,
message longtext NOT NULL,
status varchar(20) NOT NULL DEFAULT 'sent',
email_type varchar(50) DEFAULT 'general',
error_message text DEFAULT NULL,
sent_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY to_email (to_email),
KEY status (status),
KEY email_type (email_type),
KEY sent_at (sent_at)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_licenses (
id bigint(20) NOT NULL AUTO_INCREMENT,
license_key varchar(64) NOT NULL,
product_id bigint(20) NOT NULL,
order_id bigint(20) NOT NULL,
customer_id bigint(20) NOT NULL,
customer_email varchar(100) NOT NULL,
status varchar(20) NOT NULL DEFAULT 'active',
activations_count int(11) DEFAULT 0,
max_activations int(11) DEFAULT 1,
expires_at datetime DEFAULT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
last_checked datetime DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY license_key (license_key),
KEY product_id (product_id),
KEY order_id (order_id),
KEY customer_id (customer_id),
KEY status (status)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_license_activations (
id bigint(20) NOT NULL AUTO_INCREMENT,
license_id bigint(20) NOT NULL,
license_key varchar(64) NOT NULL,
site_url varchar(255) NOT NULL,
site_name varchar(255) DEFAULT NULL,
activated_at datetime DEFAULT CURRENT_TIMESTAMP,
last_checked datetime DEFAULT NULL,
wp_version varchar(20) DEFAULT NULL,
php_version varchar(20) DEFAULT NULL,
status varchar(20) NOT NULL DEFAULT 'active',
PRIMARY KEY (id),
KEY license_id (license_id),
KEY license_key (license_key),
KEY site_url (site_url),
KEY status (status)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_software_versions (
id bigint(20) NOT NULL AUTO_INCREMENT,
product_id bigint(20) NOT NULL,
version varchar(20) NOT NULL,
changelog text DEFAULT NULL,
release_notes text DEFAULT NULL,
download_url text DEFAULT NULL,
package_url text DEFAULT NULL,
min_wp_version varchar(20) DEFAULT NULL,
tested_wp_version varchar(20) DEFAULT NULL,
min_php_version varchar(20) DEFAULT NULL,
release_date datetime DEFAULT CURRENT_TIMESTAMP,
git_tag varchar(100) DEFAULT NULL,
git_commit varchar(100) DEFAULT NULL,
PRIMARY KEY (id),
KEY product_id (product_id),
KEY version (version),
KEY release_date (release_date)
) $charset_collate;";
$sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wpdd_webhook_events (
id bigint(20) NOT NULL AUTO_INCREMENT,
product_id bigint(20) NOT NULL,
event_type varchar(50) NOT NULL,
payload text DEFAULT NULL,
processed varchar(20) NOT NULL DEFAULT 'pending',
error_message text DEFAULT NULL,
received_at datetime DEFAULT CURRENT_TIMESTAMP,
processed_at datetime DEFAULT NULL,
PRIMARY KEY (id),
KEY product_id (product_id),
KEY processed (processed),
KEY received_at (received_at)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
foreach ($sql as $query) {
dbDelta($query);
}
update_option('wpdd_db_version', WPDD_VERSION);
}
public static function create_pages_optional() {
return self::create_pages();
}
private static function create_pages() {
$pages = array(
'shop' => array(
'title' => __('Shop', 'wp-digital-download'),
'content' => '[wpdd_shop]',
'option' => 'wpdd_shop_page_id'
),
'my-purchases' => array(
'title' => __('My Purchases', 'wp-digital-download'),
'content' => '[wpdd_customer_purchases]',
'option' => 'wpdd_purchases_page_id'
),
'checkout' => array(
'title' => __('Checkout', 'wp-digital-download'),
'content' => '[wpdd_checkout]',
'option' => 'wpdd_checkout_page_id'
),
'thank-you' => array(
'title' => __('Thank You', 'wp-digital-download'),
'content' => '[wpdd_thank_you]',
'option' => 'wpdd_thank_you_page_id'
)
);
$created_pages = array();
foreach ($pages as $slug => $page) {
// Check if page already exists
$existing_page_id = get_option($page['option']);
if ($existing_page_id && get_post($existing_page_id)) {
continue; // Page already exists, skip creation
}
// Check if a page with this slug already exists
$existing_page = get_page_by_path($slug);
if ($existing_page) {
update_option($page['option'], $existing_page->ID);
continue;
}
// Create the page
$page_id = wp_insert_post(array(
'post_title' => $page['title'],
'post_content' => $page['content'],
'post_status' => 'publish',
'post_type' => 'page',
'post_name' => $slug
));
if ($page_id && !is_wp_error($page_id)) {
update_option($page['option'], $page_id);
$created_pages[] = $page_id;
}
}
return $created_pages;
}
private static function create_upload_protection() {
$upload_dir = wp_upload_dir();
$protection_dir = trailingslashit($upload_dir['basedir']) . WPDD_UPLOADS_DIR;
if (!file_exists($protection_dir)) {
wp_mkdir_p($protection_dir);
}
$htaccess_content = "Options -Indexes\n";
$htaccess_content .= "deny from all\n";
$htaccess_file = trailingslashit($protection_dir) . '.htaccess';
if (!file_exists($htaccess_file)) {
file_put_contents($htaccess_file, $htaccess_content);
}
$index_content = "<?php\n// Silence is golden.\n";
$index_file = trailingslashit($protection_dir) . 'index.php';
if (!file_exists($index_file)) {
file_put_contents($index_file, $index_content);
}
}
}