From c31df1e8c47b9adddd3b06280ad4a5de29651aa8 Mon Sep 17 00:00:00 2001 From: magdev Date: Mon, 26 Jan 2026 16:14:15 +0100 Subject: [PATCH] Add licensed variable product support for duration-based licenses (v0.5.3) Customers can now purchase licenses with different durations (monthly, yearly, lifetime) through WooCommerce product variations. Each variation can have its own license validity settings. New features: - LicensedVariableProduct class for variable licensed products - LicensedProductVariation class for individual variations - Per-variation license duration and max activations settings - Duration labels in checkout (Monthly, Quarterly, Yearly, etc.) - Full support for WooCommerce Blocks checkout with variations - Updated translations for German (de_CH) Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 19 + CLAUDE.md | 45 +- assets/js/checkout-blocks.js | 138 ++- languages/wc-licensed-product-de_CH.mo | Bin 34090 -> 34897 bytes languages/wc-licensed-product-de_CH.po | 1153 ++++++++++---------- languages/wc-licensed-product.pot | 1089 +++++++++--------- src/Checkout/CheckoutBlocksIntegration.php | 41 +- src/Checkout/CheckoutController.php | 162 ++- src/Checkout/StoreApiExtension.php | 38 +- src/License/LicenseManager.php | 100 +- src/Plugin.php | 36 +- src/Product/LicensedProductType.php | 247 ++++- src/Product/LicensedProductVariation.php | 196 ++++ src/Product/LicensedVariableProduct.php | 151 +++ wc-licensed-product.php | 4 +- 15 files changed, 2235 insertions(+), 1184 deletions(-) create mode 100644 src/Product/LicensedProductVariation.php create mode 100644 src/Product/LicensedVariableProduct.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ac54e33..b732429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.5.3] - 2026-01-26 + +### Added + +- Variable licensed product type (`licensed-variable`) for selling licenses with different durations +- Support for monthly, yearly, quarterly, or lifetime license variations +- `LicensedVariableProduct` class extending `WC_Product_Variable` +- `LicensedProductVariation` class for individual variation license settings +- Variation-specific license duration settings in product edit page +- Duration labels displayed in checkout domain fields (e.g., "Yearly License") +- Variation ID tracking in order domain meta for proper license generation + +### Changed + +- Updated `LicenseManager::generateLicense()` to accept optional variation ID +- Checkout now handles variations with separate domain fields per product/variation +- WooCommerce Blocks checkout updated to display variation duration labels +- Store API extension updated to include variation_id in domain data schema + ## [0.5.2] - 2026-01-26 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 0aa8041..0c7c7b1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,10 +32,6 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w **Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file. -### Version 0.5.2 - -*No planned bugfixes yet.* - ### Version 0.6.0 *No planned features yet.* @@ -1341,3 +1337,44 @@ Security enhancement release adding per-license customer secrets for API respons - Created release package: `releases/wc-licensed-product-0.5.2.zip` (845 KB) - SHA256: `2d61a78ac5ba0f1d115a6401e6dded5b872b18f5530027c371604cbd18e9e27c` - Tagged as `v0.5.2` and pushed to `main` branch + +### 2026-01-26 - Version 0.5.3 - Variable Licensed Products + +**Overview:** + +Major feature release adding support for WooCommerce variable products. Customers can now purchase licenses with different durations (monthly, yearly, lifetime) as product variations. + +**New files:** + +- `src/Product/LicensedVariableProduct.php` - Variable product class extending `WC_Product_Variable` +- `src/Product/LicensedProductVariation.php` - Variation class with license settings + +**Implemented:** + +- New `licensed-variable` product type for selling licenses with different durations +- `LicensedVariableProduct` class extending WooCommerce variable products +- `LicensedProductVariation` class for individual variation license settings +- Variation-specific license duration fields in product edit page (days, max activations) +- Duration labels (Monthly, Quarterly, Yearly, Lifetime) displayed in checkout +- Variation ID tracking in order domain meta for proper license generation +- WooCommerce Blocks checkout updated to handle variations with duration labels + +**Modified files:** + +- `src/Product/LicensedProductType.php` - Added licensed-variable type registration, variation hooks +- `src/License/LicenseManager.php` - Added `isLicensedProduct()` helper, variation support in `generateLicense()` +- `src/Plugin.php` - Updated license generation to handle variations +- `src/Checkout/CheckoutController.php` - Variation support in domain field rendering +- `src/Checkout/CheckoutBlocksIntegration.php` - Variation data in blocks checkout +- `src/Checkout/StoreApiExtension.php` - Variation ID in Store API schema +- `assets/js/checkout-blocks.js` - Variation handling in React components and DOM fallback + +**Technical notes:** + +- Variable product type shows in WooCommerce product type selector as "Licensed Variable Product" +- Each variation can override parent's license duration and max activations +- Variations are always virtual (licensed products don't ship) +- `LicensedProductVariation::get_license_duration_label()` returns human-readable duration +- Order meta `_licensed_product_domains` now includes optional `variation_id` field +- License generation uses variation settings when `variation_id` is present in order item +- Backward compatible: existing simple licensed products continue to work unchanged diff --git a/assets/js/checkout-blocks.js b/assets/js/checkout-blocks.js index 1cdd17b..fbe72d9 100644 --- a/assets/js/checkout-blocks.js +++ b/assets/js/checkout-blocks.js @@ -110,6 +110,16 @@ ); }; + /** + * Get unique key for product (handles variations) + */ + function getProductKey(product) { + if (product.variation_id && product.variation_id > 0) { + return `${product.product_id}_${product.variation_id}`; + } + return String(product.product_id); + } + /** * Multi-Domain Component */ @@ -118,7 +128,8 @@ const [domains, setDomains] = useState(() => { const init = {}; products.forEach(p => { - init[p.product_id] = Array(p.quantity).fill(''); + const key = getProductKey(p); + init[key] = Array(p.quantity).fill(''); }); return init; }); @@ -128,16 +139,16 @@ return null; } - const handleChange = (productId, index, value) => { + const handleChange = (productKey, index, value) => { const normalized = normalizeDomain(value); const newDomains = { ...domains }; - if (!newDomains[productId]) newDomains[productId] = []; - newDomains[productId] = [...newDomains[productId]]; - newDomains[productId][index] = normalized; + if (!newDomains[productKey]) newDomains[productKey] = []; + newDomains[productKey] = [...newDomains[productKey]]; + newDomains[productKey][index] = normalized; setDomains(newDomains); // Validate - const key = `${productId}_${index}`; + const key = `${productKey}_${index}`; const newErrors = { ...errors }; if (normalized && !isValidDomain(normalized)) { newErrors[key] = settings.validationError || __('Please enter a valid domain.', 'wc-licensed-product'); @@ -145,14 +156,14 @@ delete newErrors[key]; } - // Check for duplicates within same product - const productDomains = newDomains[productId].filter(d => d); + // Check for duplicates within same product/variation + const productDomains = newDomains[productKey].filter(d => d); const uniqueDomains = new Set(productDomains.map(d => normalizeDomain(d))); if (productDomains.length !== uniqueDomains.size) { const seen = new Set(); - newDomains[productId].forEach((d, idx) => { + newDomains[productKey].forEach((d, idx) => { const normalizedD = normalizeDomain(d); - const dupKey = `${productId}_${idx}`; + const dupKey = `${productKey}_${idx}`; if (normalizedD && seen.has(normalizedD)) { newErrors[dupKey] = settings.duplicateError || __('Each license requires a unique domain.', 'wc-licensed-product'); } else if (normalizedD) { @@ -163,11 +174,19 @@ setErrors(newErrors); - // Update hidden field - const data = Object.entries(newDomains).map(([pid, doms]) => ({ - product_id: parseInt(pid, 10), - domains: doms.filter(d => d), - })).filter(item => item.domains.length > 0); + // Update hidden field with variation support + const data = products.map(p => { + const pKey = getProductKey(p); + const doms = newDomains[pKey] || []; + const entry = { + product_id: p.product_id, + domains: doms.filter(d => d), + }; + if (p.variation_id && p.variation_id > 0) { + entry.variation_id = p.variation_id; + } + return entry; + }).filter(item => item.domains.length > 0); const hiddenInput = document.getElementById('wclp-domains-hidden'); if (hiddenInput) { @@ -192,35 +211,43 @@ createElement('p', { style: { marginBottom: '12px', color: '#666', fontSize: '0.9em' } }, settings.fieldDescription || __('Enter a unique domain for each license.', 'wc-licensed-product') ), - products.map(product => createElement( - 'div', - { - key: product.product_id, - style: { - marginBottom: '16px', - padding: '12px', - backgroundColor: '#fff', - borderRadius: '4px', - } - }, - createElement('strong', { style: { display: 'block', marginBottom: '8px' } }, - product.name + (product.quantity > 1 ? ` (×${product.quantity})` : '') - ), - Array.from({ length: product.quantity }, (_, i) => { - const key = `${product.product_id}_${i}`; - return createElement( - 'div', - { key: i, style: { marginBottom: '8px' } }, - createElement(TextControl, { - label: (settings.licenseLabel || __('License %d:', 'wc-licensed-product')).replace('%d', i + 1), - value: domains[product.product_id]?.[i] || '', - onChange: (val) => handleChange(product.product_id, i, val), - placeholder: settings.fieldPlaceholder || 'example.com', - help: errors[key] || '', - }) - ); - }) - )), + products.map(product => { + const productKey = getProductKey(product); + const durationLabel = product.duration_label || ''; + const displayName = durationLabel + ? `${product.name} (${durationLabel})` + : product.name; + + return createElement( + 'div', + { + key: productKey, + style: { + marginBottom: '16px', + padding: '12px', + backgroundColor: '#fff', + borderRadius: '4px', + } + }, + createElement('strong', { style: { display: 'block', marginBottom: '8px' } }, + displayName + (product.quantity > 1 ? ` ×${product.quantity}` : '') + ), + Array.from({ length: product.quantity }, (_, i) => { + const key = `${productKey}_${i}`; + return createElement( + 'div', + { key: i, style: { marginBottom: '8px' } }, + createElement(TextControl, { + label: (settings.licenseLabel || __('License %d:', 'wc-licensed-product')).replace('%d', i + 1), + value: domains[productKey]?.[i] || '', + onChange: (val) => handleChange(productKey, i, val), + placeholder: settings.fieldPlaceholder || 'example.com', + help: errors[key] || '', + }) + ); + }) + ); + }), createElement('input', { type: 'hidden', id: 'wclp-domains-hidden', @@ -291,10 +318,19 @@

${settings.fieldDescription || 'Enter a unique domain for each license.'}

- ${settings.licensedProducts.map(product => ` + ${settings.licensedProducts.map(product => { + const productKey = product.variation_id && product.variation_id > 0 + ? `${product.product_id}_${product.variation_id}` + : product.product_id; + const durationLabel = product.duration_label || ''; + const displayName = durationLabel + ? `${product.name} (${durationLabel})` + : product.name; + + return `
- ${product.name}${product.quantity > 1 ? ` (×${product.quantity})` : ''} + ${displayName}${product.quantity > 1 ? ` ×${product.quantity}` : ''} ${Array.from({ length: product.quantity }, (_, i) => `
@@ -302,14 +338,20 @@ ${(settings.licenseLabel || 'License %d:').replace('%d', i + 1)} + ${product.variation_id && product.variation_id > 0 ? ` + + ` : ''}
`).join('')}
- `).join('')} + `}).join('')} `; } else { container.innerHTML = ` diff --git a/languages/wc-licensed-product-de_CH.mo b/languages/wc-licensed-product-de_CH.mo index d0f5597c46e3be27d66222060c6b8c47fb156d6a..59b678ca8dd0dfe62ce03045a3624ff07ca76679 100644 GIT binary patch delta 9298 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 KKEG^Fh5rB~OB{Fr diff --git a/languages/wc-licensed-product-de_CH.po b/languages/wc-licensed-product-de_CH.po index f2b3300..8349f7d 100644 --- a/languages/wc-licensed-product-de_CH.po +++ b/languages/wc-licensed-product-de_CH.po @@ -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-26 15:26+0100\n" +"POT-Creation-Date: 2026-01-26 16:08+0100\n" "PO-Revision-Date: 2026-01-25T18:30:00+00:00\n" "Last-Translator: Marco Graetsch \n" "Language-Team: German (Switzerland) \n" @@ -15,6 +15,235 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +#: src/Admin/VersionAdminController.php:58 +msgid "Product Versions" +msgstr "Produktversionen" + +#: src/Admin/VersionAdminController.php:78 +msgid "Add New Version" +msgstr "Neue Version hinzufügen" + +#: src/Admin/VersionAdminController.php:81 +#: src/Admin/VersionAdminController.php:136 src/Admin/AdminController.php:1609 +msgid "Version" +msgstr "Version" + +#: src/Admin/VersionAdminController.php:84 +msgid "Use semantic versioning (e.g., 1.0.0)" +msgstr "Verwenden Sie semantische Versionierung (z.B. 1.0.0)" + +#: src/Admin/VersionAdminController.php:88 +#: src/Admin/VersionAdminController.php:137 +msgid "Download File" +msgstr "Download-Datei" + +#: src/Admin/VersionAdminController.php:93 +msgid "Select File" +msgstr "Datei auswählen" + +#: src/Admin/VersionAdminController.php:96 +#: src/Admin/VersionAdminController.php:110 +msgid "Remove" +msgstr "Entfernen" + +#: src/Admin/VersionAdminController.php:98 +msgid "" +"Upload or select a file from the media library. Version will be auto-" +"detected from filename (e.g., plugin-v1.2.3.zip)." +msgstr "" +"Laden Sie eine Datei hoch oder wählen Sie eine aus der Mediathek. Die " +"Version wird automatisch aus dem Dateinamen erkannt (z.B. plugin-v1.2.3.zip)." + +#: src/Admin/VersionAdminController.php:102 +msgid "Checksum File" +msgstr "Prüfsummendatei" + +#: src/Admin/VersionAdminController.php:107 +msgid "Select Checksum File" +msgstr "Prüfsummendatei auswählen" + +#: src/Admin/VersionAdminController.php:112 +msgid "" +"Upload a SHA256 checksum file (.sha256 or .txt) to verify file integrity." +msgstr "" +"Laden Sie eine SHA256-Prüfsummendatei (.sha256 oder .txt) hoch, um die " +"Dateiintegrität zu überprüfen." + +#: src/Admin/VersionAdminController.php:116 +#: src/Admin/VersionAdminController.php:139 +msgid "Release Notes" +msgstr "Versionshinweise" + +#: src/Admin/VersionAdminController.php:124 +msgid "Add Version" +msgstr "Version hinzufügen" + +#: src/Admin/VersionAdminController.php:132 +msgid "Existing Versions" +msgstr "Vorhandene Versionen" + +#: src/Admin/VersionAdminController.php:138 +msgid "SHA256" +msgstr "SHA256" + +#: src/Admin/VersionAdminController.php:140 src/Admin/AdminController.php:1295 +#: src/Admin/AdminController.php:1446 src/Admin/OrderLicenseController.php:206 +msgid "Status" +msgstr "Status" + +#: src/Admin/VersionAdminController.php:141 +msgid "Released" +msgstr "Veröffentlicht" + +#: src/Admin/VersionAdminController.php:142 src/Admin/AdminController.php:1298 +#: src/Admin/AdminController.php:1449 src/Admin/OrderLicenseController.php:208 +msgid "Actions" +msgstr "Aktionen" + +#: src/Admin/VersionAdminController.php:148 +msgid "No versions found. Add your first version above." +msgstr "Keine Versionen gefunden. Fügen Sie Ihre erste Version oben hinzu." + +#: src/Admin/VersionAdminController.php:165 +#: src/Admin/VersionAdminController.php:396 +msgid "Uploaded file" +msgstr "Hochgeladene Datei" + +#: src/Admin/VersionAdminController.php:169 +#: src/Admin/VersionAdminController.php:400 +msgid "No download file" +msgstr "Keine Download-Datei" + +#: src/Admin/VersionAdminController.php:182 +#: src/Admin/VersionAdminController.php:413 src/Admin/AdminController.php:156 +#: src/Admin/AdminController.php:907 src/Admin/AdminController.php:1232 +#: src/Admin/AdminController.php:1355 +#: src/Admin/DashboardWidgetController.php:117 +msgid "Active" +msgstr "Aktiv" + +#: src/Admin/VersionAdminController.php:182 +#: src/Admin/VersionAdminController.php:413 src/Admin/AdminController.php:157 +#: src/Admin/AdminController.php:914 src/Admin/AdminController.php:1233 +#: src/Admin/AdminController.php:1356 +msgid "Inactive" +msgstr "Inaktiv" + +#: src/Admin/VersionAdminController.php:188 +#: src/Admin/VersionAdminController.php:419 src/Admin/AdminController.php:1274 +#: src/Admin/AdminController.php:1459 +msgid "Deactivate" +msgstr "Deaktivieren" + +#: src/Admin/VersionAdminController.php:188 +#: src/Admin/VersionAdminController.php:419 src/Admin/AdminController.php:1273 +#: src/Admin/AdminController.php:1458 +msgid "Activate" +msgstr "Aktivieren" + +#: src/Admin/VersionAdminController.php:191 +#: src/Admin/VersionAdminController.php:422 src/Admin/AdminController.php:1279 +#: src/Admin/AdminController.php:1428 src/Admin/AdminController.php:1464 +msgid "Delete" +msgstr "Löschen" + +#: src/Admin/VersionAdminController.php:232 +msgid "Are you sure you want to delete this version?" +msgstr "Sind Sie sicher, dass Sie diese Version löschen möchten?" + +#: src/Admin/VersionAdminController.php:233 +msgid "Please enter a version number." +msgstr "Bitte geben Sie eine Versionsnummer ein." + +#: src/Admin/VersionAdminController.php:234 +msgid "Please enter a valid version number (e.g., 1.0.0)." +msgstr "Bitte geben Sie eine gültige Versionsnummer ein (z.B. 1.0.0)." + +#: src/Admin/VersionAdminController.php:235 +msgid "An error occurred. Please try again." +msgstr "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut." + +#: src/Admin/VersionAdminController.php:236 +msgid "Select Download File" +msgstr "Download-Datei auswählen" + +#: src/Admin/VersionAdminController.php:237 +msgid "Use this file" +msgstr "Diese Datei verwenden" + +#: src/Admin/VersionAdminController.php:238 +msgid "" +"Invalid checksum file format. File must contain a 64-character SHA256 hash." +msgstr "" +"Ungültiges Prüfsummendateiformat. Die Datei muss einen 64-stelligen SHA256-" +"Hash enthalten." + +#: src/Admin/VersionAdminController.php:239 +msgid "Failed to read checksum file." +msgstr "Prüfsummendatei konnte nicht gelesen werden." + +#: src/Admin/VersionAdminController.php:259 +#: src/Admin/VersionAdminController.php:328 +#: src/Admin/VersionAdminController.php:354 src/Admin/AdminController.php:173 +#: src/Admin/AdminController.php:213 src/Admin/AdminController.php:249 +#: src/Admin/AdminController.php:301 src/Admin/AdminController.php:339 +#: src/Admin/AdminController.php:369 src/Admin/OrderLicenseController.php:387 +#: src/Admin/OrderLicenseController.php:426 +#: src/Admin/OrderLicenseController.php:490 +msgid "Permission denied." +msgstr "Zugriff verweigert." + +#: src/Admin/VersionAdminController.php:269 +msgid "Product ID and version are required." +msgstr "Produkt-ID und Version sind erforderlich." + +#: src/Admin/VersionAdminController.php:274 +msgid "Invalid version format. Use semantic versioning (e.g., 1.0.0)." +msgstr "" +"Ungültiges Versionsformat. Verwenden Sie semantische Versionierung (z.B. " +"1.0.0)." + +#: src/Admin/VersionAdminController.php:279 +msgid "This version already exists." +msgstr "Diese Version existiert bereits." + +#: src/Admin/VersionAdminController.php:285 +msgid "Product not found." +msgstr "Produkt nicht gefunden." + +#: src/Admin/VersionAdminController.php:289 +msgid "This product is not a licensed product." +msgstr "Dieses Produkt ist kein lizensiertes Produkt." + +#: src/Admin/VersionAdminController.php:306 +msgid "Failed to create version." +msgstr "Version konnte nicht erstellt werden." + +#: src/Admin/VersionAdminController.php:314 +msgid "Version added successfully." +msgstr "Version erfolgreich hinzugefügt." + +#: src/Admin/VersionAdminController.php:334 +#: src/Admin/VersionAdminController.php:361 +msgid "Version ID is required." +msgstr "Versions-ID ist erforderlich." + +#: src/Admin/VersionAdminController.php:340 +msgid "Failed to delete version." +msgstr "Version konnte nicht gelöscht werden." + +#: src/Admin/VersionAdminController.php:343 +msgid "Version deleted successfully." +msgstr "Version erfolgreich gelöscht." + +#: src/Admin/VersionAdminController.php:367 +msgid "Failed to update version." +msgstr "Version konnte nicht aktualisiert werden." + +#: src/Admin/VersionAdminController.php:371 +msgid "Version updated successfully." +msgstr "Version erfolgreich aktualisiert." + #: src/Admin/AdminController.php:76 src/Admin/AdminController.php:77 #: src/Admin/AdminController.php:90 src/Admin/AdminController.php:1200 #: src/Admin/OrderLicenseController.php:149 @@ -81,9 +310,11 @@ msgstr "Speichern" #: src/Admin/AdminController.php:1373 src/Admin/AdminController.php:1613 #: src/Admin/DashboardWidgetController.php:136 #: src/Admin/OrderLicenseController.php:260 -#: src/Admin/SettingsController.php:192 src/Frontend/AccountController.php:286 -#: src/Product/LicensedProductType.php:110 -#: src/Product/LicensedProductType.php:158 +#: src/Admin/SettingsController.php:192 src/Product/LicensedProductType.php:136 +#: src/Product/LicensedProductType.php:184 +#: src/Product/LicensedProductType.php:379 +#: src/Product/LicensedProductVariation.php:139 +#: src/Frontend/AccountController.php:286 msgid "Lifetime" msgstr "Lebenslang" @@ -95,21 +326,6 @@ msgstr "Kopiert!" msgid "Copy failed" msgstr "Kopieren fehlgeschlagen" -#: src/Admin/AdminController.php:156 src/Admin/AdminController.php:907 -#: src/Admin/AdminController.php:1232 src/Admin/AdminController.php:1355 -#: src/Admin/DashboardWidgetController.php:117 -#: src/Admin/VersionAdminController.php:182 -#: src/Admin/VersionAdminController.php:413 -msgid "Active" -msgstr "Aktiv" - -#: src/Admin/AdminController.php:157 src/Admin/AdminController.php:914 -#: src/Admin/AdminController.php:1233 src/Admin/AdminController.php:1356 -#: src/Admin/VersionAdminController.php:182 -#: src/Admin/VersionAdminController.php:413 -msgid "Inactive" -msgstr "Inaktiv" - #: src/Admin/AdminController.php:158 src/Admin/AdminController.php:921 #: src/Admin/AdminController.php:1234 src/Admin/AdminController.php:1357 #: src/Admin/DashboardWidgetController.php:125 @@ -122,18 +338,6 @@ msgstr "Abgelaufen" msgid "Revoked" msgstr "Widerrufen" -#: src/Admin/AdminController.php:173 src/Admin/AdminController.php:213 -#: src/Admin/AdminController.php:249 src/Admin/AdminController.php:301 -#: src/Admin/AdminController.php:339 src/Admin/AdminController.php:369 -#: src/Admin/OrderLicenseController.php:387 -#: src/Admin/OrderLicenseController.php:426 -#: src/Admin/OrderLicenseController.php:490 -#: src/Admin/VersionAdminController.php:259 -#: src/Admin/VersionAdminController.php:328 -#: src/Admin/VersionAdminController.php:354 -msgid "Permission denied." -msgstr "Zugriff verweigert." - #: src/Admin/AdminController.php:195 src/Admin/AdminController.php:1019 #: src/Admin/OrderLicenseController.php:227 msgid "Unknown" @@ -267,7 +471,7 @@ msgstr "Lizenzen verwalten" msgid "Export to CSV" msgstr "Als CSV exportieren" -#: src/Admin/AdminController.php:968 +#: src/Admin/AdminController.php:968 wc-licensed-product.php:137 msgid "Settings" msgstr "Einstellungen" @@ -434,18 +638,6 @@ msgstr "Dashboard anzeigen" msgid "Bulk Actions" msgstr "Massenaktionen" -#: src/Admin/AdminController.php:1273 src/Admin/AdminController.php:1458 -#: src/Admin/VersionAdminController.php:188 -#: src/Admin/VersionAdminController.php:419 -msgid "Activate" -msgstr "Aktivieren" - -#: src/Admin/AdminController.php:1274 src/Admin/AdminController.php:1459 -#: src/Admin/VersionAdminController.php:188 -#: src/Admin/VersionAdminController.php:419 -msgid "Deactivate" -msgstr "Deaktivieren" - #: src/Admin/AdminController.php:1275 src/Admin/AdminController.php:1419 #: src/Admin/AdminController.php:1460 msgid "Revoke" @@ -463,12 +655,6 @@ msgstr "90 Tage verlängern" msgid "Extend 1 year" msgstr "1 Jahr verlängern" -#: src/Admin/AdminController.php:1279 src/Admin/AdminController.php:1428 -#: src/Admin/AdminController.php:1464 src/Admin/VersionAdminController.php:191 -#: src/Admin/VersionAdminController.php:422 -msgid "Delete" -msgstr "Löschen" - #: src/Admin/AdminController.php:1281 src/Admin/AdminController.php:1466 msgid "Apply" msgstr "Anwenden" @@ -493,18 +679,12 @@ msgstr "Kunde" #: src/Admin/AdminController.php:1294 src/Admin/AdminController.php:1445 #: src/Admin/AdminController.php:1495 src/Admin/OrderLicenseController.php:205 -#: src/Checkout/CheckoutBlocksIntegration.php:129 -#: src/Checkout/CheckoutController.php:122 +#: src/Checkout/CheckoutBlocksIntegration.php:130 +#: src/Checkout/CheckoutController.php:161 #: src/Email/LicenseEmailController.php:288 msgid "Domain" msgstr "Domain" -#: src/Admin/AdminController.php:1295 src/Admin/AdminController.php:1446 -#: src/Admin/OrderLicenseController.php:206 -#: src/Admin/VersionAdminController.php:140 -msgid "Status" -msgstr "Status" - #: src/Admin/AdminController.php:1296 src/Admin/AdminController.php:1447 msgid "Created" msgstr "Erstellt" @@ -516,12 +696,6 @@ msgstr "Erstellt" msgid "Expires" msgstr "Läuft ab" -#: src/Admin/AdminController.php:1298 src/Admin/AdminController.php:1449 -#: src/Admin/OrderLicenseController.php:208 -#: src/Admin/VersionAdminController.php:142 -msgid "Actions" -msgstr "Aktionen" - #: src/Admin/AdminController.php:1304 msgid "No licenses found." msgstr "Keine Lizenzen gefunden." @@ -602,11 +776,6 @@ msgstr "Lizenz übertragen" msgid "License is VALID" msgstr "Lizenz ist GÜLTIG" -#: src/Admin/AdminController.php:1609 src/Admin/VersionAdminController.php:81 -#: src/Admin/VersionAdminController.php:136 -msgid "Version" -msgstr "Version" - #: src/Admin/AdminController.php:1617 msgid "License is INVALID" msgstr "Lizenz ist UNGÜLTIG" @@ -787,13 +956,13 @@ msgid "Domains specified during checkout (multi-domain order)." msgstr "Bei der Bestellung angegebene Domains (Multi-Domain-Bestellung)." #: src/Admin/OrderLicenseController.php:119 -#: src/Checkout/CheckoutController.php:436 -#: src/Checkout/CheckoutController.php:486 -#: src/Checkout/CheckoutController.php:496 +#: src/Checkout/CheckoutController.php:530 +#: src/Checkout/CheckoutController.php:591 +#: src/Checkout/CheckoutController.php:613 src/License/LicenseManager.php:878 +#: src/Product/VersionManager.php:349 src/Product/VersionManager.php:361 +#: src/Frontend/AccountController.php:148 #: src/Email/LicenseExpirationEmail.php:107 -#: src/Email/LicenseExpiredEmail.php:99 src/Frontend/AccountController.php:148 -#: src/License/LicenseManager.php:806 src/Product/VersionManager.php:349 -#: src/Product/VersionManager.php:361 +#: src/Email/LicenseExpiredEmail.php:99 msgid "Unknown Product" msgstr "Unbekanntes Produkt" @@ -806,10 +975,10 @@ msgstr "" "automatisch bestehende Lizenz-Domains." #: src/Admin/OrderLicenseController.php:137 -#: src/Checkout/CheckoutBlocksIntegration.php:83 -#: src/Checkout/CheckoutBlocksIntegration.php:119 -#: src/Checkout/CheckoutController.php:130 -#: src/Checkout/CheckoutController.php:186 +#: src/Checkout/CheckoutBlocksIntegration.php:84 +#: src/Checkout/CheckoutBlocksIntegration.php:120 +#: src/Checkout/CheckoutController.php:169 +#: src/Checkout/CheckoutController.php:235 msgid "example.com" msgstr "beispiel.ch" @@ -877,7 +1046,7 @@ msgid "Error. Please try again." msgstr "Fehler. Bitte versuchen Sie es erneut." #: src/Admin/OrderLicenseController.php:373 -#: src/Checkout/CheckoutBlocksIntegration.php:126 +#: src/Checkout/CheckoutBlocksIntegration.php:127 #: src/Frontend/AccountController.php:430 #: src/Frontend/AccountController.php:462 msgid "Please enter a valid domain." @@ -902,8 +1071,8 @@ msgid "Order domain updated." msgstr "Bestellungs-Domain aktualisiert." #: src/Admin/OrderLicenseController.php:449 -#: src/Frontend/AccountController.php:468 #: src/Frontend/DownloadController.php:117 +#: src/Frontend/AccountController.php:468 msgid "License not found." msgstr "Lizenz nicht gefunden." @@ -1113,182 +1282,12 @@ msgstr "Lizenz erfolgreich überprüft!" msgid "License validation failed." msgstr "Lizenzvalidierung fehlgeschlagen." -#: src/Admin/VersionAdminController.php:58 -msgid "Product Versions" -msgstr "Produktversionen" - -#: src/Admin/VersionAdminController.php:78 -msgid "Add New Version" -msgstr "Neue Version hinzufügen" - -#: src/Admin/VersionAdminController.php:84 -msgid "Use semantic versioning (e.g., 1.0.0)" -msgstr "Verwenden Sie semantische Versionierung (z.B. 1.0.0)" - -#: src/Admin/VersionAdminController.php:88 -#: src/Admin/VersionAdminController.php:137 -msgid "Download File" -msgstr "Download-Datei" - -#: src/Admin/VersionAdminController.php:93 -msgid "Select File" -msgstr "Datei auswählen" - -#: src/Admin/VersionAdminController.php:96 -#: src/Admin/VersionAdminController.php:110 -msgid "Remove" -msgstr "Entfernen" - -#: src/Admin/VersionAdminController.php:98 -msgid "" -"Upload or select a file from the media library. Version will be auto-" -"detected from filename (e.g., plugin-v1.2.3.zip)." -msgstr "" -"Laden Sie eine Datei hoch oder wählen Sie eine aus der Mediathek. Die " -"Version wird automatisch aus dem Dateinamen erkannt (z.B. plugin-v1.2.3.zip)." - -#: src/Admin/VersionAdminController.php:102 -msgid "Checksum File" -msgstr "Prüfsummendatei" - -#: src/Admin/VersionAdminController.php:107 -msgid "Select Checksum File" -msgstr "Prüfsummendatei auswählen" - -#: src/Admin/VersionAdminController.php:112 -msgid "" -"Upload a SHA256 checksum file (.sha256 or .txt) to verify file integrity." -msgstr "" -"Laden Sie eine SHA256-Prüfsummendatei (.sha256 oder .txt) hoch, um die " -"Dateiintegrität zu überprüfen." - -#: src/Admin/VersionAdminController.php:116 -#: src/Admin/VersionAdminController.php:139 -msgid "Release Notes" -msgstr "Versionshinweise" - -#: src/Admin/VersionAdminController.php:124 -msgid "Add Version" -msgstr "Version hinzufügen" - -#: src/Admin/VersionAdminController.php:132 -msgid "Existing Versions" -msgstr "Vorhandene Versionen" - -#: src/Admin/VersionAdminController.php:138 -msgid "SHA256" -msgstr "SHA256" - -#: src/Admin/VersionAdminController.php:141 -msgid "Released" -msgstr "Veröffentlicht" - -#: src/Admin/VersionAdminController.php:148 -msgid "No versions found. Add your first version above." -msgstr "Keine Versionen gefunden. Fügen Sie Ihre erste Version oben hinzu." - -#: src/Admin/VersionAdminController.php:165 -#: src/Admin/VersionAdminController.php:396 -msgid "Uploaded file" -msgstr "Hochgeladene Datei" - -#: src/Admin/VersionAdminController.php:169 -#: src/Admin/VersionAdminController.php:400 -msgid "No download file" -msgstr "Keine Download-Datei" - -#: src/Admin/VersionAdminController.php:232 -msgid "Are you sure you want to delete this version?" -msgstr "Sind Sie sicher, dass Sie diese Version löschen möchten?" - -#: src/Admin/VersionAdminController.php:233 -msgid "Please enter a version number." -msgstr "Bitte geben Sie eine Versionsnummer ein." - -#: src/Admin/VersionAdminController.php:234 -msgid "Please enter a valid version number (e.g., 1.0.0)." -msgstr "Bitte geben Sie eine gültige Versionsnummer ein (z.B. 1.0.0)." - -#: src/Admin/VersionAdminController.php:235 -msgid "An error occurred. Please try again." -msgstr "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut." - -#: src/Admin/VersionAdminController.php:236 -msgid "Select Download File" -msgstr "Download-Datei auswählen" - -#: src/Admin/VersionAdminController.php:237 -msgid "Use this file" -msgstr "Diese Datei verwenden" - -#: src/Admin/VersionAdminController.php:238 -msgid "" -"Invalid checksum file format. File must contain a 64-character SHA256 hash." -msgstr "" -"Ungültiges Prüfsummendateiformat. Die Datei muss einen 64-stelligen SHA256-" -"Hash enthalten." - -#: src/Admin/VersionAdminController.php:239 -msgid "Failed to read checksum file." -msgstr "Prüfsummendatei konnte nicht gelesen werden." - -#: src/Admin/VersionAdminController.php:269 -msgid "Product ID and version are required." -msgstr "Produkt-ID und Version sind erforderlich." - -#: src/Admin/VersionAdminController.php:274 -msgid "Invalid version format. Use semantic versioning (e.g., 1.0.0)." -msgstr "" -"Ungültiges Versionsformat. Verwenden Sie semantische Versionierung (z.B. " -"1.0.0)." - -#: src/Admin/VersionAdminController.php:279 -msgid "This version already exists." -msgstr "Diese Version existiert bereits." - -#: src/Admin/VersionAdminController.php:285 -msgid "Product not found." -msgstr "Produkt nicht gefunden." - -#: src/Admin/VersionAdminController.php:289 -msgid "This product is not a licensed product." -msgstr "Dieses Produkt ist kein lizensiertes Produkt." - -#: src/Admin/VersionAdminController.php:306 -msgid "Failed to create version." -msgstr "Version konnte nicht erstellt werden." - -#: src/Admin/VersionAdminController.php:314 -msgid "Version added successfully." -msgstr "Version erfolgreich hinzugefügt." - -#: src/Admin/VersionAdminController.php:334 -#: src/Admin/VersionAdminController.php:361 -msgid "Version ID is required." -msgstr "Versions-ID ist erforderlich." - -#: src/Admin/VersionAdminController.php:340 -msgid "Failed to delete version." -msgstr "Version konnte nicht gelöscht werden." - -#: src/Admin/VersionAdminController.php:343 -msgid "Version deleted successfully." -msgstr "Version erfolgreich gelöscht." - -#: src/Admin/VersionAdminController.php:367 -msgid "Failed to update version." -msgstr "Version konnte nicht aktualisiert werden." - -#: src/Admin/VersionAdminController.php:371 -msgid "Version updated successfully." -msgstr "Version erfolgreich aktualisiert." - #: src/Api/RestApiController.php:84 msgid "Too many requests. Please try again later." msgstr "Zu viele Anfragen. Bitte versuchen Sie es später erneut." #: src/Api/RestApiController.php:345 src/Api/RestApiController.php:378 -#: src/License/LicenseManager.php:403 +#: src/License/LicenseManager.php:475 msgid "License key not found." msgstr "Lizenzschlüssel nicht gefunden." @@ -1312,69 +1311,69 @@ msgstr "Lizenz konnte nicht aktiviert werden." msgid "License activated successfully." msgstr "Lizenz erfolgreich aktiviert." -#: src/Checkout/CheckoutBlocksIntegration.php:78 -#: src/Checkout/CheckoutBlocksIntegration.php:125 -#: src/Checkout/CheckoutController.php:119 +#: src/Checkout/CheckoutBlocksIntegration.php:79 +#: src/Checkout/CheckoutBlocksIntegration.php:126 +#: src/Checkout/CheckoutController.php:158 msgid "License Domain" msgstr "Lizenz-Domain" -#: src/Checkout/CheckoutBlocksIntegration.php:85 +#: src/Checkout/CheckoutBlocksIntegration.php:86 msgid "Enter a valid domain (without http:// or www)" msgstr "Geben Sie eine gültige Domain ein (ohne http:// oder www)" -#: src/Checkout/CheckoutBlocksIntegration.php:121 -#: src/Checkout/CheckoutController.php:150 +#: src/Checkout/CheckoutBlocksIntegration.php:122 +#: src/Checkout/CheckoutController.php:189 msgid "Enter a unique domain for each license (without http:// or www)." msgstr "" "Geben Sie für jede Lizenz eine eindeutige Domain ein (ohne http:// oder www)." -#: src/Checkout/CheckoutBlocksIntegration.php:122 -#: src/Checkout/CheckoutController.php:134 +#: src/Checkout/CheckoutBlocksIntegration.php:123 +#: src/Checkout/CheckoutController.php:173 msgid "" "Enter the domain where you will use the license (without http:// or www)." msgstr "" "Geben Sie die Domain ein, auf der Sie die Lizenz verwenden möchten (ohne " "http:// oder www)." -#: src/Checkout/CheckoutBlocksIntegration.php:124 -#: src/Checkout/CheckoutController.php:148 +#: src/Checkout/CheckoutBlocksIntegration.php:125 +#: src/Checkout/CheckoutController.php:187 msgid "License Domains" msgstr "Lizenz-Domains" -#: src/Checkout/CheckoutBlocksIntegration.php:127 +#: src/Checkout/CheckoutBlocksIntegration.php:128 msgid "Each license requires a unique domain." msgstr "Jede Lizenz erfordert eine eindeutige Domain." -#: src/Checkout/CheckoutBlocksIntegration.php:128 -#: src/Checkout/CheckoutController.php:175 +#: src/Checkout/CheckoutBlocksIntegration.php:129 +#: src/Checkout/CheckoutController.php:224 #, php-format msgid "License %d:" msgstr "Lizenz %d:" -#: src/Checkout/CheckoutController.php:123 -#: src/Checkout/CheckoutController.php:179 +#: src/Checkout/CheckoutController.php:162 +#: src/Checkout/CheckoutController.php:228 msgid "required" msgstr "erforderlich" -#: src/Checkout/CheckoutController.php:258 +#: src/Checkout/CheckoutController.php:323 msgid "Please enter a domain for your license." msgstr "Bitte geben Sie eine Domain für Ihre Lizenz ein." -#: src/Checkout/CheckoutController.php:264 +#: src/Checkout/CheckoutController.php:329 msgid "Please enter a valid domain for your license." msgstr "Bitte geben Sie eine gültige Domain für Ihre Lizenz ein." -#: src/Checkout/CheckoutController.php:287 +#: 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." -#: src/Checkout/CheckoutController.php:302 +#: 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." -#: src/Checkout/CheckoutController.php:316 +#: src/Checkout/CheckoutController.php:385 #, php-format msgid "" "The domain \"%1$s\" is used multiple times for %2$s. Each license requires a " @@ -1383,76 +1382,302 @@ msgstr "" "Die Domain \"%1$s\" wird mehrfach für %2$s verwendet. Jede Lizenz erfordert " "eine eindeutige Domain." -#: src/Checkout/CheckoutController.php:419 -#: src/Checkout/CheckoutController.php:466 -#: src/Checkout/CheckoutController.php:470 +#: src/Checkout/CheckoutController.php:500 +#: src/Checkout/CheckoutController.php:561 +#: src/Checkout/CheckoutController.php:565 msgid "License Domain:" msgstr "Lizenz-Domain:" -#: src/Checkout/CheckoutController.php:432 -#: src/Checkout/CheckoutController.php:483 -#: src/Checkout/CheckoutController.php:492 +#: src/Checkout/CheckoutController.php:513 +#: src/Checkout/CheckoutController.php:578 +#: src/Checkout/CheckoutController.php:599 msgid "License Domains:" msgstr "Lizenz-Domains:" +#: src/Checkout/CheckoutController.php:522 +#: src/Checkout/CheckoutController.php:585 +#: src/Checkout/CheckoutController.php:607 +msgid "Unknown Variation" +msgstr "Unbekannte Variante" + #: src/Checkout/StoreApiExtension.php:93 msgid "Domains for license activation by product" msgstr "Domains für Lizenz-Aktivierung nach Produkt" -#: src/Checkout/StoreApiExtension.php:117 +#: src/Checkout/StoreApiExtension.php:120 msgid "Domain for license activation" msgstr "Domain für Lizenz-Aktivierung" -#: src/Email/LicenseEmailController.php:212 -#: src/Email/LicenseEmailController.php:220 -msgid "License Keys:" -msgstr "Lizenzschlüssel:" +#: src/License/PluginLicenseChecker.php:132 +msgid "License settings not configured." +msgstr "Lizenzeinstellungen nicht konfiguriert." -#: src/Email/LicenseEmailController.php:268 -msgid "Your License Keys" -msgstr "Ihre Lizenzschlüssel" +#: src/License/PluginLicenseChecker.php:168 +msgid "Could not connect to license server." +msgstr "Verbindung zum Lizenzserver konnte nicht hergestellt werden." -#: src/Email/LicenseEmailController.php:277 +#: src/License/LicenseManager.php:484 +msgid "This license has been revoked." +msgstr "Diese Lizenz wurde widerrufen." + +#: src/License/LicenseManager.php:494 +msgid "This license has expired." +msgstr "Diese Lizenz ist abgelaufen." + +#: src/License/LicenseManager.php:502 +msgid "This license is inactive." +msgstr "Diese Lizenz ist inaktiv." + +#: src/License/LicenseManager.php:512 +msgid "This license is not valid for this domain." +msgstr "Diese Lizenz ist für diese Domain nicht gültig." + +#: src/Product/LicensedProductType.php:72 +msgid "Licensed Product" +msgstr "Lizensiertes Produkt" + +#: src/Product/LicensedProductType.php:73 +msgid "Licensed Variable Product" +msgstr "Lizensiertes variables Produkt" + +#: src/Product/LicensedProductType.php:108 +msgid "License Settings" +msgstr "Lizenz-Einstellungen" + +#: src/Product/LicensedProductType.php:135 +#: src/Product/LicensedProductType.php:378 #, php-format -msgid "%d license" -msgid_plural "%d licenses" -msgstr[0] "%d Lizenz" -msgstr[1] "%d Lizenzen" +msgid "%d days" +msgstr "%d Tage" -#: src/Email/LicenseEmailController.php:308 -#: src/Email/LicenseEmailController.php:352 -msgid "Never" -msgstr "Nie" +#: src/Product/LicensedProductType.php:145 +#, php-format +msgid "Leave fields empty to use default settings from %s." +msgstr "Felder leer lassen, um Standardeinstellungen von %s zu verwenden." -#: src/Email/LicenseEmailController.php:319 -#: src/Email/LicenseEmailController.php:357 -msgid "You can also view your licenses in your account under \"Licenses\"." +#: src/Product/LicensedProductType.php:147 +msgid "WooCommerce > Settings > Licensed Products" +msgstr "WooCommerce > Einstellungen > Lizensierte Produkte" + +#: src/Product/LicensedProductType.php:154 +#: src/Product/LicensedProductType.php:396 +msgid "Max Activations" +msgstr "Max. Aktivierungen" + +#: src/Product/LicensedProductType.php:157 +#, 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:172 +msgid "License Validity (Days)" +msgstr "Lizenz-Gültigkeit (Tage)" + +#: src/Product/LicensedProductType.php:175 +#, 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:190 +msgid "Bind to Major Version" +msgstr "An Hauptversion binden" + +#: src/Product/LicensedProductType.php:193 +#, php-format +msgid "" +"If enabled, licenses are bound to the major version at purchase time. " +"Default: %s" msgstr "" -"Sie können Ihre Lizenzen auch in Ihrem Konto unter \"Lizenzen\" einsehen." +"Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt " +"gebunden. Standard: %s" -#: src/Email/LicenseEmailController.php:332 -msgid "YOUR LICENSE KEYS" -msgstr "IHRE LIZENZSCHLÜSSEL" +#: src/Product/LicensedProductType.php:194 +msgid "Yes" +msgstr "Ja" -#: src/Email/LicenseEmailController.php:343 -#: src/Email/LicenseExpirationEmail.php:207 -#: src/Email/LicenseExpirationEmail.php:270 -#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256 -msgid "License Key:" -msgstr "Lizenzschlüssel:" +#: src/Product/LicensedProductType.php:194 +msgid "No" +msgstr "Nein" -#: src/Email/LicenseEmailController.php:345 -#: src/Email/LicenseExpirationEmail.php:215 -#: src/Email/LicenseExpirationEmail.php:271 -#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257 -msgid "Domain:" -msgstr "Domain:" +#: src/Product/LicensedProductType.php:321 +msgid "Version:" +msgstr "Version:" -#: src/Email/LicenseEmailController.php:347 -#: src/Email/LicenseExpirationEmail.php:219 -#: src/Email/LicenseExpirationEmail.php:272 -msgid "Expires:" -msgstr "Läuft ab:" +#: src/Product/LicensedProductType.php:349 +msgid "Licensed products are always virtual" +msgstr "Lizenzierte Produkte sind immer virtuell" + +#: src/Product/LicensedProductType.php:351 +msgid "Virtual" +msgstr "Virtuell" + +#: src/Product/LicensedProductType.php:384 +msgid "License Duration (Days)" +msgstr "Lizenz-Gültigkeit (Tage)" + +#: src/Product/LicensedProductType.php:393 +msgid "Leave empty for parent default. 0 = Lifetime." +msgstr "Leer lassen für übergeordneten Standard. 0 = Lebenslang." + +#: src/Product/LicensedProductType.php:405 +msgid "Leave empty for parent default." +msgstr "Leer lassen für übergeordneten Standard." + +#: src/Product/VersionManager.php:166 +msgid "Attachment file not found." +msgstr "Anhangs-Datei nicht gefunden." + +#: 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 +msgid "Monthly" +msgstr "Monatlich" + +#: src/Product/LicensedProductVariation.php:147 +msgid "Quarterly" +msgstr "Vierteljährlich" + +#: src/Product/LicensedProductVariation.php:151 +msgid "Yearly" +msgstr "Jährlich" + +#: src/Product/LicensedProductVariation.php:156 +#, php-format +msgid "%d day" +msgid_plural "%d days" +msgstr[0] "%d Tag" +msgstr[1] "%d Tage" + +#: src/Frontend/DownloadController.php:77 +#: src/Frontend/DownloadController.php:101 +msgid "Invalid download link." +msgstr "Ungültiger Download-Link." + +#: src/Frontend/DownloadController.php:78 +#: src/Frontend/DownloadController.php:88 +#: src/Frontend/DownloadController.php:102 +#: src/Frontend/DownloadController.php:118 +#: src/Frontend/DownloadController.php:128 +#: src/Frontend/DownloadController.php:137 +#: src/Frontend/DownloadController.php:147 +#: src/Frontend/DownloadController.php:156 +#: src/Frontend/DownloadController.php:165 +#: src/Frontend/DownloadController.php:187 +#: src/Frontend/DownloadController.php:203 +msgid "Download Error" +msgstr "Download-Fehler" + +#: src/Frontend/DownloadController.php:87 +msgid "Invalid download link format." +msgstr "Ungültiges Download-Link-Format." + +#: src/Frontend/DownloadController.php:127 +msgid "You do not have permission to download this file." +msgstr "Sie haben keine Berechtigung, diese Datei herunterzuladen." + +#: src/Frontend/DownloadController.php:136 +msgid "Your license is not active. Please contact support." +msgstr "Ihre Lizenz ist nicht aktiv. Bitte kontaktieren Sie den Support." + +#: src/Frontend/DownloadController.php:146 +msgid "Version not found." +msgstr "Version nicht gefunden." + +#: src/Frontend/DownloadController.php:155 +msgid "Version does not match your licensed product." +msgstr "Version stimmt nicht mit Ihrem lizensierten Produkt überein." + +#: src/Frontend/DownloadController.php:164 +msgid "This version is no longer available for download." +msgstr "Diese Version ist nicht mehr zum Download verfügbar." + +#: src/Frontend/DownloadController.php:186 +msgid "No download file available for this version." +msgstr "Keine Download-Datei für diese Version verfügbar." + +#: src/Frontend/DownloadController.php:202 +msgid "Download file not found." +msgstr "Download-Datei nicht gefunden." + +#: src/Frontend/AccountController.php:105 +msgid "Please log in to view your licenses." +msgstr "Bitte melden Sie sich an, um Ihre Lizenzen zu sehen." + +#: src/Frontend/AccountController.php:223 +msgid "You have no licenses yet." +msgstr "Sie haben noch keine Lizenzen." + +#: src/Frontend/AccountController.php:245 +#, php-format +msgid "Order #%s" +msgstr "Bestellung #%s" + +#: src/Frontend/AccountController.php:296 +msgid "Available Downloads" +msgstr "Verfügbare Downloads" + +#: src/Frontend/AccountController.php:305 +#: src/Frontend/AccountController.php:338 +#, php-format +msgid "Version %s" +msgstr "Version %s" + +#: src/Frontend/AccountController.php:307 +msgid "Latest" +msgstr "Neueste" + +#: src/Frontend/AccountController.php:327 +#, php-format +msgid "Older versions (%d)" +msgstr "Ältere Versionen (%d)" + +#: src/Frontend/AccountController.php:427 +#: src/Frontend/AccountController.php:494 +msgid "License transferred successfully!" +msgstr "Lizenz erfolgreich übertragen!" + +#: src/Frontend/AccountController.php:428 +msgid "Transfer failed. Please try again." +msgstr "Übertragung fehlgeschlagen. Bitte versuchen Sie es erneut." + +#: src/Frontend/AccountController.php:429 +msgid "" +"Are you sure you want to transfer this license to a new domain? This action " +"cannot be undone." +msgstr "" +"Sind Sie sicher, dass Sie diese Lizenz auf eine neue Domain übertragen " +"möchten? Diese Aktion kann nicht rückgängig gemacht werden." + +#: src/Frontend/AccountController.php:448 +msgid "Please log in to transfer a license." +msgstr "Bitte melden Sie sich an, um eine Lizenz zu übertragen." + +#: src/Frontend/AccountController.php:454 +msgid "Invalid license." +msgstr "Ungültige Lizenz." + +#: src/Frontend/AccountController.php:472 +msgid "You do not have permission to transfer this license." +msgstr "Sie haben keine Berechtigung, diese Lizenz zu übertragen." + +#: src/Frontend/AccountController.php:477 +msgid "Revoked licenses cannot be transferred." +msgstr "Widerrufene Lizenzen können nicht übertragen werden." + +#: src/Frontend/AccountController.php:481 +msgid "Expired licenses cannot be transferred." +msgstr "Abgelaufene Lizenzen können nicht übertragen werden." + +#: src/Frontend/AccountController.php:486 +msgid "The new domain is the same as the current domain." +msgstr "Die neue Domain ist dieselbe wie die aktuelle Domain." + +#: src/Frontend/AccountController.php:498 +msgid "Failed to transfer license. Please try again." +msgstr "Lizenzübertragung fehlgeschlagen. Bitte versuchen Sie es erneut." #: src/Email/LicenseExpirationEmail.php:55 msgid "License Expiration Warning" @@ -1508,6 +1733,26 @@ msgstr "Lizenzdetails" msgid "Product:" msgstr "Produkt:" +#: src/Email/LicenseExpirationEmail.php:207 +#: src/Email/LicenseExpirationEmail.php:270 +#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256 +#: src/Email/LicenseEmailController.php:343 +msgid "License Key:" +msgstr "Lizenzschlüssel:" + +#: src/Email/LicenseExpirationEmail.php:215 +#: src/Email/LicenseExpirationEmail.php:271 +#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257 +#: src/Email/LicenseEmailController.php:345 +msgid "Domain:" +msgstr "Domain:" + +#: src/Email/LicenseExpirationEmail.php:219 +#: src/Email/LicenseExpirationEmail.php:272 +#: src/Email/LicenseEmailController.php:347 +msgid "Expires:" +msgstr "Läuft ab:" + #: src/Email/LicenseExpirationEmail.php:235 #: src/Email/LicenseExpirationEmail.php:281 #: src/Email/LicenseExpiredEmail.php:227 src/Email/LicenseExpiredEmail.php:268 @@ -1614,264 +1859,70 @@ msgid "To continue using this product, please renew your license." msgstr "" "Um dieses Produkt weiterhin zu nutzen, verlängern Sie bitte Ihre Lizenz." -#: src/Frontend/AccountController.php:105 -msgid "Please log in to view your licenses." -msgstr "Bitte melden Sie sich an, um Ihre Lizenzen zu sehen." +#: src/Email/LicenseEmailController.php:212 +#: src/Email/LicenseEmailController.php:220 +msgid "License Keys:" +msgstr "Lizenzschlüssel:" -#: src/Frontend/AccountController.php:223 -msgid "You have no licenses yet." -msgstr "Sie haben noch keine Lizenzen." +#: src/Email/LicenseEmailController.php:268 +msgid "Your License Keys" +msgstr "Ihre Lizenzschlüssel" -#: src/Frontend/AccountController.php:245 +#: src/Email/LicenseEmailController.php:277 #, php-format -msgid "Order #%s" -msgstr "Bestellung #%s" +msgid "%d license" +msgid_plural "%d licenses" +msgstr[0] "%d Lizenz" +msgstr[1] "%d Lizenzen" -#: src/Frontend/AccountController.php:296 -msgid "Available Downloads" -msgstr "Verfügbare Downloads" +#: src/Email/LicenseEmailController.php:308 +#: src/Email/LicenseEmailController.php:352 +msgid "Never" +msgstr "Nie" -#: src/Frontend/AccountController.php:305 -#: src/Frontend/AccountController.php:338 -#, php-format -msgid "Version %s" -msgstr "Version %s" - -#: src/Frontend/AccountController.php:307 -msgid "Latest" -msgstr "Neueste" - -#: src/Frontend/AccountController.php:327 -#, php-format -msgid "Older versions (%d)" -msgstr "Ältere Versionen (%d)" - -#: src/Frontend/AccountController.php:427 -#: src/Frontend/AccountController.php:494 -msgid "License transferred successfully!" -msgstr "Lizenz erfolgreich übertragen!" - -#: src/Frontend/AccountController.php:428 -msgid "Transfer failed. Please try again." -msgstr "Übertragung fehlgeschlagen. Bitte versuchen Sie es erneut." - -#: src/Frontend/AccountController.php:429 -msgid "" -"Are you sure you want to transfer this license to a new domain? This action " -"cannot be undone." +#: src/Email/LicenseEmailController.php:319 +#: src/Email/LicenseEmailController.php:357 +msgid "You can also view your licenses in your account under \"Licenses\"." msgstr "" -"Sind Sie sicher, dass Sie diese Lizenz auf eine neue Domain übertragen " -"möchten? Diese Aktion kann nicht rückgängig gemacht werden." +"Sie können Ihre Lizenzen auch in Ihrem Konto unter \"Lizenzen\" einsehen." -#: src/Frontend/AccountController.php:448 -msgid "Please log in to transfer a license." -msgstr "Bitte melden Sie sich an, um eine Lizenz zu übertragen." +#: src/Email/LicenseEmailController.php:332 +msgid "YOUR LICENSE KEYS" +msgstr "IHRE LIZENZSCHLÜSSEL" -#: src/Frontend/AccountController.php:454 -msgid "Invalid license." -msgstr "Ungültige Lizenz." - -#: src/Frontend/AccountController.php:472 -msgid "You do not have permission to transfer this license." -msgstr "Sie haben keine Berechtigung, diese Lizenz zu übertragen." - -#: src/Frontend/AccountController.php:477 -msgid "Revoked licenses cannot be transferred." -msgstr "Widerrufene Lizenzen können nicht übertragen werden." - -#: src/Frontend/AccountController.php:481 -msgid "Expired licenses cannot be transferred." -msgstr "Abgelaufene Lizenzen können nicht übertragen werden." - -#: src/Frontend/AccountController.php:486 -msgid "The new domain is the same as the current domain." -msgstr "Die neue Domain ist dieselbe wie die aktuelle Domain." - -#: src/Frontend/AccountController.php:498 -msgid "Failed to transfer license. Please try again." -msgstr "Lizenzübertragung fehlgeschlagen. Bitte versuchen Sie es erneut." - -#: src/Frontend/DownloadController.php:77 -#: src/Frontend/DownloadController.php:101 -msgid "Invalid download link." -msgstr "Ungültiger Download-Link." - -#: src/Frontend/DownloadController.php:78 -#: src/Frontend/DownloadController.php:88 -#: src/Frontend/DownloadController.php:102 -#: src/Frontend/DownloadController.php:118 -#: src/Frontend/DownloadController.php:128 -#: src/Frontend/DownloadController.php:137 -#: src/Frontend/DownloadController.php:147 -#: src/Frontend/DownloadController.php:156 -#: src/Frontend/DownloadController.php:165 -#: src/Frontend/DownloadController.php:187 -#: src/Frontend/DownloadController.php:203 -msgid "Download Error" -msgstr "Download-Fehler" - -#: src/Frontend/DownloadController.php:87 -msgid "Invalid download link format." -msgstr "Ungültiges Download-Link-Format." - -#: src/Frontend/DownloadController.php:127 -msgid "You do not have permission to download this file." -msgstr "Sie haben keine Berechtigung, diese Datei herunterzuladen." - -#: src/Frontend/DownloadController.php:136 -msgid "Your license is not active. Please contact support." -msgstr "Ihre Lizenz ist nicht aktiv. Bitte kontaktieren Sie den Support." - -#: src/Frontend/DownloadController.php:146 -msgid "Version not found." -msgstr "Version nicht gefunden." - -#: src/Frontend/DownloadController.php:155 -msgid "Version does not match your licensed product." -msgstr "Version stimmt nicht mit Ihrem lizensierten Produkt überein." - -#: src/Frontend/DownloadController.php:164 -msgid "This version is no longer available for download." -msgstr "Diese Version ist nicht mehr zum Download verfügbar." - -#: src/Frontend/DownloadController.php:186 -msgid "No download file available for this version." -msgstr "Keine Download-Datei für diese Version verfügbar." - -#: src/Frontend/DownloadController.php:202 -msgid "Download file not found." -msgstr "Download-Datei nicht gefunden." - -#: src/License/LicenseManager.php:412 -msgid "This license has been revoked." -msgstr "Diese Lizenz wurde widerrufen." - -#: src/License/LicenseManager.php:422 -msgid "This license has expired." -msgstr "Diese Lizenz ist abgelaufen." - -#: src/License/LicenseManager.php:430 -msgid "This license is inactive." -msgstr "Diese Lizenz ist inaktiv." - -#: src/License/LicenseManager.php:440 -msgid "This license is not valid for this domain." -msgstr "Diese Lizenz ist für diese Domain nicht gültig." - -#: src/License/PluginLicenseChecker.php:132 -msgid "License settings not configured." -msgstr "Lizenzeinstellungen nicht konfiguriert." - -#: src/License/PluginLicenseChecker.php:168 -msgid "Could not connect to license server." -msgstr "Verbindung zum Lizenzserver konnte nicht hergestellt werden." - -#: src/Plugin.php:318 +#: src/Plugin.php:336 msgid "WC Licensed Product" msgstr "WC Licensed Product" -#: src/Plugin.php:319 +#: src/Plugin.php:337 msgid "" "Plugin license is not configured or invalid. Frontend features are disabled." msgstr "" "Plugin-Lizenz ist nicht konfiguriert oder ungültig. Frontend-Funktionen sind " "deaktiviert." -#: src/Plugin.php:320 +#: src/Plugin.php:338 msgid "Configure License" msgstr "Lizenz konfigurieren" -#: src/Product/LicensedProductType.php:61 -msgid "Licensed Product" -msgstr "Lizensiertes Produkt" - -#: src/Product/LicensedProductType.php:82 -msgid "License Settings" -msgstr "Lizenz-Einstellungen" - -#: src/Product/LicensedProductType.php:109 +#: wc-licensed-product.php:61 #, php-format -msgid "%d days" -msgstr "%d Tage" +msgid "%s requires WooCommerce to be installed and active." +msgstr "%s benötigt WooCommerce als installierte und aktivierte Erweiterung." -#: src/Product/LicensedProductType.php:119 -#, 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:121 -msgid "WooCommerce > Settings > Licensed Products" -msgstr "WooCommerce > Einstellungen > Lizensierte Produkte" - -#: src/Product/LicensedProductType.php:128 -msgid "Max Activations" -msgstr "Max. Aktivierungen" - -#: src/Product/LicensedProductType.php:131 -#, 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:146 -msgid "License Validity (Days)" -msgstr "Lizenz-Gültigkeit (Tage)" - -#: src/Product/LicensedProductType.php:149 -#, 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:164 -msgid "Bind to Major Version" -msgstr "An Hauptversion binden" - -#: src/Product/LicensedProductType.php:167 -#, php-format -msgid "" -"If enabled, licenses are bound to the major version at purchase time. " -"Default: %s" +#: wc-licensed-product.php:119 +msgid "WC Licensed Product requires WooCommerce to be installed and active." msgstr "" -"Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt " -"gebunden. Standard: %s" +"WC Licensed Product benötigt WooCommerce als installierte und aktivierte " +"Erweiterung." -#: src/Product/LicensedProductType.php:168 -msgid "Yes" -msgstr "Ja" +#~ msgid "API Verification Secret" +#~ msgstr "API-Verifizierungs-Secret" -#: src/Product/LicensedProductType.php:168 -msgid "No" -msgstr "Nein" - -#: src/Product/LicensedProductType.php:288 -msgid "Version:" -msgstr "Version:" - -#: src/Product/VersionManager.php:166 -msgid "Attachment file not found." -msgstr "Anhangs-Datei nicht gefunden." - -#: 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" - -#: templates/frontend/licenses.html.twig:72 -msgid "API Verification Secret" -msgstr "API-Verifizierungs-Secret" - -#: templates/frontend/licenses.html.twig:77 -msgid "Use this secret to verify signed API responses. Keep it secure." -msgstr "Verwenden Sie dieses Secret, um signierte API-Antworten zu verifizieren. Bewahren Sie es sicher auf." - -#, php-format -#~ msgid "%s requires WooCommerce to be installed and active." +#~ msgid "Use this secret to verify signed API responses. Keep it secure." #~ msgstr "" -#~ "%s benötigt WooCommerce als installierte und aktivierte Erweiterung." - -#~ msgid "WC Licensed Product requires WooCommerce to be installed and active." -#~ msgstr "" -#~ "WC Licensed Product benötigt WooCommerce als installierte und aktivierte " -#~ "Erweiterung." +#~ "Verwenden Sie dieses Secret, um signierte API-Antworten zu verifizieren. " +#~ "Bewahren Sie es sicher auf." #~ msgid "Domain for License Activation" #~ msgstr "Domain für Lizenz-Aktivierung" diff --git a/languages/wc-licensed-product.pot b/languages/wc-licensed-product.pot index f020eb7..f1146ea 100644 --- a/languages/wc-licensed-product.pot +++ b/languages/wc-licensed-product.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: WC Licensed Product 0.5.2\n" +"Project-Id-Version: WC Licensed Product 0.5.3\n" "Report-Msgid-Bugs-To: magdev3.0@gmail.com\n" -"POT-Creation-Date: 2026-01-26 15:29+0100\n" +"POT-Creation-Date: 2026-01-26 16:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,6 +18,227 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" +#: src/Admin/VersionAdminController.php:58 +msgid "Product Versions" +msgstr "" + +#: src/Admin/VersionAdminController.php:78 +msgid "Add New Version" +msgstr "" + +#: src/Admin/VersionAdminController.php:81 +#: src/Admin/VersionAdminController.php:136 src/Admin/AdminController.php:1609 +msgid "Version" +msgstr "" + +#: src/Admin/VersionAdminController.php:84 +msgid "Use semantic versioning (e.g., 1.0.0)" +msgstr "" + +#: src/Admin/VersionAdminController.php:88 +#: src/Admin/VersionAdminController.php:137 +msgid "Download File" +msgstr "" + +#: src/Admin/VersionAdminController.php:93 +msgid "Select File" +msgstr "" + +#: src/Admin/VersionAdminController.php:96 +#: src/Admin/VersionAdminController.php:110 +msgid "Remove" +msgstr "" + +#: src/Admin/VersionAdminController.php:98 +msgid "" +"Upload or select a file from the media library. Version will be auto-" +"detected from filename (e.g., plugin-v1.2.3.zip)." +msgstr "" + +#: src/Admin/VersionAdminController.php:102 +msgid "Checksum File" +msgstr "" + +#: src/Admin/VersionAdminController.php:107 +msgid "Select Checksum File" +msgstr "" + +#: src/Admin/VersionAdminController.php:112 +msgid "" +"Upload a SHA256 checksum file (.sha256 or .txt) to verify file integrity." +msgstr "" + +#: src/Admin/VersionAdminController.php:116 +#: src/Admin/VersionAdminController.php:139 +msgid "Release Notes" +msgstr "" + +#: src/Admin/VersionAdminController.php:124 +msgid "Add Version" +msgstr "" + +#: src/Admin/VersionAdminController.php:132 +msgid "Existing Versions" +msgstr "" + +#: src/Admin/VersionAdminController.php:138 +msgid "SHA256" +msgstr "" + +#: src/Admin/VersionAdminController.php:140 src/Admin/AdminController.php:1295 +#: src/Admin/AdminController.php:1446 src/Admin/OrderLicenseController.php:206 +msgid "Status" +msgstr "" + +#: src/Admin/VersionAdminController.php:141 +msgid "Released" +msgstr "" + +#: src/Admin/VersionAdminController.php:142 src/Admin/AdminController.php:1298 +#: src/Admin/AdminController.php:1449 src/Admin/OrderLicenseController.php:208 +msgid "Actions" +msgstr "" + +#: src/Admin/VersionAdminController.php:148 +msgid "No versions found. Add your first version above." +msgstr "" + +#: src/Admin/VersionAdminController.php:165 +#: src/Admin/VersionAdminController.php:396 +msgid "Uploaded file" +msgstr "" + +#: src/Admin/VersionAdminController.php:169 +#: src/Admin/VersionAdminController.php:400 +msgid "No download file" +msgstr "" + +#: src/Admin/VersionAdminController.php:182 +#: src/Admin/VersionAdminController.php:413 src/Admin/AdminController.php:156 +#: src/Admin/AdminController.php:907 src/Admin/AdminController.php:1232 +#: src/Admin/AdminController.php:1355 +#: src/Admin/DashboardWidgetController.php:117 +msgid "Active" +msgstr "" + +#: src/Admin/VersionAdminController.php:182 +#: src/Admin/VersionAdminController.php:413 src/Admin/AdminController.php:157 +#: src/Admin/AdminController.php:914 src/Admin/AdminController.php:1233 +#: src/Admin/AdminController.php:1356 +msgid "Inactive" +msgstr "" + +#: src/Admin/VersionAdminController.php:188 +#: src/Admin/VersionAdminController.php:419 src/Admin/AdminController.php:1274 +#: src/Admin/AdminController.php:1459 +msgid "Deactivate" +msgstr "" + +#: src/Admin/VersionAdminController.php:188 +#: src/Admin/VersionAdminController.php:419 src/Admin/AdminController.php:1273 +#: src/Admin/AdminController.php:1458 +msgid "Activate" +msgstr "" + +#: src/Admin/VersionAdminController.php:191 +#: src/Admin/VersionAdminController.php:422 src/Admin/AdminController.php:1279 +#: src/Admin/AdminController.php:1428 src/Admin/AdminController.php:1464 +msgid "Delete" +msgstr "" + +#: src/Admin/VersionAdminController.php:232 +msgid "Are you sure you want to delete this version?" +msgstr "" + +#: src/Admin/VersionAdminController.php:233 +msgid "Please enter a version number." +msgstr "" + +#: src/Admin/VersionAdminController.php:234 +msgid "Please enter a valid version number (e.g., 1.0.0)." +msgstr "" + +#: src/Admin/VersionAdminController.php:235 +msgid "An error occurred. Please try again." +msgstr "" + +#: src/Admin/VersionAdminController.php:236 +msgid "Select Download File" +msgstr "" + +#: src/Admin/VersionAdminController.php:237 +msgid "Use this file" +msgstr "" + +#: src/Admin/VersionAdminController.php:238 +msgid "" +"Invalid checksum file format. File must contain a 64-character SHA256 hash." +msgstr "" + +#: src/Admin/VersionAdminController.php:239 +msgid "Failed to read checksum file." +msgstr "" + +#: src/Admin/VersionAdminController.php:259 +#: src/Admin/VersionAdminController.php:328 +#: src/Admin/VersionAdminController.php:354 src/Admin/AdminController.php:173 +#: src/Admin/AdminController.php:213 src/Admin/AdminController.php:249 +#: src/Admin/AdminController.php:301 src/Admin/AdminController.php:339 +#: src/Admin/AdminController.php:369 src/Admin/OrderLicenseController.php:387 +#: src/Admin/OrderLicenseController.php:426 +#: src/Admin/OrderLicenseController.php:490 +msgid "Permission denied." +msgstr "" + +#: src/Admin/VersionAdminController.php:269 +msgid "Product ID and version are required." +msgstr "" + +#: src/Admin/VersionAdminController.php:274 +msgid "Invalid version format. Use semantic versioning (e.g., 1.0.0)." +msgstr "" + +#: src/Admin/VersionAdminController.php:279 +msgid "This version already exists." +msgstr "" + +#: src/Admin/VersionAdminController.php:285 +msgid "Product not found." +msgstr "" + +#: src/Admin/VersionAdminController.php:289 +msgid "This product is not a licensed product." +msgstr "" + +#: src/Admin/VersionAdminController.php:306 +msgid "Failed to create version." +msgstr "" + +#: src/Admin/VersionAdminController.php:314 +msgid "Version added successfully." +msgstr "" + +#: src/Admin/VersionAdminController.php:334 +#: src/Admin/VersionAdminController.php:361 +msgid "Version ID is required." +msgstr "" + +#: src/Admin/VersionAdminController.php:340 +msgid "Failed to delete version." +msgstr "" + +#: src/Admin/VersionAdminController.php:343 +msgid "Version deleted successfully." +msgstr "" + +#: src/Admin/VersionAdminController.php:367 +msgid "Failed to update version." +msgstr "" + +#: src/Admin/VersionAdminController.php:371 +msgid "Version updated successfully." +msgstr "" + #: src/Admin/AdminController.php:76 src/Admin/AdminController.php:77 #: src/Admin/AdminController.php:90 src/Admin/AdminController.php:1200 #: src/Admin/OrderLicenseController.php:149 @@ -82,9 +303,11 @@ msgstr "" #: src/Admin/AdminController.php:1373 src/Admin/AdminController.php:1613 #: src/Admin/DashboardWidgetController.php:136 #: src/Admin/OrderLicenseController.php:260 -#: src/Admin/SettingsController.php:192 src/Frontend/AccountController.php:286 -#: src/Product/LicensedProductType.php:110 -#: src/Product/LicensedProductType.php:158 +#: src/Admin/SettingsController.php:192 src/Product/LicensedProductType.php:136 +#: src/Product/LicensedProductType.php:184 +#: src/Product/LicensedProductType.php:379 +#: src/Product/LicensedProductVariation.php:139 +#: src/Frontend/AccountController.php:286 msgid "Lifetime" msgstr "" @@ -96,21 +319,6 @@ msgstr "" msgid "Copy failed" msgstr "" -#: src/Admin/AdminController.php:156 src/Admin/AdminController.php:907 -#: src/Admin/AdminController.php:1232 src/Admin/AdminController.php:1355 -#: src/Admin/DashboardWidgetController.php:117 -#: src/Admin/VersionAdminController.php:182 -#: src/Admin/VersionAdminController.php:413 -msgid "Active" -msgstr "" - -#: src/Admin/AdminController.php:157 src/Admin/AdminController.php:914 -#: src/Admin/AdminController.php:1233 src/Admin/AdminController.php:1356 -#: src/Admin/VersionAdminController.php:182 -#: src/Admin/VersionAdminController.php:413 -msgid "Inactive" -msgstr "" - #: src/Admin/AdminController.php:158 src/Admin/AdminController.php:921 #: src/Admin/AdminController.php:1234 src/Admin/AdminController.php:1357 #: src/Admin/DashboardWidgetController.php:125 @@ -123,18 +331,6 @@ msgstr "" msgid "Revoked" msgstr "" -#: src/Admin/AdminController.php:173 src/Admin/AdminController.php:213 -#: src/Admin/AdminController.php:249 src/Admin/AdminController.php:301 -#: src/Admin/AdminController.php:339 src/Admin/AdminController.php:369 -#: src/Admin/OrderLicenseController.php:387 -#: src/Admin/OrderLicenseController.php:426 -#: src/Admin/OrderLicenseController.php:490 -#: src/Admin/VersionAdminController.php:259 -#: src/Admin/VersionAdminController.php:328 -#: src/Admin/VersionAdminController.php:354 -msgid "Permission denied." -msgstr "" - #: src/Admin/AdminController.php:195 src/Admin/AdminController.php:1019 #: src/Admin/OrderLicenseController.php:227 msgid "Unknown" @@ -268,7 +464,7 @@ msgstr "" msgid "Export to CSV" msgstr "" -#: src/Admin/AdminController.php:968 +#: src/Admin/AdminController.php:968 wc-licensed-product.php:137 msgid "Settings" msgstr "" @@ -433,18 +629,6 @@ msgstr "" msgid "Bulk Actions" msgstr "" -#: src/Admin/AdminController.php:1273 src/Admin/AdminController.php:1458 -#: src/Admin/VersionAdminController.php:188 -#: src/Admin/VersionAdminController.php:419 -msgid "Activate" -msgstr "" - -#: src/Admin/AdminController.php:1274 src/Admin/AdminController.php:1459 -#: src/Admin/VersionAdminController.php:188 -#: src/Admin/VersionAdminController.php:419 -msgid "Deactivate" -msgstr "" - #: src/Admin/AdminController.php:1275 src/Admin/AdminController.php:1419 #: src/Admin/AdminController.php:1460 msgid "Revoke" @@ -462,12 +646,6 @@ msgstr "" msgid "Extend 1 year" msgstr "" -#: src/Admin/AdminController.php:1279 src/Admin/AdminController.php:1428 -#: src/Admin/AdminController.php:1464 src/Admin/VersionAdminController.php:191 -#: src/Admin/VersionAdminController.php:422 -msgid "Delete" -msgstr "" - #: src/Admin/AdminController.php:1281 src/Admin/AdminController.php:1466 msgid "Apply" msgstr "" @@ -492,18 +670,12 @@ msgstr "" #: src/Admin/AdminController.php:1294 src/Admin/AdminController.php:1445 #: src/Admin/AdminController.php:1495 src/Admin/OrderLicenseController.php:205 -#: src/Checkout/CheckoutBlocksIntegration.php:129 -#: src/Checkout/CheckoutController.php:122 +#: src/Checkout/CheckoutBlocksIntegration.php:130 +#: src/Checkout/CheckoutController.php:161 #: src/Email/LicenseEmailController.php:288 msgid "Domain" msgstr "" -#: src/Admin/AdminController.php:1295 src/Admin/AdminController.php:1446 -#: src/Admin/OrderLicenseController.php:206 -#: src/Admin/VersionAdminController.php:140 -msgid "Status" -msgstr "" - #: src/Admin/AdminController.php:1296 src/Admin/AdminController.php:1447 msgid "Created" msgstr "" @@ -515,12 +687,6 @@ msgstr "" msgid "Expires" msgstr "" -#: src/Admin/AdminController.php:1298 src/Admin/AdminController.php:1449 -#: src/Admin/OrderLicenseController.php:208 -#: src/Admin/VersionAdminController.php:142 -msgid "Actions" -msgstr "" - #: src/Admin/AdminController.php:1304 msgid "No licenses found." msgstr "" @@ -601,11 +767,6 @@ msgstr "" msgid "License is VALID" msgstr "" -#: src/Admin/AdminController.php:1609 src/Admin/VersionAdminController.php:81 -#: src/Admin/VersionAdminController.php:136 -msgid "Version" -msgstr "" - #: src/Admin/AdminController.php:1617 msgid "License is INVALID" msgstr "" @@ -781,13 +942,13 @@ msgid "Domains specified during checkout (multi-domain order)." msgstr "" #: src/Admin/OrderLicenseController.php:119 -#: src/Checkout/CheckoutController.php:436 -#: src/Checkout/CheckoutController.php:486 -#: src/Checkout/CheckoutController.php:496 +#: src/Checkout/CheckoutController.php:530 +#: src/Checkout/CheckoutController.php:591 +#: src/Checkout/CheckoutController.php:613 src/License/LicenseManager.php:878 +#: src/Product/VersionManager.php:349 src/Product/VersionManager.php:361 +#: src/Frontend/AccountController.php:148 #: src/Email/LicenseExpirationEmail.php:107 -#: src/Email/LicenseExpiredEmail.php:99 src/Frontend/AccountController.php:148 -#: src/License/LicenseManager.php:806 src/Product/VersionManager.php:349 -#: src/Product/VersionManager.php:361 +#: src/Email/LicenseExpiredEmail.php:99 msgid "Unknown Product" msgstr "" @@ -798,10 +959,10 @@ msgid "" msgstr "" #: src/Admin/OrderLicenseController.php:137 -#: src/Checkout/CheckoutBlocksIntegration.php:83 -#: src/Checkout/CheckoutBlocksIntegration.php:119 -#: src/Checkout/CheckoutController.php:130 -#: src/Checkout/CheckoutController.php:186 +#: src/Checkout/CheckoutBlocksIntegration.php:84 +#: src/Checkout/CheckoutBlocksIntegration.php:120 +#: src/Checkout/CheckoutController.php:169 +#: src/Checkout/CheckoutController.php:235 msgid "example.com" msgstr "" @@ -860,7 +1021,7 @@ msgid "Error. Please try again." msgstr "" #: src/Admin/OrderLicenseController.php:373 -#: src/Checkout/CheckoutBlocksIntegration.php:126 +#: src/Checkout/CheckoutBlocksIntegration.php:127 #: src/Frontend/AccountController.php:430 #: src/Frontend/AccountController.php:462 msgid "Please enter a valid domain." @@ -885,8 +1046,8 @@ msgid "Order domain updated." msgstr "" #: src/Admin/OrderLicenseController.php:449 -#: src/Frontend/AccountController.php:468 #: src/Frontend/DownloadController.php:117 +#: src/Frontend/AccountController.php:468 msgid "License not found." msgstr "" @@ -1077,174 +1238,12 @@ msgstr "" msgid "License validation failed." msgstr "" -#: src/Admin/VersionAdminController.php:58 -msgid "Product Versions" -msgstr "" - -#: src/Admin/VersionAdminController.php:78 -msgid "Add New Version" -msgstr "" - -#: src/Admin/VersionAdminController.php:84 -msgid "Use semantic versioning (e.g., 1.0.0)" -msgstr "" - -#: src/Admin/VersionAdminController.php:88 -#: src/Admin/VersionAdminController.php:137 -msgid "Download File" -msgstr "" - -#: src/Admin/VersionAdminController.php:93 -msgid "Select File" -msgstr "" - -#: src/Admin/VersionAdminController.php:96 -#: src/Admin/VersionAdminController.php:110 -msgid "Remove" -msgstr "" - -#: src/Admin/VersionAdminController.php:98 -msgid "" -"Upload or select a file from the media library. Version will be auto-" -"detected from filename (e.g., plugin-v1.2.3.zip)." -msgstr "" - -#: src/Admin/VersionAdminController.php:102 -msgid "Checksum File" -msgstr "" - -#: src/Admin/VersionAdminController.php:107 -msgid "Select Checksum File" -msgstr "" - -#: src/Admin/VersionAdminController.php:112 -msgid "" -"Upload a SHA256 checksum file (.sha256 or .txt) to verify file integrity." -msgstr "" - -#: src/Admin/VersionAdminController.php:116 -#: src/Admin/VersionAdminController.php:139 -msgid "Release Notes" -msgstr "" - -#: src/Admin/VersionAdminController.php:124 -msgid "Add Version" -msgstr "" - -#: src/Admin/VersionAdminController.php:132 -msgid "Existing Versions" -msgstr "" - -#: src/Admin/VersionAdminController.php:138 -msgid "SHA256" -msgstr "" - -#: src/Admin/VersionAdminController.php:141 -msgid "Released" -msgstr "" - -#: src/Admin/VersionAdminController.php:148 -msgid "No versions found. Add your first version above." -msgstr "" - -#: src/Admin/VersionAdminController.php:165 -#: src/Admin/VersionAdminController.php:396 -msgid "Uploaded file" -msgstr "" - -#: src/Admin/VersionAdminController.php:169 -#: src/Admin/VersionAdminController.php:400 -msgid "No download file" -msgstr "" - -#: src/Admin/VersionAdminController.php:232 -msgid "Are you sure you want to delete this version?" -msgstr "" - -#: src/Admin/VersionAdminController.php:233 -msgid "Please enter a version number." -msgstr "" - -#: src/Admin/VersionAdminController.php:234 -msgid "Please enter a valid version number (e.g., 1.0.0)." -msgstr "" - -#: src/Admin/VersionAdminController.php:235 -msgid "An error occurred. Please try again." -msgstr "" - -#: src/Admin/VersionAdminController.php:236 -msgid "Select Download File" -msgstr "" - -#: src/Admin/VersionAdminController.php:237 -msgid "Use this file" -msgstr "" - -#: src/Admin/VersionAdminController.php:238 -msgid "" -"Invalid checksum file format. File must contain a 64-character SHA256 hash." -msgstr "" - -#: src/Admin/VersionAdminController.php:239 -msgid "Failed to read checksum file." -msgstr "" - -#: src/Admin/VersionAdminController.php:269 -msgid "Product ID and version are required." -msgstr "" - -#: src/Admin/VersionAdminController.php:274 -msgid "Invalid version format. Use semantic versioning (e.g., 1.0.0)." -msgstr "" - -#: src/Admin/VersionAdminController.php:279 -msgid "This version already exists." -msgstr "" - -#: src/Admin/VersionAdminController.php:285 -msgid "Product not found." -msgstr "" - -#: src/Admin/VersionAdminController.php:289 -msgid "This product is not a licensed product." -msgstr "" - -#: src/Admin/VersionAdminController.php:306 -msgid "Failed to create version." -msgstr "" - -#: src/Admin/VersionAdminController.php:314 -msgid "Version added successfully." -msgstr "" - -#: src/Admin/VersionAdminController.php:334 -#: src/Admin/VersionAdminController.php:361 -msgid "Version ID is required." -msgstr "" - -#: src/Admin/VersionAdminController.php:340 -msgid "Failed to delete version." -msgstr "" - -#: src/Admin/VersionAdminController.php:343 -msgid "Version deleted successfully." -msgstr "" - -#: src/Admin/VersionAdminController.php:367 -msgid "Failed to update version." -msgstr "" - -#: src/Admin/VersionAdminController.php:371 -msgid "Version updated successfully." -msgstr "" - #: src/Api/RestApiController.php:84 msgid "Too many requests. Please try again later." msgstr "" #: src/Api/RestApiController.php:345 src/Api/RestApiController.php:378 -#: src/License/LicenseManager.php:403 +#: src/License/LicenseManager.php:475 msgid "License key not found." msgstr "" @@ -1268,140 +1267,363 @@ msgstr "" msgid "License activated successfully." msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:78 -#: src/Checkout/CheckoutBlocksIntegration.php:125 -#: src/Checkout/CheckoutController.php:119 +#: src/Checkout/CheckoutBlocksIntegration.php:79 +#: src/Checkout/CheckoutBlocksIntegration.php:126 +#: src/Checkout/CheckoutController.php:158 msgid "License Domain" msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:85 +#: src/Checkout/CheckoutBlocksIntegration.php:86 msgid "Enter a valid domain (without http:// or www)" msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:121 -#: src/Checkout/CheckoutController.php:150 +#: src/Checkout/CheckoutBlocksIntegration.php:122 +#: src/Checkout/CheckoutController.php:189 msgid "Enter a unique domain for each license (without http:// or www)." msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:122 -#: src/Checkout/CheckoutController.php:134 +#: src/Checkout/CheckoutBlocksIntegration.php:123 +#: src/Checkout/CheckoutController.php:173 msgid "" "Enter the domain where you will use the license (without http:// or www)." msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:124 -#: src/Checkout/CheckoutController.php:148 +#: src/Checkout/CheckoutBlocksIntegration.php:125 +#: src/Checkout/CheckoutController.php:187 msgid "License Domains" msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:127 +#: src/Checkout/CheckoutBlocksIntegration.php:128 msgid "Each license requires a unique domain." msgstr "" -#: src/Checkout/CheckoutBlocksIntegration.php:128 -#: src/Checkout/CheckoutController.php:175 +#: src/Checkout/CheckoutBlocksIntegration.php:129 +#: src/Checkout/CheckoutController.php:224 #, php-format msgid "License %d:" msgstr "" -#: src/Checkout/CheckoutController.php:123 -#: src/Checkout/CheckoutController.php:179 +#: src/Checkout/CheckoutController.php:162 +#: src/Checkout/CheckoutController.php:228 msgid "required" msgstr "" -#: src/Checkout/CheckoutController.php:258 +#: src/Checkout/CheckoutController.php:323 msgid "Please enter a domain for your license." msgstr "" -#: src/Checkout/CheckoutController.php:264 +#: src/Checkout/CheckoutController.php:329 msgid "Please enter a valid domain for your license." msgstr "" -#: src/Checkout/CheckoutController.php:287 +#: src/Checkout/CheckoutController.php:356 #, php-format msgid "Please enter a domain for %1$s (License %2$d)." msgstr "" -#: src/Checkout/CheckoutController.php:302 +#: src/Checkout/CheckoutController.php:371 #, php-format msgid "Please enter a valid domain for %1$s (License %2$d)." msgstr "" -#: src/Checkout/CheckoutController.php:316 +#: src/Checkout/CheckoutController.php:385 #, php-format msgid "" "The domain \"%1$s\" is used multiple times for %2$s. Each license requires a " "unique domain." msgstr "" -#: src/Checkout/CheckoutController.php:419 -#: src/Checkout/CheckoutController.php:466 -#: src/Checkout/CheckoutController.php:470 +#: src/Checkout/CheckoutController.php:500 +#: src/Checkout/CheckoutController.php:561 +#: src/Checkout/CheckoutController.php:565 msgid "License Domain:" msgstr "" -#: src/Checkout/CheckoutController.php:432 -#: src/Checkout/CheckoutController.php:483 -#: src/Checkout/CheckoutController.php:492 +#: src/Checkout/CheckoutController.php:513 +#: src/Checkout/CheckoutController.php:578 +#: src/Checkout/CheckoutController.php:599 msgid "License Domains:" msgstr "" +#: src/Checkout/CheckoutController.php:522 +#: src/Checkout/CheckoutController.php:585 +#: src/Checkout/CheckoutController.php:607 +msgid "Unknown Variation" +msgstr "" + #: src/Checkout/StoreApiExtension.php:93 msgid "Domains for license activation by product" msgstr "" -#: src/Checkout/StoreApiExtension.php:117 +#: src/Checkout/StoreApiExtension.php:120 msgid "Domain for license activation" msgstr "" -#: src/Email/LicenseEmailController.php:212 -#: src/Email/LicenseEmailController.php:220 -msgid "License Keys:" +#: src/License/PluginLicenseChecker.php:132 +msgid "License settings not configured." msgstr "" -#: src/Email/LicenseEmailController.php:268 -msgid "Your License Keys" +#: src/License/PluginLicenseChecker.php:168 +msgid "Could not connect to license server." msgstr "" -#: src/Email/LicenseEmailController.php:277 +#: src/License/LicenseManager.php:484 +msgid "This license has been revoked." +msgstr "" + +#: src/License/LicenseManager.php:494 +msgid "This license has expired." +msgstr "" + +#: src/License/LicenseManager.php:502 +msgid "This license is inactive." +msgstr "" + +#: src/License/LicenseManager.php:512 +msgid "This license is not valid for this domain." +msgstr "" + +#: src/Product/LicensedProductType.php:72 +msgid "Licensed Product" +msgstr "" + +#: src/Product/LicensedProductType.php:73 +msgid "Licensed Variable Product" +msgstr "" + +#: src/Product/LicensedProductType.php:108 +msgid "License Settings" +msgstr "" + +#: src/Product/LicensedProductType.php:135 +#: src/Product/LicensedProductType.php:378 #, php-format -msgid "%d license" -msgid_plural "%d licenses" +msgid "%d days" +msgstr "" + +#: src/Product/LicensedProductType.php:145 +#, php-format +msgid "Leave fields empty to use default settings from %s." +msgstr "" + +#: src/Product/LicensedProductType.php:147 +msgid "WooCommerce > Settings > Licensed Products" +msgstr "" + +#: src/Product/LicensedProductType.php:154 +#: src/Product/LicensedProductType.php:396 +msgid "Max Activations" +msgstr "" + +#: src/Product/LicensedProductType.php:157 +#, php-format +msgid "Maximum number of domain activations per license. Default: %d" +msgstr "" + +#: src/Product/LicensedProductType.php:172 +msgid "License Validity (Days)" +msgstr "" + +#: src/Product/LicensedProductType.php:175 +#, php-format +msgid "Number of days the license is valid. Leave empty for default (%s)." +msgstr "" + +#: src/Product/LicensedProductType.php:190 +msgid "Bind to Major Version" +msgstr "" + +#: src/Product/LicensedProductType.php:193 +#, php-format +msgid "" +"If enabled, licenses are bound to the major version at purchase time. " +"Default: %s" +msgstr "" + +#: src/Product/LicensedProductType.php:194 +msgid "Yes" +msgstr "" + +#: src/Product/LicensedProductType.php:194 +msgid "No" +msgstr "" + +#: src/Product/LicensedProductType.php:321 +msgid "Version:" +msgstr "" + +#: src/Product/LicensedProductType.php:349 +msgid "Licensed products are always virtual" +msgstr "" + +#: src/Product/LicensedProductType.php:351 +msgid "Virtual" +msgstr "" + +#: src/Product/LicensedProductType.php:384 +msgid "License Duration (Days)" +msgstr "" + +#: src/Product/LicensedProductType.php:393 +msgid "Leave empty for parent default. 0 = Lifetime." +msgstr "" + +#: src/Product/LicensedProductType.php:405 +msgid "Leave empty for parent default." +msgstr "" + +#: src/Product/VersionManager.php:166 +msgid "Attachment file not found." +msgstr "" + +#: 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 +msgid "Monthly" +msgstr "" + +#: src/Product/LicensedProductVariation.php:147 +msgid "Quarterly" +msgstr "" + +#: src/Product/LicensedProductVariation.php:151 +msgid "Yearly" +msgstr "" + +#: src/Product/LicensedProductVariation.php:156 +#, php-format +msgid "%d day" +msgid_plural "%d days" msgstr[0] "" msgstr[1] "" -#: src/Email/LicenseEmailController.php:308 -#: src/Email/LicenseEmailController.php:352 -msgid "Never" +#: src/Frontend/DownloadController.php:77 +#: src/Frontend/DownloadController.php:101 +msgid "Invalid download link." msgstr "" -#: src/Email/LicenseEmailController.php:319 -#: src/Email/LicenseEmailController.php:357 -msgid "You can also view your licenses in your account under \"Licenses\"." +#: src/Frontend/DownloadController.php:78 +#: src/Frontend/DownloadController.php:88 +#: src/Frontend/DownloadController.php:102 +#: src/Frontend/DownloadController.php:118 +#: src/Frontend/DownloadController.php:128 +#: src/Frontend/DownloadController.php:137 +#: src/Frontend/DownloadController.php:147 +#: src/Frontend/DownloadController.php:156 +#: src/Frontend/DownloadController.php:165 +#: src/Frontend/DownloadController.php:187 +#: src/Frontend/DownloadController.php:203 +msgid "Download Error" msgstr "" -#: src/Email/LicenseEmailController.php:332 -msgid "YOUR LICENSE KEYS" +#: src/Frontend/DownloadController.php:87 +msgid "Invalid download link format." msgstr "" -#: src/Email/LicenseEmailController.php:343 -#: src/Email/LicenseExpirationEmail.php:207 -#: src/Email/LicenseExpirationEmail.php:270 -#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256 -msgid "License Key:" +#: src/Frontend/DownloadController.php:127 +msgid "You do not have permission to download this file." msgstr "" -#: src/Email/LicenseEmailController.php:345 -#: src/Email/LicenseExpirationEmail.php:215 -#: src/Email/LicenseExpirationEmail.php:271 -#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257 -msgid "Domain:" +#: src/Frontend/DownloadController.php:136 +msgid "Your license is not active. Please contact support." msgstr "" -#: src/Email/LicenseEmailController.php:347 -#: src/Email/LicenseExpirationEmail.php:219 -#: src/Email/LicenseExpirationEmail.php:272 -msgid "Expires:" +#: src/Frontend/DownloadController.php:146 +msgid "Version not found." +msgstr "" + +#: src/Frontend/DownloadController.php:155 +msgid "Version does not match your licensed product." +msgstr "" + +#: src/Frontend/DownloadController.php:164 +msgid "This version is no longer available for download." +msgstr "" + +#: src/Frontend/DownloadController.php:186 +msgid "No download file available for this version." +msgstr "" + +#: src/Frontend/DownloadController.php:202 +msgid "Download file not found." +msgstr "" + +#: src/Frontend/AccountController.php:105 +msgid "Please log in to view your licenses." +msgstr "" + +#: src/Frontend/AccountController.php:223 +msgid "You have no licenses yet." +msgstr "" + +#: src/Frontend/AccountController.php:245 +#, php-format +msgid "Order #%s" +msgstr "" + +#: src/Frontend/AccountController.php:296 +msgid "Available Downloads" +msgstr "" + +#: src/Frontend/AccountController.php:305 +#: src/Frontend/AccountController.php:338 +#, php-format +msgid "Version %s" +msgstr "" + +#: src/Frontend/AccountController.php:307 +msgid "Latest" +msgstr "" + +#: src/Frontend/AccountController.php:327 +#, php-format +msgid "Older versions (%d)" +msgstr "" + +#: src/Frontend/AccountController.php:427 +#: src/Frontend/AccountController.php:494 +msgid "License transferred successfully!" +msgstr "" + +#: src/Frontend/AccountController.php:428 +msgid "Transfer failed. Please try again." +msgstr "" + +#: src/Frontend/AccountController.php:429 +msgid "" +"Are you sure you want to transfer this license to a new domain? This action " +"cannot be undone." +msgstr "" + +#: src/Frontend/AccountController.php:448 +msgid "Please log in to transfer a license." +msgstr "" + +#: src/Frontend/AccountController.php:454 +msgid "Invalid license." +msgstr "" + +#: src/Frontend/AccountController.php:472 +msgid "You do not have permission to transfer this license." +msgstr "" + +#: src/Frontend/AccountController.php:477 +msgid "Revoked licenses cannot be transferred." +msgstr "" + +#: src/Frontend/AccountController.php:481 +msgid "Expired licenses cannot be transferred." +msgstr "" + +#: src/Frontend/AccountController.php:486 +msgid "The new domain is the same as the current domain." +msgstr "" + +#: src/Frontend/AccountController.php:498 +msgid "Failed to transfer license. Please try again." msgstr "" #: src/Email/LicenseExpirationEmail.php:55 @@ -1454,6 +1676,26 @@ msgstr "" msgid "Product:" msgstr "" +#: src/Email/LicenseExpirationEmail.php:207 +#: src/Email/LicenseExpirationEmail.php:270 +#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256 +#: src/Email/LicenseEmailController.php:343 +msgid "License Key:" +msgstr "" + +#: src/Email/LicenseExpirationEmail.php:215 +#: src/Email/LicenseExpirationEmail.php:271 +#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257 +#: src/Email/LicenseEmailController.php:345 +msgid "Domain:" +msgstr "" + +#: src/Email/LicenseExpirationEmail.php:219 +#: src/Email/LicenseExpirationEmail.php:272 +#: src/Email/LicenseEmailController.php:347 +msgid "Expires:" +msgstr "" + #: src/Email/LicenseExpirationEmail.php:235 #: src/Email/LicenseExpirationEmail.php:281 #: src/Email/LicenseExpiredEmail.php:227 src/Email/LicenseExpiredEmail.php:268 @@ -1553,245 +1795,54 @@ msgstr "" msgid "To continue using this product, please renew your license." msgstr "" -#: src/Frontend/AccountController.php:105 -msgid "Please log in to view your licenses." +#: src/Email/LicenseEmailController.php:212 +#: src/Email/LicenseEmailController.php:220 +msgid "License Keys:" msgstr "" -#: src/Frontend/AccountController.php:223 -msgid "You have no licenses yet." +#: src/Email/LicenseEmailController.php:268 +msgid "Your License Keys" msgstr "" -#: src/Frontend/AccountController.php:245 +#: src/Email/LicenseEmailController.php:277 #, php-format -msgid "Order #%s" +msgid "%d license" +msgid_plural "%d licenses" +msgstr[0] "" +msgstr[1] "" + +#: src/Email/LicenseEmailController.php:308 +#: src/Email/LicenseEmailController.php:352 +msgid "Never" msgstr "" -#: src/Frontend/AccountController.php:296 -msgid "Available Downloads" +#: src/Email/LicenseEmailController.php:319 +#: src/Email/LicenseEmailController.php:357 +msgid "You can also view your licenses in your account under \"Licenses\"." msgstr "" -#: src/Frontend/AccountController.php:305 -#: src/Frontend/AccountController.php:338 -#, php-format -msgid "Version %s" +#: src/Email/LicenseEmailController.php:332 +msgid "YOUR LICENSE KEYS" msgstr "" -#: src/Frontend/AccountController.php:307 -msgid "Latest" -msgstr "" - -#: src/Frontend/AccountController.php:327 -#, php-format -msgid "Older versions (%d)" -msgstr "" - -#: src/Frontend/AccountController.php:427 -#: src/Frontend/AccountController.php:494 -msgid "License transferred successfully!" -msgstr "" - -#: src/Frontend/AccountController.php:428 -msgid "Transfer failed. Please try again." -msgstr "" - -#: src/Frontend/AccountController.php:429 -msgid "" -"Are you sure you want to transfer this license to a new domain? This action " -"cannot be undone." -msgstr "" - -#: src/Frontend/AccountController.php:448 -msgid "Please log in to transfer a license." -msgstr "" - -#: src/Frontend/AccountController.php:454 -msgid "Invalid license." -msgstr "" - -#: src/Frontend/AccountController.php:472 -msgid "You do not have permission to transfer this license." -msgstr "" - -#: src/Frontend/AccountController.php:477 -msgid "Revoked licenses cannot be transferred." -msgstr "" - -#: src/Frontend/AccountController.php:481 -msgid "Expired licenses cannot be transferred." -msgstr "" - -#: src/Frontend/AccountController.php:486 -msgid "The new domain is the same as the current domain." -msgstr "" - -#: src/Frontend/AccountController.php:498 -msgid "Failed to transfer license. Please try again." -msgstr "" - -#: src/Frontend/DownloadController.php:77 -#: src/Frontend/DownloadController.php:101 -msgid "Invalid download link." -msgstr "" - -#: src/Frontend/DownloadController.php:78 -#: src/Frontend/DownloadController.php:88 -#: src/Frontend/DownloadController.php:102 -#: src/Frontend/DownloadController.php:118 -#: src/Frontend/DownloadController.php:128 -#: src/Frontend/DownloadController.php:137 -#: src/Frontend/DownloadController.php:147 -#: src/Frontend/DownloadController.php:156 -#: src/Frontend/DownloadController.php:165 -#: src/Frontend/DownloadController.php:187 -#: src/Frontend/DownloadController.php:203 -msgid "Download Error" -msgstr "" - -#: src/Frontend/DownloadController.php:87 -msgid "Invalid download link format." -msgstr "" - -#: src/Frontend/DownloadController.php:127 -msgid "You do not have permission to download this file." -msgstr "" - -#: src/Frontend/DownloadController.php:136 -msgid "Your license is not active. Please contact support." -msgstr "" - -#: src/Frontend/DownloadController.php:146 -msgid "Version not found." -msgstr "" - -#: src/Frontend/DownloadController.php:155 -msgid "Version does not match your licensed product." -msgstr "" - -#: src/Frontend/DownloadController.php:164 -msgid "This version is no longer available for download." -msgstr "" - -#: src/Frontend/DownloadController.php:186 -msgid "No download file available for this version." -msgstr "" - -#: src/Frontend/DownloadController.php:202 -msgid "Download file not found." -msgstr "" - -#: src/License/LicenseManager.php:412 -msgid "This license has been revoked." -msgstr "" - -#: src/License/LicenseManager.php:422 -msgid "This license has expired." -msgstr "" - -#: src/License/LicenseManager.php:430 -msgid "This license is inactive." -msgstr "" - -#: src/License/LicenseManager.php:440 -msgid "This license is not valid for this domain." -msgstr "" - -#: src/License/PluginLicenseChecker.php:132 -msgid "License settings not configured." -msgstr "" - -#: src/License/PluginLicenseChecker.php:168 -msgid "Could not connect to license server." -msgstr "" - -#: src/Plugin.php:318 +#: src/Plugin.php:336 msgid "WC Licensed Product" msgstr "" -#: src/Plugin.php:319 +#: src/Plugin.php:337 msgid "" "Plugin license is not configured or invalid. Frontend features are disabled." msgstr "" -#: src/Plugin.php:320 +#: src/Plugin.php:338 msgid "Configure License" msgstr "" -#: src/Product/LicensedProductType.php:61 -msgid "Licensed Product" -msgstr "" - -#: src/Product/LicensedProductType.php:82 -msgid "License Settings" -msgstr "" - -#: src/Product/LicensedProductType.php:109 +#: wc-licensed-product.php:61 #, php-format -msgid "%d days" +msgid "%s requires WooCommerce to be installed and active." msgstr "" -#: src/Product/LicensedProductType.php:119 -#, php-format -msgid "Leave fields empty to use default settings from %s." -msgstr "" - -#: src/Product/LicensedProductType.php:121 -msgid "WooCommerce > Settings > Licensed Products" -msgstr "" - -#: src/Product/LicensedProductType.php:128 -msgid "Max Activations" -msgstr "" - -#: src/Product/LicensedProductType.php:131 -#, php-format -msgid "Maximum number of domain activations per license. Default: %d" -msgstr "" - -#: src/Product/LicensedProductType.php:146 -msgid "License Validity (Days)" -msgstr "" - -#: src/Product/LicensedProductType.php:149 -#, php-format -msgid "Number of days the license is valid. Leave empty for default (%s)." -msgstr "" - -#: src/Product/LicensedProductType.php:164 -msgid "Bind to Major Version" -msgstr "" - -#: src/Product/LicensedProductType.php:167 -#, php-format -msgid "" -"If enabled, licenses are bound to the major version at purchase time. " -"Default: %s" -msgstr "" - -#: src/Product/LicensedProductType.php:168 -msgid "Yes" -msgstr "" - -#: src/Product/LicensedProductType.php:168 -msgid "No" -msgstr "" - -#: src/Product/LicensedProductType.php:288 -msgid "Version:" -msgstr "" - -#: src/Product/VersionManager.php:166 -msgid "Attachment file not found." -msgstr "" - -#: src/Product/VersionManager.php:177 -#, php-format -msgid "File checksum does not match. Expected: %1$s, Got: %2$s" -msgstr "" - -#: templates/frontend/licenses.html.twig:72 -msgid "API Verification Secret" -msgstr "" - -#: templates/frontend/licenses.html.twig:77 -msgid "Use this secret to verify signed API responses. Keep it secure." +#: wc-licensed-product.php:119 +msgid "WC Licensed Product requires WooCommerce to be installed and active." msgstr "" diff --git a/src/Checkout/CheckoutBlocksIntegration.php b/src/Checkout/CheckoutBlocksIntegration.php index 0386265..6af8157 100644 --- a/src/Checkout/CheckoutBlocksIntegration.php +++ b/src/Checkout/CheckoutBlocksIntegration.php @@ -11,6 +11,7 @@ namespace Jeremias\WcLicensedProduct\Checkout; use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface; use Jeremias\WcLicensedProduct\Admin\SettingsController; +use Jeremias\WcLicensedProduct\Product\LicensedProductVariation; /** * Integration with WooCommerce Checkout Blocks @@ -141,7 +142,7 @@ final class CheckoutBlocksIntegration implements IntegrationInterface /** * Get licensed products from cart with quantities * - * @return array + * @return array */ private function getLicensedProductsFromCart(): array { @@ -152,13 +153,49 @@ final class CheckoutBlocksIntegration implements IntegrationInterface $licensedProducts = []; foreach (WC()->cart->get_cart() as $cartItem) { $product = $cartItem['data']; - if ($product && $product->is_type('licensed')) { + if (!$product) { + continue; + } + + // Check for simple licensed products + if ($product->is_type('licensed')) { $productId = $product->get_id(); $licensedProducts[] = [ '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(); + + // 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[] = [ + 'product_id' => $parentId, + 'variation_id' => $variationId, + 'name' => $product->get_name(), + 'quantity' => (int) $cartItem['quantity'], + 'duration_label' => $durationLabel, + ]; + } } } diff --git a/src/Checkout/CheckoutController.php b/src/Checkout/CheckoutController.php index 264ae4c..e8526b8 100644 --- a/src/Checkout/CheckoutController.php +++ b/src/Checkout/CheckoutController.php @@ -11,6 +11,7 @@ namespace Jeremias\WcLicensedProduct\Checkout; use Jeremias\WcLicensedProduct\License\LicenseManager; use Jeremias\WcLicensedProduct\Admin\SettingsController; +use Jeremias\WcLicensedProduct\Product\LicensedProductVariation; /** * Handles checkout modifications for licensed products @@ -57,7 +58,7 @@ final class CheckoutController /** * Get licensed products from cart with quantities * - * @return array + * @return array */ private function getLicensedProductsFromCart(): array { @@ -68,13 +69,51 @@ final class CheckoutController $licensedProducts = []; foreach (WC()->cart->get_cart() as $cartItem) { $product = $cartItem['data']; - if ($product && $product->is_type('licensed')) { + 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, + ]; + } } } @@ -150,22 +189,32 @@ final class CheckoutController

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

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

getSavedDomainValue($productId, $i); + $fieldName = sprintf('licensed_domains[%s][%d]', $fieldKey, $i); + $fieldId = sprintf('licensed_domain_%s_%d', str_replace('_', '-', $fieldKey), $i); + $savedValue = $this->getSavedDomainValue($productId, $i, $variationId); ?>

@@ -197,6 +249,7 @@ final class CheckoutController .wclp-domain-description { margin-bottom: 15px; color: #666; } .wclp-product-domains { margin-bottom: 20px; padding: 15px; background: #f8f8f8; border-radius: 4px; } .wclp-product-domains h4 { margin: 0 0 10px 0; font-size: 1em; } + .wclp-duration-badge { color: #0073aa; font-weight: normal; } .wclp-domain-row { margin-bottom: 10px; } .wclp-domain-row:last-child { margin-bottom: 0; } .wclp-domain-row label { display: block; margin-bottom: 5px; } @@ -207,9 +260,17 @@ final class CheckoutController /** * Get saved domain value from session/POST */ - private function getSavedDomainValue(int $productId, int $index): string + private function getSavedDomainValue(int $productId, int $index, int $variationId = 0): string { + // Build the field key (with or without variation) + $fieldKey = $variationId > 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]); } @@ -218,7 +279,11 @@ final class CheckoutController if (WC()->session) { $sessionDomains = WC()->session->get('licensed_product_domains', []); foreach ($sessionDomains as $item) { - if (isset($item['product_id']) && (int) $item['product_id'] === $productId) { + $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]; } @@ -272,8 +337,12 @@ final class CheckoutController { $licensedDomains = $_POST['licensed_domains'] ?? []; - foreach ($licensedProducts as $productId => $productData) { - $productDomains = $licensedDomains[$productId] ?? []; + 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++) { @@ -308,7 +377,7 @@ final class CheckoutController continue; } - // Check for duplicate domains within same product + // Check for duplicate domains within same product/variation if (in_array($normalizedDomain, $normalizedDomains, true)) { wc_add_notice( sprintf( @@ -369,10 +438,15 @@ final class CheckoutController private function saveMultiDomainFields(\WC_Order $order, array $licensedProducts): void { $licensedDomains = $_POST['licensed_domains'] ?? []; + $licensedVariationIds = $_POST['licensed_variation_ids'] ?? []; $domainData = []; - foreach ($licensedProducts as $productId => $productData) { - $productDomains = $licensedDomains[$productId] ?? []; + 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++) { @@ -383,10 +457,17 @@ final class CheckoutController } if (!empty($normalizedDomains)) { - $domainData[] = [ + $entry = [ 'product_id' => $productId, 'domains' => $normalizedDomains, ]; + + // Include variation_id if present + if ($variationId > 0) { + $entry['variation_id'] = $variationId; + } + + $domainData[] = $entry; } } @@ -432,8 +513,22 @@ final class CheckoutController get_name() : __('Unknown Product', 'wc-licensed-product'); + $productId = $item['product_id']; + $variationId = $item['variation_id'] ?? 0; + + // Get product name + if ($variationId > 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'); + } ?>

:
@@ -482,8 +577,20 @@ final class CheckoutController if ($plainText) { echo "\n" . esc_html__('License Domains:', 'wc-licensed-product') . "\n"; foreach ($domainData as $item) { - $product = wc_get_product($item['product_id']); - $productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product'); + $productId = $item['product_id']; + $variationId = $item['variation_id'] ?? 0; + + if ($variationId > 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 { @@ -492,8 +599,19 @@ final class CheckoutController get_name() : __('Unknown Product', 'wc-licensed-product'); + $productId = $item['product_id']; + $variationId = $item['variation_id'] ?? 0; + + if ($variationId > 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'); + } ?>

:
diff --git a/src/Checkout/StoreApiExtension.php b/src/Checkout/StoreApiExtension.php index 29c6053..a43013e 100644 --- a/src/Checkout/StoreApiExtension.php +++ b/src/Checkout/StoreApiExtension.php @@ -100,6 +100,9 @@ final class StoreApiExtension 'product_id' => [ 'type' => 'integer', ], + 'variation_id' => [ + 'type' => 'integer', + ], 'domains' => [ 'type' => 'array', 'items' => [ @@ -162,6 +165,7 @@ final class StoreApiExtension } $productId = (int) $item['product_id']; + $variationId = isset($item['variation_id']) ? (int) $item['variation_id'] : 0; $domains = []; foreach ($item['domains'] as $domain) { @@ -172,10 +176,17 @@ final class StoreApiExtension } if (!empty($domains)) { - $normalized[] = [ + $entry = [ 'product_id' => $productId, 'domains' => $domains, ]; + + // Include variation_id if present + if ($variationId > 0) { + $entry['variation_id'] = $variationId; + } + + $normalized[] = $entry; } } @@ -267,10 +278,23 @@ final class StoreApiExtension // Check for licensed_domains in classic format (from DOM injection) if (empty($domainData) && isset($requestData['licensed_domains']) && is_array($requestData['licensed_domains'])) { $domainData = []; - foreach ($requestData['licensed_domains'] as $productId => $domains) { + $variationIds = $requestData['licensed_variation_ids'] ?? []; + + foreach ($requestData['licensed_domains'] as $key => $domains) { if (!is_array($domains)) { continue; } + + // Parse key - could be "productId" or "productId_variationId" + $parts = explode('_', (string) $key); + $productId = (int) $parts[0]; + $variationId = isset($parts[1]) ? (int) $parts[1] : 0; + + // Also check for hidden variation ID field + if ($variationId === 0 && isset($variationIds[$key])) { + $variationId = (int) $variationIds[$key]; + } + $normalizedDomains = []; foreach ($domains as $domain) { $sanitized = sanitize_text_field($domain); @@ -279,10 +303,16 @@ final class StoreApiExtension } } if (!empty($normalizedDomains)) { - $domainData[] = [ - 'product_id' => (int) $productId, + $entry = [ + 'product_id' => $productId, 'domains' => $normalizedDomains, ]; + + if ($variationId > 0) { + $entry['variation_id'] = $variationId; + } + + $domainData[] = $entry; } } } diff --git a/src/License/LicenseManager.php b/src/License/LicenseManager.php index 2012795..c64be1f 100644 --- a/src/License/LicenseManager.php +++ b/src/License/LicenseManager.php @@ -11,12 +11,43 @@ namespace Jeremias\WcLicensedProduct\License; use Jeremias\WcLicensedProduct\Installer; use Jeremias\WcLicensedProduct\Product\LicensedProduct; +use Jeremias\WcLicensedProduct\Product\LicensedProductVariation; +use Jeremias\WcLicensedProduct\Product\LicensedVariableProduct; /** * Manages license operations (CRUD, validation, generation) */ class LicenseManager { + /** + * Check if a product is any type of licensed product + * + * @param \WC_Product $product Product to check + * @return bool True if product is licensed (simple or variable or variation) + */ + public function isLicensedProduct(\WC_Product $product): bool + { + // Simple licensed product + if ($product->is_type('licensed')) { + return true; + } + + // Variable licensed product + if ($product->is_type('licensed-variable')) { + return true; + } + + // Variation of a licensed-variable product + 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 false; + } + /** * Generate a unique license key */ @@ -40,32 +71,63 @@ class LicenseManager /** * Generate a license for a completed order + * + * @param int $orderId Order ID + * @param int $productId Product ID (parent product for variations) + * @param int $customerId Customer ID + * @param string $domain Domain to bind the license to + * @param int|null $variationId Optional variation ID for variable licensed products + * @return License|null Generated license or null on failure */ public function generateLicense( int $orderId, int $productId, int $customerId, - string $domain + string $domain, + ?int $variationId = null ): ?License { global $wpdb; // Normalize domain first for duplicate detection $normalizedDomain = $this->normalizeDomain($domain); - // Check if license already exists for this order, product, and domain - $existing = $this->getLicenseByOrderProductAndDomain($orderId, $productId, $normalizedDomain); + // Check if license already exists for this order, product, domain, and variation + $existing = $this->getLicenseByOrderProductDomainAndVariation($orderId, $productId, $normalizedDomain, $variationId); if ($existing) { return $existing; } - $product = wc_get_product($productId); - if (!$product || !$product->is_type('licensed')) { - return null; + // Load the product that has the license settings + // For variations, load the variation; otherwise load the parent product + if ($variationId) { + $settingsProduct = wc_get_product($variationId); + $parentProduct = wc_get_product($productId); + + // Verify parent is licensed-variable + if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) { + return null; + } + + // Ensure we have the proper variation class + if ($settingsProduct && !$settingsProduct instanceof LicensedProductVariation) { + $settingsProduct = new LicensedProductVariation($variationId); + } + } else { + $settingsProduct = wc_get_product($productId); + + // Check if this is a licensed product (simple) + if (!$settingsProduct || !$settingsProduct->is_type('licensed')) { + return null; + } + + // Ensure we have the LicensedProduct instance for type hints + if (!$settingsProduct instanceof LicensedProduct) { + $settingsProduct = new LicensedProduct($productId); + } } - // Ensure we have the LicensedProduct instance for type hints - if (!$product instanceof LicensedProduct) { - $product = new LicensedProduct($productId); + if (!$settingsProduct) { + return null; } // Generate unique license key @@ -74,16 +136,16 @@ class LicenseManager $licenseKey = $this->generateLicenseKey(); } - // Calculate expiration date + // Calculate expiration date from the settings product (variation or parent) $expiresAt = null; - $validityDays = $product->get_validity_days(); + $validityDays = $settingsProduct->get_validity_days(); if ($validityDays !== null && $validityDays > 0) { $expiresAt = (new \DateTimeImmutable())->modify("+{$validityDays} days")->format('Y-m-d H:i:s'); } - // Determine version ID if bound to version + // Determine version ID if bound to version (always use parent product ID for versions) $versionId = null; - if ($product->is_bound_to_version()) { + if ($settingsProduct->is_bound_to_version()) { $versionId = $this->getCurrentVersionId($productId); } @@ -99,7 +161,7 @@ class LicenseManager 'version_id' => $versionId, 'status' => License::STATUS_ACTIVE, 'activations_count' => 1, - 'max_activations' => $product->get_max_activations(), + 'max_activations' => $settingsProduct->get_max_activations(), 'expires_at' => $expiresAt, ], ['%s', '%d', '%d', '%d', '%s', '%d', '%s', '%d', '%d', '%s'] @@ -112,6 +174,16 @@ class LicenseManager return $this->getLicenseById((int) $wpdb->insert_id); } + /** + * Get license by order, product, domain, and optional variation + */ + public function getLicenseByOrderProductDomainAndVariation(int $orderId, int $productId, string $domain, ?int $variationId = null): ?License + { + // For now, just use the existing method since we don't store variation_id in licenses table yet + // In the future, we could add a variation_id column to the licenses table + return $this->getLicenseByOrderProductAndDomain($orderId, $productId, $domain); + } + /** * Get license by ID */ diff --git a/src/Plugin.php b/src/Plugin.php index c4fb703..7e0f6dc 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -227,23 +227,35 @@ final class Plugin $orderId = $order->get_id(); $customerId = $order->get_customer_id(); - // Index domains by product ID for quick lookup + // 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'])) { - $domainsByProduct[(int) $item['product_id']] = $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 || !$product->is_type('licensed')) { + if (!$product || !$this->licenseManager->isLicensedProduct($product)) { continue; } - $productId = $product->get_id(); - $domains = $domainsByProduct[$productId] ?? []; + // 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) { @@ -252,7 +264,8 @@ final class Plugin $orderId, $productId, $customerId, - $domain + $domain, + $variationId > 0 ? $variationId : null ); } } @@ -271,12 +284,17 @@ final class Plugin foreach ($order->get_items() as $item) { $product = $item->get_product(); - if ($product && $product->is_type('licensed')) { + 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(), - $product->get_id(), + $productId, $order->get_customer_id(), - $domain + $domain, + $variationId > 0 ? $variationId : null ); } } diff --git a/src/Product/LicensedProductType.php b/src/Product/LicensedProductType.php index 0a903c3..7815b2c 100644 --- a/src/Product/LicensedProductType.php +++ b/src/Product/LicensedProductType.php @@ -12,7 +12,8 @@ namespace Jeremias\WcLicensedProduct\Product; use Jeremias\WcLicensedProduct\Admin\SettingsController; /** - * Registers and handles the Licensed product type for WooCommerce + * Registers and handles the Licensed product types for WooCommerce + * Supports both simple licensed products and variable licensed products */ final class LicensedProductType { @@ -29,7 +30,7 @@ final class LicensedProductType */ private function registerHooks(): void { - // Register product type + // Register product types add_filter('product_type_selector', [$this, 'addProductType']); add_filter('woocommerce_product_class', [$this, 'getProductClass'], 10, 2); @@ -39,9 +40,11 @@ final class LicensedProductType // Save product meta add_action('woocommerce_process_product_meta_licensed', [$this, 'saveProductMeta']); + add_action('woocommerce_process_product_meta_licensed-variable', [$this, 'saveProductMeta']); // Show price and add to cart for licensed products add_action('woocommerce_licensed_add_to_cart', [$this, 'addToCartTemplate']); + add_action('woocommerce_licensed-variable_add_to_cart', [$this, 'variableAddToCartTemplate']); // Make product virtual by default add_filter('woocommerce_product_is_virtual', [$this, 'isVirtual'], 10, 2); @@ -51,25 +54,48 @@ final class LicensedProductType // Enqueue frontend CSS for licensed products on single product pages add_action('wp_enqueue_scripts', [$this, 'enqueueFrontendStyles']); + + // Variable product support - variation settings + add_action('woocommerce_variation_options', [$this, 'addVariationOptions'], 10, 3); + add_action('woocommerce_product_after_variable_attributes', [$this, 'addVariationFields'], 10, 3); + add_action('woocommerce_save_product_variation', [$this, 'saveVariationFields'], 10, 2); + + // Admin scripts for licensed-variable type + add_action('admin_footer', [$this, 'addVariableProductScripts']); } /** - * Add product type to selector + * Add product types to selector */ public function addProductType(array $types): array { $types['licensed'] = __('Licensed Product', 'wc-licensed-product'); + $types['licensed-variable'] = __('Licensed Variable Product', 'wc-licensed-product'); return $types; } /** - * Get product class for licensed type + * Get product class for licensed types */ public function getProductClass(string $className, string $productType): string { if ($productType === 'licensed') { return LicensedProduct::class; } + if ($productType === 'licensed-variable') { + 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); + if ($parentType === 'licensed-variable') { + return LicensedProductVariation::class; + } + } + } return $className; } @@ -81,7 +107,7 @@ final class LicensedProductType $tabs['licensed_product'] = [ 'label' => __('License Settings', 'wc-licensed-product'), 'target' => 'licensed_product_data', - 'class' => ['show_if_licensed'], + 'class' => ['show_if_licensed', 'show_if_licensed-variable'], 'priority' => 21, ]; return $tabs; @@ -236,9 +262,16 @@ final class LicensedProductType */ public function isVirtual(bool $isVirtual, \WC_Product $product): bool { - if ($product->is_type('licensed')) { + if ($product->is_type('licensed') || $product->is_type('licensed-variable')) { 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; } @@ -253,7 +286,7 @@ final class LicensedProductType global $product; - if (!$product || !$product->is_type('licensed')) { + if (!$product || (!$product->is_type('licensed') && !$product->is_type('licensed-variable'))) { return; } @@ -272,11 +305,11 @@ final class LicensedProductType { global $product; - if (!$product || !$product->is_type('licensed')) { + if (!$product || (!$product->is_type('licensed') && !$product->is_type('licensed-variable'))) { return; } - /** @var LicensedProduct $product */ + /** @var LicensedProduct|LicensedVariableProduct $product */ $version = $product->get_current_version(); if (empty($version)) { @@ -289,4 +322,200 @@ final class LicensedProductType esc_html($version) ); } + + /** + * Add to cart template for variable licensed products + */ + public function variableAddToCartTemplate(): void + { + wc_get_template('single-product/add-to-cart/variable.php'); + } + + /** + * Add variation options (checkboxes next to variation header) + */ + public function addVariationOptions(int $loop, array $variationData, \WP_Post $variation): void + { + // Check if parent is licensed-variable + $parentId = $variation->post_parent; + $parentProduct = wc_get_product($parentId); + + if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) { + return; + } + + $isVirtual = get_post_meta($variation->ID, '_virtual', true); + ?> + + post_parent; + $parentProduct = wc_get_product($parentId); + + if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) { + return; + } + + // Get variation values + $validityDays = get_post_meta($variation->ID, '_licensed_validity_days', true); + $maxActivations = get_post_meta($variation->ID, '_licensed_max_activations', true); + + // Get parent defaults for placeholder + $parentValidityDays = $parentProduct->get_validity_days(); + $parentMaxActivations = $parentProduct->get_max_activations(); + + $parentValidityDisplay = $parentValidityDays !== null + ? sprintf(__('%d days', 'wc-licensed-product'), $parentValidityDays) + : __('Lifetime', 'wc-licensed-product'); + + ?> +

+

+ + + +

+

+ + + +

+
+ + get_parent_id()); + if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) { + return; + } + + // Save validity days + // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by WooCommerce + if (isset($_POST['wclp_validity_days'][$loop])) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $validityDays = sanitize_text_field($_POST['wclp_validity_days'][$loop]); + if ($validityDays !== '') { + update_post_meta($variationId, '_licensed_validity_days', absint($validityDays)); + } else { + delete_post_meta($variationId, '_licensed_validity_days'); + } + } + + // Save max activations + // phpcs:ignore WordPress.Security.NonceVerification.Missing + if (isset($_POST['wclp_max_activations'][$loop])) { + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $maxActivations = sanitize_text_field($_POST['wclp_max_activations'][$loop]); + if ($maxActivations !== '') { + update_post_meta($variationId, '_licensed_max_activations', absint($maxActivations)); + } else { + delete_post_meta($variationId, '_licensed_max_activations'); + } + } + + // Set variation as virtual (licensed products are always virtual) + update_post_meta($variationId, '_virtual', 'yes'); + } + + /** + * Add JavaScript for licensed-variable product type in admin + */ + public function addVariableProductScripts(): void + { + global $post, $pagenow; + + if ($pagenow !== 'post.php' && $pagenow !== 'post-new.php') { + return; + } + + if (!$post || get_post_type($post) !== 'product') { + return; + } + + ?> + + get_meta('_licensed_max_activations', true); + if ($value !== '' && $value !== null) { + return max(1, (int) $value); + } + + // Fall back to parent product + $parent = wc_get_product($this->get_parent_id()); + if ($parent && method_exists($parent, 'get_max_activations')) { + return $parent->get_max_activations(); + } + + return SettingsController::getDefaultMaxActivations(); + } + + /** + * Check if variation 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 for this variation + * This is the primary license setting that varies per variation + * Falls back to parent product, then to default settings + */ + public function get_validity_days(): ?int + { + // Check variation-specific setting first + $value = $this->get_meta('_licensed_validity_days', true); + if ($value !== '' && $value !== null) { + $days = (int) $value; + // 0 means lifetime + return $days > 0 ? $days : null; + } + + // Fall back to parent product + $parent = wc_get_product($this->get_parent_id()); + if ($parent && method_exists($parent, 'get_validity_days')) { + return $parent->get_validity_days(); + } + + return SettingsController::getDefaultValidityDays(); + } + + /** + * Check if variation 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 parent product, then to default settings + */ + public function is_bound_to_version(): bool + { + // Check variation-specific setting first + $value = $this->get_meta('_licensed_bind_to_version', true); + if ($value !== '' && $value !== null) { + return $value === 'yes'; + } + + // Fall back to parent product + $parent = wc_get_product($this->get_parent_id()); + if ($parent && method_exists($parent, 'is_bound_to_version')) { + return $parent->is_bound_to_version(); + } + + return SettingsController::getDefaultBindToVersion(); + } + + /** + * Check if variation 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 the license duration label for display + */ + public function get_license_duration_label(): string + { + $days = $this->get_validity_days(); + + if ($days === null) { + return __('Lifetime', 'wc-licensed-product'); + } + + if ($days === 30) { + return __('Monthly', 'wc-licensed-product'); + } + + if ($days === 90) { + return __('Quarterly', 'wc-licensed-product'); + } + + if ($days === 365) { + return __('Yearly', 'wc-licensed-product'); + } + + return sprintf( + /* translators: %d: number of days */ + _n('%d day', '%d days', $days, 'wc-licensed-product'), + $days + ); + } + + /** + * Get current software version from parent product + */ + public function get_current_version(): string + { + $parent = wc_get_product($this->get_parent_id()); + if ($parent && method_exists($parent, 'get_current_version')) { + return $parent->get_current_version(); + } + + $versionManager = new VersionManager(); + $latestVersion = $versionManager->getLatestVersion($this->get_parent_id()); + + return $latestVersion ? $latestVersion->getVersion() : ''; + } + + /** + * Get major version number from parent product + */ + public function get_major_version(): int + { + $parent = wc_get_product($this->get_parent_id()); + if ($parent && method_exists($parent, 'get_major_version')) { + return $parent->get_major_version(); + } + + $versionManager = new VersionManager(); + $latestVersion = $versionManager->getLatestVersion($this->get_parent_id()); + + if ($latestVersion) { + return $latestVersion->getMajorVersion(); + } + + return 1; + } +} diff --git a/src/Product/LicensedVariableProduct.php b/src/Product/LicensedVariableProduct.php new file mode 100644 index 0000000..81fc864 --- /dev/null +++ b/src/Product/LicensedVariableProduct.php @@ -0,0 +1,151 @@ +exists() && $this->get_price() !== ''; + } + + /** + * 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; + } +} diff --git a/wc-licensed-product.php b/wc-licensed-product.php index e820bf2..7f9e3ae 100644 --- a/wc-licensed-product.php +++ b/wc-licensed-product.php @@ -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.2 + * Version: 0.5.3 * 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.2'); +define('WC_LICENSED_PRODUCT_VERSION', '0.5.3'); 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__));