licenseManager = $licenseManager; $this->registerHooks(); } /** * Register WordPress hooks */ private function registerHooks(): void { // Add domain field to checkout add_action('woocommerce_after_order_notes', [$this, 'addDomainField']); // Validate domain field add_action('woocommerce_checkout_process', [$this, 'validateDomainField']); // Save domain field to order meta add_action('woocommerce_checkout_update_order_meta', [$this, 'saveDomainField']); // Display domain in order details (admin) add_action('woocommerce_admin_order_data_after_billing_address', [$this, 'displayDomainInAdmin']); // Display domain in order email add_action('woocommerce_email_after_order_table', [$this, 'displayDomainInEmail'], 10, 3); } /** * Check if cart contains licensed products */ private function cartHasLicensedProducts(): bool { return !empty($this->getLicensedProductsFromCart()); } /** * Get licensed products from cart with quantities * * @return array */ private function getLicensedProductsFromCart(): array { if (!WC()->cart) { return []; } $licensedProducts = []; foreach (WC()->cart->get_cart() as $cartItem) { $product = $cartItem['data']; if (!$product) { continue; } // Check for simple licensed products if ($product->is_type('licensed')) { $productId = $product->get_id(); $licensedProducts[$productId] = [ 'product_id' => $productId, 'variation_id' => 0, 'name' => $product->get_name(), 'quantity' => (int) $cartItem['quantity'], 'duration_label' => '', ]; continue; } // Check for variations of licensed-variable products if ($product->is_type('variation')) { $parentId = $product->get_parent_id(); $parent = wc_get_product($parentId); if ($parent && $parent->is_type('licensed-variable')) { $variationId = $product->get_id(); // Use combination key to allow same product with different variations $key = "{$parentId}_{$variationId}"; // Get duration label if it's a LicensedProductVariation $durationLabel = ''; if ($product instanceof LicensedProductVariation) { $durationLabel = $product->get_license_duration_label(); } else { // Try to instantiate as LicensedProductVariation $variation = new LicensedProductVariation($variationId); $durationLabel = $variation->get_license_duration_label(); } $licensedProducts[$key] = [ 'product_id' => $parentId, 'variation_id' => $variationId, 'name' => $product->get_name(), 'quantity' => (int) $cartItem['quantity'], 'duration_label' => $durationLabel, ]; } } } return $licensedProducts; } /** * Add domain fields to checkout form * Shows multiple domain fields if multi-domain is enabled, otherwise single field */ public function addDomainField(): void { $licensedProducts = $this->getLicensedProductsFromCart(); if (empty($licensedProducts)) { return; } // Check if multi-domain licensing is enabled if (SettingsController::isMultiDomainEnabled()) { $this->renderMultiDomainFields($licensedProducts); } else { $this->renderSingleDomainField(); } } /** * Render single domain field (legacy mode) */ private function renderSingleDomainField(): void { $savedValue = ''; // Check POST data first (validation failure case) if (isset($_POST['licensed_product_domain'])) { $savedValue = sanitize_text_field($_POST['licensed_product_domain']); } elseif (WC()->session) { $savedValue = WC()->session->get('licensed_product_domain', ''); } ?>

$productData): ?> 0 ? "{$productId}_{$variationId}" : $productId; ?>

(' . esc_html($durationLabel) . ')'; } if ($productData['quantity'] > 1) { printf(' ×%d', $productData['quantity']); } ?>

getSavedDomainValue($productId, $i, $variationId); ?>

0): ?>

0 ? "{$productId}_{$variationId}" : (string) $productId; // Check POST data first (validation failure case) if (isset($_POST['licensed_domains'][$fieldKey][$index])) { return sanitize_text_field($_POST['licensed_domains'][$fieldKey][$index]); } // Also try numeric key for backward compatibility if (isset($_POST['licensed_domains'][$productId][$index])) { return sanitize_text_field($_POST['licensed_domains'][$productId][$index]); } // Check session for blocks checkout if (WC()->session) { $sessionDomains = WC()->session->get('licensed_product_domains', []); foreach ($sessionDomains as $item) { $itemProductId = (int) ($item['product_id'] ?? 0); $itemVariationId = (int) ($item['variation_id'] ?? 0); // Match by product and variation if ($itemProductId === $productId && $itemVariationId === $variationId) { if (isset($item['domains'][$index])) { return $item['domains'][$index]; } } } } return ''; } /** * Validate domain fields during checkout */ public function validateDomainField(): void { $licensedProducts = $this->getLicensedProductsFromCart(); if (empty($licensedProducts)) { return; } // Check if multi-domain licensing is enabled if (SettingsController::isMultiDomainEnabled()) { $this->validateMultiDomainFields($licensedProducts); } else { $this->validateSingleDomainField(); } } /** * Validate single domain field */ private function validateSingleDomainField(): void { $domain = isset($_POST['licensed_product_domain']) ? sanitize_text_field($_POST['licensed_product_domain']) : ''; if (empty($domain)) { wc_add_notice(__('Please enter a domain for your license.', 'wc-licensed-product'), 'error'); return; } $normalizedDomain = $this->licenseManager->normalizeDomain($domain); if (!$this->isValidDomain($normalizedDomain)) { wc_add_notice(__('Please enter a valid domain for your license.', 'wc-licensed-product'), 'error'); } } /** * Validate multi-domain fields */ private function validateMultiDomainFields(array $licensedProducts): void { $licensedDomains = $_POST['licensed_domains'] ?? []; foreach ($licensedProducts as $key => $productData) { $productId = $productData['product_id']; $variationId = $productData['variation_id'] ?? 0; $fieldKey = $variationId > 0 ? "{$productId}_{$variationId}" : (string) $productId; $productDomains = $licensedDomains[$fieldKey] ?? $licensedDomains[$productId] ?? []; $normalizedDomains = []; for ($i = 0; $i < $productData['quantity']; $i++) { $domain = isset($productDomains[$i]) ? sanitize_text_field($productDomains[$i]) : ''; // Check if domain is empty if (empty($domain)) { wc_add_notice( sprintf( /* translators: 1: product name, 2: license number */ __('Please enter a domain for %1$s (License %2$d).', 'wc-licensed-product'), $productData['name'], $i + 1 ), 'error' ); continue; } // Validate domain format $normalizedDomain = $this->licenseManager->normalizeDomain($domain); if (!$this->isValidDomain($normalizedDomain)) { wc_add_notice( sprintf( /* translators: 1: product name, 2: license number */ __('Please enter a valid domain for %1$s (License %2$d).', 'wc-licensed-product'), $productData['name'], $i + 1 ), 'error' ); continue; } // Check for duplicate domains within same product/variation if (in_array($normalizedDomain, $normalizedDomains, true)) { wc_add_notice( sprintf( /* translators: 1: domain name, 2: product name */ __('The domain "%1$s" is used multiple times for %2$s. Each license requires a unique domain.', 'wc-licensed-product'), $normalizedDomain, $productData['name'] ), 'error' ); } else { $normalizedDomains[] = $normalizedDomain; } } } } /** * Save domain fields to order meta */ public function saveDomainField(int $orderId): void { $licensedProducts = $this->getLicensedProductsFromCart(); if (empty($licensedProducts)) { return; } $order = wc_get_order($orderId); if (!$order) { return; } // Check if multi-domain licensing is enabled if (SettingsController::isMultiDomainEnabled()) { $this->saveMultiDomainFields($order, $licensedProducts); } else { $this->saveSingleDomainField($order); } } /** * Save single domain field to order meta (legacy format) */ private function saveSingleDomainField(\WC_Order $order): void { $domain = isset($_POST['licensed_product_domain']) ? sanitize_text_field($_POST['licensed_product_domain']) : ''; if (!empty($domain)) { $normalizedDomain = $this->licenseManager->normalizeDomain($domain); $order->update_meta_data('_licensed_product_domain', $normalizedDomain); $order->save(); } } /** * Save multi-domain fields to order meta */ private function saveMultiDomainFields(\WC_Order $order, array $licensedProducts): void { $licensedDomains = $_POST['licensed_domains'] ?? []; $licensedVariationIds = $_POST['licensed_variation_ids'] ?? []; $domainData = []; foreach ($licensedProducts as $key => $productData) { $productId = $productData['product_id']; $variationId = $productData['variation_id'] ?? 0; $fieldKey = $variationId > 0 ? "{$productId}_{$variationId}" : (string) $productId; $productDomains = $licensedDomains[$fieldKey] ?? $licensedDomains[$productId] ?? []; $normalizedDomains = []; for ($i = 0; $i < $productData['quantity']; $i++) { $domain = isset($productDomains[$i]) ? sanitize_text_field($productDomains[$i]) : ''; if (!empty($domain)) { $normalizedDomains[] = $this->licenseManager->normalizeDomain($domain); } } if (!empty($normalizedDomains)) { $entry = [ 'product_id' => $productId, 'domains' => $normalizedDomains, ]; // Include variation_id if present if ($variationId > 0) { $entry['variation_id'] = $variationId; } $domainData[] = $entry; } } if (!empty($domainData)) { $order->update_meta_data('_licensed_product_domains', $domainData); $order->save(); } } /** * Display domains in admin order view */ public function displayDomainInAdmin(\WC_Order $order): void { // Try new multi-domain format first $domainData = $order->get_meta('_licensed_product_domains'); if (!empty($domainData) && is_array($domainData)) { $this->displayMultiDomainsInAdmin($domainData); return; } // Fall back to legacy single domain $domain = $order->get_meta('_licensed_product_domain'); if (!$domain) { return; } ?>

0) { $variation = wc_get_product($variationId); $productName = $variation ? $variation->get_name() : __('Unknown Variation', 'wc-licensed-product'); // Add duration label if available if ($variation instanceof LicensedProductVariation) { $productName .= ' (' . $variation->get_license_duration_label() . ')'; } } else { $product = wc_get_product($productId); $productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'); } ?>

:

get_meta('_licensed_product_domains'); if (!empty($domainData) && is_array($domainData)) { $this->displayMultiDomainsInEmail($domainData, $plainText); return; } // Fall back to legacy single domain $domain = $order->get_meta('_licensed_product_domain'); if (!$domain) { return; } if ($plainText) { echo "\n" . esc_html__('License Domain:', 'wc-licensed-product') . ' ' . esc_html($domain) . "\n"; } else { ?>

0) { $variation = wc_get_product($variationId); $productName = $variation ? $variation->get_name() : __('Unknown Variation', 'wc-licensed-product'); if ($variation instanceof LicensedProductVariation) { $productName .= ' (' . $variation->get_license_duration_label() . ')'; } } else { $product = wc_get_product($productId); $productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'); } echo ' ' . esc_html($productName) . ': ' . esc_html(implode(', ', $item['domains'])) . "\n"; } } else { ?>
0) { $variation = wc_get_product($variationId); $productName = $variation ? $variation->get_name() : __('Unknown Variation', 'wc-licensed-product'); if ($variation instanceof LicensedProductVariation) { $productName .= ' (' . $variation->get_license_duration_label() . ')'; } } else { $product = wc_get_product($productId); $productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'); } ?>

:

255) { return false; } // Check for valid domain pattern $pattern = '/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/'; return (bool) preg_match($pattern, $domain); } }