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 d0f5597..59b678c 100644 Binary files a/languages/wc-licensed-product-de_CH.mo and b/languages/wc-licensed-product-de_CH.mo differ 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__));