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