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>
This commit is contained in:
2025-09-09 19:16:57 -07:00
parent ce48f1615f
commit a160fe3964
28 changed files with 3709 additions and 156 deletions

View File

@@ -371,8 +371,22 @@ class WPDD_API {
array('%d', '%d', '%d', '%s', '%s', '%s', '%s')
);
// Serve file
$filename = basename($package_path);
// Serve file with proper filename
$original_filename = basename($package_path);
$original_extension = pathinfo($original_filename, PATHINFO_EXTENSION);
// If no extension, assume it's a zip file
if (empty($original_extension)) {
$original_extension = 'zip';
}
// Create sanitized filename using product name and version
$product_name = $product->post_title;
$version = $latest_version->version;
$safe_name = str_replace([' ', '.'], ['-', '_'], $product_name . ' v' . $version);
$safe_name = sanitize_file_name($safe_name);
$filename = $safe_name . '.' . $original_extension;
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . filesize($package_path));
@@ -632,6 +646,27 @@ class WPDD_API {
// Update product version meta
update_post_meta($product_id, '_wpdd_current_version', $version);
// Update product files to include the new package
$files = get_post_meta($product_id, '_wpdd_files', true);
if (!is_array($files)) {
$files = array();
}
// Add or update the package file in the files list
$package_file = array(
'id' => 'package_' . $version,
'name' => get_the_title($product_id) . ' v' . $version,
'url' => $package_url
);
// Remove any existing package entries and add the new one as the first file
$files = array_filter($files, function($file) {
return !isset($file['id']) || strpos($file['id'], 'package_') !== 0;
});
array_unshift($files, $package_file);
update_post_meta($product_id, '_wpdd_files', $files);
// Notify customers about update (optional)
self::notify_customers_about_update($product_id, $version);
@@ -782,4 +817,128 @@ class WPDD_API {
// Optional: Send email notifications to customers with active licenses
// This could be a separate scheduled job to avoid timeout issues
}
/**
* Manually sync a software product with its latest Git release
* This can be used to fix products that don't have files or need updates
*/
public static function sync_software_product($product_id, $force_rebuild = false) {
global $wpdb;
// Check if product is software license type
$product_type = get_post_meta($product_id, '_wpdd_product_type', true);
if ($product_type !== 'software_license') {
return array(
'success' => false,
'error' => 'not_software_license',
'message' => __('Product is not a software license product.', 'wp-digital-download')
);
}
// Get Git repository settings
$git_url = get_post_meta($product_id, '_wpdd_git_repository', true);
$git_username = get_post_meta($product_id, '_wpdd_git_username', true);
$git_token = get_post_meta($product_id, '_wpdd_git_token', true);
$current_version = get_post_meta($product_id, '_wpdd_current_version', true);
if (!$git_url) {
return array(
'success' => false,
'error' => 'no_git_url',
'message' => __('No Git repository URL configured.', 'wp-digital-download')
);
}
if (!$current_version) {
return array(
'success' => false,
'error' => 'no_version',
'message' => __('No current version specified. Please set a version in the product settings.', 'wp-digital-download')
);
}
// Check if we already have this version
$existing_version = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wpdd_software_versions
WHERE product_id = %d AND version = %s",
$product_id,
$current_version
));
$package_url = null;
if (!$existing_version || $force_rebuild) {
// Build package from current version
$package_url = self::build_package_from_git($product_id, $git_url, 'v' . $current_version, $git_username, $git_token);
if (!$package_url) {
// Try without 'v' prefix
$package_url = self::build_package_from_git($product_id, $git_url, $current_version, $git_username, $git_token);
}
if (!$package_url) {
return array(
'success' => false,
'error' => 'build_failed',
'message' => __('Failed to build package from repository.', 'wp-digital-download')
);
}
// Insert or update version record
if ($existing_version) {
$wpdb->update(
$wpdb->prefix . 'wpdd_software_versions',
array(
'package_url' => $package_url,
'release_date' => current_time('mysql')
),
array('id' => $existing_version->id),
array('%s', '%s'),
array('%d')
);
} else {
$wpdb->insert(
$wpdb->prefix . 'wpdd_software_versions',
array(
'product_id' => $product_id,
'version' => $current_version,
'package_url' => $package_url,
'git_tag' => 'v' . $current_version,
'release_date' => current_time('mysql')
),
array('%d', '%s', '%s', '%s', '%s')
);
}
} else {
$package_url = $existing_version->package_url;
}
// Update product files to include the package
$files = get_post_meta($product_id, '_wpdd_files', true);
if (!is_array($files)) {
$files = array();
}
// Add or update the package file in the files list
$package_file = array(
'id' => 'package_' . $current_version,
'name' => get_the_title($product_id) . ' v' . $current_version,
'url' => $package_url
);
// Remove any existing package entries and add the new one as the first file
$files = array_filter($files, function($file) {
return !isset($file['id']) || strpos($file['id'], 'package_') !== 0;
});
array_unshift($files, $package_file);
update_post_meta($product_id, '_wpdd_files', $files);
return array(
'success' => true,
'message' => __('Product synced successfully.', 'wp-digital-download'),
'version' => $current_version,
'package_url' => $package_url
);
}
}