PluginProbe
WANotifier for Forms and Actions / 3.2.0
WANotifier for Forms and Actions v3.2.0
3.2.0 3.1.1 3.1.0 3.0.4 2.7.10 2.7.11 2.7.12 2.7.13 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 3.0.0 3.0.1 3.0.2 3.0.3 trunk 0.1.0 0.1.1 1.0.0 1.0.1 All 68 releases
← All changes | includes/classes/class-notifier-frontend.php +224 -8 2.7.3 → 3.2.0 View file →
@@ -1,5 +1,9 @@
1 1 <?php
2 +if ( ! defined( 'ABSPATH' ) ) {
3 + exit;
4 +}
5 +
2 6 /**
3 7 *
4 8 * @package Wa_Notifier
5 9 */
@@ -7,26 +11,238 @@
7 11 /**
8 12 * Init
9 13 */
10 14 public static function init() {
11 - add_action( 'wp_footer', array( __CLASS__ , 'display_whatsapp_chat_button' ) );
15 + add_action( 'wp_footer', array( __CLASS__, 'display_whatsapp_chat_button' ) );
16 + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'add_chat_button_inline_styles' ), 20 );
12 17 }
13 18
14 19 /**
20 + * Add position and device visibility CSS for the chat button.
21 + *
22 + * Device visibility is handled with media queries (not wp_is_mobile()) so it
23 + * works with page caching and can distinguish tablets.
24 + */
25 + public static function add_chat_button_inline_styles() {
26 + $handle = NOTIFIER_NAME . '-frontend-css';
27 + if ( ! wp_style_is( $handle, 'enqueued' ) ) {
28 + return;
29 + }
30 +
31 + $css = '';
32 + $position = get_option( 'notifier_ctc_position', 'bottom-right' );
33 + $offset_x = absint( get_option( 'notifier_ctc_offset_x', 20 ) );
34 + $offset_y = absint( get_option( 'notifier_ctc_offset_y', 20 ) );
35 +
36 + // The default matches frontend.css; skip it so existing theme CSS overrides of the button position keep working.
37 + if ( 'bottom-right' !== $position || 20 !== $offset_x || 20 !== $offset_y ) {
38 + $css .= self::get_position_css( $position, $offset_x, $offset_y );
39 + }
40 +
41 + $mobile_query = '(max-width: 767px)';
42 +
43 + if ( 'yes' === get_option( 'notifier_ctc_mobile_position_enable', 'no' ) ) {
44 + $css .= '@media ' . $mobile_query . '{' . self::get_position_css(
45 + get_option( 'notifier_ctc_mobile_position', 'bottom-right' ),
46 + get_option( 'notifier_ctc_mobile_offset_x', 20 ),
47 + get_option( 'notifier_ctc_mobile_offset_y', 20 )
48 + ) . '}';
49 + }
50 +
51 + $devices = (array) get_option( 'notifier_ctc_devices', array( 'desktop', 'tablet', 'mobile' ) );
52 + $media_queries = array(
53 + 'mobile' => $mobile_query,
54 + 'tablet' => '(min-width: 768px) and (max-width: 1024px)',
55 + 'desktop' => '(min-width: 1025px)',
56 + );
57 + foreach ( $media_queries as $device => $query ) {
58 + if ( ! in_array( $device, $devices, true ) ) {
59 + $css .= '@media ' . $query . '{.notifier-click-to-chat-btn{display:none !important;}}';
60 + }
61 + }
62 +
63 + if ( '' !== $css ) {
64 + wp_add_inline_style( $handle, $css );
65 + }
66 + }
67 +
68 + /**
69 + * Build the fixed-position CSS rule for a corner + offsets.
70 + *
71 + * @param string $position Corner key, e.g. 'bottom-right'.
72 + * @param int|string $offset_x Horizontal offset in px.
73 + * @param int|string $offset_y Vertical offset in px.
74 + * @return string
75 + */
76 + private static function get_position_css( $position, $offset_x, $offset_y ) {
77 + $vertical = 0 === strpos( (string) $position, 'top' ) ? 'top' : 'bottom';
78 + $horizontal = false !== strpos( (string) $position, 'left' ) ? 'left' : 'right';
79 +
80 + return sprintf(
81 + '.notifier-click-to-chat-btn{top:auto;bottom:auto;left:auto;right:auto;%1$s:%2$dpx;%3$s:%4$dpx;}',
82 + $vertical,
83 + absint( $offset_y ),
84 + $horizontal,
85 + absint( $offset_x )
86 + );
87 + }
88 +
89 + /**
90 + * Whether the chat button should be displayed on the current request.
91 + *
92 + * A page qualifies when it matches any "display" rule and no "exclude" rule.
93 + *
94 + * @return bool
95 + */
96 + public static function should_display_on_current_page() {
97 + $rules = Notifier_Backend::get_ctc_display_rules();
98 +
99 + if ( self::matches_any_rule( $rules['exclude'] ) ) {
100 + return false;
101 + }
102 +
103 + return self::matches_any_rule( $rules['display'] );
104 + }
105 +
106 + /**
107 + * @param array $rows Rule rows: array( 'rule' => key, 'specifics' => array ).
108 + * @return bool
109 + */
110 + private static function matches_any_rule( $rows ) {
111 + foreach ( (array) $rows as $row ) {
112 + if ( ! empty( $row['rule'] ) && self::rule_matches( $row['rule'], (array) ( $row['specifics'] ?? array() ) ) ) {
113 + return true;
114 + }
115 + }
116 + return false;
117 + }
118 +
119 + /**
120 + * Check one rule key against the current request. Keys are documented in
121 + * Notifier_Backend::get_ctc_location_options().
122 + *
123 + * @param string $rule
124 + * @param array $specifics
125 + * @return bool
126 + */
127 + private static function rule_matches( $rule, $specifics = array() ) {
128 + switch ( $rule ) {
129 + case 'basic-global':
130 + return true;
131 + case 'basic-singulars':
132 + return is_singular();
133 + case 'basic-archives':
134 + return is_archive();
135 + case 'special-front':
136 + return is_front_page();
137 + case 'special-blog':
138 + return is_home();
139 + case 'special-404':
140 + return is_404();
141 + case 'special-search':
142 + return is_search();
143 + case 'special-date':
144 + return is_date();
145 + case 'special-author':
146 + return is_author();
147 + case 'wc-shop':
148 + return function_exists( 'is_shop' ) && is_shop();
149 + case 'wc-cart':
150 + return function_exists( 'is_cart' ) && is_cart();
151 + case 'wc-checkout':
152 + return function_exists( 'is_checkout' ) && is_checkout();
153 + case 'wc-account':
154 + return function_exists( 'is_account_page' ) && is_account_page();
155 + case 'wc-order-received':
156 + return function_exists( 'is_order_received_page' ) && is_order_received_page();
157 + case 'specifics':
158 + foreach ( $specifics as $item ) {
159 + if ( self::specific_matches( $item ) ) {
160 + return true;
161 + }
162 + }
163 + return false;
164 + }
165 +
166 + // {post_type}|all, {post_type}|archive, {post_type}|tax|{taxonomy}
167 + $parts = explode( '|', $rule );
168 + if ( count( $parts ) < 2 || ! post_type_exists( $parts[0] ) ) {
169 + return false;
170 + }
171 + $post_type = $parts[0];
172 +
173 + if ( 'all' === $parts[1] ) {
174 + return is_singular( $post_type );
175 + }
176 + if ( 'archive' === $parts[1] ) {
177 + return is_post_type_archive( $post_type );
178 + }
179 + if ( 'tax' === $parts[1] && ! empty( $parts[2] ) ) {
180 + $taxonomy = $parts[2];
181 + if ( 'category' === $taxonomy ) {
182 + return is_category();
183 + }
184 + if ( 'post_tag' === $taxonomy ) {
185 + return is_tag();
186 + }
187 + return is_tax( $taxonomy );
188 + }
189 +
190 + return false;
191 + }
192 +
193 + /**
194 + * Match a specific item: post-{id} (that single post/page/product), tax-{id} (that
195 + * term's archive) or tax-{id}-posts (any single item that has that term).
196 + *
197 + * @param string $item
198 + * @return bool
199 + */
200 + private static function specific_matches( $item ) {
201 + if ( ! preg_match( '/^(post|tax)-(\d+)(-posts)?$/', (string) $item, $m ) ) {
202 + return false;
203 + }
204 + $id = (int) $m[2];
205 +
206 + if ( 'post' === $m[1] ) {
207 + if ( is_singular() && (int) get_queried_object_id() === $id ) {
208 + return true;
209 + }
210 + // Pages WordPress / WooCommerce render as archives rather than singulars.
211 + if ( is_home() && ! is_front_page() && (int) get_option( 'page_for_posts' ) === $id ) {
212 + return true;
213 + }
214 + if ( function_exists( 'is_shop' ) && function_exists( 'wc_get_page_id' ) && is_shop() && (int) wc_get_page_id( 'shop' ) === $id ) {
215 + return true;
216 + }
217 + return false;
218 + }
219 +
220 + if ( ! empty( $m[3] ) ) {
221 + $term = get_term( $id );
222 + return $term && ! is_wp_error( $term ) && is_singular() && has_term( $id, $term->taxonomy, get_queried_object_id() );
223 + }
224 +
225 + $queried = get_queried_object();
226 + return ( is_category() || is_tag() || is_tax() ) && $queried instanceof WP_Term && (int) $queried->term_id === $id;
227 + }
228 +
229 + /**
15 230 * Display Whatsapp chat button.
16 231 */
17 232 public static function display_whatsapp_chat_button() {
18 - $enable_click_chat = get_option('notifier_ctc_enable');
19 - $btn_style = get_option('notifier_ctc_button_style');
20 - $click_chat_number = get_option('notifier_ctc_whatsapp_number');
233 + $enable_click_chat = get_option( 'notifier_ctc_enable' );
234 + $btn_style = get_option( 'notifier_ctc_button_style' );
235 + $click_chat_number = get_option( 'notifier_ctc_whatsapp_number' );
21 236
22 - if(!$btn_style){
237 + if ( ! $btn_style ) {
23 238 return;
24 239 }
25 240
26 - if($enable_click_chat === 'yes' && !empty($click_chat_number)){
27 - if($btn_style !== 'default'){
28 - include_once NOTIFIER_PATH . 'templates/buttons/'.$btn_style.'.php';
241 + if ( 'yes' === $enable_click_chat && ! empty( $click_chat_number ) && self::should_display_on_current_page() ) {
242 + $valid_styles = array_keys( Notifier_Backend::get_button_styles() );
243 + if ( 'default' !== $btn_style && in_array( $btn_style, $valid_styles, true ) ) {
244 + include_once NOTIFIER_PATH . 'templates/buttons/' . $btn_style . '.php';
29 245 }
30 246 }
31 247 }
32 248 }