PluginProbe
Spoki – Chat Buttons and WooCommerce Notifications / 2.8.3
Spoki – Chat Buttons and WooCommerce Notifications v2.8.3
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.8.3, at spoki.php

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