PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 1.3.2
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v1.3.2
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 +149 -427 2.1.11.3.2 View file →
@@ -12,15 +12,13 @@
12 12 */
13 13
14 14 namespace ThemeAtelier\Darkify\Admin;
15 15
16 -use ThemeAtelier\Darkify\Admin\ReviewNotice\ReviewNotice;
17 -use ThemeAtelier\Darkify\Admin\ReviewNotice\ThemeAtelier_Offer_Banner;
18 -use ThemeAtelier\Darkify\Admin\Schema\SchemaDefaults;
16 +use ThemeAtelier\Darkify\Admin\TADiscountPage\TADiscountPage;
19 17 use ThemeAtelier\Darkify\Admin\Views\DarkifyOptions;
20 -use ThemeAtelier\Darkify\Includes\DarkifyExternalSupport;
21 -use ThemeAtelier\Darkify\Includes\DarkifyUtils;
22 -use ThemeAtelier\Darkify\Admin\DBUpdates;
18 +use ThemeAtelier\Darkify\Helpers\DarkifyExternalSupport;
19 +use ThemeAtelier\Darkify\Helpers\DarkifyUtils;
20 +use ThemeAtelier\Darkify\Helpers\Helpers;
23 21
24 22 /**
25 23 * The admin class
26 24 */
@@ -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,436 +41,155 @@
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 +
58 + public $unique_id;
59 +
41 60 private $plugin_name;
42 - private $version;
43 - public $unique_id;
44 61
45 62 /**
46 63 * The class constructor.
47 64 *
48 - * @param string $plugin_name The slug of the plugin.
65 + * @param string $plugin_slug The slug of the plugin.
49 66 * @param string $version Current version of the plugin.
50 67 */
51 - public function __construct($plugin_name, $version)
68 + function __construct($plugin_slug, $version)
52 69 {
53 - $this->plugin_name = $plugin_name;
54 - $this->version = $version;
55 70 $this->utils = new DarkifyUtils($this);
56 71 $this->external_support = new DarkifyExternalSupport($this);
57 - $this->min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
58 72 if (function_exists('wp_rand')) {
59 73 $this->unique_id = wp_rand();
60 74 }
75 + DarkifyOptions::options('darkify');
61 76
62 - add_action('current_screen', function ($screen) {
63 - $options = get_option('darkify');
64 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
65 - $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
66 - $is_block_editor = method_exists($screen, 'is_block_editor') && $screen->is_block_editor();
77 + $options = get_option('darkify');
78 + $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
79 + $this->plugin_name = $plugin_slug;
67 80
68 - // Block Editor Dark Mode is INDEPENDENT of Admin Panel Dark Mode:
69 - // the block editor darkens whenever Block Editor Dark Mode is on,
70 - // even if the rest of wp-admin stays light. Every other admin screen
71 - // still keys on Admin Panel Dark Mode.
72 - $needs_engine = $is_block_editor
73 - ? (bool) $block_editor_dark_mode
74 - : (bool) $enable_admin_panel_dark_mode;
81 + add_filter('plugin_action_links', array($this, 'add_plugin_action_links'), 10, 2);
75 82
76 - // Darkify's own settings/help screens additionally load it even while
77 - // the option is OFF, so flipping "Admin Panel Dark Mode" in the React
78 - // admin can show the admin-bar icon (and have it actually work)
79 - // immediately instead of only after a reload. Loading it here cannot
80 - // darken anything on its own: both the FOUC snippet in
81 - // header_script.php and darkify_check_preloading() in client_main.js
82 - // are gated on `darkify_admin_panel_dark_enabled`.
83 - if ($needs_engine || $this->is_darkify_spa_page()) {
84 - add_action('admin_bar_menu', array($this, 'darkify_admin_bar_switch'), 9999);
85 - add_action('admin_print_scripts', array($this, 'darkify_admin_header_script'), 1);
86 - add_action('admin_footer', array($this, 'darkify_admin_footer_script'));
87 - }
88 - });
83 + add_action('admin_enqueue_scripts', array($this, 'darkify_admin_enqueue'));
89 84
90 - new ReviewNotice();
91 - new DBUpdates();
92 - if (! defined('THEMEATELIER_OFFER_BANNER_LOADED')) {
93 - define('THEMEATELIER_OFFER_BANNER_LOADED', true);
94 - ThemeAtelier_Offer_Banner::instance();
85 + if ($enable_admin_panel_dark_mode) {
86 + add_action('admin_bar_menu', array($this, 'darkify_admin_bar_switch'), 9999);
87 + add_action('admin_print_scripts', array($this, 'darkify_admin_header_script'), 1);
88 + add_action('admin_footer', array($this, 'darkify_admin_footer_script'));
95 89 }
96 90
97 - // Building the schema is what evaluates every `__()` in
98 - // src/Admin/Views/*.php, so the text domain has to be registered first —
99 - // Darkify::load_textdomain() runs on this same hook at priority 1.
100 - add_action('after_setup_theme', array($this, 'init_components'));
101 - add_filter('autoptimize_filter_js_exclude', array($this, 'darkify_exclude_js_from_cache_plugins'));
102 - add_action('admin_init', [$this, 'load_classic_editor_scripts']);
103 - add_filter('admin_footer_text', array($this, 'darkify_admin_footer'), 1, 2);
104 - $active_plugins = get_option('active_plugins');
105 - foreach ($active_plugins as $active_plugin) {
106 - $_temp = strpos($active_plugin, 'darkify.php');
107 - if (false != $_temp) {
108 - add_filter('plugin_action_links_' . DARKIFY_BASENAME, array($this, 'add_plugin_action_links'), 10, 2);
109 - add_filter('plugin_row_meta', array($this, 'after_darkify_row_meta'), 10, 4);
110 - }
111 - }
91 + add_action('admin_menu', array($this, 'add_plugin_page'));
92 + new TADiscountPage();
112 93 }
113 94
114 - public function init_components()
115 - {
116 - DarkifyOptions::options('darkify');
117 - $this->seed_default_options();
118 - }
119 -
120 - /**
121 - * Write the schema's defaults into the `darkify` option on a fresh install.
122 - *
123 - * Nothing used to create that option until the first save in the admin. Up to
124 - * that point the two halves of the plugin disagreed: the React admin resolved
125 - * each field from the schema (so the Switch Toggler showed Orbit, the declared
126 - * default), while the frontend read the raw option — found nothing — and fell
127 - * back to the literal in its own template, rendering the Classic switcher. Same
128 - * class of mismatch for every other field whose template fallback differs from
129 - * its declared default.
130 - *
131 - * Seeding makes the stored values the single source both sides read, so a site
132 - * ships with exactly what the admin displays before anyone touches Settings.
133 - *
134 - * Runs on `init` (every request, admin and frontend), right after
135 - * the config classes have registered their sections, and is a no-op once the
136 - * option exists — so it never overwrites a user's saved settings, and an
137 - * upgrade from an older version is left alone.
138 - */
139 - private function seed_default_options()
140 - {
141 - $existing = get_option('darkify');
142 - if (is_array($existing) && ! empty($existing)) {
143 - return;
144 - }
145 -
146 - $defaults = SchemaDefaults::for_option('darkify');
147 - if (empty($defaults)) {
148 - return;
149 - }
150 -
151 - // `update_option` rather than `add_option`: an install that already holds
152 - // an empty/corrupt value (a stray '' or false) gets seeded too.
153 - update_option('darkify', $defaults);
154 - }
155 -
156 - /**
157 - * Whether the current screen is one of Darkify's own React SPA screens
158 - * (Settings / Get Help) — the only place the Admin Panel Dark Mode option can
159 - * be toggled, and therefore the only place that needs the engine loaded up
160 - * front so the admin-bar icon can react instantly.
161 - */
162 - public function is_darkify_spa_page(): bool
163 - {
164 - // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen check.
165 - $page = isset($_GET['page']) ? \sanitize_key(\wp_unslash($_GET['page'])) : '';
166 - return \in_array($page, Assets::SPA_SLUGS, true);
167 - }
168 -
169 - public function darkify_exclude_js_from_cache_plugins($excludes)
170 - {
171 - // Dark Reader proxies document.styleSheets and injects its own <style>
172 - // elements; an optimizer that concatenates or defers it changes when it
173 - // sees the page's sheets, which shows up as a half-darkened admin.
174 - $excludes .= ',client_main.js,client_main.min.js'
175 - . ',darkreader.js,darkreader.min.js'
176 - . ',admin_darkreader.js,admin_darkreader.min.js';
177 - return $excludes;
178 - }
179 -
180 95 public function enqueue_styles()
181 96 {
182 - $options = get_option('darkify');
183 - $enable_admin_panel_dark_mode = isset($options["enable_admin_panel_dark_mode"]) ? $options["enable_admin_panel_dark_mode"] : false;
184 - // Block Editor Dark Mode darkens the editor independently of Admin Panel
185 - // Dark Mode, so the stylesheet must load there too.
186 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
187 - $screen = \function_exists('get_current_screen') ? \get_current_screen() : null;
188 - $is_block_editor = $screen && \method_exists($screen, 'is_block_editor') && $screen->is_block_editor();
189 - $allow_admin_dark = $enable_admin_panel_dark_mode || ($is_block_editor && $block_editor_dark_mode);
190 - // Darkify's own screens always get the stylesheet so the admin-bar icon
191 - // can be revealed the moment the option is switched on. It only styles
192 - // `.darkify_*` classes, so it is inert until the engine adds them.
193 - if (($allow_admin_dark || $this->is_darkify_spa_page()) && $this->darkify_is_dark_mode_allowed()) {
194 - if (!wp_style_is('darkify-admin-switch', 'enqueued')) {
195 - 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');
196 - }
197 - }
198 - // Review notice CSS
199 - wp_enqueue_style('darkify-review-notice', DARKIFY_DIR_URL . 'src/Admin/ReviewNotice/assets/css/review-notice' . $this->min . '.css', array(), DARKIFY_VERSION, 'all');
97 + wp_enqueue_style('ico-font', DARKIFY_DIR_URL . 'src/assets/css/icofont.css', array(), DARKIFY_VERSION, 'all');
98 + wp_enqueue_style('darkify-admin', DARKIFY_DIR_URL . 'src/Admin/assets/css/darkify-admin.css', array(), DARKIFY_VERSION, 'all');
200 99 }
201 100
202 101 public function enqueue_scripts($hook)
203 102 {
204 - $options = get_option('darkify');
205 - $enable_admin_panel_dark_mode = isset($options["enable_admin_panel_dark_mode"]) ? $options["enable_admin_panel_dark_mode"] : false;
206 - $is_spa = $this->is_darkify_spa_page();
207 - if (! $this->darkify_is_dark_mode_allowed()) {
103 + if ($hook !== 'toplevel_page_darkify') {
208 104 return;
209 105 }
210 -
211 - // Mirror the `current_screen` engine-load rule exactly, so the engine is
212 - // only enqueued where its inline config (header_script.php) is ALSO
213 - // printed. Block Editor Dark Mode is independent of Admin Panel Dark
214 - // Mode: the block editor loads the engine when Block Editor Dark Mode is
215 - // on (regardless of Admin Panel Dark Mode); every other admin screen
216 - // keys on Admin Panel Dark Mode. (A SPA screen always gets the config.)
217 - if (! $is_spa) {
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 - $block_editor_dark_mode = isset($options['block_editor_dark_mode']) ? $options['block_editor_dark_mode'] : false;
221 - $allow = $is_block_editor ? (bool) $block_editor_dark_mode : (bool) $enable_admin_panel_dark_mode;
222 - if (! $allow) {
223 - return;
224 - }
225 - }
226 -
227 - /*
228 - * wp-admin runs the Dark Reader engine, NOT the per-element classifier in
229 - * client_main.js. That engine walks every element and reads
230 - * getComputedStyle on each, then re-walks on DOM mutation; wp-admin (the
231 - * block editor especially) mutates continuously, so it never settles and
232 - * the editor locks up. Dark Reader works per stylesheet instead, so the
233 - * cost does not scale with element churn at all.
234 - *
235 - * The frontend is untouched and still runs client_main.js.
236 - * See src/assets/js/admin_darkreader.js for the full rationale.
237 - */
238 - $darkify_lib_rel = 'src/assets/js/vendor/darkreader.min.js';
239 - $darkify_engine_rel = 'src/assets/js/admin_darkreader' . $this->min . '.js';
240 - $darkify_lib_url = DARKIFY_DIR_URL . $darkify_lib_rel;
241 -
242 - /*
243 - * Both in <head> (in_footer = false), deliberately. Dark Reader does not
244 - * need a parsed body to start, and enabling it before first paint is what
245 - * keeps the admin from flashing light and then darkening.
246 - */
247 - wp_enqueue_script(
248 - 'darkify-darkreader',
249 - $darkify_lib_url,
250 - array(),
251 - $this->darkify_asset_version($darkify_lib_rel),
252 - false
253 - );
254 -
255 - /*
256 - * The block editor canvas and the classic editor's TinyMCE body are
257 - * separate documents, and Dark Reader binds the realm it was loaded into.
258 - * The engine injects its own copy into each same-origin frame, so it
259 - * needs a URL it can point a <script> at from inside that frame.
260 - *
261 - * Darkify's own React settings screens are excluded from Dark Reader:
262 - * they ship a full dark theme of their own (`.dark` in
263 - * darkify-react/src/index.css) which also covers the surrounding wp-admin
264 - * chrome, so darkening them again produces a double-dark UI — and because
265 - * Dark Reader 4.9 cannot parse the `oklch()` values those tokens use, the
266 - * unresolved properties settle on `--destructive` and paint card borders
267 - * and toggles red. The engine still loads there: it owns the toggle the
268 - * admin-bar icon calls, and the SPA mirrors the class onto `.dark` itself.
269 - */
270 - wp_add_inline_script(
271 - 'darkify-darkreader',
272 - 'window.darkifyDarkReaderSrc = ' . wp_json_encode($darkify_lib_url) . ';'
273 - . 'window.darkifyAdminSelfThemed = ' . ($is_spa ? 'true' : 'false') . ';',
274 - 'before'
275 - );
276 -
277 - wp_enqueue_script(
278 - 'darkify-admin-engine',
279 - DARKIFY_DIR_URL . $darkify_engine_rel,
280 - array('darkify-darkreader'),
281 - $this->darkify_asset_version($darkify_engine_rel),
282 - false
283 - );
106 + wp_enqueue_script('darkify-admin', DARKIFY_DIR_URL . 'src/Admin/assets/js/darkify-admin.js', array(), DARKIFY_VERSION, 'all');
284 107 }
285 108
286 - /**
287 - * Cache-busting version for a bundled asset: the plugin version plus the
288 - * file's mtime, so rebuilding client_main.js/.css (which does NOT bump the
289 - * plugin version) still changes the URL and browsers fetch the new file
290 - * instead of serving a stale one. Mirrors Admin\Assets::asset_version().
291 - */
292 - public function darkify_asset_version(string $relative_path): string
109 + public function add_plugin_action_links($links, $file)
293 110 {
294 - $abs = DARKIFY_PATH . $relative_path;
295 - $mtime = \is_readable($abs) ? \filemtime($abs) : false;
296 - return $mtime ? DARKIFY_VERSION . '.' . $mtime : DARKIFY_VERSION;
297 - }
298 -
299 - /*
300 - Dequeue Darkify scripts and styles on specific admin pages (e.g., MainWP pages)
301 - */
302 -
303 - public function admin_dequeue_for_specefic_pages()
304 - {
305 - $page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
306 - $dequeue_pages = array(
307 - 'mainwp_tab',
308 - 'managesites',
309 - 'ManageClients',
310 - 'CostSummary',
311 - 'InsightsOverview',
312 - 'Extensions',
313 - 'RESTAPI',
314 - 'mainwp-setup',
315 - 'ThemesManage',
316 - 'PluginsManage',
317 - 'InsightsManage',
318 - 'ManageGroups',
319 - 'UpdatesManage',
320 - 'PluginsInstall',
321 - 'PluginsAutoUpdate',
322 - 'PluginsIgnore',
323 - 'PluginsAbandoned',
324 - 'PluginsIgnoredAbandoned',
325 - 'ThemesInstall',
326 - 'ThemesAutoUpdate',
327 - 'ThemesIgnore',
328 - 'ThemesAbandoned',
329 - 'ThemesIgnoredAbandoned',
330 - 'UserBulkManage',
331 - 'UserBulkAdd',
332 - 'BulkImportUsers',
333 - 'UpdateAdminPasswords',
334 - 'PostBulkManage',
335 - 'Extensions-Mainwp-Backups',
336 - 'Extensions-Mainwp-Security',
337 - 'Extensions-Mainwp-Analytics',
338 - 'Mainwp-Monitoring',
339 - 'Extensions-Mainwp-Agency',
340 - 'Extensions-Mainwp-Administrative',
341 - 'Extensions-Mainwp-Development',
342 - 'Extensions-Mainwp-Performance',
343 - 'ClientAddNew',
344 - 'ClientImport',
345 - 'ManageCostTracker',
346 - 'CostTrackerAdd',
347 - 'AddApiKeys',
348 - );
349 -
350 -
351 - if (in_array($page, $dequeue_pages, true)) {
352 - wp_deregister_style('darkify-admin-switch');
353 - wp_dequeue_script('darkify-admin-client-main');
111 + if (DARKIFY_BASENAME === $file) {
112 + $new_links = array(
113 + sprintf('<a href="%s">%s</a>', admin_url('admin.php?page=darkify#tab=control'), __('Settings', 'darkify')),
114 + );
115 + return array_merge($new_links, $links);
354 116 }
117 + return $links;
355 118 }
356 119
357 - /**
358 - * Replace wp-admin's footer credit with Darkify's review request, on
359 - * Darkify's own screens only.
360 - *
361 - * Screens are matched by page slug (`is_darkify_spa_page()`) rather than by
362 - * a hardcoded screen id, so both the Settings and Get Help SPA screens match
363 - * and the check keeps working if the menu is ever retitled. The slug list is
364 - * the same source of truth the assets already load from.
365 - *
366 - * @param string $text Existing footer text.
367 - *
368 - * @return string
369 - */
370 - public function darkify_admin_footer($text)
120 + public function darkify_admin_enqueue($page)
371 121 {
372 - if (! $this->is_darkify_spa_page()) {
373 - return $text;
374 - }
122 + $options = get_option('darkify');
123 + $enable_default_dark_mode = $options['enable_default_dark_mode'];
124 + $enable_time = $options['enable_time'];
125 + $enable_time_based_dark = isset($enable_time['enable_time_based_dark']) ? $enable_time['enable_time_based_dark'] : "";
126 + $time_based_dark_start = isset($enable_time['time_based_dark_start']['from']) ? $enable_time['time_based_dark_start']['from'] : "";
127 + $time_based_dark_stop = isset($enable_time['time_based_dark_start']['to']) ? $enable_time['time_based_dark_start']['to'] : "";
128 + $grayscale = $options['brightness'];
129 + $enable_low_image_brightness = isset($grayscale['enable_low_image_brightness']) ? $grayscale['enable_low_image_brightness'] : "";
130 + $low_image_brightness_label = isset($grayscale['low_image_brightness_label']) ? $grayscale['low_image_brightness_label'] : "";
131 + $grayscale = isset($options['grayscale']);
132 + $enable_image_grayscale = isset($grayscale['enable_image_grayscale']) ? $grayscale['enable_image_grayscale'] : "";
133 + $image_grayscale_label = isset($grayscale['image_grayscale_label']) ? $grayscale['image_grayscale_label'] : "";
134 + $disallowed_low_grayscale_images = isset($grayscale['disallowed_low_grayscale_images']) ? $grayscale['disallowed_low_grayscale_images'] : "";
135 + $darken_background = isset($options['darken_background']);
136 + $enable_low_image_darken = isset($darken_background['enable_low_image_darken']) ? $darken_background['enable_low_image_darken'] : "";
137 + $low_image_darken_label = isset($darken_background['low_image_darken_label']) ? $darken_background['low_image_darken_label'] : "";
138 + $video_brightness = isset($options['video_brightness']);
139 + $enable_low_video_brightness = isset($video_brightness['enable_low_video_brightness']) ? $video_brightness['enable_low_video_brightness'] : "";
140 + $low_video_brightness_label = isset($video_brightness['low_video_brightness_label']) ? $video_brightness['low_video_brightness_label'] : "";
141 + $video_grayscale = isset($options['video_grayscale']);
142 + $enable_video_grayscale = isset($video_grayscale['enable_video_grayscale']) ? $video_grayscale['enable_video_grayscale'] : "";
375 143
376 - return sprintf(
377 - /* translators: 1: start strong tag, 2: close strong tag. 3: start link 4: close link */
378 - __('<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'),
379 - '<strong>',
380 - '</strong>',
381 - '<span class="greet-footer-text-star">★★★★★</span> <a href="https://wordpress.org/support/plugin/darkify/reviews/#new-post" target="_blank" rel="noopener noreferrer">',
382 - '</a>'
383 - );
384 - }
144 + if ($options["enable_admin_panel_dark_mode"]) {
145 + if ($this->darkify_is_dark_mode_allowed()) {
146 + if (!wp_style_is('darkify-admin-switch', 'enqueued')) {
147 + wp_enqueue_style('darkify-admin-switch', DARKIFY_DIR_URL . 'src/assets/css/client_main.css', array(), DARKIFY_VERSION, 'all');
148 + }
149 + wp_enqueue_script('darkify-admin-client-main', DARKIFY_DIR_URL . 'src/assets/js/client_main.js', array(), DARKIFY_VERSION);
385 150
386 - // Plugin settings in plugin list
387 - public function add_plugin_action_links(array $links)
388 - {
389 - $new_links = array(
390 - sprintf('<a href="' . esc_url(admin_url('admin.php?page=darkify')) . '">' . esc_html__('Settings', 'darkify') . '</a>'),
391 - sprintf('<a target="_blank" href="https://wordpress.org/support/plugin/darkify/">' . esc_html__('Support', 'darkify') . '</a>'),
392 - );
393 -
394 - $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'));
395 -
396 - return array_merge($new_links, $links);
397 - }
398 -
399 - /**
400 - * Add plugin row meta link.
401 - *
402 - * @since 2.0
403 - *
404 - * @param array $plugin_meta .
405 - * @param string $file .
406 - *
407 - * @return array
408 - */
409 - public function after_darkify_row_meta($plugin_meta, $file)
410 - {
411 -
412 - if (DARKIFY_BASENAME === $file) {
413 - $plugin_meta[] = '<a href="' . DARKIFY_DEMO_URL . '" target="_blank">' . __('Live Demo', 'darkify') . '</a>';
151 + $darkify_data = array(
152 + 'darkify_switch_unique_id' => $this->unique_id,
153 + 'darkify_is_this_admin_panel' => is_admin() ? "1" : "0",
154 + 'darkify_enable_default_dark_mode' => $enable_default_dark_mode,
155 + 'darkify_enable_os_aware' => $options["enable_os_aware"],
156 + 'darkify_enable_keyboard_shortcut' => $options["enable_keyboard_shortcut"],
157 + 'darkify_enable_time_based_dark' => $enable_time_based_dark,
158 + 'darkify_time_based_dark_start' => $time_based_dark_start ? $time_based_dark_start : "19:00",
159 + 'darkify_time_based_dark_stop' => $time_based_dark_stop ? $time_based_dark_stop : "07:00",
160 + 'darkify_alternative_dark_mode_switch' => $options["alternative_dark_mode_switcher"],
161 + 'darkify_enable_low_image_brightness' => $enable_low_image_brightness,
162 + 'darkify_image_brightness_to' => $low_image_brightness_label,
163 + 'darkify_enable_image_grayscale' => $enable_image_grayscale,
164 + 'darkify_image_grayscale_to' => $image_grayscale_label,
165 + 'darkify_disallowed_grayscale_images' => $disallowed_low_grayscale_images,
166 + 'darkify_enable_bg_image_darken' => $enable_low_image_darken,
167 + 'darkify_bg_image_darken_to' => $low_image_darken_label,
168 + 'darkify_enable_invert_inline_svg' => $options["enable_invert_inline_svg"],
169 + 'darkify_enable_low_video_brightness' => $enable_low_video_brightness,
170 + 'darkify_video_brightness_to' => $low_video_brightness_label,
171 + 'darkify_enable_video_grayscale' => $enable_video_grayscale,
172 + // Add more variables as needed...
173 + );
174 + wp_localize_script('darkify-admin-client-main', 'darkify_data', $darkify_data);
175 + }
414 176 }
415 -
416 - return $plugin_meta;
417 177 }
418 178
419 179 public function darkify_admin_bar_switch($wp_admin_bar)
420 180 {
421 - if (! $this->darkify_is_dark_mode_allowed()) {
422 - return;
181 + if ($this->darkify_is_dark_mode_allowed()) {
182 + $args = array(
183 + 'parent' => 'top-secondary',
184 + 'id' => 'darkify_admin_bar_switch_container',
185 + 'meta' => array(
186 + 'class' => 'darkify_admin_bar_switch_container',
187 + 'onclick' => 'darkify_switch_trigger()',
188 + )
189 + );
190 + $wp_admin_bar->add_node($args);
423 191 }
424 -
425 - /*
426 - * The block editor already carries its own toggle in the top toolbar
427 - * (see Frontend/Templates/footer_script.php), firing the same
428 - * darkify_switch_trigger() this node would — two controls for one state,
429 - * side by side.
430 - *
431 - * The toolbar one is the keeper, not this one: Gutenberg hides the WP
432 - * admin bar in fullscreen mode, which is the default, so this node is
433 - * simply absent for most people editing a post. Dropping the toolbar
434 - * button instead would leave them no toggle at all.
435 - *
436 - * The classic editor is deliberately not included here. Its TinyMCE moon
437 - * toggles only the content frame, while this switch toggles the whole
438 - * admin — two different controls, so both belong on screen.
439 - */
440 - $screen = \function_exists('get_current_screen') ? \get_current_screen() : null;
441 - if ($screen && \method_exists($screen, 'is_block_editor') && $screen->is_block_editor()) {
442 - return;
443 - }
444 -
445 - $options = get_option('darkify');
446 - $enable_admin_panel_dark_mode = isset($options['enable_admin_panel_dark_mode']) ? $options['enable_admin_panel_dark_mode'] : false;
447 -
448 - // Everywhere but Darkify's own screens the node is only registered when
449 - // the option is on, so it is always visible. On the SPA screens it is
450 - // registered regardless and starts hidden while the option is off — the
451 - // React admin then just flips this class (see WpAdminBarBridge), which is
452 - // what makes the icon appear/disappear without a reload.
453 - $classes = 'darkify_admin_bar_switch_container';
454 - if (! $enable_admin_panel_dark_mode) {
455 - $classes .= ' darkify_admin_bar_hidden';
456 - }
457 -
458 - $wp_admin_bar->add_node(array(
459 - 'parent' => 'top-secondary',
460 - 'id' => 'darkify_admin_bar_switch_container',
461 - 'meta' => array(
462 - 'class' => $classes,
463 - 'onclick' => 'darkify_switch_trigger()',
464 - ),
465 - ));
466 192 }
467 193
468 194 public function darkify_is_dark_mode_allowed()
469 195 {
@@ -468,14 +194,9 @@
468 194 public function darkify_is_dark_mode_allowed()
469 195 {
470 196
471 197 $options = get_option('darkify');
472 - $options = is_array($options) ? $options : array();
473 - // Guarded read: `disallowed_admin_pages` has no schema default, so the
474 - // React settings save doesn't persist it until the field is used. Reading
475 - // it unguarded would emit an "Undefined array key" warning on every
476 - // dark-mode hook (see the Pro plugin's Admin.php for the full story).
477 - $disallowed_admin_pages = isset($options['disallowed_admin_pages']) ? $options['disallowed_admin_pages'] : '';
198 + $disallowed_admin_pages = isset($options["disallowed_admin_pages"]) ? $options["disallowed_admin_pages"] : "";
478 199 /* Disable on Disallowed Admin Plugins */
479 200 if ($this->utils->isRestrictedByDisallowedAdminPages($disallowed_admin_pages)) {
480 201 return False;
481 202 }
@@ -484,9 +205,9 @@
484 205
485 206 public function darkify_admin_header_script()
486 207 {
487 208 if ($this->darkify_is_dark_mode_allowed()) {
488 - include_once DarkifyUtils::darkify_locate_template('header_script.php');
209 + include_once Helpers::darkify_locate_template('header_script.php');
489 210 }
490 211 }
491 212
492 213 public function darkify_admin_footer_script()
@@ -491,43 +212,44 @@
491 212
492 213 public function darkify_admin_footer_script()
493 214 {
494 215 if ($this->darkify_is_dark_mode_allowed()) {
495 - include_once DarkifyUtils::darkify_locate_template('footer_script.php');
216 + include_once Helpers::darkify_locate_template('footer_script.php');
496 217 }
497 218 }
498 219
499 - /**
500 - * Load classic editor scripts.
501 - *
502 - * @since 5.0.0
503 - */
504 - public function load_classic_editor_scripts()
220 + public function add_plugin_page()
505 221 {
506 - $options = get_option('darkify');
507 - $classic_editor_dark_mode = isset($options['classic_editor_dark_mode']) ? $options['classic_editor_dark_mode'] : '';
508 - if (! current_user_can('edit_posts') && ! current_user_can('edit_pages')) {
509 - return;
510 - }
511 - if (get_user_option('rich_editing') !== 'true') {
512 - return;
513 - }
514 -
515 - // Bail if admin_classic_editor is not enabled.
516 - if (! wp_validate_boolean($classic_editor_dark_mode)) {
517 - return;
518 - }
519 -
520 - add_filter('mce_external_plugins', function ($plugins) {
521 - // Honour the same debug switch as every other bundled asset; gulp
522 - // builds the .min beside it and this was the one enqueue still
523 - // pointing at the readable copy in production.
524 - $plugins['darkify_button'] = plugins_url('src/assets/js/admin-classic-editor' . $this->min . '.js', DARKIFY_FILE);
525 - return $plugins;
526 - });
527 -
528 - add_filter('mce_buttons', function ($buttons) {
529 - $buttons[] = 'darkify_button';
530 - return $buttons;
531 - });
222 + // This page will be under "Settings"
223 + add_menu_page(
224 + esc_html__('Darkify', 'darkify'),
225 + esc_html__('Darkify', 'darkify'),
226 + 'manage_options',
227 + 'darkify',
228 + 'darkify_settings',
229 + '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">
230 + <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" />
231 + <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" />
232 + <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" />
233 + <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" />
234 + <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" />
235 + <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" />
236 + <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" />
237 + <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" />
238 + <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" />
239 +</svg>'),
240 + 9
241 + );
242 + do_action('darkify_before_upgrade_pro_menu');
243 + add_submenu_page(
244 + 'darkify',
245 + __(
246 + '👑 Upgrade to Pro!',
247 + 'darkify'
248 + ),
249 + sprintf('<span style="color: #35b747;font-weight: bold;" class="darkify-get-pro-text">%s</span>', __('👑 Upgrade to Pro!', 'darkify')),
250 + 'manage_options',
251 + 'https://themeatelier.net/downloads/darkify/'
252 + );
253 + do_action('darkify_after_upgrade_pro_menu');
532 254 }
533 255 }