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); } /** * 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'] ); } /** * Enqueue admin styles and scripts */ public function enqueueStyles(string $hook): void { if ($hook !== 'woocommerce_page_wc-licenses') { 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(); } } /** * 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; } } /** * Render licenses admin page */ public function renderLicensesPage(): void { $page = isset($_GET['paged']) ? absint($_GET['paged']) : 1; $perPage = 20; $licenses = $this->licenseManager->getAllLicenses($page, $perPage); $totalLicenses = $this->licenseManager->getLicenseCount(); $totalPages = ceil($totalLicenses / $perPage); // 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 : '', ]; } 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(), ]); } 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')]; } return $notices; } /** * Fallback render for licenses page */ private function renderLicensesPageFallback(array $enrichedLicenses, int $page, int $totalPages, int $totalLicenses): void { ?>

getNotices() as $notice): ?>

getLicenseKey()); ?>
getDomain()); ?> getStatus())); ?> getExpiresAt(); echo $expiresAt ? esc_html($expiresAt->format(get_option('date_format'))) : esc_html__('Never', 'wc-licensed-product'); ?> getStatus() !== License::STATUS_REVOKED): ?>
1): ?>
admin_url('admin.php?page=wc-licenses&paged=%#%'), 'format' => '', 'current' => $page, 'total' => $totalPages, ]); ?>
$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 ' ' . esc_html($domain); } else { echo ''; } } }