7 Commits

Author SHA1 Message Date
c7967f71ab Update translations for v0.3.5
- Added translations for dashboard widget strings
- Added translations for license expired email strings
- Updated fuzzy translations with proper German text
- Compiled .mo file for production use

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:10:51 +01:00
1de8257527 Add dashboard widget and auto-expire license cron (v0.3.5)
- Add admin dashboard widget with license statistics
- Add daily wp-cron to auto-expire licenses past expiration date
- Add LicenseExpiredEmail notification for expired licenses
- Add getExpiredActiveLicenses() and autoExpireLicense() to LicenseManager

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 16:05:52 +01:00
26245c0c57 Remove redundant version badge from download list
Version is already shown in the download filename

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:10:50 +01:00
a6c6d247aa Improve download list layout in customer account (v0.3.5)
- Downloads now displayed in two-row format per entry
- First row: file download link
- Second row: metadata (version, date, checksum)
- Better visual separation and readability

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:07:49 +01:00
fba8bf2352 Add release package for v0.3.4
- wc-licensed-product-0.3.4.zip (784 KB)
- SHA256: 36a81c00eb03adf5dfa633891664d44b7e5225bf1ee594904f8acc9adec6bb47

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:02:28 +01:00
12a3a37658 Add product version display on single product page (v0.3.4)
- Display current version under product title for licensed products
- Add frontend CSS styling for version badge
- Update translations for new "Version:" string
- Bump version to 0.3.4

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:01:11 +01:00
b1fe34adfd Add v0.3.3 release package
Package: wc-licensed-product-0.3.3.zip (795 KB)
SHA256: a06d29eabc2da08613ae13874ed152b8ea9363b8284a2e9bdda414e32777558c

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 11:46:33 +01:00
19 changed files with 1643 additions and 543 deletions

View File

@@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.3.5] - 2026-01-23
### Added
- Admin dashboard widget showing license statistics on WordPress dashboard
- Automatic license expiration via daily wp-cron job
- License expired email notification sent when license auto-expires
- New `LicenseExpiredEmail` WooCommerce email class (configurable via WooCommerce > Settings > Emails)
### Changed
- Improved download list layout in customer account licenses page
- Downloads now displayed in two-row format: file link on first row, metadata on second row
- Better visual separation between download link and version/date/checksum information
### Technical Details
- New `DashboardWidgetController` class in `src/Admin/` for WordPress dashboard widget
- Widget displays: total licenses, active, expiring soon, expired counts, status breakdown, license types
- New `LicenseExpiredEmail` class in `src/Email/` for expired license notifications
- Added `getExpiredActiveLicenses()` and `autoExpireLicense()` methods to `LicenseManager`
- Daily cron now auto-expires licenses with past expiration date and sends notification emails
- Updated `templates/frontend/licenses.html.twig` with new two-row structure
- Added `.download-item`, `.download-row-file`, `.download-row-meta` CSS classes
- Improved responsive behavior for download metadata
## [0.3.4] - 2026-01-23
### Added
- Current version display on single product pages for licensed products
- Version number shown directly under the product title
- Frontend CSS styling for version badge with monospace font
### Technical Details
- Added `displayCurrentVersion()` method to `LicensedProductType` class
- Hooked to `woocommerce_single_product_summary` at priority 6 (after title)
- Added `enqueueFrontendStyles()` to load CSS on product pages
- Uses `LicensedProduct::get_current_version()` to fetch latest version
## [0.3.3] - 2026-01-22 ## [0.3.3] - 2026-01-22
### Fixed ### Fixed

View File

@@ -36,6 +36,10 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
No known bugs at the moment. No known bugs at the moment.
### Version 0.4.0
- On first plugin activation, get the checksums of all security related files (at least in `src/`) as hashes, store them encrypted on the server and add a mechanism to check the integrity of the files and the license validity periodically, control via wp-cron.
## Technical Stack ## Technical Stack
- **Language:** PHP 8.3.x - **Language:** PHP 8.3.x
@@ -878,3 +882,95 @@ Updated OpenAPI specification to document response signing feature added in v0.2
- Created release package: `releases/wc-licensed-product-0.3.2.zip` (810 KB) - Created release package: `releases/wc-licensed-product-0.3.2.zip` (810 KB)
- SHA256: `ca33c81516b5dcf4a80b3192d8ae4ad39a7bf67196a1f729b563c5ae01b1d39c` - SHA256: `ca33c81516b5dcf4a80b3192d8ae4ad39a7bf67196a1f729b563c5ae01b1d39c`
- Tagged as `v0.3.2` and pushed to `main` branch - Tagged as `v0.3.2` and pushed to `main` branch
### 2026-01-22 - Version 0.3.3 - Bug Fix & License Testing
**Overview:**
Fixed version deactivation bug and added license testing functionality.
**Bug Fix:**
- Fixed version deactivation button not working in admin product versions table
- Root cause: Parameters in wrong order in `VersionAdminController::ajaxToggleVersion()`
- Changed from `updateVersion($versionId, null, null, !$currentlyActive)` to `updateVersion($versionId, null, !$currentlyActive, null)`
**Implemented:**
- Added "Test" action to license overview to validate licenses against `/validate` API endpoint
- Test License modal showing license key, domain, and validation results
- AJAX handler `handleAjaxTestLicense()` for license testing
**Modified files:**
- `src/Admin/VersionAdminController.php` - Fixed parameter order in toggle method
- `src/Admin/AdminController.php` - Added Test action to PHP fallback and AJAX handler
- `templates/admin/licenses.html.twig` - Added Test action and modal to Twig template
**Release v0.3.3:**
- Created release package: `releases/wc-licensed-product-0.3.3.zip` (795 KB)
- SHA256: `a06d29eabc2da08613ae13874ed152b8ea9363b8284a2e9bdda414e32777558c`
- Tagged as `v0.3.3` and pushed to `main` branch
### 2026-01-23 - Version 0.3.4 - Frontend Version Display
**Overview:**
Added current version display on single product pages for licensed products.
**Implemented:**
- Current version displayed directly under the product title
- Styled version badge with monospace font and subtle blue background
- Frontend CSS automatically loaded on licensed product pages
**Modified files:**
- `src/Product/LicensedProductType.php` - Added `displayCurrentVersion()` and `enqueueFrontendStyles()` methods
- `assets/css/frontend.css` - Added `.wclp-product-version` styles
**Technical notes:**
- Uses `woocommerce_single_product_summary` hook at priority 6 (after title at priority 5)
- Only displays for licensed product type
- Only displays if product has at least one version defined
- Uses `LicensedProduct::get_current_version()` which queries `VersionManager::getLatestVersion()`
### 2026-01-23 - Version 0.3.5 - Dashboard Widget & Auto-Expire
**Overview:**
Added admin dashboard widget for license statistics and automatic license expiration via daily cron job.
**Implemented:**
- Admin dashboard widget showing license statistics (total, active, expiring soon, expired)
- Status breakdown display with color-coded badges
- License type breakdown (time-limited vs lifetime)
- Daily wp-cron job to auto-expire licenses past their expiration date
- License expired email notification sent when license auto-expires
- Downloads in customer account now displayed in two-row format
**New files:**
- `src/Admin/DashboardWidgetController.php` - WordPress dashboard widget controller
- `src/Email/LicenseExpiredEmail.php` - WooCommerce email for expired license notifications
**Modified files:**
- `src/Plugin.php` - Added DashboardWidgetController instantiation
- `src/License/LicenseManager.php` - Added `getExpiredActiveLicenses()` and `autoExpireLicense()` methods
- `src/Email/LicenseEmailController.php` - Added auto-expire logic and LicenseExpiredEmail registration
- `templates/frontend/licenses.html.twig` - Restructured download list with two-row layout
- `assets/css/frontend.css` - Added dashboard widget and download list styles
**Technical notes:**
- Dashboard widget uses `wp_add_dashboard_widget()` hook, requires `manage_woocommerce` capability
- Widget displays statistics from existing `LicenseManager::getStatistics()` method
- Auto-expire runs during daily `wclp_check_expiring_licenses` cron event
- `getExpiredActiveLicenses()` finds licenses with past expiration date but still active status
- `autoExpireLicense()` updates status to expired and returns true if changed
- LicenseExpiredEmail follows same pattern as LicenseExpirationEmail (warning vs expired)
- Expired notification tracked via user meta to prevent duplicate emails

View File

@@ -202,18 +202,30 @@
padding: 0; padding: 0;
} }
.download-list li { .download-list li.download-item {
display: flex; display: flex;
align-items: center; flex-direction: column;
gap: 1em; gap: 0.35em;
padding: 0.5em 0; padding: 0.75em 0;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.download-list li:last-child { .download-list li.download-item:last-child {
border-bottom: none; border-bottom: none;
} }
.download-row-file {
display: flex;
align-items: center;
}
.download-row-meta {
display: flex;
align-items: center;
gap: 1em;
padding-left: 1.5em;
}
.download-link { .download-link {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -244,7 +256,6 @@
.download-date { .download-date {
color: #999; color: #999;
font-size: 0.85em; font-size: 0.85em;
margin-left: auto;
} }
.download-hash { .download-hash {
@@ -338,15 +349,11 @@
gap: 0.5em; gap: 0.5em;
} }
.download-list li { .download-row-meta {
padding-left: 0;
flex-wrap: wrap; flex-wrap: wrap;
} }
.download-date {
margin-left: 0;
width: 100%;
}
.woocommerce-licenses-table, .woocommerce-licenses-table,
.woocommerce-licenses-table thead, .woocommerce-licenses-table thead,
.woocommerce-licenses-table tbody, .woocommerce-licenses-table tbody,
@@ -528,3 +535,24 @@
color: #721c24; color: #721c24;
border: 1px solid #f5c6cb; border: 1px solid #f5c6cb;
} }
/* Product Version Display (Single Product Page) */
.wclp-product-version {
margin: 0.5em 0 1em 0;
font-size: 0.95em;
color: #666;
}
.wclp-product-version .version-label {
font-weight: 500;
color: #555;
}
.wclp-product-version .version-number {
font-family: 'SF Mono', Monaco, Consolas, monospace;
background: #e7f3ff;
padding: 0.15em 0.5em;
border-radius: 3px;
color: #2271b1;
font-weight: 500;
}

4
composer.lock generated
View File

@@ -12,7 +12,7 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client.git", "url": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client.git",
"reference": "83037ea0c2d9e365cf9ec0ad50251d3ebc7e4782" "reference": "a3a957914fd6ef74cb479e213d1d3bc0606f496b"
}, },
"require": { "require": {
"php": "^8.3", "php": "^8.3",
@@ -52,7 +52,7 @@
"issues": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client/issues", "issues": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client/issues",
"source": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client" "source": "https://src.bundespruefstelle.ch/magdev/wc-licensed-product-client"
}, },
"time": "2026-01-22T15:24:57+00:00" "time": "2026-01-22T20:05:48+00:00"
}, },
{ {
"name": "psr/cache", "name": "psr/cache",

View File

@@ -4,8 +4,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: WC Licensed Product 0.3.1\n" "Project-Id-Version: WC Licensed Product 0.3.1\n"
"Report-Msgid-Bugs-To: magdev3.0@gmail.com\n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-23 11:41+0100\n" "POT-Creation-Date: 2026-01-23 16:05+0100\n"
"PO-Revision-Date: 2026-01-22T17:15:00+00:00\n" "PO-Revision-Date: 2026-01-22T17:15:00+00:00\n"
"Last-Translator: Marco Graetsch <magdev3.0@gmail.com>\n" "Last-Translator: Marco Graetsch <magdev3.0@gmail.com>\n"
"Language-Team: German (Switzerland) <de_CH@li.org>\n" "Language-Team: German (Switzerland) <de_CH@li.org>\n"
@@ -79,16 +79,16 @@ msgstr ""
"Lizenzen werden generiert, sobald die Bestellung als bezahlt/abgeschlossen " "Lizenzen werden generiert, sobald die Bestellung als bezahlt/abgeschlossen "
"markiert wird." "markiert wird."
#: src/Admin/OrderLicenseController.php:144 src/Admin/AdminController.php:1280 #: src/Admin/OrderLicenseController.php:144
#: src/Admin/SettingsController.php:142 src/Admin/AdminController.php:1280
#: src/Admin/AdminController.php:1431 src/Admin/AdminController.php:1480 #: src/Admin/AdminController.php:1431 src/Admin/AdminController.php:1480
#: src/Admin/SettingsController.php:142 #: src/Email/LicenseEmailController.php:269
#: src/Email/LicenseEmailController.php:230
msgid "License Key" msgid "License Key"
msgstr "Lizenzschlüssel" msgstr "Lizenzschlüssel"
#: src/Admin/OrderLicenseController.php:145 src/Admin/AdminController.php:1281 #: src/Admin/OrderLicenseController.php:145 src/Admin/AdminController.php:1281
#: src/Admin/AdminController.php:1432 src/Admin/AdminController.php:1597 #: src/Admin/AdminController.php:1432 src/Admin/AdminController.php:1597
#: src/Email/LicenseEmailController.php:229 #: src/Email/LicenseEmailController.php:268
msgid "Product" msgid "Product"
msgstr "Produkt" msgstr "Produkt"
@@ -104,7 +104,7 @@ msgstr "Status"
#: src/Admin/OrderLicenseController.php:148 src/Admin/AdminController.php:1286 #: src/Admin/OrderLicenseController.php:148 src/Admin/AdminController.php:1286
#: src/Admin/AdminController.php:1437 src/Admin/AdminController.php:1600 #: src/Admin/AdminController.php:1437 src/Admin/AdminController.php:1600
#: src/Admin/AdminController.php:1602 src/Email/LicenseEmailController.php:231 #: src/Admin/AdminController.php:1602 src/Email/LicenseEmailController.php:270
msgid "Expires" msgid "Expires"
msgstr "Läuft ab" msgstr "Läuft ab"
@@ -129,11 +129,11 @@ msgstr "Domain bearbeiten"
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: src/Admin/OrderLicenseController.php:201 src/Admin/AdminController.php:151 #: src/Admin/OrderLicenseController.php:201
#: src/Admin/SettingsController.php:192 src/Admin/AdminController.php:151
#: src/Admin/AdminController.php:266 src/Admin/AdminController.php:1362 #: src/Admin/AdminController.php:266 src/Admin/AdminController.php:1362
#: src/Admin/AdminController.php:1602 src/Admin/SettingsController.php:192 #: src/Admin/AdminController.php:1602 src/Product/LicensedProductType.php:110
#: src/Product/LicensedProductType.php:104 #: src/Product/LicensedProductType.php:158
#: src/Product/LicensedProductType.php:152
msgid "Lifetime" msgid "Lifetime"
msgstr "Lebenslang" msgstr "Lebenslang"
@@ -213,6 +213,179 @@ msgstr "Lizenz-Domain aktualisiert."
msgid "Failed to update license domain." msgid "Failed to update license domain."
msgstr "Lizenz-Domain konnte nicht aktualisiert werden." msgstr "Lizenz-Domain konnte nicht aktualisiert werden."
#: src/Admin/SettingsController.php:54
msgid "Licensed Products"
msgstr "Lizensierte Produkte"
#: src/Admin/SettingsController.php:64 src/Admin/SettingsController.php:128
msgid "Plugin License"
msgstr "Plugin-Lizenz"
#: src/Admin/SettingsController.php:65
msgid "Default Settings"
msgstr "Standardeinstellungen"
#: src/Admin/SettingsController.php:66
msgid "Notifications"
msgstr "Benachrichtigungen"
#: src/Admin/SettingsController.php:130
msgid ""
"Configure the license for this plugin. A valid license is required for "
"frontend features to work."
msgstr ""
"Konfigurieren Sie die Lizenz für dieses Plugin. Eine gültige Lizenz ist "
"erforderlich, damit die Frontend-Funktionen funktionieren."
#: src/Admin/SettingsController.php:134
msgid "License Server URL"
msgstr "Lizenzserver-URL"
#: src/Admin/SettingsController.php:136
msgid "The URL of the license server (e.g., https://shop.example.com)."
msgstr "Die URL des Lizenzservers (z.B. https://shop.example.com)."
#: src/Admin/SettingsController.php:144
msgid "Your license key in XXXX-XXXX-XXXX-XXXX format."
msgstr "Ihr Lizenzschlüssel im Format XXXX-XXXX-XXXX-XXXX."
#: src/Admin/SettingsController.php:150
msgid "Server Secret (Optional)"
msgstr "Server-Secret (Optional)"
#: src/Admin/SettingsController.php:152
msgid ""
"If the license server uses signed responses, enter the shared secret here "
"for enhanced security."
msgstr ""
"Falls der Lizenzserver signierte Antworten verwendet, geben Sie hier das "
"gemeinsame Secret für erhöhte Sicherheit ein."
#: src/Admin/SettingsController.php:170
msgid "Default License Settings"
msgstr "Standard Lizenz-Einstellungen"
#: src/Admin/SettingsController.php:172
msgid ""
"These settings serve as defaults for new licensed products. Individual "
"product settings override these defaults."
msgstr ""
"Diese Einstellungen dienen als Standard für neue lizensierte Produkte. "
"Individuelle Produkteinstellungen überschreiben diese Standards."
#: src/Admin/SettingsController.php:176
msgid "Default Max Activations"
msgstr "Standard Max. Aktivierungen"
#: src/Admin/SettingsController.php:178
msgid "Default maximum number of domain activations per license."
msgstr "Standard maximale Anzahl der Domain-Aktivierungen pro Lizenz."
#: src/Admin/SettingsController.php:187
msgid "Default License Validity (Days)"
msgstr "Standard Lizenz-Gültigkeit (Tage)"
#: src/Admin/SettingsController.php:189
msgid ""
"Default number of days a license is valid. Leave empty or set to 0 for "
"lifetime licenses."
msgstr ""
"Standard Anzahl Tage, die eine Lizenz gültig ist. Leer lassen oder auf 0 "
"setzen für lebenslange Lizenzen."
#: src/Admin/SettingsController.php:199
msgid "Default Bind to Major Version"
msgstr "Standard An Hauptversion binden"
#: src/Admin/SettingsController.php:201
msgid ""
"If enabled, licenses are bound to the major version at purchase time by "
"default."
msgstr ""
"Falls aktiviert, werden Lizenzen standardmässig an die Hauptversion zum "
"Kaufzeitpunkt gebunden."
#: src/Admin/SettingsController.php:219
msgid "Expiration Warning Schedule"
msgstr "Ablaufwarnung Zeitplan"
#: src/Admin/SettingsController.php:223
#, php-format
msgid ""
"Configure when expiration warning emails are sent. To customize the email "
"template, enable/disable, or change the subject, go to %s."
msgstr ""
"Konfigurieren Sie, wann Ablaufwarnungs-E-Mails gesendet werden. Um die E-"
"Mail-Vorlage anzupassen, zu aktivieren/deaktivieren oder den Betreff zu "
"ändern, gehen Sie zu %s."
#: src/Admin/SettingsController.php:225
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
msgstr "WooCommerce > Einstellungen > E-Mails > Lizenzablauf-Warnung"
#: src/Admin/SettingsController.php:230
msgid "First Warning (Days Before)"
msgstr "Erste Warnung (Tage vorher)"
#: src/Admin/SettingsController.php:232
msgid "Days before expiration to send the first warning email."
msgstr "Tage vor Ablauf, um die erste Warn-E-Mail zu senden."
#: src/Admin/SettingsController.php:241
msgid "Second Warning (Days Before)"
msgstr "Zweite Warnung (Tage vorher)"
#: src/Admin/SettingsController.php:243
msgid ""
"Days before expiration to send the second warning email. Set to 0 to disable."
msgstr ""
"Tage vor Ablauf, um die zweite Warn-E-Mail zu senden. Setzen Sie auf 0, um "
"sie zu deaktivieren."
#: src/Admin/SettingsController.php:283
msgid "Running on localhost - license validation bypassed."
msgstr "Läuft auf localhost - Lizenzvalidierung übersprungen."
#: src/Admin/SettingsController.php:291
msgid "License is valid and active."
msgstr "Lizenz ist gültig und aktiv."
#: src/Admin/SettingsController.php:297
msgid "License is not valid. Frontend features are disabled."
msgstr "Lizenz ist ungültig. Frontend-Funktionen sind deaktiviert."
#: src/Admin/SettingsController.php:308 src/Admin/SettingsController.php:344
msgid "Verify License"
msgstr "Lizenz überprüfen"
#: src/Admin/SettingsController.php:322
msgid "Verifying..."
msgstr "Überprüfe..."
#: src/Admin/SettingsController.php:341
msgid "Request failed."
msgstr "Anfrage fehlgeschlagen."
#: src/Admin/SettingsController.php:454 src/Admin/AdminController.php:455
#: src/Admin/AdminController.php:475 src/Admin/AdminController.php:493
#: src/Admin/AdminController.php:511 src/Admin/AdminController.php:531
#: src/Admin/AdminController.php:549 src/Admin/AdminController.php:616
#: src/Admin/AdminController.php:806 src/Frontend/AccountController.php:326
msgid "Security check failed."
msgstr "Sicherheitsüberprüfung fehlgeschlagen."
#: src/Admin/SettingsController.php:458
msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen."
#: src/Admin/SettingsController.php:467
msgid "License verified successfully!"
msgstr "Lizenz erfolgreich überprüft!"
#: src/Admin/SettingsController.php:469
msgid "License validation failed."
msgstr "Lizenzvalidierung fehlgeschlagen."
#: src/Admin/AdminController.php:93 #: src/Admin/AdminController.php:93
msgid "Overview" msgid "Overview"
msgstr "Übersicht" msgstr "Übersicht"
@@ -261,6 +434,7 @@ msgstr "Kopieren fehlgeschlagen"
#: src/Admin/AdminController.php:1221 src/Admin/AdminController.php:1344 #: src/Admin/AdminController.php:1221 src/Admin/AdminController.php:1344
#: src/Admin/VersionAdminController.php:182 #: src/Admin/VersionAdminController.php:182
#: src/Admin/VersionAdminController.php:413 #: src/Admin/VersionAdminController.php:413
#: src/Admin/DashboardWidgetController.php:151
msgid "Active" msgid "Active"
msgstr "Aktiv" msgstr "Aktiv"
@@ -273,6 +447,8 @@ msgstr "Inaktiv"
#: src/Admin/AdminController.php:158 src/Admin/AdminController.php:916 #: src/Admin/AdminController.php:158 src/Admin/AdminController.php:916
#: src/Admin/AdminController.php:1223 src/Admin/AdminController.php:1346 #: src/Admin/AdminController.php:1223 src/Admin/AdminController.php:1346
#: src/Admin/DashboardWidgetController.php:159
#: src/Email/LicenseExpiredEmail.php:210 src/Email/LicenseExpiredEmail.php:259
msgid "Expired" msgid "Expired"
msgstr "Abgelaufen" msgstr "Abgelaufen"
@@ -333,14 +509,6 @@ msgstr "Lizenz konnte nicht widerrufen werden."
msgid "License key and domain are required." msgid "License key and domain are required."
msgstr "Lizenzschlüssel und Domain sind erforderlich." msgstr "Lizenzschlüssel und Domain sind erforderlich."
#: src/Admin/AdminController.php:455 src/Admin/AdminController.php:475
#: src/Admin/AdminController.php:493 src/Admin/AdminController.php:511
#: src/Admin/AdminController.php:531 src/Admin/AdminController.php:549
#: src/Admin/AdminController.php:616 src/Admin/AdminController.php:806
#: src/Admin/SettingsController.php:454 src/Frontend/AccountController.php:326
msgid "Security check failed."
msgstr "Sicherheitsüberprüfung fehlgeschlagen."
#: src/Admin/AdminController.php:576 #: src/Admin/AdminController.php:576
msgid "You do not have permission to export licenses." msgid "You do not have permission to export licenses."
msgstr "Sie haben keine Berechtigung, Lizenzen zu exportieren." msgstr "Sie haben keine Berechtigung, Lizenzen zu exportieren."
@@ -367,6 +535,7 @@ msgid "License Dashboard"
msgstr "Lizenz-Dashboard" msgstr "Lizenz-Dashboard"
#: src/Admin/AdminController.php:895 #: src/Admin/AdminController.php:895
#: src/Admin/DashboardWidgetController.php:147
msgid "Total Licenses" msgid "Total Licenses"
msgstr "Lizenzen insgesamt" msgstr "Lizenzen insgesamt"
@@ -603,6 +772,7 @@ msgstr "Anwenden"
#: src/Admin/AdminController.php:1282 src/Admin/AdminController.php:1433 #: src/Admin/AdminController.php:1282 src/Admin/AdminController.php:1433
#: src/Email/LicenseExpirationEmail.php:104 #: src/Email/LicenseExpirationEmail.php:104
#: src/Email/LicenseExpiredEmail.php:96
msgid "Customer" msgid "Customer"
msgstr "Kunde" msgstr "Kunde"
@@ -815,171 +985,6 @@ msgstr "Lizenz"
msgid "No domain specified" msgid "No domain specified"
msgstr "Keine Domain angegeben" msgstr "Keine Domain angegeben"
#: src/Admin/SettingsController.php:54
msgid "Licensed Products"
msgstr "Lizensierte Produkte"
#: src/Admin/SettingsController.php:64 src/Admin/SettingsController.php:128
msgid "Plugin License"
msgstr "Plugin-Lizenz"
#: src/Admin/SettingsController.php:65
msgid "Default Settings"
msgstr "Standardeinstellungen"
#: src/Admin/SettingsController.php:66
msgid "Notifications"
msgstr "Benachrichtigungen"
#: src/Admin/SettingsController.php:130
msgid ""
"Configure the license for this plugin. A valid license is required for "
"frontend features to work."
msgstr ""
"Konfigurieren Sie die Lizenz für dieses Plugin. Eine gültige Lizenz ist "
"erforderlich, damit die Frontend-Funktionen funktionieren."
#: src/Admin/SettingsController.php:134
msgid "License Server URL"
msgstr "Lizenzserver-URL"
#: src/Admin/SettingsController.php:136
msgid "The URL of the license server (e.g., https://shop.example.com)."
msgstr "Die URL des Lizenzservers (z.B. https://shop.example.com)."
#: src/Admin/SettingsController.php:144
msgid "Your license key in XXXX-XXXX-XXXX-XXXX format."
msgstr "Ihr Lizenzschlüssel im Format XXXX-XXXX-XXXX-XXXX."
#: src/Admin/SettingsController.php:150
msgid "Server Secret (Optional)"
msgstr "Server-Secret (Optional)"
#: src/Admin/SettingsController.php:152
msgid ""
"If the license server uses signed responses, enter the shared secret here "
"for enhanced security."
msgstr ""
"Falls der Lizenzserver signierte Antworten verwendet, geben Sie hier das "
"gemeinsame Secret für erhöhte Sicherheit ein."
#: src/Admin/SettingsController.php:170
msgid "Default License Settings"
msgstr "Standard Lizenz-Einstellungen"
#: src/Admin/SettingsController.php:172
msgid ""
"These settings serve as defaults for new licensed products. Individual "
"product settings override these defaults."
msgstr ""
"Diese Einstellungen dienen als Standard für neue lizensierte Produkte. "
"Individuelle Produkteinstellungen überschreiben diese Standards."
#: src/Admin/SettingsController.php:176
msgid "Default Max Activations"
msgstr "Standard Max. Aktivierungen"
#: src/Admin/SettingsController.php:178
msgid "Default maximum number of domain activations per license."
msgstr "Standard maximale Anzahl der Domain-Aktivierungen pro Lizenz."
#: src/Admin/SettingsController.php:187
msgid "Default License Validity (Days)"
msgstr "Standard Lizenz-Gültigkeit (Tage)"
#: src/Admin/SettingsController.php:189
msgid ""
"Default number of days a license is valid. Leave empty or set to 0 for "
"lifetime licenses."
msgstr ""
"Standard Anzahl Tage, die eine Lizenz gültig ist. Leer lassen oder auf 0 "
"setzen für lebenslange Lizenzen."
#: src/Admin/SettingsController.php:199
msgid "Default Bind to Major Version"
msgstr "Standard An Hauptversion binden"
#: src/Admin/SettingsController.php:201
msgid ""
"If enabled, licenses are bound to the major version at purchase time by "
"default."
msgstr ""
"Falls aktiviert, werden Lizenzen standardmässig an die Hauptversion zum "
"Kaufzeitpunkt gebunden."
#: src/Admin/SettingsController.php:219
msgid "Expiration Warning Schedule"
msgstr "Ablaufwarnung Zeitplan"
#: src/Admin/SettingsController.php:223
#, php-format
msgid ""
"Configure when expiration warning emails are sent. To customize the email "
"template, enable/disable, or change the subject, go to %s."
msgstr ""
"Konfigurieren Sie, wann Ablaufwarnungs-E-Mails gesendet werden. Um die E-"
"Mail-Vorlage anzupassen, zu aktivieren/deaktivieren oder den Betreff zu "
"ändern, gehen Sie zu %s."
#: src/Admin/SettingsController.php:225
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
msgstr "WooCommerce > Einstellungen > E-Mails > Lizenzablauf-Warnung"
#: src/Admin/SettingsController.php:230
msgid "First Warning (Days Before)"
msgstr "Erste Warnung (Tage vorher)"
#: src/Admin/SettingsController.php:232
msgid "Days before expiration to send the first warning email."
msgstr "Tage vor Ablauf, um die erste Warn-E-Mail zu senden."
#: src/Admin/SettingsController.php:241
msgid "Second Warning (Days Before)"
msgstr "Zweite Warnung (Tage vorher)"
#: src/Admin/SettingsController.php:243
msgid ""
"Days before expiration to send the second warning email. Set to 0 to disable."
msgstr ""
"Tage vor Ablauf, um die zweite Warn-E-Mail zu senden. Setzen Sie auf 0, um "
"sie zu deaktivieren."
#: src/Admin/SettingsController.php:283
msgid "Running on localhost - license validation bypassed."
msgstr "Läuft auf localhost - Lizenzvalidierung übersprungen."
#: src/Admin/SettingsController.php:291
msgid "License is valid and active."
msgstr "Lizenz ist gültig und aktiv."
#: src/Admin/SettingsController.php:297
msgid "License is not valid. Frontend features are disabled."
msgstr "Lizenz ist ungültig. Frontend-Funktionen sind deaktiviert."
#: src/Admin/SettingsController.php:308 src/Admin/SettingsController.php:344
msgid "Verify License"
msgstr "Lizenz überprüfen"
#: src/Admin/SettingsController.php:322
msgid "Verifying..."
msgstr "Überprüfe..."
#: src/Admin/SettingsController.php:341
msgid "Request failed."
msgstr "Anfrage fehlgeschlagen."
#: src/Admin/SettingsController.php:458
msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen."
#: src/Admin/SettingsController.php:467
msgid "License verified successfully!"
msgstr "Lizenz erfolgreich überprüft!"
#: src/Admin/SettingsController.php:469
msgid "License validation failed."
msgstr "Lizenzvalidierung fehlgeschlagen."
#: src/Admin/VersionAdminController.php:58 #: src/Admin/VersionAdminController.php:58
msgid "Product Versions" msgid "Product Versions"
msgstr "Produktversionen" msgstr "Produktversionen"
@@ -1150,6 +1155,56 @@ msgstr "Version konnte nicht aktualisiert werden."
msgid "Version updated successfully." msgid "Version updated successfully."
msgstr "Version erfolgreich aktualisiert." msgstr "Version erfolgreich aktualisiert."
#: src/Admin/DashboardWidgetController.php:47
msgid "License Statistics"
msgstr "Lizenzstatistiken"
#: src/Admin/DashboardWidgetController.php:155
msgid "Expiring Soon"
msgstr "Bald ablaufend"
#: src/Admin/DashboardWidgetController.php:166
msgid "Status Breakdown"
msgstr "Statusübersicht"
#: src/Admin/DashboardWidgetController.php:172
#, php-format
msgid "Active: %d"
msgstr "Aktiv: %d"
#: src/Admin/DashboardWidgetController.php:179
#, php-format
msgid "Inactive: %d"
msgstr "Inaktiv: %d"
#: src/Admin/DashboardWidgetController.php:186
#, php-format
msgid "Expired: %d"
msgstr "Abgelaufen: %d"
#: src/Admin/DashboardWidgetController.php:193
#, php-format
msgid "Revoked: %d"
msgstr "Widerrufen: %d"
#: src/Admin/DashboardWidgetController.php:202
msgid "License Types"
msgstr "Lizenztypen"
#: src/Admin/DashboardWidgetController.php:207
#, php-format
msgid "Time-limited: %d"
msgstr "Zeitlich begrenzt: %d"
#: src/Admin/DashboardWidgetController.php:213
#, php-format
msgid "Lifetime: %d"
msgstr "Lebenslang: %d"
#: src/Admin/DashboardWidgetController.php:220
msgid "View All Licenses"
msgstr "Alle Lizenzen anzeigen"
#: src/Api/RestApiController.php:84 #: src/Api/RestApiController.php:84
msgid "Too many requests. Please try again later." msgid "Too many requests. Please try again later."
msgstr "Zu viele Anfragen. Bitte versuchen Sie es später erneut." msgstr "Zu viele Anfragen. Bitte versuchen Sie es später erneut."
@@ -1241,6 +1296,7 @@ msgstr "Diese Lizenz ist für diese Domain nicht gültig."
#: src/License/LicenseManager.php:760 src/Frontend/AccountController.php:140 #: src/License/LicenseManager.php:760 src/Frontend/AccountController.php:140
#: src/Email/LicenseExpirationEmail.php:107 #: src/Email/LicenseExpirationEmail.php:107
#: src/Email/LicenseExpiredEmail.php:99
msgid "Unknown Product" msgid "Unknown Product"
msgstr "Unbekanntes Produkt" msgstr "Unbekanntes Produkt"
@@ -1252,67 +1308,6 @@ msgstr "Lizenzeinstellungen nicht konfiguriert."
msgid "Could not connect to license server." msgid "Could not connect to license server."
msgstr "Verbindung zum Lizenzserver konnte nicht hergestellt werden." msgstr "Verbindung zum Lizenzserver konnte nicht hergestellt werden."
#: src/Product/LicensedProductType.php:55
msgid "Licensed Product"
msgstr "Lizensiertes Produkt"
#: src/Product/LicensedProductType.php:76
msgid "License Settings"
msgstr "Lizenz-Einstellungen"
#: src/Product/LicensedProductType.php:103
#, php-format
msgid "%d days"
msgstr "%d Tage"
#: src/Product/LicensedProductType.php:113
#, 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:115
msgid "WooCommerce > Settings > Licensed Products"
msgstr "WooCommerce > Einstellungen > Lizensierte Produkte"
#: src/Product/LicensedProductType.php:122
msgid "Max Activations"
msgstr "Max. Aktivierungen"
#: src/Product/LicensedProductType.php:125
#, 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:140
msgid "License Validity (Days)"
msgstr "Lizenz-Gültigkeit (Tage)"
#: src/Product/LicensedProductType.php:143
#, 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:158
msgid "Bind to Major Version"
msgstr "An Hauptversion binden"
#: src/Product/LicensedProductType.php:161
#, php-format
msgid ""
"If enabled, licenses are bound to the major version at purchase time. "
"Default: %s"
msgstr ""
"Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt "
"gebunden. Standard: %s"
#: src/Product/LicensedProductType.php:162
msgid "Yes"
msgstr "Ja"
#: src/Product/LicensedProductType.php:162
msgid "No"
msgstr "Nein"
#: src/Product/VersionManager.php:166 #: src/Product/VersionManager.php:166
msgid "Attachment file not found." msgid "Attachment file not found."
msgstr "Anhangs-Datei nicht gefunden." msgstr "Anhangs-Datei nicht gefunden."
@@ -1322,6 +1317,71 @@ msgstr "Anhangs-Datei nicht gefunden."
msgid "File checksum does not match. Expected: %1$s, Got: %2$s" 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" msgstr "Datei-Prüfsumme stimmt nicht überein. Erwartet: %1$s, Erhalten: %2$s"
#: 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
#, php-format
msgid "%d days"
msgstr "%d Tage"
#: 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"
msgstr ""
"Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt "
"gebunden. Standard: %s"
#: src/Product/LicensedProductType.php:168
msgid "Yes"
msgstr "Ja"
#: src/Product/LicensedProductType.php:168
msgid "No"
msgstr "Nein"
#: src/Product/LicensedProductType.php:288
msgid "Version:"
msgstr "Version:"
#: src/Frontend/DownloadController.php:65 #: src/Frontend/DownloadController.php:65
#: src/Frontend/DownloadController.php:89 #: src/Frontend/DownloadController.php:89
msgid "Invalid download link." msgid "Invalid download link."
@@ -1382,30 +1442,32 @@ msgid "You have no licenses yet."
msgstr "Sie haben noch keine Lizenzen." msgstr "Sie haben noch keine Lizenzen."
#: src/Frontend/AccountController.php:190 #: src/Frontend/AccountController.php:190
#: src/Email/LicenseEmailController.php:173 #: src/Email/LicenseEmailController.php:212
#: src/Email/LicenseEmailController.php:177 #: src/Email/LicenseEmailController.php:216
#: src/Email/LicenseEmailController.php:281 #: src/Email/LicenseEmailController.php:320
#: src/Email/LicenseExpirationEmail.php:207 #: src/Email/LicenseExpirationEmail.php:207
#: src/Email/LicenseExpirationEmail.php:270 #: src/Email/LicenseExpirationEmail.php:270
#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256
msgid "License Key:" msgid "License Key:"
msgstr "Lizenzschlüssel:" msgstr "Lizenzschlüssel:"
#: src/Frontend/AccountController.php:201 #: src/Frontend/AccountController.php:201
#: src/Email/LicenseExpirationEmail.php:215 #: src/Email/LicenseExpirationEmail.php:215
#: src/Email/LicenseExpirationEmail.php:271 #: src/Email/LicenseExpirationEmail.php:271
#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257
msgid "Domain:" msgid "Domain:"
msgstr "Domain:" msgstr "Domain:"
#: src/Frontend/AccountController.php:213 #: src/Frontend/AccountController.php:213
#: src/Email/LicenseEmailController.php:284 #: src/Email/LicenseEmailController.php:323
#: src/Email/LicenseExpirationEmail.php:219 #: src/Email/LicenseExpirationEmail.php:219
#: src/Email/LicenseExpirationEmail.php:272 #: src/Email/LicenseExpirationEmail.php:272
msgid "Expires:" msgid "Expires:"
msgstr "Läuft ab:" msgstr "Läuft ab:"
#: src/Frontend/AccountController.php:218 #: src/Frontend/AccountController.php:218
#: src/Email/LicenseEmailController.php:248
#: src/Email/LicenseEmailController.php:287 #: src/Email/LicenseEmailController.php:287
#: src/Email/LicenseEmailController.php:326
msgid "Never" msgid "Never"
msgstr "Nie" msgstr "Nie"
@@ -1463,22 +1525,22 @@ msgstr "Die neue Domain ist dieselbe wie die aktuelle Domain."
msgid "Failed to transfer license. Please try again." msgid "Failed to transfer license. Please try again."
msgstr "Lizenzübertragung fehlgeschlagen. Bitte versuchen Sie es erneut." msgstr "Lizenzübertragung fehlgeschlagen. Bitte versuchen Sie es erneut."
#: src/Email/LicenseEmailController.php:217 #: src/Email/LicenseEmailController.php:256
msgid "Your License Keys" msgid "Your License Keys"
msgstr "Ihre Lizenzschlüssel" msgstr "Ihre Lizenzschlüssel"
#: src/Email/LicenseEmailController.php:221 #: src/Email/LicenseEmailController.php:260
#: src/Email/LicenseEmailController.php:276 #: src/Email/LicenseEmailController.php:315
msgid "Licensed Domain:" msgid "Licensed Domain:"
msgstr "Lizensierte Domain:" msgstr "Lizensierte Domain:"
#: src/Email/LicenseEmailController.php:257 #: src/Email/LicenseEmailController.php:296
#: src/Email/LicenseEmailController.php:291 #: src/Email/LicenseEmailController.php:330
msgid "You can also view your licenses in your account under \"Licenses\"." msgid "You can also view your licenses in your account under \"Licenses\"."
msgstr "" msgstr ""
"Sie können Ihre Lizenzen auch in Ihrem Konto unter \"Lizenzen\" einsehen." "Sie können Ihre Lizenzen auch in Ihrem Konto unter \"Lizenzen\" einsehen."
#: src/Email/LicenseEmailController.php:272 #: src/Email/LicenseEmailController.php:311
msgid "YOUR LICENSE KEYS" msgid "YOUR LICENSE KEYS"
msgstr "IHRE LIZENZSCHLÜSSEL" msgstr "IHRE LIZENZSCHLÜSSEL"
@@ -1508,6 +1570,7 @@ msgstr "Lizenzablauf-Benachrichtigung"
#: src/Email/LicenseExpirationEmail.php:176 #: src/Email/LicenseExpirationEmail.php:176
#: src/Email/LicenseExpirationEmail.php:246 #: src/Email/LicenseExpirationEmail.php:246
#: src/Email/LicenseExpiredEmail.php:167 src/Email/LicenseExpiredEmail.php:238
#, php-format #, php-format
msgid "Hello %s," msgid "Hello %s,"
msgstr "Guten Tag %s," msgstr "Guten Tag %s,"
@@ -1531,11 +1594,13 @@ msgstr "Lizenzdetails"
#: src/Email/LicenseExpirationEmail.php:203 #: src/Email/LicenseExpirationEmail.php:203
#: src/Email/LicenseExpirationEmail.php:269 #: src/Email/LicenseExpirationEmail.php:269
#: src/Email/LicenseExpiredEmail.php:187 src/Email/LicenseExpiredEmail.php:255
msgid "Product:" msgid "Product:"
msgstr "Produkt:" msgstr "Produkt:"
#: src/Email/LicenseExpirationEmail.php:235 #: src/Email/LicenseExpirationEmail.php:235
#: src/Email/LicenseExpirationEmail.php:281 #: src/Email/LicenseExpirationEmail.php:281
#: src/Email/LicenseExpiredEmail.php:227 src/Email/LicenseExpiredEmail.php:268
msgid "View My Licenses" msgid "View My Licenses"
msgstr "Meine Lizenzen anzeigen" msgstr "Meine Lizenzen anzeigen"
@@ -1548,54 +1613,108 @@ msgstr ""
"dem Ablaufdatum." "dem Ablaufdatum."
#: src/Email/LicenseExpirationEmail.php:301 #: src/Email/LicenseExpirationEmail.php:301
#: src/Email/LicenseExpiredEmail.php:288
#, php-format #, php-format
msgid "Available placeholders: %s" msgid "Available placeholders: %s"
msgstr "Verfügbare Platzhalter: %s" msgstr "Verfügbare Platzhalter: %s"
#: src/Email/LicenseExpirationEmail.php:307 #: src/Email/LicenseExpirationEmail.php:307
#: src/Email/LicenseExpiredEmail.php:294
msgid "Enable/Disable" msgid "Enable/Disable"
msgstr "Aktivieren/Deaktivieren" msgstr "Aktivieren/Deaktivieren"
#: src/Email/LicenseExpirationEmail.php:309 #: src/Email/LicenseExpirationEmail.php:309
#: src/Email/LicenseExpiredEmail.php:296
msgid "Enable this email notification" msgid "Enable this email notification"
msgstr "Diese E-Mail-Benachrichtigung aktivieren" msgstr "Diese E-Mail-Benachrichtigung aktivieren"
#: src/Email/LicenseExpirationEmail.php:313 #: src/Email/LicenseExpirationEmail.php:313
#: src/Email/LicenseExpiredEmail.php:300
msgid "Subject" msgid "Subject"
msgstr "Betreff" msgstr "Betreff"
#: src/Email/LicenseExpirationEmail.php:321 #: src/Email/LicenseExpirationEmail.php:321
#: src/Email/LicenseExpiredEmail.php:308
msgid "Email heading" msgid "Email heading"
msgstr "E-Mail-Überschrift" msgstr "E-Mail-Überschrift"
#: src/Email/LicenseExpirationEmail.php:329 #: src/Email/LicenseExpirationEmail.php:329
#: src/Email/LicenseExpiredEmail.php:316
msgid "Additional content" msgid "Additional content"
msgstr "Zusätzlicher Inhalt" msgstr "Zusätzlicher Inhalt"
#: src/Email/LicenseExpirationEmail.php:330 #: src/Email/LicenseExpirationEmail.php:330
#: src/Email/LicenseExpiredEmail.php:317
msgid "Text to appear below the main email content." msgid "Text to appear below the main email content."
msgstr "Text, der unter dem Haupt-E-Mail-Inhalt erscheinen soll." msgstr "Text, der unter dem Haupt-E-Mail-Inhalt erscheinen soll."
#: src/Email/LicenseExpirationEmail.php:338 #: src/Email/LicenseExpirationEmail.php:338
#: src/Email/LicenseExpiredEmail.php:325
msgid "Email type" msgid "Email type"
msgstr "E-Mail-Typ" msgstr "E-Mail-Typ"
#: src/Email/LicenseExpirationEmail.php:340 #: src/Email/LicenseExpirationEmail.php:340
#: src/Email/LicenseExpiredEmail.php:327
msgid "Choose which format of email to send." msgid "Choose which format of email to send."
msgstr "Wählen Sie, welches E-Mail-Format gesendet werden soll." msgstr "Wählen Sie, welches E-Mail-Format gesendet werden soll."
#: src/Plugin.php:255 #: src/Email/LicenseExpiredEmail.php:50 src/Email/LicenseExpiredEmail.php:76
msgid "License Expired"
msgstr "Lizenz abgelaufen"
#: src/Email/LicenseExpiredEmail.php:51
msgid ""
"License expired emails are sent to customers when their licenses have "
"expired."
msgstr ""
"Lizenzablauf-E-Mails werden an Kunden gesendet, wenn ihre Lizenzen "
"abgelaufen sind."
#: src/Email/LicenseExpiredEmail.php:68
msgid "[{site_title}] Your license for {product_name} has expired"
msgstr "[{site_title}] Ihre Lizenz für {product_name} ist abgelaufen"
#: src/Email/LicenseExpiredEmail.php:171 src/Email/LicenseExpiredEmail.php:242
#, php-format
msgid "Your license for %1$s has expired on %2$s."
msgstr "Ihre Lizenz für %1$s ist am %2$s abgelaufen."
#: src/Email/LicenseExpiredEmail.php:178 src/Email/LicenseExpiredEmail.php:248
msgid ""
"Your license is no longer valid and the product will stop working until you "
"renew."
msgstr ""
"Ihre Lizenz ist nicht mehr gültig und das Produkt wird nicht mehr "
"funktionieren, bis Sie verlängern."
#: src/Email/LicenseExpiredEmail.php:181 src/Email/LicenseExpiredEmail.php:252
msgid "Expired License Details"
msgstr "Details der abgelaufenen Lizenz"
#: src/Email/LicenseExpiredEmail.php:203 src/Email/LicenseExpiredEmail.php:258
msgid "Expired on:"
msgstr "Abgelaufen am:"
#: src/Email/LicenseExpiredEmail.php:207 src/Email/LicenseExpiredEmail.php:259
msgid "Status:"
msgstr "Status:"
#: src/Email/LicenseExpiredEmail.php:278
msgid "To continue using this product, please renew your license."
msgstr "Um dieses Produkt weiterhin zu nutzen, verlängern Sie bitte Ihre Lizenz."
#: src/Plugin.php:257
msgid "WC Licensed Product" msgid "WC Licensed Product"
msgstr "WC Licensed Product" msgstr "WC Licensed Product"
#: src/Plugin.php:256 #: src/Plugin.php:258
msgid "" msgid ""
"Plugin license is not configured or invalid. Frontend features are disabled." "Plugin license is not configured or invalid. Frontend features are disabled."
msgstr "" msgstr ""
"Plugin-Lizenz ist nicht konfiguriert oder ungültig. Frontend-Funktionen sind " "Plugin-Lizenz ist nicht konfiguriert oder ungültig. Frontend-Funktionen sind "
"deaktiviert." "deaktiviert."
#: src/Plugin.php:257 #: src/Plugin.php:259
msgid "Configure License" msgid "Configure License"
msgstr "Lizenz konfigurieren" msgstr "Lizenz konfigurieren"
@@ -1659,15 +1778,6 @@ msgstr ""
#~ msgid "of" #~ msgid "of"
#~ msgstr "von" #~ msgstr "von"
#~ msgid "Expiring Soon (30 days)"
#~ msgstr "Bald ablaufend (30 Tage)"
#~ msgid "License Types"
#~ msgstr "Lizenztypen"
#~ msgid "Time-limited Licenses"
#~ msgstr "Zeitlich begrenzte Lizenzen"
#~ msgid "Lifetime Licenses" #~ msgid "Lifetime Licenses"
#~ msgstr "Lebenslange Lizenzen" #~ msgstr "Lebenslange Lizenzen"

View File

@@ -1,14 +1,14 @@
# SOME DESCRIPTIVE TITLE. # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Marco Graetsch # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the WooCommerce Licensed Product package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: WooCommerce Licensed Product 0.3.3\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: magdev3.0@gmail.com\n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-23 11:41+0100\n" "POT-Creation-Date: 2026-01-23 16:05+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -75,16 +75,16 @@ msgstr ""
msgid "Licenses will be generated when the order is marked as paid/completed." msgid "Licenses will be generated when the order is marked as paid/completed."
msgstr "" msgstr ""
#: src/Admin/OrderLicenseController.php:144 src/Admin/AdminController.php:1280 #: src/Admin/OrderLicenseController.php:144
#: src/Admin/SettingsController.php:142 src/Admin/AdminController.php:1280
#: src/Admin/AdminController.php:1431 src/Admin/AdminController.php:1480 #: src/Admin/AdminController.php:1431 src/Admin/AdminController.php:1480
#: src/Admin/SettingsController.php:142 #: src/Email/LicenseEmailController.php:269
#: src/Email/LicenseEmailController.php:230
msgid "License Key" msgid "License Key"
msgstr "" msgstr ""
#: src/Admin/OrderLicenseController.php:145 src/Admin/AdminController.php:1281 #: src/Admin/OrderLicenseController.php:145 src/Admin/AdminController.php:1281
#: src/Admin/AdminController.php:1432 src/Admin/AdminController.php:1597 #: src/Admin/AdminController.php:1432 src/Admin/AdminController.php:1597
#: src/Email/LicenseEmailController.php:229 #: src/Email/LicenseEmailController.php:268
msgid "Product" msgid "Product"
msgstr "" msgstr ""
@@ -100,7 +100,7 @@ msgstr ""
#: src/Admin/OrderLicenseController.php:148 src/Admin/AdminController.php:1286 #: src/Admin/OrderLicenseController.php:148 src/Admin/AdminController.php:1286
#: src/Admin/AdminController.php:1437 src/Admin/AdminController.php:1600 #: src/Admin/AdminController.php:1437 src/Admin/AdminController.php:1600
#: src/Admin/AdminController.php:1602 src/Email/LicenseEmailController.php:231 #: src/Admin/AdminController.php:1602 src/Email/LicenseEmailController.php:270
msgid "Expires" msgid "Expires"
msgstr "" msgstr ""
@@ -125,11 +125,11 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: src/Admin/OrderLicenseController.php:201 src/Admin/AdminController.php:151 #: src/Admin/OrderLicenseController.php:201
#: src/Admin/SettingsController.php:192 src/Admin/AdminController.php:151
#: src/Admin/AdminController.php:266 src/Admin/AdminController.php:1362 #: src/Admin/AdminController.php:266 src/Admin/AdminController.php:1362
#: src/Admin/AdminController.php:1602 src/Admin/SettingsController.php:192 #: src/Admin/AdminController.php:1602 src/Product/LicensedProductType.php:110
#: src/Product/LicensedProductType.php:104 #: src/Product/LicensedProductType.php:158
#: src/Product/LicensedProductType.php:152
msgid "Lifetime" msgid "Lifetime"
msgstr "" msgstr ""
@@ -207,6 +207,164 @@ msgstr ""
msgid "Failed to update license domain." msgid "Failed to update license domain."
msgstr "" msgstr ""
#: src/Admin/SettingsController.php:54
msgid "Licensed Products"
msgstr ""
#: src/Admin/SettingsController.php:64 src/Admin/SettingsController.php:128
msgid "Plugin License"
msgstr ""
#: src/Admin/SettingsController.php:65
msgid "Default Settings"
msgstr ""
#: src/Admin/SettingsController.php:66
msgid "Notifications"
msgstr ""
#: src/Admin/SettingsController.php:130
msgid ""
"Configure the license for this plugin. A valid license is required for "
"frontend features to work."
msgstr ""
#: src/Admin/SettingsController.php:134
msgid "License Server URL"
msgstr ""
#: src/Admin/SettingsController.php:136
msgid "The URL of the license server (e.g., https://shop.example.com)."
msgstr ""
#: src/Admin/SettingsController.php:144
msgid "Your license key in XXXX-XXXX-XXXX-XXXX format."
msgstr ""
#: src/Admin/SettingsController.php:150
msgid "Server Secret (Optional)"
msgstr ""
#: src/Admin/SettingsController.php:152
msgid ""
"If the license server uses signed responses, enter the shared secret here "
"for enhanced security."
msgstr ""
#: src/Admin/SettingsController.php:170
msgid "Default License Settings"
msgstr ""
#: src/Admin/SettingsController.php:172
msgid ""
"These settings serve as defaults for new licensed products. Individual "
"product settings override these defaults."
msgstr ""
#: src/Admin/SettingsController.php:176
msgid "Default Max Activations"
msgstr ""
#: src/Admin/SettingsController.php:178
msgid "Default maximum number of domain activations per license."
msgstr ""
#: src/Admin/SettingsController.php:187
msgid "Default License Validity (Days)"
msgstr ""
#: src/Admin/SettingsController.php:189
msgid ""
"Default number of days a license is valid. Leave empty or set to 0 for "
"lifetime licenses."
msgstr ""
#: src/Admin/SettingsController.php:199
msgid "Default Bind to Major Version"
msgstr ""
#: src/Admin/SettingsController.php:201
msgid ""
"If enabled, licenses are bound to the major version at purchase time by "
"default."
msgstr ""
#: src/Admin/SettingsController.php:219
msgid "Expiration Warning Schedule"
msgstr ""
#: src/Admin/SettingsController.php:223
#, php-format
msgid ""
"Configure when expiration warning emails are sent. To customize the email "
"template, enable/disable, or change the subject, go to %s."
msgstr ""
#: src/Admin/SettingsController.php:225
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
msgstr ""
#: src/Admin/SettingsController.php:230
msgid "First Warning (Days Before)"
msgstr ""
#: src/Admin/SettingsController.php:232
msgid "Days before expiration to send the first warning email."
msgstr ""
#: src/Admin/SettingsController.php:241
msgid "Second Warning (Days Before)"
msgstr ""
#: src/Admin/SettingsController.php:243
msgid ""
"Days before expiration to send the second warning email. Set to 0 to disable."
msgstr ""
#: src/Admin/SettingsController.php:283
msgid "Running on localhost - license validation bypassed."
msgstr ""
#: src/Admin/SettingsController.php:291
msgid "License is valid and active."
msgstr ""
#: src/Admin/SettingsController.php:297
msgid "License is not valid. Frontend features are disabled."
msgstr ""
#: src/Admin/SettingsController.php:308 src/Admin/SettingsController.php:344
msgid "Verify License"
msgstr ""
#: src/Admin/SettingsController.php:322
msgid "Verifying..."
msgstr ""
#: src/Admin/SettingsController.php:341
msgid "Request failed."
msgstr ""
#: src/Admin/SettingsController.php:454 src/Admin/AdminController.php:455
#: src/Admin/AdminController.php:475 src/Admin/AdminController.php:493
#: src/Admin/AdminController.php:511 src/Admin/AdminController.php:531
#: src/Admin/AdminController.php:549 src/Admin/AdminController.php:616
#: src/Admin/AdminController.php:806 src/Frontend/AccountController.php:326
msgid "Security check failed."
msgstr ""
#: src/Admin/SettingsController.php:458
msgid "Insufficient permissions."
msgstr ""
#: src/Admin/SettingsController.php:467
msgid "License verified successfully!"
msgstr ""
#: src/Admin/SettingsController.php:469
msgid "License validation failed."
msgstr ""
#: src/Admin/AdminController.php:93 #: src/Admin/AdminController.php:93
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
@@ -253,6 +411,7 @@ msgstr ""
#: src/Admin/AdminController.php:1221 src/Admin/AdminController.php:1344 #: src/Admin/AdminController.php:1221 src/Admin/AdminController.php:1344
#: src/Admin/VersionAdminController.php:182 #: src/Admin/VersionAdminController.php:182
#: src/Admin/VersionAdminController.php:413 #: src/Admin/VersionAdminController.php:413
#: src/Admin/DashboardWidgetController.php:151
msgid "Active" msgid "Active"
msgstr "" msgstr ""
@@ -265,6 +424,8 @@ msgstr ""
#: src/Admin/AdminController.php:158 src/Admin/AdminController.php:916 #: src/Admin/AdminController.php:158 src/Admin/AdminController.php:916
#: src/Admin/AdminController.php:1223 src/Admin/AdminController.php:1346 #: src/Admin/AdminController.php:1223 src/Admin/AdminController.php:1346
#: src/Admin/DashboardWidgetController.php:159
#: src/Email/LicenseExpiredEmail.php:210 src/Email/LicenseExpiredEmail.php:259
msgid "Expired" msgid "Expired"
msgstr "" msgstr ""
@@ -325,14 +486,6 @@ msgstr ""
msgid "License key and domain are required." msgid "License key and domain are required."
msgstr "" msgstr ""
#: src/Admin/AdminController.php:455 src/Admin/AdminController.php:475
#: src/Admin/AdminController.php:493 src/Admin/AdminController.php:511
#: src/Admin/AdminController.php:531 src/Admin/AdminController.php:549
#: src/Admin/AdminController.php:616 src/Admin/AdminController.php:806
#: src/Admin/SettingsController.php:454 src/Frontend/AccountController.php:326
msgid "Security check failed."
msgstr ""
#: src/Admin/AdminController.php:576 #: src/Admin/AdminController.php:576
msgid "You do not have permission to export licenses." msgid "You do not have permission to export licenses."
msgstr "" msgstr ""
@@ -359,6 +512,7 @@ msgid "License Dashboard"
msgstr "" msgstr ""
#: src/Admin/AdminController.php:895 #: src/Admin/AdminController.php:895
#: src/Admin/DashboardWidgetController.php:147
msgid "Total Licenses" msgid "Total Licenses"
msgstr "" msgstr ""
@@ -593,6 +747,7 @@ msgstr ""
#: src/Admin/AdminController.php:1282 src/Admin/AdminController.php:1433 #: src/Admin/AdminController.php:1282 src/Admin/AdminController.php:1433
#: src/Email/LicenseExpirationEmail.php:104 #: src/Email/LicenseExpirationEmail.php:104
#: src/Email/LicenseExpiredEmail.php:96
msgid "Customer" msgid "Customer"
msgstr "" msgstr ""
@@ -800,156 +955,6 @@ msgstr ""
msgid "No domain specified" msgid "No domain specified"
msgstr "" msgstr ""
#: src/Admin/SettingsController.php:54
msgid "Licensed Products"
msgstr ""
#: src/Admin/SettingsController.php:64 src/Admin/SettingsController.php:128
msgid "Plugin License"
msgstr ""
#: src/Admin/SettingsController.php:65
msgid "Default Settings"
msgstr ""
#: src/Admin/SettingsController.php:66
msgid "Notifications"
msgstr ""
#: src/Admin/SettingsController.php:130
msgid ""
"Configure the license for this plugin. A valid license is required for "
"frontend features to work."
msgstr ""
#: src/Admin/SettingsController.php:134
msgid "License Server URL"
msgstr ""
#: src/Admin/SettingsController.php:136
msgid "The URL of the license server (e.g., https://shop.example.com)."
msgstr ""
#: src/Admin/SettingsController.php:144
msgid "Your license key in XXXX-XXXX-XXXX-XXXX format."
msgstr ""
#: src/Admin/SettingsController.php:150
msgid "Server Secret (Optional)"
msgstr ""
#: src/Admin/SettingsController.php:152
msgid ""
"If the license server uses signed responses, enter the shared secret here "
"for enhanced security."
msgstr ""
#: src/Admin/SettingsController.php:170
msgid "Default License Settings"
msgstr ""
#: src/Admin/SettingsController.php:172
msgid ""
"These settings serve as defaults for new licensed products. Individual "
"product settings override these defaults."
msgstr ""
#: src/Admin/SettingsController.php:176
msgid "Default Max Activations"
msgstr ""
#: src/Admin/SettingsController.php:178
msgid "Default maximum number of domain activations per license."
msgstr ""
#: src/Admin/SettingsController.php:187
msgid "Default License Validity (Days)"
msgstr ""
#: src/Admin/SettingsController.php:189
msgid ""
"Default number of days a license is valid. Leave empty or set to 0 for "
"lifetime licenses."
msgstr ""
#: src/Admin/SettingsController.php:199
msgid "Default Bind to Major Version"
msgstr ""
#: src/Admin/SettingsController.php:201
msgid ""
"If enabled, licenses are bound to the major version at purchase time by "
"default."
msgstr ""
#: src/Admin/SettingsController.php:219
msgid "Expiration Warning Schedule"
msgstr ""
#: src/Admin/SettingsController.php:223
#, php-format
msgid ""
"Configure when expiration warning emails are sent. To customize the email "
"template, enable/disable, or change the subject, go to %s."
msgstr ""
#: src/Admin/SettingsController.php:225
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
msgstr ""
#: src/Admin/SettingsController.php:230
msgid "First Warning (Days Before)"
msgstr ""
#: src/Admin/SettingsController.php:232
msgid "Days before expiration to send the first warning email."
msgstr ""
#: src/Admin/SettingsController.php:241
msgid "Second Warning (Days Before)"
msgstr ""
#: src/Admin/SettingsController.php:243
msgid ""
"Days before expiration to send the second warning email. Set to 0 to disable."
msgstr ""
#: src/Admin/SettingsController.php:283
msgid "Running on localhost - license validation bypassed."
msgstr ""
#: src/Admin/SettingsController.php:291
msgid "License is valid and active."
msgstr ""
#: src/Admin/SettingsController.php:297
msgid "License is not valid. Frontend features are disabled."
msgstr ""
#: src/Admin/SettingsController.php:308 src/Admin/SettingsController.php:344
msgid "Verify License"
msgstr ""
#: src/Admin/SettingsController.php:322
msgid "Verifying..."
msgstr ""
#: src/Admin/SettingsController.php:341
msgid "Request failed."
msgstr ""
#: src/Admin/SettingsController.php:458
msgid "Insufficient permissions."
msgstr ""
#: src/Admin/SettingsController.php:467
msgid "License verified successfully!"
msgstr ""
#: src/Admin/SettingsController.php:469
msgid "License validation failed."
msgstr ""
#: src/Admin/VersionAdminController.php:58 #: src/Admin/VersionAdminController.php:58
msgid "Product Versions" msgid "Product Versions"
msgstr "" msgstr ""
@@ -1112,6 +1117,56 @@ msgstr ""
msgid "Version updated successfully." msgid "Version updated successfully."
msgstr "" msgstr ""
#: src/Admin/DashboardWidgetController.php:47
msgid "License Statistics"
msgstr ""
#: src/Admin/DashboardWidgetController.php:155
msgid "Expiring Soon"
msgstr ""
#: src/Admin/DashboardWidgetController.php:166
msgid "Status Breakdown"
msgstr ""
#: src/Admin/DashboardWidgetController.php:172
#, php-format
msgid "Active: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:179
#, php-format
msgid "Inactive: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:186
#, php-format
msgid "Expired: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:193
#, php-format
msgid "Revoked: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:202
msgid "License Types"
msgstr ""
#: src/Admin/DashboardWidgetController.php:207
#, php-format
msgid "Time-limited: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:213
#, php-format
msgid "Lifetime: %d"
msgstr ""
#: src/Admin/DashboardWidgetController.php:220
msgid "View All Licenses"
msgstr ""
#: src/Api/RestApiController.php:84 #: src/Api/RestApiController.php:84
msgid "Too many requests. Please try again later." msgid "Too many requests. Please try again later."
msgstr "" msgstr ""
@@ -1201,6 +1256,7 @@ msgstr ""
#: src/License/LicenseManager.php:760 src/Frontend/AccountController.php:140 #: src/License/LicenseManager.php:760 src/Frontend/AccountController.php:140
#: src/Email/LicenseExpirationEmail.php:107 #: src/Email/LicenseExpirationEmail.php:107
#: src/Email/LicenseExpiredEmail.php:99
msgid "Unknown Product" msgid "Unknown Product"
msgstr "" msgstr ""
@@ -1212,65 +1268,6 @@ msgstr ""
msgid "Could not connect to license server." msgid "Could not connect to license server."
msgstr "" msgstr ""
#: src/Product/LicensedProductType.php:55
msgid "Licensed Product"
msgstr ""
#: src/Product/LicensedProductType.php:76
msgid "License Settings"
msgstr ""
#: src/Product/LicensedProductType.php:103
#, php-format
msgid "%d days"
msgstr ""
#: src/Product/LicensedProductType.php:113
#, php-format
msgid "Leave fields empty to use default settings from %s."
msgstr ""
#: src/Product/LicensedProductType.php:115
msgid "WooCommerce > Settings > Licensed Products"
msgstr ""
#: src/Product/LicensedProductType.php:122
msgid "Max Activations"
msgstr ""
#: src/Product/LicensedProductType.php:125
#, php-format
msgid "Maximum number of domain activations per license. Default: %d"
msgstr ""
#: src/Product/LicensedProductType.php:140
msgid "License Validity (Days)"
msgstr ""
#: src/Product/LicensedProductType.php:143
#, php-format
msgid "Number of days the license is valid. Leave empty for default (%s)."
msgstr ""
#: src/Product/LicensedProductType.php:158
msgid "Bind to Major Version"
msgstr ""
#: src/Product/LicensedProductType.php:161
#, php-format
msgid ""
"If enabled, licenses are bound to the major version at purchase time. "
"Default: %s"
msgstr ""
#: src/Product/LicensedProductType.php:162
msgid "Yes"
msgstr ""
#: src/Product/LicensedProductType.php:162
msgid "No"
msgstr ""
#: src/Product/VersionManager.php:166 #: src/Product/VersionManager.php:166
msgid "Attachment file not found." msgid "Attachment file not found."
msgstr "" msgstr ""
@@ -1280,6 +1277,69 @@ msgstr ""
msgid "File checksum does not match. Expected: %1$s, Got: %2$s" msgid "File checksum does not match. Expected: %1$s, Got: %2$s"
msgstr "" msgstr ""
#: src/Product/LicensedProductType.php:61
msgid "Licensed Product"
msgstr ""
#: src/Product/LicensedProductType.php:82
msgid "License Settings"
msgstr ""
#: src/Product/LicensedProductType.php:109
#, php-format
msgid "%d days"
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/Frontend/DownloadController.php:65 #: src/Frontend/DownloadController.php:65
#: src/Frontend/DownloadController.php:89 #: src/Frontend/DownloadController.php:89
msgid "Invalid download link." msgid "Invalid download link."
@@ -1340,30 +1400,32 @@ msgid "You have no licenses yet."
msgstr "" msgstr ""
#: src/Frontend/AccountController.php:190 #: src/Frontend/AccountController.php:190
#: src/Email/LicenseEmailController.php:173 #: src/Email/LicenseEmailController.php:212
#: src/Email/LicenseEmailController.php:177 #: src/Email/LicenseEmailController.php:216
#: src/Email/LicenseEmailController.php:281 #: src/Email/LicenseEmailController.php:320
#: src/Email/LicenseExpirationEmail.php:207 #: src/Email/LicenseExpirationEmail.php:207
#: src/Email/LicenseExpirationEmail.php:270 #: src/Email/LicenseExpirationEmail.php:270
#: src/Email/LicenseExpiredEmail.php:191 src/Email/LicenseExpiredEmail.php:256
msgid "License Key:" msgid "License Key:"
msgstr "" msgstr ""
#: src/Frontend/AccountController.php:201 #: src/Frontend/AccountController.php:201
#: src/Email/LicenseExpirationEmail.php:215 #: src/Email/LicenseExpirationEmail.php:215
#: src/Email/LicenseExpirationEmail.php:271 #: src/Email/LicenseExpirationEmail.php:271
#: src/Email/LicenseExpiredEmail.php:199 src/Email/LicenseExpiredEmail.php:257
msgid "Domain:" msgid "Domain:"
msgstr "" msgstr ""
#: src/Frontend/AccountController.php:213 #: src/Frontend/AccountController.php:213
#: src/Email/LicenseEmailController.php:284 #: src/Email/LicenseEmailController.php:323
#: src/Email/LicenseExpirationEmail.php:219 #: src/Email/LicenseExpirationEmail.php:219
#: src/Email/LicenseExpirationEmail.php:272 #: src/Email/LicenseExpirationEmail.php:272
msgid "Expires:" msgid "Expires:"
msgstr "" msgstr ""
#: src/Frontend/AccountController.php:218 #: src/Frontend/AccountController.php:218
#: src/Email/LicenseEmailController.php:248
#: src/Email/LicenseEmailController.php:287 #: src/Email/LicenseEmailController.php:287
#: src/Email/LicenseEmailController.php:326
msgid "Never" msgid "Never"
msgstr "" msgstr ""
@@ -1419,21 +1481,21 @@ msgstr ""
msgid "Failed to transfer license. Please try again." msgid "Failed to transfer license. Please try again."
msgstr "" msgstr ""
#: src/Email/LicenseEmailController.php:217 #: src/Email/LicenseEmailController.php:256
msgid "Your License Keys" msgid "Your License Keys"
msgstr "" msgstr ""
#: src/Email/LicenseEmailController.php:221 #: src/Email/LicenseEmailController.php:260
#: src/Email/LicenseEmailController.php:276 #: src/Email/LicenseEmailController.php:315
msgid "Licensed Domain:" msgid "Licensed Domain:"
msgstr "" msgstr ""
#: src/Email/LicenseEmailController.php:257 #: src/Email/LicenseEmailController.php:296
#: src/Email/LicenseEmailController.php:291 #: src/Email/LicenseEmailController.php:330
msgid "You can also view your licenses in your account under \"Licenses\"." msgid "You can also view your licenses in your account under \"Licenses\"."
msgstr "" msgstr ""
#: src/Email/LicenseEmailController.php:272 #: src/Email/LicenseEmailController.php:311
msgid "YOUR LICENSE KEYS" msgid "YOUR LICENSE KEYS"
msgstr "" msgstr ""
@@ -1459,6 +1521,7 @@ msgstr ""
#: src/Email/LicenseExpirationEmail.php:176 #: src/Email/LicenseExpirationEmail.php:176
#: src/Email/LicenseExpirationEmail.php:246 #: src/Email/LicenseExpirationEmail.php:246
#: src/Email/LicenseExpiredEmail.php:167 src/Email/LicenseExpiredEmail.php:238
#, php-format #, php-format
msgid "Hello %s," msgid "Hello %s,"
msgstr "" msgstr ""
@@ -1482,11 +1545,13 @@ msgstr ""
#: src/Email/LicenseExpirationEmail.php:203 #: src/Email/LicenseExpirationEmail.php:203
#: src/Email/LicenseExpirationEmail.php:269 #: src/Email/LicenseExpirationEmail.php:269
#: src/Email/LicenseExpiredEmail.php:187 src/Email/LicenseExpiredEmail.php:255
msgid "Product:" msgid "Product:"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:235 #: src/Email/LicenseExpirationEmail.php:235
#: src/Email/LicenseExpirationEmail.php:281 #: src/Email/LicenseExpirationEmail.php:281
#: src/Email/LicenseExpiredEmail.php:227 src/Email/LicenseExpiredEmail.php:268
msgid "View My Licenses" msgid "View My Licenses"
msgstr "" msgstr ""
@@ -1497,52 +1562,102 @@ msgid ""
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:301 #: src/Email/LicenseExpirationEmail.php:301
#: src/Email/LicenseExpiredEmail.php:288
#, php-format #, php-format
msgid "Available placeholders: %s" msgid "Available placeholders: %s"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:307 #: src/Email/LicenseExpirationEmail.php:307
#: src/Email/LicenseExpiredEmail.php:294
msgid "Enable/Disable" msgid "Enable/Disable"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:309 #: src/Email/LicenseExpirationEmail.php:309
#: src/Email/LicenseExpiredEmail.php:296
msgid "Enable this email notification" msgid "Enable this email notification"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:313 #: src/Email/LicenseExpirationEmail.php:313
#: src/Email/LicenseExpiredEmail.php:300
msgid "Subject" msgid "Subject"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:321 #: src/Email/LicenseExpirationEmail.php:321
#: src/Email/LicenseExpiredEmail.php:308
msgid "Email heading" msgid "Email heading"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:329 #: src/Email/LicenseExpirationEmail.php:329
#: src/Email/LicenseExpiredEmail.php:316
msgid "Additional content" msgid "Additional content"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:330 #: src/Email/LicenseExpirationEmail.php:330
#: src/Email/LicenseExpiredEmail.php:317
msgid "Text to appear below the main email content." msgid "Text to appear below the main email content."
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:338 #: src/Email/LicenseExpirationEmail.php:338
#: src/Email/LicenseExpiredEmail.php:325
msgid "Email type" msgid "Email type"
msgstr "" msgstr ""
#: src/Email/LicenseExpirationEmail.php:340 #: src/Email/LicenseExpirationEmail.php:340
#: src/Email/LicenseExpiredEmail.php:327
msgid "Choose which format of email to send." msgid "Choose which format of email to send."
msgstr "" msgstr ""
#: src/Plugin.php:255 #: src/Email/LicenseExpiredEmail.php:50 src/Email/LicenseExpiredEmail.php:76
msgid "License Expired"
msgstr ""
#: src/Email/LicenseExpiredEmail.php:51
msgid ""
"License expired emails are sent to customers when their licenses have "
"expired."
msgstr ""
#: src/Email/LicenseExpiredEmail.php:68
msgid "[{site_title}] Your license for {product_name} has expired"
msgstr ""
#: src/Email/LicenseExpiredEmail.php:171 src/Email/LicenseExpiredEmail.php:242
#, php-format
msgid "Your license for %1$s has expired on %2$s."
msgstr ""
#: src/Email/LicenseExpiredEmail.php:178 src/Email/LicenseExpiredEmail.php:248
msgid ""
"Your license is no longer valid and the product will stop working until you "
"renew."
msgstr ""
#: src/Email/LicenseExpiredEmail.php:181 src/Email/LicenseExpiredEmail.php:252
msgid "Expired License Details"
msgstr ""
#: src/Email/LicenseExpiredEmail.php:203 src/Email/LicenseExpiredEmail.php:258
msgid "Expired on:"
msgstr ""
#: src/Email/LicenseExpiredEmail.php:207 src/Email/LicenseExpiredEmail.php:259
msgid "Status:"
msgstr ""
#: src/Email/LicenseExpiredEmail.php:278
msgid "To continue using this product, please renew your license."
msgstr ""
#: src/Plugin.php:257
msgid "WC Licensed Product" msgid "WC Licensed Product"
msgstr "" msgstr ""
#: src/Plugin.php:256 #: src/Plugin.php:258
msgid "" msgid ""
"Plugin license is not configured or invalid. Frontend features are disabled." "Plugin license is not configured or invalid. Frontend features are disabled."
msgstr "" msgstr ""
#: src/Plugin.php:257 #: src/Plugin.php:259
msgid "Configure License" msgid "Configure License"
msgstr "" msgstr ""

View File

@@ -0,0 +1 @@
a06d29eabc2da08613ae13874ed152b8ea9363b8284a2e9bdda414e32777558c wc-licensed-product-0.3.3.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
36a81c00eb03adf5dfa633891664d44b7e5225bf1ee594904f8acc9adec6bb47 releases/wc-licensed-product-0.3.4.zip

Binary file not shown.

View File

@@ -0,0 +1,225 @@
<?php
/**
* Dashboard Widget Controller
*
* @package Jeremias\WcLicensedProduct\Admin
*/
declare(strict_types=1);
namespace Jeremias\WcLicensedProduct\Admin;
use Jeremias\WcLicensedProduct\License\License;
use Jeremias\WcLicensedProduct\License\LicenseManager;
/**
* Handles the WordPress admin dashboard widget for license statistics
*/
final class DashboardWidgetController
{
private LicenseManager $licenseManager;
public function __construct(LicenseManager $licenseManager)
{
$this->licenseManager = $licenseManager;
$this->registerHooks();
}
/**
* Register WordPress hooks
*/
private function registerHooks(): void
{
add_action('wp_dashboard_setup', [$this, 'registerDashboardWidget']);
}
/**
* Register the dashboard widget
*/
public function registerDashboardWidget(): void
{
if (!current_user_can('manage_woocommerce')) {
return;
}
wp_add_dashboard_widget(
'wclp_license_statistics',
__('License Statistics', 'wc-licensed-product'),
[$this, 'renderWidget']
);
}
/**
* Render the dashboard widget content
*/
public function renderWidget(): void
{
$stats = $this->licenseManager->getStatistics();
$licensesUrl = admin_url('admin.php?page=wc-licensed-product-licenses');
?>
<style>
.wclp-widget-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.wclp-stat-card {
background: #f8f9fa;
border: 1px solid #e2e4e7;
border-radius: 4px;
padding: 12px;
text-align: center;
}
.wclp-stat-card.highlight {
border-left: 3px solid #7f54b3;
}
.wclp-stat-card.warning {
border-left: 3px solid #f0b849;
}
.wclp-stat-card.danger {
border-left: 3px solid #dc3232;
}
.wclp-stat-card.success {
border-left: 3px solid #46b450;
}
.wclp-stat-number {
font-size: 28px;
font-weight: 600;
color: #1d2327;
line-height: 1.2;
}
.wclp-stat-label {
font-size: 12px;
color: #646970;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 4px;
}
.wclp-widget-divider {
border-top: 1px solid #e2e4e7;
margin: 16px 0;
}
.wclp-status-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.wclp-status-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.wclp-status-badge.active {
background: #d4edda;
color: #155724;
}
.wclp-status-badge.inactive {
background: #e2e3e5;
color: #383d41;
}
.wclp-status-badge.expired {
background: #f8d7da;
color: #721c24;
}
.wclp-status-badge.revoked {
background: #d6d8db;
color: #1b1e21;
}
.wclp-widget-footer {
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #e2e4e7;
text-align: center;
}
.wclp-widget-footer a {
text-decoration: none;
}
</style>
<div class="wclp-widget-stats">
<div class="wclp-stat-card highlight">
<div class="wclp-stat-number"><?php echo esc_html(number_format_i18n($stats['total'])); ?></div>
<div class="wclp-stat-label"><?php esc_html_e('Total Licenses', 'wc-licensed-product'); ?></div>
</div>
<div class="wclp-stat-card success">
<div class="wclp-stat-number"><?php echo esc_html(number_format_i18n($stats['by_status'][License::STATUS_ACTIVE])); ?></div>
<div class="wclp-stat-label"><?php esc_html_e('Active', 'wc-licensed-product'); ?></div>
</div>
<div class="wclp-stat-card warning">
<div class="wclp-stat-number"><?php echo esc_html(number_format_i18n($stats['expiring_soon'])); ?></div>
<div class="wclp-stat-label"><?php esc_html_e('Expiring Soon', 'wc-licensed-product'); ?></div>
</div>
<div class="wclp-stat-card danger">
<div class="wclp-stat-number"><?php echo esc_html(number_format_i18n($stats['by_status'][License::STATUS_EXPIRED])); ?></div>
<div class="wclp-stat-label"><?php esc_html_e('Expired', 'wc-licensed-product'); ?></div>
</div>
</div>
<div class="wclp-widget-divider"></div>
<h4 style="margin: 0 0 8px 0; font-size: 13px; color: #1d2327;">
<?php esc_html_e('Status Breakdown', 'wc-licensed-product'); ?>
</h4>
<div class="wclp-status-list">
<span class="wclp-status-badge active">
<span class="dashicons dashicons-yes-alt" style="font-size: 14px; width: 14px; height: 14px;"></span>
<?php printf(
esc_html__('Active: %d', 'wc-licensed-product'),
$stats['by_status'][License::STATUS_ACTIVE]
); ?>
</span>
<span class="wclp-status-badge inactive">
<span class="dashicons dashicons-marker" style="font-size: 14px; width: 14px; height: 14px;"></span>
<?php printf(
esc_html__('Inactive: %d', 'wc-licensed-product'),
$stats['by_status'][License::STATUS_INACTIVE]
); ?>
</span>
<span class="wclp-status-badge expired">
<span class="dashicons dashicons-clock" style="font-size: 14px; width: 14px; height: 14px;"></span>
<?php printf(
esc_html__('Expired: %d', 'wc-licensed-product'),
$stats['by_status'][License::STATUS_EXPIRED]
); ?>
</span>
<span class="wclp-status-badge revoked">
<span class="dashicons dashicons-dismiss" style="font-size: 14px; width: 14px; height: 14px;"></span>
<?php printf(
esc_html__('Revoked: %d', 'wc-licensed-product'),
$stats['by_status'][License::STATUS_REVOKED]
); ?>
</span>
</div>
<div class="wclp-widget-divider"></div>
<h4 style="margin: 0 0 8px 0; font-size: 13px; color: #1d2327;">
<?php esc_html_e('License Types', 'wc-licensed-product'); ?>
</h4>
<p style="margin: 0; font-size: 13px; color: #646970;">
<span class="dashicons dashicons-calendar-alt" style="font-size: 14px; width: 14px; height: 14px; vertical-align: text-bottom;"></span>
<?php printf(
esc_html__('Time-limited: %d', 'wc-licensed-product'),
$stats['expiring']
); ?>
&nbsp;&nbsp;|&nbsp;&nbsp;
<span class="dashicons dashicons-infinity" style="font-size: 14px; width: 14px; height: 14px; vertical-align: text-bottom;"></span>
<?php printf(
esc_html__('Lifetime: %d', 'wc-licensed-product'),
$stats['lifetime']
); ?>
</p>
<div class="wclp-widget-footer">
<a href="<?php echo esc_url($licensesUrl); ?>" class="button button-secondary">
<?php esc_html_e('View All Licenses', 'wc-licensed-product'); ?>
</a>
</div>
<?php
}
}

View File

@@ -55,6 +55,7 @@ final class LicenseEmailController
public function registerEmailClasses(array $email_classes): array public function registerEmailClasses(array $email_classes): array
{ {
$email_classes['WCLP_License_Expiration'] = new LicenseExpirationEmail(); $email_classes['WCLP_License_Expiration'] = new LicenseExpirationEmail();
$email_classes['WCLP_License_Expired'] = new LicenseExpiredEmail();
return $email_classes; return $email_classes;
} }
@@ -69,10 +70,13 @@ final class LicenseEmailController
} }
/** /**
* Send expiration warning emails * Send expiration warning emails and auto-expire licenses
*/ */
public function sendExpirationWarnings(): void public function sendExpirationWarnings(): void
{ {
// First, auto-expire licenses that have passed their expiration date
$this->autoExpireAndNotify();
// Check if expiration emails are enabled in settings // Check if expiration emails are enabled in settings
if (!SettingsController::isExpirationEmailsEnabled()) { if (!SettingsController::isExpirationEmailsEnabled()) {
return; return;
@@ -107,6 +111,41 @@ final class LicenseEmailController
} }
} }
/**
* Auto-expire licenses and send expired notifications
*/
private function autoExpireAndNotify(): void
{
// Get licenses that should be auto-expired
$expiredActiveLicenses = $this->licenseManager->getExpiredActiveLicenses();
if (empty($expiredActiveLicenses)) {
return;
}
// Get the WooCommerce email instance for expired notifications
$mailer = WC()->mailer();
$emails = $mailer->get_emails();
/** @var LicenseExpiredEmail|null $expiredEmail */
$expiredEmail = $emails['WCLP_License_Expired'] ?? null;
foreach ($expiredActiveLicenses as $license) {
// Auto-expire the license
$wasExpired = $this->licenseManager->autoExpireLicense($license->getId());
if ($wasExpired && $expiredEmail && $expiredEmail->is_enabled()) {
// Check if we haven't already sent an expired notification
if (!$this->licenseManager->wasExpirationNotified($license->getId(), 'license_expired')) {
// Send expired notification email
if ($expiredEmail->trigger($license)) {
$this->licenseManager->markExpirationNotified($license->getId(), 'license_expired');
}
}
}
}
}
/** /**
* Process and send expiration warnings for a specific time frame * Process and send expiration warnings for a specific time frame
* *

View File

@@ -0,0 +1,335 @@
<?php
/**
* License Expired Email
*
* @package Jeremias\WcLicensedProduct\Email
*/
declare(strict_types=1);
namespace Jeremias\WcLicensedProduct\Email;
use Jeremias\WcLicensedProduct\License\License;
use WC_Email;
/**
* License Expired Email class
*
* Sends email notifications to customers when their licenses have expired.
* Uses WooCommerce's transactional email system for consistent styling and customization.
*/
class LicenseExpiredEmail extends WC_Email
{
/**
* License object
*/
public ?License $license = null;
/**
* Product name
*/
public string $product_name = '';
/**
* Expiration date formatted
*/
public string $expiration_date = '';
/**
* Customer display name
*/
public string $customer_name = '';
/**
* Constructor
*/
public function __construct()
{
$this->id = 'wclp_license_expired';
$this->customer_email = true;
$this->title = __('License Expired', 'wc-licensed-product');
$this->description = __('License expired emails are sent to customers when their licenses have expired.', 'wc-licensed-product');
$this->placeholders = [
'{site_title}' => $this->get_blogname(),
'{product_name}' => '',
'{expiration_date}' => '',
];
// Call parent constructor
parent::__construct();
}
/**
* Get email subject
*/
public function get_default_subject(): string
{
return __('[{site_title}] Your license for {product_name} has expired', 'wc-licensed-product');
}
/**
* Get email heading
*/
public function get_default_heading(): string
{
return __('License Expired', 'wc-licensed-product');
}
/**
* Trigger the email
*
* @param License $license License object
*/
public function trigger(License $license): bool
{
$this->setup_locale();
$customer = get_userdata($license->getCustomerId());
if (!$customer || !$customer->user_email) {
$this->restore_locale();
return false;
}
$this->license = $license;
$this->recipient = $customer->user_email;
$this->customer_name = $customer->display_name ?: __('Customer', 'wc-licensed-product');
$product = wc_get_product($license->getProductId());
$this->product_name = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product');
$expiresAt = $license->getExpiresAt();
$this->expiration_date = $expiresAt ? $expiresAt->format(get_option('date_format')) : '';
// Update placeholders
$this->placeholders['{product_name}'] = $this->product_name;
$this->placeholders['{expiration_date}'] = $this->expiration_date;
if (!$this->is_enabled() || !$this->get_recipient()) {
$this->restore_locale();
return false;
}
$result = $this->send(
$this->get_recipient(),
$this->get_subject(),
$this->get_content(),
$this->get_headers(),
$this->get_attachments()
);
$this->restore_locale();
return $result;
}
/**
* Get content HTML
*/
public function get_content_html(): string
{
ob_start();
// Use WooCommerce's email header
wc_get_template('emails/email-header.php', ['email_heading' => $this->get_heading()]);
$this->render_email_body_html();
// Use WooCommerce's email footer
wc_get_template('emails/email-footer.php', ['email' => $this]);
return ob_get_clean();
}
/**
* Get content plain text
*/
public function get_content_plain(): string
{
ob_start();
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
echo esc_html(wp_strip_all_tags($this->get_heading()));
echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
$this->render_email_body_plain();
return ob_get_clean();
}
/**
* Render HTML email body content
*/
private function render_email_body_html(): void
{
$account_url = wc_get_account_endpoint_url('licenses');
?>
<p><?php printf(esc_html__('Hello %s,', 'wc-licensed-product'), esc_html($this->customer_name)); ?></p>
<p style="color: #dc3232; font-weight: 600;">
<?php printf(
esc_html__('Your license for %1$s has expired on %2$s.', 'wc-licensed-product'),
'<strong>' . esc_html($this->product_name) . '</strong>',
esc_html($this->expiration_date)
); ?>
</p>
<p>
<?php esc_html_e('Your license is no longer valid and the product will stop working until you renew.', 'wc-licensed-product'); ?>
</p>
<h2><?php esc_html_e('Expired License Details', 'wc-licensed-product'); ?></h2>
<div style="margin-bottom: 40px;">
<table class="td" cellspacing="0" cellpadding="6" style="width: 100%; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;" border="1">
<tbody>
<tr>
<th class="td" scope="row" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php esc_html_e('Product:', 'wc-licensed-product'); ?></th>
<td class="td" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php echo esc_html($this->product_name); ?></td>
</tr>
<tr>
<th class="td" scope="row" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php esc_html_e('License Key:', 'wc-licensed-product'); ?></th>
<td class="td" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;">
<code style="background: #f5f5f5; padding: 3px 8px; border-radius: 3px; font-family: monospace;">
<?php echo esc_html($this->license->getLicenseKey()); ?>
</code>
</td>
</tr>
<tr>
<th class="td" scope="row" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php esc_html_e('Domain:', 'wc-licensed-product'); ?></th>
<td class="td" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php echo esc_html($this->license->getDomain()); ?></td>
</tr>
<tr>
<th class="td" scope="row" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php esc_html_e('Expired on:', 'wc-licensed-product'); ?></th>
<td class="td" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>; color: #dc3232; font-weight: 600;"><?php echo esc_html($this->expiration_date); ?></td>
</tr>
<tr>
<th class="td" scope="row" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;"><?php esc_html_e('Status:', 'wc-licensed-product'); ?></th>
<td class="td" style="text-align:<?php echo is_rtl() ? 'right' : 'left'; ?>;">
<span style="background: #f8d7da; color: #721c24; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 500;">
<?php esc_html_e('Expired', 'wc-licensed-product'); ?>
</span>
</td>
</tr>
</tbody>
</table>
</div>
<?php
$additional_content = $this->get_additional_content();
if ($additional_content) :
?>
<p><?php echo wp_kses_post($additional_content); ?></p>
<?php endif; ?>
<p style="margin-top: 25px;">
<a href="<?php echo esc_url($account_url); ?>" class="button" style="display: inline-block; background-color: #7f54b3; color: #ffffff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: 600;">
<?php esc_html_e('View My Licenses', 'wc-licensed-product'); ?>
</a>
</p>
<?php
}
/**
* Render plain text email body content
*/
private function render_email_body_plain(): void
{
printf(esc_html__('Hello %s,', 'wc-licensed-product'), esc_html($this->customer_name));
echo "\n\n";
printf(
esc_html__('Your license for %1$s has expired on %2$s.', 'wc-licensed-product'),
esc_html($this->product_name),
esc_html($this->expiration_date)
);
echo "\n\n";
echo esc_html__('Your license is no longer valid and the product will stop working until you renew.', 'wc-licensed-product');
echo "\n\n";
echo "----------\n";
echo esc_html__('Expired License Details', 'wc-licensed-product') . "\n";
echo "----------\n\n";
echo esc_html__('Product:', 'wc-licensed-product') . ' ' . esc_html($this->product_name) . "\n";
echo esc_html__('License Key:', 'wc-licensed-product') . ' ' . esc_html($this->license->getLicenseKey()) . "\n";
echo esc_html__('Domain:', 'wc-licensed-product') . ' ' . esc_html($this->license->getDomain()) . "\n";
echo esc_html__('Expired on:', 'wc-licensed-product') . ' ' . esc_html($this->expiration_date) . "\n";
echo esc_html__('Status:', 'wc-licensed-product') . ' ' . esc_html__('Expired', 'wc-licensed-product') . "\n\n";
$additional_content = $this->get_additional_content();
if ($additional_content) {
echo "----------\n\n";
echo esc_html(wp_strip_all_tags(wptexturize($additional_content)));
echo "\n\n";
}
echo esc_html__('View My Licenses', 'wc-licensed-product') . ': ' . esc_url(wc_get_account_endpoint_url('licenses')) . "\n\n";
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
}
/**
* Default content to show below main email content
*/
public function get_default_additional_content(): string
{
return __('To continue using this product, please renew your license.', 'wc-licensed-product');
}
/**
* Initialize settings form fields
*/
public function init_form_fields(): void
{
$placeholder_text = sprintf(
/* translators: %s: list of placeholders */
__('Available placeholders: %s', 'wc-licensed-product'),
'<code>{site_title}, {product_name}, {expiration_date}</code>'
);
$this->form_fields = [
'enabled' => [
'title' => __('Enable/Disable', 'wc-licensed-product'),
'type' => 'checkbox',
'label' => __('Enable this email notification', 'wc-licensed-product'),
'default' => 'yes',
],
'subject' => [
'title' => __('Subject', 'wc-licensed-product'),
'type' => 'text',
'desc_tip' => true,
'description' => $placeholder_text,
'placeholder' => $this->get_default_subject(),
'default' => '',
],
'heading' => [
'title' => __('Email heading', 'wc-licensed-product'),
'type' => 'text',
'desc_tip' => true,
'description' => $placeholder_text,
'placeholder' => $this->get_default_heading(),
'default' => '',
],
'additional_content' => [
'title' => __('Additional content', 'wc-licensed-product'),
'description' => __('Text to appear below the main email content.', 'wc-licensed-product') . ' ' . $placeholder_text,
'css' => 'width:400px; height: 75px;',
'placeholder' => $this->get_default_additional_content(),
'type' => 'textarea',
'default' => '',
'desc_tip' => true,
],
'email_type' => [
'title' => __('Email type', 'wc-licensed-product'),
'type' => 'select',
'description' => __('Choose which format of email to send.', 'wc-licensed-product'),
'default' => 'html',
'class' => 'email_type wc-enhanced-select',
'options' => $this->get_email_type_options(),
'desc_tip' => true,
],
];
}
}

View File

@@ -862,6 +862,56 @@ class LicenseManager
return (bool) get_user_meta($license->getCustomerId(), $metaKey, true); return (bool) get_user_meta($license->getCustomerId(), $metaKey, true);
} }
/**
* Get licenses that have passed their expiration date but are still marked as active
*
* @return array Array of License objects that need to be auto-expired
*/
public function getExpiredActiveLicenses(): array
{
global $wpdb;
$tableName = Installer::getLicensesTable();
$now = new \DateTimeImmutable();
$sql = "SELECT * FROM {$tableName}
WHERE expires_at IS NOT NULL
AND expires_at < %s
AND status = %s";
$rows = $wpdb->get_results(
$wpdb->prepare($sql, $now->format('Y-m-d H:i:s'), License::STATUS_ACTIVE),
ARRAY_A
);
return array_map(fn(array $row) => License::fromArray($row), $rows ?: []);
}
/**
* Auto-expire a license and return true if status was changed
*
* @param int $licenseId License ID
* @return bool True if license was expired, false if already expired or error
*/
public function autoExpireLicense(int $licenseId): bool
{
$license = $this->getLicenseById($licenseId);
if (!$license) {
return false;
}
// Only expire if currently active and past expiration date
if ($license->getStatus() !== License::STATUS_ACTIVE) {
return false;
}
if (!$license->isExpired()) {
return false;
}
return $this->updateLicenseStatus($licenseId, License::STATUS_EXPIRED);
}
/** /**
* Import a license from CSV data * Import a license from CSV data
* *

View File

@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace Jeremias\WcLicensedProduct; namespace Jeremias\WcLicensedProduct;
use Jeremias\WcLicensedProduct\Admin\AdminController; use Jeremias\WcLicensedProduct\Admin\AdminController;
use Jeremias\WcLicensedProduct\Admin\DashboardWidgetController;
use Jeremias\WcLicensedProduct\Admin\OrderLicenseController; use Jeremias\WcLicensedProduct\Admin\OrderLicenseController;
use Jeremias\WcLicensedProduct\Admin\SettingsController; use Jeremias\WcLicensedProduct\Admin\SettingsController;
use Jeremias\WcLicensedProduct\Admin\VersionAdminController; use Jeremias\WcLicensedProduct\Admin\VersionAdminController;
@@ -151,6 +152,7 @@ final class Plugin
new VersionAdminController($this->versionManager); new VersionAdminController($this->versionManager);
new OrderLicenseController($this->licenseManager); new OrderLicenseController($this->licenseManager);
new SettingsController(); new SettingsController();
new DashboardWidgetController($this->licenseManager);
// Show admin notice if unlicensed and not on localhost // Show admin notice if unlicensed and not on localhost
if (!$isLicensed && !$licenseChecker->isLocalhost()) { if (!$isLicensed && !$licenseChecker->isLocalhost()) {

View File

@@ -45,6 +45,12 @@ final class LicensedProductType
// Make product virtual by default // Make product virtual by default
add_filter('woocommerce_product_is_virtual', [$this, 'isVirtual'], 10, 2); add_filter('woocommerce_product_is_virtual', [$this, 'isVirtual'], 10, 2);
// Display current version under product title on single product page
add_action('woocommerce_single_product_summary', [$this, 'displayCurrentVersion'], 6);
// Enqueue frontend CSS for licensed products on single product pages
add_action('wp_enqueue_scripts', [$this, 'enqueueFrontendStyles']);
} }
/** /**
@@ -235,4 +241,52 @@ final class LicensedProductType
} }
return $isVirtual; return $isVirtual;
} }
/**
* Enqueue frontend styles for licensed products on single product pages
*/
public function enqueueFrontendStyles(): void
{
if (!is_product()) {
return;
}
global $product;
if (!$product || !$product->is_type('licensed')) {
return;
}
wp_enqueue_style(
'wc-licensed-product-frontend',
WC_LICENSED_PRODUCT_PLUGIN_URL . 'assets/css/frontend.css',
[],
WC_LICENSED_PRODUCT_VERSION
);
}
/**
* Display current version under product title on single product page
*/
public function displayCurrentVersion(): void
{
global $product;
if (!$product || !$product->is_type('licensed')) {
return;
}
/** @var LicensedProduct $product */
$version = $product->get_current_version();
if (empty($version)) {
return;
}
printf(
'<p class="wclp-product-version"><span class="version-label">%s</span> <span class="version-number">%s</span></p>',
esc_html__('Version:', 'wc-licensed-product'),
esc_html($version)
);
}
} }

View File

@@ -57,12 +57,14 @@
<h4>{{ __('Available Downloads') }}</h4> <h4>{{ __('Available Downloads') }}</h4>
<ul class="download-list"> <ul class="download-list">
{% for download in item.downloads %} {% for download in item.downloads %}
<li> <li class="download-item">
<div class="download-row-file">
<a href="{{ esc_url(download.download_url) }}" class="download-link"> <a href="{{ esc_url(download.download_url) }}" class="download-link">
<span class="dashicons dashicons-download"></span> <span class="dashicons dashicons-download"></span>
{{ esc_html(download.filename ?: 'Version ' ~ download.version) }} {{ esc_html(download.filename ?: 'Version ' ~ download.version) }}
</a> </a>
<span class="download-version">v{{ esc_html(download.version) }}</span> </div>
<div class="download-row-meta">
<span class="download-date">{{ esc_html(download.released_at) }}</span> <span class="download-date">{{ esc_html(download.released_at) }}</span>
{% if download.file_hash %} {% if download.file_hash %}
<span class="download-hash" title="{{ esc_attr(download.file_hash) }}"> <span class="download-hash" title="{{ esc_attr(download.file_hash) }}">
@@ -70,6 +72,7 @@
<code>{{ download.file_hash[:12] }}...</code> <code>{{ download.file_hash[:12] }}...</code>
</span> </span>
{% endif %} {% endif %}
</div>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

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