| @@ -18,9 +18,9 @@ | ||
| 18 | 18 | use YayMailScoped\YayCommerce\AdminShell\Registry\LegacyBridge; |
| 19 | 19 | use YayMailScoped\YayCommerce\AdminShell\Registry\LicenseRegistry; |
| 20 | 20 | use YayMailScoped\YayCommerce\AdminShell\Support\AdminContext; |
| 21 | 21 | use YayMailScoped\YayCommerce\AdminShell\Support\Constants; |
| 22 | -\defined('ABSPATH') || exit; | |
| 22 | +defined('ABSPATH') || exit; | |
| 23 | 23 | /** |
| 24 | 24 | * Public facade — entry point for consuming plugins. |
| 25 | 25 | * |
| 26 | 26 | * Version election: when multiple scoped copies coexist, the highest |
| @@ -25,14 +25,13 @@ | ||
| 25 | 25 | * |
| 26 | 26 | * Version election: when multiple scoped copies coexist, the highest |
| 27 | 27 | * VERSION wins and registers menus/pages. All copies' register_plugin() |
| 28 | 28 | * and enable_license() still run — they hook global WP actions. |
| 29 | - * @internal | |
| 30 | 29 | */ |
| 31 | 30 | class AdminShell |
| 32 | 31 | { |
| 33 | 32 | /** Package version — used for cross-scope version election. */ |
| 34 | - const VERSION = '2.7.1'; | |
| 33 | + const VERSION = '2.8.5'; | |
| 35 | 34 | private static ?self $instance = null; |
| 36 | 35 | private static bool $booted = \false; |
| 37 | 36 | private static array $enabled_slugs = []; |
| 38 | 37 | /** The scoped prefix for THIS copy (derived from namespace). */ |
| @@ -48,9 +47,9 @@ | ||
| 48 | 47 | * Each scoped copy calls boot(). All register their version in a shared |
| 49 | 48 | * global. Actual menu/page registration is deferred to admin_menu where |
| 50 | 49 | * only the highest version runs. |
| 51 | 50 | */ |
| 52 | - public static function boot() : void | |
| 51 | + public static function boot(): void | |
| 53 | 52 | { |
| 54 | 53 | if (self::$booted) { |
| 55 | 54 | return; |
| 56 | 55 | } |
| @@ -59,10 +58,10 @@ | ||
| 59 | 58 | $instance = self::get_instance(); |
| 60 | 59 | // Derive our scoped prefix from the namespace (e.g. "YayMailScoped\YayCommerce\AdminShell" → "YayMailScoped") |
| 61 | 60 | $ns = __NAMESPACE__; |
| 62 | 61 | // YayCommerce\AdminShell or ScopedPrefix\YayCommerce\AdminShell |
| 63 | - $parts = \explode('\\', $ns); | |
| 64 | - self::$prefix = \count($parts) > 2 ? $parts[0] : 'default'; | |
| 62 | + $parts = explode('\\', $ns); | |
| 63 | + self::$prefix = count($parts) > 2 ? $parts[0] : 'default'; | |
| 65 | 64 | // Register this version in the shared global for cross-scope election |
| 66 | 65 | if (!isset($GLOBALS['yaycommerce_admin_shell_versions'])) { |
| 67 | 66 | $GLOBALS['yaycommerce_admin_shell_versions'] = []; |
| 68 | 67 | } |
| @@ -69,9 +68,9 @@ | ||
| 69 | 68 | $GLOBALS['yaycommerce_admin_shell_versions'][self::$prefix] = ['version' => self::VERSION, 'registry' => $instance->registry, 'boot_cb' => [static::class, 'do_shell_registration']]; |
| 70 | 69 | // Register the version election — only once (first copy to call boot sets it up). |
| 71 | 70 | // Bound to both admin_menu and network_admin_menu so election runs in the |
| 72 | 71 | // Multisite Network Admin too; only the firing hook's election actually runs. |
| 73 | - if (1 === \count($GLOBALS['yaycommerce_admin_shell_versions'])) { | |
| 72 | + if (1 === count($GLOBALS['yaycommerce_admin_shell_versions'])) { | |
| 74 | 73 | AdminContext::bind_menu([static::class, 'elect_version'], 8); |
| 75 | 74 | // bind_menu() already wired BOTH hooks here, so mark the network election |
| 76 | 75 | // as done. This is load-bearing: it stops the safety net below from adding |
| 77 | 76 | // a SECOND network binding (elect_version is NOT idempotent — a double |
| @@ -104,15 +103,15 @@ | ||
| 104 | 103 | $GLOBALS['yaycommerce_ajax_handlers_registered'] = self::VERSION; |
| 105 | 104 | RecommendedPluginsPage::get_instance(); |
| 106 | 105 | } |
| 107 | 106 | } |
| 108 | - \do_action('yaycommerce_admin_shell_booted', $instance); | |
| 107 | + do_action('yaycommerce_admin_shell_booted', $instance); | |
| 109 | 108 | } |
| 110 | 109 | /** |
| 111 | 110 | * Version election — picks the highest version and runs its shell registration. |
| 112 | 111 | * Called once at admin_menu priority 8 (before TopLevelMenu at 9). |
| 113 | 112 | */ |
| 114 | - public static function elect_version() : void | |
| 113 | + public static function elect_version(): void | |
| 115 | 114 | { |
| 116 | 115 | $versions = $GLOBALS['yaycommerce_admin_shell_versions'] ?? []; |
| 117 | 116 | if (empty($versions)) { |
| 118 | 117 | return; |
| @@ -120,9 +119,9 @@ | ||
| 120 | 119 | // Find highest version |
| 121 | 120 | $winner_prefix = ''; |
| 122 | 121 | $winner_ver = '0.0.0'; |
| 123 | 122 | foreach ($versions as $prefix => $data) { |
| 124 | - if (\version_compare($data['version'], $winner_ver, '>')) { | |
| 123 | + if (version_compare($data['version'], $winner_ver, '>')) { | |
| 125 | 124 | $winner_ver = $data['version']; |
| 126 | 125 | $winner_prefix = $prefix; |
| 127 | 126 | } |
| 128 | 127 | } |
| @@ -127,16 +126,16 @@ | ||
| 127 | 126 | } |
| 128 | 127 | } |
| 129 | 128 | // Call the winner's registration — may be a different scoped class |
| 130 | 129 | $winner = $versions[$winner_prefix]; |
| 131 | - if (isset($winner['boot_cb']) && \is_callable($winner['boot_cb'])) { | |
| 132 | - \call_user_func($winner['boot_cb']); | |
| 130 | + if (isset($winner['boot_cb']) && is_callable($winner['boot_cb'])) { | |
| 131 | + call_user_func($winner['boot_cb']); | |
| 133 | 132 | } |
| 134 | 133 | } |
| 135 | 134 | /** |
| 136 | 135 | * Register menus/pages — only called by the winning version. |
| 137 | 136 | */ |
| 138 | - public static function do_shell_registration() : void | |
| 137 | + public static function do_shell_registration(): void | |
| 139 | 138 | { |
| 140 | 139 | $instance = self::get_instance(); |
| 141 | 140 | // Merge all registries into the winner's registry |
| 142 | 141 | foreach ($GLOBALS['yaycommerce_admin_shell_versions'] ?? [] as $prefix => $data) { |
| @@ -164,9 +163,9 @@ | ||
| 164 | 163 | * Register a plugin with the admin shell. |
| 165 | 164 | * Auto-detects pro vs lite via instanceof. |
| 166 | 165 | * Runs for ALL versions (not version-gated). |
| 167 | 166 | */ |
| 168 | - public static function register_plugin(PluginMenuAdapter $adapter) : void | |
| 167 | + public static function register_plugin(PluginMenuAdapter $adapter): void | |
| 169 | 168 | { |
| 170 | 169 | self::validate_adapter($adapter); |
| 171 | 170 | // Publish this plugin's intended submenu position into a shared, |
| 172 | 171 | // cross-scope map (keyed by menu slug). The version-elected winner reads |
| @@ -183,9 +182,9 @@ | ||
| 183 | 182 | // Submenu registration — per-plugin, all versions |
| 184 | 183 | $submenu = new PluginSubmenu($adapter); |
| 185 | 184 | $submenu->init(); |
| 186 | 185 | // Action links + row meta — per-plugin, all versions |
| 187 | - if (\is_admin()) { | |
| 186 | + if (is_admin()) { | |
| 188 | 187 | self::register_plugin_links($adapter); |
| 189 | 188 | } |
| 190 | 189 | // License subsystem — per-plugin, all versions |
| 191 | 190 | if ($adapter instanceof LicenseConfigAdapter) { |
| @@ -206,9 +205,9 @@ | ||
| 206 | 205 | * not appear under the shared YayCommerce menu. |
| 207 | 206 | * |
| 208 | 207 | * @param array{parent_menu: string, menu_title: string, menu_capability: string, menu_slug: string} $config |
| 209 | 208 | */ |
| 210 | - public static function register_external_plugin_menu(array $config) : void | |
| 209 | + public static function register_external_plugin_menu(array $config): void | |
| 211 | 210 | { |
| 212 | 211 | $adapter = new ExternalPluginMenuAdapter($config); |
| 213 | 212 | $adapter->init(); |
| 214 | 213 | } |
| @@ -214,38 +213,38 @@ | ||
| 214 | 213 | } |
| 215 | 214 | /** |
| 216 | 215 | * Validate adapter values upfront. |
| 217 | 216 | */ |
| 218 | - private static function validate_adapter(PluginMenuAdapter $adapter) : void | |
| 217 | + private static function validate_adapter(PluginMenuAdapter $adapter): void | |
| 219 | 218 | { |
| 220 | 219 | $menu_title = $adapter->get_menu_title(); |
| 221 | 220 | if (empty($menu_title)) { |
| 222 | - \trigger_error('[YayCommerce AdminShell] get_menu_title() must return a non-empty string.', \E_USER_WARNING); | |
| 221 | + trigger_error('[YayCommerce AdminShell] get_menu_title() must return a non-empty string.', \E_USER_WARNING); | |
| 223 | 222 | } |
| 224 | 223 | $basename = $adapter->get_plugin_basename(); |
| 225 | 224 | if (empty($basename)) { |
| 226 | - \trigger_error('[YayCommerce AdminShell] get_plugin_basename() must return a non-empty string.', \E_USER_WARNING); | |
| 225 | + trigger_error('[YayCommerce AdminShell] get_plugin_basename() must return a non-empty string.', \E_USER_WARNING); | |
| 227 | 226 | } |
| 228 | 227 | $callback = $adapter->get_settings_page_callback(); |
| 229 | - if (null !== $callback && !\is_callable($callback)) { | |
| 230 | - \trigger_error('[YayCommerce AdminShell] get_settings_page_callback() returned a non-callable value.', \E_USER_WARNING); | |
| 228 | + if (null !== $callback && !is_callable($callback)) { | |
| 229 | + trigger_error('[YayCommerce AdminShell] get_settings_page_callback() returned a non-callable value.', \E_USER_WARNING); | |
| 231 | 230 | } |
| 232 | 231 | if ($adapter instanceof LicenseConfigAdapter) { |
| 233 | 232 | $slug = $adapter->get_plugin_slug(); |
| 234 | 233 | if (empty($slug)) { |
| 235 | - \trigger_error('[YayCommerce AdminShell] get_plugin_slug() must return a non-empty string.', \E_USER_WARNING); | |
| 234 | + trigger_error('[YayCommerce AdminShell] get_plugin_slug() must return a non-empty string.', \E_USER_WARNING); | |
| 236 | 235 | } |
| 237 | 236 | $item_id = $adapter->get_item_id(); |
| 238 | 237 | if ($item_id <= 0) { |
| 239 | - \trigger_error('[YayCommerce AdminShell] get_item_id() must return a positive integer.', \E_USER_WARNING); | |
| 238 | + trigger_error('[YayCommerce AdminShell] get_item_id() must return a positive integer.', \E_USER_WARNING); | |
| 240 | 239 | } |
| 241 | 240 | $store_url = $adapter->get_store_url(); |
| 242 | 241 | if (empty($store_url)) { |
| 243 | - \trigger_error('[YayCommerce AdminShell] get_store_url() must return a non-empty URL.', \E_USER_WARNING); | |
| 242 | + trigger_error('[YayCommerce AdminShell] get_store_url() must return a non-empty URL.', \E_USER_WARNING); | |
| 244 | 243 | } |
| 245 | 244 | $plugin_file = $adapter->get_plugin_file(); |
| 246 | 245 | if (empty($plugin_file)) { |
| 247 | - \trigger_error('[YayCommerce AdminShell] get_plugin_file() must return a non-empty path.', \E_USER_WARNING); | |
| 246 | + trigger_error('[YayCommerce AdminShell] get_plugin_file() must return a non-empty path.', \E_USER_WARNING); | |
| 248 | 247 | } |
| 249 | 248 | } |
| 250 | 249 | } |
| 251 | 250 | /** |
| @@ -251,9 +250,9 @@ | ||
| 251 | 250 | /** |
| 252 | 251 | * Enable the license subsystem for a plugin. |
| 253 | 252 | * Runs for ALL versions (not version-gated). |
| 254 | 253 | */ |
| 255 | - public static function enable_license(LicenseConfigAdapter $adapter) : void | |
| 254 | + public static function enable_license(LicenseConfigAdapter $adapter): void | |
| 256 | 255 | { |
| 257 | 256 | $slug = $adapter->get_plugin_slug(); |
| 258 | 257 | if (isset(self::$enabled_slugs[$slug])) { |
| 259 | 258 | return; |
| @@ -262,38 +261,38 @@ | ||
| 262 | 261 | $instance = self::get_instance(); |
| 263 | 262 | new LicenseHandler($adapter); |
| 264 | 263 | $info = PluginInfoFactory::from_adapter($adapter); |
| 265 | 264 | $instance->registry->register($info); |
| 266 | - \do_action('yaycommerce_admin_shell_license_enabled', $adapter); | |
| 265 | + do_action('yaycommerce_admin_shell_license_enabled', $adapter); | |
| 267 | 266 | } |
| 268 | 267 | /** |
| 269 | 268 | * Register plugin action links + row meta. |
| 270 | 269 | */ |
| 271 | - private static function register_plugin_links(PluginMenuAdapter $adapter) : void | |
| 270 | + private static function register_plugin_links(PluginMenuAdapter $adapter): void | |
| 272 | 271 | { |
| 273 | 272 | $basename = $adapter->get_plugin_basename(); |
| 274 | - add_filter('plugin_action_links_' . $basename, function (array $links) use($adapter) { | |
| 273 | + add_filter('plugin_action_links_' . $basename, function (array $links) use ($adapter) { | |
| 275 | 274 | $new = []; |
| 276 | 275 | $menu_slug = $adapter->get_menu_slug(); |
| 277 | 276 | if (!empty($menu_slug)) { |
| 278 | - $url = \admin_url('admin.php?page=' . $menu_slug); | |
| 279 | - $new['settings'] = '<a href="' . \esc_url($url) . '">' . \esc_html($adapter->get_settings_label()) . '</a>'; | |
| 277 | + $url = admin_url('admin.php?page=' . $menu_slug); | |
| 278 | + $new['settings'] = '<a href="' . esc_url($url) . '">' . esc_html($adapter->get_settings_label()) . '</a>'; | |
| 280 | 279 | } |
| 281 | 280 | $pro_url = $adapter->get_pro_url(); |
| 282 | 281 | if (!empty($pro_url)) { |
| 283 | - $new['go-pro'] = '<a href="' . \esc_url($pro_url) . '" target="_blank" style="color:#00a32a;font-weight:700;">' . \esc_html__('Go Pro', 'yaycommerce') . '</a>'; | |
| 282 | + $new['go-pro'] = '<a href="' . esc_url($pro_url) . '" target="_blank" style="color:#00a32a;font-weight:700;">' . esc_html__('Go Pro', 'yaycommerce') . '</a>'; | |
| 284 | 283 | } |
| 285 | - return \array_merge($new, $links); | |
| 284 | + return array_merge($new, $links); | |
| 286 | 285 | }); |
| 287 | - add_filter('plugin_row_meta', function (array $meta, string $file) use($adapter, $basename) { | |
| 286 | + add_filter('plugin_row_meta', function (array $meta, string $file) use ($adapter, $basename) { | |
| 288 | 287 | if ($file !== $basename) { |
| 289 | 288 | return $meta; |
| 290 | 289 | } |
| 291 | 290 | $docs_url = $adapter->get_docs_url(); |
| 292 | 291 | if (!empty($docs_url)) { |
| 293 | - $meta[] = '<a href="' . \esc_url($docs_url) . '" target="_blank">' . \esc_html__('Docs', 'yaycommerce') . '</a>'; | |
| 292 | + $meta[] = '<a href="' . esc_url($docs_url) . '" target="_blank">' . esc_html__('Docs', 'yaycommerce') . '</a>'; | |
| 294 | 293 | } |
| 295 | - $meta[] = '<a href="https://yaycommerce.com/support" target="_blank">' . \esc_html__('Support', 'yaycommerce') . '</a>'; | |
| 294 | + $meta[] = '<a href="https://yaycommerce.com/support" target="_blank">' . esc_html__('Support', 'yaycommerce') . '</a>'; | |
| 296 | 295 | return $meta; |
| 297 | 296 | }, 10, 2); |
| 298 | 297 | } |
| 299 | 298 | /** |
| @@ -298,13 +297,13 @@ | ||
| 298 | 297 | } |
| 299 | 298 | /** |
| 300 | 299 | * Return the shared registry. |
| 301 | 300 | */ |
| 302 | - public static function registry() : LicenseRegistry | |
| 301 | + public static function registry(): LicenseRegistry | |
| 303 | 302 | { |
| 304 | 303 | return self::get_instance()->registry; |
| 305 | 304 | } |
| 306 | - private static function get_instance() : self | |
| 305 | + private static function get_instance(): self | |
| 307 | 306 | { |
| 308 | 307 | if (null === self::$instance) { |
| 309 | 308 | self::$instance = new self(); |
| 310 | 309 | } |
| @@ -312,9 +311,9 @@ | ||
| 312 | 311 | } |
| 313 | 312 | /** |
| 314 | 313 | * Reset state — for unit tests only. |
| 315 | 314 | */ |
| 316 | - public static function reset() : void | |
| 315 | + public static function reset(): void | |
| 317 | 316 | { |
| 318 | 317 | self::$instance = null; |
| 319 | 318 | self::$booted = \false; |
| 320 | 319 | self::$enabled_slugs = []; |