get_type(), $type, true) || in_array('variable', $type, true); } return $this->get_type() === $type || 'variable' === $type; } /** * Licensed products are always virtual */ public function is_virtual(): bool { return true; } /** * Licensed variable products are purchasable if the parent check passes * Variable products don't have a direct price - their variations do */ public function is_purchasable(): bool { // Use the parent WC_Product_Variable logic // which checks exists() and status, not price return parent::is_purchasable(); } /** * Licensed products are always in stock (virtual, no inventory) */ public function is_in_stock(): bool { return true; } /** * Get children (variations) for this product * Override because WC_Product_Variable::get_children() checks is_type('variable') * which fails for our 'licensed-variable' type */ public function get_children($context = 'view'): array { if (!$this->get_id()) { return []; } // Query variations directly from database since WooCommerce's data store // doesn't work properly with custom variable product types global $wpdb; $children = $wpdb->get_col($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_type = 'product_variation' AND post_status IN ('publish', 'private') ORDER BY menu_order ASC, ID ASC", $this->get_id() )); $children = array_map('intval', $children); if ('view' === $context) { $children = apply_filters('woocommerce_get_children', $children, $this, false); } return is_array($children) ? $children : []; } /** * Get variation attributes for this product * Override because WC_Product_Variable uses data_store which doesn't work * properly with custom variable product types */ public function get_variation_attributes(): array { $attributes = $this->get_attributes(); if (!$attributes || !is_array($attributes)) { return []; } $variation_attributes = []; foreach ($attributes as $attribute) { // For WC_Product_Attribute objects if ($attribute instanceof \WC_Product_Attribute) { if ($attribute->get_variation()) { $attribute_name = $attribute->get_name(); // For taxonomy attributes, get term slugs if ($attribute->is_taxonomy()) { $attribute_terms = wc_get_product_terms( $this->get_id(), $attribute_name, ['fields' => 'slugs'] ); $variation_attributes[$attribute_name] = $attribute_terms; } else { // For custom attributes, get options directly $variation_attributes[$attribute_name] = $attribute->get_options(); } } } // For array-based attributes (older format) elseif (is_array($attribute) && !empty($attribute['is_variation'])) { $attribute_name = $attribute['name']; $values = isset($attribute['value']) ? explode('|', $attribute['value']) : []; $variation_attributes[$attribute_name] = array_map('trim', $values); } } return $variation_attributes; } /** * Get variation prices (regular, sale, and final prices) * Override because WC_Product_Variable uses data_store which doesn't work * properly with custom variable product types */ public function get_variation_prices($for_display = false): array { $children = $this->get_children(); if (empty($children)) { return [ 'price' => [], 'regular_price' => [], 'sale_price' => [], ]; } $prices = [ 'price' => [], 'regular_price' => [], 'sale_price' => [], ]; foreach ($children as $child_id) { $variation = wc_get_product($child_id); if ($variation) { $price = $variation->get_price(); $regular_price = $variation->get_regular_price(); $sale_price = $variation->get_sale_price(); if ('' !== $price) { $prices['price'][$child_id] = $price; } if ('' !== $regular_price) { $prices['regular_price'][$child_id] = $regular_price; } if ('' !== $sale_price) { $prices['sale_price'][$child_id] = $sale_price; } } } // Sort prices asort($prices['price']); asort($prices['regular_price']); asort($prices['sale_price']); $this->prices_array = $prices; return $this->prices_array; } /** * Get available variations for this product * Override because WC_Product_Variable uses data_store which doesn't work * properly with custom variable product types */ public function get_available_variations($return = 'array') { $children = $this->get_children(); $available_variations = []; foreach ($children as $child_id) { $variation = wc_get_product($child_id); if (!$variation) { continue; } // Check if variation should be available if (!$variation->exists()) { continue; } // Check if purchasable (has price) if (!$variation->is_purchasable()) { continue; } // Build variation data if ($return === 'array') { $variationData = $this->get_available_variation($variation); // Override availability_html to be empty for licensed products $variationData['availability_html'] = ''; $available_variations[] = $variationData; } else { $available_variations[] = $variation; } } if ($return === 'array') { $available_variations = array_values(array_filter($available_variations)); } return $available_variations; } /** * Get max activations for this product (parent default) * Falls back to default settings if not set on product */ public function get_max_activations(): int { $value = $this->get_meta('_licensed_max_activations', true); if ($value !== '' && $value !== null) { return max(1, (int) $value); } return SettingsController::getDefaultMaxActivations(); } /** * Check if product has custom max activations set */ public function has_custom_max_activations(): bool { $value = $this->get_meta('_licensed_max_activations', true); return $value !== '' && $value !== null; } /** * Get validity days (parent default - variations override this) * Falls back to default settings if not set on product */ public function get_validity_days(): ?int { $value = $this->get_meta('_licensed_validity_days', true); if ($value !== '' && $value !== null) { return (int) $value > 0 ? (int) $value : null; } return SettingsController::getDefaultValidityDays(); } /** * Check if product has custom validity days set */ public function has_custom_validity_days(): bool { $value = $this->get_meta('_licensed_validity_days', true); return $value !== '' && $value !== null; } /** * Check if license should be bound to major version * Falls back to default settings if not set on product */ public function is_bound_to_version(): bool { $value = $this->get_meta('_licensed_bind_to_version', true); if ($value !== '' && $value !== null) { return $value === 'yes'; } return SettingsController::getDefaultBindToVersion(); } /** * Check if product has custom bind to version setting */ public function has_custom_bind_to_version(): bool { $value = $this->get_meta('_licensed_bind_to_version', true); return $value !== '' && $value !== null; } /** * Get current software version (derived from latest product version) */ public function get_current_version(): string { $versionManager = new VersionManager(); $latestVersion = $versionManager->getLatestVersion($this->get_id()); return $latestVersion ? $latestVersion->getVersion() : ''; } /** * Get major version number from version string */ public function get_major_version(): int { $versionManager = new VersionManager(); $latestVersion = $versionManager->getLatestVersion($this->get_id()); if ($latestVersion) { return $latestVersion->getMajorVersion(); } return 1; } }