twig = $twig; $this->licenseManager = $licenseManager; $this->versionManager = $versionManager; $this->downloadController = $downloadController; $this->registerHooks(); } /** * Register WordPress hooks */ private function registerHooks(): void { // Add licenses endpoint add_action('init', [$this, 'addLicensesEndpoint']); // Add licenses menu item add_filter('woocommerce_account_menu_items', [$this, 'addLicensesMenuItem']); // Add licenses endpoint content add_action('woocommerce_account_licenses_endpoint', [$this, 'displayLicensesContent']); // Enqueue frontend styles and scripts add_action('wp_enqueue_scripts', [$this, 'enqueueAssets']); // AJAX handler for license transfer request add_action('wp_ajax_wclp_customer_transfer_license', [$this, 'handleTransferRequest']); } /** * Add licenses endpoint for My Account */ public function addLicensesEndpoint(): void { add_rewrite_endpoint('licenses', EP_ROOT | EP_PAGES); } /** * Add licenses menu item to My Account navigation */ public function addLicensesMenuItem(array $items): array { // Insert licenses after orders $newItems = []; foreach ($items as $key => $value) { $newItems[$key] = $value; if ($key === 'orders') { $newItems['licenses'] = __('Licenses', 'wc-licensed-product'); } } return $newItems; } /** * Display licenses content in My Account */ public function displayLicensesContent(): void { $customerId = get_current_user_id(); if (!$customerId) { echo '
' . esc_html__('Please log in to view your licenses.', 'wc-licensed-product') . '
'; return; } $licenses = $this->licenseManager->getLicensesByCustomer($customerId); // Enrich licenses with product data and downloads $enrichedLicenses = []; foreach ($licenses as $license) { $product = wc_get_product($license->getProductId()); $order = wc_get_order($license->getOrderId()); // Get available downloads for this license $downloads = []; if ($license->getStatus() === 'active') { $versions = $this->versionManager->getVersionsByProduct($license->getProductId()); foreach ($versions as $version) { if ($version->isActive() && ($version->getAttachmentId() || $version->getDownloadUrl())) { $downloads[] = [ 'version' => $version->getVersion(), 'version_id' => $version->getId(), 'filename' => $version->getDownloadFilename(), 'download_url' => $this->downloadController->generateDownloadUrl( $license->getId(), $version->getId() ), 'release_notes' => $version->getReleaseNotes(), 'released_at' => $version->getReleasedAt()->format(get_option('date_format')), ]; } } } $enrichedLicenses[] = [ 'license' => $license, 'product_name' => $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'), 'product_url' => $product ? $product->get_permalink() : '', 'order_number' => $order ? $order->get_order_number() : '', 'order_url' => $order ? $order->get_view_order_url() : '', 'downloads' => $downloads, ]; } try { echo $this->twig->render('frontend/licenses.html.twig', [ 'licenses' => $enrichedLicenses, 'has_licenses' => !empty($enrichedLicenses), ]); } catch (\Exception $e) { // Fallback to PHP template if Twig fails $this->displayLicensesFallback($enrichedLicenses); } } /** * Fallback display method if Twig is unavailable */ private function displayLicensesFallback(array $enrichedLicenses): void { if (empty($enrichedLicenses)) { echo '' . esc_html__('You have no licenses yet.', 'wc-licensed-product') . '
'; return; } ?> admin_url('admin-ajax.php'), 'transferNonce' => wp_create_nonce('wclp_customer_transfer'), 'strings' => [ 'copied' => __('Copied!', 'wc-licensed-product'), 'copyFailed' => __('Copy failed', 'wc-licensed-product'), 'transferSuccess' => __('License transferred successfully!', 'wc-licensed-product'), 'transferError' => __('Transfer failed. Please try again.', 'wc-licensed-product'), 'transferConfirm' => __('Are you sure you want to transfer this license to a new domain? This action cannot be undone.', 'wc-licensed-product'), 'invalidDomain' => __('Please enter a valid domain.', 'wc-licensed-product'), ], ]); } /** * Handle AJAX license transfer request from customer */ public function handleTransferRequest(): void { // Verify nonce if (!check_ajax_referer('wclp_customer_transfer', 'nonce', false)) { wp_send_json_error(['message' => __('Security check failed.', 'wc-licensed-product')], 403); } // Verify user is logged in $customerId = get_current_user_id(); if (!$customerId) { wp_send_json_error(['message' => __('Please log in to transfer a license.', 'wc-licensed-product')], 401); } // Get and validate license ID $licenseId = isset($_POST['license_id']) ? absint($_POST['license_id']) : 0; if (!$licenseId) { wp_send_json_error(['message' => __('Invalid license.', 'wc-licensed-product')], 400); } // Get and validate new domain $newDomain = isset($_POST['new_domain']) ? sanitize_text_field($_POST['new_domain']) : ''; $newDomain = $this->normalizeDomain($newDomain); if (empty($newDomain)) { wp_send_json_error(['message' => __('Please enter a valid domain.', 'wc-licensed-product')], 400); } // Verify the license belongs to this customer $license = $this->licenseManager->getLicenseById($licenseId); if (!$license) { wp_send_json_error(['message' => __('License not found.', 'wc-licensed-product')], 404); } if ($license->getCustomerId() !== $customerId) { wp_send_json_error(['message' => __('You do not have permission to transfer this license.', 'wc-licensed-product')], 403); } // Check if license is in a transferable state if ($license->getStatus() === 'revoked') { wp_send_json_error(['message' => __('Revoked licenses cannot be transferred.', 'wc-licensed-product')], 400); } if ($license->getStatus() === 'expired') { wp_send_json_error(['message' => __('Expired licenses cannot be transferred.', 'wc-licensed-product')], 400); } // Check if domain is the same if ($license->getDomain() === $newDomain) { wp_send_json_error(['message' => __('The new domain is the same as the current domain.', 'wc-licensed-product')], 400); } // Perform the transfer $result = $this->licenseManager->transferLicense($licenseId, $newDomain); if ($result) { wp_send_json_success([ 'message' => __('License transferred successfully!', 'wc-licensed-product'), 'new_domain' => $newDomain, ]); } else { wp_send_json_error(['message' => __('Failed to transfer license. Please try again.', 'wc-licensed-product')], 500); } } /** * Normalize domain for comparison and storage */ private function normalizeDomain(string $domain): string { // Remove protocol if present $domain = preg_replace('#^https?://#i', '', $domain); // Remove www prefix $domain = preg_replace('#^www\.#i', '', $domain); // Remove trailing slash $domain = rtrim($domain, '/'); // Remove path if present $domain = explode('/', $domain)[0]; // Convert to lowercase $domain = strtolower($domain); // Basic validation - must contain at least one dot and valid characters if (!preg_match('/^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/', $domain)) { return ''; } return $domain; } }