PluginProbe
Spoki – Chat Buttons and WooCommerce Notifications / 2.9.1
Spoki – Chat Buttons and WooCommerce Notifications v2.9.1
2.17.4 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.10.0 2.11.0 2.11.1 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.14.0 All 69 releases
spoki / spoki.php

spoki.php in Spoki – Chat Buttons and WooCommerce Notifications 2.9.1, at spoki.php

1,407 lines 61.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /* @wordpress-plugin
3 * Plugin Name: Spoki - Chat Buttons and WooCommerce Notifications
4 * Description: Integrate WhatsApp on your Business! Use Spoki to recover abandoned carts, add chat buttons and send order status notifications via WhatsApp.
5 * Version: 2.9.1
6 * Author: Reddoak Srl
7 * Author URI: https://reddoak.com
8 * Text Domain: spoki
9 * Domain Path: /languages
10 * License: GPLv2
11 */
12
13 define('SPOKI_PLUGIN_FILE', __FILE__);
14 define('SPOKI_BASE', plugin_basename(SPOKI_PLUGIN_FILE));
15 define('SPOKI_DIR', plugin_dir_path(SPOKI_PLUGIN_FILE));
16 define('SPOKI_URL', plugins_url('/', SPOKI_PLUGIN_FILE));
17 define('SPOKI_SETTING_TABLE', 'spoki_setting');
18 define('SPOKI_ABANDONMENT_TABLE', 'spoki_abandonment');
19
20 include_once SPOKI_DIR . 'includes/constants.php';
21 include_once SPOKI_DIR . 'includes/spoki-functions.php';
22 include_once SPOKI_DIR . 'modules/abandoned-carts/spoki-abandoned-carts.php';
23
24 add_filter('cron_schedules', 'add_cron_interval');
25 function add_cron_interval($schedules)
26 {
27 $schedules['every_day'] = array(
28 'interval' => 86400,
29 'display' => esc_html__('Every day'),);
30 return $schedules;
31 }
32
33 add_action('plugins_loaded', function () {
34 /** Init Spoki */
35 Spoki();
36 }, 9);
37
38
39 function Spoki()
40 {
41 return WpSpoki::instance();
42 }
43
44 final class WpSpoki
45 {
46 protected static $_instance = null;
47 public $version = '';
48 private $langs = '';
49 public $shop = [];
50
51 private $api_plan = SPOKI_BASE_API . "plan/";
52 private $api_status = SPOKI_BASE_API . "status/";
53 private $api_enable_flex = SPOKI_BASE_API . "enable/";
54 private $api_account = SPOKI_BASE_API . "account/";
55
56 public static function instance()
57 {
58 if (is_null(self::$_instance)) {
59 self::$_instance = new self();
60 }
61 return self::$_instance;
62 }
63
64 private function __construct()
65 {
66 $data = get_file_data(__FILE__, array('ver' => 'Version', 'langs' => 'Domain Path'));
67 $this->version = $data['ver'];
68 $this->langs = $data['langs'];
69 $this->options = get_option(SPOKI_OPTIONS);
70
71 $billing_data = isset($this->options['billing_data']) ? $this->options['billing_data'] : [];
72 $has_fixed_support_button = isset($this->options['buttons']['fixed_support_button_check']) && $this->options['buttons']['fixed_support_button_check'] == 1;
73 $has_product_item_listing_button = isset($this->options['woocommerce']['product_item_listing_button_check']) && $this->options['woocommerce']['product_item_listing_button_check'] == 1;
74 $has_cart_button = isset($this->options['woocommerce']['cart_button_check']) && $this->options['woocommerce']['cart_button_check'] == 1;
75 $has_single_product_button = isset($this->options['woocommerce']['single_product_button_check']) && $this->options['woocommerce']['single_product_button_check'] == 1;
76 $has_abandoned_carts = isset($this->options['abandoned_carts']['enable_tracking']) && $this->options['abandoned_carts']['enable_tracking'] == 1;
77 $has_order_updated_notification = (isset($this->options['woocommerce']['order_updated']) && $this->options['woocommerce']['order_updated'] == 1);
78 $has_order_created_notification = (isset($this->options['woocommerce']['order_created']) && $this->options['woocommerce']['order_created'] == 1);
79 $has_order_deleted_notification = (isset($this->options['woocommerce']['order_deleted']) && $this->options['woocommerce']['order_deleted'] == 1);
80 $has_order_note_added_notification = (isset($this->options['woocommerce']['order_note_added']) && $this->options['woocommerce']['order_note_added'] == 1);
81 $has_leave_review_notification = (isset($this->options['woocommerce']['leave_review']) && $this->options['woocommerce']['leave_review'] == 1);
82 $has_notifications = (
83 $has_order_updated_notification ||
84 $has_order_created_notification ||
85 $has_order_deleted_notification ||
86 $has_order_note_added_notification ||
87 $has_leave_review_notification
88 );
89 $has_order_created_to_seller_notification = (isset($this->options['woocommerce']['order_created_to_seller']) && $this->options['woocommerce']['order_created_to_seller'] == 1);
90 $has_cart_recovered_to_seller_notification = (isset($this->options['abandoned_carts']['notify_to_admin']) && $this->options['abandoned_carts']['notify_to_admin'] == 1);
91 $is_auto_update_disabled = (isset($this->options['disable_auto_update']) && $this->options['disable_auto_update'] == 1);
92 $abandoned_carts_waiting_minutes = (isset($this->options['abandoned_carts']['waiting_minutes'])) ? intval($this->options['abandoned_carts']['waiting_minutes']) : 15;
93
94 $this->shop = [
95 "plugin_version" => $this->version,
96 "name" => $this->options['shop_name'] ?? get_bloginfo('name'),
97 "url" => get_bloginfo('url'),
98 "email" => $this->options['email'] ?? get_bloginfo('admin_email'),
99 "language" => $this->options['language'] ?? get_bloginfo('language'),
100 "telephone" => ($this->options['prefix'] ?? '') . ($this->options['telephone'] ?? ''),
101 "contact_link" => isset($this->options['account_info']['contact_link']) ? $this->options['account_info']['contact_link'] : null,
102 "billing_data" => $billing_data,
103 "has_fixed_support_button" => $has_fixed_support_button,
104 "has_product_item_listing_button" => $has_product_item_listing_button,
105 "has_cart_button" => $has_cart_button,
106 "has_single_product_button" => $has_single_product_button,
107 "has_abandoned_carts" => $has_abandoned_carts,
108 "has_order_updated_notification" => $has_order_updated_notification,
109 "has_order_created_notification" => $has_order_created_notification,
110 "has_order_deleted_notification" => $has_order_deleted_notification,
111 "has_order_note_added_notification" => $has_order_note_added_notification,
112 "has_leave_review_notification" => $has_leave_review_notification,
113 "has_notifications" => $has_notifications,
114 "has_order_created_to_seller_notification" => $has_order_created_to_seller_notification,
115 "has_cart_recovered_to_seller_notification" => $has_cart_recovered_to_seller_notification,
116 "abandoned_carts_waiting_minutes" => $abandoned_carts_waiting_minutes,
117 "is_auto_update_disabled" => $is_auto_update_disabled,
118 ];
119
120 register_activation_hook(SPOKI_PLUGIN_FILE, array($this, 'activation_reset'));
121 register_deactivation_hook(SPOKI_PLUGIN_FILE, array($this, 'deactivation_reset'));
122
123 add_action('init', 'spoki_update_po_file');
124 add_action('admin_menu', array($this, 'add_option_menu'));
125 add_action('admin_menu', array($this, 'add_submenu'), 99);
126 add_filter("plugin_action_links_" . plugin_basename(__FILE__), array($this, 'plugin_settings_link'));
127 add_action('wp_enqueue_scripts', array($this, 'add_styles'));
128 add_action('admin_enqueue_scripts', array($this, 'add_admin_styles'));
129 add_action('admin_enqueue_scripts', array($this, 'add_admin_scripts'));
130 add_action('updated_option', array($this, 'on_option_added'), 10, 3);
131 add_filter('auto_update_plugin', array($this, 'auto_update_plugin'), 10, 2);
132 add_action('spoki_cron_hook', array($this, 'check_secret_status'));
133 if (!wp_next_scheduled('spoki_cron_hook')) {
134 $this->check_secret_status();
135 wp_schedule_event(time(), 'every_day', 'spoki_cron_hook');
136 }
137
138 $this->includes();
139 $this->render_buttons();
140 $this->handle_woocommerce();
141 $this->handle_abandoned_carts();
142
143 add_action('elementor/widgets/widgets_registered', array($this, 'register_elementor_widgets'));
144
145 }
146
147 /**
148 * Include all Spoki required files
149 */
150 private function includes()
151 {
152 require_once SPOKI_DIR . 'includes/spoki-functions.php';
153 }
154
155 /**
156 * Get the setting link of the plugin
157 *
158 * @param $links
159 * @return mixed
160 */
161 public function plugin_settings_link($links)
162 {
163 $settings_link = '<a href="admin.php?page=' . SPOKI_PLUGIN_NAME . '">' . __('Settings', SPOKI_PLUGIN_NAME) . '</a>';
164 array_unshift($links, $settings_link);
165 return $links;
166 }
167
168 /**
169 * Add the Spoki option to the menu
170 */
171 public function add_option_menu()
172 {
173 add_menu_page(
174 'Spoki Options',
175 "Spoki",
176 'manage_options',
177 'spoki',
178 array($this, 'render_setup_page'),
179 plugins_url() . '/' . SPOKI_PLUGIN_NAME . '/assets/images/logo.svg',
180 98
181 );
182 add_action('admin_init', array($this, 'register_option_var'));
183 }
184
185 /**
186 * Add the Spoki option submenu
187 */
188 function add_submenu()
189 {
190 $has_woocommerce = ('woocommerce/woocommerce.php');
191 $is_pro = isset($this->options['account_info']['plan']['is_pro']) && $this->options['account_info']['plan']['is_pro'] == true;
192
193 add_submenu_page('spoki', __('Statistics', "spoki"), __('Statistics', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=welcome");
194 add_submenu_page('spoki', __('Buttons', "spoki"), __('Buttons', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=buttons");
195 add_submenu_page('spoki', __('Customer Notifications', "spoki"), __('Customer Notifications', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=customer-notifications");
196 add_submenu_page('spoki', __('Seller Notifications', "spoki"), __('Seller Notifications', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=seller-notifications");
197 add_submenu_page('spoki', __('Abandoned Carts', "spoki"), __('Abandoned Carts', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=abandoned-carts");
198 add_submenu_page('spoki', __('Settings', "spoki"), __('Settings', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=settings");
199 add_submenu_page('spoki', __('Invite a friend', "spoki"), __('Invite a friend', "spoki"), 'manage_options', "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=invite-a-friend");
200 if (!$is_pro) {
201 add_submenu_page('spoki', __('Upgrade', "spoki"), "<span class='color-spoki text-bold'>⇡ " . __('Upgrade', "spoki") . "</span>", 'manage_options', $this->get_plan_link(true));
202 }
203
204 if ($has_woocommerce) {
205 add_submenu_page(
206 'woocommerce',
207 __('Spoki', "spoki"),
208 __('Spoki', "spoki"),
209 'manage_options',
210 "admin.php?page=" . SPOKI_PLUGIN_NAME . "&tab=welcome",
211 null,
212 8
213 );
214 }
215
216 }
217
218 /**
219 * Register the Spoki option
220 */
221 public function register_option_var()
222 {
223 register_setting('wp-spoki-option', SPOKI_OPTIONS);
224 }
225
226 /**
227 * Add website spoki styles
228 */
229 public function add_styles()
230 {
231 $styles = ['buttons'];
232 foreach ($styles as $style) {
233 echo "<style id='spoki-style-$style'>";
234 include SPOKI_DIR . "assets/css/$style.css";
235 echo "</style>";
236 }
237 }
238
239 /**
240 * Add admin spoki styles
241 */
242 public function add_admin_styles()
243 {
244 $styles = ['admin.css', 'onboarding.css', 'account-overview.css', 'spoki-overview.css'];
245
246 foreach ($styles as $style) {
247 echo "<style>";
248 include_once SPOKI_DIR . 'assets/css/' . $style;
249 echo "</style>";
250 }
251
252 $this->add_styles();
253 }
254
255 /**
256 * Add admin spoki scripts
257 */
258 public function add_admin_scripts()
259 {
260 $scripts = ['spoki-admin.js', 'spoki-shadowed-buttons.js'];
261
262 foreach ($scripts as $script) {
263 echo "<script>";
264 include_once SPOKI_DIR . 'assets/js/' . $script;
265 echo "</script>";
266 }
267 }
268
269 /**
270 * Enable plugin autoupdate
271 */
272 function auto_update_plugin($update, $item)
273 {
274 $plugins = array('spoki');
275 $is_auto_update_disabled = (isset($this->options['disable_auto_update']) && $this->options['disable_auto_update'] == 1);
276 if (in_array($item->slug, $plugins) && !$is_auto_update_disabled)
277 return true;
278 else return $update;
279 }
280
281 /**
282 * Register widgets for Elementor
283 */
284 public function register_elementor_widgets()
285 {
286 if (spoki_has_elementor()) {
287 include_once SPOKI_DIR . 'widgets/class-elementor-spoki-button.php';
288 \Elementor\Plugin::instance()->widgets_manager->register_widget_type(new \Elementor_Spoki_Button_Widget());
289 }
290 }
291
292 /**
293 * Handle option submit
294 *
295 * @param $option
296 * @param $old_value
297 * @param $value
298 */
299 public function on_option_added($option, $old_value, $value)
300 {
301 if ($option == SPOKI_OPTIONS) {
302
303 /** Onboarding Without WooCommerce */
304 if (isset($value['onboarding']['without_wc'])) {
305 $this->update_options($value, [
306 "telephone" => $value['onboarding']['telephone'],
307 "prefix" => $value['onboarding']['prefix'],
308 "onboarding" => null,
309 ]);
310 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=buttons');
311 header("Location: {$url}");
312 exit;
313 }
314
315 /** Onboarding With WooCommerce */
316 if (isset($value['onboarding']['with_wc'])) {
317 $response = $this->create_spoki_account($value['onboarding']['prefix'] . $value['onboarding']['telephone'], $value['onboarding']['email'], $value['onboarding']['shop_name']);
318 if (isset($response['secret']) && isset($response['delivery_url'])) {
319 $this->update_options($value, [
320 "telephone" => $value['onboarding']['telephone'],
321 "prefix" => $value['onboarding']['prefix'],
322 "email" => $value['onboarding']['email'],
323 "shop_name" => $value['onboarding']['shop_name'],
324 "secret" => $response['secret'],
325 "delivery_url" => $response['delivery_url'],
326 "onboarding" => null,
327 "woocommerce" => [
328 "order_created" => 1,
329 "order_updated" => 1,
330 "order_deleted" => 1,
331 "order_note_added" => 1,
332 "leave_review" => 1,
333 "leave_review_days" => 5,
334 "order_created_to_seller" => 1,
335 ],
336 "abandoned_carts" => [
337 "enable_tracking" => 1,
338 "notify_to_admin" => 1,
339 ]
340 ]);
341 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=customer-notifications');
342 header("Location: {$url}");
343 exit;
344 }
345 }
346
347 /** Settings updated for registered account */
348 if (isset($value['is_settings'])) {
349 $keys_changed = ($value['secret'] != $old_value['secret']) || ($value['delivery_url'] != $old_value['delivery_url']);
350
351 $shop_name_changed = $value['shop_name'] != $old_value['shop_name'];
352 $email_changed = $value['email'] != $old_value['email'];
353 $telephone_changed = $value['telephone'] != $old_value['telephone'];
354 $prefix_changed = $value['prefix'] != $old_value['prefix'];
355 $contact_link_changed = $value['contact_link'] != $old_value['contact_link'];
356 $billing_data_changed = $value['billing_data'] != $old_value['billing_data'];
357
358 if ($shop_name_changed || $email_changed || $telephone_changed || $prefix_changed || $contact_link_changed || $billing_data_changed) {
359 $this->update_spoki_account($value['prefix'] . $value['telephone'], $value['email'], $value['shop_name'], $value['contact_link'], $value['billing_data']);
360 }
361
362 if ($keys_changed) {
363 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=welcome');
364 header("Location: {$url}");
365 exit;
366 }
367 }
368
369 /** Enable all notifications from dashboard */
370 if (isset($value['enable_notifications']) && $value['enable_notifications'] == 1) {
371 $this->update_options(get_option(SPOKI_OPTIONS), [
372 "enable_notifications" => 0,
373 "woocommerce" => [
374 "order_created" => 1,
375 "order_updated" => 1,
376 "order_deleted" => 1,
377 "order_note_added" => 1,
378 "leave_review" => 1,
379 "leave_review_days" => 5,
380 ]
381 ]);
382 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=customer-notifications');
383 header("Location: {$url}");
384 exit;
385 }
386
387 /** Enable order status notifications from dashboard */
388 if (isset($value['enable_order_status_notifications']) && $value['enable_order_status_notifications'] == 1) {
389 $this->update_options(get_option(SPOKI_OPTIONS), [
390 "enable_order_status_notifications" => 0,
391 "woocommerce" => [
392 "order_created" => 1,
393 "order_updated" => 1,
394 "order_deleted" => 1,
395 ]
396 ]);
397 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=customer-notifications');
398 header("Location: {$url}");
399 exit;
400 }
401
402 /** Enable seller notifications from dashboard */
403 if (isset($value['enable_seller_notifications']) && $value['enable_seller_notifications'] == 1) {
404 $this->update_options(get_option(SPOKI_OPTIONS), [
405 "enable_seller_notifications" => 0,
406 "woocommerce" => [
407 "order_created_to_seller" => 1,
408 ]
409 ]);
410 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=seller-notifications');
411 header("Location: {$url}");
412 exit;
413 }
414
415 /** Enable leave review notification from dashboard */
416 if (isset($value['enable_leave_review_notification']) && $value['enable_leave_review_notification'] == 1) {
417 $this->update_options(get_option(SPOKI_OPTIONS), [
418 "enable_leave_review_notification" => 0,
419 "woocommerce" => [
420 "leave_review" => 1,
421 "leave_review_days" => 5,
422 ]
423 ]);
424 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=customer-notifications');
425 header("Location: {$url}");
426 exit;
427 }
428
429 /** Enable enable order note added notification from dashboard */
430 if (isset($value['enable_order_note_added_notification']) && $value['enable_order_note_added_notification'] == 1) {
431 $this->update_options(get_option(SPOKI_OPTIONS), [
432 "enable_order_note_added_notification" => 0,
433 "woocommerce" => [
434 "order_note_added" => 1,
435 ]
436 ]);
437 $url = admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME . '&tab=customer-notifications');
438 header("Location: {$url}");
439 exit;
440 }
441 }
442 }
443
444 /**
445 * Handle WooCommerce features
446 */
447 public function handle_woocommerce()
448 {
449 if (isset($this->options['secret'])) {
450 include_once(ABSPATH . 'wp-admin/includes/plugin.php');
451 if (spoki_has_woocommerce()) {
452 add_action('woocommerce_order_status_changed', array($this, 'send_woocommerce_order_created_or_updated_alert'), 1, 3);
453 add_action('woocommerce_cancelled_order', array($this, 'send_woocommerce_order_deleted_alert'), 10, 3);
454 add_action('woocommerce_trash_order', array($this, 'send_woocommerce_order_deleted_alert'), 10, 3);
455 add_action('woocommerce_order_note_added', array($this, 'send_woocommerce_note_alert'), 10, 2);
456 add_action('woocommerce_order_status_completed', array($this, 'send_woocommerce_review_alert'), 10, 2);
457
458 if (isset($this->options['woocommerce']['cart_button_hide_checkout_button_check']) && $this->options['woocommerce']['cart_button_hide_checkout_button_check'] == 1) {
459 add_action('woocommerce_proceed_to_checkout', array($this, 'disable_checkout_button'), 1);
460 }
461 }
462 }
463 $this->fetch_secret_status();
464 }
465
466 /**
467 * Handle WooCommerce abandoned carts feature
468 */
469 public function handle_abandoned_carts()
470 {
471 $Abandonment = Spoki_Abandoned_Carts::instance();
472 if (isset($this->options['abandoned_carts']['enable_tracking']) && $this->options['abandoned_carts']['enable_tracking'] == 1) {
473 $this->initialize_cart_abandonment_tables();
474 $Abandonment->set_spoki_setting_by_meta("plugin_activated", "true");
475 do_action('spoki_cartflow_ca_init');
476 } elseif (wp_next_scheduled('spoki_abandoned_carts_cron_hook')) {
477 # Unschedule hook for versions <= v2.8.1
478 $Abandonment->unschedule_hook();
479 }
480 }
481
482 /**
483 * Create new database tables for plugin updates.
484 *
485 * @return void
486 */
487 public function initialize_cart_abandonment_tables()
488 {
489 include_once SPOKI_DIR . 'modules/abandoned-carts/spoki-abandoned-carts-db.php';
490 $db = Spoki_Abandoned_Carts_Db::instance();
491 $db->create_tables();
492 $db->init_tables();
493 }
494
495 /**
496 * Send the Spoki notification for order status created or updated
497 *
498 * @param $order_get_id
499 * @param $from
500 * @param $to
501 */
502 public function send_woocommerce_order_created_or_updated_alert($order_get_id, $from, $to)
503 {
504 $this->send_woocommerce_order_alert($order_get_id, 'order.updated', ["from_status" => $from, "to_status" => $to]);
505 }
506
507 /**
508 * Send the Spoki notification for review
509 *
510 * @param $order_get_id
511 */
512 public function send_woocommerce_review_alert($order_get_id)
513 {
514 $review_link = (isset($this->options['woocommerce']['leave_review_link']) && $this->options['woocommerce']['leave_review_link'] != "") ? $this->options['woocommerce']['leave_review_link'] : "";
515 $review_link_days = (isset($this->options['woocommerce']['leave_review_days']) && $this->options['woocommerce']['leave_review_days'] != "") ? $this->options['woocommerce']['leave_review_days'] : 5;
516 $this->send_woocommerce_order_alert($order_get_id, 'order.review', ["review_link" => $review_link, "delay" => intval($review_link_days) * 24 * 60 * 60]);
517 }
518
519 /**
520 * Notify the Abandoned Cart Info Spoki notification
521 *
522 * @param $session_id
523 * @param $checkoutDetails
524 * @param $topic
525 * @return bool
526 */
527 public function notify_abandoned_cart_info($session_id, $checkoutDetails, $topic): bool
528 {
529 $other_fields = unserialize($checkoutDetails->other_fields);
530 $parts = explode(',', $other_fields['spoki_location']);
531 $country = $parts[0];
532 $data = [
533 "customer" => [
534 'email' => $checkoutDetails->email,
535 'phone' => sanitize_text_field($other_fields['spoki_phone_number']),
536 'country' => sanitize_text_field($country),
537 'first_name' => sanitize_text_field($other_fields['spoki_first_name']),
538 'last_name' => sanitize_text_field($other_fields['spoki_last_name']),
539 'name' => sanitize_text_field($other_fields['spoki_first_name']) . ' ' . sanitize_text_field($other_fields['spoki_last_name']),
540 ],
541 "checkout" => [
542 "checkout_url" => get_permalink($checkoutDetails->checkout_id) . '?session_id=' . $checkoutDetails->session_id,
543 "checkout_info" => $other_fields,
544 "cart_contacted_url" => get_bloginfo('url') . '/wp-json/api/v1/setCartAsContacted',
545 "total" => $checkoutDetails->cart_total,
546 "currency" => get_woocommerce_currency(),
547 ],
548 "session_id" => $session_id,
549 ];
550 return $this->spoki_send($data, $topic);
551 }
552
553 /**
554 * Send the Spoki notification for order deleted
555 *
556 * @param $order_get_id
557 */
558 public function send_woocommerce_order_deleted_alert($order_get_id)
559 {
560 $this->send_woocommerce_order_alert($order_get_id, 'order.deleted');
561 }
562
563 /**
564 * Send the Spoki notification for order status
565 *
566 * @param $order_get_id
567 * @param $topic
568 * @param $additional_data
569 */
570 public function send_woocommerce_order_alert($order_get_id, $topic, $additional_data = [])
571 {
572 $order = wc_get_order($order_get_id);
573 $session = [];
574 if (WC()->session) {
575 $session = ['session_id' => WC()->session->get('spoki_session_id')];
576 }
577 $order_data = array_merge(((isset($order) && false != $order ? $order->get_data() : ["id" => $order_get_id])), $additional_data);
578 $parts = explode(',', isset($order_data['billing']['country']) ? $order_data['billing']['country'] : '');
579 $country = $parts[0];
580 $customer = [
581 'email' => isset($order_data['billing']['email']) ? $order_data['billing']['email'] : '',
582 'phone' => isset($order_data['billing']['phone']) ? $order_data['billing']['phone'] : '',
583 'country' => $country ?? '',
584 'first_name' => (isset($order_data['billing']['first_name']) ? $order_data['billing']['first_name'] : ''),
585 'last_name' => (isset($order_data['billing']['last_name']) ? $order_data['billing']['last_name'] : ''),
586 'name' => (isset($order_data['billing']['first_name']) ? $order_data['billing']['first_name'] : '') . ' ' . (isset($order_data['billing']['last_name']) ? $order_data['billing']['last_name'] : ''),
587 ];
588 $this->spoki_send(array_merge([
589 "order" => $order_data,
590 "customer" => $customer,
591 ], $session), $topic);
592 }
593
594 /**
595 * Send the Spoki notification for tracking number
596 *
597 * @param $id
598 * @param $order
599 */
600 public function send_woocommerce_note_alert($id, $order)
601 {
602 $comment = get_comment($id);
603 if (isset($comment->comment_content)) {
604 $is_gls_tracking = substr(strtolower($comment->comment_content), 0, 4) == '[gls';
605 $is_user_tracking = substr(strtolower($comment->comment_content), 0, 10) == '[tracking]';
606
607 /** Send the message only if it is a tracking order note */
608 if ($comment->comment_type == 'order_note' && ($is_gls_tracking || $is_user_tracking)) {
609 $order_data = $order->get_data();
610 $this->send_woocommerce_order_alert($order_data['id'], 'order.tracking', ["note" => $comment]);
611 }
612 }
613 }
614
615 /**
616 * Send the Spoki notification
617 *
618 * @param $data
619 * @return bool
620 */
621 public function spoki_send($data, $topic): bool
622 {
623 $shop = ["shop" => $this->shop];
624
625 $request_params = array(
626 "headers" => array(
627 "Authorization" => $this->options['secret'],
628 "language" => $this->shop['language'],
629 "X-Wc-Webhook-Topic" => $topic,
630 ),
631 "body" => wp_json_encode(array_merge($data, $shop)),
632 "sslverify" => false,
633 "timeout" => 60,
634 );
635 $response = wp_remote_post($this->options['delivery_url'], $request_params);
636 $code = wp_remote_retrieve_response_code($response);
637 return $code == 200;
638 }
639
640 /**
641 * Create a new Spoki Free account
642 *
643 * @param $telephone
644 * @param $email
645 * @param $shop_name
646 * @return array
647 */
648 private function create_spoki_account($telephone, $email, $shop_name): array
649 {
650 $request_params = array(
651 "headers" => array("Authorization" => $this->options['secret'], "language" => $this->shop['language']),
652 "body" => [
653 "phone" => $telephone,
654 "email" => $email,
655 "name" => $shop_name,
656 ],
657 "sslverify" => false,
658 "timeout" => 60,
659 );
660
661 $response = wp_remote_post($this->api_enable_flex, $request_params);
662 $code = wp_remote_retrieve_response_code($response);
663
664 if ($code >= 200 && $code < 300) {
665 $data = json_decode(wp_remote_retrieve_body($response), true);
666 return [
667 "secret" => isset($data['secret']) ? $data['secret'] : null,
668 "delivery_url" => isset($data['delivery_url']) ? $data['delivery_url'] : null,
669 ];
670 }
671 return [];
672 }
673
674
675 /**
676 * Update a Spoki account
677 *
678 * @param $phone
679 * @param $email
680 * @param $shop_name
681 * @param $contact_link
682 * @param $billing_data
683 */
684 private function update_spoki_account($phone, $email, $shop_name, $contact_link, $billing_data = [])
685 {
686 $request_params = array(
687 "headers" => array("Authorization" => $this->options['secret'], "language" => $this->shop['language']),
688 "body" => [
689 "phone" => $phone ?: '',
690 "email" => $email ?: '',
691 "name" => $shop_name ?: '',
692 "contact_link" => $contact_link ?: '',
693 "zip_code" => $billing_data['zip_code'] ?: '',
694 "province" => $billing_data['province'] ?: '',
695 "country" => $billing_data['country'] ?: '',
696 "route" => $billing_data['route'] ?: '',
697 "city" => $billing_data['city'] ?: '',
698 "vat_number" => $billing_data['vat_number'] ?: '',
699 "vat_name" => $billing_data['vat_name'] ?: '',
700 "c_f" => $billing_data['c_f'] ?: '',
701 "pec" => $billing_data['pec'] ?: '',
702 "sid" => $billing_data['sid'] ?: '',
703 ],
704 "sslverify" => false,
705 "timeout" => 60,
706 );
707 wp_remote_post($this->api_account, $request_params);
708 }
709
710 /**
711 * Fetch and update the account_info
712 */
713 public function fetch_account_info()
714 {
715 $request_params = [
716 "headers" => [
717 "Authorization" => $this->options['secret'],
718 "language" => $this->shop['language']
719 ],
720 "sslverify" => false,
721 "timeout" => 60,
722 ];
723 $response = wp_remote_get($this->api_plan, $request_params);
724 $account_info = json_decode(wp_remote_retrieve_body($response), true);
725 $this->update_options(get_option(SPOKI_OPTIONS), ["account_info" => $account_info]);
726 return $account_info;
727 }
728
729 /**
730 * Check the status of the spoki keys
731 *
732 * @return array|WP_Error
733 */
734 public function check_spoki_status()
735 {
736 $secret = $this->options['secret'] ?? null;
737 $has_woocommerce = spoki_has_woocommerce();
738
739 $body = [
740 "is_plugin_active" => true,
741 "plugin_version" => $this->version,
742 "url" => $this->shop['url'],
743 "name" => $this->shop['name'],
744 "language" => $this->shop['language'],
745 "email" => $this->shop['email'],
746 "telephone" => $this->shop['telephone'],
747 "has_woocommerce" => $has_woocommerce,
748 "is_widget_fixed_enabled" => $this->shop['has_fixed_support_button'],
749 "is_widget_woo_shop_enabled" => $this->shop['has_product_item_listing_button'],
750 "is_widget_woo_item_enabled" => $this->shop['has_single_product_button'],
751 "is_widget_woo_cart_enabled" => $this->shop['has_cart_button'],
752 "is_widget_woo_checkout_enabled" => $this->shop['has_cart_button'],
753 "is_abandoned_carts_enabled" => $this->shop['has_abandoned_carts'],
754 "is_woo_send_enabled" => $has_woocommerce && $this->shop['has_notifications'],
755 "is_order_updated_notification_enabled" => $this->shop['has_order_updated_notification'],
756 "is_order_created_notification_enabled" => $this->shop['has_order_created_notification'],
757 "is_order_deleted_notification_enabled" => $this->shop['has_order_deleted_notification'],
758 "is_order_note_added_notification_enabled" => $this->shop['has_order_note_added_notification'],
759 "is_leave_review_notification_enabled" => $this->shop['has_leave_review_notification'],
760 "has_cart_recovered_to_seller_notification" => $this->shop['has_cart_recovered_to_seller_notification'],
761 "contact_link" => $this->shop['contact_link'] ?: '',
762 "zip_code" => $this->shop['billing_data']['zip_code'] ?: '',
763 "province" => $this->shop['billing_data']['province'] ?: '',
764 "country" => $this->shop['billing_data']['country'] ?: '',
765 "route" => $this->shop['billing_data']['route'] ?: '',
766 "city" => $this->shop['billing_data']['city'] ?: '',
767 "vat_number" => $this->shop['billing_data']['vat_number'] ?: '',
768 "vat_name" => $this->shop['billing_data']['vat_name'] ?: '',
769 "c_f" => $this->shop['billing_data']['c_f'] ?: '',
770 "pec" => $this->shop['billing_data']['pec'] ?: '',
771 "sid" => $this->shop['billing_data']['sid'] ?: '',
772 ];
773
774 if ($this->shop['has_abandoned_carts']) {
775 $spoki_abandoned_carts = Spoki_Abandoned_Carts::instance();
776 $abandoned_report = $spoki_abandoned_carts->get_report_by_type(SPOKI_CART_ABANDONED_ORDER);
777 $recovered_report = $spoki_abandoned_carts->get_report_by_type(SPOKI_CART_COMPLETED_ORDER);
778 $body["abandoned_carts_reports"] = [
779 "abandoned" => $abandoned_report,
780 "recovered" => $recovered_report,
781 ];
782 }
783
784 $request_params = [
785 "headers" => [
786 "Authorization" => $secret,
787 "language" => $this->shop['language'],
788 ],
789 "body" => $body,
790 "sslverify" => false,
791 "timeout" => 60,
792 ];
793 return wp_remote_post($this->api_status, $request_params);
794 }
795
796 /**
797 * Render the admin setup page
798 */
799 public function render_setup_page()
800 {
801 require_once SPOKI_DIR . 'includes/page-setup.php';
802 $this->render_components();
803 }
804
805
806 /**
807 * Render the Spoki Components
808 */
809 public function render_components()
810 {
811 $components = ['abandoned-carts-info-dialog', 'customer-notifications-info-dialog', 'buttons-info-dialog'];
812 foreach ($components as $component) {
813 include_once SPOKI_DIR . "components/$component.php";
814 }
815 }
816
817 /**
818 * Render the WhatsApp buttons in the website
819 */
820 public function render_buttons()
821 {
822 $has_fixed_support_button = isset($this->options['buttons']['fixed_support_button_check']) && $this->options['buttons']['fixed_support_button_check'] == 1;
823 $has_product_item_listing_button = isset($this->options['woocommerce']['product_item_listing_button_check']) && $this->options['woocommerce']['product_item_listing_button_check'] == 1;
824 $has_cart_button = isset($this->options['woocommerce']['cart_button_check']) && $this->options['woocommerce']['cart_button_check'] == 1;
825 $has_single_product_button = isset($this->options['woocommerce']['single_product_button_check']) && $this->options['woocommerce']['single_product_button_check'] == 1;
826
827 // Floating Button
828 if ($has_fixed_support_button) {
829 add_action('wp_footer', array($this, 'render_floating_button'), 1);
830 }
831
832 // Product Item Listing Button
833 if ($has_product_item_listing_button) {
834 add_action('woocommerce_after_shop_loop_item', array($this, 'render_product_item_listing_button'), 99);
835 }
836
837 // Cart Page Button
838 if ($has_cart_button) {
839 add_action('woocommerce_after_cart_totals', array($this, 'render_cart_button'), 99);
840 }
841
842 // Single Product Page Button
843 if ($has_single_product_button) {
844 $position = isset($this->options['woocommerce']['single_product_button_position']) ? $this->options['woocommerce']['single_product_button_position'] : 'after_atc';
845 switch ($position) {
846 case 'under_atc':
847 add_action('woocommerce_after_add_to_cart_form', array($this, 'render_single_product_button'), 99);
848 break;
849 case 'after_shortdesc':
850 add_action('woocommerce_before_add_to_cart_form', array($this, 'render_single_product_button'), 99);
851 break;
852 case 'after_atc':
853 default:
854 add_action('woocommerce_after_add_to_cart_button', array($this, 'render_single_product_button'), 99);
855 break;
856 }
857 }
858
859 /**
860 * Render the spoki button wherever you want using the shortcode
861 * @param $attrs
862 * @param null $content
863 */
864 function spoki_button_shortcode($attrs, $content = null)
865 {
866 $phone = $attrs['phone'] ?? Spoki()->shop['telephone'];
867 $cta = $attrs['cta'] ?? '';
868 $title = $attrs['title'] ?? '';
869 $final_message = urlencode($attrs['message'] ?? '');
870 $additional_class = $attrs['additional_class'] ?? '';
871 $type = 5;
872 $color = $attrs['color'] ?? null;
873 $border_type = $attrs['border_type'] ?? null;
874 $margin = [
875 "top" => $attrs['margin_top'] ?? null,
876 "bottom" => $attrs['margin_bottom'] ?? null,
877 "left" => $attrs['margin_left'] ?? null,
878 "right" => $attrs['margin_right'] ?? null,
879 ];
880 $padding = [
881 "top" => $attrs['padding_top'] ?? null,
882 "bottom" => $attrs['padding_bottom'] ?? null,
883 "left" => $attrs['padding_left'] ?? null,
884 "right" => $attrs['padding_right'] ?? null,
885 ];
886 $font_size = $attrs['font_size'] ?? null;
887 $id = $attrs['id'] ?? null;
888 $class_names = $attrs['class_names'] ?? null;
889 $custom_css = $attrs['custom_css'] ?? null;
890 Spoki()->render_relative_button($phone, $cta, $title, $final_message, $additional_class, $type, $margin, $padding, $color, $border_type, $font_size, '', $id, $class_names, $custom_css);
891 }
892
893 add_shortcode('spoki_button', 'spoki_button_shortcode');
894
895 add_action('wp_footer', array($this, 'render_shadowed_buttons'));
896 }
897
898 public function disable_checkout_button()
899 {
900 remove_action('woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20);
901 }
902
903 /**
904 *
905 */
906 public function render_shadowed_buttons()
907 {
908 echo "<script>";
909 include_once SPOKI_DIR . 'assets/js/spoki-shadowed-buttons.js';
910 echo "</script>";
911 }
912
913 /**
914 * Render the WhatsApp FAB on the website in every page
915 */
916 public function render_floating_button()
917 {
918 if (isset($this->options['telephone']) && $this->options['telephone'] != '') {
919
920 /** Check visibility conditions */
921 $hide_on_posts = isset($this->options['buttons']['fixed_support_button_hide_post_page']) && $this->options['buttons']['fixed_support_button_hide_post_page'] == 1;
922 if ($hide_on_posts && is_single() && 'post' == get_post_type()) {
923 return;
924 }
925 $hide_on_pages = isset($this->options['buttons']['fixed_support_button_hide_single_page']) && $this->options['buttons']['fixed_support_button_hide_single_page'] == 1;
926 if ($hide_on_pages && is_page() && 'page' == get_post_type()) {
927 return;
928 }
929 $hide_on_shop = isset($this->options['buttons']['fixed_support_button_hide_shop_page']) && $this->options['buttons']['fixed_support_button_hide_shop_page'] == 1;
930 if ($hide_on_shop && is_shop()) {
931 return;
932 }
933 $hide_on_product = isset($this->options['buttons']['fixed_support_button_hide_product_page']) && $this->options['buttons']['fixed_support_button_hide_product_page'] == 1;
934 if ($hide_on_product && is_product()) {
935 return;
936 }
937 $hide_on_cart = isset($this->options['buttons']['fixed_support_button_hide_cart_page']) && $this->options['buttons']['fixed_support_button_hide_cart_page'] == 1;
938 if ($hide_on_cart && is_cart()) {
939 return;
940 }
941 $hide_on_checkout = isset($this->options['buttons']['fixed_support_button_hide_checkout_page']) && $this->options['buttons']['fixed_support_button_hide_checkout_page'] == 1;
942 if ($hide_on_checkout && (is_checkout() || is_checkout_pay_page())) {
943 return;
944 }
945
946
947 /** Can render, go on */
948 $prefix = isset($this->options['prefix']) ? $this->options['prefix'] : '';
949 $phone = $prefix . $this->options['telephone'];
950 $text = $this->options['buttons']['fixed_support_button_text'];
951 $position = $this->options['buttons']['fixed_support_button_position'] == 'Left' ? 'left' : 'right';
952 $border_type = isset($this->options['buttons']['fixed_support_button_border']) ? $this->options['buttons']['fixed_support_button_border'] : 'circle';
953 $size = isset($this->options['buttons']['fixed_support_button_size']) ? $this->options['buttons']['fixed_support_button_size'] : '50';
954 $icon_size = '65%';
955 $border_radius = '50%';
956 if ($border_type == 'squared') {
957 $border_radius = '0';
958 } elseif ($border_type == 'rounded') {
959 $border_radius = '8px';
960 }
961 $color = isset($this->options['buttons']['fixed_support_button_color']) ? $this->options['buttons']['fixed_support_button_color'] : '#23D366';
962 $bottom_space = isset($this->options['buttons']['fixed_support_button_bottom_space']) ? $this->options['buttons']['fixed_support_button_bottom_space'] : '12';
963 $side_space = isset($this->options['buttons']['fixed_support_button_side_space']) ? $this->options['buttons']['fixed_support_button_side_space'] : '12';
964 $furl = get_bloginfo('url');
965 $wa_link = SPOKI_BASE_API_BUTTONS . "?type=1&phone=$phone&text=" . urlencode($text) . "&furl=$furl";
966
967 $spoki_fixed_btn_label = "";
968 $has_label = isset($this->options['buttons']['fixed_support_button_show_label']) && $this->options['buttons']['fixed_support_button_show_label'] == 1;
969 if ($has_label) {
970 $show_on_hover = (isset($this->options['buttons']['fixed_support_button_show_label_on_hover']) && $this->options['buttons']['fixed_support_button_show_label_on_hover'] == 1) ? 'hide-not-hover' : '';
971 $label_space = intval($size) + 12;
972 $label_content = (isset($this->options['buttons']['fixed_support_button_label_content']) && $this->options['buttons']['fixed_support_button_label_content'] != "") ? $this->options['buttons']['fixed_support_button_label_content'] : __('Chat with us 👋', "spoki");
973 $label_font_size = (isset($this->options['buttons']['fixed_support_button_label_font_size']) && $this->options['buttons']['fixed_support_button_label_font_size'] != "") ? $this->options['buttons']['fixed_support_button_label_font_size'] : '16';
974 $label_text_color = isset($this->options['buttons']['fixed_support_button_label_text_color']) ? $this->options['buttons']['fixed_support_button_label_text_color'] : '#333333';
975 $label_background_color = isset($this->options['buttons']['fixed_support_button_label_background_color']) ? $this->options['buttons']['fixed_support_button_label_background_color'] : '#FFFFFF';
976 $label_border_radius = $border_type == 'squared' ? '0' : '10';
977 $label_delay = (intval((isset($this->options['buttons']['fixed_support_button_label_delay']) && $this->options['buttons']['fixed_support_button_label_delay'] != "") ? $this->options['buttons']['fixed_support_button_label_delay'] : '0') * 1000) + 1000;
978 $spoki_fixed_btn_label = "<div class='spoki-fixed-btn-label $show_on_hover' style='display:none;$position:{$label_space}px;font-size:{$label_font_size}px;color:$label_text_color;background-color:$label_background_color;border-radius:{$label_border_radius}px;'>$label_content</div><script>(function (d) {setTimeout(function (){try {const shadowedButton = d.querySelector('#spoki-shadowed-fixed-button'); if (shadowedButton) {shadowedButton.shadowRoot.querySelector('.spoki-fixed-btn-label').style.display ='';}} catch (e) {console.log(e)}}, $label_delay)})(document)</script>";
979 }
980
981 $spoki_fixed_btn_popup = "";
982 $has_popup = isset($this->options['buttons']['fixed_support_button_show_popup']) && $this->options['buttons']['fixed_support_button_show_popup'] == 1;
983 if ($has_popup) {
984 $spoki_fixed_btn_popup_delay = (intval((isset($this->options['buttons']['fixed_support_button_popup_delay']) && $this->options['buttons']['fixed_support_button_popup_delay'] != "") ? $this->options['buttons']['fixed_support_button_popup_delay'] : '0') * 1000) + 1000;
985 $spoki_fixed_btn_popup = "<span class='spoki-fixed-btn-popup' style='display: none;'>1</span><script>(function (d) {setTimeout(function (){try {const shadowedButton = d.querySelector('#spoki-shadowed-fixed-button'); if (shadowedButton) {shadowedButton.shadowRoot.querySelector('.spoki-fixed-btn-popup').style.display ='';}} catch (e) {console.log(e)}}, $spoki_fixed_btn_popup_delay)})(document)</script>";
986 }
987
988 $hidden_mobile = isset($this->options['buttons']['fixed_support_button_hide_mobile']) && $this->options['buttons']['fixed_support_button_hide_mobile'] == 1 ? "hidden-mobile" : "";
989 $hidden_tablet = isset($this->options['buttons']['fixed_support_button_hide_tablet']) && $this->options['buttons']['fixed_support_button_hide_tablet'] == 1 ? "hidden-tablet" : "";
990 $hidden_desktop = isset($this->options['buttons']['fixed_support_button_hide_desktop']) && $this->options['buttons']['fixed_support_button_hide_desktop'] == 1 ? "hidden-desktop" : "";
991 $class = "$hidden_mobile $hidden_tablet $hidden_desktop";
992
993 $chat_widget = "";
994 $has_chat_widget = isset($this->options['buttons']['fixed_support_button_show_chat_widget']) && $this->options['buttons']['fixed_support_button_show_chat_widget'] == 1;
995 if ($has_chat_widget) {
996 $background_image_url = plugins_url() . '/' . SPOKI_PLUGIN_NAME . '/assets/images/wa-background.jpeg';
997 $from_message = $this->options['buttons']['fixed_support_button_chat_widget_message'];
998 $send_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="currentColor" d="M1.101 21.757L23.8 12.028 1.101 2.3l.011 7.912 13.623 1.816-13.623 1.817-.011 7.912z"></path></svg>';
999 $chat_widget_delay = intval((isset($this->options['buttons']['fixed_support_button_chat_widget_delay']) && $this->options['buttons']['fixed_support_button_chat_widget_delay'] != "") ? $this->options['buttons']['fixed_support_button_chat_widget_delay'] : '0') * 1000;
1000 $chat_widget_delay_script = "";
1001 if ($chat_widget_delay != 0) {
1002 $chat_widget_delay = $chat_widget_delay + 1000;
1003 $chat_widget_delay_script = "<script>(function (d) {setTimeout(function (){try {const shadowedButton = d.querySelector('#spoki-shadowed-fixed-button'); if (shadowedButton) {shadowedButton.shadowRoot.querySelector('#spoki-chat-preview').classList.remove('hidden')}} catch (e) {console.log(e)}}, $chat_widget_delay)})(document)</script>";
1004 }
1005 $chat_widget_script = "<script>(function (d) {setTimeout(function(){const shadowedButton = d.querySelector('#spoki-shadowed-fixed-button'); if (shadowedButton) {shadowedButton.shadowRoot.querySelector('#spoki-chat-link').addEventListener('click', function(e){e.preventDefault();document.querySelector('#spoki-shadowed-fixed-button').shadowRoot.querySelector('#spoki-chat-preview').classList.toggle('hidden')})}}, 1000)})(document)</script>$chat_widget_delay_script";
1006 $link = SPOKI_BASE_API_BUTTONS;
1007 $furl = get_bloginfo('url');
1008 $chat_widget = "<div id='spoki-chat-preview' class='hidden' style='bottom:{$size}px;background-image: url(\"{$background_image_url}\")'><div class='spoki-chat-preview-chat-message'>{$from_message}</div><div id='spoki-chat-preview-footer'><form method='get' target='_blank' action='$link'><input type='hidden' name='phone' value='{$phone}'/><input type='hidden' name='furl' value='{$furl}'/><input type='hidden' name='type' value='1'/><input id='spoki-chat-preview-message' type='text' name='text' value='$text' /><button id='spoki-chat-preview-send' type='submit'>{$send_icon}</button></form></div></div>$chat_widget_script";
1009 }
1010
1011 echo "<div id='spoki-shadowed-fixed-button' class='spoki-shadowed-button'><div id='spoki-fixed-btn' class='$class' style='$position:{$side_space}px;bottom:{$bottom_space}px;'>{$chat_widget}{$spoki_fixed_btn_label}{$spoki_fixed_btn_popup}<a id='spoki-chat-link' style='background-color:{$color};border-radius:$border_radius;height:{$size}px;width:{$size}px;' href='$wa_link' target='_blank' rel='nofollow'>" . spoki_get_wa_logo($size) . "</a></div></div>";
1012 }
1013 }
1014
1015 /**
1016 * Render the product button for every product in shop page
1017 */
1018 public function render_product_item_listing_button()
1019 {
1020 global $product;
1021 $prefix = isset($this->options['prefix']) ? $this->options['prefix'] : '';
1022 $phone = $prefix . $this->options['telephone'];
1023 $cta = __("Request support on WhatsApp", "spoki");
1024 if (isset($this->options['woocommerce']['product_item_listing_button_cta']) && !empty($this->options['woocommerce']['product_item_listing_button_cta'])) {
1025 $cta = $this->options['woocommerce']['product_item_listing_button_cta'];
1026 }
1027 $message = __("Hi, I want to buy:", "spoki");
1028 if (isset($this->options['woocommerce']['product_item_listing_button_text']) && !empty($this->options['woocommerce']['product_item_listing_button_text'])) {
1029 $message = $this->options['woocommerce']['product_item_listing_button_text'];
1030 }
1031 $color = isset($this->options['woocommerce']['product_item_listing_button_color']) ? $this->options['woocommerce']['product_item_listing_button_color'] : '#23D366';
1032 $font_size = isset($this->options['woocommerce']['product_item_listing_button_font_size']) ? $this->options['woocommerce']['product_item_listing_button_font_size'] : '12';
1033 $border_type = isset($this->options['woocommerce']['product_item_listing_button_border']) ? $this->options['woocommerce']['product_item_listing_button_border'] : 'rounded';
1034
1035 $margin = [
1036 "top" => isset($this->options['woocommerce']['product_item_listing_button_margin_top']) ? $this->options['woocommerce']['product_item_listing_button_margin_top'] : '4',
1037 "bottom" => isset($this->options['woocommerce']['product_item_listing_button_margin_bottom']) ? $this->options['woocommerce']['product_item_listing_button_margin_bottom'] : '4',
1038 "left" => isset($this->options['woocommerce']['product_item_listing_button_margin_left']) ? $this->options['woocommerce']['product_item_listing_button_margin_left'] : '0',
1039 "right" => isset($this->options['woocommerce']['product_item_listing_button_margin_right']) ? $this->options['woocommerce']['product_item_listing_button_margin_right'] : '0',
1040 ];
1041
1042 $padding = [
1043 "top" => isset($this->options['woocommerce']['product_item_listing_button_padding_top']) ? $this->options['woocommerce']['product_item_listing_button_padding_top'] : '8',
1044 "bottom" => isset($this->options['woocommerce']['product_item_listing_button_padding_bottom']) ? $this->options['woocommerce']['product_item_listing_button_padding_bottom'] : '8',
1045 "left" => isset($this->options['woocommerce']['product_item_listing_button_padding_left']) ? $this->options['woocommerce']['product_item_listing_button_padding_left'] : '14',
1046 "right" => isset($this->options['woocommerce']['product_item_listing_button_padding_right']) ? $this->options['woocommerce']['product_item_listing_button_padding_right'] : '14',
1047 ];
1048
1049 $this->render_product_button($product, $phone, $cta, $message, $margin, $padding, $color, $border_type, $font_size, true);
1050 }
1051
1052 /**
1053 * Render the button in the cart page
1054 */
1055 public function render_cart_button()
1056 {
1057 global $product;
1058 $prefix = isset($this->options['prefix']) ? $this->options['prefix'] : '';
1059 $phone = $prefix . $this->options['telephone'];
1060 $cta = __("Order via WhatsApp", "spoki");
1061 if (isset($this->options['woocommerce']['cart_button_cta']) && !empty($this->options['woocommerce']['cart_button_cta'])) {
1062 $cta = $this->options['woocommerce']['cart_button_cta'];
1063 }
1064 $message = __("Hi, I want to buy:", "spoki");
1065 if (isset($this->options['woocommerce']['cart_button_text']) && !empty($this->options['woocommerce']['cart_button_text'])) {
1066 $message = $this->options['woocommerce']['cart_button_text'];
1067 }
1068 $color = isset($this->options['woocommerce']['cart_button_color']) ? $this->options['woocommerce']['cart_button_color'] : '#23D366';
1069 $font_size = isset($this->options['woocommerce']['cart_button_font_size']) ? $this->options['woocommerce']['cart_button_font_size'] : '12';
1070 $icon_size = intval($font_size) < 16 ? 16 : $font_size;
1071 $final_message = urlencode($message);
1072 $products = WC()->cart->get_cart();
1073
1074 foreach ($products as $item) {
1075 $product_id = $item['product_id'];
1076 $qty = $item['quantity'];
1077 $product = wc_get_product($product_id);
1078 $product_url = $product->get_permalink();
1079 $product_title = $product->get_name();
1080 $price = wp_strip_all_tags(wc_price(wc_get_price_including_tax($product)));
1081 $encoded_title = urlencode($product_title);
1082 $encoded_product_url = urlencode($product_url);
1083 $final_message .= "%0D%0A%0D%0A(ID:%20$product_id)%20*$encoded_title*%20$price%20x$qty%0D%0A$encoded_product_url";
1084 }
1085 $furl = get_bloginfo('url');
1086 $href = SPOKI_BASE_API_BUTTONS . "?type=4&phone=$phone&text=$final_message&furl=$furl";
1087 $title = "$cta";
1088 $class = 'button spoki-button size-4';
1089 $border_type = isset($this->options['woocommerce']['cart_button_border']) ? $this->options['woocommerce']['cart_button_border'] : 'rounded';
1090
1091 $border_radius = '16px';
1092 if ($border_type == 'squared') {
1093 $border_radius = '0';
1094 }
1095
1096 $margin = [
1097 "top" => isset($this->options['woocommerce']['cart_button_margin_top']) ? $this->options['woocommerce']['cart_button_margin_top'] : '4',
1098 "bottom" => isset($this->options['woocommerce']['cart_button_margin_bottom']) ? $this->options['woocommerce']['cart_button_margin_bottom'] : '4',
1099 "left" => isset($this->options['woocommerce']['cart_button_margin_left']) ? $this->options['woocommerce']['cart_button_margin_left'] : '0',
1100 "right" => isset($this->options['woocommerce']['cart_button_margin_right']) ? $this->options['woocommerce']['cart_button_margin_right'] : '0',
1101 ];
1102 $padding = [
1103 "top" => isset($this->options['woocommerce']['cart_button_padding_top']) ? $this->options['woocommerce']['cart_button_padding_top'] : '8',
1104 "bottom" => isset($this->options['woocommerce']['cart_button_padding_bottom']) ? $this->options['woocommerce']['cart_button_padding_bottom'] : '8',
1105 "left" => isset($this->options['woocommerce']['cart_button_padding_left']) ? $this->options['woocommerce']['cart_button_padding_left'] : '14',
1106 "right" => isset($this->options['woocommerce']['cart_button_padding_right']) ? $this->options['woocommerce']['cart_button_padding_right'] : '14',
1107 ];
1108
1109 $margin_style = "margin-top:{$margin['top']}px;margin-bottom:{$margin['bottom']}px;margin-left:{$margin['left']}px;margin-right:{$margin['right']}px;";
1110 $padding_style = "padding-top:{$padding['top']}px;padding-bottom:{$padding['bottom']}px;padding-left:{$padding['left']}px;padding-right:{$padding['right']}px;";
1111
1112 echo "<div class='spoki-shadowed-button'><div class='spoki-button-relative' style='{$margin_style}'><a href='$href' target='_blank' rel='nofollow' title='$title' class='$class' style='background-color:$color;border-radius:$border_radius;font-size:{$font_size}px;{$padding_style}'>" . spoki_get_wa_logo($icon_size) . "<span>$cta</span></a></div></div>";
1113 }
1114
1115 /**
1116 * Render the button in the product page
1117 */
1118 public function render_single_product_button()
1119 {
1120 global $product;
1121 $prefix = isset($this->options['prefix']) ? $this->options['prefix'] : '';
1122 $phone = $prefix . $this->options['telephone'];
1123 $cta = __("Request support on WhatsApp", "spoki");
1124 if (isset($this->options['woocommerce']['single_product_button_cta']) && !empty($this->options['woocommerce']['single_product_button_cta'])) {
1125 $cta = $this->options['woocommerce']['single_product_button_cta'];
1126 }
1127 $message = __("Hi, I want to buy:", "spoki");
1128 if (isset($this->options['woocommerce']['single_product_button_text']) && !empty($this->options['woocommerce']['single_product_button_text'])) {
1129 $message = $this->options['woocommerce']['single_product_button_text'];
1130 }
1131 $position = isset($this->options['woocommerce']['single_product_button_position']) ? $this->options['woocommerce']['single_product_button_position'] : 'after_atc';
1132 $color = isset($this->options['woocommerce']['single_product_button_color']) ? $this->options['woocommerce']['single_product_button_color'] : '#23D366';
1133 $font_size = isset($this->options['woocommerce']['single_product_button_font_size']) ? $this->options['woocommerce']['single_product_button_font_size'] : '12';
1134 $border_type = isset($this->options['woocommerce']['single_product_button_border']) ? $this->options['woocommerce']['single_product_button_border'] : 'rounded';
1135
1136 $margin = [
1137 "top" => isset($this->options['woocommerce']['single_product_button_margin_top']) ? $this->options['woocommerce']['single_product_button_margin_top'] : '4',
1138 "bottom" => isset($this->options['woocommerce']['single_product_button_margin_bottom']) ? $this->options['woocommerce']['single_product_button_margin_bottom'] : '4',
1139 "left" => isset($this->options['woocommerce']['single_product_button_margin_left']) ? $this->options['woocommerce']['single_product_button_margin_left'] : '0',
1140 "right" => isset($this->options['woocommerce']['single_product_button_margin_right']) ? $this->options['woocommerce']['single_product_button_margin_right'] : '0',
1141 ];
1142
1143 $padding = [
1144 "top" => isset($this->options['woocommerce']['single_product_button_padding_top']) ? $this->options['woocommerce']['single_product_button_padding_top'] : '8',
1145 "bottom" => isset($this->options['woocommerce']['single_product_button_padding_bottom']) ? $this->options['woocommerce']['single_product_button_padding_bottom'] : '8',
1146 "left" => isset($this->options['woocommerce']['single_product_button_padding_left']) ? $this->options['woocommerce']['single_product_button_padding_left'] : '14',
1147 "right" => isset($this->options['woocommerce']['single_product_button_padding_right']) ? $this->options['woocommerce']['single_product_button_padding_right'] : '14',
1148 ];
1149
1150 $this->render_product_button($product, $phone, $cta, $message, $margin, $padding, $color, $border_type, $font_size, false, $position);
1151 }
1152
1153 /**
1154 * Render the button of a product
1155 *
1156 * @param $product
1157 * @param $phone
1158 * @param $cta
1159 * @param $message
1160 * @param null $position
1161 */
1162 public function render_product_button($product, $phone, $cta, $message, $margin, $padding, $color = '#23D366', $border_type = 'rounded', $font_size = '12', $is_listing = false, $position = null)
1163 {
1164 $product_url = $product->get_permalink();
1165 $product_title = $product->get_name();
1166 $product_id = $product->get_id();
1167 $price = wp_strip_all_tags(wc_price(wc_get_price_including_tax($product)));
1168 $encoded_message = urlencode($message);
1169 $encoded_title = urlencode($product_title);
1170 $encoded_product_url = urlencode($product_url);
1171 $final_message = "$encoded_message%0D%0A%0D%0A(ID:%20$product_id)%20*$encoded_title*%20$price%0D%0A$encoded_product_url";
1172 $type = $is_listing ? '2' : '3';
1173 $title = "$cta $product_title";
1174 $additional_class = sprintf('product_type_%s', $product->get_type());
1175
1176 $this->render_relative_button($phone, $cta, $title, $final_message, $additional_class, $type, $margin, $padding, $color, $border_type, $font_size, $position);
1177 }
1178
1179 /**
1180 * Render a relative button
1181 */
1182 public function render_relative_button($phone, $cta, $title, $final_message, $additional_class, $type, $margin, $padding, $color = '#23D366', $border_type = 'rounded', $font_size = '12', $position = null, $id = '', $class_names = '', $custom_css = '')
1183 {
1184 $class = sprintf('button spoki-button %s', $additional_class);
1185 if (isset($position)) {
1186 $class .= " size-2 $position";
1187 } else {
1188 $class .= " size-4";
1189 }
1190
1191 $border_radius = '16px';
1192 if ($border_type == 'squared') {
1193 $border_radius = '0';
1194 }
1195
1196 $furl = get_bloginfo('url');
1197 $href = SPOKI_BASE_API_BUTTONS . "?type={$type}&phone=$phone&text=$final_message&furl=$furl";
1198 $icon_size = intval($font_size) < 16 ? 16 : $font_size;
1199
1200 $margin_style = "margin-top:{$margin['top']}px;margin-bottom:{$margin['bottom']}px;margin-left:{$margin['left']}px;margin-right:{$margin['right']}px;";
1201 $padding_style = "padding-top:{$padding['top']}px;padding-bottom:{$padding['bottom']}px;padding-left:{$padding['left']}px;padding-right:{$padding['right']}px;";
1202
1203 echo "<div class='spoki-shadowed-button'><div class='spoki-button-relative $class_names' id='$id' style='$margin_style'><a href='$href' target='_blank' rel='nofollow' title='$title' class='$class' style='background-color:$color;border-radius:$border_radius;font-size:{$font_size}px;$padding_style'>" . spoki_get_wa_logo($icon_size) . "<span>$cta</span></a><style>$custom_css</style></div></div>";
1204 }
1205
1206 /**
1207 * Get the link to change plan
1208 *
1209 * @param $is_upgrade
1210 * @return string
1211 */
1212 public function get_plan_link($is_upgrade): string
1213 {
1214 $url = $this->shop['language'] == 'it-IT' ? 'https://spoki.it/spoki-flex-acquista-pacchetti-flex/?' : 'https://spoki.it/spoki-flex-acquista-pacchetti-flex/spoki-flex-packages/?';
1215
1216 if (isset($this->options['account_info']['upgrade_url'])) {
1217 $url = $this->options['account_info']['upgrade_url'];
1218 }
1219 if ($is_upgrade) {
1220 $url .= '&is_upgrade=true';
1221 }
1222 return $url;
1223 }
1224
1225 /**
1226 * Get the link to change plan to pro
1227 *
1228 * @return string
1229 */
1230 public function get_pro_plan_link(): string
1231 {
1232 return 'https://spoki.it/piani-e-prezzi-spoki-pro/?';
1233 }
1234
1235 /**
1236 * Update Spoki options
1237 *
1238 * @param $current_options
1239 * @param $new_options
1240 */
1241 private function update_options($current_options, $new_options)
1242 {
1243 $c_options = is_string($current_options) ? [] : $current_options;
1244 if (isset($new_options["woocommerce"])) {
1245 $woocommerce = [];
1246 if (isset($c_options["abandoned_carts"])) {
1247 $woocommerce = array_merge($c_options["woocommerce"], $new_options["woocommerce"]);
1248 } else {
1249 $woocommerce = $new_options["woocommerce"];
1250 }
1251 $new_options["woocommerce"] = $woocommerce;
1252 }
1253 if (isset($new_options["abandoned_carts"])) {
1254 $abandoned_carts = [];
1255 if (isset($c_options["abandoned_carts"])) {
1256 $abandoned_carts = array_merge($c_options["abandoned_carts"], $new_options["abandoned_carts"]);
1257 } else {
1258 $abandoned_carts = $new_options["abandoned_carts"];
1259 }
1260 $new_options["abandoned_carts"] = $abandoned_carts;
1261 }
1262 if (isset($new_options["secret_status"])) {
1263 $secret_status = [];
1264 if (isset($c_options["secret_status"])) {
1265 $secret_status = array_merge($c_options["secret_status"], $new_options["secret_status"]);
1266 } else {
1267 $secret_status = $new_options["secret_status"];
1268 }
1269 $new_options["secret_status"] = $secret_status;
1270 }
1271 if (isset($new_options["account_info"])) {
1272 $account_info = [];
1273 if (isset($c_options["account_info"])) {
1274 $account_info = array_merge($c_options["account_info"], $new_options["account_info"]);
1275 } else {
1276 $account_info = $new_options["account_info"];
1277 }
1278 $new_options["account_info"] = $account_info;
1279 }
1280 update_option(SPOKI_OPTIONS, array_merge($c_options, $new_options));
1281 $this->options = get_option(SPOKI_OPTIONS);
1282 }
1283
1284 /**
1285 * Check the secret status periodically
1286 */
1287 public function check_secret_status()
1288 {
1289 $this->fetch_secret_status(true);
1290 }
1291
1292 /**
1293 * Fetch the status of the Spoki keys
1294 */
1295 public function fetch_secret_status($force_checking = false)
1296 {
1297 $response = null;
1298 if ($force_checking) {
1299 $response = $this->check_spoki_status();
1300 }
1301
1302 if (!isset($this->options['secret']) || $this->options['secret'] == '' || !isset($this->options['delivery_url']) || $this->options['delivery_url'] == '') {
1303 $this->update_options(get_option(SPOKI_OPTIONS), ["secret_status" => [
1304 'secret' => isset($this->options['secret']) ? $this->options['secret'] : '',
1305 'delivery_url' => isset($this->options['delivery_url']) ? $this->options['delivery_url'] : '',
1306 'code' => 0,
1307 'message' => ''
1308 ]]);
1309 } else if (!isset($this->options['secret_status']) || $this->options['secret_status']['code'] != 200 || $this->options['secret'] != $this->options['secret_status']['secret'] || $this->options['delivery_url'] != $this->options['secret_status']['delivery_url']) {
1310 if (!$force_checking) {
1311 $response = $this->check_spoki_status();
1312 }
1313 $this->update_options(get_option(SPOKI_OPTIONS), ["secret_status" => [
1314 'secret' => isset($this->options['secret']) ? $this->options['secret'] : '',
1315 'delivery_url' => isset($this->options['delivery_url']) ? $this->options['delivery_url'] : '',
1316 'code' => wp_remote_retrieve_response_code($response),
1317 'message' => wp_remote_retrieve_response_message($response)
1318 ]]);
1319 }
1320 }
1321
1322 /**
1323 * Activation Reset
1324 */
1325 public function activation_reset()
1326 {
1327 register_uninstall_hook(SPOKI_PLUGIN_FILE, array($this, 'uninstall_plugin'));
1328 if (!class_exists('WooCommerce')) {
1329 return;
1330 }
1331 $this->initialize_cart_abandonment_tables();
1332 $Abandonment = Spoki_Abandoned_Carts::instance();
1333 $spokiDomain = $Abandonment->get_spoki_setting_by_meta("spoki_domain");
1334 $Abandonment->set_spoki_setting_by_meta("plugin_activated", "true");
1335 if ($spokiDomain != null || $spokiDomain != "")
1336 $Abandonment->save_webhook_url($spokiDomain);
1337 }
1338
1339 /**
1340 * Deactivation Reset
1341 */
1342 public function deactivation_reset()
1343 {
1344 $Abandonment = Spoki_Abandoned_Carts::instance();
1345 # Unschedule hook for versions <= v2.8.1
1346 $Abandonment->unschedule_hook();
1347 if (!class_exists('WooCommerce')) {
1348 return;
1349 }
1350 $spokiDomain = $Abandonment->get_spoki_setting_by_meta("spoki_domain");
1351 $Abandonment->set_spoki_setting_by_meta("plugin_activated", "false");
1352 if ($spokiDomain != null || $spokiDomain != "")
1353 $Abandonment->disable_webhook_url($spokiDomain);
1354 }
1355
1356 /**
1357 * Uninstall Plugin
1358 */
1359 public function uninstall_plugin()
1360 {
1361 if (!class_exists('WooCommerce')) {
1362 return;
1363 }
1364 $Abandonment = Spoki_Abandoned_Carts::instance();
1365 $spokiDomain = $Abandonment->get_spoki_setting_by_meta("spoki_domain");
1366 $Abandonment->set_spoki_setting_by_meta("plugin_activated", "false");
1367 if ($spokiDomain != null || $spokiDomain != "")
1368 $Abandonment->disable_webhook_url($spokiDomain);
1369 }
1370 }
1371
1372 /**
1373 * Executed on activation of the plugin.
1374 * Redirects the user after plugin activation.
1375 */
1376 function spoki_activation()
1377 {
1378 if (!((isset($_REQUEST['action']) && 'activate-selected' === $_REQUEST['action']) && (isset($_POST['checked']) && count($_POST['checked']) > 1))) {
1379 add_option('spoki_activation_redirect', true);
1380 }
1381 }
1382
1383 /**
1384 * Redirects the user after plugin activation.
1385 */
1386 function spoki_activation_redirect()
1387 {
1388 if (get_option('spoki_activation_redirect', false)) {
1389 delete_option('spoki_activation_redirect');
1390 exit(wp_redirect(admin_url('/admin.php?page=' . SPOKI_PLUGIN_NAME)));
1391 }
1392 }
1393
1394 /**
1395 * Executed on deactivation of the plugin
1396 */
1397 function spoki_deactivation()
1398 {
1399 // delete_option(SPOKI_OPTIONS);
1400 $timestamp = wp_next_scheduled('spoki_cron_hook');
1401 wp_unschedule_event($timestamp, 'spoki_cron_hook');
1402 }
1403
1404 register_deactivation_hook(__FILE__, 'spoki_deactivation');
1405 register_activation_hook(__FILE__, 'spoki_activation');
1406 add_action('admin_init', 'spoki_activation_redirect');
1407