| 1 |
<?php |
| 2 |
|
| 3 |
namespace YayMailScoped\YayCommerce\AdminShell\Support; |
| 4 |
|
| 5 |
use YayMailScoped\YayCommerce\AdminShell\Contracts\PluginMenuAdapter; |
| 6 |
defined('ABSPATH') || exit; |
| 7 |
/** |
| 8 |
* Resolves the current admin context (site dashboard vs Multisite Network Admin) |
| 9 |
* and the per-plugin opt-in for each context. |
| 10 |
* |
| 11 |
* WordPress fires `admin_menu` on a site dashboard and `network_admin_menu` in |
| 12 |
* Network Admin — never both in one request. Menu components bind to BOTH hooks |
| 13 |
* and consult this helper at callback time (NOT at registration time) to decide |
| 14 |
* the correct hook, capability, and whether a plugin opted into the context. |
| 15 |
* |
| 16 |
* Opt-in is read via optional adapter methods detected with method_exists() — |
| 17 |
* the same backward-compatible pattern PluginSubmenu uses for is_licensed(). |
| 18 |
* Adapters that predate these methods keep the safe defaults (site-only). |
| 19 |
*/ |
| 20 |
class AdminContext |
| 21 |
{ |
| 22 |
/** Network Admin capability — super-admins only. */ |
| 23 |
const NETWORK_CAPABILITY = 'manage_network'; |
| 24 |
/** |
| 25 |
* Both admin-menu hooks. WP fires exactly one per request: |
| 26 |
* `admin_menu` on a site dashboard, `network_admin_menu` in Network Admin. |
| 27 |
*/ |
| 28 |
const MENU_HOOKS = ['admin_menu', 'network_admin_menu']; |
| 29 |
/** |
| 30 |
* Bind a menu callback to BOTH admin-menu hooks at the same priority. |
| 31 |
* |
| 32 |
* Only the hook matching the current request fires, so the other binding |
| 33 |
* is an inert no-op. Binding both (rather than branching on context) keeps |
| 34 |
* registration timing-proof — callers may run as early as `plugins_loaded`, |
| 35 |
* before the admin context is reliably known. |
| 36 |
* |
| 37 |
* @param callable $callback |
| 38 |
*/ |
| 39 |
public static function bind_menu($callback, int $priority): void |
| 40 |
{ |
| 41 |
foreach (self::MENU_HOOKS as $hook) { |
| 42 |
add_action($hook, $callback, $priority); |
| 43 |
} |
| 44 |
} |
| 45 |
/** |
| 46 |
* True when the current request is the Multisite Network Admin. |
| 47 |
* |
| 48 |
* Guarded with function_exists() so the class is usable in non-WP unit |
| 49 |
* context, where the function is absent and site context is assumed. |
| 50 |
*/ |
| 51 |
public static function is_network(): bool |
| 52 |
{ |
| 53 |
return \function_exists('YayMailScoped\is_network_admin') && \YayMailScoped\is_network_admin(); |
| 54 |
} |
| 55 |
/** |
| 56 |
* The admin_menu hook that fires for the current context. |
| 57 |
*/ |
| 58 |
public static function hook(): string |
| 59 |
{ |
| 60 |
return self::is_network() ? 'network_admin_menu' : 'admin_menu'; |
| 61 |
} |
| 62 |
/** |
| 63 |
* Capability required for menus/pages in the current context. |
| 64 |
* Network Admin requires `manage_network`; site dashboard keeps $default. |
| 65 |
*/ |
| 66 |
public static function capability(string $default): string |
| 67 |
{ |
| 68 |
return self::is_network() ? self::NETWORK_CAPABILITY : $default; |
| 69 |
} |
| 70 |
/** |
| 71 |
* Whether the plugin wants its submenu on the per-site dashboard. |
| 72 |
* Optional method — absent ⇒ true (legacy adapters stay site-visible). |
| 73 |
*/ |
| 74 |
public static function wants_site(PluginMenuAdapter $adapter): bool |
| 75 |
{ |
| 76 |
if (!\method_exists($adapter, 'wants_site_menu')) { |
| 77 |
return \true; |
| 78 |
} |
| 79 |
return (bool) $adapter->wants_site_menu(); |
| 80 |
} |
| 81 |
/** |
| 82 |
* Whether the plugin wants its submenu in Network Admin. |
| 83 |
* Optional method — absent ⇒ false (legacy adapters never go to network). |
| 84 |
*/ |
| 85 |
public static function wants_network(PluginMenuAdapter $adapter): bool |
| 86 |
{ |
| 87 |
if (!\method_exists($adapter, 'wants_network_menu')) { |
| 88 |
return \false; |
| 89 |
} |
| 90 |
return (bool) $adapter->wants_network_menu(); |
| 91 |
} |
| 92 |
} |
| 93 |
|