PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 2.0.0
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v2.0.0
2.1.2 2.1.1 2.1.0 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 trunk 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 All 56 releases
darkify / src / Admin / Menu.php

Menu.php in Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) 2.0.0, at src/Admin/Menu.php

161 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The Darkify admin menu.
5 *
6 * Registers the top-level menu page that hosts the React SPA, plus a "Get Help"
7 * submenu entry of its own. Both render the same SPA root node; which page the
8 * app opens on is derived from the `?page=` slug (see resolveInitialRoute in
9 * darkify-react/src/main.jsx), so "Get Help" lands directly on the dedicated Help
10 * page while the main entry lands on the settings sections.
11 *
12 * The menu title, slug, icon and position all come from the args the config class
13 * already declared via `DarkifyOptions::options()`
14 * (src/Admin/Views/DarkifyOptions.php), so the menu keeps its existing slug
15 * (`darkify`), icon and place in the sidebar — an existing bookmark to
16 * `?page=darkify` still lands on the settings screen, it is simply a React one now.
17 *
18 * @package darkify
19 * @subpackage darkify/src/Admin
20 * @author ThemeAtelier<themeatelierbd@gmail.com>
21 */
22
23 namespace ThemeAtelier\Darkify\Admin;
24
25 use ThemeAtelier\Darkify\Admin\Schema\SchemaRegistry;
26
27 if (! defined('ABSPATH')) {
28 die;
29 }
30
31 class Menu
32 {
33 /**
34 * The option key whose page args drive the menu.
35 */
36 const OPTION_KEY = 'darkify';
37
38 /**
39 * The main SPA page slug (unchanged from the old options screen).
40 */
41 const PAGE_SLUG = 'darkify';
42
43 /**
44 * The Help page's own slug. It is a separate WordPress menu entry — not a tab,
45 * modal or panel — so Help is reachable in one click from anywhere in wp-admin.
46 */
47 const HELP_SLUG = 'darkify-help';
48
49 public function __construct()
50 {
51 \add_action('admin_menu', [$this, 'register']);
52 // Old bookmarks / links to the retired "Get Help" screen are forwarded to
53 // the SPA's own Help route (see redirect_legacy_help_page()).
54 \add_action('admin_page_access_denied', [$this, 'redirect_legacy_help_page']);
55 }
56
57 /**
58 * Register the top-level menu page, plus its submenu entries: an explicit
59 * "Darkify" entry, "Get Help", and the free plugin's existing
60 * "Upgrade To Pro! 👑" link.
61 *
62 * Every settings section is reached from the React admin's own header nav
63 * instead of WP submenus, but Get Help gets its own submenu entry (matching
64 * Pro's Menu.php) so it is reachable in one click from anywhere in wp-admin,
65 * not only from inside the settings screen. The upgrade submenu is the same
66 * external pricing link the free plugin has always registered — the
67 * upgrade flow is deliberately unchanged by the React migration.
68 */
69 public function register(): void
70 {
71 $args = SchemaRegistry::options(self::OPTION_KEY);
72
73 $menu_title = $args['menu_title'] ?? \esc_html__('Darkify', 'darkify');
74 $capability = \apply_filters('darkify_ui_permission', $args['menu_capability'] ?? 'manage_options');
75 $position = $args['menu_position'] ?? null;
76 $icon = $args['menu_icon'] ?? 'dashicons-lightbulb';
77
78 \add_menu_page(
79 $menu_title,
80 $menu_title,
81 $capability,
82 self::PAGE_SLUG,
83 [$this, 'render'],
84 $icon,
85 $position
86 );
87
88 // Registered explicitly with the parent's own slug so it reads "Darkify"
89 // instead of WordPress auto-duplicating the top-level menu title as the
90 // first submenu item — that auto-duplication only happens when nothing
91 // else claims the parent slug as a submenu (see Pro's Menu.php, same fix).
92 \add_submenu_page(
93 self::PAGE_SLUG,
94 $menu_title,
95 \esc_html__('Darkify', 'darkify'),
96 $capability,
97 self::PAGE_SLUG,
98 [$this, 'render']
99 );
100
101 // Get Help is no longer a WP submenu — it lives in the SPA rail's footer
102 // (see AppSidebar). The retired `?page=darkify-help` still forwards to the
103 // SPA's /help route (see below).
104
105 \add_submenu_page(
106 self::PAGE_SLUG,
107 \__('Upgrade To Pro! 👑', 'darkify'),
108 \sprintf('<span style="color: #35b747;font-weight:600;" class="darkify-get-pro-text">%s</span>', \__('Upgrade To Pro! 👑', 'darkify')),
109 'manage_options',
110 'https://darkifywp.com/pricing/?utm_source=darkify_plugin&utm_medium=submenu_page&utm_campaign=regular'
111 );
112 }
113
114 /**
115 * Forward the retired `?page=darkify-help` screen to the SPA's Help route.
116 *
117 * "Get Help" used to be its own menu page; it is now a route inside the one
118 * SPA page. Without this, an existing bookmark (or any third-party link) to
119 * the old slug would hit WordPress's "you are not allowed to access this
120 * page" wall, since the slug is no longer a registered page.
121 *
122 * Hooked on `admin_page_access_denied` — which wp-admin/includes/menu.php
123 * fires immediately before that `wp_die( …, 403 )` — because that check runs
124 * while menu.php is being required, i.e. BEFORE `admin_init`; an admin_init
125 * hook would never get the chance to redirect. The slug guard keeps this
126 * scoped to our own retired page, so every other denied page still dies
127 * normally.
128 */
129 public function redirect_legacy_help_page(): void
130 {
131 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen check, no state is changed.
132 $page = isset($_GET['page']) ? \sanitize_key(\wp_unslash($_GET['page'])) : '';
133 if ($page !== self::HELP_SLUG) {
134 return;
135 }
136
137 \wp_safe_redirect(\admin_url('admin.php?page=' . self::PAGE_SLUG . '#/help'));
138 exit;
139 }
140
141 /**
142 * The page body: just the node React mounts on. Every menu entry renders this
143 * same root — the SPA reads the `?page=` slug to decide which route to open.
144 *
145 * The `darkify_ignore` class opts the whole React admin out of Darkify's own
146 * dark-mode engine. When "Admin Panel Dark Mode" is on, that engine runs on
147 * every admin screen and force-rewrites element backgrounds/colors with
148 * `!important` (classifying nodes as `darkify_style_bg` / `darkify_style_button`
149 * etc. — see src/assets/css/client_main.css). On our settings screen that
150 * stomped the app's own theme — most visibly the colour-preset swatches, whose
151 * inline background was overwritten so every preview looked empty. The React
152 * admin already themes itself via the `.dark` class, so the engine must leave
153 * its subtree alone; `.darkify_ignore` / `.darkify_ignore *` is that engine's
154 * built-in exclusion hook.
155 */
156 public function render(): void
157 {
158 echo '<div id="darkify_react" class="darkify_ignore"></div>';
159 }
160 }
161