You've already forked wc-licensed-product
Implement self-licensing (v0.3.0) and settings sub-tabs (v0.3.1)
v0.3.0 - Self-Licensing: - Add PluginLicenseChecker singleton for license validation - Integrate magdev/wc-licensed-product-client library - Add license settings: server URL, key, optional secret - Disable frontend features without valid license (except localhost) - Add license status display with verify button in settings v0.3.1 - Settings UI Improvements: - Reorganize settings page with WooCommerce-style sub-tabs - Split settings into: Plugin License, Default Settings, Notifications - Use PHP 8 match expression for section-specific rendering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Jeremias\WcLicensedProduct\Admin;
|
||||
|
||||
use Jeremias\WcLicensedProduct\License\PluginLicenseChecker;
|
||||
|
||||
/**
|
||||
* Handles WooCommerce settings tab for license defaults
|
||||
*/
|
||||
@@ -19,6 +21,11 @@ final class SettingsController
|
||||
*/
|
||||
public const OPTION_NAME = 'wc_licensed_product_settings';
|
||||
|
||||
/**
|
||||
* Tab ID
|
||||
*/
|
||||
private const TAB_ID = 'licensed_product';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@@ -33,8 +40,10 @@ final class SettingsController
|
||||
private function registerHooks(): void
|
||||
{
|
||||
add_filter('woocommerce_settings_tabs_array', [$this, 'addSettingsTab'], 50);
|
||||
add_action('woocommerce_settings_tabs_licensed_product', [$this, 'renderSettingsTab']);
|
||||
add_action('woocommerce_update_options_licensed_product', [$this, 'saveSettings']);
|
||||
add_action('woocommerce_sections_' . self::TAB_ID, [$this, 'outputSections']);
|
||||
add_action('woocommerce_settings_' . self::TAB_ID, [$this, 'renderSettingsTab']);
|
||||
add_action('woocommerce_update_options_' . self::TAB_ID, [$this, 'saveSettings']);
|
||||
add_action('wp_ajax_wclp_verify_plugin_license', [$this, 'handleVerifyLicense']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,14 +51,119 @@ final class SettingsController
|
||||
*/
|
||||
public function addSettingsTab(array $tabs): array
|
||||
{
|
||||
$tabs['licensed_product'] = __('Licensed Products', 'wc-licensed-product');
|
||||
$tabs[self::TAB_ID] = __('Licensed Products', 'wc-licensed-product');
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings fields
|
||||
* Get available sections
|
||||
*/
|
||||
public function getSections(): array
|
||||
{
|
||||
return [
|
||||
'' => __('Plugin License', 'wc-licensed-product'),
|
||||
'defaults' => __('Default Settings', 'wc-licensed-product'),
|
||||
'notifications' => __('Notifications', 'wc-licensed-product'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current section from URL
|
||||
*/
|
||||
private function getCurrentSection(): string
|
||||
{
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return isset($_GET['section']) ? sanitize_title(wp_unslash($_GET['section'])) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output sections navigation (sub-tabs)
|
||||
*/
|
||||
public function outputSections(): void
|
||||
{
|
||||
$sections = $this->getSections();
|
||||
|
||||
if (empty($sections) || count($sections) <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentSection = $this->getCurrentSection();
|
||||
|
||||
echo '<ul class="subsubsub">';
|
||||
|
||||
$arrayKeys = array_keys($sections);
|
||||
|
||||
foreach ($sections as $id => $label) {
|
||||
$url = admin_url('admin.php?page=wc-settings&tab=' . self::TAB_ID . '§ion=' . sanitize_title($id));
|
||||
$class = ($currentSection === $id) ? 'current' : '';
|
||||
$separator = (end($arrayKeys) === $id) ? '' : ' | ';
|
||||
|
||||
echo '<li><a href="' . esc_url($url) . '" class="' . esc_attr($class) . '">' . esc_html($label) . '</a>' . $separator . '</li>';
|
||||
}
|
||||
|
||||
echo '</ul><br class="clear" />';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings fields for the current section
|
||||
*/
|
||||
public function getSettingsFields(): array
|
||||
{
|
||||
$currentSection = $this->getCurrentSection();
|
||||
|
||||
return match ($currentSection) {
|
||||
'defaults' => $this->getDefaultsSettings(),
|
||||
'notifications' => $this->getNotificationsSettings(),
|
||||
default => $this->getPluginLicenseSettings(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin license settings (default section)
|
||||
*/
|
||||
private function getPluginLicenseSettings(): array
|
||||
{
|
||||
return [
|
||||
'plugin_license_section_title' => [
|
||||
'name' => __('Plugin License', 'wc-licensed-product'),
|
||||
'type' => 'title',
|
||||
'desc' => __('Configure the license for this plugin. A valid license is required for frontend features to work.', 'wc-licensed-product'),
|
||||
'id' => 'wc_licensed_product_section_plugin_license',
|
||||
],
|
||||
'plugin_license_server_url' => [
|
||||
'name' => __('License Server URL', 'wc-licensed-product'),
|
||||
'type' => 'url',
|
||||
'desc' => __('The URL of the license server (e.g., https://shop.example.com).', 'wc-licensed-product'),
|
||||
'id' => 'wc_licensed_product_plugin_license_server_url',
|
||||
'default' => '',
|
||||
'placeholder' => 'https://shop.example.com',
|
||||
],
|
||||
'plugin_license_key' => [
|
||||
'name' => __('License Key', 'wc-licensed-product'),
|
||||
'type' => 'text',
|
||||
'desc' => __('Your license key in XXXX-XXXX-XXXX-XXXX format.', 'wc-licensed-product'),
|
||||
'id' => 'wc_licensed_product_plugin_license_key',
|
||||
'default' => '',
|
||||
'placeholder' => 'XXXX-XXXX-XXXX-XXXX',
|
||||
],
|
||||
'plugin_license_server_secret' => [
|
||||
'name' => __('Server Secret (Optional)', 'wc-licensed-product'),
|
||||
'type' => 'password',
|
||||
'desc' => __('If the license server uses signed responses, enter the shared secret here for enhanced security.', 'wc-licensed-product'),
|
||||
'id' => 'wc_licensed_product_plugin_license_server_secret',
|
||||
'default' => '',
|
||||
],
|
||||
'plugin_license_section_end' => [
|
||||
'type' => 'sectionend',
|
||||
'id' => 'wc_licensed_product_section_plugin_license_end',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default license settings
|
||||
*/
|
||||
private function getDefaultsSettings(): array
|
||||
{
|
||||
return [
|
||||
'section_title' => [
|
||||
@@ -92,7 +206,15 @@ final class SettingsController
|
||||
'type' => 'sectionend',
|
||||
'id' => 'wc_licensed_product_section_defaults_end',
|
||||
],
|
||||
// Email settings section
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notifications settings
|
||||
*/
|
||||
private function getNotificationsSettings(): array
|
||||
{
|
||||
return [
|
||||
'email_section_title' => [
|
||||
'name' => __('Expiration Warning Schedule', 'wc-licensed-product'),
|
||||
'type' => 'title',
|
||||
@@ -138,9 +260,96 @@ final class SettingsController
|
||||
*/
|
||||
public function renderSettingsTab(): void
|
||||
{
|
||||
$currentSection = $this->getCurrentSection();
|
||||
|
||||
// Only show license status on the plugin license section
|
||||
if ($currentSection === '') {
|
||||
$this->renderLicenseStatus();
|
||||
}
|
||||
|
||||
woocommerce_admin_fields($this->getSettingsFields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Render license status notice
|
||||
*/
|
||||
private function renderLicenseStatus(): void
|
||||
{
|
||||
$checker = PluginLicenseChecker::getInstance();
|
||||
|
||||
if ($checker->isLocalhost()) {
|
||||
echo '<div class="notice notice-info inline"><p>';
|
||||
echo '<span class="dashicons dashicons-info" style="color: #00a0d2;"></span> ';
|
||||
echo esc_html__('Running on localhost - license validation bypassed.', 'wc-licensed-product');
|
||||
echo '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
if ($checker->isLicenseValid()) {
|
||||
echo '<div class="notice notice-success inline"><p>';
|
||||
echo '<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> ';
|
||||
echo esc_html__('License is valid and active.', 'wc-licensed-product');
|
||||
echo '</p></div>';
|
||||
} else {
|
||||
$error = $checker->getLastError();
|
||||
echo '<div class="notice notice-error inline"><p>';
|
||||
echo '<span class="dashicons dashicons-warning" style="color: #dc3232;"></span> ';
|
||||
echo esc_html__('License is not valid. Frontend features are disabled.', 'wc-licensed-product');
|
||||
if ($error) {
|
||||
echo '<br><small>' . esc_html($error) . '</small>';
|
||||
}
|
||||
echo '</p></div>';
|
||||
}
|
||||
|
||||
// Add verify button
|
||||
$nonce = wp_create_nonce('wclp_verify_license');
|
||||
echo '<p>';
|
||||
echo '<button type="button" class="button" id="wclp-verify-license" data-nonce="' . esc_attr($nonce) . '">';
|
||||
echo esc_html__('Verify License', 'wc-licensed-product');
|
||||
echo '</button>';
|
||||
echo '<span id="wclp-verify-result" style="margin-left: 10px;"></span>';
|
||||
echo '</p>';
|
||||
|
||||
// Inline script for verify button
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
jQuery(function($) {
|
||||
$('#wclp-verify-license').on('click', function() {
|
||||
var $btn = $(this);
|
||||
var $result = $('#wclp-verify-result');
|
||||
var nonce = $btn.data('nonce');
|
||||
|
||||
$btn.prop('disabled', true).text('<?php echo esc_js(__('Verifying...', 'wc-licensed-product')); ?>');
|
||||
$result.text('');
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wclp_verify_plugin_license',
|
||||
nonce: nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$result.html('<span style="color: #46b450;">' + response.data.message + '</span>');
|
||||
location.reload();
|
||||
} else {
|
||||
$result.html('<span style="color: #dc3232;">' + response.data.message + '</span>');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$result.html('<span style="color: #dc3232;"><?php echo esc_js(__('Request failed.', 'wc-licensed-product')); ?></span>');
|
||||
},
|
||||
complete: function() {
|
||||
$btn.prop('disabled', false).text('<?php echo esc_js(__('Verify License', 'wc-licensed-product')); ?>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings
|
||||
*/
|
||||
@@ -210,4 +419,55 @@ final class SettingsController
|
||||
$value = get_option('wc_licensed_product_expiration_warning_days_second', 1);
|
||||
return max(0, (int) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin license server URL
|
||||
*/
|
||||
public static function getPluginLicenseServerUrl(): string
|
||||
{
|
||||
return (string) get_option('wc_licensed_product_plugin_license_server_url', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin license key
|
||||
*/
|
||||
public static function getPluginLicenseKey(): string
|
||||
{
|
||||
return (string) get_option('wc_licensed_product_plugin_license_key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin license server secret
|
||||
*/
|
||||
public static function getPluginLicenseServerSecret(): ?string
|
||||
{
|
||||
$secret = get_option('wc_licensed_product_plugin_license_server_secret', '');
|
||||
return !empty($secret) ? (string) $secret : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX verify license request
|
||||
*/
|
||||
public function handleVerifyLicense(): void
|
||||
{
|
||||
if (!check_ajax_referer('wclp_verify_license', 'nonce', false)) {
|
||||
wp_send_json_error(['message' => __('Security check failed.', 'wc-licensed-product')], 403);
|
||||
}
|
||||
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_send_json_error(['message' => __('Insufficient permissions.', 'wc-licensed-product')], 403);
|
||||
}
|
||||
|
||||
$checker = PluginLicenseChecker::getInstance();
|
||||
$checker->clearCache();
|
||||
|
||||
$valid = $checker->validateLicense(true);
|
||||
|
||||
if ($valid) {
|
||||
wp_send_json_success(['message' => __('License verified successfully!', 'wc-licensed-product')]);
|
||||
} else {
|
||||
$error = $checker->getLastError() ?: __('License validation failed.', 'wc-licensed-product');
|
||||
wp_send_json_error(['message' => $error]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
287
src/License/PluginLicenseChecker.php
Normal file
287
src/License/PluginLicenseChecker.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin License Checker
|
||||
*
|
||||
* Validates the plugin's own license against a remote server.
|
||||
*
|
||||
* @package Jeremias\WcLicensedProduct\License
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Jeremias\WcLicensedProduct\License;
|
||||
|
||||
use Magdev\WcLicensedProductClient\LicenseClient;
|
||||
use Magdev\WcLicensedProductClient\LicenseClientInterface;
|
||||
use Magdev\WcLicensedProductClient\SecureLicenseClient;
|
||||
use Magdev\WcLicensedProductClient\Exception\LicenseException;
|
||||
use Symfony\Component\HttpClient\HttpClient;
|
||||
|
||||
/**
|
||||
* Handles validation of this plugin's license
|
||||
*/
|
||||
final class PluginLicenseChecker
|
||||
{
|
||||
/**
|
||||
* Cache key for license validation result
|
||||
*/
|
||||
private const CACHE_KEY = 'wclp_plugin_license_valid';
|
||||
|
||||
/**
|
||||
* Cache TTL for successful validation (1 hour)
|
||||
*/
|
||||
private const CACHE_TTL = 3600;
|
||||
|
||||
/**
|
||||
* Cache key for error messages
|
||||
*/
|
||||
private const ERROR_CACHE_KEY = 'wclp_plugin_license_error';
|
||||
|
||||
/**
|
||||
* Cache TTL for errors (5 minutes)
|
||||
*/
|
||||
private const ERROR_CACHE_TTL = 300;
|
||||
|
||||
/**
|
||||
* Singleton instance
|
||||
*/
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* Cached localhost check result
|
||||
*/
|
||||
private ?bool $isLocalhostCached = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*/
|
||||
public static function getInstance(): self
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for singleton
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
// Private constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin license is valid
|
||||
*
|
||||
* Returns cached result if available, otherwise validates against server.
|
||||
*/
|
||||
public function isLicenseValid(): bool
|
||||
{
|
||||
// Always valid on localhost
|
||||
if ($this->isLocalhost()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
$cached = get_transient(self::CACHE_KEY);
|
||||
if ($cached !== false) {
|
||||
return (bool) $cached;
|
||||
}
|
||||
|
||||
// Validate against server
|
||||
return $this->validateLicense();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate license against the server
|
||||
*
|
||||
* @param bool $forceRefresh Force refresh even if cached
|
||||
* @return bool True if license is valid
|
||||
*/
|
||||
public function validateLicense(bool $forceRefresh = false): bool
|
||||
{
|
||||
// Always valid on localhost
|
||||
if ($this->isLocalhost()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check settings are configured
|
||||
$serverUrl = $this->getLicenseServerUrl();
|
||||
$licenseKey = $this->getLicenseKey();
|
||||
|
||||
if (empty($serverUrl) || empty($licenseKey)) {
|
||||
set_transient(
|
||||
self::ERROR_CACHE_KEY,
|
||||
__('License settings not configured.', 'wc-licensed-product'),
|
||||
self::ERROR_CACHE_TTL
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check cache unless force refresh
|
||||
if (!$forceRefresh) {
|
||||
$cached = get_transient(self::CACHE_KEY);
|
||||
if ($cached !== false) {
|
||||
return (bool) $cached;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->createLicenseClient();
|
||||
$domain = $this->getCurrentDomain();
|
||||
|
||||
// Validate the license
|
||||
$client->validate($licenseKey, $domain);
|
||||
|
||||
// Valid license - cache success
|
||||
set_transient(self::CACHE_KEY, 1, self::CACHE_TTL);
|
||||
delete_transient(self::ERROR_CACHE_KEY);
|
||||
|
||||
return true;
|
||||
} catch (LicenseException $e) {
|
||||
// License-specific error (invalid, expired, revoked, etc.)
|
||||
set_transient(self::CACHE_KEY, 0, self::CACHE_TTL);
|
||||
set_transient(self::ERROR_CACHE_KEY, $e->getMessage(), self::ERROR_CACHE_TTL);
|
||||
|
||||
return false;
|
||||
} catch (\Throwable $e) {
|
||||
// Network/server error - use shorter cache to allow retry
|
||||
set_transient(
|
||||
self::ERROR_CACHE_KEY,
|
||||
__('Could not connect to license server.', 'wc-licensed-product') . ' ' . $e->getMessage(),
|
||||
self::ERROR_CACHE_TTL
|
||||
);
|
||||
|
||||
// Don't cache validation failure on network errors - allow retry on next page load
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last error message
|
||||
*/
|
||||
public function getLastError(): ?string
|
||||
{
|
||||
$error = get_transient(self::ERROR_CACHE_KEY);
|
||||
return $error !== false ? (string) $error : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the validation cache
|
||||
*/
|
||||
public function clearCache(): void
|
||||
{
|
||||
delete_transient(self::CACHE_KEY);
|
||||
delete_transient(self::ERROR_CACHE_KEY);
|
||||
$this->isLocalhostCached = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if running on localhost
|
||||
*
|
||||
* Matches localhost, 127.0.0.1, ::1, and any port number.
|
||||
*/
|
||||
public function isLocalhost(): bool
|
||||
{
|
||||
if ($this->isLocalhostCached !== null) {
|
||||
return $this->isLocalhostCached;
|
||||
}
|
||||
|
||||
$domain = $this->getCurrentDomain();
|
||||
|
||||
// Remove port number if present
|
||||
$domainWithoutPort = preg_replace('/:[\d]+$/', '', $domain);
|
||||
|
||||
// Check for localhost variants
|
||||
$localhostNames = ['localhost', '127.0.0.1', '::1'];
|
||||
|
||||
if (in_array($domainWithoutPort, $localhostNames, true)) {
|
||||
$this->isLocalhostCached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for .localhost and .local subdomains
|
||||
if (
|
||||
str_ends_with($domainWithoutPort, '.localhost') ||
|
||||
str_ends_with($domainWithoutPort, '.local')
|
||||
) {
|
||||
$this->isLocalhostCached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->isLocalhostCached = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current domain from the site URL
|
||||
*/
|
||||
private function getCurrentDomain(): string
|
||||
{
|
||||
$siteUrl = get_site_url();
|
||||
$parsed = parse_url($siteUrl);
|
||||
$host = $parsed['host'] ?? 'localhost';
|
||||
|
||||
// Include port if non-standard
|
||||
if (isset($parsed['port'])) {
|
||||
$host .= ':' . $parsed['port'];
|
||||
}
|
||||
|
||||
return strtolower($host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the license server URL from settings
|
||||
*/
|
||||
private function getLicenseServerUrl(): string
|
||||
{
|
||||
return (string) get_option('wc_licensed_product_plugin_license_server_url', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the license key from settings
|
||||
*/
|
||||
private function getLicenseKey(): string
|
||||
{
|
||||
return (string) get_option('wc_licensed_product_plugin_license_key', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server secret from settings (optional)
|
||||
*/
|
||||
private function getServerSecret(): ?string
|
||||
{
|
||||
$secret = get_option('wc_licensed_product_plugin_license_server_secret', '');
|
||||
return !empty($secret) ? (string) $secret : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the license client instance
|
||||
*/
|
||||
private function createLicenseClient(): LicenseClientInterface
|
||||
{
|
||||
$httpClient = HttpClient::create([
|
||||
'timeout' => 10,
|
||||
'verify_peer' => true,
|
||||
]);
|
||||
|
||||
$serverUrl = $this->getLicenseServerUrl();
|
||||
$serverSecret = $this->getServerSecret();
|
||||
|
||||
// Use secure client if server secret is configured
|
||||
if ($serverSecret !== null) {
|
||||
return new SecureLicenseClient(
|
||||
httpClient: $httpClient,
|
||||
baseUrl: $serverUrl,
|
||||
serverSecret: $serverSecret,
|
||||
);
|
||||
}
|
||||
|
||||
return new LicenseClient(
|
||||
httpClient: $httpClient,
|
||||
baseUrl: $serverUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ use Jeremias\WcLicensedProduct\Email\LicenseEmailController;
|
||||
use Jeremias\WcLicensedProduct\Frontend\AccountController;
|
||||
use Jeremias\WcLicensedProduct\Frontend\DownloadController;
|
||||
use Jeremias\WcLicensedProduct\License\LicenseManager;
|
||||
use Jeremias\WcLicensedProduct\License\PluginLicenseChecker;
|
||||
use Jeremias\WcLicensedProduct\Product\LicensedProductType;
|
||||
use Jeremias\WcLicensedProduct\Product\VersionManager;
|
||||
use Twig\Environment;
|
||||
@@ -119,13 +120,23 @@ final class Plugin
|
||||
$this->licenseManager = new LicenseManager();
|
||||
$this->versionManager = new VersionManager();
|
||||
|
||||
// Initialize controllers
|
||||
// Check plugin license
|
||||
$licenseChecker = PluginLicenseChecker::getInstance();
|
||||
$isLicensed = $licenseChecker->isLicenseValid();
|
||||
|
||||
// Always initialize product type (needed for existing orders)
|
||||
new LicensedProductType();
|
||||
new CheckoutController($this->licenseManager);
|
||||
new StoreApiExtension($this->licenseManager);
|
||||
$this->registerCheckoutBlocksIntegration();
|
||||
$this->downloadController = new DownloadController($this->licenseManager, $this->versionManager);
|
||||
new AccountController($this->twig, $this->licenseManager, $this->versionManager, $this->downloadController);
|
||||
|
||||
// Only initialize frontend components if licensed or on localhost
|
||||
if ($isLicensed) {
|
||||
new CheckoutController($this->licenseManager);
|
||||
new StoreApiExtension($this->licenseManager);
|
||||
$this->registerCheckoutBlocksIntegration();
|
||||
$this->downloadController = new DownloadController($this->licenseManager, $this->versionManager);
|
||||
new AccountController($this->twig, $this->licenseManager, $this->versionManager, $this->downloadController);
|
||||
}
|
||||
|
||||
// Always initialize REST API and email controller
|
||||
new RestApiController($this->licenseManager);
|
||||
new LicenseEmailController($this->licenseManager);
|
||||
|
||||
@@ -134,11 +145,17 @@ final class Plugin
|
||||
(new ResponseSigner())->register();
|
||||
}
|
||||
|
||||
// Admin always available
|
||||
if (is_admin()) {
|
||||
new AdminController($this->twig, $this->licenseManager);
|
||||
new VersionAdminController($this->versionManager);
|
||||
new OrderLicenseController($this->licenseManager);
|
||||
new SettingsController();
|
||||
|
||||
// Show admin notice if unlicensed and not on localhost
|
||||
if (!$isLicensed && !$licenseChecker->isLocalhost()) {
|
||||
add_action('admin_notices', [$this, 'showUnlicensedNotice']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +181,16 @@ final class Plugin
|
||||
*/
|
||||
private function registerHooks(): void
|
||||
{
|
||||
// Generate license on order completion (multiple hooks for compatibility)
|
||||
add_action('woocommerce_order_status_completed', [$this, 'onOrderCompleted']);
|
||||
add_action('woocommerce_order_status_processing', [$this, 'onOrderCompleted']);
|
||||
// Only register order hooks if licensed (license generation requires valid license)
|
||||
$licenseChecker = PluginLicenseChecker::getInstance();
|
||||
if ($licenseChecker->isLicenseValid()) {
|
||||
// Generate license on order completion (multiple hooks for compatibility)
|
||||
add_action('woocommerce_order_status_completed', [$this, 'onOrderCompleted']);
|
||||
add_action('woocommerce_order_status_processing', [$this, 'onOrderCompleted']);
|
||||
|
||||
// Also hook into payment complete for immediate license generation
|
||||
add_action('woocommerce_payment_complete', [$this, 'onOrderCompleted']);
|
||||
// Also hook into payment complete for immediate license generation
|
||||
add_action('woocommerce_payment_complete', [$this, 'onOrderCompleted']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,4 +242,29 @@ final class Plugin
|
||||
{
|
||||
return $this->twig->render($template, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show admin notice when plugin is unlicensed
|
||||
*/
|
||||
public function showUnlicensedNotice(): void
|
||||
{
|
||||
$settingsUrl = admin_url('admin.php?page=wc-settings&tab=licensed_product');
|
||||
?>
|
||||
<div class="notice notice-warning is-dismissible">
|
||||
<p>
|
||||
<strong><?php esc_html_e('WC Licensed Product', 'wc-licensed-product'); ?>:</strong>
|
||||
<?php esc_html_e('Plugin license is not configured or invalid. Frontend features are disabled.', 'wc-licensed-product'); ?>
|
||||
<a href="<?php echo esc_url($settingsUrl); ?>"><?php esc_html_e('Configure License', 'wc-licensed-product'); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin license checker instance
|
||||
*/
|
||||
public function getLicenseChecker(): PluginLicenseChecker
|
||||
{
|
||||
return PluginLicenseChecker::getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user