| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* The file of the Darkify class. |
| 5 |
* |
| 6 |
* @link https://themeatelier.net |
| 7 |
* @since 1.0.0 |
| 8 |
* |
| 9 |
* @package Darkify |
| 10 |
*/ |
| 11 |
|
| 12 |
namespace ThemeAtelier\Darkify\Includes; |
| 13 |
|
| 14 |
use ThemeAtelier\Darkify\Admin\Admin; |
| 15 |
use ThemeAtelier\Darkify\Admin\Assets; |
| 16 |
use ThemeAtelier\Darkify\Admin\Menu; |
| 17 |
use ThemeAtelier\Darkify\Admin\Rest\HelpRest; |
| 18 |
use ThemeAtelier\Darkify\Admin\Rest\PreviewRest; |
| 19 |
use ThemeAtelier\Darkify\Admin\Rest\SettingsRest; |
| 20 |
use ThemeAtelier\Darkify\Frontend\Frontend; |
| 21 |
|
| 22 |
|
| 23 |
// don't call the file directly. |
| 24 |
if (!defined('ABSPATH')) { |
| 25 |
exit; |
| 26 |
} |
| 27 |
|
| 28 |
/** |
| 29 |
* The main class of the plugin. |
| 30 |
* |
| 31 |
* Handle all the class and methods of the plugin. |
| 32 |
* |
| 33 |
* @author ThemeAtelier <themeatelierbd@gmail.com> |
| 34 |
*/ |
| 35 |
class Darkify |
| 36 |
{ |
| 37 |
/** |
| 38 |
* Plugin version |
| 39 |
* |
| 40 |
* @since 1.0.0 |
| 41 |
* @access protected |
| 42 |
* @var string |
| 43 |
*/ |
| 44 |
protected $version; |
| 45 |
|
| 46 |
/** |
| 47 |
* Plugin slug |
| 48 |
* |
| 49 |
* @since 1.0.0 |
| 50 |
* @access protected |
| 51 |
* @var string |
| 52 |
*/ |
| 53 |
protected $plugin_slug; |
| 54 |
|
| 55 |
/** |
| 56 |
* Main Loader. |
| 57 |
* |
| 58 |
* The loader that's responsible for maintaining and registering all hooks that empowers |
| 59 |
* the plugin. |
| 60 |
* |
| 61 |
* @since 1.0.0 |
| 62 |
* @access protected |
| 63 |
* @var object |
| 64 |
*/ |
| 65 |
protected $loader; |
| 66 |
/** |
| 67 |
* Constructor for the Darkify class. |
| 68 |
* |
| 69 |
* Sets up all the appropriate hooks and actions within the plugin. |
| 70 |
*/ |
| 71 |
public function __construct() |
| 72 |
{ |
| 73 |
$this->version = DARKIFY_VERSION; |
| 74 |
$this->plugin_slug = 'darkify'; |
| 75 |
$this->load_dependencies(); |
| 76 |
$this->define_constants(); |
| 77 |
$this->define_admin_hooks(); |
| 78 |
$this->define_public_hooks(); |
| 79 |
$this->init_action(); |
| 80 |
add_action('plugin_loaded', array($this, 'init_plugin')); |
| 81 |
|
| 82 |
// Translations load on `after_setup_theme`, not alongside the boot action |
| 83 |
// above. `plugin_loaded` fires while plugin files are still being |
| 84 |
// included, before the pluggable functions exist, so `determine_locale()` |
| 85 |
// cannot yet see a per-user admin language. Priority 1 puts this ahead of |
| 86 |
// Admin::init_components (same hook, priority 10), which is where the |
| 87 |
// settings schema evaluates its `__()` calls. |
| 88 |
add_action('after_setup_theme', array($this, 'load_textdomain'), 1); |
| 89 |
|
| 90 |
add_action('elementor/controls/register', [$this, 'register_controls']); |
| 91 |
add_action('elementor/widgets/register', array($this, 'register_darkify_widget')); |
| 92 |
|
| 93 |
add_action('init', [$this, 'create_darkify_block_init']); |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Run the loader to execute all of the hooks with WordPress. |
| 98 |
* |
| 99 |
* @since 1.0.0 |
| 100 |
*/ |
| 101 |
public function run() |
| 102 |
{ |
| 103 |
$this->loader->run(); |
| 104 |
} |
| 105 |
|
| 106 |
public function darkify_redirect_to($plugin) |
| 107 |
{ |
| 108 |
if (DARKIFY_BASENAME === $plugin) { |
| 109 |
$redirect_url = esc_url(admin_url('admin.php?page=darkify')); |
| 110 |
exit(wp_kses_post(wp_safe_redirect($redirect_url))); |
| 111 |
} |
| 112 |
} |
| 113 |
|
| 114 |
/** |
| 115 |
* The slug of the plugin used to uniquely identify it within the context of |
| 116 |
* WordPress and to define internationalization functionality. |
| 117 |
* |
| 118 |
* @since 1.0.0 |
| 119 |
* @return string The name of the plugin. |
| 120 |
*/ |
| 121 |
public function get_plugin_name() |
| 122 |
{ |
| 123 |
return $this->plugin_slug; |
| 124 |
} |
| 125 |
|
| 126 |
/** |
| 127 |
* Retrieve the version number of the plugin. |
| 128 |
* |
| 129 |
* @since 1.0.0 |
| 130 |
* @return string The version number of the plugin. |
| 131 |
*/ |
| 132 |
public function get_version() |
| 133 |
{ |
| 134 |
return $this->version; |
| 135 |
} |
| 136 |
|
| 137 |
|
| 138 |
public function register_controls($controls_manager) |
| 139 |
{ |
| 140 |
require_once DARKIFY_PATH . 'src/Admin/elementor/controls/class-elementor-control-switch.php'; |
| 141 |
|
| 142 |
$controls_manager->register_control( |
| 143 |
'darkify_switch', |
| 144 |
new \Elementor_Darkify_Switcher() |
| 145 |
); |
| 146 |
} |
| 147 |
public function register_darkify_widget($widgets_manager) |
| 148 |
{ |
| 149 |
require_once DARKIFY_PATH . 'src/Admin/elementor/widgets/elementor-widget.php'; |
| 150 |
$widgets_manager->register(new \Elementor_Darkify()); |
| 151 |
} |
| 152 |
|
| 153 |
/** |
| 154 |
* Define the constants |
| 155 |
* |
| 156 |
* @return void |
| 157 |
*/ |
| 158 |
public function define_constants() |
| 159 |
{ |
| 160 |
define('DARKIFY_URL', plugins_url('', DARKIFY_FILE)); |
| 161 |
define('DARKIFY_ASSETS', DARKIFY_URL . '/src/assets/'); |
| 162 |
define('DARKIFY_ADMIN', DARKIFY_URL . '/src/Admin'); |
| 163 |
} |
| 164 |
|
| 165 |
/** |
| 166 |
* Fire the plugin's own boot action. |
| 167 |
* |
| 168 |
* @return void |
| 169 |
*/ |
| 170 |
public function init_plugin() |
| 171 |
{ |
| 172 |
do_action('darkify_loaded'); |
| 173 |
} |
| 174 |
|
| 175 |
/** |
| 176 |
* Load the plugin's translations. |
| 177 |
* |
| 178 |
* @return void |
| 179 |
*/ |
| 180 |
public function load_textdomain() |
| 181 |
{ |
| 182 |
// `load_plugin_textdomain()` wants a path *relative to WP_PLUGIN_DIR* — |
| 183 |
// it does `WP_PLUGIN_DIR . '/' . $path`. DARKIFY_DIR_NAME is an absolute |
| 184 |
// path (`dirname(__FILE__)`), so the old call built a nonsense path like |
| 185 |
// `/wp-content/plugins//Users/…/plugins/darkify/languages` and the .mo |
| 186 |
// was never found. `wp_set_script_translations()` in Admin/Assets.php |
| 187 |
// does take an absolute path and got it right, which is why the React |
| 188 |
// strings translated and nothing from PHP ever did. |
| 189 |
load_plugin_textdomain( |
| 190 |
'darkify', |
| 191 |
false, |
| 192 |
dirname(plugin_basename(DARKIFY_FILE)) . '/languages' |
| 193 |
); |
| 194 |
} |
| 195 |
|
| 196 |
/** |
| 197 |
* Load the required dependencies for this plugin. |
| 198 |
* |
| 199 |
* Include the following files that make up the plugin: |
| 200 |
* |
| 201 |
* - Loader. Orchestrates the hooks of the plugin. |
| 202 |
* - Teamproi18n. Defines internationalization functionality. |
| 203 |
* - Admin. Defines all hooks for the admin area. |
| 204 |
* - Frontend. Defines all hooks for the public side of the site. |
| 205 |
* |
| 206 |
* Create an instance of the loader which will be used to register the hooks |
| 207 |
* with WordPress. |
| 208 |
* |
| 209 |
* @since 1.0.0 |
| 210 |
* @access private |
| 211 |
*/ |
| 212 |
private function load_dependencies() |
| 213 |
{ |
| 214 |
/** |
| 215 |
* The class responsible for orchestrating the actions and filters of the |
| 216 |
* core plugin. |
| 217 |
*/ |
| 218 |
$this->loader = new Loader(); |
| 219 |
} |
| 220 |
|
| 221 |
/** |
| 222 |
* Register all of the hooks related to the admin dashboard functionality |
| 223 |
* of the plugin. |
| 224 |
* |
| 225 |
* @since 1.0.0 |
| 226 |
* @access private |
| 227 |
*/ |
| 228 |
private function define_admin_hooks() |
| 229 |
{ |
| 230 |
$plugin_admin = new Admin($this->get_plugin_name(), $this->get_version()); |
| 231 |
$this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles'); |
| 232 |
$this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts'); |
| 233 |
$this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'admin_dequeue_for_specefic_pages', 999); |
| 234 |
|
| 235 |
// The React admin: its menu page, its bundle, and the REST endpoints it |
| 236 |
// talks to. The controllers hook themselves onto `rest_api_init`, so they |
| 237 |
// must be constructed on every request, not only in wp-admin. |
| 238 |
new Menu(); |
| 239 |
new Assets(); |
| 240 |
new SettingsRest(); |
| 241 |
// Backs the Get Help page's Recommended tab (ThemeAtelier plugin |
| 242 |
// directory with one-click install/activate). |
| 243 |
new HelpRest(); |
| 244 |
// Constructed unconditionally (not admin-only): its `init` hook also has |
| 245 |
// to fire on the frontend iframe request to swap in the preview values. |
| 246 |
new PreviewRest(); |
| 247 |
} |
| 248 |
|
| 249 |
/** |
| 250 |
* Register all of the hooks related to the public-facing functionality |
| 251 |
* of the plugin. |
| 252 |
* |
| 253 |
* @since 1.0.0 |
| 254 |
* @access private |
| 255 |
*/ |
| 256 |
private function define_public_hooks() |
| 257 |
{ |
| 258 |
$plugin_public = new Frontend($this->get_plugin_name(), $this->get_version()); |
| 259 |
} |
| 260 |
|
| 261 |
public static function switcher($variant, $unique_id) |
| 262 |
{ |
| 263 |
if ('classic' == $variant) { |
| 264 |
return '<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="0px" height="0px" fill="currentColor" stroke-linecap="round" class="theme-toggle__classic" viewBox="0 0 32 32"><clipPath id="theme-toggle__classic__cutout_' . esc_attr($unique_id) . '"><path d="M0-5h30a1 1 0 0 0 9 13v24H0Z" /></clipPath><g clip-path="url(#theme-toggle__classic__cutout_' . esc_attr($unique_id) . ')"><circle cx="16" cy="16" r="9.34" /><g stroke="currentColor" stroke-width="1.5"><path d="M16 5.5v-4" /><path d="M16 30.5v-4" /><path d="M1.5 16h4" /><path d="M26.5 16h4" /><path d="m23.4 8.6 2.8-2.8" /><path d="m5.7 26.3 2.9-2.9" /><path d="m5.8 5.8 2.8 2.8" /><path d="m23.4 23.4 2.9 2.9" /></g></g></svg>'; |
| 265 |
} else if ('expand' == $variant) { |
| 266 |
return '<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="0px" height="0px" fill="currentColor" class="theme-toggle__expand" viewBox="0 0 32 32"><clipPath id="theme-toggle__expand__cutout_' . esc_attr($unique_id) . '"><path d="M0-11h25a1 1 0 0017 13v30H0Z" /></clipPath><g clip-path="url(#theme-toggle__expand__cutout_' . esc_attr($unique_id) . ')"><circle cx="16" cy="16" r="8.4" /><path d="M18.3 3.2c0 1.3-1 2.3-2.3 2.3s-2.3-1-2.3-2.3S14.7.9 16 .9s2.3 1 2.3 2.3zm-4.6 25.6c0-1.3 1-2.3 2.3-2.3s2.3 1 2.3 2.3-1 2.3-2.3 2.3-2.3-1-2.3-2.3zm15.1-10.5c-1.3 0-2.3-1-2.3-2.3s1-2.3 2.3-2.3 2.3 1 2.3 2.3-1 2.3-2.3 2.3zM3.2 13.7c1.3 0 2.3 1 2.3 2.3s-1 2.3-2.3 2.3S.9 17.3.9 16s1-2.3 2.3-2.3zm5.8-7C9 7.9 7.9 9 6.7 9S4.4 8 4.4 6.7s1-2.3 2.3-2.3S9 5.4 9 6.7zm16.3 21c-1.3 0-2.3-1-2.3-2.3s1-2.3 2.3-2.3 2.3 1 2.3 2.3-1 2.3-2.3 2.3zm2.4-21c0 1.3-1 2.3-2.3 2.3S23 7.9 23 6.7s1-2.3 2.3-2.3 2.4 1 2.4 2.3zM6.7 23C8 23 9 24 9 25.3s-1 2.3-2.3 2.3-2.3-1-2.3-2.3 1-2.3 2.3-2.3z" /></g></svg>'; |
| 267 |
} else if ('inner-moon' === $variant) { |
| 268 |
return '<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="0px" height="0px" fill="currentColor" class="theme-toggle__inner-moon" viewBox="0 0 32 32"><path d="M27.5 11.5v-7h-7L16 0l-4.5 4.5h-7v7L0 16l4.5 4.5v7h7L16 32l4.5-4.5h7v-7L32 16l-4.5-4.5zM16 25.4a9.39 9.39 0 1 1 0-18.8 9.39 9.39 0 1 1 0 18.8z" /><circle cx="16" cy="16" r="8.1" /></svg>'; |
| 269 |
} else if ('within' === $variant) { |
| 270 |
return '<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" class="theme-toggle__within" height="1em" width="1em" viewBox="0 0 32 32" fill="currentColor"><clipPath id="theme-toggle__within__clip_' . esc_attr($unique_id) . '"><path d="M0 0h32v32h-32ZM6 16A1 1 0 0026 16 1 1 0 006 16" /></clipPath><g clip-path="url(#theme-toggle__within__clip_' . esc_attr($unique_id) . ')"><path d="M30.7 21.3 27.1 16l3.7-5.3c.4-.5.1-1.3-.6-1.4l-6.3-1.1-1.1-6.3c-.1-.6-.8-.9-1.4-.6L16 5l-5.4-3.7c-.5-.4-1.3-.1-1.4.6l-1 6.3-6.4 1.1c-.6.1-.9.9-.6 1.3L4.9 16l-3.7 5.3c-.4.5-.1 1.3.6 1.4l6.3 1.1 1.1 6.3c.1.6.8.9 1.4.6l5.3-3.7 5.3 3.7c.5.4 1.3.1 1.4-.6l1.1-6.3 6.3-1.1c.8-.1 1.1-.8.7-1.4zM16 25.1c-5.1 0-9.1-4.1-9.1-9.1 0-5.1 4.1-9.1 9.1-9.1s9.1 4.1 9.1 9.1c0 5.1-4 9.1-9.1 9.1z" /></g><path class="theme-toggle__within__circle" d="M16 7.7c-4.6 0-8.2 3.7-8.2 8.2s3.6 8.4 8.2 8.4 8.2-3.7 8.2-8.2-3.6-8.4-8.2-8.4zm0 14.4c-3.4 0-6.1-2.9-6.1-6.2s2.7-6.1 6.1-6.1c3.4 0 6.1 2.9 6.1 6.2s-2.7 6.1-6.1 6.1z" /> |
| 271 |
<path class="theme-toggle__within__inner" d="M16 9.5c-3.6 0-6.4 2.9-6.4 6.4s2.8 6.5 6.4 6.5 6.4-2.9 6.4-6.4-2.8-6.5-6.4-6.5z" /></svg>'; |
| 272 |
} elseif ('orbit' === $variant) { |
| 273 |
return '<span class="toggle-track"><span class="toggle-sun"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M277.3 32h-42.7v64h42.7V32zm129.1 43.7L368 114.1l29.9 29.9 38.4-38.4-29.9-29.9zm-300.8 0l-29.9 29.9 38.4 38.4 29.9-29.9-38.4-38.4zM256 128c-70.4 0-128 57.6-128 128s57.6 128 128 128 128-57.6 128-128-57.6-128-128-128zm224 106.7h-64v42.7h64v-42.7zm-384 0H32v42.7h64v-42.7zM397.9 368L368 397.9l38.4 38.4 29.9-29.9-38.4-38.4zm-283.8 0l-38.4 38.4 29.9 29.9 38.4-38.4-29.9-29.9zm163.2 48h-42.7v64h42.7v-64z"></path></svg></span><span class="toggle-thumb"></span><span class="toggle-moon"><svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 384 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"></path></svg></span></span>'; |
| 274 |
} |
| 275 |
} |
| 276 |
|
| 277 |
public static function switcher_wrapper($type, $unique_id, $variant, $switch_size, $position_class = '', $floating_switch_position = '', $tooltip_classes = '', $hide_dark_mode_on_mobile_screen = '', $floating_switch_width = '', $tooltip_on_floating_switch = '') |
| 278 |
{ |
| 279 |
|
| 280 |
// A single `style` attribute: this used to be emitted TWICE (once with |
| 281 |
// just the scale variable, then — for `shortcode` — a second `style="..."` |
| 282 |
// appended after `class`). HTML only honors the FIRST of two duplicate |
| 283 |
// attributes, so that second one (the only place `position: relative` was |
| 284 |
// set) was silently dropped by every browser. Without it, an absolutely- |
| 285 |
// positioned inner element (e.g. a pill variant's sliding thumb) has no |
| 286 |
// guarantee of being positioned relative to THIS wrapper — it falls back |
| 287 |
// to whatever positioned ancestor happens to exist (or none), which is |
| 288 |
// exactly the kind of thing a page's content wrapper (often already |
| 289 |
// `position: relative`) can accidentally paper over while a theme's nav |
| 290 |
// `<li>` — the Menu Switch's actual context — usually doesn't. |
| 291 |
$style = '--darkify-switch-scale: ' . esc_attr($switch_size) . ';' . ($type === 'shortcode' ? ' position: relative;' : ''); |
| 292 |
$classes = 'darkify_switch darkify_switch_style switch-' . esc_attr($variant) |
| 293 |
. ($type !== 'shortcode' ? ' ' . esc_attr($position_class) . ' ' . esc_attr($floating_switch_position) . ' ' . esc_attr($tooltip_classes) . ' ' . esc_attr($hide_dark_mode_on_mobile_screen) : ''); |
| 294 |
return '<div style="' . $style . '" id="darkify_switch_' . esc_attr($unique_id) . '" onclick="darkify_switch_trigger()" class="' . $classes . '"><div class="theme-toggle">' . self::switcher($variant, $unique_id) . '</div>' . ($type !== 'shortcode' && $floating_switch_width ? '<span class="darkify_tooltiptext">' . esc_attr($tooltip_on_floating_switch) . '</span>' : '') . '</div>'; |
| 295 |
} |
| 296 |
|
| 297 |
private function init_action() |
| 298 |
{ |
| 299 |
add_action('activated_plugin', [$this, 'darkify_redirect_to']); |
| 300 |
} |
| 301 |
|
| 302 |
|
| 303 |
public function create_darkify_block_init() |
| 304 |
{ |
| 305 |
register_block_type_from_metadata( |
| 306 |
DARKIFY_PATH . 'src/Admin/blocks/', |
| 307 |
array( |
| 308 |
'render_callback' => [$this, 'render_darkify_block_callback'], |
| 309 |
) |
| 310 |
); |
| 311 |
|
| 312 |
wp_localize_script( |
| 313 |
'darkify-switcher-editor-script', |
| 314 |
'darkify', |
| 315 |
array( |
| 316 |
'dir_url' => DARKIFY_DIR_URL, |
| 317 |
) |
| 318 |
); |
| 319 |
} |
| 320 |
|
| 321 |
/** |
| 322 |
* Render callback for the Darkify Pro block. |
| 323 |
*/ |
| 324 |
public function render_darkify_block_callback($attributes) |
| 325 |
{ |
| 326 |
$style = $attributes['style'] ?? 'classic'; |
| 327 |
$size = $attributes['size'] ?? '1.0'; |
| 328 |
$custom_size = $attributes['custom_size'] ?? 100; |
| 329 |
$bg_light = $attributes['switch_bg_light'] ?? '#121116'; |
| 330 |
$bg_dark = $attributes['switch_bg_dark'] ?? '#ffffff'; |
| 331 |
$light_icon_color = $attributes['light_icon_color'] ?? '#ffffff'; |
| 332 |
$dark_icon_color = $attributes['dark_icon_color'] ?? '#121116'; |
| 333 |
$light_text_color = $attributes['light_text_color'] ?? '#121116'; |
| 334 |
$dark_text_color = $attributes['dark_text_color'] ?? '#ffffff'; |
| 335 |
$border_width = $attributes['border_width'] ?? 0; |
| 336 |
$border_light_color = $attributes['border_light_color'] ?? '#121116'; |
| 337 |
$border_dark_color = $attributes['border_dark_color'] ?? '#ffffff'; |
| 338 |
$border_radius = $attributes['border_radius'] ?? 7; |
| 339 |
$alignment = $attributes['alignment'] ?? 'left'; |
| 340 |
|
| 341 |
// Size logic |
| 342 |
if ('custom' === $size) { |
| 343 |
$switcher_size = $custom_size; |
| 344 |
} else { |
| 345 |
$switcher_size = (float) $size * 100; |
| 346 |
} |
| 347 |
|
| 348 |
return '<div style="display: flex; justify-content: ' . esc_attr($alignment) . '">' . do_shortcode(wp_sprintf( |
| 349 |
'[darkify switch="%s" icon_size="40px" light_mode_bg="%s" dark_mode_bg="%s" light_mode_color="%s" dark_mode_color="%s" border="%spx" border_light_color="%s" border_dark_color="%s" border_radius="%spx" text_light_mode_color="%s" text_dark_mode_color="%s" switch_size="%s"]', |
| 350 |
$style, |
| 351 |
$bg_light, |
| 352 |
$bg_dark, |
| 353 |
$light_icon_color, |
| 354 |
$dark_icon_color, |
| 355 |
$border_width, |
| 356 |
$border_light_color, |
| 357 |
$border_dark_color, |
| 358 |
$border_radius, |
| 359 |
$light_text_color, |
| 360 |
$dark_text_color, |
| 361 |
$switcher_size |
| 362 |
)) . '</div>'; |
| 363 |
} |
| 364 |
} |
| 365 |
|