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
notifier / includes / classes / class-notifier-frontend.php

class-notifier-frontend.php in WANotifier for Forms and Actions 3.2.0, at includes/classes/class-notifier-frontend.php

249 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit;
4 }
5
6 /**
7 *
8 * @package Wa_Notifier
9 */
10 class Notifier_Frontend {
11 /**
12 * Init
13 */
14 public static function init() {
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 );
17 }
18
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 /**
230 * Display Whatsapp chat button.
231 */
232 public static function display_whatsapp_chat_button() {
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' );
236
237 if ( ! $btn_style ) {
238 return;
239 }
240
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';
245 }
246 }
247 }
248 }
249