PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.2.8
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.2.8
3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 All 156 releases
notificationx / includes / Extensions / WooCommerce / WooCommerce.php

WooCommerce.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.2.8, at includes/Extensions/WooCommerce/WooCommerce.php

586 lines 21.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WooCommerce Extension
4 *
5 * @package NotificationX\Extensions
6 */
7
8 namespace NotificationX\Extensions\WooCommerce;
9
10 use NotificationX\Admin\Entries;
11 use NotificationX\Admin\Settings;
12 use NotificationX\Core\Helper;
13 use NotificationX\Core\PostType;
14 use NotificationX\Core\Rules;
15 use NotificationX\GetInstance;
16 use NotificationX\Extensions\Extension;
17 use NotificationX\Extensions\GlobalFields;
18 use NotificationX\Types\Conversions;
19
20 /**
21 * WooCommerce Extension Class
22 * @method static WooCommerce get_instance($args = null)
23 */
24 class WooCommerce extends Extension {
25 /**
26 * Instance of WOOReviews
27 *
28 * @var WOOReviews
29 */
30 use GetInstance;
31 use Woo;
32
33 public $priority = 5;
34 public $id = 'woocommerce';
35 public $img = NOTIFICATIONX_ADMIN_URL . 'images/extensions/sources/woocommerce.png';
36 public $doc_link = 'https://notificationx.com/docs/woocommerce-sales-notifications/';
37 public $types = 'conversions';
38 public $module = 'modules_woocommerce';
39 public $module_priority = 3;
40 public $class = '\WooCommerce';
41 public $wpml_included = [
42 'sales_count', 'donation_count'
43 ];
44
45 /**
46 * Initially Invoked when initialized.
47 */
48 public function __construct(){
49 parent::__construct();
50 }
51
52 public function init_extension()
53 {
54 $this->title = __('WooCommerce', 'notificationx');
55 $this->module_title = __('WooCommerce', 'notificationx');
56 $this->templates = Conversions::get_instance()->templates;
57 $this->templates['woo_template_new']['third_param']['product_title_raw'] = __('Product Title Raw', 'notificationx');
58 $this->templates['woo_template_sales_count']['third_param']['product_title_raw'] = __('Product Title Raw', 'notificationx');
59 }
60
61 public function init(){
62 parent::init();
63 add_action('woocommerce_order_status_changed', array($this, 'status_transition'), 10, 4);
64 add_action('woocommerce_process_shop_order_meta', array( $this, 'manual_order'), 10, 2 );
65 add_action('woocommerce_new_order_item', array($this, 'save_new_orders'), 10, 3);
66 add_filter("nx_entry_show_on_frontend_{$this->id}", [$this, 'entry_display'], 10, 3);
67 }
68
69 /**
70 * This function is responsible for checking if the product is published or not.
71 *
72 * @param boolean $data
73 * @param array $post
74 * @param array $entry
75 * @return boolean
76 */
77 public function entry_display($data, $post, $entry) {
78 $product_id = $post['product_id'];
79 if ( empty($product_id) ) {
80 return true;
81 }
82
83 $product = wc_get_product($product_id);
84 if ( ! $product ) {
85 return true;
86 }
87
88 if ( $product->get_status() !== 'publish' ) {
89 return true;
90 }
91
92 return false;
93 }
94
95
96 public function init_fields(){
97 parent::init_fields();
98 add_filter('nx_link_types', [$this, 'link_types']);
99 add_filter( 'nx_woo_order_status', array( $this, 'order_status' ), 11 );
100 $this->_init_fields();
101 }
102
103 /**
104 * This functions is hooked
105 *
106 * @return void
107 */
108 public function admin_actions() {
109 parent::admin_actions();
110 add_filter("nx_can_entry_{$this->id}", array($this, 'check_order_status'), 10, 3);
111 add_filter("nx_can_entry_{$this->id}", array($this->get_type(), 'nx_can_entry'), 10, 3);
112 }
113
114 public function public_actions(){
115 parent::public_actions();
116
117 add_filter("nx_filtered_data_{$this->id}", array($this, 'multiorder_combine'), 11, 3);
118 }
119
120 public function wpml_actions(){
121 add_filter("nx_filtered_entry_{$this->id}", array($this, 'wpml_translate'), 11, 2);
122 add_filter("nx_notification_link_{$this->id}", [$this, 'product_link'], 10, 3);
123
124 }
125
126 public function source_error_message($messages) {
127 if (!$this->class_exists()) {
128 $url = admin_url('plugin-install.php?s=woocommerce&tab=search&type=term');
129 $messages[$this->id] = [
130 'message' => sprintf( '%s <a href="%s" target="_blank">%s</a> %s',
131 __( 'You have to install', 'notificationx' ),
132 $url,
133 __( 'WooCommerce', 'notificationx' ),
134 __( 'plugin first.', 'notificationx' )
135 ),
136 'html' => true,
137 'type' => 'error',
138 'rules' => Rules::is('source', $this->id),
139 ];
140 }
141 return $messages;
142 }
143
144
145 public function order_status($options){
146 if(function_exists('wc_get_order_statuses')){
147 $options = GlobalFields::get_instance()->normalize_fields(wc_get_order_statuses(), 'source', $this->id, $options);
148 }
149 return $options;
150 }
151
152
153 /**
154 * Adds option to Link Type field in Content tab.
155 *
156 * @param array $options
157 * @return array
158 */
159 public function link_types($options) {
160 $options = GlobalFields::get_instance()->normalize_fields([
161 'product_page' => __('Product Page', 'notificationx'),
162 ], 'source', $this->id, $options);
163
164 return $options;
165 }
166
167 public function saved_post($post, $data, $nx_id) {
168 $this->delete_notification(null, $nx_id);
169 $this->get_notification_ready($data);
170 }
171
172 /**
173 * This function is responsible for making the notification ready for first time we make the notification.
174 *
175 * @param string $type
176 * @param array $data
177 * @return void
178 */
179 public function get_notification_ready($post = array()) {
180 if (!is_null($orders = $this->get_orders($post))) {
181 // $orders = Helper::sortBy($orders, 'woocommerce');
182 $entries = [];
183 foreach ($orders as $key => $order) {
184 $entries[] = [
185 'nx_id' => $post['nx_id'],
186 'source' => $this->id,
187 'entry_key' => $key,
188 'data' => $order,
189 // 'updated_at' => Helper::mysql_time($order['timestamp']),
190 ];
191 }
192 $this->update_notifications($entries);
193 }
194 return $orders;
195 }
196
197 /**
198 * It will generate and save a notification
199 * when orders are placed.
200 *
201 * @param int $item_id
202 * @param WC_Order_Item_Product $item
203 * @param int $order_id
204 * @return void
205 */
206 public function save_new_orders($item_id, $item, $order_id) {
207 $single_notification = $this->ordered_product($item_id, $item, $order_id);
208 if (!empty($single_notification)) {
209 $key = $order_id . '-' . $item_id;
210 $this->save([
211 'source' => $this->id,
212 'entry_key' => $key,
213 'data' => $single_notification,
214 ]);
215 return true;
216 }
217 return false;
218 }
219
220 public function status_transition($id, $from, $to, $order) {
221 $has_course = false;
222 $from = "wc-$from";
223 $to = "wc-$to";
224 $items = $order->get_items();
225 $posts = PostType::get_instance()->get_posts([
226 'source' => $this->id,
227 'enabled' => true,
228 ]);
229 if (!empty($posts)) {
230 foreach ($posts as $post) {
231 $done = !empty($post['order_status']) ? $post['order_status'] : ['wc-completed', 'wc-processing'];
232 $status = array_keys(wc_get_order_statuses());
233 $status = array_diff($status, $done);
234 $status[] = 'wcf-main-order';
235
236 foreach ($items as $item) {
237 $key = $id . '-' . $item->get_id();
238 if (function_exists('tutor_utils')) {
239 $has_course = tutor_utils()->product_belongs_with_course($item->get_product_id());
240 }
241 if ($has_course) {
242 continue;
243 }
244
245 if (in_array($from, $done) && in_array($to, $status)) {
246 $this->delete_notification($key);
247 }
248
249 if (in_array($from, $status) && in_array($to, $done)) {
250 $single_notification = $this->ordered_product($item->get_id(), $item, $order);
251 if (!empty($single_notification)) {
252 $this->update_notification([
253 'nx_id' => $post['nx_id'],
254 'source' => $this->id,
255 'entry_key' => $key,
256 'data' => $single_notification,
257 ], false);
258 }
259 }
260 }
261
262 }
263 }
264
265 return true;
266 }
267
268 // define the manual_order callback
269 public function manual_order( $order_id, $post ){
270 $orders = [];
271 $order = wc_get_order($order_id);
272 $items = $order->get_items();
273 foreach( $items as $item ) {
274 $tutor_product = metadata_exists('post', $item->get_product_id(), "_tutor_product");
275 if($tutor_product ) {
276 continue;
277 }
278 $order_item = $this->ordered_product($item->get_id(), $item, $order);
279 if($order_item){
280 $this->save([
281 'source' => $this->id,
282 'entry_key' => $order->get_id() . '-' . $item->get_id(),
283 'data' => $order_item,
284 ], false);
285 }
286 }
287 }
288
289 /**
290 * This function is responsible for making ready the orders data.
291 *
292 * @param int $item_id
293 * @param \WC_Order_Item_Product $item
294 * @param int $order_id
295 * @return void
296 */
297 public function ordered_product($item_id, $item, $order_id) {
298 if (!$item instanceof \WC_Order_Item_Product) {
299 return false;
300 }
301 $product = wc_get_product( $item->get_product_id() );
302 if( empty( $product ) ) {
303 return false;
304 }
305 $if_has_course = false;
306 if (function_exists('tutor_utils')) {
307 $if_has_course = tutor_utils()->product_belongs_with_course($item->get_product_id());
308 }
309 if ($if_has_course) {
310 return false;
311 }
312 $new_order = [];
313 if (is_int($order_id)) {
314 $order = new \WC_Order($order_id);
315 } else {
316 $order = $order_id;
317 }
318
319 if (!$order instanceof \WC_Order) {
320 return false;
321 }
322
323 $new_order['status'] = 'wc-' . $order->get_status();
324
325 $date = $order->get_date_created();
326 $countries = new \WC_Countries();
327 $shipping_country = $order->get_billing_country();
328 if (empty($shipping_country)) {
329 $shipping_country = $order->get_shipping_country();
330 }
331 if (!empty($shipping_country)) {
332 $new_order['country'] = isset($countries->countries[$shipping_country]) ? $countries->countries[$shipping_country] : '';
333 $billing_state = $order->get_billing_state();
334 if (!empty($billing_state)) {
335 $new_order['state'] = isset($countries->states[$shipping_country], $countries->states[$shipping_country][$billing_state]) ? $countries->states[$shipping_country][$billing_state] : $billing_state;
336 }else {
337 $shipping_state = $order->get_shipping_state();
338 if (!empty($shipping_state)) {
339 $new_order['state'] = isset($countries->states[$shipping_country], $countries->states[$shipping_country][$shipping_state]) ? $countries->states[$shipping_country][$shipping_state] : $shipping_state;
340 }
341 }
342 }
343 $new_order['city'] = $order->get_billing_city();
344 if (empty($new_order['city'])) {
345 $new_order['city'] = $order->get_shipping_city();
346 }
347
348 $new_order['ip'] = $order->get_customer_ip_address();
349 $product_data = $this->ready_product_data($item->get_data());
350 if (!empty($product_data)) {
351 $new_order['order_id'] = is_int($order_id) ? $order_id : $order_id->get_id();
352 $product = $item->get_product();
353 if ( isset($product) && $product->get_type() === 'variation' ) {
354 $new_order['var_product_id'] = $item->get_variation_id();
355 }
356 $new_order['product_id'] = $item->get_product_id();
357 $new_order['title'] = strip_tags($product_data['title']);
358 $new_order['link'] = $product_data['link'];
359 }
360 if($date && method_exists($date, 'getTimestamp')){
361 $new_order['timestamp'] = $date->getTimestamp();
362 }
363 else{
364 $new_order['timestamp'] = time();
365 }
366 return array_merge($new_order, $this->buyer($order));
367 }
368
369 /**
370 * It will take an array to make data clean
371 *
372 * @param array $data
373 * @return void
374 */
375 protected function ready_product_data($data) {
376 if (empty($data)) {
377 return;
378 }
379 $product = wc_get_product( $data['product_id'] );
380 if( empty( $product ) ) {
381 return;
382 }
383 return array(
384 'title' => $data['name'],
385 'product_name' => $product ? $product->get_name() : $data['name'],
386 'link' => get_permalink($data['product_id']),
387 );
388 }
389
390 /**
391 * This function is responsible for getting
392 * the buyer name from order.
393 *
394 * @param WC_Order $order
395 * @return void
396 */
397 protected function buyer(\WC_Order $order) {
398 $first_name = $last_name = $email = '';
399 $buyer_data = [];
400
401 if (empty($first_name)) {
402 $first_name = $order->get_billing_first_name();
403 }
404
405 if (empty($last_name)) {
406 $last_name = $order->get_billing_last_name();
407 }
408
409 if (empty($email)) {
410 $email = $order->get_billing_email();
411 }
412
413 $buyer_data['first_name'] = $first_name;
414 $buyer_data['last_name'] = $last_name;
415 $buyer_data['name'] = $first_name . ' ' . mb_substr($last_name, 0, 1);
416 $buyer_data['email'] = $email;
417
418 return $buyer_data;
419 }
420
421 /**
422 * Get all the orders from database using a date query
423 * for limitation.
424 *
425 * @param array $data
426 * @return array
427 */
428 public function get_orders($data = array()) {
429 if (empty($data) || !function_exists('wc_get_orders')) return null;
430 $orders = [];
431 $time = Helper::generate_time_string($data);
432 $from = strtotime(date('Y-m-d h:i', $time));
433 $status = !empty($data['order_status']) ? $data['order_status'] : ['wc-completed', 'wc-processing'];
434 $wc_orders = \wc_get_orders([
435 'status' => $status,
436 'date_created' => '>' . $from,
437 'numberposts' => isset($data['display_last']) ? intval($data['display_last']) : 10,
438 ]);
439 foreach ($wc_orders as $order) {
440 $items = $order->get_items();
441 foreach ($items as $item) {
442 $tutor_product = metadata_exists('post', $item->get_product_id(), "_tutor_product");
443 if ($tutor_product) {
444 continue;
445 }
446 $orders[$order->get_id() . '-' . $item->get_id()] = $this->ordered_product($item->get_id(), $item, $order);
447 }
448 }
449 return $orders;
450 }
451 /**
452 * Limit entry by selected status;
453 *
454 * @param bool $return
455 * @param array $entry
456 * @param array $settings
457 * @return boolean
458 */
459 public function check_order_status($return, $entry, $settings){
460 $done = !empty($settings['order_status']) ? $settings['order_status'] : ['wc-completed', 'wc-processing'];
461 if(!in_array($entry['data']['status'], $done)){
462 return false;
463 }
464
465 return $return;
466 }
467
468
469 public function wpml_translate($entry, $settings) {
470 if(!empty($entry['product_id'])){
471 $product_id = apply_filters( 'wpml_object_id', $entry['product_id'], 'product', false);
472 $product = wc_get_product($product_id);
473 if($product){
474 // $current_lang = apply_filters( 'wpml_current_language', NULL );
475
476 $entry['product_id'] = $product_id;
477 $entry['title'] = $product->get_name();
478 }
479 }
480
481 return $entry;
482 }
483
484
485 public function product_link($link, $post, $entry) {
486 if(!empty($entry['product_id']) && !empty( $post['link_type'] ) && $post['link_type'] === 'product_page' ){
487 $link = get_permalink($entry['product_id']);
488 }
489 return $link;
490 }
491
492 public function multiorder_combine($data, $settings) {
493 $should_combine = apply_filters('nx_should_combine', true, $data, $settings);
494 if (!$should_combine || empty($settings['combine_multiorder']) || intval($settings['combine_multiorder']) != 1 ) {
495 return $data;
496 }
497 $items = [];
498 $item_counts = [];
499 foreach ($data as $key => $item) {
500 $order_id = isset( $item['order_id'] ) ? $item['order_id'] : ( isset( $item['id'] ) ? $item['id'] : null );
501 if( $order_id === null ) {
502 continue;
503 }
504 if (!isset($items[$order_id])) {
505 $items[$order_id] = $item;
506 } else {
507 $item_counts[$order_id] = isset($item_counts[$order_id]) ? ++$item_counts[$order_id] : 1;
508 }
509 }
510
511 $products_more_title = isset($settings['combine_multiorder_text']) && !empty($settings['combine_multiorder_text']) ? __($settings['combine_multiorder_text'], 'notificationx') : __('more products', 'notificationx');
512 foreach ($item_counts as $key => $item) {
513 // translators: %1$s: title, %2$s: number of product, %3$s: Combine Multi Order Text.
514 $items[$key]['title'] = sprintf(__('%1$s & %2$s %3$s', 'notificationx'), $items[$key]['title'], $item, $products_more_title);
515 }
516
517 // @todo maybe sort
518 return $items;
519 }
520
521 /**
522 * Image action callback
523 * @param array $image_data
524 * @param array $data
525 * @param stdClass $settings
526 * @return array
527 */
528 public function notification_image($image_data, $data, $settings) {
529 if (!$settings['show_default_image'] && $settings['show_notification_image'] === 'featured_image') {
530 $id = $this->product_img_id( $data );
531 if ( ! empty( $id ) ) {
532 $image_size = (string) Settings::get_instance()->get('settings.notification_image_size', '100_100');
533 $image_size = explode('_', $image_size);
534 $width = 100;
535 $height = 100;
536 if( !empty( $image_size[0] ) && !empty( $image_size[1] ) ) {
537 $width = !empty($image_size[0]) ? $image_size[0] : 100;
538 $height = !empty( $image_size[1] ) ? $image_size[1] : 100;
539 }
540 $product_image = wp_get_attachment_image_src(
541 get_post_thumbnail_id($id),
542 [$width, $height],
543 false
544 );
545 $image_data['url'] = is_array($product_image) ? $product_image[0] : '';
546 }
547 }
548 return $image_data;
549 }
550
551 private function product_img_id( $data ) {
552 if (!empty($data['var_product_id']) && has_post_thumbnail($data['var_product_id'])) {
553 return $data['var_product_id'];
554 } elseif (!empty($data['product_id']) && has_post_thumbnail($data['product_id'])) {
555 return $data['product_id'];
556 }
557 }
558
559 public function fallback_data($data, $entry) {
560 $data['name'] = __('Someone', 'notificationx');
561 $data['first_name'] = __('Someone', 'notificationx');
562 $data['last_name'] = __('Someone', 'notificationx');
563 $data['anonymous_title'] = __('Anonymous Product', 'notificationx');
564 if(empty($entry['product_title']) && !empty($entry['title'])){
565 $data['product_title'] = $entry['title'];
566 }
567 return $data;
568 }
569
570 public function doc(){
571 return sprintf(__('<p>Make sure that you have <a target="_blank" href="%1$s">WooCommerce installed & activated</a> to use this campaign. For further assistance, check out our step by step <a target="_blank" href="%2$s">documentation</a>.</p>
572 <p>🎦 <a href="%3$s" target="_blank">Watch video tutorial</a> to learn quickly</p>
573 <p>⭐ NotificationX Integration with WooCommerce</p>
574 <p><strong>Recommended Blog:</strong></p>
575 <p>🔥 Why NotificationX is The <a target="_blank" href="%4$s">Best FOMO and Social Proof Plugin</a> for WooCommerce?</p>
576 <p>🚀 How to <a target="_blank" href="%5$s">boost WooCommerce Sales</a> Using NotificationX</p>', 'notificationx'),
577 'https://wordpress.org/plugins/woocommerce/',
578 'https://notificationx.com/docs/woocommerce-sales-notifications/',
579 'https://www.youtube.com/watch?v=dVthd36hJ-E&t=1s',
580 'https://notificationx.com/integrations/woocommerce/',
581 'https://notificationx.com/blog/best-fomo-and-social-proof-plugin-for-woocommerce/'
582 );
583 }
584
585 }
586