2026-01-21 20:32:35 +01:00
< ? php
/**
* Settings Controller
*
* @ package Jeremias\WcLicensedProduct\Admin
*/
declare ( strict_types = 1 );
namespace Jeremias\WcLicensedProduct\Admin ;
2026-01-22 18:32:17 +01:00
use Jeremias\WcLicensedProduct\License\PluginLicenseChecker ;
2026-01-21 20:32:35 +01:00
/**
* Handles WooCommerce settings tab for license defaults
*/
final class SettingsController
{
/**
* Settings option name
*/
public const OPTION_NAME = 'wc_licensed_product_settings' ;
2026-01-22 18:32:17 +01:00
/**
* Tab ID
*/
private const TAB_ID = 'licensed_product' ;
2026-01-21 20:32:35 +01:00
/**
* Constructor
*/
public function __construct ()
{
$this -> registerHooks ();
}
/**
* Register WordPress hooks
*/
private function registerHooks () : void
{
add_filter ( 'woocommerce_settings_tabs_array' , [ $this , 'addSettingsTab' ], 50 );
2026-01-22 18:32:17 +01:00
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' ]);
2026-01-21 20:32:35 +01:00
}
/**
* Add settings tab to WooCommerce settings
*/
public function addSettingsTab ( array $tabs ) : array
{
2026-01-22 18:32:17 +01:00
$tabs [ self :: TAB_ID ] = __ ( 'Licensed Products' , 'wc-licensed-product' );
2026-01-21 20:32:35 +01:00
return $tabs ;
}
/**
2026-01-22 18:32:17 +01:00
* 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
2026-01-21 20:32:35 +01:00
*/
public function getSettingsFields () : array
2026-01-22 18:32:17 +01:00
{
$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
2026-01-21 20:32:35 +01:00
{
return [
'section_title' => [
'name' => __ ( 'Default License Settings' , 'wc-licensed-product' ),
'type' => 'title' ,
'desc' => __ ( 'These settings serve as defaults for new licensed products. Individual product settings override these defaults.' , 'wc-licensed-product' ),
'id' => 'wc_licensed_product_section_defaults' ,
],
'default_max_activations' => [
'name' => __ ( 'Default Max Activations' , 'wc-licensed-product' ),
'type' => 'number' ,
'desc' => __ ( 'Default maximum number of domain activations per license.' , 'wc-licensed-product' ),
'id' => 'wc_licensed_product_default_max_activations' ,
'default' => '1' ,
'custom_attributes' => [
'min' => '1' ,
'step' => '1' ,
],
],
'default_validity_days' => [
'name' => __ ( 'Default License Validity (Days)' , 'wc-licensed-product' ),
'type' => 'number' ,
'desc' => __ ( 'Default number of days a license is valid. Leave empty or set to 0 for lifetime licenses.' , 'wc-licensed-product' ),
'id' => 'wc_licensed_product_default_validity_days' ,
'default' => '' ,
'placeholder' => __ ( 'Lifetime' , 'wc-licensed-product' ),
'custom_attributes' => [
'min' => '0' ,
'step' => '1' ,
],
],
'default_bind_to_version' => [
'name' => __ ( 'Default Bind to Major Version' , 'wc-licensed-product' ),
'type' => 'checkbox' ,
'desc' => __ ( 'If enabled, licenses are bound to the major version at purchase time by default.' , 'wc-licensed-product' ),
'id' => 'wc_licensed_product_default_bind_to_version' ,
'default' => 'no' ,
],
'section_end' => [
'type' => 'sectionend' ,
'id' => 'wc_licensed_product_section_defaults_end' ,
],
2026-01-22 18:32:17 +01:00
];
}
/**
* Get notifications settings
*/
private function getNotificationsSettings () : array
{
return [
2026-01-21 20:55:28 +01:00
'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' ,
],
2026-01-21 20:32:35 +01:00
];
}
/**
* Render settings tab content
*/
public function renderSettingsTab () : void
{
2026-01-22 18:32:17 +01:00
$currentSection = $this -> getCurrentSection ();
// Only show license status on the plugin license section
if ( $currentSection === '' ) {
$this -> renderLicenseStatus ();
}
2026-01-21 20:32:35 +01:00
woocommerce_admin_fields ( $this -> getSettingsFields ());
}
2026-01-22 18:32:17 +01:00
/**
* 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
}
2026-01-21 20:32:35 +01:00
/**
* Save settings
*/
public function saveSettings () : void
{
woocommerce_update_options ( $this -> getSettingsFields ());
}
/**
* Get default max activations
*/
public static function getDefaultMaxActivations () : int
{
$value = get_option ( 'wc_licensed_product_default_max_activations' , 1 );
return max ( 1 , ( int ) $value );
}
/**
* Get default validity days
*/
public static function getDefaultValidityDays () : ? int
{
$value = get_option ( 'wc_licensed_product_default_validity_days' , '' );
if ( $value === '' || $value === '0' ) {
return null ;
}
return ( int ) $value ;
}
/**
* Get default bind to version setting
*/
public static function getDefaultBindToVersion () : bool
{
return get_option ( 'wc_licensed_product_default_bind_to_version' , 'no' ) === 'yes' ;
}
2026-01-21 20:55:28 +01:00
/**
* 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 );
}
2026-01-22 18:32:17 +01:00
/**
* 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 ]);
}
}
2026-01-21 20:32:35 +01:00
}