PluginProbe
WP EXtra – One Click Optimize / trunk
WP EXtra – One Click Optimize vtrunk
8.7.1 8.6.8 8.7.0 trunk 5.9 8.0 8.5.0 8.5.4 8.5.5 8.6.0 8.6.1 8.6.2 8.6.3 8.6.5
wp-extra / src / Modules / Frontend / Branding.php

Branding.php in WP EXtra – One Click Optimize trunk, at src/Modules/Frontend/Branding.php

763 lines 35.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPEXtra\Modules\Frontend;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 use WPEXtra\Settings;
9 use WPEXtra\Helper;
10 use WPEXtra\Base;
11
12 class Branding extends Base {
13
14 private $wp_login_enabled = false;
15
16 public function __construct() {
17 parent::__construct();
18 $preset = Helper::get_option('login_preset', 'default');
19 if (
20 ($preset && $preset !== 'default') ||
21 Helper::get_option('login_color') ||
22 Helper::get_option('login_bg_color') ||
23 Helper::get_option('login_bg_image') ||
24 Helper::get_option('login_logo') ||
25 Helper::get_option('login_logo_hide') ||
26 Helper::get_option('login_form_radius') ||
27 Helper::get_option('login_link_form') ||
28 Helper::get_option('login_placeholder')
29 ) {
30 add_action('login_enqueue_scripts', [$this, 'login_styles']);
31 }
32
33 if (Helper::get_option('login_placeholder') || Helper::get_option('login_remember')) {
34 add_action('login_head', [$this, 'login_scripts'], 1);
35 }
36
37 if (Helper::get_option('turnstile')) {
38 add_action('login_enqueue_scripts', [$this, 'enqueue_turnstile_script']);
39 add_action('login_form', [$this, 'display_turnstile_captcha']);
40 add_filter('wp_authenticate_user', [$this, 'verify_turnstile_captcha'], 10, 3);
41 }
42
43 if (Helper::get_option('turnstile_my_account') && Helper::is_woo_active()) {
44 add_action('wp_enqueue_scripts', [$this, 'enqueue_turnstile_script']);
45 add_action('woocommerce_login_form', [$this, 'display_turnstile_captcha']);
46 add_action('woocommerce_register_form', [$this, 'display_turnstile_captcha']);
47 add_action('woocommerce_lostpassword_form', [$this, 'display_turnstile_captcha']);
48 add_action('flatsome_account_login_lightbox', [$this, 'display_turnstile_captcha']);
49 add_filter('woocommerce_process_login_errors', [$this, 'verify_woocommerce_turnstile'], 10, 3);
50 add_filter('woocommerce_process_registration_errors', [$this, 'verify_woocommerce_turnstile'], 10, 4);
51 add_action('wp_footer', [$this, 'flatsome_modal_turnstile_script'], 99);
52 }
53 }
54
55 public function login_styles() {
56 $preset = Helper::get_option('login_preset', 'default');
57 $login_css = '
58 html, body.login {
59 height: 100% !important;
60 min-height: 100vh !important;
61 }
62 body.login {
63 display: flex !important;
64 flex-direction: column !important;
65 justify-content: center !important;
66 align-items: center !important;
67 margin: 0 !important;
68 padding: 30px 16px !important;
69 box-sizing: border-box !important;
70 }
71 body.login #login {
72 padding: 0 !important;
73 width: 100% !important;
74 max-width: 390px !important;
75 margin: auto !important;
76 box-sizing: border-box !important;
77 }
78 .login form p.forgetmenot {
79 display: inline-flex;
80 align-items: center;
81 margin: 0;
82 min-height: 42px;
83 float: left;
84 }
85 .login form p.forgetmenot label {
86 display: inline-flex;
87 align-items: center;
88 gap: 8px;
89 font-size: 13px;
90 cursor: pointer;
91 line-height: 1.4;
92 }
93 .login form p.forgetmenot input[type="checkbox"] {
94 margin: 0 8px 0 0 !important;
95 border-radius: 4px;
96 width: 18px !important;
97 height: 18px !important;
98 min-width: 18px !important;
99 float: none !important;
100 vertical-align: middle !important;
101 position: relative;
102 }
103 .login form p.submit {
104 float: right;
105 margin: 0;
106 }
107 .login form p.submit .button-primary {
108 min-height: 42px !important;
109 padding: 0 22px !important;
110 line-height: 40px !important;
111 font-size: 14px !important;
112 border-radius: 8px !important;
113 font-weight: 600 !important;
114 }
115 .login form::after {
116 content: "";
117 display: table;
118 clear: both;
119 }
120 .login #nav {
121 float: right;
122 text-align: right;
123 padding: 0;
124 margin: 16px 0 0;
125 max-width: 50%;
126 box-sizing: border-box;
127 }
128 .login #backtoblog {
129 float: left;
130 text-align: left;
131 padding: 0;
132 margin: 16px 0 0;
133 max-width: 50%;
134 box-sizing: border-box;
135 }
136 .login #nav a, .login #backtoblog a {
137 font-size: 13px;
138 font-weight: 500;
139 text-decoration: none !important;
140 display: inline-block;
141 transition: opacity 0.2s ease, color 0.2s ease;
142 }
143 .login #nav a:hover, .login #backtoblog a:hover {
144 text-decoration: underline !important;
145 }
146 .login .language-switcher {
147 clear: both;
148 padding-top: 16px;
149 margin-top: 16px;
150 display: flex;
151 flex-direction: row;
152 align-items: center;
153 justify-content: center;
154 }
155 .login .language-switcher form {
156 background: transparent !important;
157 border: none !important;
158 box-shadow: none !important;
159 padding: 0 !important;
160 margin: 0 !important;
161 display: inline-flex !important;
162 align-items: center !important;
163 gap: 8px !important;
164 justify-content: center !important;
165 }
166 .login .language-switcher label {
167 display: inline-flex !important;
168 align-items: center !important;
169 gap: 6px !important;
170 margin: 0 !important;
171 padding: 0 !important;
172 position: static !important;
173 font-size: 13px !important;
174 font-weight: 500 !important;
175 line-height: 1.4 !important;
176 }
177 .login .language-switcher label .screen-reader-text {
178 position: static !important;
179 width: auto !important;
180 height: auto !important;
181 clip: auto !important;
182 overflow: visible !important;
183 margin: 0 !important;
184 padding: 0 !important;
185 font-size: 13px !important;
186 font-weight: 500 !important;
187 white-space: nowrap !important;
188 order: 1 !important;
189 }
190 .login .language-switcher label .dashicons,
191 .login .language-switcher label .dashicons-translation {
192 font-size: 18px !important;
193 width: 18px !important;
194 height: 18px !important;
195 line-height: 18px !important;
196 display: inline-block !important;
197 margin: 0 0 0 2px !important;
198 position: static !important;
199 float: none !important;
200 order: 2 !important;
201 vertical-align: middle !important;
202 }
203 .login .language-switcher select {
204 display: inline-flex !important;
205 align-items: center !important;
206 border-radius: 8px !important;
207 height: 38px !important;
208 line-height: 38px !important;
209 padding: 0 34px 0 12px !important;
210 margin: 0 !important;
211 font-size: 13px !important;
212 cursor: pointer;
213 appearance: none !important;
214 -webkit-appearance: none !important;
215 -moz-appearance: none !important;
216 background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22%23475569%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M5.293%207.293a1%201%200%20011.414%200L10%2010.586l3.293-3.293a1%201%200%20111.414%201.414l-4%204a1%201%200%2001-1.414%200l-4-4a1%201%200%20010-1.414z%22%20clip-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") !important;
217 background-repeat: no-repeat !important;
218 background-position: right 10px center !important;
219 background-size: 16px 16px !important;
220 min-width: 140px;
221 max-width: 180px;
222 box-sizing: border-box !important;
223 vertical-align: middle !important;
224 }
225 .login .language-switcher .button {
226 display: inline-flex !important;
227 align-items: center !important;
228 justify-content: center !important;
229 height: 38px !important;
230 line-height: 38px !important;
231 padding: 0 14px !important;
232 margin: 0 !important;
233 border-radius: 8px !important;
234 font-size: 13px !important;
235 font-weight: 600 !important;
236 cursor: pointer;
237 box-sizing: border-box !important;
238 vertical-align: middle !important;
239 transition: all 0.2s ease;
240 }
241 ';
242
243 if ($preset === 'minimal_white') {
244 $login_css .= '
245 body.login { background: #f8fafc; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
246 .login h1 a { background-size: contain; width: auto !important; max-width: 220px; }
247 .login form { background: #ffffff; border: 1px solid #e2e8f0; border-radius: 14px; box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.02); padding: 30px 26px; }
248 .login form label { font-weight: 500; color: #475569; font-size: 13px; }
249 .login input[type=text], .login input[type=password] { border-radius: 8px !important; border: 1px solid #cbd5e1 !important; font-size: 14px !important; padding: 6px 12px !important; }
250 .login input[type=text]:focus, .login input[type=password]:focus { border-color: #2563eb !important; box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2) !important; }
251 .wp-core-ui .button.button-large { background: #2563eb !important; border-color: #2563eb !important; border-radius: 8px !important; box-shadow: 0 4px 6px -1px rgba(37, 99, 235, 0.2) !important; transition: all 0.2s ease; }
252 .wp-core-ui .button.button-large:hover { background: #1d4ed8 !important; border-color: #1d4ed8 !important; }
253 .login #nav a, .login #backtoblog a { color: #64748b; }
254 .login #nav a:hover, .login #backtoblog a:hover { color: #2563eb; }
255 .login .language-switcher label { color: #64748b; }
256 .login .language-switcher select { background-color: #ffffff !important; border: 1px solid #cbd5e1 !important; color: #334155 !important; }
257 .login .language-switcher .button { background: #ffffff; border: 1px solid #cbd5e1; color: #334155; }
258 .login .language-switcher .button:hover { background: #f1f5f9; color: #0f172a; }
259 ';
260 } elseif ($preset === 'dark_modern') {
261 $login_css .= '
262 body.login { background: #0f172a; color: #cbd5e1; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
263 .login h1 a { filter: brightness(0) invert(1); background-size: contain; width: auto !important; max-width: 220px; }
264 .login form { background: rgba(30, 41, 59, 0.85); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5); padding: 30px 26px; color: #e2e8f0; }
265 .login form label { font-weight: 500; color: #94a3b8; font-size: 13px; }
266 .login input[type=text], .login input[type=password] { background: #1e293b !important; color: #f8fafc !important; border: 1px solid #334155 !important; border-radius: 8px !important; font-size: 14px !important; padding: 6px 12px !important; }
267 .login input[type=text]:focus, .login input[type=password]:focus { border-color: #6366f1 !important; box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.25) !important; }
268 .wp-core-ui .button.button-large { background: linear-gradient(135deg, #6366f1, #8b5cf6) !important; border: none !important; border-radius: 8px !important; box-shadow: 0 4px 14px rgba(99, 102, 241, 0.4) !important; text-shadow: none; transition: all 0.2s ease; }
269 .wp-core-ui .button.button-large:hover { opacity: 0.95; transform: translateY(-1px); }
270 .login #nav a, .login #backtoblog a { color: #94a3b8; }
271 .login #nav a:hover, .login #backtoblog a:hover { color: #c7d2fe; }
272 .login .language-switcher label { color: #94a3b8; }
273 .login .language-switcher select { background-color: #1e293b !important; border: 1px solid #334155 !important; color: #f8fafc !important; background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2020%2020%22%20fill%3D%22%23cbd5e1%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M5.293%207.293a1%201%200%20011.414%200L10%2010.586l3.293-3.293a1%201%200%20111.414%201.414l-4%204a1%201%200%2001-1.414%200l-4-4a1%201%200%20010-1.414z%22%20clip-rule%3D%22evenodd%22%2F%3E%3C%2Fsvg%3E") !important; }
274 .login .language-switcher .button { background: #334155 !important; border: 1px solid #475569 !important; color: #f8fafc !important; }
275 .login .language-switcher .button:hover { background: #475569 !important; }
276 ';
277 } elseif ($preset === 'gradient_vibrant') {
278 $login_css .= '
279 body.login { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #db2777 100%); background-attachment: fixed; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
280 .login h1 a { filter: brightness(0) invert(1); background-size: contain; width: auto !important; max-width: 220px; }
281 .login form { background: rgba(255, 255, 255, 0.96); border: none; border-radius: 16px; box-shadow: 0 20px 35px -5px rgba(0, 0, 0, 0.25); padding: 30px 26px; }
282 .login form label { font-weight: 500; color: #334155; font-size: 13px; }
283 .login input[type=text], .login input[type=password] { border-radius: 8px !important; border: 1px solid #cbd5e1 !important; font-size: 14px !important; padding: 6px 12px !important; }
284 .login input[type=text]:focus, .login input[type=password]:focus { border-color: #7c3aed !important; box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.2) !important; }
285 .wp-core-ui .button.button-large { background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%) !important; border: none !important; border-radius: 8px !important; box-shadow: 0 4px 14px rgba(124, 58, 237, 0.4) !important; text-shadow: none; }
286 .login #nav a, .login #backtoblog a { color: #ffffff !important; opacity: 0.95; font-weight: 500; text-shadow: 0 1px 3px rgba(0,0,0,0.3); }
287 .login #nav a:hover, .login #backtoblog a:hover { opacity: 1; text-decoration: underline !important; }
288 .login .language-switcher label { color: #ffffff !important; font-weight: 500; text-shadow: 0 1px 3px rgba(0,0,0,0.3); }
289 .login .language-switcher select { background-color: rgba(255, 255, 255, 0.95) !important; border: 1px solid rgba(255, 255, 255, 0.5) !important; color: #1e293b !important; }
290 .login .language-switcher .button { background: rgba(255, 255, 255, 0.25) !important; border: 1px solid rgba(255, 255, 255, 0.4) !important; color: #ffffff !important; backdrop-filter: blur(8px); }
291 .login .language-switcher .button:hover { background: rgba(255, 255, 255, 0.4) !important; }
292 ';
293 }
294
295 // Common Branding Options (Logo, Placeholders, Footer Links) - Applies to ALL Presets
296 $loginLogo = Helper::get_image_url('login_logo');
297 if (Helper::get_option('login_logo_hide')) {
298 $login_css .= 'body.login #login h1, .login h1, .login .wp-login-logo { display: none !important; }';
299 } elseif ($loginLogo) {
300 $login_css .= "body.login div#login h1 a, .login h1 a { background-image: url({$loginLogo}) !important; background-size: contain !important; width: auto !important; max-width: 100% !important; }";
301 }
302
303 $links = (array) Helper::get_option('login_link_form', []);
304 if (in_array('remember', $links, true)) {
305 $login_css .= '.login form .forgetmenot { display: none !important; } .login .button-primary { width: 100% !important; min-height: 42px !important; font-size: 15px !important; }';
306 }
307 if (in_array('privacy', $links, true)) {
308 $login_css .= '.login .privacy-policy-page-link { display: none !important; }';
309 }
310 if (Helper::get_option('login_placeholder')) {
311 $login_css .= "
312 .login label[for=user_login], .login label[for=user_pass] { display: none !important; }
313 .login input.password-input { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI'; }
314 .login input::-webkit-input-placeholder { padding-left: 5px; transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 150ms cubic-bezier(0.4, 0, 0.2, 1); font-size: 13px; }
315 .login input:focus::-webkit-input-placeholder { transform: translateX(0px) translateY(-15px); font-size: 10px; color: #999; }
316 ";
317 }
318
319 // Custom Layout Settings (Color, Background, Radius)
320 if ($preset === 'custom' || $preset === 'default') {
321 $loginButton = Helper::get_option('login_color');
322 $loginBg = Helper::get_option('login_bg_color');
323 $loginBgImg = Helper::get_image_url('login_bg_image');
324 $loginRadius = Helper::get_option('login_form_radius');
325
326 if ($loginBg) {
327 $login_css .= "body { background: {$loginBg} !important; }";
328 }
329 if ($loginBgImg) {
330 $login_css .= "body { background-image: url({$loginBgImg}) !important; background-size: cover !important; background-repeat: repeat !important; }";
331 }
332 if ($loginButton) {
333 $login_css .= "
334 .login #nav a:hover, .login #backtoblog a:hover, .login h1 a:hover, .login #nav a:focus, .login #backtoblog a:focus, .login h1 a:focus { color: {$loginButton} !important; }
335 input[type=text]:focus, input[type=password]:focus, input[type=checkbox]:focus { border-color: {$loginButton} !important; box-shadow: 0 0 2px {$loginButton} !important; }
336 .wp-core-ui .button-group.button-large .button, .wp-core-ui .button.button-large { background: {$loginButton} !important; border-color: {$loginButton} !important; box-shadow: 0 1px 0 {$loginButton} !important; }";
337 }
338 if ($loginRadius) {
339 $login_css .= ".login form { border-radius: {$loginRadius}px !important; }";
340 }
341 }
342
343 if (!empty($login_css)) {
344 wp_add_inline_style('login', Helper::minifyCSS($login_css));
345 }
346 }
347
348 protected $features = [
349 'login_title',
350 'login_url',
351 'donot_copy',
352 'login_logo_url',
353 'login_link_form',
354 'adminfooter_version',
355 'adminfooter_custom',
356 'donot_back',
357 'limit_login',
358 ];
359
360 public function login_title() {
361 add_filter('login_title', [$this, 'custom_title']);
362 }
363
364 public function custom_title($title = '') {
365 $custom = Helper::get_option('login_title');
366 return !empty($custom) ? esc_html($custom) : ($title ?: get_option('blogname'));
367 }
368
369 public function login_url() {
370 add_filter('site_url', [$this, 'site_url_custom'], 10, 4);
371 add_action('plugins_loaded', [$this, 'plugins_loaded_custom'], 2);
372 add_action('wp_loaded', [$this, 'wp_loaded_custom']);
373 add_filter('wp_redirect', [$this, 'wp_redirect_custom'], 10, 2);
374 }
375
376 private function filter_wp_login($url, $scheme = null) {
377 if (strpos($url, 'wp-login.php') !== false) {
378 if (is_ssl()) {
379 $scheme = 'https';
380 }
381 $query_string = explode('?', $url);
382 if (isset($query_string[1])) {
383 parse_str($query_string[1], $query_string);
384 if (isset($query_string['login'])) {
385 $query_string['login'] = rawurlencode($query_string['login']);
386 }
387 $url = add_query_arg($query_string, $this->login_url_custom($scheme));
388 } else {
389 $url = $this->login_url_custom($scheme);
390 }
391 }
392 return $url;
393 }
394
395 private function login_url_custom($scheme = null) {
396 $slug = $this->slug_custom();
397 if (empty($slug)) {
398 return site_url('wp-login.php', $scheme);
399 }
400 if (get_option('permalink_structure')) {
401 return $this->trailingslashit_custom(home_url('/' . $slug, $scheme));
402 } else {
403 return home_url('/', $scheme) . '?' . $slug;
404 }
405 }
406
407 private function trailingslashit_custom($string) {
408 if (substr(get_option('permalink_structure'), -1, 1) === '/') {
409 return trailingslashit($string);
410 } else {
411 return untrailingslashit($string);
412 }
413 }
414
415 private function slug_custom() {
416 return trim(Helper::get_option('login_url', ''), '/');
417 }
418
419 public function site_url_custom($url, $path, $scheme, $blog_id) {
420 return $this->filter_wp_login($url, $scheme);
421 }
422
423 public function wp_redirect_custom($location, $status) {
424 return $this->filter_wp_login($location);
425 }
426
427 public function plugins_loaded_custom() {
428 global $pagenow;
429 $slug = $this->slug_custom();
430 if (empty($slug)) {
431 return;
432 }
433
434 $URI = wp_parse_url($_SERVER['REQUEST_URI'] ?? '');
435 $req_path = untrailingslashit($URI['path'] ?? '');
436 $home_path = untrailingslashit(wp_parse_url(home_url(), PHP_URL_PATH) ?? '');
437 $site_path = untrailingslashit(wp_parse_url(site_url(), PHP_URL_PATH) ?? '');
438 $expected_slug_path = untrailingslashit($home_path . '/' . $slug);
439
440 $is_login_page = ($req_path === untrailingslashit($site_path . '/wp-login.php')) ||
441 (strpos(rawurldecode($_SERVER['REQUEST_URI'] ?? ''), 'wp-login.php') !== false) ||
442 ($req_path === untrailingslashit($site_path . '/wp-login'));
443
444 $is_register_page = ($req_path === untrailingslashit($site_path . '/wp-register.php')) ||
445 ($req_path === untrailingslashit($site_path . '/wp-signup.php')) ||
446 ($req_path === untrailingslashit($site_path . '/wp-register'));
447
448 if (!is_admin() && $is_login_page) {
449 $this->wp_login_enabled = true;
450 $_SERVER['REQUEST_URI'] = $this->trailingslashit_custom('/' . str_repeat('-/', 10));
451 $pagenow = 'index.php';
452 } elseif (!is_admin() && $is_register_page) {
453 $this->wp_login_enabled = true;
454 $_SERVER['REQUEST_URI'] = $this->trailingslashit_custom('/' . str_repeat('-/', 10));
455 $pagenow = 'index.php';
456 } elseif ($req_path === $expected_slug_path || isset($_GET[$slug])) {
457 $pagenow = 'wp-login.php';
458 }
459 }
460
461 public function wp_loaded_custom() {
462 if (!apply_filters('login_url_custom', true)) {
463 return;
464 }
465 global $pagenow;
466 $URI = wp_parse_url($_SERVER['REQUEST_URI'] ?? '');
467 if (is_admin() && !is_user_logged_in() && !defined('WP_CLI') && !defined('DOING_AJAX') && $pagenow !== 'admin-post.php' && (isset($_GET) && empty($_GET['adminhash']) && empty($_GET['newuseremail']))) {
468 $this->disable_login_url();
469 }
470 if ($pagenow === 'wp-login.php' && isset($URI['path']) && $URI['path'] !== $this->trailingslashit_custom($URI['path']) && get_option('permalink_structure')) {
471 $URL = $this->trailingslashit_custom($this->login_url_custom()) . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
472 wp_safe_redirect($URL);
473 exit;
474 } elseif ($this->wp_login_enabled) {
475 $this->disable_login_url();
476 } elseif ($pagenow === 'wp-login.php') {
477 if (is_user_logged_in() && !isset($_REQUEST['action'])) {
478 wp_safe_redirect(admin_url());
479 exit;
480 }
481 @require_once ABSPATH . 'wp-login.php';
482 exit;
483 }
484 }
485
486 public function enqueue_turnstile_script() {
487 wp_enqueue_script('cloudflare-turnstile', 'https://challenges.cloudflare.com/turnstile/v0/api.js', [], null, true);
488 }
489
490 public function display_turnstile_captcha() {
491 $site_key = Helper::get_option('turnstile_site_key', '');
492 if (!empty($site_key)) {
493 echo '<div class="cf-turnstile" data-sitekey="' . esc_attr($site_key) . '" style="margin-bottom: 15px; margin-top: 5px;"></div>';
494 }
495 }
496
497 public function verify_turnstile_captcha($user, $username, $password = '') {
498 if (isset($_POST['wp-submit'])) {
499 $secret_key = Helper::get_option('turnstile_secret_key', '');
500 $response_token = sanitize_text_field($_POST['cf-turnstile-response'] ?? '');
501
502 if (empty($secret_key)) {
503 return $user;
504 }
505
506 if (empty($response_token)) {
507 return new \WP_Error('authentication_failed', __('<strong>ERROR</strong>: Please complete the security captcha.', 'wp-extra'));
508 }
509
510 $remote_ip = sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '');
511
512 $response = wp_safe_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
513 'body' => [
514 'secret' => $secret_key,
515 'response' => $response_token,
516 'remoteip' => $remote_ip
517 ],
518 'timeout' => 10,
519 ]);
520
521 if (is_wp_error($response)) {
522 return new \WP_Error('authentication_failed', __('<strong>ERROR</strong>: Unable to verify captcha.', 'wp-extra'));
523 }
524
525 $body = json_decode(wp_remote_retrieve_body($response), true);
526
527 if (empty($body['success'])) {
528 return new \WP_Error('authentication_failed', __('<strong>ERROR</strong>: Captcha verification failed.', 'wp-extra'));
529 }
530 }
531 return $user;
532 }
533
534 public function verify_woocommerce_turnstile($validation_error, $username = '', $password = '') {
535 $secret_key = Helper::get_option('turnstile_secret_key', '');
536 $response_token = sanitize_text_field($_POST['cf-turnstile-response'] ?? '');
537
538 if (empty($secret_key)) {
539 return $validation_error;
540 }
541
542 if (is_wp_error($validation_error) && $validation_error->get_error_code()) {
543 return $validation_error;
544 }
545
546 if (empty($response_token)) {
547 $error = new \WP_Error();
548 $error->add('authentication_failed', __('<strong>ERROR</strong>: Please complete the security captcha.', 'wp-extra'));
549 return $error;
550 }
551
552 $remote_ip = sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? '');
553
554 $response = wp_safe_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
555 'body' => [
556 'secret' => $secret_key,
557 'response' => $response_token,
558 'remoteip' => $remote_ip
559 ],
560 'timeout' => 10,
561 ]);
562
563 if (is_wp_error($response)) {
564 $error = new \WP_Error();
565 $error->add('authentication_failed', __('<strong>ERROR</strong>: Unable to verify captcha.', 'wp-extra'));
566 return $error;
567 }
568
569 $body = json_decode(wp_remote_retrieve_body($response), true);
570
571 if (empty($body['success'])) {
572 $error = new \WP_Error();
573 $error->add('authentication_failed', __('<strong>ERROR</strong>: Captcha verification failed.', 'wp-extra'));
574 return $error;
575 }
576
577 return $validation_error;
578 }
579
580 public function flatsome_modal_turnstile_script() {
581 if (!Helper::get_option('turnstile_my_account') || !Helper::is_woo_active()) {
582 return;
583 }
584 $site_key = Helper::get_option('turnstile_site_key', '');
585 if (empty($site_key)) {
586 return;
587 }
588 ?>
589 <script type="text/javascript">
590 jQuery(function($) {
591 function initTurnstileInPopups() {
592 if (window.turnstile && typeof window.turnstile.render === 'function') {
593 $('#login-form-popup .cf-turnstile, .mfp-content .cf-turnstile').each(function() {
594 if (!$(this).children().length) {
595 var sk = $(this).data('sitekey') || '<?php echo esc_js($site_key); ?>';
596 window.turnstile.render(this, { sitekey: sk });
597 }
598 });
599 }
600 }
601
602 $(document).on('mfpOpen click', '.account-item > a, a[href="#header-account-register"], a[href="#header-account-login"], a[data-open="#login-form-popup"]', function() {
603 setTimeout(initTurnstileInPopups, 120);
604 });
605 });
606 </script>
607 <?php
608 }
609
610 private function disable_login_url() {
611 wp_safe_redirect(home_url());
612 exit;
613 }
614
615 public function donot_copy() {
616 add_action('wp_enqueue_scripts', [$this, 'donot_scripts']);
617 }
618
619 public function donot_scripts() {
620 if (is_user_logged_in()) {
621 return;
622 }
623 $copyright = Helper::get_option('donot_copyright', 'WP EXtra');
624 $select_text = (bool) Helper::get_option('donot_content');
625 wp_enqueue_script('donotcopy', plugins_url('/assets/js/copyright.min.js', WPEX_FILE), ['jquery']);
626 wp_localize_script('donotcopy', 'wpEXtra', ['copyright' => $copyright, 'select_text' => $select_text]);
627 }
628
629 public function login_logo_url() {
630 add_filter('login_headerurl', [$this, 'logo_url']);
631 add_filter('login_headertext', [$this, 'logo_url']);
632 }
633
634 public function logo_url() {
635 return Helper::get_option('login_logo_url') ?: home_url('/');
636 }
637
638 public function login_link_form() {
639 $links = (array) Helper::get_option('login_link_form', []);
640 if (in_array('language', $links, true)) {
641 add_filter('login_display_language_dropdown', '__return_false');
642 }
643 if (in_array('lost', $links, true)) {
644 add_filter('option_users_can_register', [$this, 'remove_siteuser']);
645 add_filter('lost_password_html_link', [$this, 'remove_site_link']);
646 }
647 if (in_array('backto', $links, true)) {
648 add_filter('login_site_html_link', [$this, 'remove_site_link']);
649 }
650 }
651
652 public function remove_siteuser($value) {
653 $script = basename(parse_url($_SERVER['SCRIPT_NAME'] ?? '', PHP_URL_PATH));
654 if ($script === 'wp-login.php') {
655 $value = false;
656 }
657 return $value;
658 }
659
660 public function remove_site_link($link) {
661 return '';
662 }
663
664 public function login_scripts() {
665 $loginPlaceholder = Helper::get_option('login_placeholder');
666 $loginRemember = Helper::get_option('login_remember');
667 ?>
668 <script>
669 document.addEventListener("DOMContentLoaded", function() {
670 <?php if ($loginPlaceholder): ?>
671 var u = document.getElementById("user_login");
672 var p = document.getElementById("user_pass");
673 if (u) u.placeholder = "<?php echo esc_js(__('Username or Email Address')); ?>";
674 if (p) p.placeholder = "<?php echo esc_js(__('Password')); ?>";
675 <?php endif; ?>
676 <?php if ($loginRemember): ?>
677 var r = document.getElementById("rememberme");
678 if (r) r.checked = true;
679 <?php endif; ?>
680 });
681 </script>
682 <?php
683 }
684
685 public function adminfooter_version() {
686 add_filter('update_footer', '__return_empty_string', 99);
687 }
688
689 public function adminfooter_custom() {
690 add_filter('admin_footer_text', [$this, 'render_admin_footer_custom'], 99);
691 }
692
693 public function render_admin_footer_custom($footer_text) {
694 $custom = Helper::get_option('adminfooter_custom');
695 if (!empty($custom)) {
696 return wp_kses_post(wpautop($custom));
697 }
698 return $footer_text;
699 }
700
701 public function donot_back() {
702 add_action('wp_enqueue_scripts', [$this, 'donot_back_scripts']);
703 }
704
705 public function donot_back_scripts() {
706 if (is_user_logged_in()) {
707 return;
708 }
709 wp_enqueue_script('history-back', plugins_url('/assets/js/back.min.js', WPEX_FILE), ['jquery']);
710 }
711
712 public function limit_login() {
713 add_action('wp_login_failed', [$this, 'handle_failed_login']);
714 add_action('wp_login', [$this, 'clear_failed_login_transient'], 10, 2);
715 add_filter('authenticate', [$this, 'check_lockout'], 30, 3);
716 }
717
718 public function check_lockout($user, $username, $password) {
719 if (empty($username)) {
720 return $user;
721 }
722 $ip = sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown');
723 $key = 'wpex_failed_login_' . md5(sanitize_user($username) . '_' . $ip);
724 $attempts = get_transient($key);
725 $max_limit = intval(Helper::get_option('limit_login', 5));
726
727 if ($attempts && $attempts['count'] >= $max_limit) {
728 $remaining_time = time() - $attempts['last_attempt'];
729 if ($remaining_time < 600) {
730 $wait_minutes = ceil((600 - $remaining_time) / 60);
731 return new \WP_Error('lockout', sprintf(__('Too many failed login attempts. Please wait %d minutes before trying again.', 'wp-extra'), $wait_minutes));
732 } else {
733 delete_transient($key);
734 }
735 }
736
737 return $user;
738 }
739
740 public function handle_failed_login($username) {
741 if (empty($username)) {
742 return;
743 }
744 $ip = sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown');
745 $key = 'wpex_failed_login_' . md5(sanitize_user($username) . '_' . $ip);
746 $attempts = get_transient($key);
747
748 if (!$attempts) {
749 $attempts = ['count' => 1, 'last_attempt' => time()];
750 } else {
751 $attempts['count']++;
752 $attempts['last_attempt'] = time();
753 }
754
755 set_transient($key, $attempts, 600);
756 }
757
758 public function clear_failed_login_transient($user_login, $user) {
759 $ip = sanitize_text_field($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? 'unknown');
760 delete_transient('wpex_failed_login_' . md5(sanitize_user($user_login) . '_' . $ip));
761 }
762
763 }