You've already forked wc-licensed-product
Fix admin order license generation bug
- Add 'Generate Licenses' button to order meta box for admin-created orders - Add AJAX handler for manual license generation - Show warning when domain is not set or order is not paid - Handle partial license generation (when some products already have licenses) - Update German translations for new strings (365 translated) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,9 @@
|
|||||||
// Order domain save
|
// Order domain save
|
||||||
$('#wclp-save-order-domain').on('click', this.saveOrderDomain.bind(this));
|
$('#wclp-save-order-domain').on('click', this.saveOrderDomain.bind(this));
|
||||||
|
|
||||||
|
// Generate licenses button
|
||||||
|
$(document).on('click', '#wclp-generate-licenses', this.generateLicenses.bind(this));
|
||||||
|
|
||||||
// License domain edit/save/cancel
|
// License domain edit/save/cancel
|
||||||
$(document).on('click', '.wclp-edit-domain-btn', this.startEditDomain);
|
$(document).on('click', '.wclp-edit-domain-btn', this.startEditDomain);
|
||||||
$(document).on('click', '.wclp-save-domain-btn', this.saveLicenseDomain.bind(this));
|
$(document).on('click', '.wclp-save-domain-btn', this.saveLicenseDomain.bind(this));
|
||||||
@@ -135,6 +138,54 @@
|
|||||||
$editBtn.show();
|
$editBtn.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate licenses for order
|
||||||
|
*/
|
||||||
|
generateLicenses: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var $btn = $(e.currentTarget);
|
||||||
|
var $spinner = $btn.siblings('.spinner');
|
||||||
|
var $status = $btn.siblings('.wclp-generate-status');
|
||||||
|
|
||||||
|
var orderId = $btn.data('order-id');
|
||||||
|
|
||||||
|
$btn.prop('disabled', true);
|
||||||
|
$spinner.addClass('is-active');
|
||||||
|
$status.text('').removeClass('success error');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: wclpOrderLicenses.ajaxUrl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'wclp_generate_order_licenses',
|
||||||
|
nonce: wclpOrderLicenses.nonce,
|
||||||
|
order_id: orderId
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
$status.text(response.data.message).addClass('success');
|
||||||
|
if (response.data.reload) {
|
||||||
|
// Reload the page after a short delay to show the new licenses
|
||||||
|
setTimeout(function() {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$status.text(response.data.message || wclpOrderLicenses.strings.error).addClass('error');
|
||||||
|
$btn.prop('disabled', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$status.text(wclpOrderLicenses.strings.error).addClass('error');
|
||||||
|
$btn.prop('disabled', false);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
$spinner.removeClass('is-active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save license domain
|
* Save license domain
|
||||||
*/
|
*/
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ final class OrderLicenseController
|
|||||||
// Handle AJAX actions
|
// Handle AJAX actions
|
||||||
add_action('wp_ajax_wclp_update_order_domain', [$this, 'ajaxUpdateOrderDomain']);
|
add_action('wp_ajax_wclp_update_order_domain', [$this, 'ajaxUpdateOrderDomain']);
|
||||||
add_action('wp_ajax_wclp_update_license_domain', [$this, 'ajaxUpdateLicenseDomain']);
|
add_action('wp_ajax_wclp_update_license_domain', [$this, 'ajaxUpdateLicenseDomain']);
|
||||||
|
add_action('wp_ajax_wclp_generate_order_licenses', [$this, 'ajaxGenerateOrderLicenses']);
|
||||||
|
|
||||||
// Enqueue admin scripts
|
// Enqueue admin scripts
|
||||||
add_action('admin_enqueue_scripts', [$this, 'enqueueScripts']);
|
add_action('admin_enqueue_scripts', [$this, 'enqueueScripts']);
|
||||||
@@ -126,6 +127,18 @@ final class OrderLicenseController
|
|||||||
|
|
||||||
<h4><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></h4>
|
<h4><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></h4>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Count licensed products to check if all have licenses
|
||||||
|
$licensedProductCount = 0;
|
||||||
|
foreach ($order->get_items() as $item) {
|
||||||
|
$product = $item->get_product();
|
||||||
|
if ($product && $product->is_type('licensed')) {
|
||||||
|
$licensedProductCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$missingLicenses = $licensedProductCount - count($licenses);
|
||||||
|
?>
|
||||||
|
|
||||||
<?php if (empty($licenses)): ?>
|
<?php if (empty($licenses)): ?>
|
||||||
<p class="description">
|
<p class="description">
|
||||||
<?php esc_html_e('No licenses have been generated for this order yet.', 'wc-licensed-product'); ?>
|
<?php esc_html_e('No licenses have been generated for this order yet.', 'wc-licensed-product'); ?>
|
||||||
@@ -137,6 +150,20 @@ final class OrderLicenseController
|
|||||||
<em><?php esc_html_e('Licenses will be generated when the order is marked as paid/completed.', 'wc-licensed-product'); ?></em>
|
<em><?php esc_html_e('Licenses will be generated when the order is marked as paid/completed.', 'wc-licensed-product'); ?></em>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</p>
|
</p>
|
||||||
|
<?php if ($orderDomain && $order->is_paid()): ?>
|
||||||
|
<p style="margin-top: 10px;">
|
||||||
|
<button type="button" class="button button-primary" id="wclp-generate-licenses" data-order-id="<?php echo esc_attr($order->get_id()); ?>">
|
||||||
|
<?php esc_html_e('Generate Licenses', 'wc-licensed-product'); ?>
|
||||||
|
</button>
|
||||||
|
<span class="spinner" style="float: none; margin-top: 4px;"></span>
|
||||||
|
<span class="wclp-generate-status"></span>
|
||||||
|
</p>
|
||||||
|
<?php elseif (!$orderDomain): ?>
|
||||||
|
<p class="description" style="margin-top: 10px; color: #d63638;">
|
||||||
|
<span class="dashicons dashicons-warning"></span>
|
||||||
|
<?php esc_html_e('Please set the order domain above before generating licenses.', 'wc-licensed-product'); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<table class="widefat striped wclp-licenses-table">
|
<table class="widefat striped wclp-licenses-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -223,6 +250,29 @@ final class OrderLicenseController
|
|||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<?php if ($missingLicenses > 0 && $orderDomain && $order->is_paid()): ?>
|
||||||
|
<p style="margin-top: 10px;">
|
||||||
|
<span class="dashicons dashicons-warning" style="color: #dba617;"></span>
|
||||||
|
<?php
|
||||||
|
printf(
|
||||||
|
/* translators: %d: Number of missing licenses */
|
||||||
|
esc_html(_n(
|
||||||
|
'%d licensed product is missing a license.',
|
||||||
|
'%d licensed products are missing licenses.',
|
||||||
|
$missingLicenses,
|
||||||
|
'wc-licensed-product'
|
||||||
|
)),
|
||||||
|
$missingLicenses
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<button type="button" class="button" id="wclp-generate-licenses" data-order-id="<?php echo esc_attr($order->get_id()); ?>">
|
||||||
|
<?php esc_html_e('Generate Missing Licenses', 'wc-licensed-product'); ?>
|
||||||
|
</button>
|
||||||
|
<span class="spinner" style="float: none; margin-top: 4px;"></span>
|
||||||
|
<span class="wclp-generate-status"></span>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -248,6 +298,9 @@ final class OrderLicenseController
|
|||||||
.wclp-lifetime { color: #0073aa; font-weight: 500; }
|
.wclp-lifetime { color: #0073aa; font-weight: 500; }
|
||||||
.wclp-edit-domain-btn { color: #0073aa; text-decoration: none; }
|
.wclp-edit-domain-btn { color: #0073aa; text-decoration: none; }
|
||||||
.wclp-edit-domain-btn .dashicons { font-size: 16px; width: 16px; height: 16px; }
|
.wclp-edit-domain-btn .dashicons { font-size: 16px; width: 16px; height: 16px; }
|
||||||
|
.wclp-generate-status { font-style: italic; margin-left: 8px; }
|
||||||
|
.wclp-generate-status.success { color: #46b450; }
|
||||||
|
.wclp-generate-status.error { color: #dc3232; }
|
||||||
</style>
|
</style>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
@@ -284,8 +337,9 @@ final class OrderLicenseController
|
|||||||
'strings' => [
|
'strings' => [
|
||||||
'saving' => __('Saving...', 'wc-licensed-product'),
|
'saving' => __('Saving...', 'wc-licensed-product'),
|
||||||
'saved' => __('Saved!', 'wc-licensed-product'),
|
'saved' => __('Saved!', 'wc-licensed-product'),
|
||||||
'error' => __('Error saving. Please try again.', 'wc-licensed-product'),
|
'error' => __('Error. Please try again.', 'wc-licensed-product'),
|
||||||
'invalidDomain' => __('Please enter a valid domain.', 'wc-licensed-product'),
|
'invalidDomain' => __('Please enter a valid domain.', 'wc-licensed-product'),
|
||||||
|
'generating' => __('Generating...', 'wc-licensed-product'),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -392,4 +446,96 @@ final class OrderLicenseController
|
|||||||
$pattern = '/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/';
|
$pattern = '/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/';
|
||||||
return (bool) preg_match($pattern, $domain);
|
return (bool) preg_match($pattern, $domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX handler for generating order licenses
|
||||||
|
*/
|
||||||
|
public function ajaxGenerateOrderLicenses(): void
|
||||||
|
{
|
||||||
|
check_ajax_referer('wclp_order_license_actions', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$orderId = absint($_POST['order_id'] ?? 0);
|
||||||
|
|
||||||
|
if (!$orderId) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid order ID.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$order = wc_get_order($orderId);
|
||||||
|
if (!$order) {
|
||||||
|
wp_send_json_error(['message' => __('Order not found.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if order is paid
|
||||||
|
if (!$order->is_paid()) {
|
||||||
|
wp_send_json_error(['message' => __('Order must be paid before licenses can be generated.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get domain
|
||||||
|
$domain = $order->get_meta('_licensed_product_domain');
|
||||||
|
if (empty($domain)) {
|
||||||
|
wp_send_json_error(['message' => __('Please set the order domain before generating licenses.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate licenses for each licensed product
|
||||||
|
$generated = 0;
|
||||||
|
$skipped = 0;
|
||||||
|
|
||||||
|
foreach ($order->get_items() as $item) {
|
||||||
|
$product = $item->get_product();
|
||||||
|
if ($product && $product->is_type('licensed')) {
|
||||||
|
$license = $this->licenseManager->generateLicense(
|
||||||
|
$orderId,
|
||||||
|
$product->get_id(),
|
||||||
|
$order->get_customer_id(),
|
||||||
|
$domain
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($license) {
|
||||||
|
// Check if this is a new license or existing
|
||||||
|
$existingLicenses = $this->licenseManager->getLicensesByOrder($orderId);
|
||||||
|
$isNew = true;
|
||||||
|
foreach ($existingLicenses as $existing) {
|
||||||
|
if ($existing->getProductId() === $product->get_id() && $existing->getId() !== $license->getId()) {
|
||||||
|
$isNew = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($isNew) {
|
||||||
|
$generated++;
|
||||||
|
} else {
|
||||||
|
$skipped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($generated > 0) {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => sprintf(
|
||||||
|
/* translators: %d: Number of licenses generated */
|
||||||
|
_n(
|
||||||
|
'%d license generated successfully.',
|
||||||
|
'%d licenses generated successfully.',
|
||||||
|
$generated,
|
||||||
|
'wc-licensed-product'
|
||||||
|
),
|
||||||
|
$generated
|
||||||
|
),
|
||||||
|
'generated' => $generated,
|
||||||
|
'skipped' => $skipped,
|
||||||
|
'reload' => true,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('All licenses already exist for this order.', 'wc-licensed-product'),
|
||||||
|
'generated' => 0,
|
||||||
|
'skipped' => $skipped,
|
||||||
|
'reload' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user