You've already forked wc-licensed-product
- Add multi-domain checkout support for WooCommerce Blocks - Fix domain field rendering using ExperimentalOrderMeta slot - Add DOM injection fallback for checkout field rendering - Update translations with new multi-domain strings (de_CH) - Update email templates for grouped license display - Refactor account page to group licenses by product/order Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
361 lines
14 KiB
PHP
361 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* License Email Controller
|
|
*
|
|
* @package Jeremias\WcLicensedProduct\Email
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Jeremias\WcLicensedProduct\Email;
|
|
|
|
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
|
use Jeremias\WcLicensedProduct\Admin\SettingsController;
|
|
|
|
/**
|
|
* Handles email notifications for licenses
|
|
*/
|
|
final class LicenseEmailController
|
|
{
|
|
private LicenseManager $licenseManager;
|
|
|
|
public function __construct(LicenseManager $licenseManager)
|
|
{
|
|
$this->licenseManager = $licenseManager;
|
|
$this->registerHooks();
|
|
}
|
|
|
|
/**
|
|
* Register WordPress hooks
|
|
*/
|
|
private function registerHooks(): void
|
|
{
|
|
// Register custom WooCommerce email classes
|
|
add_filter('woocommerce_email_classes', [$this, 'registerEmailClasses']);
|
|
|
|
// Add license info to order completed email
|
|
add_action('woocommerce_email_after_order_table', [$this, 'addLicenseInfoToEmail'], 20, 4);
|
|
|
|
// Add license info to order details in emails
|
|
add_action('woocommerce_order_item_meta_end', [$this, 'addLicenseToOrderItem'], 10, 4);
|
|
|
|
// Schedule cron job for expiration warnings
|
|
add_action('init', [$this, 'scheduleExpirationCheck']);
|
|
|
|
// Cron action for checking expiring licenses
|
|
add_action('wclp_check_expiring_licenses', [$this, 'sendExpirationWarnings']);
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
$email_classes['WCLP_License_Expired'] = new LicenseExpiredEmail();
|
|
return $email_classes;
|
|
}
|
|
|
|
/**
|
|
* Schedule the expiration check cron job
|
|
*/
|
|
public function scheduleExpirationCheck(): void
|
|
{
|
|
if (!wp_next_scheduled('wclp_check_expiring_licenses')) {
|
|
wp_schedule_event(time(), 'daily', 'wclp_check_expiring_licenses');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send expiration warning emails and auto-expire licenses
|
|
*/
|
|
public function sendExpirationWarnings(): void
|
|
{
|
|
// First, auto-expire licenses that have passed their expiration date
|
|
$this->autoExpireAndNotify();
|
|
|
|
// Check if expiration emails are enabled in settings
|
|
if (!SettingsController::isExpirationEmailsEnabled()) {
|
|
return;
|
|
}
|
|
|
|
// Get the WooCommerce email instance
|
|
$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');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @param LicenseExpirationEmail $email Email instance
|
|
* @param int $days Days until expiration
|
|
* @param string $notificationType Notification type identifier
|
|
*/
|
|
private function processExpirationWarnings(LicenseExpirationEmail $email, int $days, string $notificationType): void
|
|
{
|
|
$licenses = $this->licenseManager->getLicensesExpiringSoon($days);
|
|
|
|
foreach ($licenses as $license) {
|
|
// Skip if already notified
|
|
if ($this->licenseManager->wasExpirationNotified($license->getId(), $notificationType)) {
|
|
continue;
|
|
}
|
|
|
|
// Send the warning email using WooCommerce email system
|
|
if ($email->trigger($license, $days)) {
|
|
// Mark as notified
|
|
$this->licenseManager->markExpirationNotified($license->getId(), $notificationType);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add license information to order completed email
|
|
*/
|
|
public function addLicenseInfoToEmail(\WC_Order $order, bool $sentToAdmin, bool $plainText, $email): void
|
|
{
|
|
// Only add to completed order email sent to customer
|
|
if ($sentToAdmin || !$email || $email->id !== 'customer_completed_order') {
|
|
return;
|
|
}
|
|
|
|
$licenses = $this->getLicensesForOrder($order);
|
|
if (empty($licenses)) {
|
|
return;
|
|
}
|
|
|
|
if ($plainText) {
|
|
$this->renderPlainTextLicenseInfo($licenses, $order);
|
|
} else {
|
|
$this->renderHtmlLicenseInfo($licenses, $order);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add license key(s) to order item in email
|
|
*/
|
|
public function addLicenseToOrderItem(int $itemId, \WC_Order_Item $item, \WC_Order $order, bool $plainText): void
|
|
{
|
|
$product = $item->get_product();
|
|
if (!$product || !$product->is_type('licensed')) {
|
|
return;
|
|
}
|
|
|
|
$licenses = $this->licenseManager->getLicensesByOrderAndProduct($order->get_id(), $product->get_id());
|
|
if (empty($licenses)) {
|
|
return;
|
|
}
|
|
|
|
if ($plainText) {
|
|
echo "\n" . esc_html__('License Keys:', 'wc-licensed-product') . "\n";
|
|
foreach ($licenses as $license) {
|
|
echo ' - ' . esc_html($license->getLicenseKey());
|
|
echo ' (' . esc_html($license->getDomain()) . ')' . "\n";
|
|
}
|
|
} else {
|
|
?>
|
|
<div style="margin-top: 10px; padding: 10px; background-color: #f8f9fa; border-left: 3px solid #7f54b3;">
|
|
<strong><?php esc_html_e('License Keys:', 'wc-licensed-product'); ?></strong>
|
|
<?php foreach ($licenses as $license) : ?>
|
|
<div style="margin-top: 5px; padding: 5px; background: #fff;">
|
|
<code style="font-family: monospace;">
|
|
<?php echo esc_html($license->getLicenseKey()); ?>
|
|
</code>
|
|
<span style="color: #666; margin-left: 10px;">
|
|
<?php echo esc_html($license->getDomain()); ?>
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all licenses for an order grouped by product
|
|
*
|
|
* @return array Array of products with their licenses
|
|
*/
|
|
private function getLicensesForOrder(\WC_Order $order): array
|
|
{
|
|
$products = [];
|
|
|
|
foreach ($order->get_items() as $item) {
|
|
$product = $item->get_product();
|
|
if ($product && $product->is_type('licensed')) {
|
|
$licenses = $this->licenseManager->getLicensesByOrderAndProduct($order->get_id(), $product->get_id());
|
|
if (!empty($licenses)) {
|
|
$products[] = [
|
|
'product_name' => $product->get_name(),
|
|
'licenses' => $licenses,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $products;
|
|
}
|
|
|
|
/**
|
|
* Render license info in HTML format
|
|
*/
|
|
private function renderHtmlLicenseInfo(array $products, \WC_Order $order): void
|
|
{
|
|
?>
|
|
<div style="margin: 20px 0; padding: 20px; background-color: #f8f9fa; border: 1px solid #e5e5e5; border-radius: 4px;">
|
|
<h2 style="margin-top: 0; color: #333;"><?php esc_html_e('Your License Keys', 'wc-licensed-product'); ?></h2>
|
|
|
|
<?php foreach ($products as $product) : ?>
|
|
<div style="margin-bottom: 20px;">
|
|
<h3 style="margin: 0 0 10px 0; font-size: 1.1em; color: #333;">
|
|
<?php echo esc_html($product['product_name']); ?>
|
|
<span style="font-weight: normal; color: #666; font-size: 0.9em;">
|
|
(<?php
|
|
printf(
|
|
esc_html(_n('%d license', '%d licenses', count($product['licenses']), 'wc-licensed-product')),
|
|
count($product['licenses'])
|
|
);
|
|
?>)
|
|
</span>
|
|
</h3>
|
|
|
|
<table style="width: 100%; border-collapse: collapse; background: #fff;">
|
|
<thead>
|
|
<tr>
|
|
<th style="text-align: left; padding: 8px 10px; border-bottom: 2px solid #ddd; font-size: 0.9em;"><?php esc_html_e('License Key', 'wc-licensed-product'); ?></th>
|
|
<th style="text-align: left; padding: 8px 10px; border-bottom: 2px solid #ddd; font-size: 0.9em;"><?php esc_html_e('Domain', 'wc-licensed-product'); ?></th>
|
|
<th style="text-align: left; padding: 8px 10px; border-bottom: 2px solid #ddd; font-size: 0.9em;"><?php esc_html_e('Expires', 'wc-licensed-product'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($product['licenses'] as $license) : ?>
|
|
<tr>
|
|
<td style="padding: 8px 10px; border-bottom: 1px solid #eee;">
|
|
<code style="background: #f5f5f5; padding: 3px 6px; font-family: monospace; font-size: 0.9em;">
|
|
<?php echo esc_html($license->getLicenseKey()); ?>
|
|
</code>
|
|
</td>
|
|
<td style="padding: 8px 10px; border-bottom: 1px solid #eee;">
|
|
<?php echo esc_html($license->getDomain()); ?>
|
|
</td>
|
|
<td style="padding: 8px 10px; border-bottom: 1px solid #eee;">
|
|
<?php
|
|
$expiresAt = $license->getExpiresAt();
|
|
echo $expiresAt
|
|
? esc_html($expiresAt->format(get_option('date_format')))
|
|
: esc_html__('Never', 'wc-licensed-product');
|
|
?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
|
|
<p style="margin-top: 15px; margin-bottom: 0; font-size: 0.9em; color: #666;">
|
|
<?php esc_html_e('You can also view your licenses in your account under "Licenses".', 'wc-licensed-product'); ?>
|
|
</p>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render license info in plain text format
|
|
*/
|
|
private function renderPlainTextLicenseInfo(array $products, \WC_Order $order): void
|
|
{
|
|
echo "\n\n";
|
|
echo "==========================================================\n";
|
|
echo esc_html__('YOUR LICENSE KEYS', 'wc-licensed-product') . "\n";
|
|
echo "==========================================================\n\n";
|
|
|
|
foreach ($products as $product) {
|
|
echo esc_html($product['product_name']);
|
|
echo ' (' . count($product['licenses']) . ' ' .
|
|
_n('license', 'licenses', count($product['licenses']), 'wc-licensed-product') . ')';
|
|
echo "\n";
|
|
echo "-----------------------------------------------------------\n";
|
|
|
|
foreach ($product['licenses'] as $license) {
|
|
echo esc_html__('License Key:', 'wc-licensed-product') . ' ';
|
|
echo esc_html($license->getLicenseKey()) . "\n";
|
|
echo esc_html__('Domain:', 'wc-licensed-product') . ' ';
|
|
echo esc_html($license->getDomain()) . "\n";
|
|
echo esc_html__('Expires:', 'wc-licensed-product') . ' ';
|
|
|
|
$expiresAt = $license->getExpiresAt();
|
|
echo $expiresAt
|
|
? esc_html($expiresAt->format(get_option('date_format')))
|
|
: esc_html__('Never', 'wc-licensed-product');
|
|
echo "\n\n";
|
|
}
|
|
}
|
|
|
|
echo esc_html__('You can also view your licenses in your account under "Licenses".', 'wc-licensed-product') . "\n";
|
|
echo "==========================================================\n\n";
|
|
}
|
|
}
|