12 Commits

Author SHA1 Message Date
142500cab0 Fix stock indicator on licensed variable products (v0.5.12)
- Fixed stock indicator appearing in cart for licensed variable products
- Override get_children() with direct SQL query to bypass WooCommerce type check
- Override get_variation_attributes() for proper taxonomy attribute loading
- Override get_variation_prices() to prevent null array errors
- Override get_available_variations() with empty availability_html
- Added is_type() override to pass variable type checks
- Added multiple stock-related filters for comprehensive coverage
- Improved isLicensedProductOrVariation() with DB-level parent type check

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 14:44:57 +01:00
20fb39d1a1 Update CLAUDE.md with v0.5.8-0.5.11 session history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:59:00 +01:00
953aa6c8e8 Fix licensed variable products showing as sold out (v0.5.11)
- Fixed is_purchasable() method in LicensedVariableProduct to delegate to
  parent WC_Product_Variable instead of checking for price (variable products
  don't have direct prices, only their variations do)
- Fixed getProductClass() filter to accept all 4 WooCommerce parameters
  and use product_id for reliable variation parent detection
- Fallback to global $post when product_id not available for backwards compat

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:58:07 +01:00
db4966caf2 Add release package v0.5.10
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:52:51 +01:00
9c4232f14f Fix licensed variable products not showing variations (v0.5.10)
- Re-load product via wc_get_product() to ensure correct class instance
- Removed overly strict type check that prevented variations from displaying
- Now mirrors WooCommerce's standard woocommerce_variable_add_to_cart()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:51:46 +01:00
0638767ce3 Add release package v0.5.9
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:41:58 +01:00
9826c8181e Fix frontend error on licensed variable products without attributes (v0.5.9)
- Added null checks for get_variation_attributes(), get_available_variations(), get_default_attributes()
- Show informative message when product has no variations configured
- Changed product type check from instanceof to is_type() for better compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:40:50 +01:00
fa972ceaf0 Add release package v0.5.8
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:36:24 +01:00
3abf05cff3 Update translations for v0.5.8
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:35:02 +01:00
169eed65eb Fix critical error and variants tab on licensed variable products (v0.5.8)
- Fixed critical error on frontend product pages for licensed variable products
- Variable product add-to-cart template now passes required variables
- Variants tab no longer disappears when saving attributes
- Added WooCommerce AJAX event listeners for tab visibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:34:27 +01:00
90cb8d97bd Update CLAUDE.md with v0.5.6 and v0.5.7 session history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 11:59:48 +01:00
fc281f7f4a Add release package v0.5.7
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 11:56:26 +01:00
22 changed files with 781 additions and 86 deletions

View File

@@ -7,6 +7,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.5.12] - 2026-01-27
### Fixed
- **CRITICAL:** Fixed stock indicator ("1 in stock") appearing in cart for licensed variable product variations
- Override `get_children()` with direct SQL query to bypass WooCommerce's `is_type('variable')` check
- Override `get_variation_attributes()` to properly load taxonomy attribute terms
- Override `get_variation_prices()` to prevent fatal error with null `$this->prices_array`
- Override `get_available_variations()` with empty `availability_html` for variations
- Added `is_type()` override to return true for both 'licensed-variable' and 'variable' type checks
- Added multiple stock-related filters: `woocommerce_get_availability_text`, `woocommerce_product_get_stock_quantity`, `woocommerce_product_variation_get_stock_quantity`
- Improved `isLicensedProductOrVariation()` check using `WC_Product_Factory::get_product_type()` for reliable parent type detection
### Changed
- `LicensedProductVariation` now includes `get_availability()`, `managing_stock()`, and `is_purchasable()` overrides
- Simplified `isVirtual()` to use shared `isLicensedProductOrVariation()` helper
## [0.5.11] - 2026-01-27
### Fixed
- **CRITICAL:** Fixed "sold out" message on licensed variable products by correcting `is_purchasable()` method
- Variable products don't have a direct price - `is_purchasable()` now delegates to parent `WC_Product_Variable` class
- Fixed variation class detection by using product ID parameter instead of unreliable global `$post`
- Product class filter now properly accepts all 4 WooCommerce filter parameters for reliable variation detection
## [0.5.10] - 2026-01-27
### Fixed
- Fixed licensed variable products not showing variations even when attributes are defined
- Re-load product via `wc_get_product()` to ensure correct class instance is used
- Removed overly strict type check that was preventing variations from displaying
- Now mirrors WooCommerce's standard `woocommerce_variable_add_to_cart()` implementation
## [0.5.9] - 2026-01-27
### Fixed
- Fixed frontend error on licensed variable products when no attributes are defined
- Added null checks for `get_variation_attributes()`, `get_available_variations()`, and `get_default_attributes()`
- Show informative message instead of error when product has no variations configured
- Changed product type check from `instanceof` to `is_type()` for better compatibility
## [0.5.8] - 2026-01-27
### Fixed
- **CRITICAL:** Fixed critical error on frontend product pages for licensed variable products
- Variable product add-to-cart template now passes required variables (`available_variations`, `attributes`, `selected_attributes`)
- Variants tab no longer disappears when saving attributes on licensed variable products
- Added WooCommerce AJAX event listeners to maintain tab visibility during attribute operations
### Changed
- Improved JavaScript event handling for licensed-variable product type in admin
- Added listeners for `woocommerce_variations_loaded`, `woocommerce_variations_added`, `woocommerce_variations_saved` events
- Added AJAX complete handler for attribute save operations
## [0.5.7] - 2026-01-27
### Changed

106
CLAUDE.md
View File

@@ -1437,3 +1437,109 @@ Critical bug fix for response signing. The key derivation algorithm was incompat
- Length: 32 bytes (256 bits)
- Info: license_key (context-specific info)
- **Breaking change for existing signatures** - customer secrets will change after upgrade
### 2026-01-27 - Version 0.5.6 - License Settings Tab Visibility Fix
**Overview:**
Fixed License Settings tab visibility for non-licensed product types and updated README with v0.5.x features.
**Bug Fix:**
- License Settings tab now only shows for Licensed Product and Licensed Variable Product types
- Previously the tab was visible on all product types due to CSS `!important` override forcing `display: block`
**Modified files:**
- `assets/css/admin.css` - Changed from `display: block !important` to `display: none` for `.show_if_licensed` and `.show_if_licensed-variable`
- `src/Product/LicensedProductType.php` - Added consolidated `toggleLicensedProductOptions()` JavaScript function
- `README.md` - Updated with complete feature documentation for v0.5.x features
**Technical notes:**
- CSS now hides License Settings tab by default
- JavaScript `toggleLicensedProductOptions()` function shows/hides tab based on product type selector
- Function is called both on page load and on product type change
- README updated with: Variable Licensed Products, Multi-Domain Licensing, Per-License Secrets, Download Statistics, Configurable Rate Limiting
**Release v0.5.6:**
- Created release package: `releases/wc-licensed-product-0.5.6.zip` (1.1 MB)
- SHA256: `4d35a319fe4cb4e7055bae17fc030487ca05e5e9ac905f76d0ac62002bde4336`
- Tagged as `v0.5.6` and pushed to `main` branch
### 2026-01-27 - Version 0.5.7 - Settings UI Cleanup
**Overview:**
Removed redundant "Default" prefix from setting labels on the Default Settings page for cleaner UI.
**Changed:**
- "Max Activations" (was "Default Max Activations")
- "License Validity (Days)" (was "Default License Validity (Days)")
- "Bind to Major Version" (was "Default Bind to Major Version")
**Modified files:**
- `src/Admin/SettingsController.php` - Removed "Default" prefix from three setting labels
**Technical notes:**
- Labels are cleaner since the page section itself is already named "Default Settings"
- No functional changes, purely UI improvement
- Updated all translations (388 strings)
**Release v0.5.7:**
- Created release package: `releases/wc-licensed-product-0.5.7.zip` (856 KB)
- SHA256: `ceb4d57598f576f4f172153ff80df8c180ecd4dca873cf109327fc5ac718930f`
- Tagged as `v0.5.7` and pushed to `main` branch
### 2026-01-27 - Version 0.5.8-0.5.11 - Licensed Variable Product Fixes
**Overview:**
Series of bug fixes for licensed variable products that were showing frontend errors and not displaying properly.
**v0.5.8 - Initial Fix:**
- Fixed critical error on frontend product pages for licensed variable products
- Variable product add-to-cart template now passes required variables (`available_variations`, `attributes`, `selected_attributes`)
- Added JavaScript event listeners for WooCommerce AJAX events to maintain admin variants tab visibility
**v0.5.9 - Null Checks:**
- Added null checks for `get_variation_attributes()`, `get_available_variations()`, and `get_default_attributes()`
- Show informative message instead of error when product has no variations configured
- Changed product type check from `instanceof` to `is_type()` for better compatibility
**v0.5.10 - Product Loading:**
- Re-load product via `wc_get_product()` to ensure correct class instance is used
- Removed overly strict type check that was preventing variations from displaying
**v0.5.11 - Final Fix:**
- **CRITICAL:** Fixed "sold out" message on licensed variable products
- `LicensedVariableProduct::is_purchasable()` now delegates to parent `WC_Product_Variable` class (variable products don't have direct prices - only variations do)
- Fixed `getProductClass()` filter to accept all 4 WooCommerce parameters and use product_id for reliable variation parent detection
- Added fallback to global `$post` when product_id not available
**Modified files:**
- `src/Product/LicensedProductType.php` - Fixed `variableAddToCartTemplate()` and `getProductClass()` methods
- `src/Product/LicensedVariableProduct.php` - Fixed `is_purchasable()` method
- `wc-licensed-product.php` - Version bumps
**Technical notes:**
- WooCommerce `woocommerce_product_class` filter has 4 parameters: `$className`, `$productType`, `$postType`, `$productId`
- Variable products delegate purchasability to their variations - checking `get_price()` on parent is incorrect
- Variation parent detection must use product ID, not global `$post` which may not be set on frontend
**Release v0.5.11:**
- Created release package: `releases/wc-licensed-product-0.5.11.zip` (857 KB)
- SHA256: `32571178bfa8f0d0a03ed05b498d5f9b3c860104393a96732e86a03b6de298d2`
- Committed to `dev` branch

View File

@@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: WC Licensed Product 0.5.0\n"
"Report-Msgid-Bugs-To: magdev3.0@gmail.com\n"
"POT-Creation-Date: 2026-01-27 11:54+0100\n"
"POT-Creation-Date: 2026-01-27 14:41+0100\n"
"PO-Revision-Date: 2026-01-25T18:30:00+00:00\n"
"Last-Translator: Marco Graetsch <magdev3.0@gmail.com>\n"
"Language-Team: German (Switzerland) <de_CH@li.org>\n"
@@ -311,10 +311,10 @@ msgstr "Speichern"
#: src/Admin/DashboardWidgetController.php:136
#: src/Admin/OrderLicenseController.php:260
#: src/Admin/SettingsController.php:192
#: src/Product/LicensedProductVariation.php:139
#: src/Product/LicensedProductType.php:136
#: src/Product/LicensedProductType.php:184
#: src/Product/LicensedProductType.php:385
#: src/Product/LicensedProductVariation.php:194
#: src/Product/LicensedProductType.php:164
#: src/Product/LicensedProductType.php:212
#: src/Product/LicensedProductType.php:553
#: src/Frontend/AccountController.php:286
msgid "Lifetime"
msgstr "Lebenslang"
@@ -492,6 +492,7 @@ msgstr "Lizenz erfolgreich verlängert."
msgid "License set to lifetime successfully."
msgstr "Lizenz erfolgreich auf lebenslang gesetzt."
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1106
#, php-format
msgid "%d license activated."
@@ -499,6 +500,7 @@ msgid_plural "%d licenses activated."
msgstr[0] "%d Lizenz aktiviert."
msgstr[1] "%d Lizenzen aktiviert."
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1114
#, php-format
msgid "%d license deactivated."
@@ -506,6 +508,7 @@ msgid_plural "%d licenses deactivated."
msgstr[0] "%d Lizenz deaktiviert."
msgstr[1] "%d Lizenzen deaktiviert."
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1122
#, php-format
msgid "%d license revoked."
@@ -513,6 +516,7 @@ msgid_plural "%d licenses revoked."
msgstr[0] "%d Lizenz widerrufen."
msgstr[1] "%d Lizenzen widerrufen."
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1130
#, php-format
msgid "%d license deleted."
@@ -520,6 +524,7 @@ msgid_plural "%d licenses deleted."
msgstr[0] "%d Lizenz gelöscht."
msgstr[1] "%d Lizenzen gelöscht."
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1138
#, php-format
msgid "%d license extended."
@@ -541,6 +546,7 @@ msgstr ""
msgid "No licenses to export."
msgstr "Keine Lizenzen zum Exportieren."
#. translators: %d: number of licenses imported
#: src/Admin/AdminController.php:1159
#, php-format
msgid "%d license imported."
@@ -548,6 +554,7 @@ msgid_plural "%d licenses imported."
msgstr[0] "%d Lizenz importiert."
msgstr[1] "%d Lizenzen importiert."
#. translators: %d: number of licenses updated
#: src/Admin/AdminController.php:1166
#, php-format
msgid "%d updated."
@@ -555,6 +562,7 @@ msgid_plural "%d updated."
msgstr[0] "%d aktualisiert."
msgstr[1] "%d aktualisiert."
#. translators: %d: number of licenses skipped
#: src/Admin/AdminController.php:1174
#, php-format
msgid "%d skipped."
@@ -562,6 +570,7 @@ msgid_plural "%d skipped."
msgstr[0] "%d übersprungen."
msgstr[1] "%d übersprungen."
#. translators: %d: number of errors
#: src/Admin/AdminController.php:1182
#, php-format
msgid "%d error."
@@ -1020,6 +1029,7 @@ msgstr "Domain bearbeiten"
msgid "View in Licenses"
msgstr "In Lizenzen anzeigen"
#. translators: %s: Link to licenses page
#: src/Admin/OrderLicenseController.php:280
#, php-format
msgid "For more actions (revoke, extend, delete), go to the %s page."
@@ -1166,8 +1176,8 @@ msgstr ""
"Diese Einstellungen dienen als Standard für neue lizensierte Produkte. "
"Individuelle Produkteinstellungen überschreiben diese Standards."
#: src/Admin/SettingsController.php:176 src/Product/LicensedProductType.php:154
#: src/Product/LicensedProductType.php:402
#: src/Admin/SettingsController.php:176 src/Product/LicensedProductType.php:182
#: src/Product/LicensedProductType.php:570
msgid "Max Activations"
msgstr "Max. Aktivierungen"
@@ -1175,7 +1185,7 @@ msgstr "Max. Aktivierungen"
msgid "Default maximum number of domain activations per license."
msgstr "Standard maximale Anzahl der Domain-Aktivierungen pro Lizenz."
#: src/Admin/SettingsController.php:187 src/Product/LicensedProductType.php:172
#: src/Admin/SettingsController.php:187 src/Product/LicensedProductType.php:200
msgid "License Validity (Days)"
msgstr "Lizenz-Gültigkeit (Tage)"
@@ -1187,7 +1197,7 @@ msgstr ""
"Standard Anzahl Tage, die eine Lizenz gültig ist. Leer lassen oder auf 0 "
"setzen für lebenslange Lizenzen."
#: src/Admin/SettingsController.php:199 src/Product/LicensedProductType.php:190
#: src/Admin/SettingsController.php:199 src/Product/LicensedProductType.php:218
msgid "Bind to Major Version"
msgstr "An Hauptversion binden"
@@ -1215,6 +1225,7 @@ msgstr ""
msgid "Expiration Warning Schedule"
msgstr "Ablaufwarnung Zeitplan"
#. translators: %s: URL to WooCommerce email settings
#: src/Admin/SettingsController.php:230
#, php-format
msgid ""
@@ -1346,6 +1357,7 @@ msgstr "Lizenz-Domains"
msgid "Each license requires a unique domain."
msgstr "Jede Lizenz erfordert eine eindeutige Domain."
#. translators: %d: license number
#: src/Checkout/CheckoutBlocksIntegration.php:129
#: src/Checkout/CheckoutController.php:224
#, php-format
@@ -1357,6 +1369,16 @@ msgstr "Lizenz %d:"
msgid "required"
msgstr "erforderlich"
#: src/Checkout/CheckoutController.php:215
#, php-format
msgid "licensed_domains[%s][%d]"
msgstr "licensed_domains[%s][%d]"
#: src/Checkout/CheckoutController.php:216
#, php-format
msgid "licensed_domain_%s_%d"
msgstr "licensed_domain_%s_%d"
#: src/Checkout/CheckoutController.php:323
msgid "Please enter a domain for your license."
msgstr "Bitte geben Sie eine Domain für Ihre Lizenz ein."
@@ -1365,16 +1387,19 @@ msgstr "Bitte geben Sie eine Domain für Ihre Lizenz ein."
msgid "Please enter a valid domain for your license."
msgstr "Bitte geben Sie eine gültige Domain für Ihre Lizenz ein."
#. translators: 1: product name, 2: license number
#: src/Checkout/CheckoutController.php:356
#, php-format
msgid "Please enter a domain for %1$s (License %2$d)."
msgstr "Bitte geben Sie eine Domain für %1$s (Lizenz %2$d) ein."
#. translators: 1: product name, 2: license number
#: src/Checkout/CheckoutController.php:371
#, php-format
msgid "Please enter a valid domain for %1$s (License %2$d)."
msgstr "Bitte geben Sie eine gültige Domain für %1$s (Lizenz %2$d) ein."
#. translators: 1: domain name, 2: product name
#: src/Checkout/CheckoutController.php:385
#, php-format
msgid ""
@@ -1438,68 +1463,74 @@ msgstr "Diese Lizenz ist für diese Domain nicht gültig."
msgid "Attachment file not found."
msgstr "Anhangs-Datei nicht gefunden."
#. translators: 1: provided hash, 2: calculated hash
#: src/Product/VersionManager.php:177
#, php-format
msgid "File checksum does not match. Expected: %1$s, Got: %2$s"
msgstr "Datei-Prüfsumme stimmt nicht überein. Erwartet: %1$s, Erhalten: %2$s"
#: src/Product/LicensedProductVariation.php:143
#: src/Product/LicensedProductVariation.php:198
msgid "Monthly"
msgstr "Monatlich"
#: src/Product/LicensedProductVariation.php:147
#: src/Product/LicensedProductVariation.php:202
msgid "Quarterly"
msgstr "Vierteljährlich"
#: src/Product/LicensedProductVariation.php:151
#: src/Product/LicensedProductVariation.php:206
msgid "Yearly"
msgstr "Jährlich"
#: src/Product/LicensedProductVariation.php:156
#. translators: %d: number of days
#: src/Product/LicensedProductVariation.php:211
#, php-format
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d Tag"
msgstr[1] "%d Tage"
#: src/Product/LicensedProductType.php:72
#: src/Product/LicensedProductType.php:82
msgid "Licensed Product"
msgstr "Lizensiertes Produkt"
#: src/Product/LicensedProductType.php:73
#: src/Product/LicensedProductType.php:83
msgid "Licensed Variable Product"
msgstr "Lizensiertes variables Produkt"
#: src/Product/LicensedProductType.php:108
#: src/Product/LicensedProductType.php:136
msgid "License Settings"
msgstr "Lizenz-Einstellungen"
#: src/Product/LicensedProductType.php:135
#: src/Product/LicensedProductType.php:384
#: src/Product/LicensedProductType.php:163
#: src/Product/LicensedProductType.php:552
#, php-format
msgid "%d days"
msgstr "%d Tage"
#: src/Product/LicensedProductType.php:145
#. translators: %s: URL to settings page
#: src/Product/LicensedProductType.php:173
#, php-format
msgid "Leave fields empty to use default settings from %s."
msgstr "Felder leer lassen, um Standardeinstellungen von %s zu verwenden."
#: src/Product/LicensedProductType.php:147
#: src/Product/LicensedProductType.php:175
msgid "WooCommerce > Settings > Licensed Products"
msgstr "WooCommerce > Einstellungen > Lizensierte Produkte"
#: src/Product/LicensedProductType.php:157
#. translators: %d: default max activations value
#: src/Product/LicensedProductType.php:185
#, php-format
msgid "Maximum number of domain activations per license. Default: %d"
msgstr "Maximale Anzahl der Domain-Aktivierungen pro Lizenz. Standard: %d"
#: src/Product/LicensedProductType.php:175
#. translators: %s: default validity value
#: src/Product/LicensedProductType.php:203
#, php-format
msgid "Number of days the license is valid. Leave empty for default (%s)."
msgstr "Anzahl Tage, die die Lizenz gültig ist. Leer lassen für Standard (%s)."
#: src/Product/LicensedProductType.php:193
#. translators: %s: default bind to version value (Yes/No)
#: src/Product/LicensedProductType.php:221
#, php-format
msgid ""
"If enabled, licenses are bound to the major version at purchase time. "
@@ -1508,35 +1539,35 @@ msgstr ""
"Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt "
"gebunden. Standard: %s"
#: src/Product/LicensedProductType.php:194
#: src/Product/LicensedProductType.php:222
msgid "Yes"
msgstr "Ja"
#: src/Product/LicensedProductType.php:194
#: src/Product/LicensedProductType.php:222
msgid "No"
msgstr "Nein"
#: src/Product/LicensedProductType.php:327
#: src/Product/LicensedProductType.php:447
msgid "Version:"
msgstr "Version:"
#: src/Product/LicensedProductType.php:355
#: src/Product/LicensedProductType.php:523
msgid "Licensed products are always virtual"
msgstr "Lizenzierte Produkte sind immer virtuell"
#: src/Product/LicensedProductType.php:357
#: src/Product/LicensedProductType.php:525
msgid "Virtual"
msgstr "Virtuell"
#: src/Product/LicensedProductType.php:390
#: src/Product/LicensedProductType.php:558
msgid "License Duration (Days)"
msgstr "Lizenz-Gültigkeit (Tage)"
#: src/Product/LicensedProductType.php:399
#: src/Product/LicensedProductType.php:567
msgid "Leave empty for parent default. 0 = Lifetime."
msgstr "Leer lassen für übergeordneten Standard. 0 = Lebenslang."
#: src/Product/LicensedProductType.php:411
#: src/Product/LicensedProductType.php:579
msgid "Leave empty for parent default."
msgstr "Leer lassen für übergeordneten Standard."
@@ -1599,6 +1630,7 @@ msgstr "Bitte melden Sie sich an, um Ihre Lizenzen zu sehen."
msgid "You have no licenses yet."
msgstr "Sie haben noch keine Lizenzen."
#. translators: %s: order number
#: src/Frontend/AccountController.php:245
#, php-format
msgid "Order #%s"
@@ -1756,6 +1788,7 @@ msgstr ""
"Um dieses Produkt weiterhin zu nutzen, verlängern Sie bitte Ihre Lizenz vor "
"dem Ablaufdatum."
#. translators: %s: list of placeholders
#: src/Email/LicenseExpirationEmail.php:301
#: src/Email/LicenseExpiredEmail.php:288
#, php-format
@@ -1894,6 +1927,7 @@ msgstr ""
msgid "Configure License"
msgstr "Lizenz konfigurieren"
#. translators: %s: WooCommerce plugin name
#: wc-licensed-product.php:61
#, php-format
msgid "%s requires WooCommerce to be installed and active."

View File

@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: WC Licensed Product 0.5.7\n"
"Project-Id-Version: WC Licensed Product 0.5.12\n"
"Report-Msgid-Bugs-To: magdev3.0@gmail.com\n"
"POT-Creation-Date: 2026-01-27 11:54+0100\n"
"POT-Creation-Date: 2026-01-27 14:41+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -304,10 +304,10 @@ msgstr ""
#: src/Admin/DashboardWidgetController.php:136
#: src/Admin/OrderLicenseController.php:260
#: src/Admin/SettingsController.php:192
#: src/Product/LicensedProductVariation.php:139
#: src/Product/LicensedProductType.php:136
#: src/Product/LicensedProductType.php:184
#: src/Product/LicensedProductType.php:385
#: src/Product/LicensedProductVariation.php:194
#: src/Product/LicensedProductType.php:164
#: src/Product/LicensedProductType.php:212
#: src/Product/LicensedProductType.php:553
#: src/Frontend/AccountController.php:286
msgid "Lifetime"
msgstr ""
@@ -485,6 +485,7 @@ msgstr ""
msgid "License set to lifetime successfully."
msgstr ""
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1106
#, php-format
msgid "%d license activated."
@@ -492,6 +493,7 @@ msgid_plural "%d licenses activated."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1114
#, php-format
msgid "%d license deactivated."
@@ -499,6 +501,7 @@ msgid_plural "%d licenses deactivated."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1122
#, php-format
msgid "%d license revoked."
@@ -506,6 +509,7 @@ msgid_plural "%d licenses revoked."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1130
#, php-format
msgid "%d license deleted."
@@ -513,6 +517,7 @@ msgid_plural "%d licenses deleted."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses
#: src/Admin/AdminController.php:1138
#, php-format
msgid "%d license extended."
@@ -532,6 +537,7 @@ msgstr ""
msgid "No licenses to export."
msgstr ""
#. translators: %d: number of licenses imported
#: src/Admin/AdminController.php:1159
#, php-format
msgid "%d license imported."
@@ -539,6 +545,7 @@ msgid_plural "%d licenses imported."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses updated
#: src/Admin/AdminController.php:1166
#, php-format
msgid "%d updated."
@@ -546,6 +553,7 @@ msgid_plural "%d updated."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of licenses skipped
#: src/Admin/AdminController.php:1174
#, php-format
msgid "%d skipped."
@@ -553,6 +561,7 @@ msgid_plural "%d skipped."
msgstr[0] ""
msgstr[1] ""
#. translators: %d: number of errors
#: src/Admin/AdminController.php:1182
#, php-format
msgid "%d error."
@@ -997,6 +1006,7 @@ msgstr ""
msgid "View in Licenses"
msgstr ""
#. translators: %s: Link to licenses page
#: src/Admin/OrderLicenseController.php:280
#, php-format
msgid "For more actions (revoke, extend, delete), go to the %s page."
@@ -1133,8 +1143,8 @@ msgid ""
"product settings override these defaults."
msgstr ""
#: src/Admin/SettingsController.php:176 src/Product/LicensedProductType.php:154
#: src/Product/LicensedProductType.php:402
#: src/Admin/SettingsController.php:176 src/Product/LicensedProductType.php:182
#: src/Product/LicensedProductType.php:570
msgid "Max Activations"
msgstr ""
@@ -1142,7 +1152,7 @@ msgstr ""
msgid "Default maximum number of domain activations per license."
msgstr ""
#: src/Admin/SettingsController.php:187 src/Product/LicensedProductType.php:172
#: src/Admin/SettingsController.php:187 src/Product/LicensedProductType.php:200
msgid "License Validity (Days)"
msgstr ""
@@ -1152,7 +1162,7 @@ msgid ""
"lifetime licenses."
msgstr ""
#: src/Admin/SettingsController.php:199 src/Product/LicensedProductType.php:190
#: src/Admin/SettingsController.php:199 src/Product/LicensedProductType.php:218
msgid "Bind to Major Version"
msgstr ""
@@ -1176,6 +1186,7 @@ msgstr ""
msgid "Expiration Warning Schedule"
msgstr ""
#. translators: %s: URL to WooCommerce email settings
#: src/Admin/SettingsController.php:230
#, php-format
msgid ""
@@ -1299,6 +1310,7 @@ msgstr ""
msgid "Each license requires a unique domain."
msgstr ""
#. translators: %d: license number
#: src/Checkout/CheckoutBlocksIntegration.php:129
#: src/Checkout/CheckoutController.php:224
#, php-format
@@ -1310,6 +1322,16 @@ msgstr ""
msgid "required"
msgstr ""
#: src/Checkout/CheckoutController.php:215
#, php-format
msgid "licensed_domains[%s][%d]"
msgstr ""
#: src/Checkout/CheckoutController.php:216
#, php-format
msgid "licensed_domain_%s_%d"
msgstr ""
#: src/Checkout/CheckoutController.php:323
msgid "Please enter a domain for your license."
msgstr ""
@@ -1318,16 +1340,19 @@ msgstr ""
msgid "Please enter a valid domain for your license."
msgstr ""
#. translators: 1: product name, 2: license number
#: src/Checkout/CheckoutController.php:356
#, php-format
msgid "Please enter a domain for %1$s (License %2$d)."
msgstr ""
#. translators: 1: product name, 2: license number
#: src/Checkout/CheckoutController.php:371
#, php-format
msgid "Please enter a valid domain for %1$s (License %2$d)."
msgstr ""
#. translators: 1: domain name, 2: product name
#: src/Checkout/CheckoutController.php:385
#, php-format
msgid ""
@@ -1389,103 +1414,109 @@ msgstr ""
msgid "Attachment file not found."
msgstr ""
#. translators: 1: provided hash, 2: calculated hash
#: src/Product/VersionManager.php:177
#, php-format
msgid "File checksum does not match. Expected: %1$s, Got: %2$s"
msgstr ""
#: src/Product/LicensedProductVariation.php:143
#: src/Product/LicensedProductVariation.php:198
msgid "Monthly"
msgstr ""
#: src/Product/LicensedProductVariation.php:147
#: src/Product/LicensedProductVariation.php:202
msgid "Quarterly"
msgstr ""
#: src/Product/LicensedProductVariation.php:151
#: src/Product/LicensedProductVariation.php:206
msgid "Yearly"
msgstr ""
#: src/Product/LicensedProductVariation.php:156
#. translators: %d: number of days
#: src/Product/LicensedProductVariation.php:211
#, php-format
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
msgstr[1] ""
#: src/Product/LicensedProductType.php:72
#: src/Product/LicensedProductType.php:82
msgid "Licensed Product"
msgstr ""
#: src/Product/LicensedProductType.php:73
#: src/Product/LicensedProductType.php:83
msgid "Licensed Variable Product"
msgstr ""
#: src/Product/LicensedProductType.php:108
#: src/Product/LicensedProductType.php:136
msgid "License Settings"
msgstr ""
#: src/Product/LicensedProductType.php:135
#: src/Product/LicensedProductType.php:384
#: src/Product/LicensedProductType.php:163
#: src/Product/LicensedProductType.php:552
#, php-format
msgid "%d days"
msgstr ""
#: src/Product/LicensedProductType.php:145
#. translators: %s: URL to settings page
#: src/Product/LicensedProductType.php:173
#, php-format
msgid "Leave fields empty to use default settings from %s."
msgstr ""
#: src/Product/LicensedProductType.php:147
#: src/Product/LicensedProductType.php:175
msgid "WooCommerce > Settings > Licensed Products"
msgstr ""
#: src/Product/LicensedProductType.php:157
#. translators: %d: default max activations value
#: src/Product/LicensedProductType.php:185
#, php-format
msgid "Maximum number of domain activations per license. Default: %d"
msgstr ""
#: src/Product/LicensedProductType.php:175
#. translators: %s: default validity value
#: src/Product/LicensedProductType.php:203
#, php-format
msgid "Number of days the license is valid. Leave empty for default (%s)."
msgstr ""
#: src/Product/LicensedProductType.php:193
#. translators: %s: default bind to version value (Yes/No)
#: src/Product/LicensedProductType.php:221
#, php-format
msgid ""
"If enabled, licenses are bound to the major version at purchase time. "
"Default: %s"
msgstr ""
#: src/Product/LicensedProductType.php:194
#: src/Product/LicensedProductType.php:222
msgid "Yes"
msgstr ""
#: src/Product/LicensedProductType.php:194
#: src/Product/LicensedProductType.php:222
msgid "No"
msgstr ""
#: src/Product/LicensedProductType.php:327
#: src/Product/LicensedProductType.php:447
msgid "Version:"
msgstr ""
#: src/Product/LicensedProductType.php:355
#: src/Product/LicensedProductType.php:523
msgid "Licensed products are always virtual"
msgstr ""
#: src/Product/LicensedProductType.php:357
#: src/Product/LicensedProductType.php:525
msgid "Virtual"
msgstr ""
#: src/Product/LicensedProductType.php:390
#: src/Product/LicensedProductType.php:558
msgid "License Duration (Days)"
msgstr ""
#: src/Product/LicensedProductType.php:399
#: src/Product/LicensedProductType.php:567
msgid "Leave empty for parent default. 0 = Lifetime."
msgstr ""
#: src/Product/LicensedProductType.php:411
#: src/Product/LicensedProductType.php:579
msgid "Leave empty for parent default."
msgstr ""
@@ -1548,6 +1579,7 @@ msgstr ""
msgid "You have no licenses yet."
msgstr ""
#. translators: %s: order number
#: src/Frontend/AccountController.php:245
#, php-format
msgid "Order #%s"
@@ -1697,6 +1729,7 @@ msgid ""
"expiration date."
msgstr ""
#. translators: %s: list of placeholders
#: src/Email/LicenseExpirationEmail.php:301
#: src/Email/LicenseExpiredEmail.php:288
#, php-format
@@ -1827,6 +1860,7 @@ msgstr ""
msgid "Configure License"
msgstr ""
#. translators: %s: WooCommerce plugin name
#: wc-licensed-product.php:61
#, php-format
msgid "%s requires WooCommerce to be installed and active."

Binary file not shown.

View File

@@ -0,0 +1 @@
2bbc0655f724e201367247f0e40974ddce6d7c559987e661f2b06b43294fc99f wc-licensed-product-0.5.10.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
32571178bfa8f0d0a03ed05b498d5f9b3c860104393a96732e86a03b6de298d2 wc-licensed-product-0.5.11.zip

View File

@@ -0,0 +1 @@
20bb5cd453de9bca781864430ebd152c82f660b6f9fc3f09107ba03489a71d75 /home/magdev/workspaces/php/wordpress/wp-content/plugins/wc-licensed-product/releases/wc-licensed-product-0.5.12.zip

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
ceb4d57598f576f4f172153ff80df8c180ecd4dca873cf109327fc5ac718930f wc-licensed-product-0.5.7.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
670c2f5182ea7140ccf9533c2b4179daf7890019a244973f467f2a5c7622b9f4 wc-licensed-product-0.5.8.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
fae77dab56cb8f46693cf44fe6a1dc38ad0526d881cab2cd1f0878b234afaa8b wc-licensed-product-0.5.9.zip

View File

@@ -55,6 +55,14 @@ class LicensedProduct extends WC_Product
return $this->exists() && $this->get_price() !== '';
}
/**
* Licensed products are always in stock (virtual, no inventory)
*/
public function is_in_stock(): bool
{
return true;
}
/**
* Get max activations for this product
* Falls back to default settings if not set on product

View File

@@ -32,7 +32,7 @@ final class LicensedProductType
{
// Register product types
add_filter('product_type_selector', [$this, 'addProductType']);
add_filter('woocommerce_product_class', [$this, 'getProductClass'], 10, 2);
add_filter('woocommerce_product_class', [$this, 'getProductClass'], 10, 4);
// Add product data tabs
add_filter('woocommerce_product_data_tabs', [$this, 'addProductDataTab']);
@@ -46,9 +46,19 @@ final class LicensedProductType
add_action('woocommerce_licensed_add_to_cart', [$this, 'addToCartTemplate']);
add_action('woocommerce_licensed-variable_add_to_cart', [$this, 'variableAddToCartTemplate']);
// Use variable product add-to-cart handler for licensed-variable products
add_filter('woocommerce_add_to_cart_handler', [$this, 'addToCartHandler'], 10, 2);
// Make product virtual by default
add_filter('woocommerce_product_is_virtual', [$this, 'isVirtual'], 10, 2);
// Hide stock HTML for licensed products
add_filter('woocommerce_get_stock_html', [$this, 'hideStockHtml'], 10, 2);
add_filter('woocommerce_get_availability', [$this, 'hideAvailability'], 10, 2);
add_filter('woocommerce_get_availability_text', [$this, 'hideAvailabilityText'], 10, 2);
add_filter('woocommerce_product_get_stock_quantity', [$this, 'hideStockQuantity'], 10, 2);
add_filter('woocommerce_product_variation_get_stock_quantity', [$this, 'hideStockQuantity'], 10, 2);
// Display current version under product title on single product page
add_action('woocommerce_single_product_summary', [$this, 'displayCurrentVersion'], 6);
@@ -76,8 +86,13 @@ final class LicensedProductType
/**
* Get product class for licensed types
*
* @param string $className Default class name
* @param string $productType Product type
* @param string $postType Post type (usually 'product' or 'product_variation')
* @param mixed $productId Product ID (can be int or string)
*/
public function getProductClass(string $className, string $productType): string
public function getProductClass(string $className, string $productType, string $postType = '', $productId = 0): string
{
if ($productType === 'licensed') {
return LicensedProduct::class;
@@ -86,11 +101,24 @@ final class LicensedProductType
return LicensedVariableProduct::class;
}
// Handle variations of licensed-variable products
if ($productType === 'variation') {
// Check if parent is licensed-variable
global $post;
if ($post && $post->post_parent) {
$parentType = \WC_Product_Factory::get_product_type($post->post_parent);
// Check both by product type and by post type for variations
if ($productType === 'variation' || $postType === 'product_variation') {
// Get parent ID from the product post
$parentId = 0;
$productIdInt = (int) $productId;
if ($productIdInt > 0) {
$parentId = wp_get_post_parent_id($productIdInt);
}
// Fallback to global $post if product ID not available
if (!$parentId) {
global $post;
if ($post && $post->post_parent) {
$parentId = (int) $post->post_parent;
}
}
if ($parentId) {
$parentType = \WC_Product_Factory::get_product_type($parentId);
if ($parentType === 'licensed-variable') {
return LicensedProductVariation::class;
}
@@ -201,19 +229,21 @@ final class LicensedProductType
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Show/hide panels based on product type
// Show/hide panels based on product type for license settings tab
function toggleLicensedProductOptions() {
var productType = $('#product-type').val();
var isLicensed = productType === 'licensed';
var isLicensedVariable = productType === 'licensed-variable';
if (isLicensed || isLicensedVariable) {
// Show license settings tab
$('.show_if_licensed').show();
$('.show_if_licensed-variable').show();
$('.general_options').show();
$('.pricing').show();
$('.general_tab').show();
} else {
// Hide license settings tab for other product types
$('.show_if_licensed').hide();
$('.show_if_licensed-variable').hide();
}
@@ -263,26 +293,111 @@ final class LicensedProductType
wc_get_template('single-product/add-to-cart/simple.php');
}
/**
* Use the variable product add-to-cart handler for licensed-variable products
* WooCommerce uses product type to determine which handler to use
*/
public function addToCartHandler(string $handler, \WC_Product $product): string
{
if ($product->is_type('licensed-variable')) {
return 'variable';
}
return $handler;
}
/**
* Hide stock HTML for licensed products (they're always virtual/in-stock)
*/
public function hideStockHtml(string $html, \WC_Product $product): string
{
if ($this->isLicensedProductOrVariation($product)) {
return '';
}
return $html;
}
/**
* Hide availability data for licensed products (they're always virtual/in-stock)
*/
public function hideAvailability(array $availability, \WC_Product $product): array
{
if ($this->isLicensedProductOrVariation($product)) {
return [
'availability' => '',
'class' => '',
];
}
return $availability;
}
/**
* Hide availability text for licensed products
*/
public function hideAvailabilityText(string $availability, \WC_Product $product): string
{
if ($this->isLicensedProductOrVariation($product)) {
return '';
}
return $availability;
}
/**
* Hide stock quantity for licensed products (return null = no stock display)
*
* @param int|null $quantity
* @param \WC_Product $product
* @return int|null
*/
public function hideStockQuantity($quantity, \WC_Product $product)
{
if ($this->isLicensedProductOrVariation($product)) {
return null;
}
return $quantity;
}
/**
* Check if product is a licensed product or variation of one
*/
private function isLicensedProductOrVariation(\WC_Product $product): bool
{
// Direct licensed products
if ($product->is_type('licensed') || $product->is_type('licensed-variable')) {
return true;
}
// Check by class name for our custom variation class
if ($product instanceof LicensedProductVariation) {
return true;
}
// Check if this is a variation with a licensed-variable parent
// Use WC_Product_Factory::get_product_type() to get parent type directly from DB
// This is more reliable than loading the full product object
$parentId = $product->get_parent_id();
if ($parentId) {
$parentType = \WC_Product_Factory::get_product_type($parentId);
if ($parentType === 'licensed-variable') {
return true;
}
}
return false;
}
/**
* Make licensed products virtual by default
*/
public function isVirtual(bool $isVirtual, \WC_Product $product): bool
{
if ($product->is_type('licensed') || $product->is_type('licensed-variable')) {
if ($this->isLicensedProductOrVariation($product)) {
return true;
}
// Also handle variations of licensed-variable products
if ($product->is_type('variation') && $product->get_parent_id()) {
$parent = wc_get_product($product->get_parent_id());
if ($parent && $parent->is_type('licensed-variable')) {
return true;
}
}
return $isVirtual;
}
/**
* Enqueue frontend styles for licensed products on single product pages
* Enqueue frontend styles and scripts for licensed products on single product pages
*/
public function enqueueFrontendStyles(): void
{
@@ -302,6 +417,11 @@ final class LicensedProductType
[],
WC_LICENSED_PRODUCT_VERSION
);
// For licensed-variable products, enqueue WooCommerce variation scripts
if ($product->is_type('licensed-variable')) {
wp_enqueue_script('wc-add-to-cart-variation');
}
}
/**
@@ -331,10 +451,58 @@ final class LicensedProductType
/**
* Add to cart template for variable licensed products
* This mirrors WooCommerce's woocommerce_variable_add_to_cart() function
*/
public function variableAddToCartTemplate(): void
{
wc_get_template('single-product/add-to-cart/variable.php');
global $product;
// The hook woocommerce_licensed-variable_add_to_cart only fires for this product type
// so we just need to verify the product exists
if (!$product) {
return;
}
// Ensure we're working with a product that has variable product methods
// Re-load the product to ensure we get the correct class instance
$productId = $product->get_id();
$variableProduct = wc_get_product($productId);
if (!$variableProduct || !method_exists($variableProduct, 'get_variation_attributes')) {
// Fallback to simple add to cart if not a variable product
wc_get_template('single-product/add-to-cart/simple.php');
return;
}
// Update global $product to use the correctly loaded instance
// This ensures the template has the right product type
$product = $variableProduct;
// Get variations count to determine if we should load them via AJAX
$children = $variableProduct->get_children();
$getVariations = count($children) <= apply_filters('woocommerce_ajax_variation_threshold', 30, $variableProduct);
// Get template variables - WooCommerce expects these to be set
$availableVariations = $getVariations ? $variableProduct->get_available_variations() : false;
$attributes = $variableProduct->get_variation_attributes();
$selectedAttributes = $variableProduct->get_default_attributes();
// Ensure arrays (WooCommerce template expects arrays, not null)
if (!is_array($attributes)) {
$attributes = [];
}
if (!is_array($selectedAttributes)) {
$selectedAttributes = [];
}
wc_get_template(
'single-product/add-to-cart/variable.php',
[
'available_variations' => $availableVariations,
'attributes' => $attributes,
'selected_attributes' => $selectedAttributes,
]
);
}
/**
@@ -507,9 +675,13 @@ final class LicensedProductType
// Show general and variations tabs
$('.general_tab').show();
$('.variations_tab').show();
$('.variations_options').show();
// Hide shipping tab (virtual products)
$('.shipping_tab').hide();
// Ensure the variations panel can be displayed
$('#variable_product_options').show();
}
}
@@ -518,8 +690,29 @@ final class LicensedProductType
// On product type change
$('#product-type').on('change', function() {
toggleLicensedVariableOptions();
// Use setTimeout to let WooCommerce finish its own processing first
setTimeout(toggleLicensedVariableOptions, 100);
});
// Re-apply after WooCommerce AJAX operations that may reset visibility
$(document).on('woocommerce_variations_loaded', toggleLicensedVariableOptions);
$(document).on('woocommerce_variations_added', toggleLicensedVariableOptions);
$(document).on('woocommerce_variations_saved', toggleLicensedVariableOptions);
// Handle AJAX complete events for attribute saving
$(document).ajaxComplete(function(event, xhr, settings) {
// Check if this was a product data save or attribute action
if (settings.data && (
settings.data.indexOf('action=woocommerce_save_attributes') !== -1 ||
settings.data.indexOf('action=woocommerce_load_variations') !== -1 ||
settings.data.indexOf('action=woocommerce_add_variation') !== -1
)) {
setTimeout(toggleLicensedVariableOptions, 100);
}
});
// Also listen for the WooCommerce product type show/hide trigger
$('body').on('woocommerce-product-type-change', toggleLicensedVariableOptions);
});
</script>
<?php

View File

@@ -35,6 +35,61 @@ class LicensedProductVariation extends WC_Product_Variation
return true;
}
/**
* Licensed products are always in stock (virtual, no inventory)
*/
public function is_in_stock(): bool
{
return true;
}
/**
* Get availability - empty for licensed products (no stock indicator)
*/
public function get_availability(): array
{
return [
'availability' => '',
'class' => '',
];
}
/**
* Don't manage stock for licensed products
*/
public function managing_stock(): bool
{
return false;
}
/**
* Check if variation is purchasable
* Override to handle custom parent product type
*/
public function is_purchasable(): bool
{
// Check if variation exists
if (!$this->exists()) {
return false;
}
// Check parent product status
$parentId = $this->get_parent_id();
$parentStatus = get_post_status($parentId);
if ($parentStatus !== 'publish' && !current_user_can('edit_post', $parentId)) {
return false;
}
// Check if variation has a price
$price = $this->get_price();
if ($price === '' || $price === null) {
return false;
}
return apply_filters('woocommerce_variation_is_purchasable', true, $this);
}
/**
* Get max activations for this variation
* Falls back to parent product, then to default settings

View File

@@ -41,6 +41,19 @@ class LicensedVariableProduct extends WC_Product_Variable
return 'licensed-variable';
}
/**
* Check if product is of a certain type
* Override to return true for 'variable' as well, so WooCommerce internal
* checks pass (many methods in WC_Product_Variable check is_type('variable'))
*/
public function is_type($type): bool
{
if (is_array($type)) {
return in_array($this->get_type(), $type, true) || in_array('variable', $type, true);
}
return $this->get_type() === $type || 'variable' === $type;
}
/**
* Licensed products are always virtual
*/
@@ -50,11 +63,197 @@ class LicensedVariableProduct extends WC_Product_Variable
}
/**
* Licensed products are purchasable
* 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
{
return $this->exists() && $this->get_price() !== '';
// 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;
}
/**

View File

@@ -3,7 +3,7 @@
* Plugin Name: WooCommerce Licensed Product
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-licensed-product
* Description: WooCommerce plugin to sell software products using license keys with domain-based validation.
* Version: 0.5.7
* Version: 0.5.12
* Author: Marco Graetsch
* Author URI: https://src.bundespruefstelle.ch/magdev
* License: GPL-2.0-or-later
@@ -28,7 +28,7 @@ if (!defined('ABSPATH')) {
}
// Plugin constants
define('WC_LICENSED_PRODUCT_VERSION', '0.5.7');
define('WC_LICENSED_PRODUCT_VERSION', '0.5.12');
define('WC_LICENSED_PRODUCT_PLUGIN_FILE', __FILE__);
define('WC_LICENSED_PRODUCT_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WC_LICENSED_PRODUCT_PLUGIN_URL', plugin_dir_url(__FILE__));