PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.1.13
ShopBuilder – WooCommerce Builder For Elementor v2.1.13
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
← All changes | app/Helpers/Fns.php +713 -93 2.0.1 → 2.1.13 View file →
@@ -11,14 +11,15 @@
11 11 if ( ! defined( 'ABSPATH' ) ) {
12 12 exit( 'This script cannot be accessed directly.' );
13 13 }
14 14
15 +use WC_Product;
16 +use WC_Product_Query;
15 17 use Elementor\Icons_Manager;
16 18 use RadiusTheme\SB\Models\ReSizer;
17 19 use RadiusTheme\SB\Models\DataModel;
18 20 use RadiusTheme\SB\Models\ModuleList;
19 21 use RadiusTheme\SB\Models\Settings;
20 -use WC_Product;
21 22
22 23 /**
23 24 * Fns class
24 25 */
@@ -24,8 +25,13 @@
24 25 */
25 26 class Fns {
26 27
27 28 /**
29 + * @var array
30 + */
31 + private static $cache = [];
32 +
33 + /**
28 34 * Verify nonce.
29 35 *
30 36 * @return bool
31 37 */
@@ -38,9 +44,58 @@
38 44
39 45 return false;
40 46 }
41 47
48 + public static function get_nonce() {
49 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
50 + return isset( $_REQUEST[ rtsb()->nonceId ] ) ? sanitize_text_field( $_REQUEST[ rtsb()->nonceId ] ) : null;
51 + }
52 +
53 +
42 54 /**
55 + * Set Cookie or Session
56 + *
57 + * @param $name
58 + * @param $value
59 + */
60 + public static function setSession( $name, $value ) {
61 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
62 + session_start();
63 + $_SESSION[ $name ] = $value;
64 + // session_write_close();
65 + } else {
66 + $_SESSION[ $name ] = $value;
67 + }
68 + }
69 + /**
70 + * Get Cookie or Session
71 + *
72 + * @param $name
73 + *
74 + * @return bool
75 + */
76 + public static function getSession( $name ) {
77 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
78 + session_start();
79 + }
80 + return $_SESSION[ $name ] ?? null;
81 + }
82 + /**
83 + * Remove Session Variable
84 + *
85 + * @param string $name The name of the session variable to remove.
86 + */
87 + public static function removeSession( $name ) {
88 + if ( ! headers_sent() && session_status() == PHP_SESSION_NONE ) {
89 + session_start();
90 + unset( $_SESSION[ $name ] );
91 + // session_write_close();
92 + } else {
93 + unset( $_SESSION[ $name ] );
94 + }
95 + }
96 +
97 + /**
43 98 * @param $plugin_slug
44 99 *
45 100 * @return bool
46 101 */
@@ -114,14 +169,15 @@
114 169
115 170 /**
116 171 * @param string $template_name Template name.
117 172 * @param string $template_path Template path. (default: '').
118 - * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin
173 + * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin.
119 174 *
120 175 * @return mixed|void
121 176 */
122 177 public static function locate_template( $template_name, $template_path = '', $plugin_path = '' ) {
123 - $template_name = $template_name . '.php';
178 + $template_name = self::sanitize_file_name( $template_name ) . '.php';
179 +
124 180 if ( ! $template_path ) {
125 181 $template_path = rtsb()->get_template_path();
126 182 }
127 183 if ( ! $plugin_path ) {
@@ -164,8 +220,19 @@
164 220 return apply_filters( 'rtsb/core/locate_template', $located, $template_name );
165 221 }
166 222
167 223 /**
224 + * Remove any character that is not alphanumeric, /, _, or -.
225 + *
226 + * @param string $name Name to sanitize.
227 + *
228 + * @return array|string|string[]|null
229 + */
230 + private static function sanitize_file_name( $name ) {
231 + return preg_replace( '/[^a-zA-Z0-9\/_\-]/', '', $name );
232 + }
233 +
234 + /**
168 235 * Template Content
169 236 *
170 237 * @param string $template_name Template name.
171 238 * @param array $args Arguments. (default: array).
@@ -198,8 +265,9 @@
198 265 }
199 266
200 267 do_action( 'rtsb/core/before_template_part', $template_name, $located, $args );
201 268 include $located;
269 +
202 270 do_action( 'rtsb/core/after_template_part', $template_name, $located, $args );
203 271
204 272 if ( $return ) {
205 273 return ob_get_clean();
@@ -312,10 +380,11 @@
312 380 $cache_key = 'rtsb_prepared_product_id';
313 381 $_post_id = wp_cache_get( $cache_key );
314 382
315 383 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
384 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
316 385 $_post_id = $wpdb->get_var(
317 - $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status = %s", 'product', 'publish' )
386 + $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status IN('publish', 'draft')", 'product' )
318 387 );
319 388 wp_cache_set( $cache_key, $_post_id, '', 12 * HOUR_IN_SECONDS );
320 389 }
321 390
@@ -322,19 +391,27 @@
322 391 return $_post_id;
323 392 }
324 393
325 394 /**
326 - * Get the product function.
395 + * Get the product function. Only used in single page widgets.
327 396 *
328 397 * @return object
329 398 */
330 399 public static function get_product() {
331 - global $post, $product;
400 + global $product;
401 +
332 402 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
403 + // do_action( 'rtsb_before_product_template_render' );.
333 404 return $product;
334 405 }
335 -
336 - return wc_get_product( self::get_prepared_product_id() );
406 + $cache_key = 'prepared_product_for_preview';
407 + if ( isset( self::$cache[ $cache_key ] ) ) {
408 + return self::$cache[ $cache_key ];
409 + }
410 + $product = wc_get_product( self::get_prepared_product_id() );
411 + self::$cache[ $cache_key ] = $product;
412 + do_action( 'rtsb_before_product_template_render' );
413 + return $product;
337 414 }
338 415
339 416 /**
340 417 * Error function
@@ -346,9 +423,9 @@
346 423 * @return void
347 424 */
348 425 public static function doing_it_wrong( $function, $message, $version ) {
349 426 $message .= ' Backtrace: ' . wp_debug_backtrace_summary();
350 - _doing_it_wrong( $function, $message, $version );
427 + _doing_it_wrong( esc_html( $function ), wp_kses_post( $message ), esc_html( $version ) );
351 428 }
352 429
353 430 /**
354 431 * @return array
@@ -479,13 +556,14 @@
479 556 }
480 557
481 558 if ( strlen( $page_content ) > 0 ) {
482 559 // Search for an existing page with the specified page content (typically a shortcode).
483 - $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
560 + $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
561 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
484 562 $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_content LIKE %s LIMIT 1;", "%{$shortcode}%" ) );
485 563 } else {
486 564 // Search for an existing page with the specified page slug.
487 - $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) );
565 + $valid_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status NOT IN ( 'pending', 'trash', 'future', 'auto-draft' ) AND post_name = %s LIMIT 1;", $slug ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
488 566 }
489 567
490 568 $valid_page_found = apply_filters( 'rtsb/core/create_page_id', $valid_page_found, $slug, $page_content, $options );
491 569
@@ -507,12 +585,12 @@
507 585
508 586 // Search for a matching valid trashed page.
509 587 if ( strlen( $page_content ) > 0 ) {
510 588 // Search for an existing page with the specified page content (typically a shortcode).
511 - $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) );
589 + $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_content LIKE %s LIMIT 1;", "%{$page_content}%" ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
512 590 } else {
513 591 // Search for an existing page with the specified page slug.
514 - $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) );
592 + $trashed_page_found = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type='page' AND post_status = 'trash' AND post_name = %s LIMIT 1;", $slug ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
515 593 }
516 594
517 595 if ( $trashed_page_found ) {
518 596 $page_id = $trashed_page_found;
@@ -883,8 +961,38 @@
883 961 return $allowed_html;
884 962 }
885 963
886 964 /**
965 + * Safe print a validated HTML tag.
966 + *
967 + * @param string $tag
968 + */
969 + public static function print_validated_html_tag( $tag ) {
970 + $allowed_html_wrapper_tags = [
971 + 'a',
972 + 'article',
973 + 'aside',
974 + 'button',
975 + 'div',
976 + 'footer',
977 + 'h1',
978 + 'h2',
979 + 'h3',
980 + 'h4',
981 + 'h5',
982 + 'h6',
983 + 'header',
984 + 'main',
985 + 'nav',
986 + 'p',
987 + 'section',
988 + 'span',
989 + ];
990 +
991 + self::print_html( in_array( strtolower( $tag ), $allowed_html_wrapper_tags, true ) ? $tag : 'div' );
992 + }
993 +
994 + /**
887 995 * Insert Some array element
888 996 *
889 997 * @param [type] $key The elements will insert nearby this key.
890 998 * @param [type] $main_array Original array.
@@ -960,9 +1068,8 @@
960 1068 'slider-layout2' => esc_html__( 'Slider Layout 2', 'shopbuilder' ),
961 1069 'category-single-layout1' => esc_html__( 'Category Single Layout 1', 'shopbuilder' ),
962 1070 'category-layout1' => esc_html__( 'Category Layout 1', 'shopbuilder' ),
963 1071 'category-layout2' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
964 - 'category-layout3' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
965 1072 ];
966 1073
967 1074 return apply_filters( 'rtsb/elements/elementor/free_layouts', $layouts );
968 1075 }
@@ -980,10 +1087,19 @@
980 1087
981 1088 if ( empty( $taxonomy ) ) {
982 1089 return $terms;
983 1090 }
1091 + $cache_key = 'rtsb_get_all_terms_by_tax_name_' . $taxonomy;
1092 + if ( isset( self::$cache[ $cache_key ] ) ) {
1093 + return self::$cache[ $cache_key ];
1094 + }
984 1095
985 - $termList = get_terms( [ $taxonomy ], [ 'hide_empty' => 0 ] );
1096 + $termList = get_terms(
1097 + [
1098 + 'taxonomy' => $taxonomy,
1099 + 'hide_empty' => false,
1100 + ]
1101 + );
986 1102
987 1103 if ( is_array( $termList ) && ! empty( $termList ) && empty( $termList['errors'] ) ) {
988 1104 if ( $placeholder ) {
989 1105 $terms = [
@@ -994,9 +1110,9 @@
994 1110 foreach ( $termList as $term ) {
995 1111 $terms[ $term->term_id ] = esc_html( $term->name );
996 1112 }
997 1113 }
998 -
1114 + self::$cache[ $cache_key ] = $terms;
999 1115 return $terms;
1000 1116 }
1001 1117
1002 1118 /**
@@ -1012,9 +1128,9 @@
1012 1128
1013 1129 if ( $attributes ) {
1014 1130 foreach ( $attributes as $tax ) {
1015 1131 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1016 - $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ), 'orderby=name&hide_empty=0' );
1132 + $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ) );
1017 1133 }
1018 1134 }
1019 1135 }
1020 1136
@@ -1106,8 +1222,12 @@
1106 1222 *
1107 1223 * @return int|array
1108 1224 */
1109 1225 public static function get_terms( $taxonomy, $first_term = false, $only_parents = false, $return_slug = false ) {
1226 + if ( ! is_admin() ) {
1227 + return [];
1228 + }
1229 +
1110 1230 // TODO:: Return Slug always true for all settings.
1111 1231 $term_list = [];
1112 1232 $args = [
1113 1233 'taxonomy' => $taxonomy,
@@ -1141,30 +1261,87 @@
1141 1261
1142 1262 /**
1143 1263 * Get product rating.
1144 1264 *
1265 + * @param array $args Arguments.
1266 + *
1145 1267 * @return string|void
1146 1268 */
1147 - public static function get_product_rating_html() {
1269 + public static function get_product_rating_html( $args = [] ) {
1148 1270 global $product;
1149 1271
1150 - $html = '';
1151 - $rating_count = $product->get_rating_count();
1152 - $average = $product->get_average_rating();
1272 + $html = '';
1273 + $rating_count = $product->get_rating_count();
1274 + $average_rating = $product->get_average_rating();
1153 1275
1154 - if ( ! $rating_count || empty( wc_get_rating_html( $average, $rating_count ) ) ) {
1155 - return $html;
1276 + if ( ! rtsb()->has_pro() || empty( $args ) ) {
1277 + if ( ! $rating_count || empty( wc_get_rating_html( $average_rating, $rating_count ) ) ) {
1278 + return $html;
1279 + }
1280 +
1281 + $html .= '<div class="product-rating">';
1282 + $html .= wc_get_rating_html( $average_rating, $rating_count );
1283 + $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1284 + $html .= '</div>';
1285 +
1286 + self::print_html( $html, true );
1287 +
1288 + return;
1156 1289 }
1157 1290
1158 - $html .= '<div class="product-rating">';
1159 - $html .= wc_get_rating_html( $average, $rating_count );
1160 - // TODO:: Disable Rating Count option need to apply.
1161 - $html .= ! empty( $html ) ? '<span class="count">(' . $average . ')</span>' : '';
1291 + if ( empty( $rating_count ) && ! $args['show_empty_rating'] ) {
1292 + return '';
1293 + }
1294 +
1295 + $preset = ! empty( $args['preset'] ) ? $args['preset'] : 'preset1';
1296 + $average = $args['show_average_rating'] ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1297 + $count = '';
1298 +
1299 + if ( $args['show_rating_count'] ) {
1300 + $count .= '<div class="rtsb-count">';
1301 + $count .= sprintf(
1302 + /* translators: %s is the number of reviews */
1303 + _n( '%s Review', '%s Reviews', $rating_count, 'shopbuilder' ),
1304 + $rating_count
1305 + );
1306 + $count .= '</div>';
1307 + }
1308 +
1309 + if ( 'preset2' === $preset ) {
1310 + $average = $args['show_average_rating'] ? '<div class="inner-wrapper"><span class="star-icon"></span><span class="average-rating">' . $average_rating . '</span></div>' : '';
1311 + }
1312 +
1313 + $html .= '<div class="product-rating ' . esc_attr( $preset ) . '">';
1314 +
1315 + if ( 'preset1' === $preset ) {
1316 + $html .= wc_get_rating_html( $average_rating, $rating_count );
1317 + $html .= $average;
1318 + $html .= $count;
1319 + } elseif ( 'preset2' === $preset ) {
1320 + $html .= $average;
1321 + $html .= $count;
1322 + }
1323 +
1162 1324 $html .= '</div>';
1163 1325
1164 1326 self::print_html( $html, true );
1165 1327 }
1166 1328
1329 + public static function get_product_simple_rating_html() {
1330 + global $product;
1331 + $html = null;
1332 + $rating_count = $product->get_rating_count();
1333 + $average_rating = $product->get_average_rating();
1334 + $html .= '<div class="product-rating">';
1335 + $html .= wc_get_rating_html( $average_rating, $rating_count );
1336 + $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1337 + $html .= '</div>';
1338 +
1339 + self::print_html( $html, true );
1340 + }
1341 +
1342 +
1343 +
1167 1344 /**
1168 1345 * Text truncation.
1169 1346 *
1170 1347 * @param string $text_to_truncate Text.
@@ -1207,8 +1384,14 @@
1207 1384 *
1208 1385 * @return void
1209 1386 */
1210 1387 public static function get_badge_html( $text, $class = 'fill' ) {
1388 + if ( self::is_module_active( 'product_badges' ) && 'rtsb_yes' === $text ) {
1389 + do_action( 'rtsb/modules/product_badges/frontend/display' );
1390 +
1391 + return;
1392 + }
1393 +
1211 1394 if ( empty( $text ) ) {
1212 1395 return;
1213 1396 }
1214 1397 ob_start();
@@ -1215,10 +1398,9 @@
1215 1398 ?>
1216 1399
1217 1400 <ul class="rtsb-promotion-list">
1218 1401 <li class="rtsb-promotion-list-item">
1219 - <span
1220 - class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1402 + <span class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1221 1403 </li>
1222 1404 </ul>
1223 1405
1224 1406 <?php
@@ -1257,9 +1439,8 @@
1257 1439 <?php
1258 1440 self::print_html( ob_get_clean() );
1259 1441 }
1260 1442
1261 -
1262 1443 /**
1263 1444 * Get Featured Image HTML.
1264 1445 *
1265 1446 * @param string $type Image type.
@@ -1269,12 +1450,13 @@
1269 1450 * @param array $custom_img_size Custom image size.
1270 1451 * @param bool $lazy Lazy load check.
1271 1452 * @param bool $hover Hover image.
1272 1453 * @param bool $gallery Gallery image.
1454 + * @param bool $custom_image_id Custom category image ID.
1273 1455 *
1274 1456 * @return string|null
1275 1457 */
1276 - public static function get_product_image_html( $type = 'product', $post_id = null, $f_img_size = 'medium', $default_img_id = null, $custom_img_size = [], $lazy = false, $hover = false, $gallery = false ) {
1458 + public static function get_product_image_html( $type = 'product', $post_id = null, $f_img_size = 'medium', $default_img_id = null, $custom_img_size = [], $lazy = false, $hover = false, $gallery = false, $custom_image_id = 0 ) {
1277 1459 $img_html = null;
1278 1460 $attachment_id = null;
1279 1461 $c_size = false;
1280 1462 $hover_class = '';
@@ -1298,9 +1480,9 @@
1298 1480 if ( 'product' === $type ) {
1299 1481 $a_id = get_post_thumbnail_id( $post_id );
1300 1482 $post_title = get_the_title( $post_id );
1301 1483 } elseif ( 'category' === $type ) {
1302 - $a_id = get_term_meta( $post_id, 'thumbnail_id', true );
1484 + $a_id = $custom_image_id ? $custom_image_id : get_term_meta( $post_id, 'thumbnail_id', true );
1303 1485 $post_title = get_term( $post_id )->name;
1304 1486 } else {
1305 1487 $a_id = $default_img_id;
1306 1488 }
@@ -1450,8 +1632,20 @@
1450 1632 *
1451 1633 * @return string
1452 1634 */
1453 1635 public static function icons_manager( $control, $class = '', $builder = 'elementor' ): string {
1636 + if ( empty( $control['value'] ) ) {
1637 + return '';
1638 + }
1639 + if ( is_array( $control['value'] ) ) {
1640 + $cache_key = 'icons_managet_' . str_replace( ' ', '', md5( serialize( $control['value'] ) ) );
1641 + } else {
1642 + $cache_key = 'icons_managet_' . str_replace( ' ', '', $control['value'] );
1643 + }
1644 + if ( isset( self::$cache[ $cache_key ] ) ) {
1645 + return self::$cache[ $cache_key ];
1646 + }
1647 +
1454 1648 $attributes = [
1455 1649 'aria-hidden' => 'true',
1456 1650 ];
1457 1651
@@ -1464,9 +1658,11 @@
1464 1658 if ( defined( 'ELEMENTOR_VERSION' ) && ! empty( $control ) && 'elementor' === $builder ) {
1465 1659 Icons_Manager::render_icon( $control, $attributes );
1466 1660 }
1467 1661
1468 - return ob_get_clean();
1662 + $icons = ob_get_clean();
1663 + self::$cache[ $cache_key ] = $icons;
1664 + return $icons;
1469 1665 }
1470 1666
1471 1667 /**
1472 1668 * Get product swatches.
@@ -1504,9 +1700,90 @@
1504 1700 if ( ! $product->is_on_sale() ) {
1505 1701 return '';
1506 1702 }
1507 1703
1508 - $badge_text = '';
1704 + $badge_text = '';
1705 + $percentage = self::calculate_sale_percentage( $product );
1706 +
1707 + if ( $percentage > 0 ) {
1708 + $badge_text = '-' . round( $percentage ) . '%';
1709 + }
1710 +
1711 + if ( 'text' === $type ) {
1712 + $badge_text = $text;
1713 + }
1714 +
1715 + return $badge_text;
1716 + }
1717 +
1718 + /**
1719 + * Get sale badge
1720 + *
1721 + * @param object $product Product Object.
1722 + * @param string $type Badge type.
1723 + * @param string $text Badge text.
1724 + * @param string $out_of_stock_text Out of stock text.
1725 + *
1726 + * @return string
1727 + */
1728 + public static function get_promo_badge( $product, $type, $text, $out_of_stock_text ) {
1729 + $disable_badges = self::get_option( 'general', 'guest_user', 'hide_badges', '' );
1730 + $badges_visibility = rtsb()->has_pro() && ! is_user_logged_in() && $disable_badges;
1731 +
1732 + if ( $badges_visibility ) {
1733 + return '';
1734 + }
1735 +
1736 + if ( is_null( $product ) ) {
1737 + global $product;
1738 + }
1739 +
1740 + if ( ! $product instanceof WC_Product ) {
1741 + return '';
1742 + }
1743 +
1744 + if ( ! $product->is_in_stock() ) {
1745 + return $out_of_stock_text;
1746 + }
1747 +
1748 + if ( ! $product->is_on_sale() ) {
1749 + return '';
1750 + }
1751 +
1752 + $badge_text = '';
1753 + $percentage = self::calculate_sale_percentage( $product );
1754 +
1755 + if ( $percentage > 0 ) {
1756 + $badge_text = '-' . round( $percentage ) . '%';
1757 + }
1758 +
1759 + if ( 'text' === $type ) {
1760 + $badge_text = $text;
1761 + }
1762 +
1763 + return $badge_text;
1764 + }
1765 +
1766 + /**
1767 + * Calculate Sale Percentage
1768 + *
1769 + * @param object $product Product object.
1770 + *
1771 + * @return float|int|string
1772 + */
1773 + public static function calculate_sale_percentage( $product = null ) {
1774 + if ( is_null( $product ) ) {
1775 + global $product;
1776 + }
1777 +
1778 + if ( ! $product instanceof WC_Product ) {
1779 + return '';
1780 + }
1781 +
1782 + if ( ! $product->is_on_sale() ) {
1783 + return '';
1784 + }
1785 +
1509 1786 $percentage = null;
1510 1787 $max_percentage = '';
1511 1788
1512 1789 if ( $product->is_type( 'simple' ) ) {
@@ -1531,16 +1808,12 @@
1531 1808 }
1532 1809 }
1533 1810
1534 1811 if ( $max_percentage > 0 ) {
1535 - $badge_text = '-' . round( $max_percentage ) . '%';
1812 + return round( $max_percentage );
1536 1813 }
1537 1814
1538 - if ( 'text' === $type ) {
1539 - $badge_text = $text;
1540 - }
1541 -
1542 - return $badge_text;
1815 + return 0;
1543 1816 }
1544 1817
1545 1818 /**
1546 1819 * Pagination
@@ -1551,10 +1824,10 @@
1551 1824 *
1552 1825 * @return string
1553 1826 */
1554 1827 public static function custom_pagination( $pages = '', $range = 4, $ajax = false ) {
1555 - $html = null;
1556 - $showitems = ( $range * 2 ) + 1;
1828 + $html = null;
1829 + $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', ( $range * 2 ) + 1 );
1557 1830
1558 1831 global $paged;
1559 1832
1560 1833 if ( is_front_page() ) {
@@ -1582,8 +1855,9 @@
1582 1855
1583 1856 if ( $ajax ) {
1584 1857 $ajaxClass = ' rtsb-pagination-ajax';
1585 1858 $dataAttr = "data-paged='1'";
1859 + $dataAttr .= ' data-visible-range="' . esc_attr( $visible_range ) . '"';
1586 1860 }
1587 1861
1588 1862 if ( 1 !== $pages ) {
1589 1863 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
@@ -1588,29 +1862,29 @@
1588 1862 if ( 1 !== $pages ) {
1589 1863 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
1590 1864 $html .= '<ul class="pagination-list">';
1591 1865
1592 - if ( $paged > 2 && $paged > $range + 1 && $showitems < $pages ) {
1866 + if ( $paged > 2 && $paged > $visible_range + 1 && $visible_range < $pages ) {
1593 1867 $html .= "<li><a data-paged='1' href='" . get_pagenum_link( 1 ) . "' aria-label='First'>&laquo;</a></li>";
1594 1868 }
1595 1869
1596 - if ( $paged > 1 && $showitems < $pages ) {
1870 + if ( $paged > 1 && $visible_range < $pages ) {
1597 1871 $p = $paged - 1;
1598 1872 $html .= "<li><a data-paged='{$p}' href='" . get_pagenum_link( $p ) . "' aria-label='Previous'>&lsaquo;</a></li>";
1599 1873 }
1600 1874
1601 1875 for ( $i = 1; $i <= $pages; $i++ ) {
1602 - if ( 1 != $pages && ( ! ( $i >= $paged + $range + 1 || $i <= $paged - $range - 1 ) || $pages <= $showitems ) ) {
1876 + if ( 1 != $pages && ( ! ( $i >= $paged + $visible_range + 1 || $i <= $paged - $visible_range - 1 ) || $pages <= $visible_range ) ) {
1603 1877 $html .= ( $paged == $i ) ? '<li class="active"><span>' . $i . '</span></li>' : "<li><a data-paged='{$i}' href='" . get_pagenum_link( $i ) . "'>" . $i . '</a></li>';
1604 1878 }
1605 1879 }
1606 1880
1607 - if ( $paged < $pages && $showitems < $pages ) {
1881 + if ( $paged < $pages && $visible_range < $pages ) {
1608 1882 $p = $paged + 1;
1609 1883 $html .= "<li><a data-paged='{$p}' href=\"" . get_pagenum_link( $paged + 1 ) . "\" aria-label='Next'>&rsaquo;</a></li>";
1610 1884 }
1611 1885
1612 - if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $showitems < $pages ) {
1886 + if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $visible_range < $pages ) {
1613 1887 $html .= "<li><a data-paged='{$pages}' href='" . get_pagenum_link( $pages ) . "' aria-label='Last'>&raquo;</a></li>";
1614 1888 }
1615 1889
1616 1890 $html .= '</ul>';
@@ -1666,9 +1940,8 @@
1666 1940 */
1667 1941 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1668 1942 $html .= ob_get_clean();
1669 1943
1670 - // $html .= self::get_action_button_by_type( $items, 'quick_checkout' );
1671 1944 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1672 1945 $html .= self::get_action_button_by_type( $items, 'compare' );
1673 1946 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1674 1947 $html .= '</ul>';
@@ -1681,9 +1954,8 @@
1681 1954 } elseif ( 'preset1' === $preset || 'preset2' === $preset || 'preset4' === $preset ) {
1682 1955 $html .= '<div class="rtsb-action-buttons ' . esc_attr( $class ) . '">';
1683 1956 $html .= '<ul class="rtsb-action-button-list">';
1684 1957 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1685 - // $html .= self::get_action_button_by_type( $items, 'quick_checkout' );
1686 1958
1687 1959 ob_start();
1688 1960 /**
1689 1961 * Additional formatted action buttons hook.
@@ -1970,9 +2242,8 @@
1970 2242 return true;
1971 2243 }
1972 2244 }
1973 2245
1974 -
1975 2246 /***
1976 2247 * Save Settings data
1977 2248 *
1978 2249 * @param $section_id
@@ -1984,10 +2255,9 @@
1984 2255 public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) {
1985 2256 $section_id = ! empty( $section_id ) ? sanitize_text_field( wp_unslash( $section_id ) ) : '';
1986 2257 $block_id = ! empty( $block_id ) ? sanitize_text_field( wp_unslash( $block_id ) ) : '';
1987 2258 $rawOptions = ! empty( $rawOptions ) ? $rawOptions : [];
1988 -
1989 - $results = [
2259 + $results = [
1990 2260 'status' => true,
1991 2261 'message' => '',
1992 2262 ];
1993 2263 if ( ! $section_id || ! $block_id ) {
@@ -2003,9 +2273,9 @@
2003 2273
2004 2274 return $results;
2005 2275 }
2006 2276
2007 - $options = DataModel::source()->get_option( $section_id, [] );
2277 + $options = DataModel::source()->get_option( $section_id, [], false );
2008 2278 $changed = false;
2009 2279 $fields = [];
2010 2280 if ( isset( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) && ! empty( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) ) {
2011 2281 $fields = $sections[ $section_id ]['list'][ $block_id ]['fields'];
@@ -2040,10 +2310,16 @@
2040 2310 } else {
2041 2311 $value = [];
2042 2312 }
2043 2313 } else {
2044 - if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2045 - $value = $field['sanitize_fn']( $raw_value );
2314 + if ( ! empty( $field['sanitize_fn'] ) ) {
2315 + if ( is_callable( $field['sanitize_fn'] ) ) {
2316 + $value = $field['sanitize_fn']( $raw_value );
2317 + } elseif ( 'pass_all' === $field['sanitize_fn'] ) {
2318 + $value = $raw_value;
2319 + } else {
2320 + $value = $field['sanitize_fn']( $raw_value );
2321 + }
2046 2322 } else {
2047 2323 $value = sanitize_text_field( $raw_value );
2048 2324 }
2049 2325 }
@@ -2089,36 +2365,27 @@
2089 2365 * }
2090 2366 * return;
2091 2367 * }
2092 2368 */
2093 -
2094 2369 public static function count_products_by_taxonomies( $terms, $taxonomy = 'product_cat' ) {
2095 2370 if ( empty( $terms ) || ! is_array( $terms ) ) {
2096 2371 return;
2097 2372 }
2098 2373
2099 - $category_ids = array_map(
2100 - function ( $name ) {
2101 - return sanitize_title( $name );
2102 - },
2103 - $terms
2104 - );
2105 -
2106 2374 $args = [
2107 - 'post_type' => 'product',
2108 - 'posts_per_page' => - 1,
2109 - 'tax_query' => [
2110 - [
2111 - 'taxonomy' => $taxonomy,
2112 - 'field' => 'term_id',
2113 - 'terms' => $category_ids,
2114 - ],
2115 - ],
2375 + 'limit' => -1,
2376 + 'return' => 'ids',
2116 2377 ];
2117 2378
2118 - $query = new \WP_Query( $args );
2379 + if ( 'product_cat' === $taxonomy ) {
2380 + $args['product_category_id'] = $terms;
2381 + } else {
2382 + $args['product_tag_id'] = $terms;
2383 + }
2119 2384
2120 - return $query->found_posts;
2385 + $query = new WC_Product_Query( $args );
2386 +
2387 + return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2121 2388 }
2122 2389
2123 2390 /**
2124 2391 * Check if product filters widget has ajax.
@@ -2131,9 +2398,20 @@
2131 2398 if ( empty( $page ) ) {
2132 2399 return false;
2133 2400 }
2134 2401
2135 - $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2402 + if ( 'page' === get_post_type() ) {
2403 + return false;
2404 + }
2405 +
2406 + $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2407 + if ( ! $id ) {
2408 + return false;
2409 + }
2410 + $cache_key = 'product_filters_has_ajax_' . $id;
2411 + if ( isset( self::$cache[ $cache_key ] ) ) {
2412 + return self::$cache[ $cache_key ];
2413 + }
2136 2414 $elmap = ElementorDataMap::instance();
2137 2415 $ajax = [];
2138 2416
2139 2417 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
@@ -2139,8 +2417,9 @@
2139 2417 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2140 2418 $ajax[] = isset( $data['settings']['ajax_mode'] ) ? false : true;
2141 2419 }
2142 2420
2421 + self::$cache[ $cache_key ] = ! empty( $ajax[0] );
2143 2422 return ! empty( $ajax[0] );
2144 2423 }
2145 2424
2146 2425 /**
@@ -2157,9 +2436,11 @@
2157 2436
2158 2437 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2159 2438 $elmap = ElementorDataMap::instance();
2160 2439 $ajax = [];
2161 -
2440 + if ( ! $id ) {
2441 + return false;
2442 + }
2162 2443 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2163 2444 $ajax[] = ! isset( $data['settings']['active_filter'] );
2164 2445
2165 2446 }
@@ -2174,24 +2455,39 @@
2174 2455 *
2175 2456 * @return array An array of product categories with 'value' and 'label' keys.
2176 2457 */
2177 2458 public static function products_category_query( $search_query = null ) {
2178 - global $wpdb;
2459 + if ( ! is_admin() ) {
2460 + return [];
2461 + }
2179 2462
2180 - $sql = "SELECT t.term_id, t.name
2463 + $cache_key = 'rtsb_product_categories_' . md5( serialize( $search_query ) );
2464 +
2465 + if ( isset( self::$cache[ $cache_key ] ) ) {
2466 + return self::$cache[ $cache_key ];
2467 + }
2468 +
2469 + $results = wp_cache_get( $cache_key );
2470 +
2471 + if ( ! $results ) {
2472 + global $wpdb;
2473 + $sql = "SELECT t.term_id, t.name
2181 2474 FROM {$wpdb->terms} t
2182 2475 JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2183 2476 WHERE tt.taxonomy = 'product_cat'";
2184 2477
2185 - if ( $search_query ) {
2186 - $sql .= ' AND t.name LIKE %s';
2187 - $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // Escaping and wildcard
2188 - } else {
2189 - $prepared_sql = $sql; // No need for placeholders
2478 + if ( $search_query ) {
2479 + $sql .= ' AND t.name LIKE %s';
2480 + $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2481 + } else {
2482 + $prepared_sql = $sql; // No need for placeholders.
2483 + }
2484 +
2485 + $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
2486 + // Cache the results in the object cache for future use.
2487 + wp_cache_set( $cache_key, $results ); // Adjust the expiration time as needed.
2190 2488 }
2191 2489
2192 - $results = $wpdb->get_results( $prepared_sql );
2193 -
2194 2490 $cats = [];
2195 2491 if ( ! is_array( $results ) && ! count( $results ) ) {
2196 2492 return $cats;
2197 2493 }
@@ -2204,18 +2500,20 @@
2204 2500 'label' => $category_title,
2205 2501 ];
2206 2502 }
2207 2503
2504 + // Cache the results in a static variable for the next call.
2505 + self::$cache[ $cache_key ] = $cats;
2208 2506 return $cats;
2209 2507 }
2210 2508
2211 2509 /**
2212 - * @param $search_query
2510 + * @param string $search_query Search query.
2213 2511 *
2214 2512 * @return array
2215 2513 */
2216 2514 public static function get_registered_post_types( $search_query = null ) {
2217 - // Get all registered post types
2515 + // Get all registered post types.
2218 2516 $registered_post_types = get_post_types(
2219 2517 [
2220 2518 'public' => true,
2221 2519 '_builtin' => false,
@@ -2228,11 +2526,11 @@
2228 2526 unset( $registered_post_types['rtsb_builder'] );
2229 2527
2230 2528 $post_types = [];
2231 2529
2232 - // Check if a search query is provided
2530 + // Check if a search query is provided.
2233 2531 if ( ! empty( $search_query ) ) {
2234 - // Filter post types based on the search query
2532 + // Filter post types based on the search query.
2235 2533 $registered_post_types = array_filter(
2236 2534 $registered_post_types,
2237 2535 function ( $post_type ) use ( $search_query ) {
2238 2536 return stripos( $post_type->labels->singular_name, $search_query ) !== false;
@@ -2237,9 +2535,9 @@
2237 2535 function ( $post_type ) use ( $search_query ) {
2238 2536 return stripos( $post_type->labels->singular_name, $search_query ) !== false;
2239 2537 }
2240 2538 );
2241 - // Check if 'post' match the search query and include them if they do
2539 + // Check if 'post' match the search query and include them if they do.
2242 2540 if ( stripos( 'post', $search_query ) !== false ) {
2243 2541 $post_types[] = [
2244 2542 'value' => 'post',
2245 2543 'label' => 'Post',
@@ -2251,9 +2549,9 @@
2251 2549 'label' => 'Post',
2252 2550 ];
2253 2551 }
2254 2552
2255 - // Convert the filtered post types to an array
2553 + // Convert the filtered post types to an array.
2256 2554 foreach ( $registered_post_types as $post_type ) {
2257 2555 $post_types[] = [
2258 2556 'value' => $post_type->name,
2259 2557 'label' => $post_type->labels->singular_name,
@@ -2265,9 +2563,9 @@
2265 2563
2266 2564 /**
2267 2565 * Get Post Types.
2268 2566 *
2269 - * @param $search_query
2567 + * @param string $search_query Search query.
2270 2568 *
2271 2569 * @return array
2272 2570 */
2273 2571 public static function get_post_types( $search_query = null, $args = [] ) {
@@ -2278,8 +2576,9 @@
2278 2576 'post_type' => 'any',
2279 2577 'posts_per_page' => 20,
2280 2578 'orderby' => 'ID',
2281 2579 'order' => 'DESC',
2580 + // 'suppress_filters' => false, // WPML Support, Remove Others Languages.
2282 2581 ]
2283 2582 );
2284 2583
2285 2584 if ( 'archive' !== $args['post_type'] ) {
@@ -2290,9 +2589,9 @@
2290 2589 $query_results = get_posts( $args );
2291 2590 foreach ( $query_results as $post ) {
2292 2591 $posts[] = [
2293 2592 'value' => $post->ID,
2294 - 'label' => $post->post_title,
2593 + 'label' => $post->post_title . ' (' . $post->ID . ')',
2295 2594 ];
2296 2595 }
2297 2596 if ( $search_query ) {
2298 2597 if ( strpos( '-error 404', $search_query ) ) {
@@ -2320,6 +2619,327 @@
2320 2619 * @return array
2321 2620 */
2322 2621 public static function get_elementor_breakpoints() {
2323 2622 return ( new \Elementor\Core\Breakpoints\Manager() )->get_breakpoints_config();
2623 + }
2624 +
2625 + /**
2626 + * Render view.
2627 + *
2628 + * @param string $viewName View name.
2629 + * @param array $args View args.
2630 + * @param boolean $return View return.
2631 + * @return string|void
2632 + */
2633 + public static function renderView( $viewName, $args = [], $return = false ) {
2634 + $viewName = str_replace( '.', '/', $viewName );
2635 +
2636 + if ( ! empty( $args ) && is_array( $args ) ) {
2637 + extract( $args );
2638 + }
2639 +
2640 + $view_file = rtsb()->plugin_path() . '/resources/' . $viewName . '.php';
2641 +
2642 + if ( ! file_exists( $view_file ) ) {
2643 + _doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', esc_html( $view_file ) ), '1.7.0' );
2644 +
2645 + return;
2646 + }
2647 +
2648 + if ( $return ) {
2649 + ob_start();
2650 + include $view_file;
2651 +
2652 + return ob_get_clean();
2653 + } else {
2654 + include $view_file;
2655 + }
2656 + }
2657 +
2658 + /**
2659 + * Best selling product query.
2660 + *
2661 + * @param int $minimum_sale Minimum sale/
2662 + * @param int $limit Total limit.
2663 + *
2664 + * @return array|mixed
2665 + */
2666 + public static function best_selling_products_ids( $minimum_sale = 1, $limit = 10 ) {
2667 + $cache_key = 'rtsb_best_selling_products_' . $minimum_sale . '_' . $limit;
2668 + // Check if the data is in the cache.
2669 + if ( isset( self::$cache[ $cache_key ] ) ) {
2670 + return self::$cache[ $cache_key ];
2671 + }
2672 + $cached_data = get_transient( $cache_key );
2673 + if ( $cached_data ) {
2674 + self::$cache[ $cache_key ] = $cached_data;
2675 + return $cached_data;
2676 + }
2677 + global $wpdb;
2678 + $sql = $wpdb->prepare(
2679 + "
2680 + SELECT ID
2681 + FROM {$wpdb->posts} AS p
2682 + LEFT JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
2683 + WHERE p.post_type = 'product'
2684 + AND p.post_status = 'publish'
2685 + AND pm.meta_key = 'total_sales'
2686 + AND pm.meta_value >= %d
2687 + ORDER BY pm.meta_value + 0 DESC
2688 + LIMIT %d
2689 + ",
2690 + $minimum_sale,
2691 + $limit
2692 + );
2693 + $best_selling = $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
2694 + // Cache the result for future use.
2695 + set_transient( $cache_key, $best_selling, DAY_IN_SECONDS );
2696 + self::$cache[ $cache_key ] = $best_selling;
2697 + return $best_selling;
2698 + }
2699 +
2700 + /**
2701 + * @param null|Object $product Product Object.
2702 + * @param int $days_threshold Date String.
2703 + *
2704 + * @return bool
2705 + */
2706 + public static function is_new_product( $product = null, $days_threshold = 30 ) {
2707 + if ( is_null( $product ) ) {
2708 + global $product;
2709 + }
2710 + if ( ! $product instanceof WC_Product ) {
2711 + return false;
2712 + }
2713 +
2714 + $product_id = $product->get_id();
2715 + $days_threshold = ! empty( $days_threshold ) ? $days_threshold : 30;
2716 + $cache_key = 'is_new_product_' . $product_id . '_' . $days_threshold;
2717 +
2718 + if ( isset( self::$cache[ $cache_key ] ) ) {
2719 + return self::$cache[ $cache_key ];
2720 + }
2721 +
2722 + // Get the product publish date.
2723 + $publish_timestamp = strtotime( $product->get_date_created()->date_i18n( 'Y-m-d H:i:s' ) );
2724 +
2725 + // Calculate the difference in days.
2726 + $days_difference = abs( time() - $publish_timestamp ) / ( 60 * 60 * 24 );
2727 +
2728 + // Check if the product is considered new.
2729 + $is_new = $days_difference <= $days_threshold;
2730 +
2731 + // Cache the result for a day.
2732 + self::$cache[ $cache_key ] = $is_new;
2733 +
2734 + return $is_new;
2735 + }
2736 +
2737 + /**
2738 + * Checks if a feature is disabled for guest users based on the provided option.
2739 + *
2740 + * @param string $option The option to retrieve from the item.
2741 + * @param mixed $default The default value if the option is not found.
2742 + *
2743 + * @return bool
2744 + */
2745 + public static function is_guest_feature_disabled( $option, $default ) {
2746 + $disabled = self::get_option( 'general', 'guest_user', $option, $default );
2747 +
2748 + return ! is_user_logged_in() && $disabled;
2749 + }
2750 +
2751 + /**
2752 + * Checks if a feature is disabled for guest users based on the provided option.
2753 + *
2754 + * @param string $option The option to retrieve from the item.
2755 + * @param mixed $default The default value if the option is not found.
2756 + *
2757 + * @return bool
2758 + */
2759 + public static function woocommerce_output_all_notices() {
2760 + if ( function_exists( 'wc_print_notices' ) ) :
2761 + echo '<div class="rtsb-notice">';
2762 + woocommerce_output_all_notices();
2763 + echo '</div>';
2764 + endif;
2765 + }
2766 +
2767 + /**
2768 + * @param object $product Product.
2769 + * @return bool
2770 + */
2771 + public static function is_visible_qty_input( $product = null ) {
2772 + $is_visible_qty = true;
2773 + if ( $product instanceof \WC_Product ) {
2774 + if ( $product->is_sold_individually() ) {
2775 + $is_visible_qty = false;
2776 + } elseif ( $product->managing_stock() ) {
2777 + if ( in_array( $product->get_backorders(), [ 'notify' , 'yes' ], true ) ) {
2778 + $is_visible_qty = true;
2779 + } elseif ( $product->get_stock_quantity() < 2 ) {
2780 + $is_visible_qty = false;
2781 + }
2782 + }
2783 + }
2784 + return $is_visible_qty;
2785 + }
2786 +
2787 + /**
2788 + * @param int $object_id Object id.
2789 + * @param string $element_type element type.
2790 + * @param string|null $current_lang null for current language, default for default languages.
2791 + * @return false|mixed|null
2792 + */
2793 + public static function wpml_object_id( $object_id, $element_type, $current_lang = 'default' ) {
2794 + if ( ! defined( 'ICL_SITEPRESS_VERSION' ) ) {
2795 + return $object_id;
2796 + }
2797 + if ( ! $object_id || ! $element_type ) {
2798 + return $object_id;
2799 + }
2800 + if ( 'default' === $current_lang ) {
2801 + $lang = apply_filters( 'wpml_default_language', null );
2802 + } else {
2803 + $lang = null;
2804 + }
2805 + return apply_filters( 'wpml_object_id', $object_id, $element_type, false, $lang );
2806 + }
2807 +
2808 + /**
2809 + * @return string
2810 + */
2811 + public static function wpml_current_language() {
2812 + $current = apply_filters( 'wpml_current_language', null );
2813 + $default = apply_filters( 'wpml_default_language', null );
2814 + return $default !== $current ? $current : null;
2815 + }
2816 +
2817 + /**
2818 + * Custom icons.
2819 + *
2820 + * @return string[]
2821 + */
2822 + public static function get_custom_icon_names() {
2823 + return [
2824 + 'heart-empty',
2825 + 'heart',
2826 + 'eye',
2827 + 'exchange',
2828 + 'plus',
2829 + 'minus',
2830 + 'avatar',
2831 + 'pay',
2832 + 'share',
2833 + 'clock',
2834 + 'check-alt',
2835 + 'check',
2836 + 'delete',
2837 + 'marker',
2838 + 'list',
2839 + 'list-2',
2840 + 'power',
2841 + 'cart',
2842 + 'cart-2',
2843 + 'cart-3',
2844 + 'downloads',
2845 + 'zoom',
2846 + 'user-edit',
2847 + 'grid',
2848 + 'filter',
2849 + 'billing',
2850 + 'login',
2851 + 'payment',
2852 + 'search',
2853 + 'edit',
2854 + 'coupon',
2855 + 'arrows-cw',
2856 + 'trash-empty',
2857 + ];
2858 + }
2859 +
2860 + /**
2861 + * Flushes rewrite rules.
2862 + *
2863 + * @return void
2864 + */
2865 + public static function maybe_flush_rewrite_rules() {
2866 + add_action( 'wp_loaded', [ __CLASS__, 'flush_rewrite_rules' ] );
2867 + add_action( 'shutdown', [ __CLASS__, 'flush_rewrite_rules_once_more' ] );
2868 + }
2869 +
2870 + /**
2871 + * Flushes rewrite rules if needed.
2872 + *
2873 + * @return void
2874 + */
2875 + public static function flush_rewrite_rules() {
2876 + if ( get_option( 'rtsb_permalinks_need_flush' ) === 'yes' ) {
2877 + flush_rewrite_rules();
2878 + }
2879 +
2880 + if ( get_option( 'rtsb_permalinks_flushed' ) === 'yes' ) {
2881 + flush_rewrite_rules();
2882 + delete_option( 'rtsb_permalinks_flushed' );
2883 + }
2884 + }
2885 +
2886 + /**
2887 + * Flushes rewrite rules if needed for second time.
2888 + *
2889 + * @return void
2890 + */
2891 + public static function flush_rewrite_rules_once_more() {
2892 + if ( get_option( 'rtsb_permalinks_need_flush' ) === 'yes' ) {
2893 + flush_rewrite_rules();
2894 + update_option( 'rtsb_permalinks_flushed', 'yes' );
2895 + delete_option( 'rtsb_permalinks_need_flush' );
2896 + }
2897 + }
2898 +
2899 + /**
2900 + * Social Share Platforms.
2901 + *
2902 + * @return mixed|null
2903 + */
2904 + public static function set_repeater_options( $section_id, $block_id, $option_key, $compare_key, $compare_value, $values ) {
2905 + // $options = self::get_options( $section_id, $block_id );
2906 + $modules = DataModel::source()->get_option( $section_id, [], false );
2907 + if ( empty( $modules[ $block_id ] ) ) {
2908 + return;
2909 + }
2910 + $options = $modules[ $block_id ];
2911 + if ( empty( $options[ $option_key ] ) ) {
2912 + return;
2913 + }
2914 + $repeater_options = json_decode( $options[ $option_key ], true );
2915 + // Check if options are not empty and are in array format.
2916 + if ( ! is_array( $repeater_options ) ) {
2917 + return;
2918 + }
2919 + // Use array_column to get an array of values from $compare_key.
2920 + $column_values = array_column( $repeater_options, $compare_key );
2921 + // Use array_search to find the index of the $compare_value.
2922 + $index = array_search( $compare_value, $column_values, true );
2923 + if ( false === $index ) {
2924 + return;
2925 + }
2926 + $repeater_options[ $index ] = wp_parse_args( $values, $repeater_options[ $index ] );
2927 + $options[ $option_key ] = wp_json_encode( $repeater_options );
2928 + self::set_options( $section_id, $block_id, $options );
2929 + }
2930 +
2931 +
2932 + /**
2933 + * @param array $ids products id.
2934 + * @return array
2935 + */
2936 + public static function get_available_products_by_ids( $ids = [] ) {
2937 + $ids = array_filter(
2938 + $ids,
2939 + function ( $id ) {
2940 + return 'product' === get_post_type( $id ) && 'publish' === get_post_status( $id );
2941 + }
2942 + );
2943 + return $ids;
2324 2944 }
2325 2945 }