PluginProbe
Bolt CTA Button – Floating Call Button for WordPress / 2.0.9
Bolt CTA Button – Floating Call Button for WordPress v2.0.9
2.0.9 2.0.8 2.0.7 2.0.6 trunk 2.0.4 2.0.5
bolt-cta-button / bolt-cta-button.php

bolt-cta-button.php in Bolt CTA Button – Floating Call Button for WordPress 2.0.9, at bolt-cta-button.php

308 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Bolt CTA Button – Floating Call Button for WordPress
4 * Plugin URI: https://wordpress.org/plugins/bolt-cta-button/
5 * Description: A call now button & floating action button for WhatsApp, Phone, and more. Two templates, click analytics, WooCommerce support, and full customization.
6 * Version: 2.0.9
7 * Author: ismeteroglu
8 * Author URI: https://ismeteroglu.com
9 * License: GPL-2.0-or-later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: bolt-cta-button
12 * Domain Path: /languages
13 * Requires at least: 5.4
14 * Requires PHP: 7.4
15 * Tested up to: 7.0
16 */
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 define( 'CNCB_VERSION', '2.0.9' );
23 define( 'CNCB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
24 define( 'CNCB_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
25 define( 'CNCB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
26
27 /* ───────────────────────── Language ───────────────────────── */
28
29 /**
30 * Supported admin languages.
31 */
32 function cncb_get_supported_languages() {
33 return array(
34 'en_US' => array( 'label' => 'English', 'flag' => '&#x1F1EC;&#x1F1E7;' ),
35 'tr_TR' => array( 'label' => 'Türkçe', 'flag' => '&#x1F1F9;&#x1F1F7;' ),
36 'de_DE' => array( 'label' => 'Deutsch', 'flag' => '&#x1F1E9;&#x1F1EA;' ),
37 'es_ES' => array( 'label' => 'Español', 'flag' => '&#x1F1EA;&#x1F1F8;' ),
38 'fr_FR' => array( 'label' => 'Français', 'flag' => '&#x1F1EB;&#x1F1F7;' ),
39 'ar' => array( 'label' => 'العربية', 'flag' => '&#x1F1F8;&#x1F1E6;' ),
40 'pt_BR' => array( 'label' => 'Português', 'flag' => '&#x1F1E7;&#x1F1F7;' ),
41 'ru_RU' => array( 'label' => 'Русский', 'flag' => '&#x1F1F7;&#x1F1FA;' ),
42 'it_IT' => array( 'label' => 'Italiano', 'flag' => '&#x1F1EE;&#x1F1F9;' ),
43 'nl_NL' => array( 'label' => 'Nederlands', 'flag' => '&#x1F1F3;&#x1F1F1;' ),
44 'uk' => array( 'label' => 'Українська', 'flag' => '&#x1F1FA;&#x1F1E6;' ),
45 );
46 }
47
48 function cncb_get_admin_locale() {
49 $override = get_option( 'cncb_admin_lang', '' );
50 $supported = array_keys( cncb_get_supported_languages() );
51 if ( in_array( $override, $supported, true ) ) {
52 return $override;
53 }
54 return get_user_locale();
55 }
56
57 function cncb_load_textdomain() {
58 $locale = apply_filters( 'cncb_plugin_locale', cncb_get_admin_locale(), 'bolt-cta-button' );
59 $mo_file = CNCB_PLUGIN_DIR . 'languages/bolt-cta-button-' . $locale . '.mo';
60 if ( file_exists( $mo_file ) ) {
61 load_textdomain( 'bolt-cta-button', $mo_file );
62 }
63 }
64 add_action( 'plugins_loaded', 'cncb_load_textdomain' );
65
66 function cncb_ajax_set_lang() {
67 check_ajax_referer( 'cncb_save_nonce', 'nonce' );
68 if ( ! current_user_can( 'manage_options' ) ) {
69 wp_send_json_error();
70 }
71 $lang = isset( $_POST['lang'] ) && is_string( $_POST['lang'] ) ? sanitize_text_field( wp_unslash( $_POST['lang'] ) ) : '';
72 if ( ! in_array( $lang, array_keys( cncb_get_supported_languages() ), true ) ) {
73 wp_send_json_error();
74 }
75 update_option( 'cncb_admin_lang', $lang );
76 wp_send_json_success();
77 }
78 add_action( 'wp_ajax_cncb_set_lang', 'cncb_ajax_set_lang' );
79
80 /* ───────────────────────── Defaults ───────────────────────── */
81
82 function cncb_get_defaults() {
83 return array(
84 // General.
85 'enabled' => 1,
86 'show_mobile' => 1,
87 'show_desktop' => 1,
88
89 // Template.
90 'template' => 'bar',
91 'template_mobile' => 'bar',
92 'template_desktop' => 'fab',
93
94 // Bar settings.
95 'scroll_behavior' => 'always',
96 'bar_bg_color' => '#ffffff',
97 'bar_bg_opacity' => 0,
98 'border_radius' => 12,
99 'animation' => 'none',
100 'bar_padding' => 8,
101 'bar_position' => 'bottom',
102 'bar_shadow' => 'none',
103 'bar_button_shadow' => 'none',
104 'bar_margin_top' => 0,
105 'bar_margin_right' => 0,
106 'bar_margin_bottom' => 0,
107 'bar_margin_left' => 0,
108
109 // FAB settings.
110 'fab_position' => 'right-bottom',
111 'fab_icon' => 'phone',
112 'fab_bg_color' => '#075e54',
113 'fab_text_color' => '#ffffff',
114 'fab_size' => 56,
115 'fab_open_direction' => 'up',
116 'fab_open_animation' => 'fan',
117 'fab_animation' => 'none',
118 'fab_shadow' => 'none',
119 'fab_badge' => 0,
120 'fab_tooltip' => 1,
121 'fab_margin_top' => 0,
122 'fab_margin_right' => 20,
123 'fab_margin_bottom' => 70,
124 'fab_margin_left' => 0,
125
126 // Timing & triggers.
127 'trigger_delay' => 0,
128 'trigger_scroll' => 0,
129
130 // Visibility.
131 'visibility_mode' => 'all',
132 'visibility_pages' => '',
133
134 // WooCommerce.
135 'woo_enabled' => 0,
136 'woo_shop_buttons' => '',
137 'woo_product_buttons' => '',
138 'woo_cart_buttons' => '',
139 'woo_checkout_buttons' => '',
140
141 // Buttons.
142 'buttons' => array(
143 array(
144 'enabled' => 1,
145 'label' => 'WhatsApp',
146 'url' => 'https://wa.me/905551234567',
147 'icon' => 'whatsapp',
148 'bg_color' => '#075e54',
149 'text_color' => '#ffffff',
150 'target' => '_blank',
151 'order' => 0,
152 ),
153 array(
154 'enabled' => 1,
155 'label' => 'Call Now',
156 'url' => 'tel:+905551234567',
157 'icon' => 'phone',
158 'bg_color' => '#3958ea',
159 'text_color' => '#ffffff',
160 'target' => '_self',
161 'order' => 1,
162 ),
163 ),
164 );
165 }
166
167 function cncb_get_options() {
168 $defaults = cncb_get_defaults();
169 $options = get_option( 'cncb_options', array() );
170 if ( empty( $options ) ) {
171 return $defaults;
172 }
173 $merged = wp_parse_args( $options, $defaults );
174 if ( ! isset( $options['buttons'] ) ) {
175 $merged['buttons'] = $defaults['buttons'];
176 }
177 return $merged;
178 }
179
180 /* ───────────────────────── Migration ───────────────────────── */
181
182 function cncb_maybe_migrate() {
183 $db_version = get_option( 'cncb_db_version', '1.0.0' );
184 if ( version_compare( $db_version, '2.0.0', '>=' ) ) {
185 return;
186 }
187 $options = get_option( 'cncb_options', array() );
188 if ( ! empty( $options ) ) {
189 $options = wp_parse_args( $options, cncb_get_defaults() );
190 update_option( 'cncb_options', $options );
191 }
192 if ( false === get_option( 'cncb_click_stats' ) ) {
193 add_option( 'cncb_click_stats', array( 'buttons' => array(), 'last_prune' => '' ), '', 'no' );
194 }
195 update_option( 'cncb_db_version', '2.0.0' );
196 }
197 add_action( 'admin_init', 'cncb_maybe_migrate' );
198
199 /* ───────────────────────── Load Classes ───────────────────────── */
200
201 if ( is_admin() ) {
202 require_once CNCB_PLUGIN_DIR . 'admin/class-cncb-admin.php';
203 require_once CNCB_PLUGIN_DIR . 'admin/class-cncb-stats.php';
204 new CNCB_Admin();
205 }
206
207 require_once CNCB_PLUGIN_DIR . 'public/class-cncb-frontend.php';
208 new CNCB_Frontend();
209
210 /* ───────────────────────── Click Tracking AJAX ───────────────────────── */
211
212 function cncb_ajax_track_click() {
213 check_ajax_referer( 'cncb_click_nonce', 'nonce' );
214
215 // Rate limit: max 1 click per IP per 3 seconds.
216 $ip_hash = md5( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown' );
217 $transient = 'cncb_clk_' . $ip_hash;
218 if ( false !== get_transient( $transient ) ) {
219 wp_send_json_success(); // Silently accept but don't record.
220 return;
221 }
222 set_transient( $transient, 1, 3 );
223
224 $btn_index = isset( $_POST['button_index'] ) ? absint( $_POST['button_index'] ) : 0;
225
226 // Validate button index against actual configured buttons.
227 $options = cncb_get_options();
228 $btn_count = ! empty( $options['buttons'] ) ? count( $options['buttons'] ) : 0;
229 if ( $btn_index >= $btn_count ) {
230 wp_send_json_error();
231 }
232
233 $stats = get_option( 'cncb_click_stats', array( 'buttons' => array(), 'last_prune' => '' ) );
234 $key = 'btn_' . $btn_index;
235 $date = current_time( 'Y-m-d' );
236
237 if ( ! isset( $stats['buttons'][ $key ] ) ) {
238 $stats['buttons'][ $key ] = array( 'total' => 0, 'daily' => array() );
239 }
240
241 $stats['buttons'][ $key ]['total']++;
242
243 if ( ! isset( $stats['buttons'][ $key ]['daily'][ $date ] ) ) {
244 $stats['buttons'][ $key ]['daily'][ $date ] = 0;
245 }
246 $stats['buttons'][ $key ]['daily'][ $date ]++;
247
248 // Prune data older than 90 days (max once per day).
249 if ( $stats['last_prune'] !== $date ) {
250 $cutoff = gmdate( 'Y-m-d', strtotime( '-90 days' ) );
251 foreach ( $stats['buttons'] as &$btn_data ) {
252 if ( ! empty( $btn_data['daily'] ) ) {
253 $btn_data['daily'] = array_filter( $btn_data['daily'], function( $v, $d ) use ( $cutoff ) {
254 return $d >= $cutoff;
255 }, ARRAY_FILTER_USE_BOTH );
256 }
257 }
258 unset( $btn_data );
259 $stats['last_prune'] = $date;
260 }
261
262 update_option( 'cncb_click_stats', $stats, false );
263 wp_send_json_success();
264 }
265 add_action( 'wp_ajax_cncb_track_click', 'cncb_ajax_track_click' );
266 add_action( 'wp_ajax_nopriv_cncb_track_click', 'cncb_ajax_track_click' );
267
268 /* ───────────────────────── Reset Options to Defaults ───────────────────────── */
269
270 function cncb_ajax_reset_options() {
271 check_ajax_referer( 'cncb_reset_options_nonce', 'nonce' );
272
273 if ( ! current_user_can( 'manage_options' ) ) {
274 wp_send_json_error( __( 'Unauthorized', 'bolt-cta-button' ) );
275 }
276
277 update_option( 'cncb_options', cncb_get_defaults() );
278 wp_send_json_success( __( 'Settings reset to defaults.', 'bolt-cta-button' ) );
279 }
280 add_action( 'wp_ajax_cncb_reset_options', 'cncb_ajax_reset_options' );
281
282 /* ───────────────────────── Plugin Links ───────────────────────── */
283
284 function cncb_settings_link( $links ) {
285 $settings_link = '<a href="' . esc_url( admin_url( 'options-general.php?page=bolt-cta-button' ) ) . '">' . esc_html__( 'Settings', 'bolt-cta-button' ) . '</a>';
286 array_unshift( $links, $settings_link );
287 return $links;
288 }
289 add_filter( 'plugin_action_links_' . CNCB_PLUGIN_BASENAME, 'cncb_settings_link' );
290
291 /* ───────────────────────── Activation ───────────────────────── */
292
293 function cncb_activate() {
294 if ( ! get_option( 'cncb_options' ) ) {
295 update_option( 'cncb_options', cncb_get_defaults() );
296 }
297 if ( false === get_option( 'cncb_click_stats' ) ) {
298 add_option( 'cncb_click_stats', array( 'buttons' => array(), 'last_prune' => '' ), '', 'no' );
299 }
300 update_option( 'cncb_db_version', '2.0.0' );
301 }
302 register_activation_hook( __FILE__, 'cncb_activate' );
303
304 function cncb_deactivate() {
305 // Options preserved for reactivation.
306 }
307 register_deactivation_hook( __FILE__, 'cncb_deactivate' );
308