PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 1.4.6
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v1.4.6
2.1.3 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 All 57 releases
← All changes | src/Admin/Admin.php +92 -521 trunk1.4.6 View file →
@@ -13,13 +13,11 @@
13 13
14 14 namespace ThemeAtelier\Darkify\Admin;
15 15
16 16 use ThemeAtelier\Darkify\Admin\ReviewNotice\ReviewNotice;
17 -use ThemeAtelier\Darkify\Admin\ReviewNotice\ThemeAtelier_Offer_Banner;
18 -use ThemeAtelier\Darkify\Admin\Schema\SchemaDefaults;
19 17 use ThemeAtelier\Darkify\Admin\Views\DarkifyOptions;
20 -use ThemeAtelier\Darkify\Includes\DarkifyExternalSupport;
21 -use ThemeAtelier\Darkify\Includes\DarkifyUtils;
18 +use ThemeAtelier\Darkify\Helpers\DarkifyExternalSupport;
19 +use ThemeAtelier\Darkify\Helpers\DarkifyUtils;
22 20 use ThemeAtelier\Darkify\Admin\DBUpdates;
23 21
24 22 /**
25 23 * The admin class
@@ -27,8 +25,17 @@
27 25 class Admin
28 26 {
29 27
30 28 /**
29 + * The slug of this plugin.
30 + *
31 + * @since 1.0.0
32 + * @access private
33 + * @var string $plugin_slug The slug of this plugin.
34 + */
35 + private $plugin_slug;
36 +
37 + /**
31 38 * The min of this plugin.
32 39 *
33 40 * @since 1.0.0
34 41 * @access private
@@ -34,197 +41,77 @@
34 41 * @access private
35 42 * @var string $min The slug of this plugin.
36 43 */
37 44 private $min;
45 +
46 + /**
47 + * The version of this plugin.
48 + *
49 + * @since 1.0.0
50 + * @access private
51 + * @var string $version The current version of this plugin.
52 + */
53 + private $version;
38 54 public $utils;
39 55 public $settings;
40 56 public $external_support;
57 + public $unique_id;
41 58 private $plugin_name;
42 - private $version;
43 - public $unique_id;
44 59
45 - /**
46 - * Whether enqueue_scripts() actually put the admin engine in the queue on
47 - * this request. Read by darkify_restore_no_conflict_scripts(), which must
48 - * only re-add handles Darkify itself asked for.
49 - *
50 - * @var bool
51 - */
52 - private $engine_enqueued = false;
53 60
54 61 /**
55 62 * The class constructor.
56 63 *
57 - * @param string $plugin_name The slug of the plugin.
64 + * @param string $plugin_slug The slug of the plugin.
58 65 * @param string $version Current version of the plugin.
59 66 */
60 - public function __construct($plugin_name, $version)
67 + function __construct($plugin_slug, $version)
61 68 {
62 - $this->plugin_name = $plugin_name;
63 - $this->version = $version;
64 69 $this->utils = new DarkifyUtils($this);
65 70 $this->external_support = new DarkifyExternalSupport($this);
66 - $this->min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
67 71 if (function_exists('wp_rand')) {
68 72 $this->unique_id = wp_rand();
69 73 }
70 74
71 - add_action('current_screen', function ($screen) {
72 - $options = get_option('darkify');
73 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
74 - $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
75 - $is_block_editor = method_exists($screen, 'is_block_editor') && $screen->is_block_editor();
75 + $options = get_option('darkify');
76 76
77 - // Block Editor Dark Mode is INDEPENDENT of Admin Panel Dark Mode:
78 - // the block editor darkens whenever Block Editor Dark Mode is on,
79 - // even if the rest of wp-admin stays light. Every other admin screen
80 - // still keys on Admin Panel Dark Mode.
81 - $needs_engine = $is_block_editor
82 - ? (bool) $block_editor_dark_mode
83 - : (bool) $enable_admin_panel_dark_mode;
84 -
85 - // Darkify's own settings/help screens additionally load it even while
86 - // the option is OFF, so flipping "Admin Panel Dark Mode" in the React
87 - // admin can show the admin-bar icon (and have it actually work)
88 - // immediately instead of only after a reload. Loading it here cannot
89 - // darken anything on its own: both the FOUC snippet in
90 - // header_script.php and darkify_check_preloading() in client_main.js
91 - // are gated on `darkify_admin_panel_dark_enabled`.
92 - if ($needs_engine || $this->is_darkify_spa_page()) {
93 - add_action('admin_bar_menu', array($this, 'darkify_admin_bar_switch'), 9999);
94 - add_action('admin_print_scripts', array($this, 'darkify_admin_header_script'), 1);
95 - add_action('admin_footer', array($this, 'darkify_admin_footer_script'));
96 - }
97 - });
98 -
77 + $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
78 + $this->plugin_name = $plugin_slug;
79 + $this->min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
80 + add_filter('plugin_action_links_' . DARKIFY_BASENAME, array($this, 'add_plugin_action_links'), 10, 2);
81 + if ($enable_admin_panel_dark_mode) {
82 + add_action('admin_bar_menu', array($this, 'darkify_admin_bar_switch'), 9999);
83 + add_action('admin_print_scripts', array($this, 'darkify_admin_header_script'), 1);
84 + add_action('admin_footer', array($this, 'darkify_admin_footer_script'));
85 + }
86 + add_action('admin_menu', array($this, 'add_plugin_page'));
99 87 new ReviewNotice();
100 88 new DBUpdates();
101 - if (! defined('THEMEATELIER_OFFER_BANNER_LOADED')) {
102 - define('THEMEATELIER_OFFER_BANNER_LOADED', true);
103 - ThemeAtelier_Offer_Banner::instance();
104 - }
105 -
106 - // Building the schema is what evaluates every `__()` in
107 - // src/Admin/Views/*.php, so the text domain has to be registered first —
108 - // Darkify::load_textdomain() runs on this same hook at priority 1.
109 89 add_action('after_setup_theme', array($this, 'init_components'));
110 90 add_filter('autoptimize_filter_js_exclude', array($this, 'darkify_exclude_js_from_cache_plugins'));
111 -
112 - /*
113 - * "No-conflict mode" compatibility.
114 - *
115 - * Fluent Forms, FluentCRM, Fluent Booking, FluentSMTP, WP Social Ninja
116 - * and friends dequeue *every* script served from wp-content/plugins on
117 - * their own admin screens (Fluent Forms: app/Hooks/actions.php, on
118 - * `wp_print_scripts` priority 1). That runs after `admin_enqueue_scripts`,
119 - * so it strips Darkify's engine while the admin-bar node — printed by
120 - * `admin_bar_menu` and untouched by the dequeue — keeps its inline
121 - * onclick, producing "darkify_switch_trigger is not defined" and a dead
122 - * toggle on e.g. admin.php?page=fluent_forms.
123 - *
124 - * Two layers: the published allow-list filters where a plugin offers one,
125 - * and a generic re-enqueue after their sweep for the ones that don't.
126 - */
127 - add_filter('fluentform/exclude_js_slugs_from_dequeue', array($this, 'darkify_no_conflict_allow_slugs'));
128 - add_filter('fluent_crm_asset_listed_slugs', array($this, 'darkify_no_conflict_allow_slugs'));
129 - add_filter('fluent_booking/asset_listed_slugs', array($this, 'darkify_no_conflict_allow_slugs'));
130 - add_action('wp_print_scripts', array($this, 'darkify_restore_no_conflict_scripts'), 100);
131 - add_action('wp_print_styles', array($this, 'darkify_restore_no_conflict_styles'), 100);
132 -
133 - add_action('admin_init', [$this, 'load_classic_editor_scripts']);
134 - add_filter('admin_footer_text', array($this, 'darkify_admin_footer'), 1, 2);
135 - $active_plugins = get_option('active_plugins');
136 - foreach ($active_plugins as $active_plugin) {
137 - $_temp = strpos($active_plugin, 'darkify.php');
138 - if (false != $_temp) {
139 - add_filter('plugin_action_links_' . DARKIFY_BASENAME, array($this, 'add_plugin_action_links'), 10, 2);
140 - add_filter('plugin_row_meta', array($this, 'after_darkify_row_meta'), 10, 4);
141 - }
142 - }
143 91 }
144 92
145 - public function init_components()
146 - {
93 + public function init_components() {
147 94 DarkifyOptions::options('darkify');
148 - $this->seed_default_options();
149 95 }
150 96
151 - /**
152 - * Write the schema's defaults into the `darkify` option on a fresh install.
153 - *
154 - * Nothing used to create that option until the first save in the admin. Up to
155 - * that point the two halves of the plugin disagreed: the React admin resolved
156 - * each field from the schema (so the Switch Toggler showed Orbit, the declared
157 - * default), while the frontend read the raw option — found nothing — and fell
158 - * back to the literal in its own template, rendering the Classic switcher. Same
159 - * class of mismatch for every other field whose template fallback differs from
160 - * its declared default.
161 - *
162 - * Seeding makes the stored values the single source both sides read, so a site
163 - * ships with exactly what the admin displays before anyone touches Settings.
164 - *
165 - * Runs on `init` (every request, admin and frontend), right after
166 - * the config classes have registered their sections, and is a no-op once the
167 - * option exists — so it never overwrites a user's saved settings, and an
168 - * upgrade from an older version is left alone.
169 - */
170 - private function seed_default_options()
171 - {
172 - $existing = get_option('darkify');
173 - if (is_array($existing) && ! empty($existing)) {
174 - return;
175 - }
176 -
177 - $defaults = SchemaDefaults::for_option('darkify');
178 - if (empty($defaults)) {
179 - return;
180 - }
181 -
182 - // `update_option` rather than `add_option`: an install that already holds
183 - // an empty/corrupt value (a stray '' or false) gets seeded too.
184 - update_option('darkify', $defaults);
185 - }
186 -
187 - /**
188 - * Whether the current screen is one of Darkify's own React SPA screens
189 - * (Settings / Get Help) — the only place the Admin Panel Dark Mode option can
190 - * be toggled, and therefore the only place that needs the engine loaded up
191 - * front so the admin-bar icon can react instantly.
192 - */
193 - public function is_darkify_spa_page(): bool
194 - {
195 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen check.
196 - $page = isset($_GET['page']) ? \sanitize_key(\wp_unslash($_GET['page'])) : '';
197 - return \in_array($page, Assets::SPA_SLUGS, true);
198 - }
199 -
200 97 public function darkify_exclude_js_from_cache_plugins($excludes)
201 98 {
202 - // Dark Reader proxies document.styleSheets and injects its own <style>
203 - // elements; an optimizer that concatenates or defers it changes when it
204 - // sees the page's sheets, which shows up as a half-darkened admin.
205 - $excludes .= ',client_main.js,client_main.min.js'
206 - . ',darkreader.js,darkreader.min.js'
207 - . ',admin_darkreader.js,admin_darkreader.min.js';
99 + $excludes .= ',client_main.js,client_main.min.js';
208 100 return $excludes;
209 101 }
210 102
211 - public function enqueue_styles()
103 + public function enqueue_styles($hook)
212 104 {
105 + if($hook === 'toplevel_page_darkify') {
106 + wp_enqueue_style('darkify-help-page', DARKIFY_DIR_URL . 'src/Admin/HelpPage/assets/css/help-page' . $this->min . '.css', array(), DARKIFY_VERSION, 'all');
107 + }
108 +
213 109 $options = get_option('darkify');
214 110 $enable_admin_panel_dark_mode = isset($options["enable_admin_panel_dark_mode"]) ? $options["enable_admin_panel_dark_mode"] : false;
215 - // Block Editor Dark Mode darkens the editor independently of Admin Panel
216 - // Dark Mode, so the stylesheet must load there too.
217 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
218 - $screen = \function_exists('get_current_screen') ? \get_current_screen() : null;
219 - $is_block_editor = $screen && \method_exists($screen, 'is_block_editor') && $screen->is_block_editor();
220 - $allow_admin_dark = $enable_admin_panel_dark_mode || ($is_block_editor && $block_editor_dark_mode);
221 - // Darkify's own screens always get the stylesheet so the admin-bar icon
222 - // can be revealed the moment the option is switched on. It only styles
223 - // `.darkify_*` classes, so it is inert until the engine adds them.
224 - if (($allow_admin_dark || $this->is_darkify_spa_page()) && $this->darkify_is_dark_mode_allowed()) {
111 + if ($enable_admin_panel_dark_mode) {
225 112 if (!wp_style_is('darkify-admin-switch', 'enqueued')) {
226 - wp_enqueue_style('darkify-admin-switch', DARKIFY_DIR_URL . 'src/assets/css/client_main' . $this->min . '.css', array(), $this->darkify_asset_version('src/assets/css/client_main' . $this->min . '.css'), 'all');
113 + wp_enqueue_style('darkify-admin-switch', DARKIFY_DIR_URL . 'src/assets/css/client_main' . $this->min . '.css', array(), DARKIFY_VERSION, 'all');
227 114 }
228 115 }
229 116 // Review notice CSS
230 117 wp_enqueue_style('darkify-review-notice', DARKIFY_DIR_URL . 'src/Admin/ReviewNotice/assets/css/review-notice' . $this->min . '.css', array(), DARKIFY_VERSION, 'all');
@@ -233,403 +120,87 @@
233 120 public function enqueue_scripts($hook)
234 121 {
235 122 $options = get_option('darkify');
236 123 $enable_admin_panel_dark_mode = isset($options["enable_admin_panel_dark_mode"]) ? $options["enable_admin_panel_dark_mode"] : false;
237 - $is_spa = $this->is_darkify_spa_page();
238 - if (! $this->darkify_is_dark_mode_allowed()) {
239 - return;
124 + if ($enable_admin_panel_dark_mode) {
125 + wp_enqueue_script('darkify-admin-client-main', DARKIFY_DIR_URL . 'src/assets/js/client_main' . $this->min . '.js', array(), DARKIFY_VERSION);
240 126 }
241 127
242 - // Mirror the `current_screen` engine-load rule exactly, so the engine is
243 - // only enqueued where its inline config (header_script.php) is ALSO
244 - // printed. Block Editor Dark Mode is independent of Admin Panel Dark
245 - // Mode: the block editor loads the engine when Block Editor Dark Mode is
246 - // on (regardless of Admin Panel Dark Mode); every other admin screen
247 - // keys on Admin Panel Dark Mode. (A SPA screen always gets the config.)
248 - if (! $is_spa) {
249 - $screen = \function_exists('get_current_screen') ? \get_current_screen() : null;
250 - $is_block_editor = $screen && \method_exists($screen, 'is_block_editor') && $screen->is_block_editor();
251 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
252 - $allow = $is_block_editor ? (bool) $block_editor_dark_mode : (bool) $enable_admin_panel_dark_mode;
253 - if (! $allow) {
254 - return;
255 - }
128 + if($hook === 'toplevel_page_darkify') {
129 + wp_enqueue_script('darkify-help-page', DARKIFY_DIR_URL . 'src/Admin/HelpPage/assets/js/help-page' . $this->min . '.js', array('darkify-plugins'), DARKIFY_VERSION, true);
256 130 }
257 -
258 - /*
259 - * wp-admin runs the Dark Reader engine, NOT the per-element classifier in
260 - * client_main.js. That engine walks every element and reads
261 - * getComputedStyle on each, then re-walks on DOM mutation; wp-admin (the
262 - * block editor especially) mutates continuously, so it never settles and
263 - * the editor locks up. Dark Reader works per stylesheet instead, so the
264 - * cost does not scale with element churn at all.
265 - *
266 - * The frontend is untouched and still runs client_main.js.
267 - * See src/assets/js/admin_darkreader.js for the full rationale.
268 - */
269 - $darkify_lib_rel = 'src/assets/js/vendor/darkreader.min.js';
270 - $darkify_engine_rel = 'src/assets/js/admin_darkreader' . $this->min . '.js';
271 - $darkify_lib_url = DARKIFY_DIR_URL . $darkify_lib_rel;
272 -
273 - /*
274 - * Both in <head> (in_footer = false), deliberately. Dark Reader does not
275 - * need a parsed body to start, and enabling it before first paint is what
276 - * keeps the admin from flashing light and then darkening.
277 - */
278 - wp_enqueue_script(
279 - 'darkify-darkreader',
280 - $darkify_lib_url,
281 - array(),
282 - $this->darkify_asset_version($darkify_lib_rel),
283 - false
284 - );
285 -
286 - /*
287 - * The block editor canvas and the classic editor's TinyMCE body are
288 - * separate documents, and Dark Reader binds the realm it was loaded into.
289 - * The engine injects its own copy into each same-origin frame, so it
290 - * needs a URL it can point a <script> at from inside that frame.
291 - *
292 - * Darkify's own React settings screens are excluded from Dark Reader:
293 - * they ship a full dark theme of their own (`.dark` in
294 - * darkify-react/src/index.css) which also covers the surrounding wp-admin
295 - * chrome, so darkening them again produces a double-dark UI — and because
296 - * Dark Reader 4.9 cannot parse the `oklch()` values those tokens use, the
297 - * unresolved properties settle on `--destructive` and paint card borders
298 - * and toggles red. The engine still loads there: it owns the toggle the
299 - * admin-bar icon calls, and the SPA mirrors the class onto `.dark` itself.
300 - */
301 - wp_add_inline_script(
302 - 'darkify-darkreader',
303 - 'window.darkifyDarkReaderSrc = ' . wp_json_encode($darkify_lib_url) . ';'
304 - . 'window.darkifyAdminSelfThemed = ' . ($is_spa ? 'true' : 'false') . ';',
305 - 'before'
306 - );
307 -
308 - wp_enqueue_script(
309 - 'darkify-admin-engine',
310 - DARKIFY_DIR_URL . $darkify_engine_rel,
311 - array('darkify-darkreader'),
312 - $this->darkify_asset_version($darkify_engine_rel),
313 - false
314 - );
315 -
316 - $this->engine_enqueued = true;
317 131 }
318 132
319 - /**
320 - * Add Darkify to a no-conflict allow-list.
321 - *
322 - * Both filter shapes this is attached to pass an array of substrings matched
323 - * against the script `src`, so the plugin folder name is the right token.
324 - *
325 - * @param array $slugs Allowed slugs.
326 - *
327 - * @return array
328 - */
329 - public function darkify_no_conflict_allow_slugs($slugs)
330 - {
331 - if (! is_array($slugs)) {
332 - return $slugs;
333 - }
334 -
335 - $slugs[] = 'darkify';
336 -
337 - return array_unique($slugs);
338 - }
339 -
340 - /**
341 - * Put the admin engine back after a no-conflict sweep dropped it.
342 - *
343 - * Runs on `wp_print_scripts` at 100 — after the sweeps, which all hook it at
344 - * 1 — and before `do_items()`, so a re-enqueue here still prints. Gated on
345 - * `$engine_enqueued`, so it never resurrects a script Darkify itself chose
346 - * not to load (or dequeued in admin_dequeue_for_specefic_pages).
347 - */
348 - public function darkify_restore_no_conflict_scripts()
349 - {
350 - if (! $this->engine_enqueued || ! is_admin()) {
351 - return;
352 - }
353 -
354 - foreach (array('darkify-darkreader', 'darkify-admin-engine') as $handle) {
355 - if (wp_script_is($handle, 'registered') && ! wp_script_is($handle, 'enqueued')) {
356 - wp_enqueue_script($handle);
357 - }
358 - }
359 - }
360 -
361 - /**
362 - * Same sweep, styles side: the engine adds `.darkify_dark_mode_enabled` and
363 - * the switch classes, and client_main.css is what they mean. Losing only the
364 - * stylesheet leaves a half-styled admin bar icon.
365 - */
366 - public function darkify_restore_no_conflict_styles()
367 - {
368 - if (! $this->engine_enqueued || ! is_admin()) {
369 - return;
370 - }
371 -
372 - if (wp_style_is('darkify-admin-switch', 'registered') && ! wp_style_is('darkify-admin-switch', 'enqueued')) {
373 - wp_enqueue_style('darkify-admin-switch');
374 - }
375 - }
376 -
377 - /**
378 - * Cache-busting version for a bundled asset: the plugin version plus the
379 - * file's mtime, so rebuilding client_main.js/.css (which does NOT bump the
380 - * plugin version) still changes the URL and browsers fetch the new file
381 - * instead of serving a stale one. Mirrors Admin\Assets::asset_version().
382 - */
383 - public function darkify_asset_version(string $relative_path): string
384 - {
385 - $abs = DARKIFY_PATH . $relative_path;
386 - $mtime = \is_readable($abs) ? \filemtime($abs) : false;
387 - return $mtime ? DARKIFY_VERSION . '.' . $mtime : DARKIFY_VERSION;
388 - }
389 -
390 - /*
391 - Dequeue Darkify scripts and styles on specific admin pages (e.g., MainWP pages)
392 - */
393 -
394 - public function admin_dequeue_for_specefic_pages()
395 - {
396 - $page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
397 - $dequeue_pages = array(
398 - 'mainwp_tab',
399 - 'managesites',
400 - 'ManageClients',
401 - 'CostSummary',
402 - 'InsightsOverview',
403 - 'Extensions',
404 - 'RESTAPI',
405 - 'mainwp-setup',
406 - 'ThemesManage',
407 - 'PluginsManage',
408 - 'InsightsManage',
409 - 'ManageGroups',
410 - 'UpdatesManage',
411 - 'PluginsInstall',
412 - 'PluginsAutoUpdate',
413 - 'PluginsIgnore',
414 - 'PluginsAbandoned',
415 - 'PluginsIgnoredAbandoned',
416 - 'ThemesInstall',
417 - 'ThemesAutoUpdate',
418 - 'ThemesIgnore',
419 - 'ThemesAbandoned',
420 - 'ThemesIgnoredAbandoned',
421 - 'UserBulkManage',
422 - 'UserBulkAdd',
423 - 'BulkImportUsers',
424 - 'UpdateAdminPasswords',
425 - 'PostBulkManage',
426 - 'Extensions-Mainwp-Backups',
427 - 'Extensions-Mainwp-Security',
428 - 'Extensions-Mainwp-Analytics',
429 - 'Mainwp-Monitoring',
430 - 'Extensions-Mainwp-Agency',
431 - 'Extensions-Mainwp-Administrative',
432 - 'Extensions-Mainwp-Development',
433 - 'Extensions-Mainwp-Performance',
434 - 'ClientAddNew',
435 - 'ClientImport',
436 - 'ManageCostTracker',
437 - 'CostTrackerAdd',
438 - 'AddApiKeys',
439 - );
440 -
441 -
442 - if (in_array($page, $dequeue_pages, true)) {
443 - wp_deregister_style('darkify-admin-switch');
444 - wp_dequeue_script('darkify-admin-client-main');
445 - wp_dequeue_script('darkify-admin-engine');
446 - wp_dequeue_script('darkify-darkreader');
447 - // Keep darkify_restore_no_conflict_scripts() from undoing this.
448 - $this->engine_enqueued = false;
449 - }
450 - }
451 -
452 - /**
453 - * Replace wp-admin's footer credit with Darkify's review request, on
454 - * Darkify's own screens only.
455 - *
456 - * Screens are matched by page slug (`is_darkify_spa_page()`) rather than by
457 - * a hardcoded screen id, so both the Settings and Get Help SPA screens match
458 - * and the check keeps working if the menu is ever retitled. The slug list is
459 - * the same source of truth the assets already load from.
460 - *
461 - * @param string $text Existing footer text.
462 - *
463 - * @return string
464 - */
465 - public function darkify_admin_footer($text)
466 - {
467 - if (! $this->is_darkify_spa_page()) {
468 - return $text;
469 - }
470 -
471 - return sprintf(
472 - /* translators: 1: start strong tag, 2: close strong tag. 3: start link 4: close link */
473 - __('<i>Enjoying %1$sDarkify?%2$s Please rate us %3$sWordPress.org%4$s. Your positive feedback will help us grow more. Thank you! 😊</i>', 'darkify'),
474 - '<strong>',
475 - '</strong>',
476 - '<span class="greet-footer-text-star">★★★★★</span> <a href="https://wordpress.org/support/plugin/darkify/reviews/#new-post" target="_blank" rel="noopener noreferrer">',
477 - '</a>'
478 - );
479 - }
480 -
481 133 // Plugin settings in plugin list
482 134 public function add_plugin_action_links(array $links)
483 135 {
484 136 $new_links = array(
485 137 sprintf('<a href="' . esc_url(admin_url('admin.php?page=darkify')) . '">' . esc_html__('Settings', 'darkify') . '</a>'),
486 - sprintf('<a target="_blank" href="https://wordpress.org/support/plugin/darkify/">' . esc_html__('Support', 'darkify') . '</a>'),
138 + sprintf('<a target="_blank" href="https://themeatelier.net/contact">' . esc_html__('Support', 'darkify') . '</a>'),
139 + sprintf('<a style="font-weight: bold;color:#171717" target="_blank" href="https://darkifywp.com/pricing/?ref=1">' . esc_html__('Go Pro', 'darkify') . '</a>'),
487 140 );
488 -
489 - $links[] = sprintf('<a style="font-weight: bold;color:#35b747" target="_blank" href="https://darkifywp.com/pricing/?utm_source=darkify_plugin&utm_medium=action_link&utm_campaign=regular">%s</a>', esc_html__('Go Pro!', 'darkify'));
490 -
491 141 return array_merge($new_links, $links);
492 142 }
493 143
494 - /**
495 - * Add plugin row meta link.
496 - *
497 - * @since 2.0
498 - *
499 - * @param array $plugin_meta .
500 - * @param string $file .
501 - *
502 - * @return array
503 - */
504 - public function after_darkify_row_meta($plugin_meta, $file)
505 - {
506 144
507 - if (DARKIFY_BASENAME === $file) {
508 - $plugin_meta[] = '<a href="' . DARKIFY_DEMO_URL . '" target="_blank">' . __('Live Demo', 'darkify') . '</a>';
509 - }
510 -
511 - return $plugin_meta;
512 - }
513 -
514 145 public function darkify_admin_bar_switch($wp_admin_bar)
515 146 {
516 - if (! $this->darkify_is_dark_mode_allowed()) {
517 - return;
518 - }
519 -
520 - /*
521 - * The block editor already carries its own toggle in the top toolbar
522 - * (see Frontend/Templates/footer_script.php), firing the same
523 - * darkify_switch_trigger() this node would — two controls for one state,
524 - * side by side.
525 - *
526 - * The toolbar one is the keeper, not this one: Gutenberg hides the WP
527 - * admin bar in fullscreen mode, which is the default, so this node is
528 - * simply absent for most people editing a post. Dropping the toolbar
529 - * button instead would leave them no toggle at all.
530 - *
531 - * The classic editor is deliberately not included here. Its TinyMCE moon
532 - * toggles only the content frame, while this switch toggles the whole
533 - * admin — two different controls, so both belong on screen.
534 - */
535 - $screen = \function_exists('get_current_screen') ? \get_current_screen() : null;
536 - if ($screen && \method_exists($screen, 'is_block_editor') && $screen->is_block_editor()) {
537 - return;
538 - }
539 -
540 - $options = get_option('darkify');
541 - $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
542 -
543 - // Everywhere but Darkify's own screens the node is only registered when
544 - // the option is on, so it is always visible. On the SPA screens it is
545 - // registered regardless and starts hidden while the option is off — the
546 - // React admin then just flips this class (see WpAdminBarBridge), which is
547 - // what makes the icon appear/disappear without a reload.
548 - $classes = 'darkify_admin_bar_switch_container';
549 - if (! $enable_admin_panel_dark_mode) {
550 - $classes .= ' darkify_admin_bar_hidden';
551 - }
552 -
553 - $wp_admin_bar->add_node(array(
147 + $args = array(
554 148 'parent' => 'top-secondary',
555 149 'id' => 'darkify_admin_bar_switch_container',
556 150 'meta' => array(
557 - 'class' => $classes,
558 - // Guarded call: a plugin's "no-conflict mode" can still strip the
559 - // engine on its own screens (see the compatibility hooks in the
560 - // constructor). A missing function should be an inert click, not
561 - // an uncaught ReferenceError in every visitor's console.
562 - // No quotes in the expression: the admin bar escapes `meta`
563 - // attributes, and the escaped quotes come back as literal
564 - // entities inside the handler ("Invalid or unexpected token").
565 - 'onclick' => 'window.darkify_switch_trigger&&window.darkify_switch_trigger()',
566 - ),
567 - ));
151 + 'class' => 'darkify_admin_bar_switch_container',
152 + 'onclick' => 'darkify_switch_trigger()',
153 + )
154 + );
155 + $wp_admin_bar->add_node($args);
568 156 }
569 157
570 - public function darkify_is_dark_mode_allowed()
571 - {
572 -
573 - $options = get_option('darkify');
574 - $options = is_array($options) ? $options : array();
575 - // Guarded read: `disallowed_admin_pages` has no schema default, so the
576 - // React settings save doesn't persist it until the field is used. Reading
577 - // it unguarded would emit an "Undefined array key" warning on every
578 - // dark-mode hook (see the Pro plugin's Admin.php for the full story).
579 - $disallowed_admin_pages = isset($options['disallowed_admin_pages']) ? $options['disallowed_admin_pages'] : '';
580 - /* Disable on Disallowed Admin Plugins */
581 - if ($this->utils->isRestrictedByDisallowedAdminPages($disallowed_admin_pages)) {
582 - return False;
583 - }
584 - return True;
585 - }
586 -
587 158 public function darkify_admin_header_script()
588 159 {
589 - if ($this->darkify_is_dark_mode_allowed()) {
590 - include_once DarkifyUtils::darkify_locate_template('header_script.php');
591 - }
160 + include_once DarkifyUtils::darkify_locate_template('header_script.php');
592 161 }
593 162
594 163 public function darkify_admin_footer_script()
595 164 {
596 - if ($this->darkify_is_dark_mode_allowed()) {
597 - include_once DarkifyUtils::darkify_locate_template('footer_script.php');
598 - }
165 + include_once DarkifyUtils::darkify_locate_template('footer_script.php');
599 166 }
600 167
601 - /**
602 - * Load classic editor scripts.
603 - *
604 - * @since 5.0.0
605 - */
606 - public function load_classic_editor_scripts()
168 + public function add_plugin_page()
607 169 {
608 - $options = get_option('darkify');
609 - $classic_editor_dark_mode = isset($options['classic_editor_dark_mode']) ? $options['classic_editor_dark_mode'] : '';
610 - if (! current_user_can('edit_posts') && ! current_user_can('edit_pages')) {
611 - return;
612 - }
613 - if (get_user_option('rich_editing') !== 'true') {
614 - return;
615 - }
170 + // This page will be under "Settings"
171 + add_menu_page(
172 + esc_html__('Darkify', 'darkify'),
173 + esc_html__('Darkify', 'darkify'),
174 + 'manage_options',
175 + 'darkify',
176 + [$this, 'darkify_settings'],
177 + 'data:image/svg+xml;base64,' . base64_encode('<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
178 + <path d="M23.5 1.5C23.5 0.671573 22.8284 0 22 0C21.1716 0 20.5 0.671573 20.5 1.5V4.5C20.5 5.32843 21.1716 6 22 6C22.8284 6 23.5 5.32843 23.5 4.5V1.5Z" fill="white" />
179 + <path d="M34 22C34 28.6274 28.6274 34 22 34C15.3726 34 10 28.6274 10 22C10 15.3726 15.3726 10 22 10C28.6274 10 34 15.3726 34 22Z" fill="white" />
180 + <path d="M22 38C22.8284 38 23.5 38.6716 23.5 39.5V42.5C23.5 43.3284 22.8284 44 22 44C21.1716 44 20.5 43.3284 20.5 42.5V39.5C20.5 38.6716 21.1716 38 22 38Z" fill="white" />
181 + <path d="M10.6863 10.6863C10.1005 11.2721 9.15074 11.2721 8.56495 10.6863L6.44363 8.56498C5.85784 7.97919 5.85784 7.02945 6.44363 6.44366C7.02942 5.85787 7.97916 5.85787 8.56495 6.44366L10.6863 8.56498C11.2721 9.15077 11.2721 10.1005 10.6863 10.6863Z" fill="white" />
182 + <path d="M35.435 37.5564C36.0208 38.1421 36.9705 38.1421 37.5563 37.5564C38.1421 36.9706 38.1421 36.0208 37.5563 35.435L35.435 33.3137C34.8492 32.7279 33.8995 32.7279 33.3137 33.3137C32.7279 33.8995 32.7279 34.8493 33.3137 35.435L35.435 37.5564Z" fill="white" />
183 + <path d="M10.6863 33.3137C11.2721 33.8995 11.2721 34.8492 10.6863 35.435L8.56495 37.5563C7.97916 38.1421 7.02942 38.1421 6.44363 37.5563C5.85784 36.9706 5.85784 36.0208 6.44363 35.435L8.56495 33.3137C9.15074 32.7279 10.1005 32.7279 10.6863 33.3137Z" fill="white" />
184 + <path d="M37.5563 8.56496C38.1421 7.97918 38.1421 7.02943 37.5563 6.44364C36.9705 5.85786 36.0208 5.85786 35.435 6.44364L33.3137 8.56496C32.7279 9.15075 32.7279 10.1005 33.3137 10.6863C33.8995 11.2721 34.8492 11.2721 35.435 10.6863L37.5563 8.56496Z" fill="white" />
185 + <path d="M6 22C6 22.8284 5.32843 23.5 4.5 23.5H1.5C0.671573 23.5 0 22.8284 0 22C0 21.1716 0.671573 20.5 1.5 20.5H4.5C5.32843 20.5 6 21.1716 6 22Z" fill="white" />
186 + <path d="M42.5 23.5C43.3284 23.5 44 22.8284 44 22C44 21.1716 43.3284 20.5 42.5 20.5H39.5C38.6716 20.5 38 21.1716 38 22C38 22.8284 38.6716 23.5 39.5 23.5H42.5Z" fill="white" />
187 + </svg>'),
188 + 9
189 + );
190 + do_action('darkify_before_upgrade_pro_menu');
191 + add_submenu_page(
192 + 'darkify',
193 + __(
194 + '👑 Upgrade to Pro!',
195 + 'darkify'
196 + ),
197 + sprintf('<span style="color: #35b747;font-weight: bold;" class="darkify-get-pro-text">%s</span>', __('👑 Upgrade to Pro!', 'darkify')),
198 + 'manage_options',
199 + 'https://darkifywp.com/pricing/?ref=1'
200 + );
201 + do_action('darkify_after_upgrade_pro_menu');
202 + }
616 203
617 - // Bail if admin_classic_editor is not enabled.
618 - if (! wp_validate_boolean($classic_editor_dark_mode)) {
619 - return;
620 - }
204 + public function darkify_settings() {}
621 205
622 - add_filter('mce_external_plugins', function ($plugins) {
623 - // Honour the same debug switch as every other bundled asset; gulp
624 - // builds the .min beside it and this was the one enqueue still
625 - // pointing at the readable copy in production.
626 - $plugins['darkify_button'] = plugins_url('src/assets/js/admin-classic-editor' . $this->min . '.js', DARKIFY_FILE);
627 - return $plugins;
628 - });
629 -
630 - add_filter('mce_buttons', function ($buttons) {
631 - $buttons[] = 'darkify_button';
632 - return $buttons;
633 - });
634 - }
635 206 }