| @@ -112,10 +112,17 @@ | ||
| 112 | 112 | $request_params = $request->get_params(); |
| 113 | 113 | $type = isset( $request_params['type'] ) ? $request_params['type'] : ''; |
| 114 | 114 | $id = isset( $request_params['id'] ) ? $request_params['id'] : ''; |
| 115 | 115 | |
| 116 | - if ( 'hello_bar' === $type && ! empty( $id ) ) { | |
| 117 | - 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 ); | |
| 118 | 125 | } |
| 119 | 126 | |
| 120 | 127 | return new \WP_REST_Response( |
| 121 | 128 | array( |
| @@ -132,9 +139,9 @@ | ||
| 132 | 139 | * @return void |
| 133 | 140 | */ |
| 134 | 141 | public function set_dismiss_notice_callback() { |
| 135 | 142 | |
| 136 | - 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' ) ) { | |
| 137 | 144 | return; |
| 138 | 145 | } |
| 139 | 146 | |
| 140 | 147 | $durbin_key = sanitize_text_field( wp_unslash( $_GET['ultp_durbin_key'] ?? '' ) ); |
| @@ -193,9 +200,9 @@ | ||
| 193 | 200 | * |
| 194 | 201 | * @return void |
| 195 | 202 | */ |
| 196 | 203 | public function ultp_dashboard_banner_notice( $return_bool = false ) { |
| 197 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 204 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 198 | 205 | $banner_notices = array( |
| 199 | 206 | // FLash sale. |
| 200 | 207 | array( |
| 201 | 208 | 'key' => 'ultp_banner_flash_sale_2026_1', |
| @@ -562,8 +569,15 @@ | ||
| 562 | 569 | */ |
| 563 | 570 | public function ultp_dashboard_durbin_notice_callback() { |
| 564 | 571 | $durbin_key = 'ultp_durbin_dc1'; |
| 565 | 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 | + | |
| 566 | 580 | if ( |
| 567 | 581 | isset( $_GET['ultp_durbin_key'] ) || // phpcs:ignore |
| 568 | 582 | 'off' === Xpo::get_transient_without_cache( 'ultp_durbin_notice_' . $durbin_key ) |
| 569 | 583 | ) { |
| @@ -573,9 +587,9 @@ | ||
| 573 | 587 | if ( ! $this->notice_js_css_applied ) { |
| 574 | 588 | $this->notice_js_css_applied = true; |
| 575 | 589 | } |
| 576 | 590 | |
| 577 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 591 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 578 | 592 | |
| 579 | 593 | ?> |
| 580 | 594 | <style> |
| 581 | 595 | .ultp-consent-box { |
| @@ -727,13 +741,13 @@ | ||
| 727 | 741 | ), |
| 728 | 742 | |
| 729 | 743 | ); |
| 730 | 744 | |
| 731 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 745 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 732 | 746 | |
| 733 | 747 | foreach ( $content_notices as $key => $notice ) { |
| 734 | 748 | $notice_key = isset( $notice['key'] ) ? $notice['key'] : $this->notice_version; |
| 735 | - 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. | |
| 736 | 750 | continue; |
| 737 | 751 | } else { |
| 738 | 752 | $border_color = $notice['border_color']; |
| 739 | 753 | |
| @@ -926,9 +940,34 @@ | ||
| 926 | 940 | * Handle Plugin Notice for all plugins |
| 927 | 941 | * |
| 928 | 942 | * @return bool |
| 929 | 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 | + | |
| 930 | 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. | |
| 931 | 970 | $active_notices = apply_filters( 'xpo_active_notice_lists', array() ); |
| 932 | 971 | if ( empty( $active_notices ) ) { |
| 933 | 972 | return true; |
| 934 | 973 | } |
| @@ -936,9 +975,9 @@ | ||
| 936 | 975 | return array_key_first( $active_notices ) === $this->plugin_notice_priority_key; |
| 937 | 976 | } |
| 938 | 977 | |
| 939 | 978 | public function ultp_dashboard_image_banner_notice( $return_bool = false ) { |
| 940 | - $ultp_db_nonce = wp_create_nonce( 'ultp-nonce' ); | |
| 979 | + $ultp_db_nonce = wp_create_nonce( 'ultp-admin-nonce' ); | |
| 941 | 980 | $banner_notices = array( |
| 942 | 981 | array( |
| 943 | 982 | 'key' => 'ultp_summer_sale_2612', |
| 944 | 983 | 'start' => '2026-07-20 00:00 Asia/Dhaka', // format YY-MM-DD always set time 00:00 and zone Asia/Dhaka |