2026-01-21 18:55:18 +01:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Admin Controller
|
|
|
|
|
*
|
|
|
|
|
* @package Jeremias\WcLicensedProduct\Admin
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Jeremias\WcLicensedProduct\Admin;
|
|
|
|
|
|
|
|
|
|
use Jeremias\WcLicensedProduct\License\License;
|
|
|
|
|
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
|
|
|
|
use Twig\Environment;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles admin pages for license management
|
|
|
|
|
*/
|
|
|
|
|
final class AdminController
|
|
|
|
|
{
|
|
|
|
|
private Environment $twig;
|
|
|
|
|
private LicenseManager $licenseManager;
|
|
|
|
|
|
|
|
|
|
public function __construct(Environment $twig, LicenseManager $licenseManager)
|
|
|
|
|
{
|
|
|
|
|
$this->twig = $twig;
|
|
|
|
|
$this->licenseManager = $licenseManager;
|
|
|
|
|
$this->registerHooks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Register WordPress hooks
|
|
|
|
|
*/
|
|
|
|
|
private function registerHooks(): void
|
|
|
|
|
{
|
|
|
|
|
// Add admin menu
|
|
|
|
|
add_action('admin_menu', [$this, 'addAdminMenu']);
|
|
|
|
|
|
|
|
|
|
// Enqueue admin styles
|
|
|
|
|
add_action('admin_enqueue_scripts', [$this, 'enqueueStyles']);
|
|
|
|
|
|
|
|
|
|
// Handle admin actions
|
|
|
|
|
add_action('admin_init', [$this, 'handleAdminActions']);
|
|
|
|
|
|
|
|
|
|
// Add licenses column to orders list
|
|
|
|
|
add_filter('manage_edit-shop_order_columns', [$this, 'addOrdersLicenseColumn']);
|
|
|
|
|
add_action('manage_shop_order_posts_custom_column', [$this, 'displayOrdersLicenseColumn'], 10, 2);
|
|
|
|
|
|
|
|
|
|
// HPOS compatibility
|
|
|
|
|
add_filter('woocommerce_shop_order_list_table_columns', [$this, 'addOrdersLicenseColumn']);
|
|
|
|
|
add_action('woocommerce_shop_order_list_table_custom_column', [$this, 'displayOrdersLicenseColumnHpos'], 10, 2);
|
2026-01-21 20:32:35 +01:00
|
|
|
|
|
|
|
|
// Add to WooCommerce Reports
|
|
|
|
|
add_filter('woocommerce_admin_reports', [$this, 'addLicenseReports']);
|
2026-01-21 18:55:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add admin menu pages
|
|
|
|
|
*/
|
|
|
|
|
public function addAdminMenu(): void
|
|
|
|
|
{
|
|
|
|
|
add_submenu_page(
|
|
|
|
|
'woocommerce',
|
|
|
|
|
__('Licenses', 'wc-licensed-product'),
|
|
|
|
|
__('Licenses', 'wc-licensed-product'),
|
|
|
|
|
'manage_woocommerce',
|
|
|
|
|
'wc-licenses',
|
|
|
|
|
[$this, 'renderLicensesPage']
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 20:32:35 +01:00
|
|
|
/**
|
|
|
|
|
* Add license reports to WooCommerce Reports
|
|
|
|
|
*/
|
|
|
|
|
public function addLicenseReports(array $reports): array
|
|
|
|
|
{
|
|
|
|
|
$reports['licenses'] = [
|
|
|
|
|
'title' => __('Licenses', 'wc-licensed-product'),
|
|
|
|
|
'reports' => [
|
|
|
|
|
'overview' => [
|
|
|
|
|
'title' => __('Overview', 'wc-licensed-product'),
|
|
|
|
|
'description' => '',
|
|
|
|
|
'hide_title' => true,
|
|
|
|
|
'callback' => [$this, 'renderDashboardPage'],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $reports;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 18:55:18 +01:00
|
|
|
/**
|
|
|
|
|
* Enqueue admin styles and scripts
|
|
|
|
|
*/
|
|
|
|
|
public function enqueueStyles(string $hook): void
|
|
|
|
|
{
|
2026-01-21 20:32:35 +01:00
|
|
|
// Check for our pages and WooCommerce Reports page with licenses tab
|
|
|
|
|
$isLicensePage = in_array($hook, ['woocommerce_page_wc-licenses', 'woocommerce_page_wc-license-dashboard'], true);
|
|
|
|
|
$isReportsPage = $hook === 'woocommerce_page_wc-reports' && isset($_GET['tab']) && $_GET['tab'] === 'licenses';
|
|
|
|
|
|
|
|
|
|
if (!$isLicensePage && !$isReportsPage) {
|
2026-01-21 18:55:18 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wp_enqueue_style(
|
|
|
|
|
'wc-licensed-product-admin',
|
|
|
|
|
WC_LICENSED_PRODUCT_PLUGIN_URL . 'assets/css/admin.css',
|
|
|
|
|
[],
|
|
|
|
|
WC_LICENSED_PRODUCT_VERSION
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle admin actions (update, delete licenses)
|
|
|
|
|
*/
|
|
|
|
|
public function handleAdminActions(): void
|
|
|
|
|
{
|
|
|
|
|
if (!isset($_GET['page']) || $_GET['page'] !== 'wc-licenses') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!current_user_can('manage_woocommerce')) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle status update
|
|
|
|
|
if (isset($_POST['action']) && $_POST['action'] === 'update_license_status') {
|
|
|
|
|
$this->handleStatusUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle delete
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['license_id'])) {
|
|
|
|
|
$this->handleDelete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle revoke
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'revoke' && isset($_GET['license_id'])) {
|
|
|
|
|
$this->handleRevoke();
|
|
|
|
|
}
|
2026-01-21 20:32:35 +01:00
|
|
|
|
|
|
|
|
// Handle extend
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'extend' && isset($_GET['license_id'])) {
|
|
|
|
|
$this->handleExtend();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle set lifetime
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'lifetime' && isset($_GET['license_id'])) {
|
|
|
|
|
$this->handleSetLifetime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle bulk actions
|
|
|
|
|
if (isset($_POST['bulk_action']) && !empty($_POST['license_ids'])) {
|
|
|
|
|
$this->handleBulkAction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle transfer
|
|
|
|
|
if (isset($_POST['action']) && $_POST['action'] === 'transfer_license') {
|
|
|
|
|
$this->handleTransfer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle CSV export
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'export_csv') {
|
|
|
|
|
$this->handleCsvExport();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle CSV import page
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'import_csv') {
|
|
|
|
|
// Show import form - handled in renderImportPage
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle CSV import upload
|
|
|
|
|
if (isset($_POST['action']) && $_POST['action'] === 'process_import_csv') {
|
|
|
|
|
$this->handleCsvImport();
|
|
|
|
|
}
|
2026-01-21 18:55:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle license status update
|
|
|
|
|
*/
|
|
|
|
|
private function handleStatusUpdate(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_POST['_wpnonce'] ?? '', 'update_license_status')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_POST['license_id'] ?? 0);
|
|
|
|
|
$status = sanitize_text_field($_POST['status'] ?? '');
|
|
|
|
|
|
|
|
|
|
if ($licenseId && in_array($status, [License::STATUS_ACTIVE, License::STATUS_INACTIVE, License::STATUS_REVOKED], true)) {
|
|
|
|
|
$this->licenseManager->updateLicenseStatus($licenseId, $status);
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&updated=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle license deletion
|
|
|
|
|
*/
|
|
|
|
|
private function handleDelete(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'delete_license')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_GET['license_id'] ?? 0);
|
|
|
|
|
if ($licenseId) {
|
|
|
|
|
$this->licenseManager->deleteLicense($licenseId);
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&deleted=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle license revocation
|
|
|
|
|
*/
|
|
|
|
|
private function handleRevoke(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'revoke_license')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_GET['license_id'] ?? 0);
|
|
|
|
|
if ($licenseId) {
|
|
|
|
|
$this->licenseManager->updateLicenseStatus($licenseId, License::STATUS_REVOKED);
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&revoked=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 20:32:35 +01:00
|
|
|
/**
|
|
|
|
|
* Handle license extension
|
|
|
|
|
*/
|
|
|
|
|
private function handleExtend(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'extend_license')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_GET['license_id'] ?? 0);
|
|
|
|
|
$days = absint($_GET['days'] ?? 30);
|
|
|
|
|
|
|
|
|
|
if ($licenseId && $days > 0) {
|
|
|
|
|
$this->licenseManager->extendLicense($licenseId, $days);
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&extended=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle set license to lifetime
|
|
|
|
|
*/
|
|
|
|
|
private function handleSetLifetime(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'lifetime_license')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_GET['license_id'] ?? 0);
|
|
|
|
|
if ($licenseId) {
|
|
|
|
|
$this->licenseManager->setLicenseLifetime($licenseId);
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&lifetime=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle license transfer
|
|
|
|
|
*/
|
|
|
|
|
private function handleTransfer(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_POST['_wpnonce'] ?? '', 'transfer_license')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenseId = absint($_POST['license_id'] ?? 0);
|
|
|
|
|
$newDomain = sanitize_text_field($_POST['new_domain'] ?? '');
|
|
|
|
|
|
|
|
|
|
if ($licenseId && !empty($newDomain)) {
|
|
|
|
|
$success = $this->licenseManager->transferLicense($licenseId, $newDomain);
|
|
|
|
|
|
|
|
|
|
if ($success) {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&transferred=1'));
|
|
|
|
|
} else {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&transfer_failed=1'));
|
|
|
|
|
}
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle CSV export
|
|
|
|
|
*/
|
|
|
|
|
private function handleCsvExport(): void
|
|
|
|
|
{
|
|
|
|
|
if (!current_user_can('manage_woocommerce')) {
|
|
|
|
|
wp_die(__('You do not have permission to export licenses.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $this->licenseManager->exportLicensesForCsv();
|
|
|
|
|
|
|
|
|
|
if (empty($data)) {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&export_empty=1'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set headers for CSV download
|
|
|
|
|
$filename = 'licenses-export-' . gmdate('Y-m-d-His') . '.csv';
|
|
|
|
|
header('Content-Type: text/csv; charset=utf-8');
|
|
|
|
|
header('Content-Disposition: attachment; filename=' . $filename);
|
|
|
|
|
header('Pragma: no-cache');
|
|
|
|
|
header('Expires: 0');
|
|
|
|
|
|
|
|
|
|
$output = fopen('php://output', 'w');
|
|
|
|
|
|
|
|
|
|
// Write BOM for UTF-8
|
|
|
|
|
fwrite($output, "\xEF\xBB\xBF");
|
|
|
|
|
|
|
|
|
|
// Write header row
|
|
|
|
|
fputcsv($output, array_keys($data[0]));
|
|
|
|
|
|
|
|
|
|
// Write data rows
|
|
|
|
|
foreach ($data as $row) {
|
|
|
|
|
fputcsv($output, $row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose($output);
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle CSV import
|
|
|
|
|
*/
|
|
|
|
|
private function handleCsvImport(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_POST['_wpnonce'] ?? '', 'import_licenses_csv')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!current_user_can('manage_woocommerce')) {
|
|
|
|
|
wp_die(__('You do not have permission to import licenses.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if file was uploaded
|
|
|
|
|
if (!isset($_FILES['import_file']) || $_FILES['import_file']['error'] !== UPLOAD_ERR_OK) {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&action=import_csv&import_error=upload'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$file = $_FILES['import_file'];
|
|
|
|
|
|
|
|
|
|
// Validate file type
|
|
|
|
|
$fileType = wp_check_filetype($file['name']);
|
|
|
|
|
if ($fileType['ext'] !== 'csv') {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&action=import_csv&import_error=filetype'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read the CSV file
|
|
|
|
|
$handle = fopen($file['tmp_name'], 'r');
|
|
|
|
|
if (!$handle) {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&action=import_csv&import_error=read'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get import options
|
|
|
|
|
$skipFirstRow = isset($_POST['skip_first_row']) && $_POST['skip_first_row'] === '1';
|
|
|
|
|
$updateExisting = isset($_POST['update_existing']) && $_POST['update_existing'] === '1';
|
|
|
|
|
|
|
|
|
|
// Skip BOM if present
|
|
|
|
|
$bom = fread($handle, 3);
|
|
|
|
|
if ($bom !== "\xEF\xBB\xBF") {
|
|
|
|
|
rewind($handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read header row if skipping
|
|
|
|
|
if ($skipFirstRow) {
|
|
|
|
|
fgetcsv($handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$imported = 0;
|
|
|
|
|
$updated = 0;
|
|
|
|
|
$skipped = 0;
|
|
|
|
|
$errors = [];
|
|
|
|
|
|
|
|
|
|
while (($row = fgetcsv($handle)) !== false) {
|
|
|
|
|
// Skip empty rows
|
|
|
|
|
if (empty($row) || (count($row) === 1 && empty($row[0]))) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Map CSV columns (expected format from export):
|
|
|
|
|
// ID, License Key, Product, Product ID, Order ID, Order Number, Customer, Customer Email, Customer ID, Domain, Status, Activations, Max Activations, Expires At, Created At, Updated At
|
|
|
|
|
// For import we need: License Key (or generate), Product ID, Customer ID, Domain, Status, Max Activations, Expires At
|
|
|
|
|
$result = $this->processImportRow($row, $updateExisting);
|
|
|
|
|
|
|
|
|
|
if ($result === 'imported') {
|
|
|
|
|
$imported++;
|
|
|
|
|
} elseif ($result === 'updated') {
|
|
|
|
|
$updated++;
|
|
|
|
|
} elseif ($result === 'skipped') {
|
|
|
|
|
$skipped++;
|
|
|
|
|
} else {
|
|
|
|
|
$errors[] = $result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fclose($handle);
|
|
|
|
|
|
|
|
|
|
// Build redirect URL with results
|
|
|
|
|
$redirectUrl = add_query_arg([
|
|
|
|
|
'page' => 'wc-licenses',
|
|
|
|
|
'imported' => $imported,
|
|
|
|
|
'updated' => $updated,
|
|
|
|
|
'skipped' => $skipped,
|
|
|
|
|
'import_errors' => count($errors),
|
|
|
|
|
], admin_url('admin.php'));
|
|
|
|
|
|
|
|
|
|
wp_redirect($redirectUrl);
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Process a single import row
|
|
|
|
|
*
|
|
|
|
|
* @param array $row CSV row data
|
|
|
|
|
* @param bool $updateExisting Whether to update existing licenses
|
|
|
|
|
* @return string Result: 'imported', 'updated', 'skipped', or error message
|
|
|
|
|
*/
|
|
|
|
|
private function processImportRow(array $row, bool $updateExisting): string
|
|
|
|
|
{
|
|
|
|
|
// Determine if this is from our export format or simplified format
|
|
|
|
|
// Export format has 16 columns, simplified has fewer
|
|
|
|
|
|
|
|
|
|
if (count($row) >= 10) {
|
|
|
|
|
// Full export format
|
|
|
|
|
$licenseKey = trim($row[1] ?? '');
|
|
|
|
|
$productId = absint($row[3] ?? 0);
|
|
|
|
|
$orderId = absint($row[4] ?? 0);
|
|
|
|
|
$customerId = absint($row[8] ?? 0);
|
|
|
|
|
$domain = trim($row[9] ?? '');
|
|
|
|
|
$status = strtolower(trim($row[10] ?? 'active'));
|
|
|
|
|
$activationsCount = absint($row[11] ?? 1);
|
|
|
|
|
$maxActivations = absint($row[12] ?? 1);
|
|
|
|
|
$expiresAt = trim($row[13] ?? '');
|
|
|
|
|
} else {
|
|
|
|
|
// Simplified format: License Key, Product ID, Customer ID, Domain, Status, Max Activations, Expires At
|
|
|
|
|
$licenseKey = trim($row[0] ?? '');
|
|
|
|
|
$productId = absint($row[1] ?? 0);
|
|
|
|
|
$customerId = absint($row[2] ?? 0);
|
|
|
|
|
$domain = trim($row[3] ?? '');
|
|
|
|
|
$status = strtolower(trim($row[4] ?? 'active'));
|
|
|
|
|
$maxActivations = absint($row[5] ?? 1);
|
|
|
|
|
$expiresAt = trim($row[6] ?? '');
|
|
|
|
|
$orderId = 0;
|
|
|
|
|
$activationsCount = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate required fields
|
|
|
|
|
if (empty($domain)) {
|
|
|
|
|
return sprintf(__('Row missing domain', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($productId <= 0) {
|
|
|
|
|
return sprintf(__('Row missing valid product ID', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if license key already exists
|
|
|
|
|
if (!empty($licenseKey)) {
|
|
|
|
|
$existing = $this->licenseManager->getLicenseByKey($licenseKey);
|
|
|
|
|
if ($existing) {
|
|
|
|
|
if ($updateExisting) {
|
|
|
|
|
// Update existing license
|
|
|
|
|
$this->licenseManager->updateLicenseDomain($existing->getId(), $domain);
|
|
|
|
|
if (in_array($status, [License::STATUS_ACTIVE, License::STATUS_INACTIVE, License::STATUS_REVOKED], true)) {
|
|
|
|
|
$this->licenseManager->updateLicenseStatus($existing->getId(), $status);
|
|
|
|
|
}
|
|
|
|
|
return 'updated';
|
|
|
|
|
}
|
|
|
|
|
return 'skipped';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Generate new license key
|
|
|
|
|
$licenseKey = $this->licenseManager->generateLicenseKey();
|
|
|
|
|
while ($this->licenseManager->getLicenseByKey($licenseKey)) {
|
|
|
|
|
$licenseKey = $this->licenseManager->generateLicenseKey();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize status
|
|
|
|
|
if (!in_array($status, [License::STATUS_ACTIVE, License::STATUS_INACTIVE, License::STATUS_EXPIRED, License::STATUS_REVOKED], true)) {
|
|
|
|
|
$status = License::STATUS_ACTIVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse expiration date
|
|
|
|
|
$expiresAtParsed = null;
|
|
|
|
|
if (!empty($expiresAt) && strtolower($expiresAt) !== 'lifetime') {
|
|
|
|
|
try {
|
|
|
|
|
$expiresAtParsed = new \DateTimeImmutable($expiresAt);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
// Invalid date, leave as null (lifetime)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the license
|
|
|
|
|
$result = $this->licenseManager->importLicense(
|
|
|
|
|
$licenseKey,
|
|
|
|
|
$productId,
|
|
|
|
|
$customerId,
|
|
|
|
|
$domain,
|
|
|
|
|
$orderId,
|
|
|
|
|
$status,
|
|
|
|
|
$maxActivations,
|
|
|
|
|
$activationsCount,
|
|
|
|
|
$expiresAtParsed
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return $result ? 'imported' : sprintf(__('Failed to import license for domain %s', 'wc-licensed-product'), $domain);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle bulk actions
|
|
|
|
|
*/
|
|
|
|
|
private function handleBulkAction(): void
|
|
|
|
|
{
|
|
|
|
|
if (!wp_verify_nonce($_POST['_wpnonce'] ?? '', 'bulk_license_action')) {
|
|
|
|
|
wp_die(__('Security check failed.', 'wc-licensed-product'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$action = sanitize_text_field($_POST['bulk_action'] ?? '');
|
|
|
|
|
$licenseIds = array_map('absint', (array) ($_POST['license_ids'] ?? []));
|
|
|
|
|
|
|
|
|
|
if (empty($licenseIds)) {
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses'));
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$count = 0;
|
|
|
|
|
|
|
|
|
|
switch ($action) {
|
|
|
|
|
case 'activate':
|
|
|
|
|
$count = $this->licenseManager->bulkUpdateStatus($licenseIds, License::STATUS_ACTIVE);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_activated=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'deactivate':
|
|
|
|
|
$count = $this->licenseManager->bulkUpdateStatus($licenseIds, License::STATUS_INACTIVE);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_deactivated=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'revoke':
|
|
|
|
|
$count = $this->licenseManager->bulkUpdateStatus($licenseIds, License::STATUS_REVOKED);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_revoked=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'delete':
|
|
|
|
|
$count = $this->licenseManager->bulkDelete($licenseIds);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_deleted=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'extend_30':
|
|
|
|
|
$count = $this->licenseManager->bulkExtend($licenseIds, 30);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_extended=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'extend_90':
|
|
|
|
|
$count = $this->licenseManager->bulkExtend($licenseIds, 90);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_extended=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'extend_365':
|
|
|
|
|
$count = $this->licenseManager->bulkExtend($licenseIds, 365);
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses&bulk_extended=' . $count));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
wp_redirect(admin_url('admin.php?page=wc-licenses'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render license dashboard page
|
|
|
|
|
*/
|
|
|
|
|
public function renderDashboardPage(): void
|
|
|
|
|
{
|
|
|
|
|
$stats = $this->licenseManager->getStatistics();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
echo $this->twig->render('admin/dashboard.html.twig', [
|
|
|
|
|
'stats' => $stats,
|
|
|
|
|
'admin_url' => admin_url('admin.php'),
|
|
|
|
|
]);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
// Fallback to PHP template
|
|
|
|
|
$this->renderDashboardPageFallback($stats);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fallback render for dashboard page
|
|
|
|
|
*/
|
|
|
|
|
private function renderDashboardPageFallback(array $stats): void
|
|
|
|
|
{
|
|
|
|
|
?>
|
|
|
|
|
<div class="wrap wclp-dashboard">
|
|
|
|
|
<h1><?php esc_html_e('License Dashboard', 'wc-licensed-product'); ?></h1>
|
|
|
|
|
|
|
|
|
|
<div class="wclp-dashboard-stats">
|
|
|
|
|
<div class="wclp-stat-cards">
|
|
|
|
|
<div class="wclp-stat-card wclp-stat-total">
|
|
|
|
|
<div class="wclp-stat-icon"><span class="dashicons dashicons-admin-network"></span></div>
|
|
|
|
|
<div class="wclp-stat-content">
|
|
|
|
|
<span class="wclp-stat-number"><?php echo esc_html($stats['total']); ?></span>
|
|
|
|
|
<span class="wclp-stat-label"><?php esc_html_e('Total Licenses', 'wc-licensed-product'); ?></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wclp-stat-card wclp-stat-active">
|
|
|
|
|
<div class="wclp-stat-icon"><span class="dashicons dashicons-yes-alt"></span></div>
|
|
|
|
|
<div class="wclp-stat-content">
|
|
|
|
|
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status'][License::STATUS_ACTIVE]); ?></span>
|
|
|
|
|
<span class="wclp-stat-label"><?php esc_html_e('Active', 'wc-licensed-product'); ?></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wclp-stat-card wclp-stat-inactive">
|
|
|
|
|
<div class="wclp-stat-icon"><span class="dashicons dashicons-marker"></span></div>
|
|
|
|
|
<div class="wclp-stat-content">
|
|
|
|
|
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status'][License::STATUS_INACTIVE]); ?></span>
|
|
|
|
|
<span class="wclp-stat-label"><?php esc_html_e('Inactive', 'wc-licensed-product'); ?></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wclp-stat-card wclp-stat-expired">
|
|
|
|
|
<div class="wclp-stat-icon"><span class="dashicons dashicons-calendar-alt"></span></div>
|
|
|
|
|
<div class="wclp-stat-content">
|
|
|
|
|
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status'][License::STATUS_EXPIRED]); ?></span>
|
|
|
|
|
<span class="wclp-stat-label"><?php esc_html_e('Expired', 'wc-licensed-product'); ?></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wclp-stat-card wclp-stat-revoked">
|
|
|
|
|
<div class="wclp-stat-icon"><span class="dashicons dashicons-dismiss"></span></div>
|
|
|
|
|
<div class="wclp-stat-content">
|
|
|
|
|
<span class="wclp-stat-number"><?php echo esc_html($stats['by_status'][License::STATUS_REVOKED]); ?></span>
|
|
|
|
|
<span class="wclp-stat-label"><?php esc_html_e('Revoked', 'wc-licensed-product'); ?></span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<?php if ($stats['expiring_soon'] > 0): ?>
|
|
|
|
|
<div class="notice notice-warning">
|
|
|
|
|
<p>
|
|
|
|
|
<span class="dashicons dashicons-warning"></span>
|
|
|
|
|
<strong><?php esc_html_e('Attention:', 'wc-licensed-product'); ?></strong>
|
|
|
|
|
<?php
|
|
|
|
|
printf(
|
|
|
|
|
/* translators: %d: number of licenses expiring */
|
|
|
|
|
_n(
|
|
|
|
|
'%d license is expiring within the next 30 days.',
|
|
|
|
|
'%d licenses are expiring within the next 30 days.',
|
|
|
|
|
$stats['expiring_soon'],
|
|
|
|
|
'wc-licensed-product'
|
|
|
|
|
),
|
|
|
|
|
$stats['expiring_soon']
|
|
|
|
|
);
|
|
|
|
|
?>
|
|
|
|
|
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>"><?php esc_html_e('View Licenses', 'wc-licensed-product'); ?></a>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
<div class="wclp-dashboard-actions">
|
|
|
|
|
<h2><?php esc_html_e('Quick Actions', 'wc-licensed-product'); ?></h2>
|
|
|
|
|
<div class="wclp-action-buttons">
|
|
|
|
|
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>" class="button button-primary">
|
|
|
|
|
<span class="dashicons dashicons-admin-network"></span>
|
|
|
|
|
<?php esc_html_e('Manage Licenses', 'wc-licensed-product'); ?>
|
|
|
|
|
</a>
|
|
|
|
|
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses&action=export_csv')); ?>" class="button">
|
|
|
|
|
<span class="dashicons dashicons-download"></span>
|
|
|
|
|
<?php esc_html_e('Export to CSV', 'wc-licensed-product'); ?>
|
|
|
|
|
</a>
|
|
|
|
|
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-settings&tab=licensed_product')); ?>" class="button">
|
|
|
|
|
<span class="dashicons dashicons-admin-generic"></span>
|
|
|
|
|
<?php esc_html_e('Settings', 'wc-licensed-product'); ?>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<?php
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 18:55:18 +01:00
|
|
|
/**
|
|
|
|
|
* Render licenses admin page
|
|
|
|
|
*/
|
|
|
|
|
public function renderLicensesPage(): void
|
|
|
|
|
{
|
2026-01-21 20:32:35 +01:00
|
|
|
// Check if showing import page
|
|
|
|
|
if (isset($_GET['action']) && $_GET['action'] === 'import_csv') {
|
|
|
|
|
$this->renderImportPage();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 18:55:18 +01:00
|
|
|
$page = isset($_GET['paged']) ? absint($_GET['paged']) : 1;
|
|
|
|
|
$perPage = 20;
|
|
|
|
|
|
2026-01-21 20:32:35 +01:00
|
|
|
// Build filters from query params
|
|
|
|
|
$filters = [];
|
|
|
|
|
if (!empty($_GET['s'])) {
|
|
|
|
|
$filters['search'] = sanitize_text_field($_GET['s']);
|
|
|
|
|
}
|
|
|
|
|
if (!empty($_GET['status']) && $_GET['status'] !== 'all') {
|
|
|
|
|
$filters['status'] = sanitize_text_field($_GET['status']);
|
|
|
|
|
}
|
|
|
|
|
if (!empty($_GET['product_id'])) {
|
|
|
|
|
$filters['product_id'] = absint($_GET['product_id']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$licenses = $this->licenseManager->getAllLicenses($page, $perPage, $filters);
|
|
|
|
|
$totalLicenses = $this->licenseManager->getLicenseCount($filters);
|
|
|
|
|
$totalPages = (int) ceil($totalLicenses / $perPage);
|
|
|
|
|
|
|
|
|
|
// Get products for filter dropdown
|
|
|
|
|
$licensedProducts = $this->licenseManager->getLicensedProducts();
|
2026-01-21 18:55:18 +01:00
|
|
|
|
|
|
|
|
// Enrich licenses with related data
|
|
|
|
|
$enrichedLicenses = [];
|
|
|
|
|
foreach ($licenses as $license) {
|
|
|
|
|
$product = wc_get_product($license->getProductId());
|
|
|
|
|
$order = wc_get_order($license->getOrderId());
|
|
|
|
|
$customer = get_userdata($license->getCustomerId());
|
|
|
|
|
|
|
|
|
|
$enrichedLicenses[] = [
|
|
|
|
|
'license' => $license,
|
|
|
|
|
'product_name' => $product ? $product->get_name() : __('Unknown', 'wc-licensed-product'),
|
|
|
|
|
'product_edit_url' => $product ? get_edit_post_link($product->get_id()) : '',
|
|
|
|
|
'order_number' => $order ? $order->get_order_number() : '',
|
|
|
|
|
'order_edit_url' => $order ? $order->get_edit_order_url() : '',
|
|
|
|
|
'customer_name' => $customer ? $customer->display_name : __('Guest', 'wc-licensed-product'),
|
|
|
|
|
'customer_email' => $customer ? $customer->user_email : '',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 20:32:35 +01:00
|
|
|
// Add URL helper functions to Twig
|
|
|
|
|
$this->twig->addFunction(new \Twig\TwigFunction('extend_url', function (int $licenseId, int $days = 30): string {
|
|
|
|
|
return wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=extend&license_id=' . $licenseId . '&days=' . $days),
|
|
|
|
|
'extend_license'
|
|
|
|
|
);
|
|
|
|
|
}));
|
|
|
|
|
$this->twig->addFunction(new \Twig\TwigFunction('lifetime_url', function (int $licenseId): string {
|
|
|
|
|
return wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=lifetime&license_id=' . $licenseId),
|
|
|
|
|
'lifetime_license'
|
|
|
|
|
);
|
|
|
|
|
}));
|
|
|
|
|
$this->twig->addFunction(new \Twig\TwigFunction('revoke_url', function (int $licenseId): string {
|
|
|
|
|
return wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=revoke&license_id=' . $licenseId),
|
|
|
|
|
'revoke_license'
|
|
|
|
|
);
|
|
|
|
|
}));
|
|
|
|
|
$this->twig->addFunction(new \Twig\TwigFunction('delete_url', function (int $licenseId): string {
|
|
|
|
|
return wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=delete&license_id=' . $licenseId),
|
|
|
|
|
'delete_license'
|
|
|
|
|
);
|
|
|
|
|
}));
|
|
|
|
|
$this->twig->addFunction(new \Twig\TwigFunction('transfer_nonce', function (): string {
|
|
|
|
|
return wp_create_nonce('transfer_license');
|
|
|
|
|
}));
|
|
|
|
|
|
2026-01-21 18:55:18 +01:00
|
|
|
try {
|
|
|
|
|
echo $this->twig->render('admin/licenses.html.twig', [
|
|
|
|
|
'licenses' => $enrichedLicenses,
|
|
|
|
|
'current_page' => $page,
|
|
|
|
|
'total_pages' => $totalPages,
|
|
|
|
|
'total_licenses' => $totalLicenses,
|
|
|
|
|
'admin_url' => admin_url('admin.php?page=wc-licenses'),
|
|
|
|
|
'notices' => $this->getNotices(),
|
2026-01-21 20:32:35 +01:00
|
|
|
'filters' => $filters,
|
|
|
|
|
'products' => $licensedProducts,
|
2026-01-21 18:55:18 +01:00
|
|
|
]);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
// Fallback to PHP template
|
|
|
|
|
$this->renderLicensesPageFallback($enrichedLicenses, $page, $totalPages, $totalLicenses);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get admin notices
|
|
|
|
|
*/
|
|
|
|
|
private function getNotices(): array
|
|
|
|
|
{
|
|
|
|
|
$notices = [];
|
|
|
|
|
|
|
|
|
|
if (isset($_GET['updated'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License updated successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['deleted'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License deleted successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['revoked'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License revoked successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
2026-01-21 20:32:35 +01:00
|
|
|
if (isset($_GET['extended'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License extended successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['lifetime'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License set to lifetime successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['bulk_activated'])) {
|
|
|
|
|
$count = absint($_GET['bulk_activated']);
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => sprintf(
|
|
|
|
|
/* translators: %d: number of licenses */
|
|
|
|
|
_n('%d license activated.', '%d licenses activated.', $count, 'wc-licensed-product'),
|
|
|
|
|
$count
|
|
|
|
|
)];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['bulk_deactivated'])) {
|
|
|
|
|
$count = absint($_GET['bulk_deactivated']);
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => sprintf(
|
|
|
|
|
/* translators: %d: number of licenses */
|
|
|
|
|
_n('%d license deactivated.', '%d licenses deactivated.', $count, 'wc-licensed-product'),
|
|
|
|
|
$count
|
|
|
|
|
)];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['bulk_revoked'])) {
|
|
|
|
|
$count = absint($_GET['bulk_revoked']);
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => sprintf(
|
|
|
|
|
/* translators: %d: number of licenses */
|
|
|
|
|
_n('%d license revoked.', '%d licenses revoked.', $count, 'wc-licensed-product'),
|
|
|
|
|
$count
|
|
|
|
|
)];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['bulk_deleted'])) {
|
|
|
|
|
$count = absint($_GET['bulk_deleted']);
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => sprintf(
|
|
|
|
|
/* translators: %d: number of licenses */
|
|
|
|
|
_n('%d license deleted.', '%d licenses deleted.', $count, 'wc-licensed-product'),
|
|
|
|
|
$count
|
|
|
|
|
)];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['bulk_extended'])) {
|
|
|
|
|
$count = absint($_GET['bulk_extended']);
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => sprintf(
|
|
|
|
|
/* translators: %d: number of licenses */
|
|
|
|
|
_n('%d license extended.', '%d licenses extended.', $count, 'wc-licensed-product'),
|
|
|
|
|
$count
|
|
|
|
|
)];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['transferred'])) {
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => __('License transferred to new domain successfully.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['transfer_failed'])) {
|
|
|
|
|
$notices[] = ['type' => 'error', 'message' => __('Failed to transfer license. The license may be revoked or invalid.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['export_empty'])) {
|
|
|
|
|
$notices[] = ['type' => 'warning', 'message' => __('No licenses to export.', 'wc-licensed-product')];
|
|
|
|
|
}
|
|
|
|
|
if (isset($_GET['imported'])) {
|
|
|
|
|
$imported = absint($_GET['imported']);
|
|
|
|
|
$updated = absint($_GET['updated'] ?? 0);
|
|
|
|
|
$skipped = absint($_GET['skipped'] ?? 0);
|
|
|
|
|
$errors = absint($_GET['import_errors'] ?? 0);
|
|
|
|
|
|
|
|
|
|
$message = sprintf(
|
|
|
|
|
/* translators: %d: number of licenses imported */
|
|
|
|
|
_n('%d license imported.', '%d licenses imported.', $imported, 'wc-licensed-product'),
|
|
|
|
|
$imported
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($updated > 0) {
|
|
|
|
|
$message .= ' ' . sprintf(
|
|
|
|
|
/* translators: %d: number of licenses updated */
|
|
|
|
|
_n('%d updated.', '%d updated.', $updated, 'wc-licensed-product'),
|
|
|
|
|
$updated
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($skipped > 0) {
|
|
|
|
|
$message .= ' ' . sprintf(
|
|
|
|
|
/* translators: %d: number of licenses skipped */
|
|
|
|
|
_n('%d skipped.', '%d skipped.', $skipped, 'wc-licensed-product'),
|
|
|
|
|
$skipped
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($errors > 0) {
|
|
|
|
|
$message .= ' ' . sprintf(
|
|
|
|
|
/* translators: %d: number of errors */
|
|
|
|
|
_n('%d error.', '%d errors.', $errors, 'wc-licensed-product'),
|
|
|
|
|
$errors
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$notices[] = ['type' => 'success', 'message' => $message];
|
|
|
|
|
}
|
2026-01-21 18:55:18 +01:00
|
|
|
|
|
|
|
|
return $notices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fallback render for licenses page
|
|
|
|
|
*/
|
|
|
|
|
private function renderLicensesPageFallback(array $enrichedLicenses, int $page, int $totalPages, int $totalLicenses): void
|
|
|
|
|
{
|
|
|
|
|
?>
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
<h1><?php esc_html_e('Licenses', 'wc-licensed-product'); ?></h1>
|
|
|
|
|
|
|
|
|
|
<?php foreach ($this->getNotices() as $notice): ?>
|
|
|
|
|
<div class="notice notice-<?php echo esc_attr($notice['type']); ?> is-dismissible">
|
|
|
|
|
<p><?php echo esc_html($notice['message']); ?></p>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
|
2026-01-21 20:32:35 +01:00
|
|
|
<p class="description">
|
|
|
|
|
<?php esc_html_e('Total licenses:', 'wc-licensed-product'); ?> <strong><?php echo esc_html($totalLicenses); ?></strong>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<form method="post" action="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>">
|
|
|
|
|
<?php wp_nonce_field('bulk_license_action'); ?>
|
|
|
|
|
|
|
|
|
|
<div class="tablenav top">
|
|
|
|
|
<div class="alignleft actions bulkactions">
|
|
|
|
|
<select name="bulk_action" id="bulk-action-selector">
|
|
|
|
|
<option value=""><?php esc_html_e('Bulk Actions', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="activate"><?php esc_html_e('Activate', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="deactivate"><?php esc_html_e('Deactivate', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="revoke"><?php esc_html_e('Revoke', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_30"><?php esc_html_e('Extend 30 days', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_90"><?php esc_html_e('Extend 90 days', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_365"><?php esc_html_e('Extend 1 year', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="delete"><?php esc_html_e('Delete', 'wc-licensed-product'); ?></option>
|
|
|
|
|
</select>
|
|
|
|
|
<input type="submit" class="button action" value="<?php esc_attr_e('Apply', 'wc-licensed-product'); ?>">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<table class="wp-list-table widefat fixed striped licenses-table">
|
|
|
|
|
<thead>
|
2026-01-21 18:55:18 +01:00
|
|
|
<tr>
|
2026-01-21 20:32:35 +01:00
|
|
|
<td class="manage-column column-cb check-column">
|
|
|
|
|
<input type="checkbox" id="cb-select-all-1">
|
|
|
|
|
</td>
|
|
|
|
|
<th><?php esc_html_e('License Key', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Product', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Customer', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Domain', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Status', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Expires', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Actions', 'wc-licensed-product'); ?></th>
|
2026-01-21 18:55:18 +01:00
|
|
|
</tr>
|
2026-01-21 20:32:35 +01:00
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
<?php if (empty($enrichedLicenses)): ?>
|
2026-01-21 18:55:18 +01:00
|
|
|
<tr>
|
2026-01-21 20:32:35 +01:00
|
|
|
<td colspan="8"><?php esc_html_e('No licenses found.', 'wc-licensed-product'); ?></td>
|
2026-01-21 18:55:18 +01:00
|
|
|
</tr>
|
2026-01-21 20:32:35 +01:00
|
|
|
<?php else: ?>
|
|
|
|
|
<?php foreach ($enrichedLicenses as $item): ?>
|
|
|
|
|
<tr>
|
|
|
|
|
<th scope="row" class="check-column">
|
|
|
|
|
<input type="checkbox" name="license_ids[]" value="<?php echo esc_attr($item['license']->getId()); ?>">
|
|
|
|
|
</th>
|
|
|
|
|
<td><code><?php echo esc_html($item['license']->getLicenseKey()); ?></code></td>
|
|
|
|
|
<td>
|
|
|
|
|
<?php if ($item['product_edit_url']): ?>
|
|
|
|
|
<a href="<?php echo esc_url($item['product_edit_url']); ?>">
|
|
|
|
|
<?php echo esc_html($item['product_name']); ?>
|
|
|
|
|
</a>
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
<?php echo esc_html($item['product_name']); ?>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<?php echo esc_html($item['customer_name']); ?>
|
|
|
|
|
<?php if ($item['customer_email']): ?>
|
|
|
|
|
<br><small><?php echo esc_html($item['customer_email']); ?></small>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</td>
|
|
|
|
|
<td><?php echo esc_html($item['license']->getDomain()); ?></td>
|
|
|
|
|
<td>
|
|
|
|
|
<span class="license-status license-status-<?php echo esc_attr($item['license']->getStatus()); ?>">
|
|
|
|
|
<?php echo esc_html(ucfirst($item['license']->getStatus())); ?>
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td>
|
|
|
|
|
<?php
|
|
|
|
|
$expiresAt = $item['license']->getExpiresAt();
|
|
|
|
|
if ($expiresAt) {
|
|
|
|
|
echo esc_html($expiresAt->format(get_option('date_format')));
|
|
|
|
|
} else {
|
|
|
|
|
echo '<span class="license-lifetime">' . esc_html__('Lifetime', 'wc-licensed-product') . '</span>';
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="license-actions">
|
|
|
|
|
<div class="row-actions">
|
|
|
|
|
<?php if ($item['license']->getStatus() !== License::STATUS_REVOKED): ?>
|
|
|
|
|
<span class="extend">
|
|
|
|
|
<a href="<?php echo esc_url(wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=extend&license_id=' . $item['license']->getId() . '&days=30'),
|
|
|
|
|
'extend_license'
|
|
|
|
|
)); ?>" title="<?php esc_attr_e('Extend by 30 days', 'wc-licensed-product'); ?>">+30d</a> |
|
|
|
|
|
</span>
|
|
|
|
|
<span class="lifetime">
|
|
|
|
|
<a href="<?php echo esc_url(wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=lifetime&license_id=' . $item['license']->getId()),
|
|
|
|
|
'lifetime_license'
|
|
|
|
|
)); ?>" title="<?php esc_attr_e('Set to lifetime', 'wc-licensed-product'); ?>">∞</a> |
|
|
|
|
|
</span>
|
|
|
|
|
<span class="revoke">
|
|
|
|
|
<a href="<?php echo esc_url(wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=revoke&license_id=' . $item['license']->getId()),
|
|
|
|
|
'revoke_license'
|
|
|
|
|
)); ?>" onclick="return confirm('<?php esc_attr_e('Are you sure?', 'wc-licensed-product'); ?>')">
|
|
|
|
|
<?php esc_html_e('Revoke', 'wc-licensed-product'); ?>
|
|
|
|
|
</a> |
|
|
|
|
|
</span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<span class="delete">
|
|
|
|
|
<a href="<?php echo esc_url(wp_nonce_url(
|
|
|
|
|
admin_url('admin.php?page=wc-licenses&action=delete&license_id=' . $item['license']->getId()),
|
|
|
|
|
'delete_license'
|
|
|
|
|
)); ?>" class="submitdelete" onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this license?', 'wc-licensed-product'); ?>')">
|
|
|
|
|
<?php esc_html_e('Delete', 'wc-licensed-product'); ?>
|
|
|
|
|
</a>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</tbody>
|
|
|
|
|
<tfoot>
|
|
|
|
|
<tr>
|
|
|
|
|
<td class="manage-column column-cb check-column">
|
|
|
|
|
<input type="checkbox" id="cb-select-all-2">
|
|
|
|
|
</td>
|
|
|
|
|
<th><?php esc_html_e('License Key', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Product', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Customer', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Domain', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Status', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Expires', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<th><?php esc_html_e('Actions', 'wc-licensed-product'); ?></th>
|
|
|
|
|
</tr>
|
|
|
|
|
</tfoot>
|
|
|
|
|
</table>
|
2026-01-21 18:55:18 +01:00
|
|
|
|
|
|
|
|
<div class="tablenav bottom">
|
2026-01-21 20:32:35 +01:00
|
|
|
<div class="alignleft actions bulkactions">
|
|
|
|
|
<select name="bulk_action_2" id="bulk-action-selector-bottom">
|
|
|
|
|
<option value=""><?php esc_html_e('Bulk Actions', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="activate"><?php esc_html_e('Activate', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="deactivate"><?php esc_html_e('Deactivate', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="revoke"><?php esc_html_e('Revoke', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_30"><?php esc_html_e('Extend 30 days', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_90"><?php esc_html_e('Extend 90 days', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="extend_365"><?php esc_html_e('Extend 1 year', 'wc-licensed-product'); ?></option>
|
|
|
|
|
<option value="delete"><?php esc_html_e('Delete', 'wc-licensed-product'); ?></option>
|
|
|
|
|
</select>
|
|
|
|
|
<input type="submit" class="button action" value="<?php esc_attr_e('Apply', 'wc-licensed-product'); ?>">
|
|
|
|
|
</div>
|
|
|
|
|
<?php if ($totalPages > 1): ?>
|
|
|
|
|
<div class="tablenav-pages">
|
|
|
|
|
<?php
|
|
|
|
|
echo paginate_links([
|
|
|
|
|
'base' => admin_url('admin.php?page=wc-licenses&paged=%#%'),
|
|
|
|
|
'format' => '',
|
|
|
|
|
'current' => $page,
|
|
|
|
|
'total' => $totalPages,
|
|
|
|
|
]);
|
|
|
|
|
?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
(function($) {
|
|
|
|
|
$('#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);
|
|
|
|
|
});
|
|
|
|
|
$('#bulk-action-selector, #bulk-action-selector-bottom').on('change', function() {
|
|
|
|
|
$('#bulk-action-selector, #bulk-action-selector-bottom').val($(this).val());
|
|
|
|
|
});
|
|
|
|
|
$('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);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
})(jQuery);
|
|
|
|
|
</script>
|
|
|
|
|
</div>
|
|
|
|
|
<?php
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render the CSV import page
|
|
|
|
|
*/
|
|
|
|
|
private function renderImportPage(): void
|
|
|
|
|
{
|
|
|
|
|
$importError = $_GET['import_error'] ?? '';
|
|
|
|
|
?>
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
<h1>
|
|
|
|
|
<?php esc_html_e('Import Licenses', 'wc-licensed-product'); ?>
|
|
|
|
|
<a href="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>" class="page-title-action">
|
|
|
|
|
<?php esc_html_e('Back to Licenses', 'wc-licensed-product'); ?>
|
|
|
|
|
</a>
|
|
|
|
|
</h1>
|
|
|
|
|
|
|
|
|
|
<?php if ($importError): ?>
|
|
|
|
|
<div class="notice notice-error">
|
|
|
|
|
<p>
|
2026-01-21 18:55:18 +01:00
|
|
|
<?php
|
2026-01-21 20:32:35 +01:00
|
|
|
switch ($importError) {
|
|
|
|
|
case 'upload':
|
|
|
|
|
esc_html_e('Error uploading file. Please try again.', 'wc-licensed-product');
|
|
|
|
|
break;
|
|
|
|
|
case 'filetype':
|
|
|
|
|
esc_html_e('Invalid file type. Please upload a CSV file.', 'wc-licensed-product');
|
|
|
|
|
break;
|
|
|
|
|
case 'read':
|
|
|
|
|
esc_html_e('Error reading file. Please check the file format.', 'wc-licensed-product');
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
esc_html_e('An error occurred during import.', 'wc-licensed-product');
|
|
|
|
|
}
|
2026-01-21 18:55:18 +01:00
|
|
|
?>
|
2026-01-21 20:32:35 +01:00
|
|
|
</p>
|
2026-01-21 18:55:18 +01:00
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
2026-01-21 20:32:35 +01:00
|
|
|
|
|
|
|
|
<div class="card" style="max-width: 800px; padding: 20px;">
|
|
|
|
|
<h2><?php esc_html_e('Import Licenses from CSV', 'wc-licensed-product'); ?></h2>
|
|
|
|
|
|
|
|
|
|
<p class="description">
|
|
|
|
|
<?php esc_html_e('Upload a CSV file to import licenses. You can use the exported CSV format or a simplified format.', 'wc-licensed-product'); ?>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h3><?php esc_html_e('CSV Format', 'wc-licensed-product'); ?></h3>
|
|
|
|
|
<p class="description">
|
|
|
|
|
<?php esc_html_e('The CSV file should contain the following columns:', 'wc-licensed-product'); ?>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<div style="background: #f8f9fa; padding: 15px; border-radius: 4px; margin: 15px 0;">
|
|
|
|
|
<p><strong><?php esc_html_e('Full Format (from Export):', 'wc-licensed-product'); ?></strong></p>
|
|
|
|
|
<code style="display: block; margin-bottom: 10px; font-size: 12px;">ID, License Key, Product, Product ID, Order ID, Order Number, Customer, Customer Email, Customer ID, Domain, Status, Activations, Max Activations, Expires At, Created At, Updated At</code>
|
|
|
|
|
|
|
|
|
|
<p><strong><?php esc_html_e('Simplified Format:', 'wc-licensed-product'); ?></strong></p>
|
|
|
|
|
<code style="display: block; font-size: 12px;">License Key, Product ID, Customer ID, Domain, Status, Max Activations, Expires At</code>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<p class="description">
|
|
|
|
|
<strong><?php esc_html_e('Notes:', 'wc-licensed-product'); ?></strong><br>
|
|
|
|
|
- <?php esc_html_e('Leave License Key empty to auto-generate.', 'wc-licensed-product'); ?><br>
|
|
|
|
|
- <?php esc_html_e('Status can be: active, inactive, expired, revoked (defaults to active).', 'wc-licensed-product'); ?><br>
|
|
|
|
|
- <?php esc_html_e('Expires At should be in YYYY-MM-DD format or "Lifetime".', 'wc-licensed-product'); ?>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<hr style="margin: 20px 0;">
|
|
|
|
|
|
|
|
|
|
<form method="post" action="<?php echo esc_url(admin_url('admin.php?page=wc-licenses')); ?>" enctype="multipart/form-data">
|
|
|
|
|
<?php wp_nonce_field('import_licenses_csv'); ?>
|
|
|
|
|
<input type="hidden" name="action" value="process_import_csv">
|
|
|
|
|
|
|
|
|
|
<table class="form-table">
|
|
|
|
|
<tr>
|
|
|
|
|
<th scope="row">
|
|
|
|
|
<label for="import_file"><?php esc_html_e('CSV File', 'wc-licensed-product'); ?></label>
|
|
|
|
|
</th>
|
|
|
|
|
<td>
|
|
|
|
|
<input type="file" name="import_file" id="import_file" accept=".csv" required>
|
|
|
|
|
<p class="description"><?php esc_html_e('Select a CSV file to import.', 'wc-licensed-product'); ?></p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<th scope="row"><?php esc_html_e('Options', 'wc-licensed-product'); ?></th>
|
|
|
|
|
<td>
|
|
|
|
|
<label>
|
|
|
|
|
<input type="checkbox" name="skip_first_row" value="1" checked>
|
|
|
|
|
<?php esc_html_e('Skip first row (header row)', 'wc-licensed-product'); ?>
|
|
|
|
|
</label>
|
|
|
|
|
<br><br>
|
|
|
|
|
<label>
|
|
|
|
|
<input type="checkbox" name="update_existing" value="1">
|
|
|
|
|
<?php esc_html_e('Update existing licenses (by license key)', 'wc-licensed-product'); ?>
|
|
|
|
|
</label>
|
|
|
|
|
<p class="description">
|
|
|
|
|
<?php esc_html_e('If enabled, licenses with matching keys will be updated instead of skipped.', 'wc-licensed-product'); ?>
|
|
|
|
|
</p>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
<p class="submit">
|
|
|
|
|
<button type="submit" class="button button-primary">
|
|
|
|
|
<span class="dashicons dashicons-upload" style="vertical-align: middle;"></span>
|
|
|
|
|
<?php esc_html_e('Import Licenses', 'wc-licensed-product'); ?>
|
|
|
|
|
</button>
|
|
|
|
|
</p>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
2026-01-21 18:55:18 +01:00
|
|
|
</div>
|
|
|
|
|
<?php
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add license column to orders list
|
|
|
|
|
*/
|
|
|
|
|
public function addOrdersLicenseColumn(array $columns): array
|
|
|
|
|
{
|
|
|
|
|
$newColumns = [];
|
|
|
|
|
foreach ($columns as $key => $value) {
|
|
|
|
|
$newColumns[$key] = $value;
|
|
|
|
|
if ($key === 'order_status') {
|
|
|
|
|
$newColumns['license'] = __('License', 'wc-licensed-product');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $newColumns;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display license column content
|
|
|
|
|
*/
|
|
|
|
|
public function displayOrdersLicenseColumn(string $column, int $postId): void
|
|
|
|
|
{
|
|
|
|
|
if ($column !== 'license') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$order = wc_get_order($postId);
|
|
|
|
|
$this->outputLicenseColumnContent($order);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display license column content (HPOS)
|
|
|
|
|
*/
|
|
|
|
|
public function displayOrdersLicenseColumnHpos(string $column, \WC_Order $order): void
|
|
|
|
|
{
|
|
|
|
|
if ($column !== 'license') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->outputLicenseColumnContent($order);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Output license column content
|
|
|
|
|
*/
|
|
|
|
|
private function outputLicenseColumnContent(?\WC_Order $order): void
|
|
|
|
|
{
|
|
|
|
|
if (!$order) {
|
|
|
|
|
echo '—';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$hasLicensedProduct = false;
|
|
|
|
|
foreach ($order->get_items() as $item) {
|
|
|
|
|
$product = $item->get_product();
|
|
|
|
|
if ($product && $product->is_type('licensed')) {
|
|
|
|
|
$hasLicensedProduct = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$hasLicensedProduct) {
|
|
|
|
|
echo '—';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domain = $order->get_meta('_licensed_product_domain');
|
|
|
|
|
if ($domain) {
|
|
|
|
|
echo '<span class="dashicons dashicons-admin-network"></span> ' . esc_html($domain);
|
|
|
|
|
} else {
|
|
|
|
|
echo '<span class="dashicons dashicons-warning" title="' . esc_attr__('No domain specified', 'wc-licensed-product') . '"></span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|