PluginProbe
YayMail – WooCommerce Email Customizer / 4.4.6
YayMail – WooCommerce Email Customizer v4.4.6
4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 trunk 1.9.6 2.1.4 2.1.5 3.2.2 3.2.6 3.2.7.1 3.2.8.1 3.2.9 3.3 3.3.1 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4 3.4.1 All 57 releases
← All changes | vendor-prefixed/src/Menu/PluginSubmenu.php +56 -12 4.4.1 → 4.4.6 View file →
@@ -4,8 +4,10 @@
4 4
5 5 use YayMailScoped\YayCommerce\AdminShell\Contracts\PluginMenuAdapter;
6 6 use YayMailScoped\YayCommerce\AdminShell\License\Contracts\LicenseConfigAdapter;
7 7 use YayMailScoped\YayCommerce\AdminShell\License\License;
8 +use YayMailScoped\YayCommerce\AdminShell\Pages\WooCommerceRequiredPage;
9 +use YayMailScoped\YayCommerce\AdminShell\Support\AdminContext;
8 10 /**
9 11 * Registers a plugin-named submenu under YayCommerce.
10 12 * Works with any PluginMenuAdapter (lite or pro).
11 13 *
@@ -18,14 +20,26 @@
18 20 public function __construct(PluginMenuAdapter $adapter)
19 21 {
20 22 $this->adapter = $adapter;
21 23 }
22 - public function init() : void
24 + public function init(): void
23 25 {
24 - \add_action('admin_menu', [$this, 'register'], 10);
26 + // Bound to both hooks; the context gate in register() decides whether the
27 + // plugin actually registers in the current (site vs network) context.
28 + AdminContext::bind_menu([$this, 'register'], 10);
25 29 }
26 - public function register() : void
30 + public function register(): void
27 31 {
32 + // Context gate: register only where the plugin opted in. Network Admin
33 + // requires wants_network_menu(); site dashboard requires wants_site_menu().
34 + // Defaults (legacy adapters): site=true, network=false.
35 + if (AdminContext::is_network()) {
36 + if (!AdminContext::wants_network($this->adapter)) {
37 + return;
38 + }
39 + } elseif (!AdminContext::wants_site($this->adapter)) {
40 + return;
41 + }
28 42 $menu_slug = $this->adapter->get_menu_slug();
29 43 if (empty($menu_slug)) {
30 44 return;
31 45 }
@@ -35,10 +49,10 @@
35 49 if (isset($submenu['yaycommerce'])) {
36 50 $yaycommerce_menu = $submenu['yaycommerce'];
37 51 foreach ($yaycommerce_menu as $key => $value) {
38 52 if ($value[2] === $menu_slug) {
39 - if (\method_exists($this->adapter, 'is_licensed') && $this->adapter->is_licensed() || !\method_exists($this->adapter, 'is_licensed')) {
40 - \remove_submenu_page('yaycommerce', $menu_slug);
53 + if (method_exists($this->adapter, 'is_licensed') && $this->adapter->is_licensed() || !method_exists($this->adapter, 'is_licensed')) {
54 + remove_submenu_page('yaycommerce', $menu_slug);
41 55 $is_override = \true;
42 56 } else {
43 57 $has_menu = \true;
44 58 }
@@ -50,9 +64,9 @@
50 64 return;
51 65 }
52 66 $callback = $this->adapter->get_settings_page_callback();
53 67 // Guard: ensure callback is actually callable
54 - if (null !== $callback && !\is_callable($callback)) {
68 + if (null !== $callback && !is_callable($callback)) {
55 69 $callback = null;
56 70 }
57 71 // Pro plugins without active license → override callback to redirect
58 72 $needs_redirect = \false;
@@ -57,23 +71,53 @@
57 71 // Pro plugins without active license → override callback to redirect
58 72 $needs_redirect = \false;
59 73 if ($this->adapter instanceof LicenseConfigAdapter) {
60 74 $license = new License($this->adapter);
61 - if (!$license->is_active() || $license->is_expired()) {
75 + if (!$license->is_active()) {
62 76 $needs_redirect = \true;
63 77 $callback = null;
64 78 }
65 79 }
66 - $page_id = \add_submenu_page('yaycommerce', $this->adapter->get_page_title(), $this->adapter->get_menu_title(), $this->adapter->get_capability(), $menu_slug, $callback ?? '__return_false', $this->adapter->get_settings_page_position());
80 + // WooCommerce dependency gate: when the plugin depends on WooCommerce and it
81 + // is inactive, render the shared "install WooCommerce" screen in place of the
82 + // settings page. Applied only on the real render path — the license redirect
83 + // above takes precedence, so unlicensed pro plugins still land on Licenses.
84 + // Opt-in via the optional needs_woocommerce_screen() adapter method; the
85 + // adapter owns the runtime WooCommerce check so it runs un-prefixed under
86 + // PHP-Scoper (the adapter class is excluded from scoping in each plugin).
87 + if (!$needs_redirect && method_exists($this->adapter, 'needs_woocommerce_screen') && $this->adapter->needs_woocommerce_screen()) {
88 + $screen_copy = method_exists($this->adapter, 'get_woocommerce_screen_copy') ? (array) $this->adapter->get_woocommerce_screen_copy() : [];
89 + $callback = static function () use ($screen_copy) {
90 + WooCommerceRequiredPage::render($screen_copy);
91 + };
92 + }
93 + $page_id = add_submenu_page(
94 + 'yaycommerce',
95 + $this->adapter->get_page_title(),
96 + $this->adapter->get_menu_title(),
97 + // In Network Admin, elevate to manage_network (super-admin only).
98 + AdminContext::capability($this->adapter->get_capability()),
99 + $menu_slug,
100 + $callback ?? '__return_false',
101 + // Position intentionally null here. WP treats add_submenu_page()'s
102 + // $position as a fragile array insertion index, not an ordering rank.
103 + // Final ordering is applied later by SubmenuPositioner, which reorders
104 + // the assembled $submenu['yaycommerce'] from the shared position map.
105 + null
106 + );
67 107 if ($is_override) {
68 - \remove_all_actions('load-' . $page_id);
108 + remove_all_actions('load-' . $page_id);
69 109 }
70 110 if ($needs_redirect) {
71 - \add_action('load-' . $page_id, [__CLASS__, 'redirect_to_licenses']);
111 + add_action('load-' . $page_id, [__CLASS__, 'redirect_to_licenses']);
72 112 }
73 113 }
74 - public static function redirect_to_licenses() : void
114 + public static function redirect_to_licenses(): void
75 115 {
76 - \wp_safe_redirect(\admin_url('admin.php?page=yaycommerce-licenses'));
116 + // The load hook fires in the current context — target the matching dashboard
117 + // so a network-flagged pro plugin redirects within Network Admin, not the site.
118 + $path = 'admin.php?page=yaycommerce-licenses';
119 + $url = AdminContext::is_network() ? network_admin_url($path) : admin_url($path);
120 + wp_safe_redirect($url);
77 121 exit;
78 122 }
79 123 }