Files
wc-licensed-product/templates/admin/licenses.html.twig

476 lines
25 KiB
Twig
Raw Normal View History

<div class="wrap">
<h1 class="wp-heading-inline">{{ __('Licenses') }}</h1>
<a href="{{ export_csv_url() }}" class="page-title-action">
<span class="dashicons dashicons-download" style="vertical-align: middle;"></span>
{{ __('Export CSV') }}
</a>
<a href="{{ admin_url }}?action=import_csv" class="page-title-action">
<span class="dashicons dashicons-upload" style="vertical-align: middle;"></span>
{{ __('Import CSV') }}
</a>
<hr class="wp-header-end">
{% for notice in notices %}
<div class="notice notice-{{ notice.type }} is-dismissible">
<p>{{ esc_html(notice.message) }}</p>
</div>
{% endfor %}
<!-- Search and Filter Form -->
<form method="get" action="" class="wclp-filter-form">
<input type="hidden" name="page" value="wc-licenses">
<p class="search-box">
<label class="screen-reader-text" for="license-search-input">{{ __('Search Licenses') }}</label>
<input type="search" id="license-search-input" name="s" value="{{ filters.search|default('') }}"
placeholder="{{ __('Search license key or domain...') }}">
<input type="submit" id="search-submit" class="button" value="{{ __('Search') }}">
</p>
<div class="tablenav top">
<div class="alignleft actions">
<select name="status">
<option value="all">{{ __('All Statuses') }}</option>
<option value="active" {{ filters.status|default('') == 'active' ? 'selected' : '' }}>{{ __('Active') }}</option>
<option value="inactive" {{ filters.status|default('') == 'inactive' ? 'selected' : '' }}>{{ __('Inactive') }}</option>
<option value="expired" {{ filters.status|default('') == 'expired' ? 'selected' : '' }}>{{ __('Expired') }}</option>
<option value="revoked" {{ filters.status|default('') == 'revoked' ? 'selected' : '' }}>{{ __('Revoked') }}</option>
</select>
<select name="product_id">
<option value="">{{ __('All Products') }}</option>
{% for id, name in products %}
<option value="{{ id }}" {{ filters.product_id|default('') == id ? 'selected' : '' }}>{{ esc_html(name) }}</option>
{% endfor %}
</select>
<input type="submit" class="button" value="{{ __('Filter') }}">
{% if filters is not empty %}
<a href="{{ admin_url }}" class="button">{{ __('Clear') }}</a>
{% endif %}
</div>
<div class="tablenav-pages">
<span class="displaying-num">{{ total_licenses }} {{ total_licenses == 1 ? __('item') : __('items') }}</span>
</div>
</div>
</form>
<p class="description">
{{ __('Showing') }} {{ total_licenses }} {{ total_licenses == 1 ? __('license') : __('licenses') }}
{% if filters is not empty %}
({{ __('filtered') }})
{% endif %}
| <a href="{{ constant('admin_url')('admin.php?page=wc-reports&tab=licenses') }}">{{ __('View Dashboard') }}</a>
</p>
<form method="post" action="{{ admin_url }}" id="licenses-bulk-form">
{{ wp_nonce_field('bulk_license_action', '_wpnonce', true, false)|raw }}
<div class="tablenav top">
<div class="alignleft actions bulkactions">
<select name="bulk_action" id="bulk-action-selector">
<option value="">{{ __('Bulk Actions') }}</option>
<option value="activate">{{ __('Activate') }}</option>
<option value="deactivate">{{ __('Deactivate') }}</option>
<option value="revoke">{{ __('Revoke') }}</option>
<option value="extend_30">{{ __('Extend 30 days') }}</option>
<option value="extend_90">{{ __('Extend 90 days') }}</option>
<option value="extend_365">{{ __('Extend 1 year') }}</option>
<option value="delete">{{ __('Delete') }}</option>
</select>
<input type="submit" class="button action" value="{{ __('Apply') }}">
</div>
</div>
<table class="wp-list-table widefat fixed striped licenses-table">
<thead>
<tr>
<td class="manage-column column-cb check-column">
<input type="checkbox" id="cb-select-all-1">
</td>
<th>{{ __('License Key') }}</th>
<th>{{ __('Product') }}</th>
<th>{{ __('Customer') }}</th>
<th>{{ __('Domain') }}</th>
<th>{{ __('Status') }}</th>
<th>{{ __('Created') }}</th>
<th>{{ __('Expires') }}</th>
<th>{{ __('Actions') }}</th>
</tr>
</thead>
<tbody>
{% if licenses is empty %}
<tr>
<td colspan="9">{{ __('No licenses found.') }}</td>
</tr>
{% else %}
{% for item in licenses %}
<tr>
<th scope="row" class="check-column">
<input type="checkbox" name="license_ids[]" value="{{ item.license.id }}">
</th>
<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>
{% if item.product_edit_url %}
<a href="{{ esc_url(item.product_edit_url) }}">{{ esc_html(item.product_name) }}</a>
{% else %}
{{ esc_html(item.product_name) }}
{% endif %}
</td>
<td>
{{ esc_html(item.customer_name) }}
{% if item.customer_email %}
<br><small>{{ esc_html(item.customer_email) }}</small>
{% endif %}
</td>
<td class="wclp-editable-cell" data-field="domain" data-license-id="{{ item.license.id }}">
<span class="wclp-display-value">{{ esc_html(item.license.domain) }}</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="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 class="wclp-editable-cell" data-field="status" data-license-id="{{ item.license.id }}">
<span class="wclp-display-value">
<span class="license-status license-status-{{ esc_attr(item.license.status) }}">
{{ esc_html(item.license.status)|capitalize }}
</span>
</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-created-cell">
{{ item.license.createdAt|date('Y-m-d') }}
</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 class="license-actions">
<div class="row-actions">
<span class="test">
<a href="#" class="wclp-test-license-link"
data-license-id="{{ item.license.id }}"
data-license-key="{{ esc_attr(item.license.licenseKey) }}"
data-domain="{{ esc_attr(item.license.domain) }}"
title="{{ __('Test license against API') }}">{{ __('Test') }}</a> |
</span>
{% if item.license.status != 'revoked' %}
<span class="transfer">
<a href="#" class="wclp-transfer-link"
data-license-id="{{ item.license.id }}"
data-current-domain="{{ esc_attr(item.license.domain) }}"
title="{{ __('Transfer to new domain') }}">{{ __('Transfer') }}</a> |
</span>
<span class="extend">
<a href="{{ extend_url(item.license.id, 30) }}" title="{{ __('Extend by 30 days') }}">+30d</a> |
</span>
<span class="lifetime">
<a href="{{ lifetime_url(item.license.id) }}" title="{{ __('Set to lifetime') }}">∞</a> |
</span>
<span class="revoke">
<a href="{{ revoke_url(item.license.id) }}"
onclick="return confirm('{{ __('Are you sure?') }}')">{{ __('Revoke') }}</a> |
</span>
{% endif %}
<span class="delete">
<a href="{{ delete_url(item.license.id) }}"
class="submitdelete"
onclick="return confirm('{{ __('Are you sure you want to delete this license?') }}')">{{ __('Delete') }}</a>
</span>
</div>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
<tfoot>
<tr>
<td class="manage-column column-cb check-column">
<input type="checkbox" id="cb-select-all-2">
</td>
<th>{{ __('License Key') }}</th>
<th>{{ __('Product') }}</th>
<th>{{ __('Customer') }}</th>
<th>{{ __('Domain') }}</th>
<th>{{ __('Status') }}</th>
<th>{{ __('Created') }}</th>
<th>{{ __('Expires') }}</th>
<th>{{ __('Actions') }}</th>
</tr>
</tfoot>
</table>
<div class="tablenav bottom">
<div class="alignleft actions bulkactions">
<select name="bulk_action_2" id="bulk-action-selector-bottom">
<option value="">{{ __('Bulk Actions') }}</option>
<option value="activate">{{ __('Activate') }}</option>
<option value="deactivate">{{ __('Deactivate') }}</option>
<option value="revoke">{{ __('Revoke') }}</option>
<option value="extend_30">{{ __('Extend 30 days') }}</option>
<option value="extend_90">{{ __('Extend 90 days') }}</option>
<option value="extend_365">{{ __('Extend 1 year') }}</option>
<option value="delete">{{ __('Delete') }}</option>
</select>
<input type="submit" class="button action" value="{{ __('Apply') }}">
</div>
{% if total_pages > 1 %}
{% set filter_params = '' %}
{% if filters.search is defined and filters.search %}{% set filter_params = filter_params ~ '&s=' ~ filters.search %}{% endif %}
{% if filters.status is defined and filters.status %}{% set filter_params = filter_params ~ '&status=' ~ filters.status %}{% endif %}
{% if filters.product_id is defined and filters.product_id %}{% set filter_params = filter_params ~ '&product_id=' ~ filters.product_id %}{% endif %}
<div class="tablenav-pages">
<span class="pagination-links">
{% if current_page > 1 %}
<a class="prev-page button" href="{{ admin_url ~ '&paged=' ~ (current_page - 1) ~ filter_params }}">
<span aria-hidden="true"></span>
</a>
{% endif %}
<span class="paging-input">
{{ current_page }} {{ __('of') }} {{ total_pages }}
</span>
{% if current_page < total_pages %}
<a class="next-page button" href="{{ admin_url ~ '&paged=' ~ (current_page + 1) ~ filter_params }}">
<span aria-hidden="true"></span>
</a>
{% endif %}
</span>
</div>
{% endif %}
</div>
</form>
</div>
<!-- Test License Modal -->
<div id="wclp-test-modal" class="wclp-modal" style="display:none;">
<div class="wclp-modal-content">
<span class="wclp-modal-close">&times;</span>
<h2>{{ __('License Validation Test') }}</h2>
<div class="wclp-test-info">
<table class="form-table">
<tr>
<th scope="row">{{ __('License Key') }}</th>
<td><code id="test-license-key"></code></td>
</tr>
<tr>
<th scope="row">{{ __('Domain') }}</th>
<td><code id="test-domain"></code></td>
</tr>
</table>
</div>
<div id="wclp-test-loading" style="display:none; text-align:center; padding:20px;">
<span class="spinner is-active" style="float:none;"></span>
<p>{{ __('Testing license...') }}</p>
</div>
<div id="wclp-test-result" style="display:none;">
<div id="wclp-test-result-content"></div>
</div>
<p class="submit">
<button type="button" class="button wclp-modal-cancel">{{ __('Close') }}</button>
</p>
</div>
</div>
<!-- Transfer Modal -->
<div id="wclp-transfer-modal" class="wclp-modal" style="display:none;">
<div class="wclp-modal-content">
<span class="wclp-modal-close">&times;</span>
<h2>{{ __('Transfer License to New Domain') }}</h2>
<form method="post" action="{{ admin_url }}">
<input type="hidden" name="action" value="transfer_license">
<input type="hidden" name="_wpnonce" value="{{ transfer_nonce() }}">
<input type="hidden" name="license_id" id="transfer-license-id" value="">
<table class="form-table">
<tr>
<th scope="row"><label>{{ __('Current Domain') }}</label></th>
<td><code id="transfer-current-domain"></code></td>
</tr>
<tr>
<th scope="row"><label for="new_domain">{{ __('New Domain') }}</label></th>
<td>
<input type="text" name="new_domain" id="transfer-new-domain" class="regular-text"
placeholder="example.com" required>
<p class="description">{{ __('Enter the new domain without http:// or www.') }}</p>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button button-primary">{{ __('Transfer License') }}</button>
<button type="button" class="button wclp-modal-cancel">{{ __('Cancel') }}</button>
</p>
</form>
</div>
</div>
<script>
(function($) {
// Select all checkboxes
$('#cb-select-all-1, #cb-select-all-2').on('change', function() {
$('input[name="license_ids[]"]').prop('checked', this.checked);
$('#cb-select-all-1, #cb-select-all-2').prop('checked', this.checked);
});
// Sync bulk action selects
$('#bulk-action-selector, #bulk-action-selector-bottom').on('change', function() {
var value = $(this).val();
$('#bulk-action-selector, #bulk-action-selector-bottom').val(value);
});
// Use bottom select value if top is empty
$('form').on('submit', function() {
var topAction = $('#bulk-action-selector').val();
var bottomAction = $('#bulk-action-selector-bottom').val();
if (!topAction && bottomAction) {
$('#bulk-action-selector').val(bottomAction);
}
});
// Transfer modal
var $modal = $('#wclp-transfer-modal');
$('.wclp-transfer-link').on('click', function(e) {
e.preventDefault();
var licenseId = $(this).data('license-id');
var currentDomain = $(this).data('current-domain');
$('#transfer-license-id').val(licenseId);
$('#transfer-current-domain').text(currentDomain);
$('#transfer-new-domain').val('');
$modal.show();
});
$('.wclp-modal-close, .wclp-modal-cancel').on('click', function() {
$modal.hide();
});
$(window).on('click', function(e) {
if ($(e.target).is($modal)) {
$modal.hide();
}
});
// Test License modal
var $testModal = $('#wclp-test-modal');
var $testLoading = $('#wclp-test-loading');
var $testResult = $('#wclp-test-result');
var $testResultContent = $('#wclp-test-result-content');
$('.wclp-test-license-link').on('click', function(e) {
e.preventDefault();
var licenseKey = $(this).data('license-key');
var domain = $(this).data('domain');
// Show modal with info
$('#test-license-key').text(licenseKey);
$('#test-domain').text(domain);
$testLoading.show();
$testResult.hide();
$testModal.show();
// Call the test endpoint
$.ajax({
url: wclpAdmin.ajaxUrl,
type: 'POST',
data: {
action: 'wclp_test_license',
nonce: wclpAdmin.editNonce,
license_key: licenseKey,
domain: domain
},
success: function(response) {
$testLoading.hide();
if (response.success) {
var result = response.data;
var html = '';
if (result.valid) {
html = '<div class="notice notice-success inline"><p><strong>✓ {{ __('License is VALID') }}</strong></p></div>';
html += '<table class="widefat striped"><tbody>';
html += '<tr><th>{{ __('Product') }}</th><td><strong>' + escapeHtml(result.product_name || '-') + '</strong></td></tr>';
if (result.expires_at) {
html += '<tr><th>{{ __('Expires') }}</th><td>' + escapeHtml(result.expires_at) + '</td></tr>';
} else {
html += '<tr><th>{{ __('Expires') }}</th><td><span class="license-lifetime">{{ __('Lifetime') }}</span></td></tr>';
}
html += '</tbody></table>';
} else {
html = '<div class="notice notice-error inline"><p><strong>✗ {{ __('License is INVALID') }}</strong></p></div>';
html += '<table class="widefat striped"><tbody>';
html += '<tr><th>{{ __('Error Code') }}</th><td><code>' + escapeHtml(result.error || 'unknown') + '</code></td></tr>';
html += '<tr><th>{{ __('Message') }}</th><td>' + escapeHtml(result.message || '-') + '</td></tr>';
html += '</tbody></table>';
}
$testResultContent.html(html);
$testResult.show();
} else {
$testResultContent.html('<div class="notice notice-error inline"><p>' + escapeHtml(response.data.message || 'Error') + '</p></div>');
$testResult.show();
}
},
error: function() {
$testLoading.hide();
$testResultContent.html('<div class="notice notice-error inline"><p>{{ __('Failed to test license. Please try again.') }}</p></div>');
$testResult.show();
}
});
});
// Close test modal
$testModal.find('.wclp-modal-close, .wclp-modal-cancel').on('click', function() {
$testModal.hide();
});
$(window).on('click', function(e) {
if ($(e.target).is($testModal)) {
$testModal.hide();
}
});
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
})(jQuery);
</script>