You've already forked wc-licensed-product
Implement version 0.0.8 features
- Remove Current Version field from product license settings - Derive current version from latest product version in database - Refactor email system to use WooCommerce email notification classes - Add LicenseExpirationEmail WC_Email class for expiration warnings - Add customizable email templates (HTML and plain text) - Update settings to link to WooCommerce email configuration - Update translations for new email-related strings Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
39
CLAUDE.md
39
CLAUDE.md
@@ -36,10 +36,6 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
|
|||||||
|
|
||||||
_No known bugs at this time._
|
_No known bugs at this time._
|
||||||
|
|
||||||
### Version 0.0.8 (Next)
|
|
||||||
|
|
||||||
_No planned features yet. Add items as needed._
|
|
||||||
|
|
||||||
## Technical Stack
|
## Technical Stack
|
||||||
|
|
||||||
- **Language:** PHP 8.3.x
|
- **Language:** PHP 8.3.x
|
||||||
@@ -429,3 +425,38 @@ Full API documentation available in `openapi.json` (OpenAPI 3.1 specification).
|
|||||||
- Expiration warnings use WordPress cron with daily schedule
|
- Expiration warnings use WordPress cron with daily schedule
|
||||||
- CSV import supports both exported format and simplified format
|
- CSV import supports both exported format and simplified format
|
||||||
- User meta tracks expiration notifications to prevent duplicates
|
- User meta tracks expiration notifications to prevent duplicates
|
||||||
|
|
||||||
|
### 2026-01-21 - Version 0.0.8 Features
|
||||||
|
|
||||||
|
**Implemented:**
|
||||||
|
|
||||||
|
- Removed "Current Version" field from product license settings
|
||||||
|
- Current version now automatically derived from latest product version in database
|
||||||
|
- Refactored email system to use WooCommerce email notification classes
|
||||||
|
- License Expiration Warning email is now configurable via WooCommerce > Settings > Emails
|
||||||
|
- Email templates can be overridden in themes (woocommerce/emails/license-expiration.php)
|
||||||
|
- Configurable warning days for expiration notifications (first and second warning)
|
||||||
|
|
||||||
|
**New classes:**
|
||||||
|
|
||||||
|
- `LicenseExpirationEmail` - WooCommerce WC_Email subclass for license expiration warnings
|
||||||
|
|
||||||
|
**New files:**
|
||||||
|
|
||||||
|
- `templates/emails/license-expiration.php` - HTML email template
|
||||||
|
- `templates/emails/plain/license-expiration.php` - Plain text email template
|
||||||
|
|
||||||
|
**Modified classes:**
|
||||||
|
|
||||||
|
- `LicensedProduct` - `get_current_version()` and `get_major_version()` now query VersionManager
|
||||||
|
- `LicensedProductType` - Removed Current Version field from product data panel
|
||||||
|
- `VersionAdminController` - Removed automatic update of `_licensed_current_version` meta
|
||||||
|
- `LicenseEmailController` - Registers WooCommerce email class, uses WC email system for sending
|
||||||
|
- `SettingsController` - Updated email section to link to WooCommerce email settings
|
||||||
|
|
||||||
|
**Technical notes:**
|
||||||
|
|
||||||
|
- WooCommerce email system allows admins to customize subject, heading, content, and enable/disable
|
||||||
|
- Email templates follow WooCommerce conventions and can be overridden in themes
|
||||||
|
- Expiration warning schedule (days before) remains in plugin settings
|
||||||
|
- Email enable/disable is controlled through WooCommerce email settings
|
||||||
|
|||||||
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
# This file is distributed under the GPL-2.0-or-later.
|
# This file is distributed under the GPL-2.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WC Licensed Product 0.0.7\n"
|
"Project-Id-Version: WC Licensed Product 0.0.8\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
||||||
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
"PO-Revision-Date: 2026-01-21T00:00:00+00:00\n"
|
"PO-Revision-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
@@ -63,12 +63,6 @@ msgstr "Falls aktiviert, werden Lizenzen an die Hauptversion zum Kaufzeitpunkt g
|
|||||||
msgid "If enabled, licenses are bound to the major version at purchase time by default."
|
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."
|
msgstr "Falls aktiviert, werden Lizenzen standardmässig an die Hauptversion zum Kaufzeitpunkt gebunden."
|
||||||
|
|
||||||
msgid "Current Version"
|
|
||||||
msgstr "Aktuelle Version"
|
|
||||||
|
|
||||||
msgid "Current software version (e.g., 1.0.0)"
|
|
||||||
msgstr "Aktuelle Software-Version (z.B. 1.0.0)"
|
|
||||||
|
|
||||||
#. Global settings
|
#. Global settings
|
||||||
msgid "Default License Settings"
|
msgid "Default License Settings"
|
||||||
msgstr "Standard Lizenz-Einstellungen"
|
msgstr "Standard Lizenz-Einstellungen"
|
||||||
@@ -818,3 +812,56 @@ msgstr "Ja"
|
|||||||
|
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr "Nein"
|
msgstr "Nein"
|
||||||
|
|
||||||
|
#. Email settings
|
||||||
|
msgid "Expiration Warning Schedule"
|
||||||
|
msgstr "Ablaufwarnung Zeitplan"
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
|
||||||
|
msgstr "WooCommerce > Einstellungen > E-Mails > Lizenzablauf-Warnung"
|
||||||
|
|
||||||
|
msgid "First Warning (Days Before)"
|
||||||
|
msgstr "Erste Warnung (Tage vorher)"
|
||||||
|
|
||||||
|
msgid "Days before expiration to send the first warning email."
|
||||||
|
msgstr "Tage vor Ablauf, um die erste Warn-E-Mail zu senden."
|
||||||
|
|
||||||
|
msgid "Second Warning (Days Before)"
|
||||||
|
msgstr "Zweite Warnung (Tage vorher)"
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
#. WooCommerce Email Class
|
||||||
|
msgid "License Expiration Warning"
|
||||||
|
msgstr "Lizenzablauf-Warnung"
|
||||||
|
|
||||||
|
msgid "License expiration warning emails are sent to customers when their licenses are about to expire."
|
||||||
|
msgstr "Lizenzablauf-Warnungs-E-Mails werden an Kunden gesendet, wenn ihre Lizenzen bald ablaufen."
|
||||||
|
|
||||||
|
msgid "[{site_title}] Your license for {product_name} expires in {days_remaining} days"
|
||||||
|
msgstr "[{site_title}] Ihre Lizenz für {product_name} läuft in {days_remaining} Tagen ab"
|
||||||
|
|
||||||
|
msgid "Available placeholders: %s"
|
||||||
|
msgstr "Verfügbare Platzhalter: %s"
|
||||||
|
|
||||||
|
msgid "Enable this email notification"
|
||||||
|
msgstr "Diese E-Mail-Benachrichtigung aktivieren"
|
||||||
|
|
||||||
|
msgid "Email heading"
|
||||||
|
msgstr "E-Mail-Überschrift"
|
||||||
|
|
||||||
|
msgid "Additional content"
|
||||||
|
msgstr "Zusätzlicher Inhalt"
|
||||||
|
|
||||||
|
msgid "Text to appear below the main email content."
|
||||||
|
msgstr "Text, der unter dem Haupt-E-Mail-Inhalt erscheinen soll."
|
||||||
|
|
||||||
|
msgid "Email type"
|
||||||
|
msgstr "E-Mail-Typ"
|
||||||
|
|
||||||
|
msgid "Choose which format of email to send."
|
||||||
|
msgstr "Wählen Sie, welches E-Mail-Format gesendet werden soll."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# This file is distributed under the GPL-2.0-or-later.
|
# This file is distributed under the GPL-2.0-or-later.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: WC Licensed Product 0.0.7\n"
|
"Project-Id-Version: WC Licensed Product 0.0.8\n"
|
||||||
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/magdev/wc-licensed-product/issues\n"
|
||||||
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
"POT-Creation-Date: 2026-01-21T00:00:00+00:00\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@@ -60,12 +60,6 @@ msgstr ""
|
|||||||
msgid "If enabled, licenses are bound to the major version at purchase time by default."
|
msgid "If enabled, licenses are bound to the major version at purchase time by default."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Current Version"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Current software version (e.g., 1.0.0)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. Global settings
|
#. Global settings
|
||||||
msgid "Default License Settings"
|
msgid "Default License Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -815,3 +809,59 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Email settings
|
||||||
|
msgid "Expiration Warning Schedule"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Configure when expiration warning emails are sent. To customize the email template, enable/disable, or change the subject, go to %s."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "WooCommerce > Settings > Emails > License Expiration Warning"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "First Warning (Days Before)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Days before expiration to send the first warning email."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Second Warning (Days Before)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Days before expiration to send the second warning email. Set to 0 to disable."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. WooCommerce Email Class
|
||||||
|
msgid "License Expiration Warning"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "License expiration warning emails are sent to customers when their licenses are about to expire."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "[{site_title}] Your license for {product_name} expires in {days_remaining} days"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Available placeholders: %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Enable this email notification"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Email heading"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Additional content"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Text to appear below the main email content."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Email type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Choose which format of email to send."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Customer"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -92,6 +92,44 @@ final class SettingsController
|
|||||||
'type' => 'sectionend',
|
'type' => 'sectionend',
|
||||||
'id' => 'wc_licensed_product_section_defaults_end',
|
'id' => 'wc_licensed_product_section_defaults_end',
|
||||||
],
|
],
|
||||||
|
// Email settings section
|
||||||
|
'email_section_title' => [
|
||||||
|
'name' => __('Expiration Warning Schedule', 'wc-licensed-product'),
|
||||||
|
'type' => 'title',
|
||||||
|
'desc' => sprintf(
|
||||||
|
/* translators: %s: URL to WooCommerce email settings */
|
||||||
|
__('Configure when expiration warning emails are sent. To customize the email template, enable/disable, or change the subject, go to %s.', 'wc-licensed-product'),
|
||||||
|
'<a href="' . esc_url(admin_url('admin.php?page=wc-settings&tab=email§ion=wclp_license_expiration')) . '">' .
|
||||||
|
__('WooCommerce > Settings > Emails > License Expiration Warning', 'wc-licensed-product') . '</a>'
|
||||||
|
),
|
||||||
|
'id' => 'wc_licensed_product_section_email',
|
||||||
|
],
|
||||||
|
'expiration_warning_days_first' => [
|
||||||
|
'name' => __('First Warning (Days Before)', 'wc-licensed-product'),
|
||||||
|
'type' => 'number',
|
||||||
|
'desc' => __('Days before expiration to send the first warning email.', 'wc-licensed-product'),
|
||||||
|
'id' => 'wc_licensed_product_expiration_warning_days_first',
|
||||||
|
'default' => '7',
|
||||||
|
'custom_attributes' => [
|
||||||
|
'min' => '1',
|
||||||
|
'step' => '1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'expiration_warning_days_second' => [
|
||||||
|
'name' => __('Second Warning (Days Before)', 'wc-licensed-product'),
|
||||||
|
'type' => 'number',
|
||||||
|
'desc' => __('Days before expiration to send the second warning email. Set to 0 to disable.', 'wc-licensed-product'),
|
||||||
|
'id' => 'wc_licensed_product_expiration_warning_days_second',
|
||||||
|
'default' => '1',
|
||||||
|
'custom_attributes' => [
|
||||||
|
'min' => '0',
|
||||||
|
'step' => '1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'email_section_end' => [
|
||||||
|
'type' => 'sectionend',
|
||||||
|
'id' => 'wc_licensed_product_section_email_end',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,4 +177,37 @@ final class SettingsController
|
|||||||
{
|
{
|
||||||
return get_option('wc_licensed_product_default_bind_to_version', 'no') === 'yes';
|
return get_option('wc_licensed_product_default_bind_to_version', 'no') === 'yes';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if expiration warning emails are enabled
|
||||||
|
* This checks both the WooCommerce email setting and the old setting for backwards compatibility
|
||||||
|
*/
|
||||||
|
public static function isExpirationEmailsEnabled(): bool
|
||||||
|
{
|
||||||
|
// Check WooCommerce email enabled status
|
||||||
|
$emailEnabled = get_option('woocommerce_wclp_license_expiration_settings');
|
||||||
|
if (is_array($emailEnabled) && isset($emailEnabled['enabled'])) {
|
||||||
|
return $emailEnabled['enabled'] === 'yes';
|
||||||
|
}
|
||||||
|
// Default to enabled if not yet configured
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get first warning days before expiration
|
||||||
|
*/
|
||||||
|
public static function getFirstWarningDays(): int
|
||||||
|
{
|
||||||
|
$value = get_option('wc_licensed_product_expiration_warning_days_first', 7);
|
||||||
|
return max(1, (int) $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get second warning days before expiration
|
||||||
|
*/
|
||||||
|
public static function getSecondWarningDays(): int
|
||||||
|
{
|
||||||
|
$value = get_option('wc_licensed_product_expiration_warning_days_second', 1);
|
||||||
|
return max(0, (int) $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,9 +263,6 @@ final class VersionAdminController
|
|||||||
wp_send_json_error(['message' => __('Failed to create version.', 'wc-licensed-product')]);
|
wp_send_json_error(['message' => __('Failed to create version.', 'wc-licensed-product')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also update the product's current version meta
|
|
||||||
update_post_meta($productId, '_licensed_current_version', $version);
|
|
||||||
|
|
||||||
wp_send_json_success([
|
wp_send_json_success([
|
||||||
'message' => __('Version added successfully.', 'wc-licensed-product'),
|
'message' => __('Version added successfully.', 'wc-licensed-product'),
|
||||||
'version' => $newVersion->toArray(),
|
'version' => $newVersion->toArray(),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||||||
namespace Jeremias\WcLicensedProduct\Email;
|
namespace Jeremias\WcLicensedProduct\Email;
|
||||||
|
|
||||||
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
||||||
|
use Jeremias\WcLicensedProduct\Admin\SettingsController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles email notifications for licenses
|
* Handles email notifications for licenses
|
||||||
@@ -29,6 +30,9 @@ final class LicenseEmailController
|
|||||||
*/
|
*/
|
||||||
private function registerHooks(): void
|
private function registerHooks(): void
|
||||||
{
|
{
|
||||||
|
// Register custom WooCommerce email classes
|
||||||
|
add_filter('woocommerce_email_classes', [$this, 'registerEmailClasses']);
|
||||||
|
|
||||||
// Add license info to order completed email
|
// Add license info to order completed email
|
||||||
add_action('woocommerce_email_after_order_table', [$this, 'addLicenseInfoToEmail'], 20, 4);
|
add_action('woocommerce_email_after_order_table', [$this, 'addLicenseInfoToEmail'], 20, 4);
|
||||||
|
|
||||||
@@ -40,6 +44,45 @@ final class LicenseEmailController
|
|||||||
|
|
||||||
// Cron action for checking expiring licenses
|
// Cron action for checking expiring licenses
|
||||||
add_action('wclp_check_expiring_licenses', [$this, 'sendExpirationWarnings']);
|
add_action('wclp_check_expiring_licenses', [$this, 'sendExpirationWarnings']);
|
||||||
|
|
||||||
|
// Add email templates location for theme overrides
|
||||||
|
add_filter('woocommerce_locate_template', [$this, 'locateTemplate'], 10, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register custom email classes with WooCommerce
|
||||||
|
*
|
||||||
|
* @param array $email_classes Existing email classes
|
||||||
|
* @return array Modified email classes
|
||||||
|
*/
|
||||||
|
public function registerEmailClasses(array $email_classes): array
|
||||||
|
{
|
||||||
|
$email_classes['WCLP_License_Expiration'] = new LicenseExpirationEmail();
|
||||||
|
return $email_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate templates in plugin directory
|
||||||
|
*
|
||||||
|
* @param string $template Template path
|
||||||
|
* @param string $template_name Template name
|
||||||
|
* @param string $template_path Template path prefix
|
||||||
|
* @return string Modified template path
|
||||||
|
*/
|
||||||
|
public function locateTemplate(string $template, string $template_name, string $template_path): string
|
||||||
|
{
|
||||||
|
// Only handle our email templates
|
||||||
|
if (strpos($template_name, 'emails/license-') !== 0) {
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
$plugin_template = WC_LICENSED_PRODUCT_PLUGIN_DIR . 'templates/' . $template_name;
|
||||||
|
|
||||||
|
if (file_exists($plugin_template)) {
|
||||||
|
return $plugin_template;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $template;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,20 +100,48 @@ final class LicenseEmailController
|
|||||||
*/
|
*/
|
||||||
public function sendExpirationWarnings(): void
|
public function sendExpirationWarnings(): void
|
||||||
{
|
{
|
||||||
// Check for licenses expiring in 7 days
|
// Check if expiration emails are enabled in settings
|
||||||
$this->processExpirationWarnings(7, 'expiring_7_days');
|
if (!SettingsController::isExpirationEmailsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for licenses expiring in 1 day
|
// Get the WooCommerce email instance
|
||||||
$this->processExpirationWarnings(1, 'expiring_1_day');
|
$mailer = WC()->mailer();
|
||||||
|
$emails = $mailer->get_emails();
|
||||||
|
|
||||||
|
if (!isset($emails['WCLP_License_Expiration'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var LicenseExpirationEmail $expirationEmail */
|
||||||
|
$expirationEmail = $emails['WCLP_License_Expiration'];
|
||||||
|
|
||||||
|
// Check if the email is enabled
|
||||||
|
if (!$expirationEmail->is_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get configurable warning days
|
||||||
|
$firstWarningDays = SettingsController::getFirstWarningDays();
|
||||||
|
$secondWarningDays = SettingsController::getSecondWarningDays();
|
||||||
|
|
||||||
|
// Check for licenses expiring at first warning threshold
|
||||||
|
$this->processExpirationWarnings($expirationEmail, $firstWarningDays, 'expiring_first_warning');
|
||||||
|
|
||||||
|
// Check for licenses expiring at second warning threshold (if enabled)
|
||||||
|
if ($secondWarningDays > 0 && $secondWarningDays < $firstWarningDays) {
|
||||||
|
$this->processExpirationWarnings($expirationEmail, $secondWarningDays, 'expiring_second_warning');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process and send expiration warnings for a specific time frame
|
* Process and send expiration warnings for a specific time frame
|
||||||
*
|
*
|
||||||
|
* @param LicenseExpirationEmail $email Email instance
|
||||||
* @param int $days Days until expiration
|
* @param int $days Days until expiration
|
||||||
* @param string $notificationType Notification type identifier
|
* @param string $notificationType Notification type identifier
|
||||||
*/
|
*/
|
||||||
private function processExpirationWarnings(int $days, string $notificationType): void
|
private function processExpirationWarnings(LicenseExpirationEmail $email, int $days, string $notificationType): void
|
||||||
{
|
{
|
||||||
$licenses = $this->licenseManager->getLicensesExpiringSoon($days);
|
$licenses = $this->licenseManager->getLicensesExpiringSoon($days);
|
||||||
|
|
||||||
@@ -80,166 +151,14 @@ final class LicenseEmailController
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the warning email
|
// Send the warning email using WooCommerce email system
|
||||||
if ($this->sendExpirationWarningEmail($license, $days)) {
|
if ($email->trigger($license, $days)) {
|
||||||
// Mark as notified
|
// Mark as notified
|
||||||
$this->licenseManager->markExpirationNotified($license->getId(), $notificationType);
|
$this->licenseManager->markExpirationNotified($license->getId(), $notificationType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send expiration warning email to customer
|
|
||||||
*
|
|
||||||
* @param \Jeremias\WcLicensedProduct\License\License $license License object
|
|
||||||
* @param int $daysRemaining Days until expiration
|
|
||||||
* @return bool Whether email was sent successfully
|
|
||||||
*/
|
|
||||||
private function sendExpirationWarningEmail($license, int $daysRemaining): bool
|
|
||||||
{
|
|
||||||
$customer = get_userdata($license->getCustomerId());
|
|
||||||
if (!$customer || !$customer->user_email) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$product = wc_get_product($license->getProductId());
|
|
||||||
$productName = $product ? $product->get_name() : __('Unknown Product', 'wc-licensed-product');
|
|
||||||
|
|
||||||
$siteName = get_bloginfo('name');
|
|
||||||
$expiresAt = $license->getExpiresAt();
|
|
||||||
$expirationDate = $expiresAt ? $expiresAt->format(get_option('date_format')) : '';
|
|
||||||
|
|
||||||
// Email subject
|
|
||||||
if ($daysRemaining === 1) {
|
|
||||||
$subject = sprintf(
|
|
||||||
/* translators: 1: Product name, 2: Site name */
|
|
||||||
__('[%2$s] Your license for %1$s expires tomorrow', 'wc-licensed-product'),
|
|
||||||
$productName,
|
|
||||||
$siteName
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$subject = sprintf(
|
|
||||||
/* translators: 1: Product name, 2: Number of days, 3: Site name */
|
|
||||||
__('[%3$s] Your license for %1$s expires in %2$d days', 'wc-licensed-product'),
|
|
||||||
$productName,
|
|
||||||
$daysRemaining,
|
|
||||||
$siteName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email content
|
|
||||||
$message = $this->buildExpirationWarningHtml($license, $customer, $productName, $daysRemaining, $expirationDate);
|
|
||||||
|
|
||||||
// Send email
|
|
||||||
$headers = [
|
|
||||||
'Content-Type: text/html; charset=UTF-8',
|
|
||||||
'From: ' . $siteName . ' <' . get_option('admin_email') . '>',
|
|
||||||
];
|
|
||||||
|
|
||||||
return wp_mail($customer->user_email, $subject, $message, $headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build HTML content for expiration warning email
|
|
||||||
*
|
|
||||||
* @param \Jeremias\WcLicensedProduct\License\License $license License object
|
|
||||||
* @param \WP_User $customer Customer user object
|
|
||||||
* @param string $productName Product name
|
|
||||||
* @param int $daysRemaining Days until expiration
|
|
||||||
* @param string $expirationDate Formatted expiration date
|
|
||||||
* @return string HTML email content
|
|
||||||
*/
|
|
||||||
private function buildExpirationWarningHtml($license, $customer, string $productName, int $daysRemaining, string $expirationDate): string
|
|
||||||
{
|
|
||||||
$siteName = get_bloginfo('name');
|
|
||||||
$siteUrl = home_url();
|
|
||||||
$accountUrl = wc_get_account_endpoint_url('licenses');
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
</head>
|
|
||||||
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
||||||
<div style="background: #f8f9fa; padding: 30px; border-radius: 8px;">
|
|
||||||
<h1 style="color: #333; margin-top: 0; font-size: 24px;">
|
|
||||||
<?php esc_html_e('License Expiration Notice', 'wc-licensed-product'); ?>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p><?php printf(esc_html__('Hello %s,', 'wc-licensed-product'), esc_html($customer->display_name)); ?></p>
|
|
||||||
|
|
||||||
<?php if ($daysRemaining === 1): ?>
|
|
||||||
<p style="color: #dc3545; font-weight: 600;">
|
|
||||||
<?php printf(
|
|
||||||
esc_html__('Your license for %s will expire tomorrow (%s).', 'wc-licensed-product'),
|
|
||||||
esc_html($productName),
|
|
||||||
esc_html($expirationDate)
|
|
||||||
); ?>
|
|
||||||
</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<p style="color: #ffc107; font-weight: 600;">
|
|
||||||
<?php printf(
|
|
||||||
esc_html__('Your license for %1$s will expire in %2$d days (%3$s).', 'wc-licensed-product'),
|
|
||||||
esc_html($productName),
|
|
||||||
$daysRemaining,
|
|
||||||
esc_html($expirationDate)
|
|
||||||
); ?>
|
|
||||||
</p>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div style="background: #fff; padding: 20px; border-radius: 4px; margin: 20px 0; border: 1px solid #e5e5e5;">
|
|
||||||
<h3 style="margin-top: 0; font-size: 16px;"><?php esc_html_e('License Details', 'wc-licensed-product'); ?></h3>
|
|
||||||
<table style="width: 100%; border-collapse: collapse;">
|
|
||||||
<tr>
|
|
||||||
<td style="padding: 8px 0; color: #666;"><?php esc_html_e('Product:', 'wc-licensed-product'); ?></td>
|
|
||||||
<td style="padding: 8px 0; font-weight: 600;"><?php echo esc_html($productName); ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="padding: 8px 0; color: #666;"><?php esc_html_e('License Key:', 'wc-licensed-product'); ?></td>
|
|
||||||
<td style="padding: 8px 0;">
|
|
||||||
<code style="background: #f5f5f5; padding: 3px 8px; border-radius: 3px; font-family: monospace;">
|
|
||||||
<?php echo esc_html($license->getLicenseKey()); ?>
|
|
||||||
</code>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="padding: 8px 0; color: #666;"><?php esc_html_e('Domain:', 'wc-licensed-product'); ?></td>
|
|
||||||
<td style="padding: 8px 0;"><?php echo esc_html($license->getDomain()); ?></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="padding: 8px 0; color: #666;"><?php esc_html_e('Expires:', 'wc-licensed-product'); ?></td>
|
|
||||||
<td style="padding: 8px 0; color: #dc3545; font-weight: 600;"><?php echo esc_html($expirationDate); ?></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p><?php esc_html_e('To continue using this product, please renew your license before the expiration date.', 'wc-licensed-product'); ?></p>
|
|
||||||
|
|
||||||
<p style="margin-top: 25px;">
|
|
||||||
<a href="<?php echo esc_url($accountUrl); ?>"
|
|
||||||
style="display: inline-block; background: #2271b1; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: 600;">
|
|
||||||
<?php esc_html_e('View My Licenses', 'wc-licensed-product'); ?>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<hr style="border: none; border-top: 1px solid #e5e5e5; margin: 30px 0;">
|
|
||||||
|
|
||||||
<p style="font-size: 14px; color: #666; margin-bottom: 0;">
|
|
||||||
<?php printf(
|
|
||||||
esc_html__('This email was sent from %s.', 'wc-licensed-product'),
|
|
||||||
'<a href="' . esc_url($siteUrl) . '" style="color: #2271b1;">' . esc_html($siteName) . '</a>'
|
|
||||||
); ?>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<?php
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add license information to order completed email
|
* Add license information to order completed email
|
||||||
*/
|
*/
|
||||||
@@ -281,7 +200,7 @@ final class LicenseEmailController
|
|||||||
echo "\n" . esc_html__('License Key:', 'wc-licensed-product') . ' ' . esc_html($license->getLicenseKey()) . "\n";
|
echo "\n" . esc_html__('License Key:', 'wc-licensed-product') . ' ' . esc_html($license->getLicenseKey()) . "\n";
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
<div style="margin-top: 10px; padding: 10px; background-color: #f8f9fa; border-left: 3px solid #0073aa;">
|
<div style="margin-top: 10px; padding: 10px; background-color: #f8f9fa; border-left: 3px solid #7f54b3;">
|
||||||
<strong><?php esc_html_e('License Key:', 'wc-licensed-product'); ?></strong>
|
<strong><?php esc_html_e('License Key:', 'wc-licensed-product'); ?></strong>
|
||||||
<code style="display: block; margin-top: 5px; padding: 5px; background: #fff; font-family: monospace;">
|
<code style="display: block; margin-top: 5px; padding: 5px; background: #fff; font-family: monospace;">
|
||||||
<?php echo esc_html($license->getLicenseKey()); ?>
|
<?php echo esc_html($license->getLicenseKey()); ?>
|
||||||
|
|||||||
240
src/Email/LicenseExpirationEmail.php
Normal file
240
src/Email/LicenseExpirationEmail.php
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* License Expiration Warning Email
|
||||||
|
*
|
||||||
|
* @package Jeremias\WcLicensedProduct\Email
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Jeremias\WcLicensedProduct\Email;
|
||||||
|
|
||||||
|
use Jeremias\WcLicensedProduct\License\License;
|
||||||
|
use WC_Email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* License Expiration Warning Email class
|
||||||
|
*
|
||||||
|
* Sends email notifications to customers when their licenses are about to expire.
|
||||||
|
*/
|
||||||
|
class LicenseExpirationEmail extends WC_Email
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* License object
|
||||||
|
*/
|
||||||
|
public ?License $license = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Days remaining until expiration
|
||||||
|
*/
|
||||||
|
public int $days_remaining = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product name
|
||||||
|
*/
|
||||||
|
public string $product_name = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expiration date formatted
|
||||||
|
*/
|
||||||
|
public string $expiration_date = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->id = 'wclp_license_expiration';
|
||||||
|
$this->customer_email = true;
|
||||||
|
$this->title = __('License Expiration Warning', 'wc-licensed-product');
|
||||||
|
$this->description = __('License expiration warning emails are sent to customers when their licenses are about to expire.', 'wc-licensed-product');
|
||||||
|
|
||||||
|
$this->template_html = 'emails/license-expiration.php';
|
||||||
|
$this->template_plain = 'emails/plain/license-expiration.php';
|
||||||
|
$this->template_base = WC_LICENSED_PRODUCT_PLUGIN_DIR . 'templates/';
|
||||||
|
|
||||||
|
$this->placeholders = [
|
||||||
|
'{site_title}' => $this->get_blogname(),
|
||||||
|
'{product_name}' => '',
|
||||||
|
'{days_remaining}' => '',
|
||||||
|
'{expiration_date}' => '',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Call parent constructor
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get email subject
|
||||||
|
*/
|
||||||
|
public function get_default_subject(): string
|
||||||
|
{
|
||||||
|
return __('[{site_title}] Your license for {product_name} expires in {days_remaining} days', 'wc-licensed-product');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get email heading
|
||||||
|
*/
|
||||||
|
public function get_default_heading(): string
|
||||||
|
{
|
||||||
|
return __('License Expiration Notice', 'wc-licensed-product');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger the email
|
||||||
|
*
|
||||||
|
* @param License $license License object
|
||||||
|
* @param int $days_remaining Days until expiration
|
||||||
|
*/
|
||||||
|
public function trigger(License $license, int $days_remaining): bool
|
||||||
|
{
|
||||||
|
$this->setup_locale();
|
||||||
|
|
||||||
|
$customer = get_userdata($license->getCustomerId());
|
||||||
|
if (!$customer || !$customer->user_email) {
|
||||||
|
$this->restore_locale();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->license = $license;
|
||||||
|
$this->days_remaining = $days_remaining;
|
||||||
|
$this->recipient = $customer->user_email;
|
||||||
|
|
||||||
|
$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['{days_remaining}'] = (string) $days_remaining;
|
||||||
|
$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
|
||||||
|
{
|
||||||
|
return wc_get_template_html(
|
||||||
|
$this->template_html,
|
||||||
|
[
|
||||||
|
'license' => $this->license,
|
||||||
|
'days_remaining' => $this->days_remaining,
|
||||||
|
'product_name' => $this->product_name,
|
||||||
|
'expiration_date' => $this->expiration_date,
|
||||||
|
'email_heading' => $this->get_heading(),
|
||||||
|
'additional_content' => $this->get_additional_content(),
|
||||||
|
'sent_to_admin' => false,
|
||||||
|
'plain_text' => false,
|
||||||
|
'email' => $this,
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
$this->template_base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content plain text
|
||||||
|
*/
|
||||||
|
public function get_content_plain(): string
|
||||||
|
{
|
||||||
|
return wc_get_template_html(
|
||||||
|
$this->template_plain,
|
||||||
|
[
|
||||||
|
'license' => $this->license,
|
||||||
|
'days_remaining' => $this->days_remaining,
|
||||||
|
'product_name' => $this->product_name,
|
||||||
|
'expiration_date' => $this->expiration_date,
|
||||||
|
'email_heading' => $this->get_heading(),
|
||||||
|
'additional_content' => $this->get_additional_content(),
|
||||||
|
'sent_to_admin' => false,
|
||||||
|
'plain_text' => true,
|
||||||
|
'email' => $this,
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
$this->template_base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 before the expiration date.', '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}, {days_remaining}, {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,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ namespace Jeremias\WcLicensedProduct\Product;
|
|||||||
|
|
||||||
use Jeremias\WcLicensedProduct\Admin\SettingsController;
|
use Jeremias\WcLicensedProduct\Admin\SettingsController;
|
||||||
use WC_Product;
|
use WC_Product;
|
||||||
|
use Jeremias\WcLicensedProduct\Product\VersionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed Product type extending WooCommerce Product
|
* Licensed Product type extending WooCommerce Product
|
||||||
@@ -121,11 +122,14 @@ class LicensedProduct extends WC_Product
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current software version
|
* Get current software version (derived from latest product version)
|
||||||
*/
|
*/
|
||||||
public function get_current_version(): string
|
public function get_current_version(): string
|
||||||
{
|
{
|
||||||
return $this->get_meta('_licensed_current_version', true) ?: '';
|
$versionManager = new VersionManager();
|
||||||
|
$latestVersion = $versionManager->getLatestVersion($this->get_id());
|
||||||
|
|
||||||
|
return $latestVersion ? $latestVersion->getVersion() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,12 +137,13 @@ class LicensedProduct extends WC_Product
|
|||||||
*/
|
*/
|
||||||
public function get_major_version(): int
|
public function get_major_version(): int
|
||||||
{
|
{
|
||||||
$version = $this->get_current_version();
|
$versionManager = new VersionManager();
|
||||||
if (empty($version)) {
|
$latestVersion = $versionManager->getLatestVersion($this->get_id());
|
||||||
return 1;
|
|
||||||
|
if ($latestVersion) {
|
||||||
|
return $latestVersion->getMajorVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
$parts = explode('.', $version);
|
return 1;
|
||||||
return (int) ($parts[0] ?? 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,14 +164,6 @@ final class LicensedProductType
|
|||||||
'value' => $currentBindToVersion ?: ($defaultBindToVersion ? 'yes' : 'no'),
|
'value' => $currentBindToVersion ?: ($defaultBindToVersion ? 'yes' : 'no'),
|
||||||
'cbvalue' => 'yes',
|
'cbvalue' => 'yes',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
woocommerce_wp_text_input([
|
|
||||||
'id' => '_licensed_current_version',
|
|
||||||
'label' => __('Current Version', 'wc-licensed-product'),
|
|
||||||
'description' => __('Current software version (e.g., 1.0.0)', 'wc-licensed-product'),
|
|
||||||
'desc_tip' => true,
|
|
||||||
'placeholder' => '1.0.0',
|
|
||||||
]);
|
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -223,12 +215,6 @@ final class LicensedProductType
|
|||||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||||
$bindToVersion = isset($_POST['_licensed_bind_to_version']) ? 'yes' : 'no';
|
$bindToVersion = isset($_POST['_licensed_bind_to_version']) ? 'yes' : 'no';
|
||||||
update_post_meta($postId, '_licensed_bind_to_version', $bindToVersion);
|
update_post_meta($postId, '_licensed_bind_to_version', $bindToVersion);
|
||||||
|
|
||||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
|
||||||
$currentVersion = isset($_POST['_licensed_current_version'])
|
|
||||||
? sanitize_text_field($_POST['_licensed_current_version'])
|
|
||||||
: '';
|
|
||||||
update_post_meta($postId, '_licensed_current_version', $currentVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
97
templates/emails/license-expiration.php
Normal file
97
templates/emails/license-expiration.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* License Expiration Warning Email (HTML)
|
||||||
|
*
|
||||||
|
* This template can be overridden by copying it to yourtheme/woocommerce/emails/license-expiration.php.
|
||||||
|
*
|
||||||
|
* @package Jeremias\WcLicensedProduct\Templates\Emails
|
||||||
|
* @version 0.0.8
|
||||||
|
*
|
||||||
|
* @var \Jeremias\WcLicensedProduct\License\License $license
|
||||||
|
* @var int $days_remaining
|
||||||
|
* @var string $product_name
|
||||||
|
* @var string $expiration_date
|
||||||
|
* @var string $email_heading
|
||||||
|
* @var string $additional_content
|
||||||
|
* @var bool $sent_to_admin
|
||||||
|
* @var bool $plain_text
|
||||||
|
* @var \Jeremias\WcLicensedProduct\Email\LicenseExpirationEmail $email
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header
|
||||||
|
*/
|
||||||
|
do_action('woocommerce_email_header', $email_heading, $email);
|
||||||
|
|
||||||
|
$customer = get_userdata($license->getCustomerId());
|
||||||
|
$customer_name = $customer ? $customer->display_name : __('Customer', 'wc-licensed-product');
|
||||||
|
$account_url = wc_get_account_endpoint_url('licenses');
|
||||||
|
?>
|
||||||
|
|
||||||
|
<p><?php printf(esc_html__('Hello %s,', 'wc-licensed-product'), esc_html($customer_name)); ?></p>
|
||||||
|
|
||||||
|
<?php if ($days_remaining === 1) : ?>
|
||||||
|
<p style="color: #dc3545; font-weight: 600;">
|
||||||
|
<?php printf(
|
||||||
|
esc_html__('Your license for %s will expire tomorrow (%s).', 'wc-licensed-product'),
|
||||||
|
esc_html($product_name),
|
||||||
|
esc_html($expiration_date)
|
||||||
|
); ?>
|
||||||
|
</p>
|
||||||
|
<?php else : ?>
|
||||||
|
<p style="color: #856404; font-weight: 600;">
|
||||||
|
<?php printf(
|
||||||
|
esc_html__('Your license for %1$s will expire in %2$d days (%3$s).', 'wc-licensed-product'),
|
||||||
|
esc_html($product_name),
|
||||||
|
$days_remaining,
|
||||||
|
esc_html($expiration_date)
|
||||||
|
); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<h2><?php esc_html_e('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:left;"><?php esc_html_e('Product:', 'wc-licensed-product'); ?></th>
|
||||||
|
<td class="td" style="text-align:left;"><?php echo esc_html($product_name); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="td" scope="row" style="text-align:left;"><?php esc_html_e('License Key:', 'wc-licensed-product'); ?></th>
|
||||||
|
<td class="td" style="text-align:left;">
|
||||||
|
<code style="background: #f5f5f5; padding: 3px 8px; border-radius: 3px; font-family: monospace;">
|
||||||
|
<?php echo esc_html($license->getLicenseKey()); ?>
|
||||||
|
</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="td" scope="row" style="text-align:left;"><?php esc_html_e('Domain:', 'wc-licensed-product'); ?></th>
|
||||||
|
<td class="td" style="text-align:left;"><?php echo esc_html($license->getDomain()); ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="td" scope="row" style="text-align:left;"><?php esc_html_e('Expires:', 'wc-licensed-product'); ?></th>
|
||||||
|
<td class="td" style="text-align:left; color: #dc3545; font-weight: 600;"><?php echo esc_html($expiration_date); ?></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php 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); ?>" style="display: inline-block; background: #7f54b3; color: #fff; 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
|
||||||
|
/*
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer
|
||||||
|
*/
|
||||||
|
do_action('woocommerce_email_footer', $email);
|
||||||
64
templates/emails/plain/license-expiration.php
Normal file
64
templates/emails/plain/license-expiration.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* License Expiration Warning Email (Plain Text)
|
||||||
|
*
|
||||||
|
* This template can be overridden by copying it to yourtheme/woocommerce/emails/plain/license-expiration.php.
|
||||||
|
*
|
||||||
|
* @package Jeremias\WcLicensedProduct\Templates\Emails
|
||||||
|
* @version 0.0.8
|
||||||
|
*
|
||||||
|
* @var \Jeremias\WcLicensedProduct\License\License $license
|
||||||
|
* @var int $days_remaining
|
||||||
|
* @var string $product_name
|
||||||
|
* @var string $expiration_date
|
||||||
|
* @var string $email_heading
|
||||||
|
* @var string $additional_content
|
||||||
|
* @var bool $sent_to_admin
|
||||||
|
* @var bool $plain_text
|
||||||
|
* @var \Jeremias\WcLicensedProduct\Email\LicenseExpirationEmail $email
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
echo esc_html(wp_strip_all_tags($email_heading));
|
||||||
|
echo "\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";
|
||||||
|
|
||||||
|
$customer = get_userdata($license->getCustomerId());
|
||||||
|
$customer_name = $customer ? $customer->display_name : __('Customer', 'wc-licensed-product');
|
||||||
|
|
||||||
|
echo sprintf(esc_html__('Hello %s,', 'wc-licensed-product'), esc_html($customer_name)) . "\n\n";
|
||||||
|
|
||||||
|
if ($days_remaining === 1) {
|
||||||
|
echo sprintf(
|
||||||
|
esc_html__('Your license for %s will expire tomorrow (%s).', 'wc-licensed-product'),
|
||||||
|
esc_html($product_name),
|
||||||
|
esc_html($expiration_date)
|
||||||
|
) . "\n\n";
|
||||||
|
} else {
|
||||||
|
echo sprintf(
|
||||||
|
esc_html__('Your license for %1$s will expire in %2$d days (%3$s).', 'wc-licensed-product'),
|
||||||
|
esc_html($product_name),
|
||||||
|
$days_remaining,
|
||||||
|
esc_html($expiration_date)
|
||||||
|
) . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "----------\n";
|
||||||
|
echo esc_html__('License Details', 'wc-licensed-product') . "\n";
|
||||||
|
echo "----------\n\n";
|
||||||
|
|
||||||
|
echo esc_html__('Product:', 'wc-licensed-product') . ' ' . esc_html($product_name) . "\n";
|
||||||
|
echo esc_html__('License Key:', 'wc-licensed-product') . ' ' . esc_html($license->getLicenseKey()) . "\n";
|
||||||
|
echo esc_html__('Domain:', 'wc-licensed-product') . ' ' . esc_html($license->getDomain()) . "\n";
|
||||||
|
echo esc_html__('Expires:', 'wc-licensed-product') . ' ' . esc_html($expiration_date) . "\n\n";
|
||||||
|
|
||||||
|
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";
|
||||||
@@ -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.0.7
|
* Version: 0.0.8
|
||||||
* 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.0.7');
|
define('WC_LICENSED_PRODUCT_VERSION', '0.0.8');
|
||||||
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__));
|
||||||
|
|||||||
Reference in New Issue
Block a user