PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
← All changes | includes/extensions/Age_Gate/Age_Gate.php +232 -29 51.1.44 → 51.1.86 View file →
@@ -249,13 +249,17 @@
249 249 * @return void
250 250 */
251 251 public function enqueue_front_assets(): void
252 252 {
253 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
254 + return;
255 + }
256 +
253 257 if (is_admin()) {
254 258 return;
255 259 }
256 260
257 - $should_render = $this->should_render() || $this->should_render_block_state();
261 + $should_render = $this->should_render() || $this->should_render_block_state() || $this->is_preview_mode();
258 262
259 263 if (!$should_render) {
260 264 return;
261 265 }
@@ -287,11 +291,15 @@
287 291 * @return void
288 292 */
289 293 public function render_frontend_markup(): void
290 294 {
291 - if (!$this->should_render() && !$this->should_render_block_state()) {
295 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
292 296 return;
293 297 }
298 +
299 + if (!$this->should_render() && !$this->should_render_block_state() && !$this->is_preview_mode()) {
300 + return;
301 + }
294 302 ?>
295 303 <div id="king-addons-age-gate" class="king-addons-age-gate" aria-hidden="true">
296 304 <div class="king-addons-age-gate__overlay"></div>
297 305 <div class="king-addons-age-gate__card" role="dialog" aria-modal="true" aria-label="<?php echo esc_attr($this->options['design']['title']); ?>">
@@ -307,12 +315,16 @@
307 315 * @return void
308 316 */
309 317 public function render_portal_container(): void
310 318 {
311 - if (!$this->should_render() && !$this->should_render_block_state()) {
319 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
312 320 return;
313 321 }
314 322
323 + if (!$this->should_render() && !$this->should_render_block_state() && !$this->is_preview_mode()) {
324 + return;
325 + }
326 +
315 327 echo '<div id="king-addons-age-gate-portal" class="king-addons-age-gate__portal" aria-hidden="true"></div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
316 328 }
317 329
318 330 /**
@@ -321,8 +333,12 @@
321 333 * @return void
322 334 */
323 335 public function maybe_handle_denied_redirect(): void
324 336 {
337 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
338 + return;
339 + }
340 +
325 341 if (is_admin() || wp_doing_ajax()) {
326 342 return;
327 343 }
328 344
@@ -331,23 +347,44 @@
331 347 }
332 348
333 349 $status = $this->get_cookie_status();
334 350 $behaviour = $this->options['behaviour'];
335 - $redirect_id = isset($behaviour['deny_redirect_page']) ? (int)$behaviour['deny_redirect_page'] : 0;
336 351
337 - if ($status !== 'denied' || $behaviour['deny_action'] !== 'redirect' || !$redirect_id) {
352 + if ($status !== 'denied' || !$this->is_deny_action_redirect($behaviour['deny_action'] ?? '')) {
338 353 return;
339 354 }
340 355
341 - if (is_page($redirect_id)) {
356 + $deny_action = (string) ($behaviour['deny_action'] ?? '');
357 + if ($deny_action === 'redirect_page' || $deny_action === 'redirect') {
358 + $redirect_id = isset($behaviour['deny_redirect_page']) ? (int) $behaviour['deny_redirect_page'] : 0;
359 + if ($redirect_id && is_page($redirect_id)) {
360 + return;
361 + }
362 + }
363 +
364 + $url = $this->resolve_deny_redirect_url();
365 + if (!$url) {
342 366 return;
343 367 }
344 368
345 - $url = get_permalink($redirect_id);
346 - if ($url) {
369 + $request_uri = isset($_SERVER['REQUEST_URI']) ? (string) wp_unslash($_SERVER['REQUEST_URI']) : '';
370 + if ($request_uri) {
371 + $current_url = home_url($request_uri);
372 + if (untrailingslashit($current_url) === untrailingslashit($url)) {
373 + return;
374 + }
375 + }
376 +
377 + $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
378 + $target_host = wp_parse_url($url, PHP_URL_HOST);
379 +
380 + // Prefer safe redirects for internal destinations; allow external URLs when configured by admin.
381 + if ($site_host && $target_host && strtolower((string) $site_host) === strtolower((string) $target_host)) {
347 382 wp_safe_redirect($url);
348 - exit;
383 + } else {
384 + wp_redirect($url);
349 385 }
386 + exit;
350 387 }
351 388
352 389 /**
353 390 * Determines whether the overlay should render.
@@ -355,8 +392,12 @@
355 392 * @return bool
356 393 */
357 394 public function should_render(): bool
358 395 {
396 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
397 + return false;
398 + }
399 +
359 400 if (is_admin() || wp_doing_ajax()) {
360 401 return false;
361 402 }
362 403
@@ -363,8 +404,12 @@
363 404 if (!$this->is_enabled()) {
364 405 return false;
365 406 }
366 407
408 + if ($this->is_preview_mode()) {
409 + return true;
410 + }
411 +
367 412 if (!$this->passes_display_rules()) {
368 413 return false;
369 414 }
370 415
@@ -377,9 +422,9 @@
377 422 if ($status === 'allowed') {
378 423 return false;
379 424 }
380 425
381 - if ($status === 'denied' && $this->options['behaviour']['deny_action'] === 'redirect') {
426 + if ($status === 'denied' && $this->is_deny_action_redirect($this->options['behaviour']['deny_action'] ?? '') && $this->has_deny_redirect_target()) {
382 427 return false;
383 428 }
384 429
385 430 return true;
@@ -391,14 +436,18 @@
391 436 * @return bool
392 437 */
393 438 protected function should_render_block_state(): bool
394 439 {
440 + if (defined('KING_ADDONS_IS_MAINTENANCE_PAGE') && KING_ADDONS_IS_MAINTENANCE_PAGE) {
441 + return false;
442 + }
443 +
395 444 if (!$this->is_enabled()) {
396 445 return false;
397 446 }
398 447
399 448 $status = $this->get_cookie_status();
400 - return $status === 'denied' && $this->options['behaviour']['deny_action'] === 'block';
449 + return $status === 'denied' && (($this->options['behaviour']['deny_action'] ?? '') === 'block');
401 450 }
402 451
403 452 /**
404 453 * Checks if general display rules allow the gate.
@@ -420,9 +469,9 @@
420 469 return is_singular('post');
421 470 }
422 471
423 472 if ($scope === 'pages') {
424 - return is_page();
473 + return $this->is_pages_scope_match();
425 474 }
426 475
427 476 return true;
428 477 }
@@ -427,8 +476,23 @@
427 476 return true;
428 477 }
429 478
430 479 /**
480 + * Pages scope includes WordPress pages and the WooCommerce shop archive.
481 + *
482 + * WooCommerce shop is a product archive, so is_page() is false there even
483 + * when the Shop page ID is selected as a page.
484 + */
485 + protected function is_pages_scope_match(): bool
486 + {
487 + if (is_page()) {
488 + return true;
489 + }
490 +
491 + return function_exists('is_shop') && is_shop();
492 + }
493 +
494 + /**
431 495 * Checks if current page is excluded from gating.
432 496 *
433 497 * @return bool
434 498 */
@@ -433,13 +497,21 @@
433 497 * @return bool
434 498 */
435 499 protected function is_excluded_page(): bool
436 500 {
501 + $exclude_ids = array_map('absint', $this->options['display']['exclude_ids']);
502 +
503 + if (function_exists('is_shop') && is_shop() && function_exists('wc_get_page_id')) {
504 + $shop_id = (int) wc_get_page_id('shop');
505 + if ($shop_id && in_array($shop_id, $exclude_ids, true)) {
506 + return true;
507 + }
508 + }
509 +
437 510 if (!is_singular()) {
438 511 return false;
439 512 }
440 513
441 - $exclude_ids = array_map('absint', $this->options['display']['exclude_ids']);
442 514 $current_id = get_the_ID();
443 515
444 516 if (in_array($current_id, $exclude_ids, true)) {
445 517 return true;
@@ -464,8 +536,16 @@
464 536 $behaviour = $this->options['behaviour'];
465 537 $general = $this->options['general'];
466 538 $dob = $this->options['dob'];
467 539
540 + $deny_redirect_id = isset($behaviour['deny_redirect_page']) ? (int) $behaviour['deny_redirect_page'] : 0;
541 + $deny_redirect_url = $this->resolve_deny_redirect_url();
542 + $deny_action_raw = (string) ($behaviour['deny_action'] ?? 'block');
543 + $deny_action = $this->is_deny_action_redirect($deny_action_raw) ? 'redirect' : 'block';
544 + if ($deny_action === 'redirect' && !$deny_redirect_url) {
545 + $deny_action = 'block';
546 + }
547 +
468 548 return [
469 549 'enabled' => $this->is_enabled(),
470 550 'mode' => $general['mode'],
471 551 'minAge' => $this->resolve_minimum_age(),
@@ -500,14 +580,14 @@
500 580 'logo' => $design['logo'],
501 581 'backgroundImage' => $design['background_image'],
502 582 ],
503 583 'behaviour' => [
504 - 'denyAction' => $behaviour['deny_action'],
505 - 'denyRedirect' => (int) $behaviour['deny_redirect_page'],
506 - 'denyRedirectUrl' => $behaviour['deny_redirect_page'] ? get_permalink((int) $behaviour['deny_redirect_page']) : '',
584 + 'denyAction' => $deny_action,
585 + 'denyRedirect' => $deny_redirect_id,
586 + 'denyRedirectUrl' => $deny_redirect_url,
507 587 'blockMessage' => $behaviour['block_message'],
508 588 'consentCheckbox' => (bool) $behaviour['consent_checkbox'],
509 - 'consentLabel' => esc_html__('I agree to the policy.', 'king-addons'),
589 + 'consentLabel' => __('I agree to the policy.', 'king-addons'),
510 590 'repeatMode' => $behaviour['repeat_mode'] ?? 'days',
511 591 'repeatDays' => (int) ($behaviour['repeat_days'] ?? 30),
512 592 ],
513 593 'cookie' => [
@@ -524,9 +604,10 @@
524 604 'denied' => $dob['error_denied'],
525 605 ],
526 606 ],
527 607 'status' => $this->get_cookie_status(),
528 - 'shouldRender' => $this->should_render(),
608 + 'shouldRender' => $this->should_render() || $this->is_preview_mode(),
609 + 'isPreview' => $this->is_preview_mode(),
529 610 'ajax' => [
530 611 'url' => admin_url('admin-ajax.php'),
531 612 'nonce' => wp_create_nonce(self::NONCE_ACTION),
532 613 'action' => self::AJAX_ACTION_DOB,
@@ -536,8 +617,29 @@
536 617 ];
537 618 }
538 619
539 620 /**
621 + * Preview mode: force render Age Gate for admins via query param.
622 + * Example: /?ka_age_gate_preview=1
623 + */
624 + protected function is_preview_mode(): bool
625 + {
626 + if (is_admin() || wp_doing_ajax()) {
627 + return false;
628 + }
629 +
630 + if (!$this->is_enabled()) {
631 + return false;
632 + }
633 +
634 + if (!is_user_logged_in() || !current_user_can('manage_options')) {
635 + return false;
636 + }
637 +
638 + return isset($_GET['ka_age_gate_preview']) && (string) $_GET['ka_age_gate_preview'] === '1';
639 + }
640 +
641 + /**
540 642 * Indicates whether premium code is available.
541 643 *
542 644 * @return bool
543 645 */
@@ -561,8 +663,12 @@
561 663 * @return array<string, mixed>
562 664 */
563 665 public function get_options(): array
564 666 {
667 + if (class_exists('King_Addons\\Text_Entities_Migration')) {
668 + Text_Entities_Migration::maybe_run();
669 + }
670 +
565 671 $saved = get_option(self::OPTION_NAME, []);
566 672 $defaults = $this->get_default_options();
567 673
568 674 return wp_parse_args($saved, $defaults);
@@ -601,12 +707,12 @@
601 707 'overlay_opacity' => 0.7,
602 708 'card_background' => '#ffffff',
603 709 'card_width' => 520,
604 710 'card_align' => 'center',
605 - 'title' => esc_html__('Age verification required', 'king-addons'),
606 - 'subtitle' => esc_html__('This content is restricted to visitors over the specified age.', 'king-addons'),
607 - 'button_yes' => esc_html__('Yes, continue', 'king-addons'),
608 - 'button_no' => esc_html__('No, leave', 'king-addons'),
711 + 'title' => __('Age verification required', 'king-addons'),
712 + 'subtitle' => __('This content is restricted to visitors over the specified age.', 'king-addons'),
713 + 'button_yes' => __('Yes, continue', 'king-addons'),
714 + 'button_no' => __('No, leave', 'king-addons'),
609 715 'text_color' => '#111827',
610 716 'title_size' => 24,
611 717 'body_size' => 16,
612 718 'title_weight' => 700,
@@ -624,11 +730,12 @@
624 730 'button_no_hover_color' => '#ffffff',
625 731 'elementor_template' => 0,
626 732 ],
627 733 'behaviour' => [
628 - 'deny_action' => 'redirect',
734 + 'deny_action' => 'redirect_url',
629 735 'deny_redirect_page' => 0,
630 - 'block_message' => esc_html__('Access denied. You do not meet the minimum age requirement.', 'king-addons'),
736 + 'deny_redirect_url' => 'https://google.com',
737 + 'block_message' => __('Access denied. You do not meet the minimum age requirement.', 'king-addons'),
631 738 'consent_checkbox' => false,
632 739 'reset_on_rule_change' => true,
633 740 'repeat_mode' => 'days',
634 741 'repeat_days' => 30,
@@ -643,10 +750,10 @@
643 750 ],
644 751 'dob' => [
645 752 'format' => 'dmy',
646 753 'max_age' => 120,
647 - 'error_invalid' => esc_html__('Enter a valid date of birth.', 'king-addons'),
648 - 'error_denied' => esc_html__('You do not meet the minimum age requirement.', 'king-addons'),
754 + 'error_invalid' => __('Enter a valid date of birth.', 'king-addons'),
755 + 'error_denied' => __('You do not meet the minimum age requirement.', 'king-addons'),
649 756 ],
650 757 ];
651 758 }
652 759
@@ -666,8 +773,37 @@
666 773 $display = $raw['display'] ?? [];
667 774 $design = $raw['design'] ?? [];
668 775 $behaviour = $raw['behaviour'] ?? [];
669 776
777 + $allowed_templates = [
778 + 'center-card',
779 + 'bottom-card',
780 + 'top-card',
781 + 'side-left',
782 + 'side-right',
783 + 'fullscreen',
784 + ];
785 +
786 + $template = in_array($design['template'] ?? 'center-card', $allowed_templates, true)
787 + ? $design['template']
788 + : 'center-card';
789 +
790 + // Back-compat: if card_align isn't explicitly set, infer it from template.
791 + $inferred_align = 'center';
792 + if ($template === 'bottom-card') {
793 + $inferred_align = 'bottom';
794 + } elseif ($template === 'top-card') {
795 + $inferred_align = 'top';
796 + }
797 +
798 + $deny_action_raw = (string) ($behaviour['deny_action'] ?? ($defaults['behaviour']['deny_action'] ?? 'redirect_url'));
799 + if ($deny_action_raw === 'redirect') {
800 + // Back-compat: older saved values.
801 + $deny_action_raw = 'redirect_page';
802 + }
803 + $allowed_deny_actions = ['redirect_page', 'redirect_url', 'block'];
804 + $deny_action = in_array($deny_action_raw, $allowed_deny_actions, true) ? $deny_action_raw : ($defaults['behaviour']['deny_action'] ?? 'redirect_url');
805 +
670 806 $sanitized = [
671 807 'general' => [
672 808 'enabled' => !empty($general['enabled']),
673 809 'audience' => in_array($general['audience'] ?? 'guests', ['guests', 'all'], true) ? $general['audience'] : $defaults['general']['audience'],
@@ -679,14 +815,14 @@
679 815 'scope' => in_array($display['scope'] ?? 'site', ['site', 'posts', 'pages'], true) ? $display['scope'] : $defaults['display']['scope'],
680 816 'exclude_ids' => array_values(array_filter(array_map('absint', $display['exclude_ids'] ?? []))),
681 817 ],
682 818 'design' => [
683 - 'template' => in_array($design['template'] ?? 'center-card', ['center-card', 'bottom-card'], true) ? $design['template'] : 'center-card',
819 + 'template' => $template,
684 820 'overlay_color' => sanitize_hex_color($design['overlay_color'] ?? $defaults['design']['overlay_color']) ?: $defaults['design']['overlay_color'],
685 821 'overlay_opacity' => min(1, max(0, floatval($design['overlay_opacity'] ?? $defaults['design']['overlay_opacity']))),
686 822 'card_background' => sanitize_hex_color($design['card_background'] ?? $defaults['design']['card_background']) ?: $defaults['design']['card_background'],
687 823 'card_width' => max(280, absint($design['card_width'] ?? $defaults['design']['card_width'])),
688 - 'card_align' => in_array($design['card_align'] ?? 'center', ['center', 'bottom'], true) ? $design['card_align'] : 'center',
824 + 'card_align' => in_array($design['card_align'] ?? $inferred_align, ['center', 'bottom', 'top'], true) ? ($design['card_align'] ?? $inferred_align) : 'center',
689 825 'title' => sanitize_text_field($design['title'] ?? $defaults['design']['title']),
690 826 'subtitle' => sanitize_text_field($design['subtitle'] ?? $defaults['design']['subtitle']),
691 827 'button_yes' => sanitize_text_field($design['button_yes'] ?? $defaults['design']['button_yes']),
692 828 'button_no' => sanitize_text_field($design['button_no'] ?? $defaults['design']['button_no']),
@@ -703,10 +839,11 @@
703 839 'logo' => esc_url_raw($design['logo'] ?? ''),
704 840 'background_image' => esc_url_raw($design['background_image'] ?? ''),
705 841 ],
706 842 'behaviour' => [
707 - 'deny_action' => in_array($behaviour['deny_action'] ?? 'redirect', ['redirect', 'block'], true) ? $behaviour['deny_action'] : 'redirect',
843 + 'deny_action' => $deny_action,
708 844 'deny_redirect_page' => absint($behaviour['deny_redirect_page'] ?? 0),
845 + 'deny_redirect_url' => $this->sanitize_redirect_url($behaviour['deny_redirect_url'] ?? ($defaults['behaviour']['deny_redirect_url'] ?? '')),
709 846 'block_message' => sanitize_text_field($behaviour['block_message'] ?? $defaults['behaviour']['block_message']),
710 847 'consent_checkbox' => !empty($behaviour['consent_checkbox']),
711 848 'reset_on_rule_change' => isset($behaviour['reset_on_rule_change']) ? (bool) $behaviour['reset_on_rule_change'] : $defaults['behaviour']['reset_on_rule_change'],
712 849 'repeat_mode' => in_array($behaviour['repeat_mode'] ?? 'days', ['days', 'session', 'once'], true) ? $behaviour['repeat_mode'] : 'days',
@@ -790,8 +927,74 @@
790 927 'max_age' => max(10, absint($dob['max_age'] ?? $defaults['max_age'])),
791 928 'error_invalid' => sanitize_text_field($dob['error_invalid'] ?? $defaults['error_invalid']),
792 929 'error_denied' => sanitize_text_field($dob['error_denied'] ?? $defaults['error_denied']),
793 930 ];
931 + }
932 +
933 + /**
934 + * Sanitizes a custom redirect URL.
935 + *
936 + * @param mixed $raw_url Raw URL.
937 + * @return string Sanitized URL or empty string.
938 + */
939 + protected function sanitize_redirect_url($raw_url): string
940 + {
941 + $url = is_string($raw_url) ? trim($raw_url) : '';
942 + if ($url === '') {
943 + return '';
944 + }
945 +
946 + $url = esc_url_raw($url);
947 + if ($url && wp_http_validate_url($url)) {
948 + return $url;
949 + }
950 +
951 + return '';
952 + }
953 +
954 + /**
955 + * Returns the effective redirect destination when denial action is redirect.
956 + * Prefers an internal selected page, otherwise falls back to the custom URL.
957 + */
958 + protected function resolve_deny_redirect_url(): string
959 + {
960 + $behaviour = $this->options['behaviour'] ?? [];
961 + $deny_action = (string) ($behaviour['deny_action'] ?? '');
962 +
963 + // Legacy back-compat.
964 + if ($deny_action === 'redirect') {
965 + $deny_action = 'redirect_page';
966 + }
967 +
968 + $custom_url = isset($behaviour['deny_redirect_url']) ? $this->sanitize_redirect_url($behaviour['deny_redirect_url']) : '';
969 +
970 + if ($deny_action === 'redirect_url') {
971 + return $custom_url;
972 + }
973 +
974 + // redirect_page (default): prefer page ID, but fall back to URL if provided.
975 + $redirect_id = isset($behaviour['deny_redirect_page']) ? (int) $behaviour['deny_redirect_page'] : 0;
976 + if ($redirect_id) {
977 + return get_permalink($redirect_id) ?: '';
978 + }
979 +
980 + return $custom_url;
981 + }
982 +
983 + /**
984 + * True for redirect-based deny actions.
985 + */
986 + protected function is_deny_action_redirect(string $deny_action): bool
987 + {
988 + return in_array($deny_action, ['redirect', 'redirect_page', 'redirect_url'], true);
989 + }
990 +
991 + /**
992 + * Whether a denial redirect destination is configured.
993 + */
994 + protected function has_deny_redirect_target(): bool
995 + {
996 + return $this->resolve_deny_redirect_url() !== '';
794 997 }
795 998
796 999 /**
797 1000 * Determines whether the feature is active.