🔧 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>
612 lines
22 KiB
JavaScript
612 lines
22 KiB
JavaScript
jQuery(document).ready(function($) {
|
|
'use strict';
|
|
|
|
/**
|
|
* Main WPDD Frontend Object
|
|
*/
|
|
window.WPDD = {
|
|
|
|
init: function() {
|
|
this.bindEvents();
|
|
this.initProductCards();
|
|
this.initCheckout();
|
|
},
|
|
|
|
bindEvents: function() {
|
|
// Add to cart buttons
|
|
$(document).on('click', '.wpdd-add-to-cart', this.addToCart);
|
|
|
|
// Free download buttons
|
|
$(document).on('click', '.wpdd-free-download', this.processFreeDownload);
|
|
|
|
// Product quickview
|
|
$(document).on('click', '.wpdd-quickview', this.showQuickview);
|
|
|
|
// Filter form submission
|
|
$(document).on('submit', '.wpdd-filter-form', this.handleFilters);
|
|
|
|
// Download status check
|
|
$(document).on('click', '.wpdd-check-download', this.checkDownloadStatus);
|
|
|
|
// Copy license key
|
|
$(document).on('click', '.wpdd-copy-license', this.copyLicenseKey);
|
|
},
|
|
|
|
initProductCards: function() {
|
|
// Add hover effects and animations
|
|
$('.wpdd-product-card').each(function() {
|
|
var $card = $(this);
|
|
var $image = $card.find('.wpdd-product-image img');
|
|
|
|
$card.hover(
|
|
function() {
|
|
$(this).addClass('hovered');
|
|
},
|
|
function() {
|
|
$(this).removeClass('hovered');
|
|
}
|
|
);
|
|
});
|
|
},
|
|
|
|
initCheckout: function() {
|
|
// Free product checkout handler
|
|
$('#wpdd-checkout-form').on('submit', function(e) {
|
|
var $form = $(this);
|
|
var isFree = $form.data('product-free') == '1';
|
|
|
|
if (isFree) {
|
|
e.preventDefault();
|
|
WPDD.processFreeCheckout($form);
|
|
}
|
|
});
|
|
|
|
// Price display toggle based on free checkbox
|
|
$('#wpdd_is_free').on('change', function() {
|
|
var $priceFields = $('.wpdd-price-field');
|
|
if ($(this).is(':checked')) {
|
|
$priceFields.hide();
|
|
} else {
|
|
$priceFields.show();
|
|
}
|
|
});
|
|
},
|
|
|
|
addToCart: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $button = $(this);
|
|
var productId = $button.data('product-id');
|
|
|
|
if (!productId) {
|
|
WPDD.showNotice('Invalid product', 'error');
|
|
return;
|
|
}
|
|
|
|
$button.addClass('loading').prop('disabled', true);
|
|
|
|
$.ajax({
|
|
url: wpdd_ajax.url,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wpdd_add_to_cart',
|
|
product_id: productId,
|
|
nonce: wpdd_ajax.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
WPDD.showNotice(response.data.message, 'success');
|
|
|
|
// Update cart count if element exists
|
|
$('.wpdd-cart-count').text(response.data.cart_count);
|
|
|
|
// Show checkout button
|
|
if (response.data.checkout_url) {
|
|
$button.after('<a href="' + response.data.checkout_url +
|
|
'" class="wpdd-btn wpdd-btn-primary">Checkout</a>');
|
|
}
|
|
} else {
|
|
WPDD.showNotice(response.data, 'error');
|
|
}
|
|
},
|
|
error: function() {
|
|
WPDD.showNotice('An error occurred. Please try again.', 'error');
|
|
},
|
|
complete: function() {
|
|
$button.removeClass('loading').prop('disabled', false);
|
|
}
|
|
});
|
|
},
|
|
|
|
processFreeDownload: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $button = $(this);
|
|
var productId = $button.data('product-id');
|
|
var $form = $button.closest('form');
|
|
|
|
if (!productId) {
|
|
WPDD.showNotice('Invalid product', 'error');
|
|
return;
|
|
}
|
|
|
|
var customerEmail = $form.find('input[name="customer_email"]').val();
|
|
var customerName = $form.find('input[name="customer_name"]').val();
|
|
|
|
if (!customerEmail && !WPDD.isUserLoggedIn()) {
|
|
WPDD.showNotice('Please provide your email address', 'error');
|
|
return;
|
|
}
|
|
|
|
$button.addClass('loading').prop('disabled', true);
|
|
|
|
$.ajax({
|
|
url: wpdd_ajax.url,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wpdd_process_free_download',
|
|
product_id: productId,
|
|
customer_email: customerEmail,
|
|
customer_name: customerName,
|
|
nonce: wpdd_ajax.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
window.location.href = response.data.redirect_url;
|
|
} else {
|
|
WPDD.showNotice(response.data, 'error');
|
|
}
|
|
},
|
|
error: function() {
|
|
WPDD.showNotice('An error occurred. Please try again.', 'error');
|
|
},
|
|
complete: function() {
|
|
$button.removeClass('loading').prop('disabled', false);
|
|
}
|
|
});
|
|
},
|
|
|
|
processFreeCheckout: function($form) {
|
|
var formData = $form.serialize();
|
|
var $submitBtn = $form.find('button[type="submit"]');
|
|
var productId = $form.find('input[name="product_id"]').val();
|
|
|
|
$submitBtn.addClass('loading').prop('disabled', true);
|
|
|
|
// Add visual feedback
|
|
$submitBtn.text('Processing...');
|
|
|
|
$.ajax({
|
|
url: wpdd_ajax.url,
|
|
type: 'POST',
|
|
data: formData + '&action=wpdd_process_free_download&nonce=' + wpdd_ajax.nonce + '&product_id=' + productId,
|
|
success: function(response) {
|
|
if (response.success) {
|
|
window.location.href = response.data.redirect_url;
|
|
} else {
|
|
WPDD.showNotice(response.data || 'Failed to process download', 'error');
|
|
$submitBtn.text('Get Free Download');
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('AJAX Error:', status, error);
|
|
WPDD.showNotice('An error occurred. Please try again.', 'error');
|
|
$submitBtn.text('Get Free Download');
|
|
},
|
|
complete: function() {
|
|
$submitBtn.removeClass('loading').prop('disabled', false);
|
|
}
|
|
});
|
|
},
|
|
|
|
showQuickview: function(e) {
|
|
e.preventDefault();
|
|
|
|
var productId = $(this).data('product-id');
|
|
|
|
if (!productId) {
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: wpdd_ajax.url,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wpdd_get_product_details',
|
|
product_id: productId,
|
|
nonce: wpdd_ajax.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
WPDD.displayQuickview(response.data);
|
|
} else {
|
|
WPDD.showNotice('Unable to load product details', 'error');
|
|
}
|
|
},
|
|
error: function() {
|
|
WPDD.showNotice('An error occurred. Please try again.', 'error');
|
|
}
|
|
});
|
|
},
|
|
|
|
displayQuickview: function(product) {
|
|
var modal = $('<div class="wpdd-modal"><div class="wpdd-modal-content"></div></div>');
|
|
var content = '';
|
|
|
|
content += '<div class="wpdd-modal-header">';
|
|
content += '<h2>' + product.title + '</h2>';
|
|
content += '<button class="wpdd-modal-close">×</button>';
|
|
content += '</div>';
|
|
|
|
content += '<div class="wpdd-modal-body">';
|
|
if (product.thumbnail) {
|
|
content += '<img src="' + product.thumbnail + '" alt="' + product.title + '" class="wpdd-modal-image">';
|
|
}
|
|
content += '<div class="wpdd-modal-details">';
|
|
content += '<p class="wpdd-modal-price">';
|
|
if (product.is_free) {
|
|
content += '<span class="wpdd-price-free">Free</span>';
|
|
} else {
|
|
content += '<span class="wpdd-price-regular">$' + product.final_price + '</span>';
|
|
}
|
|
content += '</p>';
|
|
content += '<p class="wpdd-modal-creator">by ' + product.creator.name + '</p>';
|
|
content += '<div class="wpdd-modal-description">' + product.description + '</div>';
|
|
content += '<p><strong>Files:</strong> ' + product.files_count + '</p>';
|
|
content += '<p><strong>Download Limit:</strong> ' + product.download_limit + '</p>';
|
|
content += '<p><strong>Expires:</strong> ' + product.download_expiry + '</p>';
|
|
content += '</div>';
|
|
content += '</div>';
|
|
|
|
content += '<div class="wpdd-modal-footer">';
|
|
content += '<a href="' + window.location.origin + '?product_id=' + product.id +
|
|
'" class="wpdd-btn wpdd-btn-primary">View Details</a>';
|
|
content += '</div>';
|
|
|
|
modal.find('.wpdd-modal-content').html(content);
|
|
$('body').append(modal);
|
|
|
|
modal.addClass('active');
|
|
|
|
// Close modal events
|
|
modal.on('click', '.wpdd-modal-close, .wpdd-modal', function(e) {
|
|
if (e.target === this) {
|
|
modal.removeClass('active');
|
|
setTimeout(function() {
|
|
modal.remove();
|
|
}, 300);
|
|
}
|
|
});
|
|
},
|
|
|
|
handleFilters: function(e) {
|
|
// Let the form submit naturally for now
|
|
// Could be enhanced with AJAX filtering
|
|
},
|
|
|
|
checkDownloadStatus: function(e) {
|
|
e.preventDefault();
|
|
|
|
var productId = $(this).data('product-id');
|
|
var $button = $(this);
|
|
|
|
if (!WPDD.isUserLoggedIn()) {
|
|
WPDD.showNotice('Please login to check download status', 'error');
|
|
return;
|
|
}
|
|
|
|
$button.addClass('loading');
|
|
|
|
$.ajax({
|
|
url: wpdd_ajax.url,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wpdd_check_download_status',
|
|
product_id: productId,
|
|
nonce: wpdd_ajax.nonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
var data = response.data;
|
|
if (data.can_download && data.download_url) {
|
|
window.location.href = data.download_url;
|
|
} else {
|
|
WPDD.showNotice(data.message, data.can_download ? 'success' : 'error');
|
|
}
|
|
} else {
|
|
WPDD.showNotice(response.data, 'error');
|
|
}
|
|
},
|
|
error: function() {
|
|
WPDD.showNotice('An error occurred. Please try again.', 'error');
|
|
},
|
|
complete: function() {
|
|
$button.removeClass('loading');
|
|
}
|
|
});
|
|
},
|
|
|
|
showNotice: function(message, type) {
|
|
type = type || 'info';
|
|
|
|
var notice = $('<div class="wpdd-notice wpdd-notice-' + type + '">' +
|
|
'<p>' + message + '</p>' +
|
|
'<button class="wpdd-notice-dismiss">×</button>' +
|
|
'</div>');
|
|
|
|
$('body').prepend(notice);
|
|
|
|
notice.addClass('active');
|
|
|
|
// Auto-dismiss after 5 seconds
|
|
setTimeout(function() {
|
|
notice.removeClass('active');
|
|
setTimeout(function() {
|
|
notice.remove();
|
|
}, 300);
|
|
}, 5000);
|
|
|
|
// Manual dismiss
|
|
notice.find('.wpdd-notice-dismiss').on('click', function() {
|
|
notice.removeClass('active');
|
|
setTimeout(function() {
|
|
notice.remove();
|
|
}, 300);
|
|
});
|
|
},
|
|
|
|
isUserLoggedIn: function() {
|
|
return $('body').hasClass('logged-in');
|
|
},
|
|
|
|
formatPrice: function(price) {
|
|
return '$' + parseFloat(price).toFixed(2);
|
|
},
|
|
|
|
copyLicenseKey: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $button = $(this);
|
|
var licenseKey = $button.data('license');
|
|
|
|
if (!licenseKey) {
|
|
WPDD.showNotice('No license key to copy', 'error');
|
|
return;
|
|
}
|
|
|
|
// Try to use the modern clipboard API
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(licenseKey).then(function() {
|
|
WPDD.showNotice('License key copied to clipboard!', 'success');
|
|
$button.text('Copied!');
|
|
setTimeout(function() {
|
|
$button.text('Copy');
|
|
}, 2000);
|
|
}).catch(function() {
|
|
WPDD.fallbackCopyTextToClipboard(licenseKey, $button);
|
|
});
|
|
} else {
|
|
WPDD.fallbackCopyTextToClipboard(licenseKey, $button);
|
|
}
|
|
},
|
|
|
|
fallbackCopyTextToClipboard: function(text, $button) {
|
|
var textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.left = '-999999px';
|
|
textArea.style.top = '-999999px';
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
var successful = document.execCommand('copy');
|
|
if (successful) {
|
|
WPDD.showNotice('License key copied to clipboard!', 'success');
|
|
$button.text('Copied!');
|
|
setTimeout(function() {
|
|
$button.text('Copy');
|
|
}, 2000);
|
|
} else {
|
|
WPDD.showNotice('Failed to copy license key', 'error');
|
|
}
|
|
} catch (err) {
|
|
WPDD.showNotice('Failed to copy license key', 'error');
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
}
|
|
};
|
|
|
|
// Initialize frontend functionality
|
|
WPDD.init();
|
|
|
|
// Add modal and notice styles if not already present
|
|
if (!$('#wpdd-dynamic-styles').length) {
|
|
var styles = `
|
|
<style id="wpdd-dynamic-styles">
|
|
.wpdd-modal {
|
|
display: flex;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0,0,0,0.8);
|
|
z-index: 999999;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: all 0.3s;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.wpdd-modal.active {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
|
|
.wpdd-modal-content {
|
|
background: white;
|
|
max-width: 600px;
|
|
width: 90%;
|
|
max-height: 80vh;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
transform: scale(0.8);
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.wpdd-modal.active .wpdd-modal-content {
|
|
transform: scale(1);
|
|
}
|
|
|
|
.wpdd-modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.wpdd-modal-header h2 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.wpdd-modal-close {
|
|
background: none;
|
|
border: none;
|
|
font-size: 24px;
|
|
cursor: pointer;
|
|
color: #666;
|
|
padding: 0;
|
|
width: 30px;
|
|
height: 30px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.wpdd-modal-body {
|
|
padding: 20px;
|
|
max-height: 50vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.wpdd-modal-image {
|
|
width: 100%;
|
|
max-width: 200px;
|
|
height: auto;
|
|
float: left;
|
|
margin: 0 20px 20px 0;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.wpdd-modal-footer {
|
|
padding: 20px;
|
|
border-top: 1px solid #eee;
|
|
text-align: right;
|
|
}
|
|
|
|
.wpdd-notice {
|
|
position: fixed;
|
|
top: 32px;
|
|
right: 20px;
|
|
max-width: 400px;
|
|
z-index: 999999;
|
|
opacity: 0;
|
|
transform: translateX(100%);
|
|
transition: all 0.3s;
|
|
border-radius: 4px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.wpdd-notice.active {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.wpdd-notice p {
|
|
margin: 0;
|
|
padding: 15px 40px 15px 15px;
|
|
}
|
|
|
|
.wpdd-notice-dismiss {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 10px;
|
|
background: none;
|
|
border: none;
|
|
font-size: 18px;
|
|
cursor: pointer;
|
|
color: inherit;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.wpdd-notice-success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
|
|
.wpdd-notice-error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
|
|
.wpdd-notice-info {
|
|
background: #d1ecf1;
|
|
color: #0c5460;
|
|
border: 1px solid #bee5eb;
|
|
}
|
|
|
|
.loading {
|
|
position: relative;
|
|
pointer-events: none;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.loading::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 16px;
|
|
height: 16px;
|
|
margin: -8px 0 0 -8px;
|
|
border: 2px solid transparent;
|
|
border-top: 2px solid currentColor;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.wpdd-modal-content {
|
|
width: 95%;
|
|
}
|
|
|
|
.wpdd-modal-image {
|
|
float: none;
|
|
margin: 0 0 20px 0;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.wpdd-notice {
|
|
right: 10px;
|
|
left: 10px;
|
|
max-width: none;
|
|
}
|
|
}
|
|
</style>
|
|
`;
|
|
$('head').append(styles);
|
|
}
|
|
}); |