You've already forked wc-licensed-product
Add product version management: - ProductVersion model and VersionManager class - VersionAdminController with meta box on product edit page - AJAX-based version CRUD (add, delete, toggle status) - JavaScript for version management UI Add email notifications: - LicenseEmailController for order emails - License keys included in order completed emails - Support for both HTML and plain text emails Add REST API rate limiting: - 30 requests per minute per IP - Cloudflare and proxy-aware IP detection - HTTP 429 response with Retry-After header Other changes: - Bump version to 0.0.2 - Update CHANGELOG.md - Add version status styles to admin.css Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
182 lines
6.5 KiB
JavaScript
182 lines
6.5 KiB
JavaScript
/**
|
|
* WC Licensed Product - Version Management
|
|
*
|
|
* @package Jeremias\WcLicensedProduct
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
var WCLicensedProductVersions = {
|
|
init: function() {
|
|
this.bindEvents();
|
|
},
|
|
|
|
bindEvents: function() {
|
|
$('#add-version-btn').on('click', this.addVersion);
|
|
$(document).on('click', '.delete-version-btn', this.deleteVersion);
|
|
$(document).on('click', '.toggle-version-btn', this.toggleVersion);
|
|
},
|
|
|
|
addVersion: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $btn = $(this);
|
|
var $spinner = $btn.siblings('.spinner');
|
|
var productId = $btn.data('product-id');
|
|
var version = $('#new_version').val().trim();
|
|
var downloadUrl = $('#new_download_url').val().trim();
|
|
var releaseNotes = $('#new_release_notes').val().trim();
|
|
|
|
// Validate version
|
|
if (!version) {
|
|
alert(wcLicensedProductVersions.strings.versionRequired);
|
|
return;
|
|
}
|
|
|
|
if (!/^\d+\.\d+\.\d+$/.test(version)) {
|
|
alert(wcLicensedProductVersions.strings.versionInvalid);
|
|
return;
|
|
}
|
|
|
|
$btn.prop('disabled', true);
|
|
$spinner.addClass('is-active');
|
|
|
|
$.ajax({
|
|
url: wcLicensedProductVersions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wc_licensed_product_add_version',
|
|
nonce: wcLicensedProductVersions.nonce,
|
|
product_id: productId,
|
|
version: version,
|
|
download_url: downloadUrl,
|
|
release_notes: releaseNotes
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
// Remove "no versions" row if present
|
|
$('#versions-table tbody .no-versions').remove();
|
|
|
|
// Add new row to table
|
|
$('#versions-table tbody').prepend(response.data.html);
|
|
|
|
// Clear form
|
|
$('#new_version').val('');
|
|
$('#new_download_url').val('');
|
|
$('#new_release_notes').val('');
|
|
} else {
|
|
alert(response.data.message || wcLicensedProductVersions.strings.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert(wcLicensedProductVersions.strings.error);
|
|
},
|
|
complete: function() {
|
|
$btn.prop('disabled', false);
|
|
$spinner.removeClass('is-active');
|
|
}
|
|
});
|
|
},
|
|
|
|
deleteVersion: function(e) {
|
|
e.preventDefault();
|
|
|
|
if (!confirm(wcLicensedProductVersions.strings.confirmDelete)) {
|
|
return;
|
|
}
|
|
|
|
var $btn = $(this);
|
|
var $row = $btn.closest('tr');
|
|
var versionId = $btn.data('version-id');
|
|
|
|
$btn.prop('disabled', true);
|
|
|
|
$.ajax({
|
|
url: wcLicensedProductVersions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wc_licensed_product_delete_version',
|
|
nonce: wcLicensedProductVersions.nonce,
|
|
version_id: versionId
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
$row.fadeOut(300, function() {
|
|
$(this).remove();
|
|
|
|
// Show "no versions" message if table is empty
|
|
if ($('#versions-table tbody tr').length === 0) {
|
|
$('#versions-table tbody').append(
|
|
'<tr class="no-versions"><td colspan="6">' +
|
|
'No versions found. Add your first version above.' +
|
|
'</td></tr>'
|
|
);
|
|
}
|
|
});
|
|
} else {
|
|
alert(response.data.message || wcLicensedProductVersions.strings.error);
|
|
$btn.prop('disabled', false);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert(wcLicensedProductVersions.strings.error);
|
|
$btn.prop('disabled', false);
|
|
}
|
|
});
|
|
},
|
|
|
|
toggleVersion: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $btn = $(this);
|
|
var $row = $btn.closest('tr');
|
|
var versionId = $btn.data('version-id');
|
|
var currentlyActive = $btn.data('active') === 1 || $btn.data('active') === '1';
|
|
|
|
$btn.prop('disabled', true);
|
|
|
|
$.ajax({
|
|
url: wcLicensedProductVersions.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wc_licensed_product_toggle_version',
|
|
nonce: wcLicensedProductVersions.nonce,
|
|
version_id: versionId,
|
|
currently_active: currentlyActive ? 1 : 0
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
var isActive = response.data.isActive;
|
|
var $status = $row.find('.version-status');
|
|
|
|
// Update status badge
|
|
$status
|
|
.removeClass('version-status-active version-status-inactive')
|
|
.addClass('version-status-' + (isActive ? 'active' : 'inactive'))
|
|
.text(isActive ? 'Active' : 'Inactive');
|
|
|
|
// Update button
|
|
$btn
|
|
.data('active', isActive ? 1 : 0)
|
|
.text(isActive ? 'Deactivate' : 'Activate');
|
|
} else {
|
|
alert(response.data.message || wcLicensedProductVersions.strings.error);
|
|
}
|
|
},
|
|
error: function() {
|
|
alert(wcLicensedProductVersions.strings.error);
|
|
},
|
|
complete: function() {
|
|
$btn.prop('disabled', false);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
$(document).ready(function() {
|
|
WCLicensedProductVersions.init();
|
|
});
|
|
|
|
})(jQuery);
|