| @@ -95,9 +95,11 @@ | ||
| 95 | 95 | */ |
| 96 | 96 | public static function get_hellobar_config() { |
| 97 | 97 | return array( |
| 98 | 98 | // Final hours sale. |
| 99 | - 'ultp_helloBar_summer_sale_2026_v1' => Xpo::get_transient_without_cache( 'ultp_helloBar_summer_sale_2026_v1' ), | |
| 99 | + 'ultp_helloBar_summer_sale_2026_v1' => Xpo::get_transient_without_cache( 'ultp_helloBar_summer_sale_2026_v1' ), | |
| 100 | + 'ultp_helloBar_summer_sale_2026_v1_2' => Xpo::get_transient_without_cache( 'ultp_helloBar_summer_sale_2026_v1_2' ), | |
| 101 | + 'ultp_helloBar_base_price_2026' => Xpo::get_transient_without_cache( 'ultp_helloBar_base_price_2026' ), | |
| 100 | 102 | ); |
| 101 | 103 | } |
| 102 | 104 | |
| 103 | 105 | /** |
| @@ -110,10 +112,17 @@ | ||
| 110 | 112 | $request_params = $request->get_params(); |
| 111 | 113 | $type = isset( $request_params['type'] ) ? $request_params['type'] : ''; |
| 112 | 114 | $id = isset( $request_params['id'] ) ? $request_params['id'] : ''; |
| 113 | 115 | |
| 114 | - if ( 'hello_bar' === $type && ! empty( $id ) ) { | |
| 115 | - Xpo::set_transient_without_cache( $id, 'hide', 1296000 ); | |
| 116 | + // Guideline 11 — a dismissal must stick. This previously expired after 15 | |
| 117 | + // days (1296000s), so a dismissed sale bar came back on its own. Passing 0 | |
| 118 | + // stores it with no timeout, which Xpo::get_transient_without_cache() reads | |
| 119 | + // back indefinitely. | |
| 120 | + // | |
| 121 | + // $id names the option that gets written, so it is checked against the | |
| 122 | + // known hellobar keys rather than trusted from the request. | |
| 123 | + if ( 'hello_bar' === $type && ! empty( $id ) && array_key_exists( $id, self::get_hellobar_config() ) ) { | |
| 124 | + Xpo::set_transient_without_cache( $id, 'hide', 0 ); | |
| 116 | 125 | } |
| 117 | 126 | |
| 118 | 127 | return new \WP_REST_Response( |
| 119 | 128 | array( |
| @@ -130,9 +139,9 @@ | ||
| 130 | 139 | * @return void |
| 131 | 140 | */ |
| 132 | 141 | public function set_dismiss_notice_callback() { |
| 133 | 142 | |
| 134 | - if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['wpnonce'] ?? '' ) ), 'ultp-nonce' ) ) { | |
| 143 | + if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['wpnonce'] ?? '' ) ), 'ultp-admin-nonce' ) ) { | |
| 135 | 144 | return; |
| 136 | 145 | } |
| 137 | 146 | |
| 138 | 147 | $durbin_key = sanitize_text_field( wp_unslash( $_GET['ultp_durbin_key'] ?? '' ) ); |
| @@ -191,9 +200,9 @@ | ||
| 191 | 200 | * |
| 192 | 201 | * @return void |
| 193 | 202 | */ |
| 194 | 203 | public function ultp_dashboard_banner_notice( $return_bool = false ) { |
| 195 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 204 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 196 | 205 | $banner_notices = array( |
| 197 | 206 | // FLash sale. |
| 198 | 207 | array( |
| 199 | 208 | 'key' => 'ultp_banner_flash_sale_2026_1', |
| @@ -560,8 +569,15 @@ | ||
| 560 | 569 | */ |
| 561 | 570 | public function ultp_dashboard_durbin_notice_callback() { |
| 562 | 571 | $durbin_key = 'ultp_durbin_dc1'; |
| 563 | 572 | |
| 573 | + // Guideline 11 — the consent notice is allowed on PostX's own screens and, | |
| 574 | + // as is standard immediately after activation, on plugins.php. Nowhere else. | |
| 575 | + global $pagenow; | |
| 576 | + if ( ! $this->is_postx_screen() && 'plugins.php' !== $pagenow ) { | |
| 577 | + return; | |
| 578 | + } | |
| 579 | + | |
| 564 | 580 | if ( |
| 565 | 581 | isset( $_GET['ultp_durbin_key'] ) || // phpcs:ignore |
| 566 | 582 | 'off' === Xpo::get_transient_without_cache( 'ultp_durbin_notice_' . $durbin_key ) |
| 567 | 583 | ) { |
| @@ -571,9 +587,9 @@ | ||
| 571 | 587 | if ( ! $this->notice_js_css_applied ) { |
| 572 | 588 | $this->notice_js_css_applied = true; |
| 573 | 589 | } |
| 574 | 590 | |
| 575 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 591 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 576 | 592 | |
| 577 | 593 | ?> |
| 578 | 594 | <style> |
| 579 | 595 | .ultp-consent-box { |
| @@ -704,11 +720,11 @@ | ||
| 704 | 720 | 'button_text' => __( 'Upgrade Now!', 'ultimate-post' ), |
| 705 | 721 | 'is_discount_logo' => true, |
| 706 | 722 | ), |
| 707 | 723 | array( |
| 708 | - 'key' => 'ultp_dashboard_content_notice_summer_sale_2026_vv2', | |
| 709 | - 'start' => '2026-07-13 00:00 Asia/Dhaka', | |
| 710 | - 'end' => '2026-07-19 23:59 Asia/Dhaka', | |
| 724 | + 'key' => 'ultp_dashboard_content_notice_summer_sale_2026_vv3', | |
| 725 | + 'start' => '2026-08-02 00:00 Asia/Dhaka', | |
| 726 | + 'end' => '2026-08-08 23:59 Asia/Dhaka', | |
| 711 | 727 | 'url' => Xpo::generate_utm_link( |
| 712 | 728 | array( |
| 713 | 729 | 'utmKey' => 'summer_db', |
| 714 | 730 | ) |
| @@ -725,13 +741,13 @@ | ||
| 725 | 741 | ), |
| 726 | 742 | |
| 727 | 743 | ); |
| 728 | 744 | |
| 729 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 745 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 730 | 746 | |
| 731 | 747 | foreach ( $content_notices as $key => $notice ) { |
| 732 | 748 | $notice_key = isset( $notice['key'] ) ? $notice['key'] : $this->notice_version; |
| 733 | - if ( isset( $_GET['ultp_notice'] ) && $notice_key === $_GET['ultp_notice'] ) { | |
| 749 | + if ( isset( $_GET['ultp_notice'] ) && $notice_key === sanitize_text_field( wp_unslash( $_GET['ultp_notice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only; hides the notice on this page view. | |
| 734 | 750 | continue; |
| 735 | 751 | } else { |
| 736 | 752 | $border_color = $notice['border_color']; |
| 737 | 753 | |
| @@ -924,9 +940,34 @@ | ||
| 924 | 940 | * Handle Plugin Notice for all plugins |
| 925 | 941 | * |
| 926 | 942 | * @return bool |
| 927 | 943 | */ |
| 944 | + /** | |
| 945 | + * Whether the current admin screen belongs to PostX. | |
| 946 | + * | |
| 947 | + * Guideline 11 limits promotional UI to the plugin's own screens. Every PostX | |
| 948 | + * screen carries `ultp` in its screen id — `toplevel_page_ultp-settings`, | |
| 949 | + * `postx_page_ultp-setup-wizard`, and the `ultp_builder` / `ultp_templates` / | |
| 950 | + * `ultp_custom_font` post-type screens. | |
| 951 | + * | |
| 952 | + * @return BOOL True when the current screen is a PostX screen. | |
| 953 | + */ | |
| 954 | + public function is_postx_screen() { | |
| 955 | + if ( ! function_exists( 'get_current_screen' ) ) { | |
| 956 | + return false; | |
| 957 | + } | |
| 958 | + $screen = get_current_screen(); | |
| 959 | + return $screen && false !== strpos( $screen->id, 'ultp' ); | |
| 960 | + } | |
| 961 | + | |
| 928 | 962 | public function is_available_for_notice() { |
| 963 | + // Guideline 11 — sale banners and upgrade prompts belong on PostX's own | |
| 964 | + // screens, not on core screens or other plugins' pages. | |
| 965 | + if ( ! $this->is_postx_screen() ) { | |
| 966 | + return false; | |
| 967 | + } | |
| 968 | + | |
| 969 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Shared across WPXPO plugins (ProductX, WholesaleX, …); renaming breaks them. | |
| 929 | 970 | $active_notices = apply_filters( 'xpo_active_notice_lists', array() ); |
| 930 | 971 | if ( empty( $active_notices ) ) { |
| 931 | 972 | return true; |
| 932 | 973 | } |
| @@ -934,9 +975,9 @@ | ||
| 934 | 975 | return array_key_first( $active_notices ) === $this->plugin_notice_priority_key; |
| 935 | 976 | } |
| 936 | 977 | |
| 937 | 978 | public function ultp_dashboard_image_banner_notice( $return_bool = false ) { |
| 938 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 979 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 939 | 980 | $banner_notices = array( |
| 940 | 981 | array( |
| 941 | 982 | 'key' => 'ultp_summer_sale_2612', |
| 942 | 983 | 'start' => '2026-07-20 00:00 Asia/Dhaka', // format YY-MM-DD always set time 00:00 and zone Asia/Dhaka |
| @@ -943,12 +984,43 @@ | ||
| 943 | 984 | 'end' => '2026-08-01 23:59 Asia/Dhaka', // format YY-MM-DD always set time 23:59 and zone Asia/Dhaka |
| 944 | 985 | 'banner_src' => ULTP_URL . 'assets/img/dashboard_banner/dashboard_banner_summer_sale.png', |
| 945 | 986 | 'url' => Xpo::generate_utm_link( |
| 946 | 987 | array( |
| 947 | - 'utmKey' => 'summer_db', | |
| 988 | + 'utmKey' => 'sub_menu_offer', | |
| 948 | 989 | ) |
| 949 | 990 | ), |
| 950 | 991 | 'close_color' => '#000000', |
| 992 | + 'visibility' => ! Xpo::is_lc_active(), | |
| 993 | + ), | |
| 994 | + array( | |
| 995 | + 'key' => 'ultp_summer_sale_2613', | |
| 996 | + 'start' => '2026-08-09 00:00 Asia/Dhaka', // format YY-MM-DD always set time 00:00 and zone Asia/Dhaka | |
| 997 | + 'end' => '2026-08-16 23:59 Asia/Dhaka', // format YY-MM-DD always set time 23:59 and zone Asia/Dhaka | |
| 998 | + 'banner_src' => ULTP_URL . 'assets/img/dashboard_banner/dashboard_banner_summer_sale.png', | |
| 999 | + 'url' => Xpo::generate_utm_link( | |
| 1000 | + array( | |
| 1001 | + 'utmKey' => 'sub_menu_offer', | |
| 1002 | + ) | |
| 1003 | + ), | |
| 1004 | + 'close_color' => '#000000', | |
| 1005 | + 'visibility' => ! Xpo::is_lc_active(), | |
| 1006 | + ), | |
| 1007 | + array( | |
| 1008 | + 'key' => 'ultp_preco_sale_campaign_262_1', | |
| 1009 | + 'start' => '2026-08-23 00:00 Asia/Dhaka', // format YY-MM-DD always set time 00:00 and zone Asia/Dhaka | |
| 1010 | + 'end' => '2026-08-29 23:59 Asia/Dhaka', // format YY-MM-DD always set time 23:59 and zone Asia/Dhaka | |
| 1011 | + 'banner_src' => ULTP_URL . 'assets/img/dashboard_banner/preco_banner_insider_deal.png', | |
| 1012 | + 'url' => 'https://www.wpxpo.com/product/wowrecommend/?utm_source=db-postx-notice&utm_medium=insider-deal&utm_campaign=postx-dashboard/#pricing', | |
| 1013 | + 'close_color' => '#ffffff', | |
| 1014 | + 'visibility' => ! Xpo::is_lc_active(), | |
| 1015 | + ), | |
| 1016 | + array( | |
| 1017 | + 'key' => 'ultp_preco_sale_campaign_262_2', | |
| 1018 | + 'start' => '2026-08-30 00:00 Asia/Dhaka', // format YY-MM-DD always set time 00:00 and zone Asia/Dhaka. | |
| 1019 | + 'end' => '2026-09-19 23:59 Asia/Dhaka', // format YY-MM-DD always set time 23:59 and zone Asia/Dhaka. | |
| 1020 | + 'banner_src' => ULTP_URL . 'assets/img/dashboard_banner/preco_banner_early_bird.png', | |
| 1021 | + 'url' => 'https://www.wpxpo.com/product/wowrecommend/?utm_source=db-postx-notice&utm_medium=early-bird&utm_campaign=postx-dashboard/#pricing', | |
| 1022 | + 'close_color' => '#ffffff', | |
| 951 | 1023 | 'visibility' => ! Xpo::is_lc_active(), |
| 952 | 1024 | ), |
| 953 | 1025 | ); |
| 954 | 1026 | |