PluginProbe
Whols – Wholesale Prices and B2B Store Solution for WooCommerce / trunk
Whols – Wholesale Prices and B2B Store Solution for WooCommerce vtrunk
2.4.12 2.4.11 trunk 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 All 55 releases
whols / includes / functions.php

functions.php in Whols – Wholesale Prices and B2B Store Solution for WooCommerce trunk, at includes/functions.php

1,544 lines 58.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Whols Functions
4 *
5 * Necessary functions of the plugin.
6 *
7 * @since 1.0.0
8 */
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13
14 /**
15 * Get global options value.
16 *
17 * @since 1.0.0
18 *
19 * @param string $option_name Option name.
20 * @param null $default Default value.
21 *
22 * @return string|null
23 */
24 if( !function_exists('whols_get_option') ){
25 function whols_get_option( $option_name = '', $default = null, $check_empty = false ) {
26 $options = get_option( 'whols_options' );
27
28 if( $check_empty && empty($options[$option_name]) ){
29 return $default;
30 }
31
32 return ( isset( $options[$option_name] ) ) ? $options[$option_name] : $default;
33 }
34 }
35
36 /**
37 * Resolve a redirect setting to a URL.
38 *
39 * These settings store a page ID. Installs created before the field became a
40 * page selector stored a full URL instead, so non-numeric values are passed
41 * through unchanged.
42 *
43 * @since 2.4.11
44 *
45 * @param string $option_name Option name holding the page ID or legacy URL.
46 *
47 * @return string Resolved URL, or an empty string when unset or the page is gone.
48 */
49 if( !function_exists('whols_get_redirect_url') ){
50 function whols_get_redirect_url( $option_name ){
51 $value = whols_get_option( $option_name );
52
53 if( empty($value) ){
54 return '';
55 }
56
57 // Legacy value: the field used to accept a raw URL.
58 if( !is_numeric($value) ){
59 return esc_url_raw( $value );
60 }
61
62 $permalink = get_permalink( (int) $value );
63
64 return $permalink ? $permalink : '';
65 }
66 }
67
68 /**
69 * Get term meta value.
70 *
71 * @since 1.0.0
72 *
73 * @param string $term_id Term ID
74 * @param null $meta_opt_name Meta key name
75 *
76 * @return string
77 */
78 if( !function_exists('whols_get_term_meta') ){
79 function whols_get_term_meta( $term_id, $meta_opt_name ){
80 $meta_value = get_term_meta( $term_id, $meta_opt_name, true );
81
82 return $meta_value;
83 }
84 }
85
86 /**
87 * List payment gateways.
88 *
89 * @since 1.0.0
90 *
91 * @return array
92 */
93 if(!function_exists('whols_get_payment_gateways')){
94 function whols_get_payment_gateways(){
95 // Sample payment gateways, windcave gateway was causing fatal error
96 $gateway_list = array(
97 'cod' => __('Cash on Delivery', 'whols'),
98 'cheque' => __('Cheque Payment', 'whols'),
99 'paypal' => __('PayPal', 'whols'),
100 );
101
102 return $gateway_list;
103 }
104 }
105
106 /**
107 * List given taxonomy terms
108 *
109 * @since 1.0.0
110 *
111 * @return array
112 */
113 if( !function_exists('whols_get_taxonomy_terms') ){
114 function whols_get_taxonomy_terms( $taxonomy = 'whols_role_cat' ){
115 if( class_exists('WP_Term_Query') ){
116 $term_query = new WP_Term_Query(array(
117 'taxonomy' => $taxonomy,
118 'hide_empty' => false,
119 ));
120
121 $term_list = array();
122 foreach ( $term_query->get_terms() as $term ) {
123 $term_list[ $term->slug ] = $term->name;
124 }
125
126 return $term_list;
127 }
128
129 return array();
130 }
131 }
132
133 /**
134 * Validate user registration process
135 *
136 * @since 1.0.0
137 *
138 * @return true|json_message
139 */
140 if( !function_exists('whols_get_user_reg_validation_status') ){
141 function whols_get_user_reg_validation_status( $posted_data ){
142 // Empty check
143 $empty_fields = array();
144 foreach( $posted_data as $key => $value ){
145 $key = str_replace('_whols_', '', $key);
146 $fields = whols_get_registration_fields();
147 $field_info = !empty($fields[$key]) ? $fields[$key] : array();
148 $required = !empty($field_info['required']) ? $field_info['required'] : false;
149
150 if($field_info){
151 if( !$value && $required ){
152 $empty_fields[] = $field_info['label'];
153 }
154 }
155 }
156
157 if($empty_fields){
158 return json_encode( [
159 'registerauth' => false,
160 'message' => implode(', ', $empty_fields) . esc_html__( ' cannot be empty.', 'whols')
161 ] );
162 }
163
164 if( !empty( $posted_data['reg_username'] ) ){
165
166 if ( 4 > strlen( $posted_data['reg_username'] ) ) {
167 return json_encode( [
168 'registerauth' =>false,
169 'message'=> esc_html__('Username too short. At least 4 characters is required', 'whols')
170 ] );
171 }
172
173 if ( username_exists( $posted_data['reg_username'] ) ){
174 return json_encode( [
175 'registerauth' =>false,
176 'message'=> esc_html__('Sorry, that username already exists!', 'whols')
177 ] );
178 }
179
180 if ( !validate_username( $posted_data['reg_username'] ) ) {
181 return json_encode( [
182 'registerauth' =>false,
183 'message'=> esc_html__('Sorry, the username you entered is not valid', 'whols') ]
184 );
185 }
186
187 }
188
189 if( !empty( $posted_data['reg_password'] ) ){
190
191 if ( 5 > strlen( $posted_data['reg_password'] ) ) {
192 return json_encode( [
193 'registerauth' =>false,
194 'message'=> esc_html__('Password length must be greater than 5', 'whols') ]
195 );
196 }
197
198 }
199
200 if( !empty( $posted_data['reg_email'] ) ){
201
202 if ( !is_email( $posted_data['reg_email'] ) ) {
203 return json_encode( [
204 'registerauth' =>false,
205 'message'=> esc_html__('Email is not valid', 'whols') ]
206 );
207 }
208
209 if ( email_exists( $posted_data['reg_email'] ) ) {
210 return json_encode( [
211 'registerauth' =>false,
212 'message'=> esc_html__('Email Already in Use', 'whols') ]
213 );
214 }
215
216 }
217
218 return true;
219
220 }
221 }
222
223 /**
224 * Return wholesaler price
225 *
226 * @param $price_type Price type. Can be 'flat_rate' or 'percentage'.
227 * @param $price_value e.g. '14' or '10:14', formate like this '10:14' where 10 is min and 14 refers to the max price.
228 * @param $product Product object
229 *
230 * @since 1.0.0
231 */
232 if( !function_exists('whols_get_wholesaler_price') ){
233 function whols_get_wholesaler_price( $price_type, $price_value, $product = '' ){
234 $product_type = $product->get_type();
235 $retailer_price = $product->get_regular_price();
236
237 if( $product_type == 'simple' ){
238 if( $price_type == 'flat_rate'){
239 $wholesale_price_info = apply_filters( 'whols_override_wholesale_price', array(
240 'price' => $price_value,
241 ), $product ); // For multicurrency support
242
243 $price_value = $wholesale_price_info['price'];
244
245 $wholesaler_price = wc_price( wc_get_price_to_display( $product, array(
246 'price' => $price_value
247 )) ) . $product->get_price_suffix( $price_value );
248 } else {
249 $wholesaler_price = whols_get_percent_of( $retailer_price, $price_value );
250 $wholesaler_price = wc_price( wc_get_price_to_display( $product, array(
251 'price' => $wholesaler_price
252 )) ) . $product->get_price_suffix( $wholesaler_price );
253 }
254 } elseif( $product_type = 'variable' ){
255
256 if( $price_type == 'flat_rate'){
257 $prices = $product->get_variation_prices( true );
258 $min_price = current( $prices['price'] );
259 $max_price = end( $prices['price'] );
260 $variable_has_no_range = $min_price == $max_price;
261
262 $price = explode(':', $price_value);
263 if( $price_value && count($price) > 1 ){
264 $wholesale_price_info = apply_filters( 'whols_override_wholesale_price', array(
265 'min_price' => $price[0],
266 'max_price' => $price[1],
267 ), $product ); // For multicurrency support
268
269 $wholesale_min_price = $wholesale_price_info['min_price'];
270 $wholesale_max_price = $wholesale_price_info['max_price'];
271
272 // When both price is same, show only one price
273 if( $variable_has_no_range && $wholesale_min_price == $wholesale_max_price ){
274 $wholesaler_price = wc_price( $wholesale_min_price );
275 } else {
276 $wholesaler_price = wc_price( $wholesale_min_price ).' - '. wc_price( $wholesale_max_price );
277 }
278 } else {
279 $wholesaler_price = wc_price( $price_value );
280 }
281 } else {
282 $min_variation_price = $product->get_variation_price();
283 $max_variation_price = $product->get_variation_price( 'max' );
284
285 $wholesaler_min_variation_price = whols_get_percent_of( $min_variation_price, $price_value );
286 $wholesaler_max_variation_price = whols_get_percent_of( $max_variation_price, $price_value );
287
288 if( $wholesaler_min_variation_price == $wholesaler_max_variation_price ){
289 $wholesaler_price = wc_price( $wholesaler_min_variation_price );
290 } else{
291 $wholesaler_price = wc_price( $wholesaler_min_variation_price ).' - '. wc_price( $wholesaler_max_variation_price );
292 }
293 }
294 }
295
296 return $wholesaler_price;
297 }
298 }
299 /**
300 * Check if the current user is wholesaler
301 *
302 * @since 1.0.0
303 *
304 * @return true|false
305 */
306 if( !function_exists('whols_is_wholesaler') ){
307 function whols_is_wholesaler( $user_id = '' ){
308 $show_wholesale_price_for = whols_get_option('show_wholesale_price_for');
309 if( $show_wholesale_price_for == 'all_users' ){
310 return true;
311 }
312
313 if( empty($user_id) ){
314 $user_id = get_current_user_id();
315 }
316
317 if( $user_id ){
318 $user_obj = get_user_by( 'id', $user_id );
319 $user_roles = array_flip( $user_obj->roles );
320
321 if( array_intersect_key( $user_roles, whols_get_taxonomy_terms()) ){
322 return true;
323 }
324 }
325
326 return false;
327 }
328 }
329
330 /**
331 * It returns an array of the current user's roles
332 *
333 * @param user_id The ID of the user you want to get the roles for. If left blank, it will default to
334 * the current user.
335 *
336 * @return array of the current user's roles with key => role_name pair.
337 */
338 if( !function_exists('whols_get_current_user_roles') ){
339 function whols_get_current_user_roles( $user_id = '' ){
340 $user_roles = array();
341
342 if( empty($user_id) ){
343 $user_id = get_current_user_id();
344 }
345
346 if( $user_id ){
347 $user_obj = get_user_by( 'id', $user_id );
348 $user_roles = $user_obj->roles;
349 }
350
351 return $user_roles;
352 }
353 }
354
355 /**
356 * Return price saving (discount) info
357 *
358 * @since 1.0.0
359 */
360 if( !function_exists('whols_get_price_save_info') ){
361 function whols_get_price_save_info( $price_type, $price_value, $product = '' ){
362 $product_type = $product->get_type();
363 $retailer_price = $product->get_regular_price();
364 $save_info = '';
365 $saving_message = '';
366
367 if( $product_type == 'simple' ){
368 if( $price_type == 'flat_rate' ){
369 $wholesale_price_info = apply_filters( 'whols_override_wholesale_price', array(
370 'price' => $price_value
371 ), $product ); // For multicurrency
372
373 $price_value = $wholesale_price_info['price'];
374
375 $save_price = (float) $retailer_price - (float) $price_value;
376 $save_info = $save_price >= 1 ? wc_price( $save_price ) . ' ('. round(whols_get_discount_percent( $retailer_price, $price_value )) .'%)' : '';
377 } else {
378 $new_wholesaler_price = whols_get_percent_of( $retailer_price, $price_value );
379 $save_price = (float) $retailer_price - (float) $new_wholesaler_price;
380 $save_info = $save_price >= 1 ? wc_price( $save_price ) . ' ('. round(whols_get_discount_percent( $retailer_price, $new_wholesaler_price )) .'%)' : '';
381
382 }
383 } elseif( $product_type == 'variable' ){
384 $old_min_price = $product->get_variation_price();
385 $old_max_price = $product->get_variation_price( 'max' );
386
387 if( $price_type == 'flat_rate' ){
388 $price = explode(':', $price_value);
389
390 if( $price_value && count($price) == 1 ){
391 $save_info = '';
392 }elseif( $price_value && count($price) > 1 ){
393 $wholesale_price_info = apply_filters( 'whols_override_wholesale_price', array(
394 'min_price' => $price[0],
395 'max_price' => $price[1],
396 ), $product ); // For multicurrency
397
398 $new_min_price = $wholesale_price_info['min_price'];
399 $new_max_price = $wholesale_price_info['max_price'];
400
401 $discount1 = round(whols_get_discount_percent( $old_min_price, $new_min_price ));
402 $discount2 = round(whols_get_discount_percent( $old_max_price, $new_max_price ));
403
404 $upto_text = apply_filters('whols_label_upto', __('Upto ', 'whols'));
405
406 if( $new_min_price == $new_max_price ){
407 $save_info = $upto_text . $discount2 .'%';
408 } elseif( $discount1 == $discount2 ){
409
410 $save_info = $discount1 < 1 ? '' : $discount1 .'%';
411
412 } elseif( $discount1 > $discount2 ){
413
414 $save_info = $discount2 < 1 ?
415 $upto_text . $discount1 .'%' :
416 $discount2 .'% - '. $discount1 .'%';
417
418 } elseif( $discount2 > $discount1 ) {
419
420 $save_info = $discount1 < 1 ?
421 $upto_text . $discount2 .'%' :
422 $discount1 .'% - '. $discount2 .'%';
423
424 }
425 } else {
426 $min_variation_price = $product->get_variation_price();
427 $max_variation_price = $product->get_variation_price('max');
428 $save_info = round(whols_get_discount_percent( $min_variation_price, $price_value )) .'% - '. round(whols_get_discount_percent( $max_variation_price, $price_value )) .'%';
429 }
430 } else {
431 $save_info = (100 - (int)$price_value) . '%';
432 }
433 }
434
435 if( $save_info ){
436 $discount_label_options = whols_get_option( 'discount_label_options' );
437 $discount_percent_custom_label = $discount_label_options['discount_percent_custom_label'];
438
439 $saving_message .= '<span class="whols_label">';
440 $saving_message .= '<span class="whols_label_left">';
441 $saving_message .= $discount_percent_custom_label ? esc_html( $discount_percent_custom_label ) : esc_html__( 'Save: ', 'whols' );
442 $saving_message .= '</span>';
443 $saving_message .= '<span class="whols_label_right">';
444 $saving_message .= $save_info;
445 $saving_message .= '</span>';
446 $saving_message .= '</span>';
447 }
448
449 return $saving_message;
450 }
451 }
452
453 /**
454 * Calculate discount compared by old & new price
455 *
456 * @since 1.0.0
457 */
458 if( !function_exists('whols_get_discount_percent') ){
459 function whols_get_discount_percent( $old_price, $new_price ){
460 $decrease = (float) $old_price - (float) $new_price;
461
462 if( $decrease > 0 ){
463 $percent = $decrease / (float) $old_price * 100;
464 return (float) $percent;
465 }
466
467 return 0;
468 }
469 }
470
471 /**
472 * Calculate percent of an amount. e.g: 50 percent of 30 = 15
473 *
474 * @since 1.0.0
475 */
476 if( !function_exists('whols_get_percent_of') ){
477 function whols_get_percent_of( $x, $percent_limit ){
478 if($x){
479 $percent = (float) $percent_limit / 100;
480 return $x * $percent;
481 }
482
483 return 0;
484 }
485 }
486
487 if( !function_exists('whols_get_registration_fields') ){
488 function whols_get_registration_fields(){
489 $default_fields = array(
490 'reg_name' => array(
491 'label' => __('Name', 'whols'),
492 'type' => 'text',
493 'required' => true,
494 'placeholder' => __('Name', 'whols'),
495 'value' => '',
496 'priority' => 10,
497 ),
498 'reg_username' => array(
499 'label' => __('Username', 'whols'),
500 'type' => 'text',
501 'required' => true,
502 'placeholder' => __('Username', 'whols'),
503 'value' => '',
504 'priority' => 20,
505 ),
506 'reg_email' => array(
507 'label' => __('Email', 'whols'),
508 'type' => 'email',
509 'required' => true,
510 'placeholder' => __('Your Email', 'whols'),
511 'value' => '',
512 'priority' => 30,
513 ),
514 'reg_password' => array(
515 'label' => __('Password', 'whols'),
516 'type' => 'password',
517 'required' => true,
518 'placeholder' => __('Your Password', 'whols'),
519 'value' => '',
520 'priority' => 40,
521 ),
522 );
523
524 // When the plugin updated to the version 1.1.7 the registration fields value doesn't updated unless click on the save button.
525 $fields = array();
526 if( whols_get_option('registration_fields') && is_array(whols_get_option('registration_fields')) ){
527 $registration_fields = (array) whols_get_option('registration_fields');
528
529 // Prepare and array of the fields with field key as key
530 foreach ($registration_fields as $key => $field) {
531 $field_name = $field['field'];
532 unset($field['field']);
533
534 if( isset($default_fields[$field_name]) ){
535 $fields[$field_name] = wp_parse_args( $field, $default_fields[$field_name] );
536 }
537
538 $i = (int) $key + 1;
539 $fields[$field_name]['priority'] = 10 * $i;
540 $fields[$field_name]['class'] = !empty($fields[$field_name]['class']) ? explode(' ', $fields[$field_name]['class']) : array();
541 }
542 } else {
543 $fields = $default_fields;
544 }
545
546 // required field properties are label, type, order, is_additional
547 $fields = apply_filters( 'whols_registration_fields', $fields );
548
549 // Ordering support
550 // Each field must have the order value otherwise ordering support won't work
551 $order = array_column($fields, 'priority');
552 if(count($order) == count($fields)){
553 array_multisort($order, SORT_ASC, $fields);
554 }
555
556 return $fields;
557 }
558 }
559
560
561 if ( !function_exists( 'whols_form_field' ) ) {
562
563 /**
564 * Outputs registration form field.
565 *
566 * @param string $key Key.
567 * @param mixed $args Arguments.
568 * @param string $value (default: null).
569 * @return string
570 */
571 function whols_form_field( $key, $args, $value = null ) {
572 // add __ with the name to detect the field as additional
573 if ( !empty($args['is_additional']) && $args['is_additional'] ) {
574 $key = '_whols_' . $key;
575 }
576
577 $defaults = array(
578 'type' => 'text',
579 'label' => '',
580 'description' => '',
581 'placeholder' => '',
582 'maxlength' => false,
583 'required' => false,
584 'autocomplete' => false,
585 'id' => $key,
586 'class' => array(),
587 'label_class' => array(),
588 'input_class' => array(),
589 'return' => false,
590 'options' => array(),
591 'custom_attributes' => array(),
592 'validate' => array(),
593 'default' => '',
594 'autofocus' => '',
595 'priority' => '99',
596 'is_additional' => false,
597 );
598
599 $args = wp_parse_args( $args, $defaults );
600
601 if ( $args['required'] ) {
602 $args['class'][] = 'validate-required';
603 $required = '&nbsp;<abbr class="required" title="' . esc_attr__( 'required', 'whols' ) . '">*</abbr>';
604 } else {
605 $required = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'whols' ) . ')</span>';
606 }
607
608 if ( is_string( $args['label_class'] ) ) {
609 $args['label_class'] = array( $args['label_class'] );
610 }
611
612 if ( is_null( $value ) ) {
613 $value = $args['default'];
614 }
615
616 // Custom attribute handling.
617 $custom_attributes = array();
618 $args['custom_attributes'] = array_filter( (array) $args['custom_attributes'], 'strlen' );
619
620 if ( $args['maxlength'] ) {
621 $args['custom_attributes']['maxlength'] = absint( $args['maxlength'] );
622 }
623
624 if ( ! empty( $args['autocomplete'] ) ) {
625 $args['custom_attributes']['autocomplete'] = $args['autocomplete'];
626 }
627
628 if ( true === $args['autofocus'] ) {
629 $args['custom_attributes']['autofocus'] = 'autofocus';
630 }
631
632 if ( $args['description'] ) {
633 $args['custom_attributes']['aria-describedby'] = $args['id'] . '-description';
634 }
635
636 if ( ! empty( $args['custom_attributes'] ) && is_array( $args['custom_attributes'] ) ) {
637 foreach ( $args['custom_attributes'] as $attribute => $attribute_value ) {
638 $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
639 }
640 }
641
642 if ( ! empty( $args['validate'] ) ) {
643 foreach ( $args['validate'] as $validate ) {
644 $args['class'][] = 'validate-' . $validate;
645 }
646 }
647
648 $field = '';
649 $label_id = $args['id'];
650 $sort = $args['priority'] ? $args['priority'] : '';
651 $field_container = '<p class="whols-form-row %1$s" id="%2$s" data-priority="' . esc_attr( $sort ) . '">%3$s</p>';
652
653 switch ( $args['type'] ) {
654 case 'textarea':
655 $field .= '<textarea name="' . esc_attr( $key ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" ' . ( empty( $args['custom_attributes']['rows'] ) ? ' rows="2"' : '' ) . ( empty( $args['custom_attributes']['cols'] ) ? ' cols="5"' : '' ) . implode( ' ', $custom_attributes ) . '>' . esc_textarea( $value ) . '</textarea>';
656
657 break;
658 case 'text':
659 case 'password':
660 case 'datetime':
661 case 'datetime-local':
662 case 'date':
663 case 'month':
664 case 'time':
665 case 'week':
666 case 'number':
667 case 'email':
668 case 'url':
669 case 'tel':
670 $field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-text ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" placeholder="' . esc_attr( $args['placeholder'] ) . '" value="' . esc_attr( $value ) . '" required="' . esc_attr( $args['required'] ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
671
672 break;
673 case 'hidden':
674 $field .= '<input type="' . esc_attr( $args['type'] ) . '" class="input-hidden ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="' . esc_attr( $value ) . '" ' . implode( ' ', $custom_attributes ) . ' />';
675
676 break;
677 case 'select':
678 $field = '';
679 $options = '';
680
681 if ( ! empty( $args['options'] ) ) {
682 foreach ( $args['options'] as $option_key => $option_text ) {
683 if ( '' === $option_key ) {
684 // If we have a blank option, select2 needs a placeholder.
685 if ( empty( $args['placeholder'] ) ) {
686 $args['placeholder'] = $option_text ? $option_text : __( 'Choose an option', 'whols' );
687 }
688 $custom_attributes[] = 'data-allow_clear="true"';
689 }
690 $options .= '<option value="' . esc_attr( $option_key ) . '" ' . selected( $value, $option_key, false ) . '>' . esc_html( $option_text ) . '</option>';
691 }
692
693 $field .= '<select name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" class="select ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . ' data-placeholder="' . esc_attr( $args['placeholder'] ) . '">
694 ' . $options . '
695 </select>';
696 }
697
698 break;
699 case 'radio':
700 $label_id .= '_' . current( array_keys( $args['options'] ) );
701
702 if ( ! empty( $args['options'] ) ) {
703 foreach ( $args['options'] as $option_key => $option_text ) {
704 $field .= '<input type="radio" class="input-radio ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" value="' . esc_attr( $option_key ) . '" name="' . esc_attr( $key ) . '" ' . implode( ' ', $custom_attributes ) . ' id="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '"' . checked( $value, $option_key, false ) . ' />';
705 $field .= '<label for="' . esc_attr( $args['id'] ) . '_' . esc_attr( $option_key ) . '" class="radio ' . implode( ' ', $args['label_class'] ) . '">' . esc_html( $option_text ) . '</label>';
706 }
707 }
708
709 break;
710
711 case 'checkbox':
712 $field = '<input type="' . esc_attr( $args['type'] ) . '" class="input-checkbox ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="1" ' . checked( $value, 1, false ) . ' /> ';
713
714 break;
715
716 }
717
718 if ( ! empty( $field ) ) {
719 $field_html = '';
720
721 if ( $args['label'] ) {
722 $field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . wp_kses_post( $args['label'] ) . $required . '</label>';
723 }
724
725 $field_html .= '<span class="whols-input-wrapper">' . $field;
726
727 if ( $args['description'] ) {
728 $field_html .= '<span class="description" id="' . esc_attr( $args['id'] ) . '-description" aria-hidden="true">' . wp_kses_post( $args['description'] ) . '</span>';
729 }
730
731 $field_html .= '</span>';
732
733
734 $args['class'][] = 'type--'. $args['type'];
735 $container_class = esc_attr( implode( ' ', $args['class'] ) );
736 $container_id = esc_attr( $args['id'] ) . '_field';
737 $field = sprintf( $field_container, $container_class, $container_id, $field_html );
738 }
739
740 if ( $args['return'] ) {
741 return $field;
742 } else {
743 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
744 echo $field;
745 }
746 }
747 }
748
749 /**
750 * backrward compatibility for str_starts_with function, since it is introduced in php 8.0
751 * @return string
752 */
753 if (!function_exists('str_starts_with')) {
754 function str_starts_with($haystack, $needle) {
755 return (string)$needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
756 }
757 }
758
759 if( !function_exists('whols_is_on_wholesale') ){
760 function whols_is_on_wholesale( $product_id = '', $variation_id = '' ){
761 $status = false;
762
763 $p_id = $variation_id ? $variation_id : $product_id;
764 $product = wc_get_product($p_id);
765
766 $product_type = $product->get_type();
767 $pricing_model = 'single_role';
768 $price_type = '';
769 $price_value = '';
770 $minimum_quantity = '';
771
772 if( $pricing_model == 'single_role' ){
773 $price_type_1_properties = whols_get_option( 'price_type_1_properties' );
774 $enable_this_pricing = $price_type_1_properties['enable_this_pricing'];
775 $price_type = $price_type_1_properties['price_type'];
776 $price_value = $price_type_1_properties['price_value'];
777 $minimum_quantity = $price_type_1_properties['minimum_quantity'];
778
779 // override from category level
780 $term_meta = '';
781 $current_product_category_ids = $product->get_category_ids();
782 foreach( $current_product_category_ids as $id ){
783 $term_meta = whols_get_term_meta( $id, 'whols_product_category_meta' );
784
785 if( isset( $term_meta[ 'price_type_1_properties' ] ) && $term_meta[ 'price_type_1_properties' ] ){
786 $price_type_1_properties = $term_meta[ 'price_type_1_properties' ];
787 if( $price_type_1_properties['enable_this_pricing'] ){
788 $enable_this_pricing = $price_type_1_properties['enable_this_pricing'];
789 $price_type = $price_type_1_properties['price_type'];
790 $price_value = $price_type_1_properties['price_value'];
791 $minimum_quantity = $price_type_1_properties['minimum_quantity'];
792
793 break;
794 }
795 }
796 }
797
798 // override from simple product level
799 $price_type_1_properties = get_post_meta( $product->get_id(), '_whols_price_type_1_properties', true);
800 if( ($price_type_1_properties && $product_type == 'simple') ){
801
802 $price_type_1_properties_arr = explode( ':', $price_type_1_properties );
803 if( isset( $price_type_1_properties_arr[0] ) ){
804
805 $enable_this_pricing = true;
806 $price_type = 'flat_rate';
807 $price_value = $price_type_1_properties_arr[0];
808 $minimum_quantity = !empty($price_type_1_properties_arr[1]) ? $price_type_1_properties_arr[1] : '';
809
810 }
811 }
812
813 // override from variation product level
814 if( $product_type == 'variation' ){
815 $new_min_price = 9999999999;
816 $new_max_price = 0;
817 $has_price = false;
818
819 $regular_price = (float) get_post_meta( $p_id, '_price', true );
820 $meta_info = get_post_meta( $p_id, '_whols_price_type_1_properties', true );
821 $meta_arr = explode( ':', $meta_info );
822 $meta_price = (float) $meta_arr[0];
823 $minimum_quantity = '';
824
825 if( $meta_price ){
826 $has_price = true;
827 }
828
829 if( $regular_price && $meta_price ){
830 if( $meta_price && $meta_price != 0 && $meta_price < $new_min_price ){
831 $new_min_price = $meta_price;
832 }
833
834 if( $meta_price && $meta_price > $new_max_price ){
835 $new_max_price = $meta_price;
836 }
837 } else{
838 if( $regular_price < $new_min_price ){
839 $new_min_price = $regular_price;
840 }
841
842 if( $regular_price > $new_max_price ){
843 $new_max_price = $regular_price;
844 }
845 }
846
847 if( $has_price ){
848 $enable_this_pricing = true;
849 $price_type = 'flat_rate';
850 $price_value = "$new_min_price:$new_max_price";
851 $minimum_quantity = !empty($meta_arr['1']) ? (int) $meta_arr['1'] : 0;
852 }
853 }else if( $product_type == 'variable' ){
854 $old_min_price = $product->get_variation_price('min');
855 $old_max_price = $product->get_variation_price('max');
856 $current_product_variations = $product->get_available_variations();
857
858 $new_min_price = 9999999999;
859 $new_max_price = 0;
860 $has_price = false;
861
862 foreach( $current_product_variations as $variation ){
863 $regular_price = (float) get_post_meta( $variation['variation_id'], '_price', true );
864 $meta_info = get_post_meta( $variation['variation_id'], '_whols_price_type_1_properties', true );
865 $meta_arr = explode( ':', $meta_info );
866 $meta_price = (float) $meta_arr[0];
867 if( $meta_price ){
868 $has_price = true;
869 }
870
871 if( $regular_price && $meta_price ){
872 if( $meta_price && $meta_price != 0 && $meta_price < $new_min_price ){
873 $new_min_price = $meta_price;
874 }
875
876 if( $meta_price && $meta_price > $new_max_price ){
877 $new_max_price = $meta_price;
878 }
879 } else{
880 if( $regular_price < $new_min_price ){
881 $new_min_price = $regular_price;
882 }
883
884 if( $regular_price > $new_max_price ){
885 $new_max_price = $regular_price;
886 }
887 }
888 }
889
890 if( $has_price ){
891 $enable_this_pricing = true;
892 $price_type = 'flat_rate';
893 $price_value = "$new_min_price:$new_max_price";
894 $minimum_quantity = '';
895 }
896 } // product type
897
898 return array(
899 'enable_this_pricing' => $enable_this_pricing,
900 'price_type' => $price_type,
901 'price_value' => $price_value,
902 'minimum_quantity' => $minimum_quantity
903 );
904 } // pricing model
905 }
906 }
907
908 /**
909 * Accepts either normal product or a variation product.
910 * Returns the informations about the product info whether it should or not qualified as a wholesale product.
911 *
912 * @param $product_data Product/Variation object.
913 *
914 * @return array(
915 'enable_this_pricing' => '', bool
916 'price_type' => '', flat_rate/percent
917 'price_value' => '', input_price/new_input_min_price:new_input_max_price
918 'minimum_quantity' => '', number
919 )
920 */
921 if( !function_exists('whols_get_product_status') ){
922 function whols_get_product_status( $product_data ) {
923 // current user role
924 $current_user_roles = whols_get_current_user_roles();
925 $current_user_role = isset( $current_user_roles[0] ) ? $current_user_roles[0] : '';
926 $current_user_id = get_current_user_id();
927
928 $pricing_model = whols_get_option( 'pricing_model' );
929 $term_meta = '';
930
931 $price_type_1_properties = whols_get_option( 'price_type_1_properties' );
932 $enable_this_pricing = $price_type_1_properties['enable_this_pricing'];
933 $price_type = $price_type_1_properties['price_type'];
934 $price_value = $price_type_1_properties['price_value'];
935 $minimum_quantity = $price_type_1_properties['minimum_quantity'];
936
937 if( $product_data->is_type('variation') ){
938 $product = wc_get_product( $product_data->get_parent_id() );
939 } else {
940 $product = $product_data;
941 }
942
943 $tiers = array();
944
945 $current_product_category_ids = $product->get_category_ids();
946
947 foreach( $current_product_category_ids as $id ){
948 $term_meta = whols_get_term_meta( $id, 'whols_product_category_meta' );
949
950 if( $pricing_model == 'single_role' ){
951 $price_type_1_properties = whols_get_option( 'price_type_1_properties' );
952 $enable_this_pricing = $price_type_1_properties['enable_this_pricing'];
953 $price_type = $price_type_1_properties['price_type'];
954 $price_value = $price_type_1_properties['price_value'];
955 $minimum_quantity = $price_type_1_properties['minimum_quantity'];
956
957 // override from category level
958 if( isset( $term_meta[ 'price_type_1_properties' ] ) && $term_meta[ 'price_type_1_properties' ] ){
959 $price_type_1_properties = $term_meta[ 'price_type_1_properties' ];
960 if( $price_type_1_properties['enable_this_pricing'] ){
961 $enable_this_pricing = $price_type_1_properties['enable_this_pricing'];
962 $price_type = $price_type_1_properties['price_type'];
963 $price_value = $price_type_1_properties['price_value'];
964 $minimum_quantity = $price_type_1_properties['minimum_quantity'];
965
966 break;
967 }
968 }
969 } else { // Multiple role
970 $price_type_2_properties = whols_get_option( 'price_type_2_properties' );
971 $show_wholesale_price_for = whols_get_option('show_wholesale_price_for');
972 $select_role_for_all_users_price = whols_get_option('select_role_for_all_users_price');
973
974 // Support for test mode
975 if( $show_wholesale_price_for == 'administrator' ){
976 $select_role_for_all_users_price = 'whols_default_role';
977 }
978
979 if( in_array($show_wholesale_price_for, array('all_users', 'administrator')) && $select_role_for_all_users_price ){
980
981 $enable_this_pricing = isset($price_type_2_properties[$select_role_for_all_users_price. '__enable_this_pricing']) ? $price_type_2_properties[$select_role_for_all_users_price. '__enable_this_pricing'] : '';
982 $price_type = isset($price_type_2_properties[$select_role_for_all_users_price. '__price_type']) ? $price_type_2_properties[$select_role_for_all_users_price. '__price_type'] : '';
983 $price_value = isset($price_type_2_properties[$select_role_for_all_users_price. '__price_value']) ? $price_type_2_properties[$select_role_for_all_users_price. '__price_value'] : '';
984 $minimum_quantity = isset($price_type_2_properties[$select_role_for_all_users_price. '__minimum_quantity']) ? $price_type_2_properties[$select_role_for_all_users_price. '__minimum_quantity'] : '';
985
986 } else { // only wholesalers
987 $enable_this_pricing = isset($price_type_2_properties[$current_user_role. '__enable_this_pricing']) ? $price_type_2_properties[$current_user_role. '__enable_this_pricing'] : '';
988 $price_type = isset($price_type_2_properties[$current_user_role. '__price_type']) ? $price_type_2_properties[$current_user_role. '__price_type'] : '';
989 $price_value = isset($price_type_2_properties[$current_user_role. '__price_value']) ? $price_type_2_properties[$current_user_role. '__price_value'] : '';
990 $minimum_quantity = isset($price_type_2_properties[$current_user_role. '__minimum_quantity']) ? $price_type_2_properties[$current_user_role. '__minimum_quantity'] : '';
991 }
992
993
994 // override from category level
995 if( isset( $term_meta[ 'price_type_2_properties' ] ) && $term_meta[ 'price_type_2_properties' ] ){
996 $price_type_2_properties = $term_meta[ 'price_type_2_properties' ];
997 if( isset($price_type_2_properties[$current_user_role. '__enable_this_pricing']) && $price_type_2_properties[$current_user_role. '__enable_this_pricing'] ){
998 $enable_this_pricing = $price_type_2_properties[$current_user_role. '__enable_this_pricing'];
999 $price_type = $price_type_2_properties[$current_user_role. '__price_type'];
1000 $price_value = $price_type_2_properties[$current_user_role. '__price_value'];
1001 $minimum_quantity = $price_type_2_properties[$current_user_role. '__minimum_quantity'];
1002
1003 break;
1004 }
1005 }
1006 }
1007 }
1008
1009 // override from product level
1010 if( $pricing_model == 'single_role' ){
1011 $price_type_1_properties_meta = get_post_meta( $product_data->get_id(), '_whols_price_type_1_properties', true);
1012
1013 if( $price_type_1_properties_meta ){
1014 $price_type_1_properties_arr = explode( ':', $price_type_1_properties_meta );
1015
1016 if( $price_type_1_properties_arr[0] ){
1017 $enable_this_pricing = true;
1018 $price_type = 'flat_rate';
1019 $price_value = $price_type_1_properties_arr[0];
1020 $minimum_quantity = $price_type_1_properties_arr[1] ? $price_type_1_properties_arr[1] : 1;
1021 }
1022 }
1023
1024 } elseif( $pricing_model == 'multiple_role' ){
1025 // Fix, Show price for all user doesn't show for multiple role
1026 if( $show_wholesale_price_for == 'all_users' && $select_role_for_all_users_price ){
1027 $current_user_roles[] = $select_role_for_all_users_price;
1028 }
1029
1030 $price_type_2_properties_meta = get_post_meta( $product_data->get_id(), '_whols_price_type_2_properties', true);
1031
1032 if( $price_type_2_properties_meta ){
1033 $roles_data_list = explode( ';', $price_type_2_properties_meta );
1034
1035 foreach( $roles_data_list as $role_data ){
1036 $role_data_arr = explode( ':', $role_data );
1037
1038 if(
1039 ( is_admin() && !wp_doing_ajax() ) || // Don't need to check role for admin
1040 in_array( 'any_role', $role_data_arr ) ||
1041 array_intersect($current_user_roles, $role_data_arr) // Fix, Show price for all user doesn't show for multiple role
1042 ){
1043 if( !empty($role_data_arr[1]) ){
1044 $enable_this_pricing = true;
1045 $price_type = 'flat_rate';
1046 $price_value = $role_data_arr[1];
1047 $minimum_quantity = $role_data_arr[2] ? $role_data_arr[2] : 1;
1048 }
1049
1050 break;
1051 }
1052 }
1053 }
1054
1055 } // elseif multiple_role
1056
1057 return array(
1058 'enable_this_pricing' => $enable_this_pricing,
1059 'price_type' => $price_type,
1060 'price_value' => $price_value,
1061 'minimum_quantity' => $minimum_quantity,
1062 'tiers' => $tiers
1063 );
1064 }
1065 }
1066
1067 /**
1068 * Sanitize checkbox
1069 *
1070 * @since 1.0.0
1071 */
1072 if( !function_exists('whols_sanitize_checkbox') ){
1073 function whols_sanitize_checkbox( $input ){
1074 //returns true if checkbox is checked
1075 return ( isset( $input ) ? 'yes' : 'no' );
1076 }
1077 }
1078
1079 /**
1080 * Capabilities
1081 */
1082 if( !function_exists('whols_get_capabilities') ){
1083 function whols_get_capabilities(){
1084 $capabilities = array(
1085 'manage_settings' => 'manage_options',
1086 'manage_roles' => 'manage_options',
1087 'manage_requests' => 'manage_options'
1088 );
1089
1090 return apply_filters('whols_capabilities', $capabilities );
1091 }
1092 }
1093
1094 if( !function_exists('whols_insert_element_after_specific_array_key') ){
1095
1096 /**
1097 * It takes an array, a specific key, and a new element, and inserts the new element after the
1098 * specific key
1099 *
1100 * @param array arr The array where you want to insert the new element.
1101 * @param string specific_key The key of the array element you want to insert after.
1102 * @param array new_element The new element to be inserted.
1103 *
1104 * @return array
1105 */
1106 function whols_insert_element_after_specific_array_key( $arr, $specific_key, $new_element ){
1107 if( !is_array($arr) || !is_array($new_element) ){
1108 return $arr;
1109 }
1110
1111 if( !array_key_exists( $specific_key, $arr ) ){
1112 return $arr;
1113 }
1114
1115 $array_keys = array_keys( $arr );
1116 $start = (int) array_search( $specific_key, $array_keys, true ) + 1; // Offset
1117
1118 $spliced_arr = array_splice( $arr, $start );
1119 $new_element_key = $new_element['key'];
1120 $arr[$new_element_key] = $new_element['value'];
1121 $new_arr = array_merge( $arr, $spliced_arr );
1122
1123 return $new_arr;
1124 }
1125 }
1126
1127 /**
1128 * Get all WordPress pages for dropdown options
1129 *
1130 * @return array Array of pages with id as key and title as value
1131 */
1132 function whols_pages_dropdown_options( $post_type = 'page' ) {
1133 $query = new WP_Query(array(
1134 'post_type' => $post_type,
1135 'post_status' => 'publish',
1136 'posts_per_page' => 200, // @todo apply_filter
1137 'orderby' => 'title',
1138 'order' => 'ASC',
1139 ));
1140
1141 $options = array();
1142
1143 if ($query->have_posts()) {
1144 while ($query->have_posts()) {
1145 $query->the_post();
1146 $options[] = array(
1147 'id' => (string) get_the_ID(),
1148 'title' => get_the_title()
1149 );
1150 }
1151 wp_reset_postdata();
1152 }
1153
1154 return $options;
1155 }
1156
1157 if( !function_exists('whols_product_category_dropdown_options') ){
1158 function whols_product_category_dropdown_options(){
1159 $query = new WP_Term_Query( array(
1160 'taxonomy' => 'product_cat',
1161 'hide_empty' => true,
1162 ) );
1163
1164 $options = array();
1165
1166 if ( ! is_wp_error( $query ) && !empty( $query->terms ) ) {
1167 foreach ( $query->terms as $item ) {
1168 $options[$item->slug] = $item->name;
1169 }
1170 }
1171
1172 return $options;
1173 }
1174 }
1175
1176 /**
1177 * Get all WordPress terms for dropdown options
1178 *
1179 * @param string $taxonomy Taxonomy name
1180 * @return array Array of terms with id as key and title as value
1181 */
1182 function whols_terms_dropdown_options($taxonomy = 'product_cat') {
1183 $terms = get_terms(array(
1184 'taxonomy' => $taxonomy,
1185 'hide_empty' => true,
1186 'orderby' => 'name',
1187 'order' => 'ASC',
1188 'number' => 0, // Get all terms
1189 'fields' => 'all', // Get all term fields
1190 'update_term_meta_cache' => false // Don't fetch term meta if not needed
1191 ));
1192
1193 if (is_wp_error($terms)) {
1194 return array();
1195 }
1196
1197 // Use array_map for better performance than foreach
1198 return array_map(function($term) {
1199 return array(
1200 'id' => (string) $term->term_id,
1201 'slug' => $term->slug,
1202 'title' => $term->name
1203 );
1204 }, $terms);
1205 }
1206
1207 /**
1208 * Get users dropdown options
1209 *
1210 * @return array
1211 */
1212 if( !function_exists('whols_users_dropdown_options') ){
1213 function whols_users_dropdown_options() {
1214 $users = get_users([
1215 'fields' => ['ID', 'display_name'],
1216 'number' => 200,
1217 'orderby' => 'display_name',
1218 'order' => 'ASC'
1219 ]);
1220
1221 return array_reduce($users, function($options, $user) {
1222 $options[$user->ID] = $user->display_name;
1223 return $options;
1224 }, []);
1225 }
1226 }
1227
1228 if( !function_exists('whols_roles_dropdown_options') ){
1229 function whols_roles_dropdown_options(){
1230 $query = new WP_Term_Query( array(
1231 'taxonomy' => 'whols_role_cat',
1232 'hide_empty' => false,
1233 ) );
1234
1235 $options = array();
1236
1237 if ( ! is_wp_error( $query ) && !empty( $query->terms ) ) {
1238 foreach ( $query->terms as $item ) {
1239 $options[$item->slug] = $item->name;
1240 }
1241 }
1242
1243 return $options;
1244 }
1245 }
1246
1247 /**
1248 * Get countries
1249 *
1250 * @return array
1251 */
1252 function whols_get_countries() {
1253 $countries = array();
1254
1255 if( class_exists('WC_Countries') ){
1256 if( WC()->countries ){
1257 $countries = WC()->countries->get_countries();
1258 } else {
1259 $c = new WC_Countries(); // WC_Countries instance created after woocommerce_init hook
1260 $countries = $c->get_countries();
1261 }
1262 }
1263
1264 return $countries;
1265 }
1266
1267 /**
1268 * List enabled payment gateways.
1269 *
1270 * @return array
1271 */
1272 function whols_get_enabled_payment_gateways(){
1273 global $wpdb;
1274
1275 $query = "SELECT option_name, option_value
1276 FROM {$wpdb->prefix}options
1277 WHERE option_name LIKE 'woocommerce_%_settings'
1278 AND option_name NOT LIKE 'woocommerce_settings_%'";
1279
1280 $results = $wpdb->get_results($query); // @phpcs:ignore
1281
1282 $enabled_gateways = array();
1283 foreach ($results as $result) {
1284 // Extract gateway ID from option name (e.g., 'woocommerce_cod_settings' -> 'cod')
1285 $gateway_id = str_replace(['woocommerce_', '_settings'], '', $result->option_name);
1286
1287 // Unserialize the settings
1288 $settings = maybe_unserialize($result->option_value);
1289
1290 $enabled = isset($settings['enabled']) ? $settings['enabled'] : 'no';
1291 if (is_array($settings) && isset($settings['title']) && $enabled == 'yes') {
1292 $enabled_gateways[$gateway_id] = $settings['title'];
1293 }
1294 }
1295
1296 return $enabled_gateways;
1297 }
1298
1299 if( !function_exists('whols_is_wholesale_priced') ){
1300 /**
1301 * Check wheather the give products is applicable for wholesale price or not.
1302 *
1303 * @param product_id The product ID of the item being added to the cart.
1304 * @param qty The quantity of the product being added to the cart.
1305 *
1306 * @return array|bool
1307 */
1308 function whols_is_wholesale_priced( $product_id, $qty ){
1309 $product_data = wc_get_product($product_id);
1310 $variation_id = '';
1311
1312 if($product_data->is_type('simple')){
1313 $product_id = $product_data->get_id();
1314 } elseif( $product_data->is_type('variation') ){
1315 $product_id = $product_data->get_parent_id();
1316 $variation_id = $product_data->get_id();
1317 }
1318
1319 $wholesale_status = whols_is_on_wholesale( $product_id, $variation_id );
1320 $enable_this_pricing = $wholesale_status['enable_this_pricing'];
1321 $price_value = $wholesale_status['price_value'];
1322 $minimum_quantity = $wholesale_status['minimum_quantity'];
1323
1324 if( whols_is_wholesaler(get_current_user_id()) && $enable_this_pricing && $price_value && $qty >= $minimum_quantity ){
1325 // Returned array in case we need to pass any other data in the future
1326 return array(
1327 'wholesale_priced' => 'yes'
1328 );
1329 }
1330
1331 return false;
1332 }
1333 }
1334
1335 if( !function_exists('whols_get_product_price_tiers') ){
1336 /**
1337 * It takes a product object and a boolean value as parameters and returns an array of price tiers
1338 *
1339 * @param product The product/variation object
1340 * @param return_prepared_prices If set to true, the function will return an array of min qty =>
1341 * price pairs. If set to false, it will return an array of arrays, each containing the role, price
1342 * and min qty.
1343 *
1344 * @return array
1345 */
1346 function whols_get_product_price_tiers( $product, $return_prepared_prices ){
1347 $tiers = array(); // tier_qty => price pair
1348
1349 // For price tyep 2 / multiple role
1350 $price_type_2_properties = get_post_meta( $product->get_id(), '_whols_price_type_2_properties', true);
1351 $roles_data_list = explode( ';', $price_type_2_properties );
1352
1353 // Prepare price tiers for multiple role
1354 if( $price_type_2_properties && whols_get_option('pricing_model') == 'multiple_role' ){
1355 foreach( $roles_data_list as $role_data ){
1356 if(
1357 ( is_admin() && !wp_doing_ajax() ) || // Don't need to check for admin
1358 in_array( 'any_role', explode( ':', $role_data ) ) ||
1359 array_intersect( whols_get_current_user_roles(), explode( ':', $role_data ))
1360 ){
1361
1362 $price_data = explode( ':', $role_data );
1363
1364 if( $return_prepared_prices ){
1365 $min_qty = !empty($price_data[2]) ? $price_data[2] : 1;
1366
1367 // Make sure min qty & price has given
1368 // then take price to the array
1369 if( !empty($price_data[1]) ){
1370 $tiers[$min_qty] = $price_data[1];
1371 }
1372 } else {
1373 $tiers[] = explode( ':', $role_data );
1374 }
1375 }
1376 }
1377 }
1378
1379 return $tiers;
1380 }
1381 }
1382
1383 /**
1384 * Returns true if the request is a non-legacy REST API request.
1385 *
1386 * Legacy REST requests should still run some extra code for backwards compatibility.
1387 *
1388 * @todo: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061.
1389 *
1390 * @return bool
1391 */
1392 function whols_is_rest_api_request() {
1393 if ( empty( $_SERVER['REQUEST_URI'] ) ) {
1394 return false;
1395 }
1396
1397 $rest_prefix = trailingslashit( rest_get_url_prefix() );
1398 $is_rest_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix ) ); // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1399
1400 return $is_rest_api_request;
1401 }
1402
1403 /**
1404 * What type of request is this?
1405 *
1406 * @param string $type admin, ajax, cron or frontend.
1407 * @return bool
1408 */
1409 function whols_is_request( $type ) {
1410 switch ( $type ) {
1411 case 'admin':
1412 return is_admin();
1413 case 'ajax':
1414 return defined( 'DOING_AJAX' );
1415 case 'cron':
1416 return defined( 'DOING_CRON' );
1417 case 'frontend':
1418 return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' ) && ! whols_is_rest_api_request();
1419 }
1420 }
1421
1422 /**
1423 * Get conversation count
1424 *
1425 * @param string $status
1426 *
1427 * @return int
1428 */
1429 function whols_get_conversation_count( $status = 'pending' ) {
1430 $args = array(
1431 'post_type' => 'whols_conversation',
1432 'post_status' => 'pending',
1433 'numberposts' => -1,
1434 'fields' => 'ids',
1435 );
1436
1437 $conversations = get_posts( $args );
1438
1439 return count( $conversations );
1440 }
1441
1442 /**
1443 * Retrieves remote data and caches it using WordPress transients.
1444 *
1445 * This function fetches remote data from a specified URL and caches it using a transient.
1446 * If the transient is already set and not expired, it returns the cached data.
1447 * Otherwise, it makes a remote request to fetch the data, caches it, and then returns it.
1448 *
1449 * @param string|null $version The version of the data to retrieve. It is used to flush the transient cache when the version changes.
1450 * @return array The remote data retrieved and cached.
1451 */
1452 function whols_get_plugin_remote_data($version = null) {
1453 $transient_key = 'whols_remote_data_v' . $version;
1454 $frequency_to_update = 2 * DAY_IN_SECONDS; // N Days later fetch data again
1455 $remote_url = 'https://feed.hasthemes.com/notices/whols.json';
1456 // $remote_url = WHOLS_URL . '/remote.json';
1457
1458 $remote_banner_data = [];
1459 $transient_data = get_transient($transient_key);
1460
1461 // Check if we should force update or if transient is not set
1462 if ( $transient_data ) {
1463 $remote_banner_data = $transient_data;
1464 } elseif( false === $transient_data ) {
1465 $remote_banner_req = wp_remote_get($remote_url, array(
1466 'timeout' => 10,
1467 'sslverify' => false,
1468 ));
1469
1470 // If request success, set data to transient
1471 if ( !is_wp_error($remote_banner_req) && $remote_banner_req['response']['code'] == 200 ) {
1472 $remote_banner_data = json_decode($remote_banner_req['body'], true);
1473
1474 // Store in version-specific transient if force update, otherwise use regular transient
1475 set_transient($transient_key, $remote_banner_data, $frequency_to_update);
1476 } else {
1477 // Cache failure to avoid retrying on every page load
1478 set_transient($transient_key, [], 6 * HOUR_IN_SECONDS);
1479 }
1480 }
1481
1482 return $remote_banner_data;
1483 }
1484
1485 /**
1486 * If flush rewrite rules flag is set, then flush the rewrite rules, and remove the flag.
1487 *
1488 * @return void
1489 */
1490 if(!function_exists('whols_maybe_flush_rewrite_rules')){
1491 function whols_maybe_flush_rewrite_rules() {
1492 if (get_option('whols_flush_rewrite_rules_flag')) {
1493 flush_rewrite_rules(); // Flush the rewrite rules
1494 delete_option('whols_flush_rewrite_rules_flag'); // Remove the flag
1495 }
1496 }
1497 }
1498
1499 /**
1500 * Include a plugin file safely
1501 *
1502 * @param string $path File path relative to plugin directory e.g: 'includes/functions/actions.php'
1503 * @return bool True if file was included successfully, false otherwise
1504 */
1505 if( !function_exists('whols_include_plugin_file') ){
1506 function whols_include_plugin_file( $path ){
1507 // Get plugin directory path
1508 $plugin_dir = plugin_dir_path( Whols\PL_FILE );
1509
1510 // Clean the path and ensure it's relative
1511 $path = ltrim( str_replace('\\', '/', $path), '/' );
1512
1513 // Build full path
1514 $full_path = $plugin_dir . $path;
1515
1516 // Validate path is within plugin directory
1517 if( strpos(realpath($full_path), realpath($plugin_dir)) !== 0 ){
1518 return false;
1519 }
1520
1521 if( file_exists($full_path) ){
1522 return include $full_path;
1523 }
1524
1525 return false;
1526 }
1527 }
1528
1529 /**
1530 * Check if the onboarding should be shown
1531 *
1532 * @return bool
1533 */
1534 function whols_should_show_onboarding() {
1535 $installed_timestamp = get_option( 'whols_installed' );
1536 $cutoff_timestamp = strtotime('2025-05-15'); // Show onboarding after this date
1537 $onboarding_status = get_option('whols_onboarding');
1538
1539 if ( $installed_timestamp > $cutoff_timestamp && $onboarding_status != 'complete' ) {
1540 return true;
1541 }
1542
1543 return false;
1544 }