🔧 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>
		
			
				
	
	
		
			508 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
if (!defined('ABSPATH')) {
 | 
						|
    exit;
 | 
						|
}
 | 
						|
 | 
						|
class WPDD_Metaboxes {
 | 
						|
    
 | 
						|
    public static function init() {
 | 
						|
        add_action('add_meta_boxes', array(__CLASS__, 'add_meta_boxes'));
 | 
						|
        add_action('save_post_wpdd_product', array(__CLASS__, 'save_product_meta'), 10, 2);
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function add_meta_boxes() {
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_product_type',
 | 
						|
            __('Product Type', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_product_type_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'side',
 | 
						|
            'high'
 | 
						|
        );
 | 
						|
        
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_product_pricing',
 | 
						|
            __('Product Pricing', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_pricing_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'side',
 | 
						|
            'high'
 | 
						|
        );
 | 
						|
        
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_product_files',
 | 
						|
            __('Downloadable Files', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_files_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'normal',
 | 
						|
            'high'
 | 
						|
        );
 | 
						|
        
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_software_licensing',
 | 
						|
            __('Software Licensing', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_software_licensing_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'normal',
 | 
						|
            'high'
 | 
						|
        );
 | 
						|
        
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_product_settings',
 | 
						|
            __('Product Settings', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_settings_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'normal',
 | 
						|
            'default'
 | 
						|
        );
 | 
						|
        
 | 
						|
        add_meta_box(
 | 
						|
            'wpdd_product_stats',
 | 
						|
            __('Product Statistics', 'wp-digital-download'),
 | 
						|
            array(__CLASS__, 'render_stats_metabox'),
 | 
						|
            'wpdd_product',
 | 
						|
            'side',
 | 
						|
            'low'
 | 
						|
        );
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_pricing_metabox($post) {
 | 
						|
        wp_nonce_field('wpdd_save_product_meta', 'wpdd_product_meta_nonce');
 | 
						|
        
 | 
						|
        $price = get_post_meta($post->ID, '_wpdd_price', true);
 | 
						|
        $is_free = get_post_meta($post->ID, '_wpdd_is_free', true);
 | 
						|
        $sale_price = get_post_meta($post->ID, '_wpdd_sale_price', true);
 | 
						|
        ?>
 | 
						|
        <div class="wpdd-metabox-content">
 | 
						|
            <p>
 | 
						|
                <label>
 | 
						|
                    <input type="checkbox" name="wpdd_is_free" id="wpdd_is_free" value="1" <?php checked($is_free, '1'); ?> />
 | 
						|
                    <?php _e('This is a free product', 'wp-digital-download'); ?>
 | 
						|
                </label>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p class="wpdd-price-field">
 | 
						|
                <label for="wpdd_price"><?php _e('Regular Price', 'wp-digital-download'); ?> ($)</label>
 | 
						|
                <input type="number" id="wpdd_price" name="wpdd_price" value="<?php echo esc_attr($price); ?>" step="0.01" min="0" />
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p class="wpdd-price-field">
 | 
						|
                <label for="wpdd_sale_price"><?php _e('Sale Price', 'wp-digital-download'); ?> ($)</label>
 | 
						|
                <input type="number" id="wpdd_sale_price" name="wpdd_sale_price" value="<?php echo esc_attr($sale_price); ?>" step="0.01" min="0" />
 | 
						|
                <span class="description"><?php _e('Leave blank for no sale', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
        </div>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_product_type_metabox($post) {
 | 
						|
        $product_type = get_post_meta($post->ID, '_wpdd_product_type', true) ?: 'digital_download';
 | 
						|
        ?>
 | 
						|
        <p>
 | 
						|
            <label>
 | 
						|
                <input type="radio" name="wpdd_product_type" value="digital_download" <?php checked($product_type, 'digital_download'); ?> />
 | 
						|
                <?php _e('Digital Download', 'wp-digital-download'); ?>
 | 
						|
            </label>
 | 
						|
            <br>
 | 
						|
            <span class="description"><?php _e('Standard downloadable file (PDF, images, etc.)', 'wp-digital-download'); ?></span>
 | 
						|
        </p>
 | 
						|
        <p>
 | 
						|
            <label>
 | 
						|
                <input type="radio" name="wpdd_product_type" value="software_license" <?php checked($product_type, 'software_license'); ?> />
 | 
						|
                <?php _e('Software License', 'wp-digital-download'); ?>
 | 
						|
            </label>
 | 
						|
            <br>
 | 
						|
            <span class="description"><?php _e('WordPress plugin/theme with license key and automatic updates', 'wp-digital-download'); ?></span>
 | 
						|
        </p>
 | 
						|
        
 | 
						|
        <script type="text/javascript">
 | 
						|
        jQuery(document).ready(function($) {
 | 
						|
            function toggleMetaboxes() {
 | 
						|
                var productType = $('input[name="wpdd_product_type"]:checked').val();
 | 
						|
                if (productType === 'software_license') {
 | 
						|
                    $('#wpdd_software_licensing').show();
 | 
						|
                    $('#wpdd_product_files').hide();
 | 
						|
                } else {
 | 
						|
                    $('#wpdd_software_licensing').hide();
 | 
						|
                    $('#wpdd_product_files').show();
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            $('input[name="wpdd_product_type"]').on('change', toggleMetaboxes);
 | 
						|
            toggleMetaboxes();
 | 
						|
        });
 | 
						|
        </script>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_files_metabox($post) {
 | 
						|
        $files = get_post_meta($post->ID, '_wpdd_files', true);
 | 
						|
        if (!is_array($files)) {
 | 
						|
            $files = array();
 | 
						|
        }
 | 
						|
        ?>
 | 
						|
        <div class="wpdd-files-container">
 | 
						|
            <div id="wpdd-files-list">
 | 
						|
                <?php if (!empty($files)) : ?>
 | 
						|
                    <?php foreach ($files as $index => $file) : ?>
 | 
						|
                        <div class="wpdd-file-item" data-index="<?php echo $index; ?>">
 | 
						|
                            <div class="wpdd-file-header">
 | 
						|
                                <span class="wpdd-file-handle dashicons dashicons-menu"></span>
 | 
						|
                                <input type="text" name="wpdd_files[<?php echo $index; ?>][name]" 
 | 
						|
                                       value="<?php echo esc_attr($file['name']); ?>" 
 | 
						|
                                       placeholder="<?php _e('File Name', 'wp-digital-download'); ?>" />
 | 
						|
                                <button type="button" class="button wpdd-remove-file">
 | 
						|
                                    <?php _e('Remove', 'wp-digital-download'); ?>
 | 
						|
                                </button>
 | 
						|
                            </div>
 | 
						|
                            <div class="wpdd-file-content">
 | 
						|
                                <div class="wpdd-file-url">
 | 
						|
                                    <input type="text" name="wpdd_files[<?php echo $index; ?>][url]" 
 | 
						|
                                           value="<?php echo esc_url($file['url']); ?>" 
 | 
						|
                                           placeholder="<?php _e('File URL', 'wp-digital-download'); ?>" 
 | 
						|
                                           class="wpdd-file-url-input" />
 | 
						|
                                    <button type="button" class="button wpdd-upload-file">
 | 
						|
                                        <?php _e('Upload File', 'wp-digital-download'); ?>
 | 
						|
                                    </button>
 | 
						|
                                </div>
 | 
						|
                                <input type="hidden" name="wpdd_files[<?php echo $index; ?>][id]" 
 | 
						|
                                       value="<?php echo esc_attr($file['id']); ?>" 
 | 
						|
                                       class="wpdd-file-id" />
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    <?php endforeach; ?>
 | 
						|
                <?php endif; ?>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <button type="button" id="wpdd-add-file" class="button button-primary">
 | 
						|
                <?php _e('Add File', 'wp-digital-download'); ?>
 | 
						|
            </button>
 | 
						|
            
 | 
						|
            <template id="wpdd-file-template">
 | 
						|
                <div class="wpdd-file-item" data-index="">
 | 
						|
                    <div class="wpdd-file-header">
 | 
						|
                        <span class="wpdd-file-handle dashicons dashicons-menu"></span>
 | 
						|
                        <input type="text" name="wpdd_files[INDEX][name]" 
 | 
						|
                               placeholder="<?php _e('File Name', 'wp-digital-download'); ?>" />
 | 
						|
                        <button type="button" class="button wpdd-remove-file">
 | 
						|
                            <?php _e('Remove', 'wp-digital-download'); ?>
 | 
						|
                        </button>
 | 
						|
                    </div>
 | 
						|
                    <div class="wpdd-file-content">
 | 
						|
                        <div class="wpdd-file-url">
 | 
						|
                            <input type="text" name="wpdd_files[INDEX][url]" 
 | 
						|
                                   placeholder="<?php _e('File URL', 'wp-digital-download'); ?>" 
 | 
						|
                                   class="wpdd-file-url-input" />
 | 
						|
                            <button type="button" class="button wpdd-upload-file">
 | 
						|
                                <?php _e('Upload File', 'wp-digital-download'); ?>
 | 
						|
                            </button>
 | 
						|
                        </div>
 | 
						|
                        <input type="hidden" name="wpdd_files[INDEX][id]" class="wpdd-file-id" />
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </template>
 | 
						|
        </div>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_software_licensing_metabox($post) {
 | 
						|
        $git_repository = get_post_meta($post->ID, '_wpdd_git_repository', true);
 | 
						|
        $git_username = get_post_meta($post->ID, '_wpdd_git_username', true);
 | 
						|
        $git_token = get_post_meta($post->ID, '_wpdd_git_token', true);
 | 
						|
        $webhook_passcode = get_post_meta($post->ID, '_wpdd_webhook_passcode', true);
 | 
						|
        $max_activations = get_post_meta($post->ID, '_wpdd_max_activations', true) ?: 1;
 | 
						|
        $license_duration = get_post_meta($post->ID, '_wpdd_license_duration', true);
 | 
						|
        $current_version = get_post_meta($post->ID, '_wpdd_current_version', true);
 | 
						|
        $min_wp_version = get_post_meta($post->ID, '_wpdd_min_wp_version', true);
 | 
						|
        $tested_wp_version = get_post_meta($post->ID, '_wpdd_tested_wp_version', true);
 | 
						|
        
 | 
						|
        // Generate webhook passcode if not set
 | 
						|
        if (!$webhook_passcode) {
 | 
						|
            $webhook_passcode = wp_generate_password(32, false);
 | 
						|
            update_post_meta($post->ID, '_wpdd_webhook_passcode', $webhook_passcode);
 | 
						|
        }
 | 
						|
        
 | 
						|
        $webhook_url = home_url("/wp-json/wpdd/v1/webhook/{$post->ID}/{$webhook_passcode}");
 | 
						|
        ?>
 | 
						|
        <div class="wpdd-metabox-content">
 | 
						|
            <h4><?php _e('Repository Settings', 'wp-digital-download'); ?></h4>
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_git_repository"><?php _e('Git Repository URL', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="url" id="wpdd_git_repository" name="wpdd_git_repository" value="<?php echo esc_attr($git_repository); ?>" class="widefat" placeholder="https://github.com/username/repository" />
 | 
						|
                <span class="description"><?php _e('Full URL to your Git repository', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_git_username"><?php _e('Git Username (optional)', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="text" id="wpdd_git_username" name="wpdd_git_username" value="<?php echo esc_attr($git_username); ?>" class="widefat" />
 | 
						|
                <span class="description"><?php _e('For private repositories', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_git_token"><?php _e('Git Access Token (optional)', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="password" id="wpdd_git_token" name="wpdd_git_token" value="<?php echo esc_attr($git_token); ?>" class="widefat" />
 | 
						|
                <span class="description"><?php _e('Personal access token for private repositories', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <h4><?php _e('Webhook Configuration', 'wp-digital-download'); ?></h4>
 | 
						|
            <p>
 | 
						|
                <label><?php _e('Webhook URL', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="text" value="<?php echo esc_attr($webhook_url); ?>" class="widefat" readonly onclick="this.select();" />
 | 
						|
                <span class="description"><?php _e('Add this URL to your repository webhook settings (GitHub, GitLab, etc.) to receive release notifications', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <button type="button" class="button" onclick="wpddRegenerateWebhookPasscode(<?php echo $post->ID; ?>)">
 | 
						|
                    <?php _e('Regenerate Webhook URL', 'wp-digital-download'); ?>
 | 
						|
                </button>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <h4><?php _e('License Settings', 'wp-digital-download'); ?></h4>
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_max_activations"><?php _e('Max Activations per License', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="number" id="wpdd_max_activations" name="wpdd_max_activations" value="<?php echo esc_attr($max_activations); ?>" min="1" />
 | 
						|
                <span class="description"><?php _e('Number of sites where the license can be activated', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_license_duration"><?php _e('License Duration (days)', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="number" id="wpdd_license_duration" name="wpdd_license_duration" value="<?php echo esc_attr($license_duration); ?>" min="0" />
 | 
						|
                <span class="description"><?php _e('Leave blank or 0 for lifetime licenses', 'wp-digital-download'); ?></span>
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <h4><?php _e('Version Information', 'wp-digital-download'); ?></h4>
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_current_version"><?php _e('Current Version', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="text" id="wpdd_current_version" name="wpdd_current_version" value="<?php echo esc_attr($current_version); ?>" placeholder="1.0.0" />
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_min_wp_version"><?php _e('Minimum WordPress Version', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="text" id="wpdd_min_wp_version" name="wpdd_min_wp_version" value="<?php echo esc_attr($min_wp_version); ?>" placeholder="5.0" />
 | 
						|
            </p>
 | 
						|
            
 | 
						|
            <p>
 | 
						|
                <label for="wpdd_tested_wp_version"><?php _e('Tested up to WordPress Version', 'wp-digital-download'); ?></label><br>
 | 
						|
                <input type="text" id="wpdd_tested_wp_version" name="wpdd_tested_wp_version" value="<?php echo esc_attr($tested_wp_version); ?>" placeholder="6.4" />
 | 
						|
            </p>
 | 
						|
        </div>
 | 
						|
        
 | 
						|
        <script type="text/javascript">
 | 
						|
        function wpddRegenerateWebhookPasscode(productId) {
 | 
						|
            if (confirm('<?php _e('Are you sure? This will invalidate the existing webhook URL.', 'wp-digital-download'); ?>')) {
 | 
						|
                var newPasscode = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
 | 
						|
                jQuery('#wpdd_webhook_passcode').val(newPasscode);
 | 
						|
                var newUrl = '<?php echo home_url("/wp-json/wpdd/v1/webhook/"); ?>' + productId + '/' + newPasscode;
 | 
						|
                jQuery('input[readonly]').val(newUrl);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        </script>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_settings_metabox($post) {
 | 
						|
        $download_limit = get_post_meta($post->ID, '_wpdd_download_limit', true);
 | 
						|
        $download_expiry = get_post_meta($post->ID, '_wpdd_download_expiry', true);
 | 
						|
        $enable_watermark = get_post_meta($post->ID, '_wpdd_enable_watermark', true);
 | 
						|
        $watermark_text = get_post_meta($post->ID, '_wpdd_watermark_text', true);
 | 
						|
        ?>
 | 
						|
        <div class="wpdd-settings-grid">
 | 
						|
            <div class="wpdd-setting-group">
 | 
						|
                <h4><?php _e('Download Settings', 'wp-digital-download'); ?></h4>
 | 
						|
                
 | 
						|
                <p>
 | 
						|
                    <label for="wpdd_download_limit">
 | 
						|
                        <?php _e('Download Limit', 'wp-digital-download'); ?>
 | 
						|
                    </label>
 | 
						|
                    <input type="number" id="wpdd_download_limit" name="wpdd_download_limit" 
 | 
						|
                           value="<?php echo esc_attr($download_limit ?: 0); ?>" min="0" />
 | 
						|
                    <span class="description">
 | 
						|
                        <?php _e('Number of times a customer can download after purchase. 0 = unlimited', 'wp-digital-download'); ?>
 | 
						|
                    </span>
 | 
						|
                </p>
 | 
						|
                
 | 
						|
                <p>
 | 
						|
                    <label for="wpdd_download_expiry">
 | 
						|
                        <?php _e('Download Expiry (days)', 'wp-digital-download'); ?>
 | 
						|
                    </label>
 | 
						|
                    <input type="number" id="wpdd_download_expiry" name="wpdd_download_expiry" 
 | 
						|
                           value="<?php echo esc_attr($download_expiry ?: 30); ?>" min="0" />
 | 
						|
                    <span class="description">
 | 
						|
                        <?php _e('Number of days download link remains valid. 0 = never expires', 'wp-digital-download'); ?>
 | 
						|
                    </span>
 | 
						|
                </p>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <div class="wpdd-setting-group">
 | 
						|
                <h4><?php _e('Watermark Settings', 'wp-digital-download'); ?></h4>
 | 
						|
                
 | 
						|
                <p>
 | 
						|
                    <label>
 | 
						|
                        <input type="checkbox" name="wpdd_enable_watermark" value="1" 
 | 
						|
                               <?php checked($enable_watermark, '1'); ?> />
 | 
						|
                        <?php _e('Enable watermarking for images and PDFs', 'wp-digital-download'); ?>
 | 
						|
                    </label>
 | 
						|
                </p>
 | 
						|
                
 | 
						|
                <p>
 | 
						|
                    <label for="wpdd_watermark_text">
 | 
						|
                        <?php _e('Watermark Text', 'wp-digital-download'); ?>
 | 
						|
                    </label>
 | 
						|
                    <input type="text" id="wpdd_watermark_text" name="wpdd_watermark_text" 
 | 
						|
                           value="<?php echo esc_attr($watermark_text); ?>" 
 | 
						|
                           placeholder="<?php _e('e.g., {customer_email} - {order_id}', 'wp-digital-download'); ?>" />
 | 
						|
                    <span class="description">
 | 
						|
                        <?php _e('Available placeholders: {customer_name}, {customer_email}, {order_id}, {date}', 'wp-digital-download'); ?>
 | 
						|
                    </span>
 | 
						|
                </p>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function render_stats_metabox($post) {
 | 
						|
        global $wpdb;
 | 
						|
        
 | 
						|
        $total_sales = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_orders 
 | 
						|
             WHERE product_id = %d AND status = 'completed'",
 | 
						|
            $post->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        $total_revenue = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT SUM(amount) FROM {$wpdb->prefix}wpdd_orders 
 | 
						|
             WHERE product_id = %d AND status = 'completed'",
 | 
						|
            $post->ID
 | 
						|
        ));
 | 
						|
        
 | 
						|
        $total_downloads = $wpdb->get_var($wpdb->prepare(
 | 
						|
            "SELECT COUNT(*) FROM {$wpdb->prefix}wpdd_downloads 
 | 
						|
             WHERE product_id = %d",
 | 
						|
            $post->ID
 | 
						|
        ));
 | 
						|
        ?>
 | 
						|
        <div class="wpdd-stats">
 | 
						|
            <p>
 | 
						|
                <strong><?php _e('Total Sales:', 'wp-digital-download'); ?></strong> 
 | 
						|
                <?php echo intval($total_sales); ?>
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                <strong><?php _e('Total Revenue:', 'wp-digital-download'); ?></strong> 
 | 
						|
                $<?php echo number_format(floatval($total_revenue), 2); ?>
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                <strong><?php _e('Total Downloads:', 'wp-digital-download'); ?></strong> 
 | 
						|
                <?php echo intval($total_downloads); ?>
 | 
						|
            </p>
 | 
						|
        </div>
 | 
						|
        <?php
 | 
						|
    }
 | 
						|
    
 | 
						|
    public static function save_product_meta($post_id, $post) {
 | 
						|
        if (!isset($_POST['wpdd_product_meta_nonce']) || 
 | 
						|
            !wp_verify_nonce($_POST['wpdd_product_meta_nonce'], 'wpdd_save_product_meta')) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (!current_user_can('edit_post', $post_id)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        
 | 
						|
        update_post_meta($post_id, '_wpdd_is_free', 
 | 
						|
            isset($_POST['wpdd_is_free']) ? '1' : '0');
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_price'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_price', 
 | 
						|
                floatval($_POST['wpdd_price']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_sale_price'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_sale_price', 
 | 
						|
                floatval($_POST['wpdd_sale_price']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_files']) && is_array($_POST['wpdd_files'])) {
 | 
						|
            $files = array();
 | 
						|
            foreach ($_POST['wpdd_files'] as $file) {
 | 
						|
                if (!empty($file['url'])) {
 | 
						|
                    $files[] = array(
 | 
						|
                        'id' => sanitize_text_field($file['id']),
 | 
						|
                        'name' => sanitize_text_field($file['name']),
 | 
						|
                        'url' => esc_url_raw($file['url'])
 | 
						|
                    );
 | 
						|
                }
 | 
						|
            }
 | 
						|
            update_post_meta($post_id, '_wpdd_files', $files);
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_download_limit'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_download_limit', 
 | 
						|
                intval($_POST['wpdd_download_limit']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_download_expiry'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_download_expiry', 
 | 
						|
                intval($_POST['wpdd_download_expiry']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        update_post_meta($post_id, '_wpdd_enable_watermark', 
 | 
						|
            isset($_POST['wpdd_enable_watermark']) ? '1' : '0');
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_watermark_text'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_watermark_text', 
 | 
						|
                sanitize_text_field($_POST['wpdd_watermark_text']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Product type
 | 
						|
        if (isset($_POST['wpdd_product_type'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_product_type', 
 | 
						|
                sanitize_text_field($_POST['wpdd_product_type']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        // Software licensing fields
 | 
						|
        if (isset($_POST['wpdd_git_repository'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_git_repository', 
 | 
						|
                esc_url_raw($_POST['wpdd_git_repository']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_git_username'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_git_username', 
 | 
						|
                sanitize_text_field($_POST['wpdd_git_username']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_git_token'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_git_token', 
 | 
						|
                sanitize_text_field($_POST['wpdd_git_token']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_max_activations'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_max_activations', 
 | 
						|
                intval($_POST['wpdd_max_activations']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_license_duration'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_license_duration', 
 | 
						|
                intval($_POST['wpdd_license_duration']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_current_version'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_current_version', 
 | 
						|
                sanitize_text_field($_POST['wpdd_current_version']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_min_wp_version'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_min_wp_version', 
 | 
						|
                sanitize_text_field($_POST['wpdd_min_wp_version']));
 | 
						|
        }
 | 
						|
        
 | 
						|
        if (isset($_POST['wpdd_tested_wp_version'])) {
 | 
						|
            update_post_meta($post_id, '_wpdd_tested_wp_version', 
 | 
						|
                sanitize_text_field($_POST['wpdd_tested_wp_version']));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |