| 1 |
<?php |
| 2 |
|
| 3 |
namespace YayMailScoped\YayCommerce\AdminShell\License; |
| 4 |
|
| 5 |
use YayMailScoped\YayCommerce\AdminShell\License\Contracts\LicenseConfigAdapter; |
| 6 |
use YayMailScoped\YayCommerce\AdminShell\Support\Slug; |
| 7 |
defined('ABSPATH') || exit; |
| 8 |
/** |
| 9 |
* License activation/deactivation/check state machine. |
| 10 |
* |
| 11 |
* NOTE: This file exceeds 200 LOC because it aggregates all WordPress admin |
| 12 |
* hook wiring (notices, cron, auto-update, plugin-row notifications) into one |
| 13 |
* class — splitting would require injecting WP globals and create more |
| 14 |
* indirection without clarity gain. Acceptable exception per code standards. |
| 15 |
* |
| 16 |
* |
| 17 |
* Ported from YayMail Pro LicenseHandler.php — all YAYMAIL_* constants and |
| 18 |
* CorePlugin::get() calls replaced with $this->adapter->get_*() methods. |
| 19 |
* The hardcoded yaycommerce_licensing_plugins filter registration at priority 100 |
| 20 |
* is preserved for backwards compat — new code uses LicenseRegistry instead. |
| 21 |
* |
| 22 |
* NOTE: The original LicenseHandler used CorePlugin::get() for item_id/slug/etc. |
| 23 |
* Here those values come from the injected LicenseConfigAdapter. Port is faithful; |
| 24 |
* any oddities from source preserved with // TODO: investigate comments. |
| 25 |
*/ |
| 26 |
class LicenseHandler |
| 27 |
{ |
| 28 |
protected LicenseConfigAdapter $adapter; |
| 29 |
protected License $license; |
| 30 |
public function __construct(LicenseConfigAdapter $adapter) |
| 31 |
{ |
| 32 |
$this->adapter = $adapter; |
| 33 |
$this->license = new License($adapter); |
| 34 |
new RestAPI($adapter); |
| 35 |
if (is_admin()) { |
| 36 |
$this->do_hooks(); |
| 37 |
$this->do_cron_job(); |
| 38 |
$this->do_post_requests(); |
| 39 |
$this->show_plugin_page_notification(); |
| 40 |
} |
| 41 |
} |
| 42 |
public function do_hooks(): void |
| 43 |
{ |
| 44 |
add_action('admin_notices', [$this, 'not_activate_license_notice']); |
| 45 |
add_action('admin_enqueue_scripts', [$this, 'enqueue_license_scripts']); |
| 46 |
add_action('yaycommerce_licenses_page', [$this, 'render_license_settings'], 100); |
| 47 |
// Legacy filter — new code uses LicenseRegistry, but this keeps |
| 48 |
// backward compat so old Licenses page views still work during transition. |
| 49 |
add_filter('yaycommerce_licensing_plugins', [$this, 'register_licensing_plugins'], 100); |
| 50 |
/** Expired license admin notice */ |
| 51 |
add_action('admin_notices', [$this, 'license_expired_admin_notice']); |
| 52 |
// License-specific action link: "Enter license key" when inactive |
| 53 |
if (!$this->license->is_active() || $this->license->is_expired()) { |
| 54 |
add_filter('plugin_action_links_' . $this->adapter->get_plugin_basename(), [$this, 'add_license_action_link']); |
| 55 |
} |
| 56 |
add_action('admin_init', [$this, 'auto_update']); |
| 57 |
add_filter('auto_update_plugin', [$this, 'add_disabled_auto_update_text'], 100, 2); |
| 58 |
add_filter('plugins_list', [$this, 'support_auto_update'], 100); |
| 59 |
} |
| 60 |
/** |
| 61 |
* Handle POST-based license form submissions (legacy AJAX path). |
| 62 |
* REST API (RestAPI.php) is the primary path — this is a no-op |
| 63 |
* placeholder for backward compatibility with any direct form posts. |
| 64 |
*/ |
| 65 |
public function do_post_requests(): void |
| 66 |
{ |
| 67 |
// REST API handles all license actions. No legacy POST handling needed. |
| 68 |
} |
| 69 |
public function do_cron_job(): void |
| 70 |
{ |
| 71 |
add_filter('cron_schedules', [$this, 'custom_schedules']); |
| 72 |
add_action('check_license_cron_' . $this->adapter->get_plugin_slug(), [$this, 'check_license_cron_run']); |
| 73 |
$cron_hook = 'check_license_cron_' . $this->adapter->get_plugin_slug(); |
| 74 |
if (!wp_next_scheduled($cron_hook)) { |
| 75 |
wp_schedule_event(time(), 'daily', $cron_hook); |
| 76 |
} |
| 77 |
} |
| 78 |
public function custom_schedules(array $schedules): array |
| 79 |
{ |
| 80 |
$schedules['3hours'] = ['interval' => 60 * 60 * 3, 'display' => 'Three Hours']; |
| 81 |
return $schedules; |
| 82 |
} |
| 83 |
public function check_license_cron_run(): void |
| 84 |
{ |
| 85 |
$this->license->update(); |
| 86 |
} |
| 87 |
public function enqueue_license_scripts(): void |
| 88 |
{ |
| 89 |
if (!isset($_GET['page']) || 'yaycommerce-licenses' !== $_GET['page']) { |
| 90 |
// phpcs:ignore WordPress.Security.NonceVerification |
| 91 |
return; |
| 92 |
} |
| 93 |
$slug = $this->adapter->get_plugin_slug(); |
| 94 |
$assets_url = plugin_dir_url(__FILE__) . '../../assets/'; |
| 95 |
wp_enqueue_script('yaycommerce-license', $assets_url . 'js/license.js', ['jquery'], $this->adapter->get_plugin_version(), \true); |
| 96 |
wp_localize_script('yaycommerce-license', Slug::to_var_name($slug) . 'LicenseData', ['slug' => $slug, 'apiSettings' => ['restNonce' => wp_create_nonce('wp_rest'), 'restUrl' => esc_url_raw(rest_url(Slug::to_var_name($slug) . '/v1')), 'adminUrl' => admin_url()]]); |
| 97 |
} |
| 98 |
public function render_license_settings(): void |
| 99 |
{ |
| 100 |
$license = $this->license; |
| 101 |
$_plugin = ['slug' => $this->adapter->get_plugin_slug(), 'name' => $this->adapter->get_plugin_name()]; |
| 102 |
if ($license->is_active()) { |
| 103 |
include __DIR__ . '/../../views/license-card.php'; |
| 104 |
} else { |
| 105 |
include __DIR__ . '/../../views/license-activate-card.php'; |
| 106 |
} |
| 107 |
} |
| 108 |
public function register_licensing_plugins(array $plugins = []): array |
| 109 |
{ |
| 110 |
$plugin_data = ['name' => $this->adapter->get_plugin_name(), 'slug' => $this->adapter->get_plugin_slug(), 'basename' => $this->adapter->get_plugin_basename(), 'file' => $this->adapter->get_plugin_file(), 'url' => $this->adapter->get_store_link(), 'item_id' => $this->adapter->get_item_id()]; |
| 111 |
return array_merge($plugins, [$plugin_data]); |
| 112 |
} |
| 113 |
public function not_activate_license_notice(): void |
| 114 |
{ |
| 115 |
$current_screen = get_current_screen(); |
| 116 |
if (!$current_screen || $current_screen->id !== 'plugins') { |
| 117 |
return; |
| 118 |
} |
| 119 |
if ($this->license->is_active()) { |
| 120 |
return; |
| 121 |
} |
| 122 |
$slug = $this->adapter->get_plugin_slug(); |
| 123 |
$name = $this->adapter->get_plugin_name(); |
| 124 |
?> |
| 125 |
<div class="error"> |
| 126 |
<p> |
| 127 |
<?php |
| 128 |
// translators: %1$s plugin name, %2$s link open, %3$s link close |
| 129 |
printf(esc_html__('%1$s license key is required. %2$sPlease enter your license key to start using the plugin%3$s.', 'yaycommerce'), esc_html($name), '<a href="' . esc_url(admin_url('admin.php?page=yaycommerce-licenses')) . '">', '</a>'); |
| 130 |
?> |
| 131 |
</p> |
| 132 |
</div> |
| 133 |
<?php |
| 134 |
} |
| 135 |
public function license_expired_admin_notice(): void |
| 136 |
{ |
| 137 |
if ($this->license->is_active() && $this->license->is_expired()) { |
| 138 |
$name = $this->adapter->get_plugin_name(); |
| 139 |
$renewal_url = $this->license->get_renewal_url(); |
| 140 |
?> |
| 141 |
<div class="notice notice-warning"> |
| 142 |
<p> |
| 143 |
<?php |
| 144 |
printf( |
| 145 |
/* translators: %1$s plugin name, %2$s renewal URL */ |
| 146 |
esc_html__('%1$s: Your license has expired. %2$sRenew now%3$s to continue receiving updates.', 'yaycommerce'), |
| 147 |
esc_html($name), |
| 148 |
'<a href="' . esc_url($renewal_url) . '" target="_blank">', |
| 149 |
'</a>' |
| 150 |
); |
| 151 |
?> |
| 152 |
</p> |
| 153 |
</div> |
| 154 |
<?php |
| 155 |
} |
| 156 |
} |
| 157 |
public function show_plugin_page_notification(): void |
| 158 |
{ |
| 159 |
add_action('after_plugin_row_' . $this->adapter->get_plugin_basename(), [$this, 'plugin_notifications'], 10, 2); |
| 160 |
} |
| 161 |
public function plugin_notifications(string $file): void |
| 162 |
{ |
| 163 |
if ($this->adapter->get_plugin_basename() !== $file) { |
| 164 |
return; |
| 165 |
} |
| 166 |
if (!$this->license->is_active() || $this->license->is_expired()) { |
| 167 |
?> |
| 168 |
<tr class="plugin-update-tr active"><td colspan="4" class="plugin-update colspanchange" style="box-shadow:none;"> |
| 169 |
<?php |
| 170 |
if (!$this->license->is_active()) { |
| 171 |
?> |
| 172 |
<div class="update-message notice inline notice-warning notice-alt"> |
| 173 |
<p> |
| 174 |
<a href="<?php |
| 175 |
echo esc_url(admin_url('admin.php?page=yaycommerce-licenses')); |
| 176 |
?>"> |
| 177 |
<?php |
| 178 |
esc_html_e('Please activate your license for access to premium features and automatic updates', 'yaycommerce'); |
| 179 |
?> |
| 180 |
</a>. |
| 181 |
</p> |
| 182 |
</div> |
| 183 |
<?php |
| 184 |
} |
| 185 |
?> |
| 186 |
<?php |
| 187 |
if ($this->license->is_expired()) { |
| 188 |
?> |
| 189 |
<div class="update-message notice inline notice-warning notice-alt"> |
| 190 |
<p class="license_expired_text"> |
| 191 |
<span><?php |
| 192 |
echo esc_html('Your license has expired, please '); |
| 193 |
?></span> |
| 194 |
<a target="_blank" href="<?php |
| 195 |
echo esc_url($this->license->get_renewal_url()); |
| 196 |
?>"><?php |
| 197 |
echo esc_html('renew this license'); |
| 198 |
?></a> |
| 199 |
<span><?php |
| 200 |
echo esc_html(' to download this update. '); |
| 201 |
?></span> |
| 202 |
</p> |
| 203 |
</div> |
| 204 |
<?php |
| 205 |
} |
| 206 |
?> |
| 207 |
</td></tr> |
| 208 |
<?php |
| 209 |
} |
| 210 |
} |
| 211 |
/** |
| 212 |
* Add "Enter license key" action link when license is inactive. |
| 213 |
*/ |
| 214 |
public function add_license_action_link(array $action_links): array |
| 215 |
{ |
| 216 |
$link = ['license' => '<a href="' . admin_url('admin.php?page=yaycommerce-licenses') . '">' . esc_html__('Enter license key', 'yaycommerce') . '</a>']; |
| 217 |
return array_merge($link, $action_links); |
| 218 |
} |
| 219 |
public function auto_update(): void |
| 220 |
{ |
| 221 |
$doing_cron = defined('DOING_CRON') && \DOING_CRON; |
| 222 |
if (!current_user_can($this->adapter->get_capability()) && !$doing_cron) { |
| 223 |
return; |
| 224 |
} |
| 225 |
$license_key = ''; |
| 226 |
if ($this->license->is_active() && !$this->license->is_expired()) { |
| 227 |
$license_key = (string) $this->license->get_license_key(); |
| 228 |
} |
| 229 |
$args = ['version' => $this->adapter->get_plugin_version(), 'license' => $license_key, 'author' => 'YayCommerce', 'item_id' => $this->adapter->get_item_id()]; |
| 230 |
new EDD_SL_Plugin_Updater($this->adapter->get_store_url(), $this->adapter->get_plugin_file(), $args); |
| 231 |
} |
| 232 |
public static function remove_site_plugin_check(LicenseConfigAdapter $adapter): void |
| 233 |
{ |
| 234 |
global $pagenow; |
| 235 |
if ('plugin-install.php' === $pagenow) { |
| 236 |
return; |
| 237 |
} |
| 238 |
if (!function_exists('YayMailScoped\get_plugin_data')) { |
| 239 |
require_once \ABSPATH . 'wp-admin/includes/plugin.php'; |
| 240 |
} |
| 241 |
$basename = $adapter->get_plugin_basename(); |
| 242 |
$site_transient_update_plugins = get_site_transient('update_plugins'); |
| 243 |
if (isset($site_transient_update_plugins->checked[$basename])) { |
| 244 |
unset($site_transient_update_plugins->checked[$basename]); |
| 245 |
set_site_transient('update_plugins', $site_transient_update_plugins); |
| 246 |
} |
| 247 |
} |
| 248 |
public function support_auto_update(array $plugins): array |
| 249 |
{ |
| 250 |
foreach ($plugins['all'] as $ind => $active_plugin) { |
| 251 |
if ('YayCommerce' === $active_plugin['Author']) { |
| 252 |
$plugins['all'][$ind]['update-supported'] = \true; |
| 253 |
} |
| 254 |
} |
| 255 |
return $plugins; |
| 256 |
} |
| 257 |
public function add_disabled_auto_update_text($value, $plugin_info) |
| 258 |
{ |
| 259 |
if (!isset($plugin_info->plugin)) { |
| 260 |
return $value; |
| 261 |
} |
| 262 |
if ($plugin_info->plugin === $this->adapter->get_plugin_basename()) { |
| 263 |
if (!$this->license->is_active() || $this->license->is_expired()) { |
| 264 |
return \false; |
| 265 |
} |
| 266 |
} |
| 267 |
return $value; |
| 268 |
} |
| 269 |
public function is_license_inactive(): bool |
| 270 |
{ |
| 271 |
return !$this->license->is_active(); |
| 272 |
} |
| 273 |
public function get_license(): License |
| 274 |
{ |
| 275 |
return $this->license; |
| 276 |
} |
| 277 |
} |
| 278 |
|