initTwig(); $this->initComponents(); $this->registerHooks(); } /** * Initialize Twig environment */ private function initTwig(): void { $loader = new FilesystemLoader(WC_LICENSED_PRODUCT_PLUGIN_DIR . 'templates'); $this->twig = new Environment($loader, [ 'cache' => WP_CONTENT_DIR . '/cache/wc-licensed-product/twig', 'auto_reload' => true, // Always check for template changes 'autoescape' => 'html', // Explicitly enable HTML autoescape for XSS protection ]); // Add WordPress functions as Twig functions $this->twig->addFunction(new \Twig\TwigFunction('__', function (string $text, string $domain = 'wc-licensed-product'): string { return __($text, $domain); })); $this->twig->addFunction(new \Twig\TwigFunction('esc_html', 'esc_html')); $this->twig->addFunction(new \Twig\TwigFunction('esc_attr', 'esc_attr')); $this->twig->addFunction(new \Twig\TwigFunction('esc_url', 'esc_url')); $this->twig->addFunction(new \Twig\TwigFunction('wp_nonce_field', 'wp_nonce_field', ['is_safe' => ['html']])); $this->twig->addFunction(new \Twig\TwigFunction('admin_url', 'admin_url')); $this->twig->addFunction(new \Twig\TwigFunction('wc_get_endpoint_url', 'wc_get_endpoint_url')); } /** * Initialize plugin components */ private function initComponents(): void { $this->licenseManager = new LicenseManager(); $this->versionManager = new VersionManager(); // Check plugin license $licenseChecker = PluginLicenseChecker::getInstance(); $isLicensed = $licenseChecker->isLicenseValid(); // Always initialize product type (needed for existing orders) new LicensedProductType(); // Only initialize frontend components if licensed or on localhost if ($isLicensed) { new CheckoutController($this->licenseManager); new StoreApiExtension($this->licenseManager); $this->registerCheckoutBlocksIntegration(); $this->downloadController = new DownloadController($this->licenseManager, $this->versionManager); new AccountController($this->twig, $this->licenseManager, $this->versionManager, $this->downloadController); } // Always initialize REST API and email controller new RestApiController($this->licenseManager); new LicenseEmailController($this->licenseManager); // Initialize response signing if server secret is configured if (defined('WC_LICENSE_SERVER_SECRET') && WC_LICENSE_SERVER_SECRET !== '') { (new ResponseSigner())->register(); } // Admin always available if (is_admin()) { new AdminController($this->twig, $this->licenseManager); new VersionAdminController($this->versionManager); new OrderLicenseController($this->licenseManager); new SettingsController(); new DashboardWidgetController($this->licenseManager); new DownloadWidgetController($this->versionManager); // Show admin notice if unlicensed and not on localhost if (!$isLicensed && !$licenseChecker->isLocalhost()) { add_action('admin_notices', [$this, 'showUnlicensedNotice']); } } } /** * Register WooCommerce Checkout Blocks integration */ private function registerCheckoutBlocksIntegration(): void { add_action('woocommerce_blocks_loaded', function (): void { if (class_exists('Automattic\WooCommerce\Blocks\Integrations\IntegrationRegistry')) { add_action( 'woocommerce_blocks_checkout_block_registration', function ($integration_registry): void { $integration_registry->register(new CheckoutBlocksIntegration()); } ); } }); } /** * Register plugin hooks */ private function registerHooks(): void { // Only register order hooks if licensed (license generation requires valid license) $licenseChecker = PluginLicenseChecker::getInstance(); if ($licenseChecker->isLicenseValid()) { // Generate license on order completion (multiple hooks for compatibility) add_action('woocommerce_order_status_completed', [$this, 'onOrderCompleted']); add_action('woocommerce_order_status_processing', [$this, 'onOrderCompleted']); // Also hook into payment complete for immediate license generation add_action('woocommerce_payment_complete', [$this, 'onOrderCompleted']); } } /** * Handle order completion - generate licenses */ public function onOrderCompleted(int $orderId): void { $order = wc_get_order($orderId); if (!$order) { return; } // Try new multi-domain format first $domainData = $order->get_meta('_licensed_product_domains'); if (!empty($domainData) && is_array($domainData)) { $this->generateLicensesMultiDomain($order, $domainData); return; } // Fall back to legacy single domain format $this->generateLicensesSingleDomain($order); } /** * Generate licenses for new multi-domain format */ private function generateLicensesMultiDomain(\WC_Order $order, array $domainData): void { $orderId = $order->get_id(); $customerId = $order->get_customer_id(); // Index domains by product ID (and variation ID for variable products) $domainsByProduct = []; foreach ($domainData as $item) { if (isset($item['product_id']) && isset($item['domains']) && is_array($item['domains'])) { $productId = (int) $item['product_id']; $variationId = isset($item['variation_id']) ? (int) $item['variation_id'] : 0; $key = $variationId > 0 ? "{$productId}_{$variationId}" : (string) $productId; $domainsByProduct[$key] = [ 'domains' => $item['domains'], 'variation_id' => $variationId, ]; } } // Generate licenses for each licensed product foreach ($order->get_items() as $item) { $product = $item->get_product(); if (!$product || !$this->licenseManager->isLicensedProduct($product)) { continue; } // Get the parent product ID (for variations, this is the main product) $productId = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id(); $variationId = $item->get_variation_id(); // Look up domains - first try with variation, then without $key = $variationId > 0 ? "{$productId}_{$variationId}" : (string) $productId; $domainInfo = $domainsByProduct[$key] ?? $domainsByProduct[(string) $productId] ?? null; $domains = $domainInfo['domains'] ?? []; // Generate a license for each domain foreach ($domains as $domain) { if (!empty($domain)) { $this->licenseManager->generateLicense( $orderId, $productId, $customerId, $domain, $variationId > 0 ? $variationId : null ); } } } } /** * Generate licenses for legacy single domain format */ private function generateLicensesSingleDomain(\WC_Order $order): void { $domain = $order->get_meta('_licensed_product_domain'); if (empty($domain)) { return; } foreach ($order->get_items() as $item) { $product = $item->get_product(); if ($product && $this->licenseManager->isLicensedProduct($product)) { // Get the parent product ID (for variations, this is the main product) $productId = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id(); $variationId = $item->get_variation_id(); $this->licenseManager->generateLicense( $order->get_id(), $productId, $order->get_customer_id(), $domain, $variationId > 0 ? $variationId : null ); } } } /** * Get Twig environment */ public function getTwig(): Environment { return $this->twig; } /** * Get license manager */ public function getLicenseManager(): LicenseManager { return $this->licenseManager; } /** * Render a Twig template */ public function render(string $template, array $context = []): string { return $this->twig->render($template, $context); } /** * Show admin notice when plugin is unlicensed */ public function showUnlicensedNotice(): void { $settingsUrl = admin_url('admin.php?page=wc-settings&tab=licensed_product'); ?>