You've already forked wc-licensed-product
Add inline editing for licenses and copy license key button
- Add inline editing for status, expiry date, and domain fields - Add copy-to-clipboard button for license keys - Add AJAX handlers for inline editing with nonce verification - Update LicenseManager with updateLicenseExpiry method - Add new translations for inline editing strings (de_CH) - Compile updated German translations to .mo file Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -579,3 +579,144 @@
|
|||||||
#license-search-input {
|
#license-search-input {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline Editing Styles */
|
||||||
|
.wclp-editable-cell {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-editable-cell .wclp-display-value {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-editable-cell .wclp-edit-btn {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
color: #2271b1;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-editable-cell:hover .wclp-edit-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-editable-cell .wclp-edit-btn .dashicons {
|
||||||
|
font-size: 14px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-editable-cell .wclp-edit-btn:hover {
|
||||||
|
color: #135e96;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form .wclp-edit-input {
|
||||||
|
max-width: 150px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form .wclp-edit-input[type="date"] {
|
||||||
|
max-width: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form select.wclp-edit-input {
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form .button-small {
|
||||||
|
height: 26px;
|
||||||
|
line-height: 24px;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-edit-form .wclp-lifetime-btn {
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline notice animation */
|
||||||
|
.wclp-inline-notice {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make editable cells have a minimum height for consistency */
|
||||||
|
.licenses-table .wclp-editable-cell {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Domain column wider for edit form */
|
||||||
|
.licenses-table td[data-field="domain"] {
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status column */
|
||||||
|
.licenses-table td[data-field="status"] {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expiry column */
|
||||||
|
.licenses-table td[data-field="expiry"] {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy License Key Button */
|
||||||
|
.wclp-copy-btn {
|
||||||
|
color: #2271b1;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-copy-btn:hover {
|
||||||
|
color: #135e96;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-copy-btn .dashicons {
|
||||||
|
font-size: 16px;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-copy-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: #1d2327;
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 100;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-copy-tooltip::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 5px solid transparent;
|
||||||
|
border-top-color: #1d2327;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wclp-license-key {
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|||||||
@@ -234,4 +234,266 @@
|
|||||||
// Initialize when document is ready
|
// Initialize when document is ready
|
||||||
$(document).ready(init);
|
$(document).ready(init);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Inline Editing Functionality
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize inline editing
|
||||||
|
*/
|
||||||
|
function initInlineEditing() {
|
||||||
|
// Edit button click
|
||||||
|
$(document).on('click', '.wclp-edit-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $cell = $(this).closest('.wclp-editable-cell');
|
||||||
|
showEditForm($cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel button click
|
||||||
|
$(document).on('click', '.wclp-cancel-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $cell = $(this).closest('.wclp-editable-cell');
|
||||||
|
hideEditForm($cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save button click
|
||||||
|
$(document).on('click', '.wclp-save-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $cell = $(this).closest('.wclp-editable-cell');
|
||||||
|
saveEdit($cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lifetime button click (for expiry field)
|
||||||
|
$(document).on('click', '.wclp-lifetime-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $cell = $(this).closest('.wclp-editable-cell');
|
||||||
|
$cell.find('.wclp-edit-input').val('');
|
||||||
|
saveEdit($cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enter key saves, Escape cancels
|
||||||
|
$(document).on('keydown', '.wclp-edit-input', function(e) {
|
||||||
|
var $cell = $(this).closest('.wclp-editable-cell');
|
||||||
|
if (e.keyCode === 13) { // Enter
|
||||||
|
e.preventDefault();
|
||||||
|
saveEdit($cell);
|
||||||
|
} else if (e.keyCode === 27) { // Escape
|
||||||
|
e.preventDefault();
|
||||||
|
hideEditForm($cell);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show edit form for a cell
|
||||||
|
*/
|
||||||
|
function showEditForm($cell) {
|
||||||
|
$cell.find('.wclp-display-value, .wclp-edit-btn').hide();
|
||||||
|
$cell.find('.wclp-edit-form').show();
|
||||||
|
$cell.find('.wclp-edit-input').focus().select();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide edit form for a cell
|
||||||
|
*/
|
||||||
|
function hideEditForm($cell) {
|
||||||
|
$cell.find('.wclp-edit-form').hide();
|
||||||
|
$cell.find('.wclp-display-value, .wclp-edit-btn').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save edit via AJAX
|
||||||
|
*/
|
||||||
|
function saveEdit($cell) {
|
||||||
|
var field = $cell.data('field');
|
||||||
|
var licenseId = $cell.data('license-id');
|
||||||
|
var $input = $cell.find('.wclp-edit-input');
|
||||||
|
var value = $input.val();
|
||||||
|
var $saveBtn = $cell.find('.wclp-save-btn');
|
||||||
|
var originalText = $saveBtn.text();
|
||||||
|
|
||||||
|
// Determine action based on field
|
||||||
|
var action;
|
||||||
|
var data = {
|
||||||
|
action: '',
|
||||||
|
nonce: wclpAdmin.editNonce,
|
||||||
|
license_id: licenseId
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'status':
|
||||||
|
data.action = 'wclp_update_license_status';
|
||||||
|
data.status = value;
|
||||||
|
break;
|
||||||
|
case 'expiry':
|
||||||
|
data.action = 'wclp_update_license_expiry';
|
||||||
|
data.expiry_date = value;
|
||||||
|
break;
|
||||||
|
case 'domain':
|
||||||
|
data.action = 'wclp_update_license_domain';
|
||||||
|
data.domain = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show saving state
|
||||||
|
$saveBtn.text(wclpAdmin.strings.saving).prop('disabled', true);
|
||||||
|
$cell.find('.wclp-cancel-btn, .wclp-lifetime-btn').prop('disabled', true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: wclpAdmin.ajaxUrl,
|
||||||
|
type: 'POST',
|
||||||
|
data: data,
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Update display value
|
||||||
|
updateDisplayValue($cell, field, response.data);
|
||||||
|
hideEditForm($cell);
|
||||||
|
showNotice('success', response.data.message);
|
||||||
|
} else {
|
||||||
|
showNotice('error', response.data.message || wclpAdmin.strings.saveFailed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
showNotice('error', wclpAdmin.strings.saveFailed);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
$saveBtn.text(originalText).prop('disabled', false);
|
||||||
|
$cell.find('.wclp-cancel-btn, .wclp-lifetime-btn').prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the display value after successful save
|
||||||
|
*/
|
||||||
|
function updateDisplayValue($cell, field, data) {
|
||||||
|
var $display = $cell.find('.wclp-display-value');
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case 'status':
|
||||||
|
var statusHtml = '<span class="license-status license-status-' + escapeHtml(data.status) + '">' +
|
||||||
|
escapeHtml(data.status_label) + '</span>';
|
||||||
|
$display.html(statusHtml);
|
||||||
|
break;
|
||||||
|
case 'expiry':
|
||||||
|
if (data.expiry_date) {
|
||||||
|
$display.html(escapeHtml(data.expiry_display));
|
||||||
|
} else {
|
||||||
|
$display.html('<span class="license-lifetime">' + wclpAdmin.strings.lifetime + '</span>');
|
||||||
|
}
|
||||||
|
// Update the input value
|
||||||
|
$cell.find('.wclp-edit-input').val(data.expiry_date || '');
|
||||||
|
break;
|
||||||
|
case 'domain':
|
||||||
|
$display.text(data.domain);
|
||||||
|
// Update the input value
|
||||||
|
$cell.find('.wclp-edit-input').val(data.domain);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a temporary notice
|
||||||
|
*/
|
||||||
|
function showNotice(type, message) {
|
||||||
|
var $notice = $('<div class="notice notice-' + type + ' is-dismissible wclp-inline-notice"><p>' +
|
||||||
|
escapeHtml(message) + '</p></div>');
|
||||||
|
|
||||||
|
// Insert at the top of the wrap
|
||||||
|
$('.wrap h1').first().after($notice);
|
||||||
|
|
||||||
|
// Auto-dismiss after 3 seconds
|
||||||
|
setTimeout(function() {
|
||||||
|
$notice.fadeOut(300, function() {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize inline editing when document is ready
|
||||||
|
$(document).ready(initInlineEditing);
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// Copy License Key Functionality
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize copy license key buttons
|
||||||
|
*/
|
||||||
|
function initCopyButtons() {
|
||||||
|
$(document).on('click', '.wclp-copy-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $btn = $(this);
|
||||||
|
var licenseKey = $btn.data('license-key');
|
||||||
|
|
||||||
|
copyToClipboard(licenseKey).then(function() {
|
||||||
|
showCopyFeedback($btn, true);
|
||||||
|
}).catch(function() {
|
||||||
|
showCopyFeedback($btn, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy text to clipboard
|
||||||
|
*/
|
||||||
|
function copyToClipboard(text) {
|
||||||
|
// Modern Clipboard API
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
return navigator.clipboard.writeText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for older browsers
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var textarea = document.createElement('textarea');
|
||||||
|
textarea.value = text;
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.opacity = '0';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var successful = document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
if (successful) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show copy feedback
|
||||||
|
*/
|
||||||
|
function showCopyFeedback($btn, success) {
|
||||||
|
var $icon = $btn.find('.dashicons');
|
||||||
|
var originalClass = 'dashicons-clipboard';
|
||||||
|
var feedbackClass = success ? 'dashicons-yes' : 'dashicons-no';
|
||||||
|
var feedbackColor = success ? '#00a32a' : '#d63638';
|
||||||
|
|
||||||
|
// Change icon temporarily
|
||||||
|
$icon.removeClass(originalClass).addClass(feedbackClass).css('color', feedbackColor);
|
||||||
|
|
||||||
|
// Show tooltip
|
||||||
|
var message = success ? wclpAdmin.strings.copied : wclpAdmin.strings.copyFailed;
|
||||||
|
var $tooltip = $('<span class="wclp-copy-tooltip">' + escapeHtml(message) + '</span>');
|
||||||
|
$btn.append($tooltip);
|
||||||
|
|
||||||
|
// Reset after delay
|
||||||
|
setTimeout(function() {
|
||||||
|
$icon.removeClass(feedbackClass).addClass(originalClass).css('color', '');
|
||||||
|
$tooltip.remove();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize copy buttons when document is ready
|
||||||
|
$(document).ready(initCopyButtons);
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|||||||
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
# This file is distributed under the GPL-2.0-or-later.
|
# This file is distributed under the GPL-2.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WC Licensed Product 0.0.8\n"
|
"Project-Id-Version: WC Licensed Product 0.0.10\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
||||||
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
"PO-Revision-Date: 2026-01-21T00:00:00+00:00\n"
|
"PO-Revision-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
@@ -865,3 +865,77 @@ msgstr "E-Mail-Typ"
|
|||||||
|
|
||||||
msgid "Choose which format of email to send."
|
msgid "Choose which format of email to send."
|
||||||
msgstr "Wählen Sie, welches E-Mail-Format gesendet werden soll."
|
msgstr "Wählen Sie, welches E-Mail-Format gesendet werden soll."
|
||||||
|
|
||||||
|
#. Admin - Inline Editing
|
||||||
|
msgid "Searching..."
|
||||||
|
msgstr "Suche..."
|
||||||
|
|
||||||
|
msgid "No results found"
|
||||||
|
msgstr "Keine Ergebnisse gefunden"
|
||||||
|
|
||||||
|
msgid "Saving..."
|
||||||
|
msgstr "Speichere..."
|
||||||
|
|
||||||
|
msgid "Saved"
|
||||||
|
msgstr "Gespeichert"
|
||||||
|
|
||||||
|
msgid "Save failed"
|
||||||
|
msgstr "Speichern fehlgeschlagen"
|
||||||
|
|
||||||
|
msgid "Are you sure you want to revoke this license? This action cannot be undone."
|
||||||
|
msgstr "Sind Sie sicher, dass Sie diese Lizenz widerrufen möchten? Diese Aktion kann nicht rückgängig gemacht werden."
|
||||||
|
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Bearbeiten"
|
||||||
|
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "Speichern"
|
||||||
|
|
||||||
|
msgid "Status updated successfully."
|
||||||
|
msgstr "Status erfolgreich aktualisiert."
|
||||||
|
|
||||||
|
msgid "Expiry date updated successfully."
|
||||||
|
msgstr "Ablaufdatum erfolgreich aktualisiert."
|
||||||
|
|
||||||
|
msgid "License set to lifetime."
|
||||||
|
msgstr "Lizenz auf lebenslang gesetzt."
|
||||||
|
|
||||||
|
msgid "Domain updated successfully."
|
||||||
|
msgstr "Domain erfolgreich aktualisiert."
|
||||||
|
|
||||||
|
msgid "Invalid license ID."
|
||||||
|
msgstr "Ungültige Lizenz-ID."
|
||||||
|
|
||||||
|
msgid "Invalid status."
|
||||||
|
msgstr "Ungültiger Status."
|
||||||
|
|
||||||
|
msgid "Invalid date format."
|
||||||
|
msgstr "Ungültiges Datumsformat."
|
||||||
|
|
||||||
|
msgid "Domain cannot be empty."
|
||||||
|
msgstr "Domain darf nicht leer sein."
|
||||||
|
|
||||||
|
msgid "Failed to update status."
|
||||||
|
msgstr "Status konnte nicht aktualisiert werden."
|
||||||
|
|
||||||
|
msgid "Failed to update expiry date."
|
||||||
|
msgstr "Ablaufdatum konnte nicht aktualisiert werden."
|
||||||
|
|
||||||
|
msgid "Failed to update domain."
|
||||||
|
msgstr "Domain konnte nicht aktualisiert werden."
|
||||||
|
|
||||||
|
msgid "Failed to revoke license."
|
||||||
|
msgstr "Lizenz konnte nicht widerrufen werden."
|
||||||
|
|
||||||
|
msgid "Leave empty for lifetime"
|
||||||
|
msgstr "Leer lassen für lebenslang"
|
||||||
|
|
||||||
|
#. Admin - Order License Management
|
||||||
|
msgid "Saved!"
|
||||||
|
msgstr "Gespeichert!"
|
||||||
|
|
||||||
|
msgid "Error saving. Please try again."
|
||||||
|
msgstr "Fehler beim Speichern. Bitte versuchen Sie es erneut."
|
||||||
|
|
||||||
|
msgid "Failed to update license domain."
|
||||||
|
msgstr "Lizenz-Domain konnte nicht aktualisiert werden."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# This file is distributed under the GPL-2.0-or-later.
|
# This file is distributed under the GPL-2.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WC Licensed Product 0.0.8\n"
|
"Project-Id-Version: WC Licensed Product 0.0.10\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
||||||
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@@ -865,3 +865,77 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Admin - Inline Editing
|
||||||
|
msgid "Searching..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No results found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Saving..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Saved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Save failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Are you sure you want to revoke this license? This action cannot be undone."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Save"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Status updated successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Expiry date updated successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "License set to lifetime."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Domain updated successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Invalid license ID."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Invalid status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Invalid date format."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Domain cannot be empty."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to update status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to update expiry date."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to update domain."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to revoke license."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Leave empty for lifetime"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Admin - Order License Management
|
||||||
|
msgid "Saved!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Error saving. Please try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to update license domain."
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ final class AdminController
|
|||||||
|
|
||||||
// AJAX handler for live search
|
// AJAX handler for live search
|
||||||
add_action('wp_ajax_wclp_live_search', [$this, 'handleLiveSearch']);
|
add_action('wp_ajax_wclp_live_search', [$this, 'handleLiveSearch']);
|
||||||
|
|
||||||
|
// AJAX handlers for inline editing
|
||||||
|
add_action('wp_ajax_wclp_update_license_status', [$this, 'handleAjaxStatusUpdate']);
|
||||||
|
add_action('wp_ajax_wclp_update_license_expiry', [$this, 'handleAjaxExpiryUpdate']);
|
||||||
|
add_action('wp_ajax_wclp_update_license_domain', [$this, 'handleAjaxDomainUpdate']);
|
||||||
|
add_action('wp_ajax_wclp_revoke_license', [$this, 'handleAjaxRevoke']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,10 +131,27 @@ final class AdminController
|
|||||||
wp_localize_script('wc-licensed-product-admin', 'wclpAdmin', [
|
wp_localize_script('wc-licensed-product-admin', 'wclpAdmin', [
|
||||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||||
'nonce' => wp_create_nonce('wclp_live_search'),
|
'nonce' => wp_create_nonce('wclp_live_search'),
|
||||||
|
'editNonce' => wp_create_nonce('wclp_inline_edit'),
|
||||||
'strings' => [
|
'strings' => [
|
||||||
'noResults' => __('No licenses found', 'wc-licensed-product'),
|
'noResults' => __('No licenses found', 'wc-licensed-product'),
|
||||||
'searching' => __('Searching...', 'wc-licensed-product'),
|
'searching' => __('Searching...', 'wc-licensed-product'),
|
||||||
'error' => __('Search failed', 'wc-licensed-product'),
|
'error' => __('Search failed', 'wc-licensed-product'),
|
||||||
|
'saving' => __('Saving...', 'wc-licensed-product'),
|
||||||
|
'saved' => __('Saved', 'wc-licensed-product'),
|
||||||
|
'saveFailed' => __('Save failed', 'wc-licensed-product'),
|
||||||
|
'confirmRevoke' => __('Are you sure you want to revoke this license? This action cannot be undone.', 'wc-licensed-product'),
|
||||||
|
'edit' => __('Edit', 'wc-licensed-product'),
|
||||||
|
'cancel' => __('Cancel', 'wc-licensed-product'),
|
||||||
|
'save' => __('Save', 'wc-licensed-product'),
|
||||||
|
'lifetime' => __('Lifetime', 'wc-licensed-product'),
|
||||||
|
'copied' => __('Copied!', 'wc-licensed-product'),
|
||||||
|
'copyFailed' => __('Copy failed', 'wc-licensed-product'),
|
||||||
|
],
|
||||||
|
'statuses' => [
|
||||||
|
['value' => 'active', 'label' => __('Active', 'wc-licensed-product')],
|
||||||
|
['value' => 'inactive', 'label' => __('Inactive', 'wc-licensed-product')],
|
||||||
|
['value' => 'expired', 'label' => __('Expired', 'wc-licensed-product')],
|
||||||
|
['value' => 'revoked', 'label' => __('Revoked', 'wc-licensed-product')],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -174,6 +197,162 @@ final class AdminController
|
|||||||
wp_send_json_success(['results' => $results]);
|
wp_send_json_success(['results' => $results]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle AJAX status update
|
||||||
|
*/
|
||||||
|
public function handleAjaxStatusUpdate(): void
|
||||||
|
{
|
||||||
|
check_ajax_referer('wclp_inline_edit', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$licenseId = isset($_POST['license_id']) ? absint($_POST['license_id']) : 0;
|
||||||
|
$status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : '';
|
||||||
|
|
||||||
|
if (!$licenseId) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid license ID.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$validStatuses = [License::STATUS_ACTIVE, License::STATUS_INACTIVE, License::STATUS_EXPIRED, License::STATUS_REVOKED];
|
||||||
|
if (!in_array($status, $validStatuses, true)) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid status.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = $this->licenseManager->updateLicenseStatus($licenseId, $status);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('Status updated successfully.', 'wc-licensed-product'),
|
||||||
|
'status' => $status,
|
||||||
|
'status_label' => ucfirst($status),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(['message' => __('Failed to update status.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle AJAX expiry date update
|
||||||
|
*/
|
||||||
|
public function handleAjaxExpiryUpdate(): void
|
||||||
|
{
|
||||||
|
check_ajax_referer('wclp_inline_edit', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$licenseId = isset($_POST['license_id']) ? absint($_POST['license_id']) : 0;
|
||||||
|
$expiryDate = isset($_POST['expiry_date']) ? sanitize_text_field($_POST['expiry_date']) : '';
|
||||||
|
|
||||||
|
if (!$licenseId) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid license ID.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle "lifetime" option
|
||||||
|
if (empty($expiryDate) || strtolower($expiryDate) === 'lifetime') {
|
||||||
|
$success = $this->licenseManager->setLicenseLifetime($licenseId);
|
||||||
|
if ($success) {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('License set to lifetime.', 'wc-licensed-product'),
|
||||||
|
'expiry_date' => '',
|
||||||
|
'expiry_display' => __('Lifetime', 'wc-licensed-product'),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(['message' => __('Failed to update expiry date.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate date format
|
||||||
|
try {
|
||||||
|
$date = new \DateTimeImmutable($expiryDate);
|
||||||
|
$success = $this->licenseManager->updateLicenseExpiry($licenseId, $date);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('Expiry date updated successfully.', 'wc-licensed-product'),
|
||||||
|
'expiry_date' => $date->format('Y-m-d'),
|
||||||
|
'expiry_display' => $date->format(get_option('date_format')),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(['message' => __('Failed to update expiry date.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid date format.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle AJAX domain update
|
||||||
|
*/
|
||||||
|
public function handleAjaxDomainUpdate(): void
|
||||||
|
{
|
||||||
|
check_ajax_referer('wclp_inline_edit', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$licenseId = isset($_POST['license_id']) ? absint($_POST['license_id']) : 0;
|
||||||
|
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
|
||||||
|
|
||||||
|
if (!$licenseId) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid license ID.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
wp_send_json_error(['message' => __('Domain cannot be empty.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = $this->licenseManager->transferLicense($licenseId, $domain);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
// Get the normalized domain from the license
|
||||||
|
$license = $this->licenseManager->getLicenseById($licenseId);
|
||||||
|
$normalizedDomain = $license ? $license->getDomain() : $domain;
|
||||||
|
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('Domain updated successfully.', 'wc-licensed-product'),
|
||||||
|
'domain' => $normalizedDomain,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(['message' => __('Failed to update domain.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle AJAX revoke
|
||||||
|
*/
|
||||||
|
public function handleAjaxRevoke(): void
|
||||||
|
{
|
||||||
|
check_ajax_referer('wclp_inline_edit', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_woocommerce')) {
|
||||||
|
wp_send_json_error(['message' => __('Permission denied.', 'wc-licensed-product')], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$licenseId = isset($_POST['license_id']) ? absint($_POST['license_id']) : 0;
|
||||||
|
|
||||||
|
if (!$licenseId) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid license ID.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = $this->licenseManager->updateLicenseStatus($licenseId, License::STATUS_REVOKED);
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
wp_send_json_success([
|
||||||
|
'message' => __('License revoked successfully.', 'wc-licensed-product'),
|
||||||
|
'status' => License::STATUS_REVOKED,
|
||||||
|
'status_label' => ucfirst(License::STATUS_REVOKED),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
wp_send_json_error(['message' => __('Failed to revoke license.', 'wc-licensed-product')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle admin actions (update, delete licenses)
|
* Handle admin actions (update, delete licenses)
|
||||||
*/
|
*/
|
||||||
@@ -1089,7 +1268,12 @@ final class AdminController
|
|||||||
<th scope="row" class="check-column">
|
<th scope="row" class="check-column">
|
||||||
<input type="checkbox" name="license_ids[]" value="<?php echo esc_attr($item['license']->getId()); ?>">
|
<input type="checkbox" name="license_ids[]" value="<?php echo esc_attr($item['license']->getId()); ?>">
|
||||||
</th>
|
</th>
|
||||||
<td><code><?php echo esc_html($item['license']->getLicenseKey()); ?></code></td>
|
<td>
|
||||||
|
<code class="wclp-license-key"><?php echo esc_html($item['license']->getLicenseKey()); ?></code>
|
||||||
|
<button type="button" class="wclp-copy-btn button-link" data-license-key="<?php echo esc_attr($item['license']->getLicenseKey()); ?>" title="<?php esc_attr_e('Copy to clipboard', 'wc-licensed-product'); ?>">
|
||||||
|
<span class="dashicons dashicons-clipboard"></span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ($item['product_edit_url']): ?>
|
<?php if ($item['product_edit_url']): ?>
|
||||||
<a href="<?php echo esc_url($item['product_edit_url']); ?>">
|
<a href="<?php echo esc_url($item['product_edit_url']); ?>">
|
||||||
@@ -1105,21 +1289,55 @@ final class AdminController
|
|||||||
<br><small><?php echo esc_html($item['customer_email']); ?></small>
|
<br><small><?php echo esc_html($item['customer_email']); ?></small>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?php echo esc_html($item['license']->getDomain()); ?></td>
|
<td class="wclp-editable-cell" data-field="domain" data-license-id="<?php echo esc_attr($item['license']->getId()); ?>">
|
||||||
<td>
|
<span class="wclp-display-value"><?php echo esc_html($item['license']->getDomain()); ?></span>
|
||||||
<span class="license-status license-status-<?php echo esc_attr($item['license']->getStatus()); ?>">
|
<button type="button" class="wclp-edit-btn button-link" title="<?php esc_attr_e('Edit', 'wc-licensed-product'); ?>">
|
||||||
<?php echo esc_html(ucfirst($item['license']->getStatus())); ?>
|
<span class="dashicons dashicons-edit"></span>
|
||||||
</span>
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<input type="text" class="wclp-edit-input" value="<?php echo esc_attr($item['license']->getDomain()); ?>">
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary"><?php esc_html_e('Save', 'wc-licensed-product'); ?></button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small"><?php esc_html_e('Cancel', 'wc-licensed-product'); ?></button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="wclp-editable-cell" data-field="status" data-license-id="<?php echo esc_attr($item['license']->getId()); ?>">
|
||||||
<?php
|
<span class="wclp-display-value">
|
||||||
$expiresAt = $item['license']->getExpiresAt();
|
<span class="license-status license-status-<?php echo esc_attr($item['license']->getStatus()); ?>">
|
||||||
if ($expiresAt) {
|
<?php echo esc_html(ucfirst($item['license']->getStatus())); ?>
|
||||||
echo esc_html($expiresAt->format(get_option('date_format')));
|
</span>
|
||||||
} else {
|
</span>
|
||||||
echo '<span class="license-lifetime">' . esc_html__('Lifetime', 'wc-licensed-product') . '</span>';
|
<button type="button" class="wclp-edit-btn button-link" title="<?php esc_attr_e('Edit', 'wc-licensed-product'); ?>">
|
||||||
}
|
<span class="dashicons dashicons-edit"></span>
|
||||||
?>
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<select class="wclp-edit-input">
|
||||||
|
<option value="active" <?php selected($item['license']->getStatus(), 'active'); ?>><?php esc_html_e('Active', 'wc-licensed-product'); ?></option>
|
||||||
|
<option value="inactive" <?php selected($item['license']->getStatus(), 'inactive'); ?>><?php esc_html_e('Inactive', 'wc-licensed-product'); ?></option>
|
||||||
|
<option value="expired" <?php selected($item['license']->getStatus(), 'expired'); ?>><?php esc_html_e('Expired', 'wc-licensed-product'); ?></option>
|
||||||
|
<option value="revoked" <?php selected($item['license']->getStatus(), 'revoked'); ?>><?php esc_html_e('Revoked', 'wc-licensed-product'); ?></option>
|
||||||
|
</select>
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary"><?php esc_html_e('Save', 'wc-licensed-product'); ?></button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small"><?php esc_html_e('Cancel', 'wc-licensed-product'); ?></button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="wclp-editable-cell" data-field="expiry" data-license-id="<?php echo esc_attr($item['license']->getId()); ?>">
|
||||||
|
<?php $expiresAt = $item['license']->getExpiresAt(); ?>
|
||||||
|
<span class="wclp-display-value">
|
||||||
|
<?php if ($expiresAt): ?>
|
||||||
|
<?php echo esc_html($expiresAt->format(get_option('date_format'))); ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="license-lifetime"><?php esc_html_e('Lifetime', 'wc-licensed-product'); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</span>
|
||||||
|
<button type="button" class="wclp-edit-btn button-link" title="<?php esc_attr_e('Edit', 'wc-licensed-product'); ?>">
|
||||||
|
<span class="dashicons dashicons-edit"></span>
|
||||||
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<input type="date" class="wclp-edit-input" value="<?php echo $expiresAt ? esc_attr($expiresAt->format('Y-m-d')) : ''; ?>" placeholder="<?php esc_attr_e('Leave empty for lifetime', 'wc-licensed-product'); ?>">
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary"><?php esc_html_e('Save', 'wc-licensed-product'); ?></button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small"><?php esc_html_e('Cancel', 'wc-licensed-product'); ?></button>
|
||||||
|
<button type="button" class="wclp-lifetime-btn button button-small" title="<?php esc_attr_e('Set to lifetime', 'wc-licensed-product'); ?>">∞</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="license-actions">
|
<td class="license-actions">
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
|
|||||||
@@ -424,6 +424,39 @@ class LicenseManager
|
|||||||
return $result !== false;
|
return $result !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update license expiry date
|
||||||
|
*
|
||||||
|
* @param int $licenseId License ID
|
||||||
|
* @param \DateTimeImmutable $expiresAt New expiry date
|
||||||
|
* @return bool Success
|
||||||
|
*/
|
||||||
|
public function updateLicenseExpiry(int $licenseId, \DateTimeImmutable $expiresAt): bool
|
||||||
|
{
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$license = $this->getLicenseById($licenseId);
|
||||||
|
if (!$license) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableName = Installer::getLicensesTable();
|
||||||
|
$result = $wpdb->update(
|
||||||
|
$tableName,
|
||||||
|
['expires_at' => $expiresAt->format('Y-m-d H:i:s')],
|
||||||
|
['id' => $licenseId],
|
||||||
|
['%s'],
|
||||||
|
['%d']
|
||||||
|
);
|
||||||
|
|
||||||
|
// If license was expired and new date is in the future, reactivate it
|
||||||
|
if ($result !== false && $license->getStatus() === License::STATUS_EXPIRED && $expiresAt > new \DateTimeImmutable()) {
|
||||||
|
$this->updateLicenseStatus($licenseId, License::STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result !== false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update license domain
|
* Update license domain
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -110,7 +110,12 @@
|
|||||||
<th scope="row" class="check-column">
|
<th scope="row" class="check-column">
|
||||||
<input type="checkbox" name="license_ids[]" value="{{ item.license.id }}">
|
<input type="checkbox" name="license_ids[]" value="{{ item.license.id }}">
|
||||||
</th>
|
</th>
|
||||||
<td><code>{{ item.license.licenseKey }}</code></td>
|
<td>
|
||||||
|
<code class="wclp-license-key">{{ item.license.licenseKey }}</code>
|
||||||
|
<button type="button" class="wclp-copy-btn button-link" data-license-key="{{ esc_attr(item.license.licenseKey) }}" title="{{ __('Copy to clipboard') }}">
|
||||||
|
<span class="dashicons dashicons-clipboard"></span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if item.product_edit_url %}
|
{% if item.product_edit_url %}
|
||||||
<a href="{{ esc_url(item.product_edit_url) }}">{{ esc_html(item.product_name) }}</a>
|
<a href="{{ esc_url(item.product_edit_url) }}">{{ esc_html(item.product_name) }}</a>
|
||||||
@@ -124,18 +129,54 @@
|
|||||||
<br><small>{{ esc_html(item.customer_email) }}</small>
|
<br><small>{{ esc_html(item.customer_email) }}</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ esc_html(item.license.domain) }}</td>
|
<td class="wclp-editable-cell" data-field="domain" data-license-id="{{ item.license.id }}">
|
||||||
<td>
|
<span class="wclp-display-value">{{ esc_html(item.license.domain) }}</span>
|
||||||
<span class="license-status license-status-{{ item.license.status }}">
|
<button type="button" class="wclp-edit-btn button-link" title="{{ __('Edit') }}">
|
||||||
{{ item.license.status|capitalize }}
|
<span class="dashicons dashicons-edit"></span>
|
||||||
</span>
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<input type="text" class="wclp-edit-input" value="{{ esc_attr(item.license.domain) }}">
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary">{{ __('Save') }}</button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small">{{ __('Cancel') }}</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="wclp-editable-cell" data-field="status" data-license-id="{{ item.license.id }}">
|
||||||
{% if item.license.expiresAt %}
|
<span class="wclp-display-value">
|
||||||
{{ item.license.expiresAt|date('Y-m-d') }}
|
<span class="license-status license-status-{{ item.license.status }}">
|
||||||
{% else %}
|
{{ item.license.status|capitalize }}
|
||||||
<span class="license-lifetime">{{ __('Lifetime') }}</span>
|
</span>
|
||||||
{% endif %}
|
</span>
|
||||||
|
<button type="button" class="wclp-edit-btn button-link" title="{{ __('Edit') }}">
|
||||||
|
<span class="dashicons dashicons-edit"></span>
|
||||||
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<select class="wclp-edit-input">
|
||||||
|
<option value="active" {{ item.license.status == 'active' ? 'selected' : '' }}>{{ __('Active') }}</option>
|
||||||
|
<option value="inactive" {{ item.license.status == 'inactive' ? 'selected' : '' }}>{{ __('Inactive') }}</option>
|
||||||
|
<option value="expired" {{ item.license.status == 'expired' ? 'selected' : '' }}>{{ __('Expired') }}</option>
|
||||||
|
<option value="revoked" {{ item.license.status == 'revoked' ? 'selected' : '' }}>{{ __('Revoked') }}</option>
|
||||||
|
</select>
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary">{{ __('Save') }}</button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small">{{ __('Cancel') }}</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="wclp-editable-cell" data-field="expiry" data-license-id="{{ item.license.id }}">
|
||||||
|
<span class="wclp-display-value">
|
||||||
|
{% if item.license.expiresAt %}
|
||||||
|
{{ item.license.expiresAt|date('Y-m-d') }}
|
||||||
|
{% else %}
|
||||||
|
<span class="license-lifetime">{{ __('Lifetime') }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
<button type="button" class="wclp-edit-btn button-link" title="{{ __('Edit') }}">
|
||||||
|
<span class="dashicons dashicons-edit"></span>
|
||||||
|
</button>
|
||||||
|
<div class="wclp-edit-form" style="display:none;">
|
||||||
|
<input type="date" class="wclp-edit-input" value="{{ item.license.expiresAt ? item.license.expiresAt|date('Y-m-d') : '' }}" placeholder="{{ __('Leave empty for lifetime') }}">
|
||||||
|
<button type="button" class="wclp-save-btn button button-small button-primary">{{ __('Save') }}</button>
|
||||||
|
<button type="button" class="wclp-cancel-btn button button-small">{{ __('Cancel') }}</button>
|
||||||
|
<button type="button" class="wclp-lifetime-btn button button-small" title="{{ __('Set to lifetime') }}">∞</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="license-actions">
|
<td class="license-actions">
|
||||||
<div class="row-actions">
|
<div class="row-actions">
|
||||||
|
|||||||
Reference in New Issue
Block a user