diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00157ae..7701221 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.5.14] - 2026-01-27
+
+### Fixed
+
+- **CRITICAL:** Fixed Product Versions meta box not appearing for licensed-variable products
+- Product Versions meta box now always added to product pages, visibility controlled via CSS/JavaScript
+- Added `Installer::registerProductTypes()` to create product type terms in the `product_type` taxonomy
+- Product type terms are now ensured to exist on `woocommerce_init` hook for existing installations
+- Fixed License Settings tab and Product Versions visibility toggling when changing product types
+
## [0.5.13] - 2026-01-27
### Fixed
diff --git a/assets/css/admin.css b/assets/css/admin.css
index d002011..4a615b1 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -51,11 +51,18 @@ code.file-hash {
}
/* License Product Tab - Hidden by default, shown via JS based on product type */
-#woocommerce-product-data .show_if_licensed,
-#woocommerce-product-data .show_if_licensed-variable {
+#woocommerce-product-data ul.wc-tabs li.show_if_licensed,
+#woocommerce-product-data ul.wc-tabs li.show_if_licensed-variable {
display: none;
}
+/* When shown, restore proper display for tab list items */
+#woocommerce-product-data ul.wc-tabs li.show_if_licensed.wclp-active,
+#woocommerce-product-data ul.wc-tabs li.show_if_licensed-variable.wclp-active {
+ display: block;
+}
+
+/* Hide elements for non-licensed products */
#woocommerce-product-data .hide_if_licensed {
display: none !important;
}
diff --git a/src/Admin/VersionAdminController.php b/src/Admin/VersionAdminController.php
index a559d0e..269d2da 100644
--- a/src/Admin/VersionAdminController.php
+++ b/src/Admin/VersionAdminController.php
@@ -43,25 +43,21 @@ final class VersionAdminController
/**
* Add versions meta box to product edit page
+ * Always adds the meta box - visibility is controlled via CSS/JavaScript based on product type
*/
public function addVersionsMetaBox(): void
{
global $post;
- // Only add meta box for licensed products or new products
if ($post && $post->post_type === 'product') {
- $product = wc_get_product($post->ID);
- // Show for licensed products or new products (where type might be selected later)
- if (!$product || $product->is_type('licensed') || $post->post_status === 'auto-draft') {
- add_meta_box(
- 'wc_licensed_product_versions',
- __('Product Versions', 'wc-licensed-product'),
- [$this, 'renderVersionsMetaBox'],
- 'product',
- 'normal',
- 'high'
- );
- }
+ add_meta_box(
+ 'wc_licensed_product_versions',
+ __('Product Versions', 'wc-licensed-product'),
+ [$this, 'renderVersionsMetaBox'],
+ 'product',
+ 'normal',
+ 'high'
+ );
}
}
@@ -280,12 +276,13 @@ final class VersionAdminController
}
// Verify product exists and is of type licensed
- $product = wc_get_product($productId);
- if (!$product) {
+ // Use WC_Product_Factory::get_product_type() for reliable type detection
+ $productType = \WC_Product_Factory::get_product_type($productId);
+ if (!$productType) {
wp_send_json_error(['message' => __('Product not found.', 'wc-licensed-product')]);
}
- if (!$product->is_type('licensed')) {
+ if (!in_array($productType, ['licensed', 'licensed-variable'], true)) {
wp_send_json_error(['message' => __('This product is not a licensed product.', 'wc-licensed-product')]);
}
diff --git a/src/Installer.php b/src/Installer.php
index 8f861b0..354a236 100644
--- a/src/Installer.php
+++ b/src/Installer.php
@@ -31,6 +31,7 @@ final class Installer
{
self::createTables();
self::createCacheDir();
+ self::registerProductTypes();
// Set version in options
update_option('wc_licensed_product_version', WC_LICENSED_PRODUCT_VERSION);
@@ -43,6 +44,28 @@ final class Installer
flush_rewrite_rules();
}
+ /**
+ * Register custom product type terms in the product_type taxonomy
+ * This is required for WC_Product_Factory::get_product_type() to work correctly
+ */
+ public static function registerProductTypes(): void
+ {
+ // Ensure WooCommerce taxonomies are registered
+ if (!taxonomy_exists('product_type')) {
+ return;
+ }
+
+ // Register 'licensed' product type term if it doesn't exist
+ if (!term_exists('licensed', 'product_type')) {
+ wp_insert_term('licensed', 'product_type');
+ }
+
+ // Register 'licensed-variable' product type term if it doesn't exist
+ if (!term_exists('licensed-variable', 'product_type')) {
+ wp_insert_term('licensed-variable', 'product_type');
+ }
+ }
+
/**
* Run on plugin deactivation
*/
diff --git a/src/Product/LicensedProductType.php b/src/Product/LicensedProductType.php
index 57e6665..c0de937 100644
--- a/src/Product/LicensedProductType.php
+++ b/src/Product/LicensedProductType.php
@@ -30,6 +30,9 @@ final class LicensedProductType
*/
private function registerHooks(): void
{
+ // Ensure product type terms exist in taxonomy (for WC_Product_Factory::get_product_type())
+ add_action('woocommerce_init', [$this, 'ensureProductTypeTermsExist']);
+
// Register product types
add_filter('product_type_selector', [$this, 'addProductType']);
add_filter('woocommerce_product_class', [$this, 'getProductClass'], 10, 4);
@@ -74,6 +77,15 @@ final class LicensedProductType
add_action('admin_footer', [$this, 'addVariableProductScripts']);
}
+ /**
+ * Ensure product type terms exist in the product_type taxonomy
+ * This is required for WC_Product_Factory::get_product_type() to work correctly
+ */
+ public function ensureProductTypeTermsExist(): void
+ {
+ \Jeremias\WcLicensedProduct\Installer::registerProductTypes();
+ }
+
/**
* Add product types to selector
*/
@@ -227,35 +239,6 @@ final class LicensedProductType
?>
-