PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.1.10
ShopBuilder – WooCommerce Builder For Elementor v2.1.10
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
shopbuilder / app / Helpers / Fns.php

Fns.php in ShopBuilder – WooCommerce Builder For Elementor 2.1.10, at app/Helpers/Fns.php

2,758 lines 81.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Fns Helpers class
4 *
5 * @package RadiusTheme\SB
6 */
7
8 namespace RadiusTheme\SB\Helpers;
9
10 // Do not allow directly accessing this file.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit( 'This script cannot be accessed directly.' );
13 }
14
15 use WC_Product;
16 use WC_Product_Query;
17 use Elementor\Icons_Manager;
18 use RadiusTheme\SB\Models\ReSizer;
19 use RadiusTheme\SB\Models\DataModel;
20 use RadiusTheme\SB\Models\ModuleList;
21 use RadiusTheme\SB\Models\Settings;
22
23 /**
24 * Fns class
25 */
26 class Fns {
27
28 /**
29 * @var array
30 */
31 private static $cache = [];
32
33 /**
34 * Verify nonce.
35 *
36 * @return bool
37 */
38 public static function verify_nonce() {
39 $nonce = isset( $_REQUEST[ rtsb()->nonceId ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ rtsb()->nonceId ] ) ) : null;
40 $nonceText = rtsb()->nonceText;
41 if ( wp_verify_nonce( $nonce, $nonceText ) ) {
42 return true;
43 }
44
45 return false;
46 }
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
54 /**
55 * @param $plugin_slug
56 *
57 * @return bool
58 */
59 public static function check_plugin_installed( $plugin_slug ): bool {
60 $installed_plugins = get_plugins();
61
62 return array_key_exists( $plugin_slug, $installed_plugins ) || in_array( $plugin_slug, $installed_plugins, true );
63 }
64
65 /**
66 * @param $plugin_slug
67 *
68 * @return bool
69 */
70 public static function check_plugin_active( $plugin_slug ): bool {
71 if ( is_plugin_active( $plugin_slug ) ) {
72 return true;
73 }
74
75 return false;
76 }
77
78 /**
79 * @param $data
80 *
81 * @return mixed|string
82 */
83 public static function stripslashes_value( $data ) {
84 if ( is_string( $data ) && strpos( $data, '\\' ) !== false ) {
85 return stripslashes( $data );
86 } elseif ( is_array( $data ) ) {
87 foreach ( $data as $key => $value ) {
88 $data[ $key ] = self::stripslashes_value( $value );
89 }
90 }
91
92 return $data;
93 }
94
95 /**
96 * @param $string
97 *
98 * @return array
99 */
100 public static function multiselect_settings_field_value( $string ) {
101
102 $values = [];
103 if ( empty( $string ) ) {
104 return $values;
105 }
106
107 $stripslashes_data = self::stripslashes_value( $string );
108 if ( is_array( $stripslashes_data ) && count( $stripslashes_data ) ) {
109 foreach ( $stripslashes_data as $item ) {
110 $item = is_array( $item ) ? $item : json_decode( $item, true );
111 $values[] = $item['value'];
112 }
113 }
114
115 return $values;
116 }
117
118 /**
119 * @return bool
120 */
121 public static function check_is_block_theme(): bool {
122 global $wp_version;
123
124 return version_compare( $wp_version, '5.9', '>=' ) && function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
125 }
126
127 /**
128 * @param string $template_name Template name.
129 * @param string $template_path Template path. (default: '').
130 * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin
131 *
132 * @return mixed|void
133 */
134 public static function locate_template( $template_name, $template_path = '', $plugin_path = '' ) {
135 $template_name = $template_name . '.php';
136 if ( ! $template_path ) {
137 $template_path = rtsb()->get_template_path();
138 }
139 if ( ! $plugin_path ) {
140 $plugin_path = RTSB_ABSPATH . 'templates/';
141 }
142
143 $template_rtsb_path = trailingslashit( $template_path ) . $template_name;
144 $template_path = '/' . $template_name;
145 $plugin_path = $plugin_path . $template_name;
146 $pro_plugin_path = rtsb()->has_pro() ? RTSBPRO_PATH . 'templates/' . $template_name : '';
147
148 $located = locate_template(
149 apply_filters(
150 'rtsb/core/locate_template_files',
151 [
152 $template_rtsb_path, // Search in <theme>/shopbuilder/.
153 $template_path, // Search in <theme>/.
154 ]
155 )
156 );
157
158 if ( ! $located ) {
159 if ( file_exists( $plugin_path ) ) {
160 return apply_filters( 'rtsb/core/locate_template', $plugin_path, $template_name );
161 } elseif ( $pro_plugin_path && rtsb()->has_pro() && file_exists( $pro_plugin_path ) ) {
162 return apply_filters( 'rtsb/core/locate_template', $pro_plugin_path, $template_name );
163 }
164 }
165
166 /**
167 * APPLY_FILTERS: rtsb/core/locate_template
168 *
169 * Filter the location of the templates.
170 *
171 * @param string $located Template found
172 * @param string $path Template path
173 *
174 * @return string
175 */
176 return apply_filters( 'rtsb/core/locate_template', $located, $template_name );
177 }
178
179 /**
180 * Template Content
181 *
182 * @param string $template_name Template name.
183 * @param array $args Arguments. (default: array).
184 * @param bool $return Whether to return or print the template.
185 * @param string $template_path Template path. (default: '').
186 * @param string $plugin_path Fallback path from where file will load if fail to load from template. (default: '').
187 *
188 * @return false|string|void
189 */
190 public static function load_template( $template_name, array $args = null, $return = false, $template_path = '', $plugin_path = '' ) {
191 $located = self::locate_template( $template_name, $template_path, $plugin_path );
192
193 if ( ! file_exists( $located ) ) {
194 // translators: %s template.
195 self::doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'shopbuilder' ), '<code>' . $located . '</code>' ), '1.0' );
196
197 return;
198 }
199
200 if ( ! empty( $args ) && is_array( $args ) ) {
201 $atts = $args;
202 extract( $args ); // @codingStandardsIgnoreLine
203 }
204
205 // Allow 3rd party plugin filter template file from their plugin.
206 $located = apply_filters( 'rtsb/core/get_template', $located, $template_name, $args );
207
208 if ( $return ) {
209 ob_start();
210 }
211
212 do_action( 'rtsb/core/before_template_part', $template_name, $located, $args );
213 include $located;
214
215 do_action( 'rtsb/core/after_template_part', $template_name, $located, $args );
216
217 if ( $return ) {
218 return ob_get_clean();
219 }
220 }
221
222 /**
223 * Verify nonce.
224 *
225 * @return string
226 */
227 public static function is_woocommerce() {
228 return is_woocommerce() || is_cart() || is_checkout() || is_account_page();
229 }
230
231 /**
232 * Page builder
233 *
234 * @param int $post_id post id.
235 *
236 * @return string
237 */
238 public static function page_edit_with( $post_id ) {
239 if ( ! $post_id ) {
240 return '';
241 }
242
243 $edit_with = get_post_meta( $post_id, '_elementor_edit_mode', true );
244
245 if ( 'builder' === $edit_with ) {
246 $edit_by = 'elementor';
247 } else {
248 $edit_by = 'gutenberg';
249 }
250
251 return $edit_by;
252 }
253
254 /**
255 * Returns default expiration for wishlist cookie
256 *
257 * @return int Number of seconds the cookie should last.
258 */
259 public static function get_cookie_expiration() {
260 return intval( apply_filters( 'rtsb/cookie_expiration', 60 * 60 * 24 * 30 ) );
261 }
262
263 /**
264 * Create a cookie.
265 *
266 * @param string $name Cookie name.
267 * @param mixed $value Cookie value.
268 * @param int $time Cookie expiration time.
269 * @param bool $secure Whether cookie should be available to secured connection only.
270 * @param bool $httponly Whether cookie should be available to HTTP request only (no js handling).
271 *
272 * @return bool
273 * @since 1.0.0
274 */
275 public static function setcookie( $name, $value = [], $time = null, $secure = false, $httponly = false ) {
276
277 if ( ! apply_filters( 'rtsb/set_cookie', true ) || empty( $name ) ) {
278 return false;
279 }
280
281 $time = ! empty( $time ) ? $time : time() + self::get_cookie_expiration();
282
283 $value = wp_json_encode( stripslashes_deep( $value ) );
284 $expiration = apply_filters( 'rtsb/cookie_expiration_time', $time ); // Default 30 days.
285
286 $_COOKIE[ $name ] = $value;
287 wc_setcookie( $name, $value, $expiration, $secure, $httponly );
288
289 return true;
290 }
291
292 /**
293 * Retrieve the value of a cookie.
294 *
295 * @param string $name Cookie name.
296 *
297 * @return mixed
298 * @since 1.0.0
299 */
300 public static function getcookie( $name ) {
301 if ( isset( $_COOKIE[ $name ] ) ) {
302 return json_decode( sanitize_text_field( wp_unslash( $_COOKIE[ $name ] ) ), true );
303 }
304
305 return [];
306 }
307
308 /**
309 * Woocommerce Last product id return
310 */
311 public static function get_prepared_product_id() {
312 if ( is_singular( 'product' ) ) {
313 return get_the_ID();
314 }
315 // Return the Builder Template id.
316
317 if ( get_post_type( get_the_ID() ) == BuilderFns::$post_type_tb ) {
318 $product_id = get_post_meta( get_the_ID(), BuilderFns::$product_template_meta, true );
319 if ( $product_id ) {
320 return $product_id;
321 }
322 }
323
324 global $wpdb;
325 $cache_key = 'rtsb_prepared_product_id';
326 $_post_id = wp_cache_get( $cache_key );
327
328 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
329 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
330 $_post_id = $wpdb->get_var(
331 $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status IN('publish', 'draft')", 'product' )
332 );
333 wp_cache_set( $cache_key, $_post_id, '', 12 * HOUR_IN_SECONDS );
334 }
335
336 return $_post_id;
337 }
338
339 /**
340 * Get the product function. Only used in single page widgets.
341 *
342 * @return object
343 */
344 public static function get_product() {
345 global $product;
346
347 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
348 // do_action( 'rtsb_before_product_template_render' );.
349 return $product;
350 }
351 $cache_key = 'prepared_product_for_preview';
352 if ( isset( self::$cache[ $cache_key ] ) ) {
353 return self::$cache[ $cache_key ];
354 }
355 $product = wc_get_product( self::get_prepared_product_id() );
356 self::$cache[ $cache_key ] = $product;
357 do_action( 'rtsb_before_product_template_render' );
358 return $product;
359 }
360
361 /**
362 * Error function
363 *
364 * @param [type] $function function name.
365 * @param [type] $message message.
366 * @param [type] $version version.
367 *
368 * @return void
369 */
370 public static function doing_it_wrong( $function, $message, $version ) {
371 $message .= ' Backtrace: ' . wp_debug_backtrace_summary();
372 _doing_it_wrong( esc_html( $function ), wp_kses_post( $message ), esc_html( $version ) );
373 }
374
375 /**
376 * @return array
377 */
378 public static function get_pages() {
379 $pages = [];
380 $rawPages = get_pages();
381 if ( ! empty( $rawPages ) ) {
382 foreach ( $rawPages as $page ) {
383 $pages[ $page->ID ] = $page->post_title;
384 }
385 }
386
387 return $pages;
388 }
389
390 public static function get_section_items( $section_id ) {
391 if ( ! $section_id ) {
392 return [];
393 }
394
395 return DataModel::source()->get_option( $section_id, [] );
396 }
397
398 public static function get_options( $section_id, $item_id ) {
399 if ( ! $section_id || ! $item_id ) {
400 return [];
401 }
402 $sections = self::get_section_items( $section_id );
403
404 return isset( $sections[ $item_id ] ) ? $sections[ $item_id ] : [];
405 }
406
407 /**
408 * Get options value with default value if options doesn't exist.
409 *
410 * @param $group
411 * @param $option_key
412 * @param $default_value
413 *
414 * @return mixed|string
415 */
416 public static function get_options_by_default_val( $group, $option_key, $default_value = '' ) {
417
418 if ( ! $option_key || ! isset( $group[ $option_key ] ) ) {
419 return $default_value;
420 }
421
422 return $group[ $option_key ];
423 }
424
425 /**
426 * @param string $section_id
427 * @param string $item_id
428 * @param string $option_id
429 * @param null $default EXCEPT multi_checkbox you can provide default value if given option does not set any value
430 * @param null $type checkbox, multi_checkbox, number
431 *
432 * @return bool|int|mixed|null
433 */
434 public static function get_option( $section_id, $item_id, $option_id, $default = null, $type = null ) {
435 $options = self::get_options( $section_id, $item_id );
436
437 if ( $type === 'checkbox' ) {
438 if ( isset( $options[ $option_id ] ) ) {
439 return $options[ $option_id ] === 'on';
440 }
441
442 return $default;
443 } elseif ( $type === 'multi_checkbox' ) {
444 return isset( $options[ $option_id ] ) && is_array( $options[ $option_id ] ) && in_array( $default, $options[ $option_id ] );
445 } elseif ( $type === 'number' ) {
446 return isset( $options[ $option_id ] ) ? absint( $options[ $option_id ] ) : absint( $default );
447 }
448
449 return isset( $options[ $option_id ] ) && ! empty( $options[ $option_id ] ) ? $options[ $option_id ] : $default;
450 }
451
452
453 /**
454 * Create a page and store the ID in an option.
455 *
456 * @param mixed $slug Slug for the new page.
457 * @param array $options ['section_id', 'item_id', 'option_id']Option name to store the page's ID.
458 * @param string $page_title (default: '') Title for the new page.
459 * @param string $page_content (default: '') Content for the new page.
460 * @param int $post_parent (default: 0) Parent for the new page.
461 * @param string $post_status (default: publish) The post status of the new page.
462 *
463 * @return int page ID.
464 */
465 public static function create_page( $slug, $options = '', $page_title = '', $page_content = '', $post_parent = 0, $post_status = 'publish' ) {
466 global $wpdb;
467
468 $option_value = 0;
469 if ( ! empty( $options ) ) {
470 if ( is_array( $options ) ) {
471 $options = wp_parse_args(
472 $options,
473 [
474 'section_id' => '',
475 'item_id' => '',
476 'option_id' => '',
477 ]
478 );
479 $option_value = self::get_option( $options['section_id'], $options['item_id'], $options['option_id'], 0, 'number' );
480 } else {
481 $option_value = absint( get_option( $options ) );
482 }
483 }
484
485 if ( $option_value > 0 ) {
486 $page_object = get_post( $option_value );
487
488 if ( $page_object && 'page' === $page_object->post_type && ! in_array(
489 $page_object->post_status,
490 [
491 'pending',
492 'trash',
493 'future',
494 'auto-draft',
495 ],
496 true
497 ) ) {
498 // Valid page is already in place.
499 return $page_object->ID;
500 }
501 }
502
503 if ( strlen( $page_content ) > 0 ) {
504 // Search for an existing page with the specified page content (typically a shortcode).
505 $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
506 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
507 $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}%" ) );
508 } else {
509 // Search for an existing page with the specified page slug.
510 $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
511 }
512
513 $valid_page_found = apply_filters( 'rtsb/core/create_page_id', $valid_page_found, $slug, $page_content, $options );
514
515 if ( $valid_page_found ) {
516 if ( is_array( $options ) ) {
517 if ( ! empty( $options['section_id'] ) && $options['item_id'] && $options['option_id'] ) {
518 $section_items = DataModel::source()->get_option( $options['section_id'], [] );
519 $section_items[ $options['item_id'] ][ $options['option_id'] ] = $valid_page_found;
520 DataModel::source()->set_option( $options['section_id'], $section_items );
521 }
522 } else {
523 if ( $options ) {
524 update_option( $options, $valid_page_found );
525 }
526 }
527
528 return $valid_page_found;
529 }
530
531 // Search for a matching valid trashed page.
532 if ( strlen( $page_content ) > 0 ) {
533 // Search for an existing page with the specified page content (typically a shortcode).
534 $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
535 } else {
536 // Search for an existing page with the specified page slug.
537 $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
538 }
539
540 if ( $trashed_page_found ) {
541 $page_id = $trashed_page_found;
542 $page_data = [
543 'ID' => $page_id,
544 'post_status' => $post_status,
545 ];
546 wp_update_post( $page_data );
547 } else {
548 $page_data = [
549 'post_status' => $post_status,
550 'post_type' => 'page',
551 'post_author' => 1,
552 'post_name' => $slug,
553 'post_title' => $page_title,
554 'post_content' => $page_content,
555 'post_parent' => $post_parent,
556 'comment_status' => 'closed',
557 ];
558 $page_id = wp_insert_post( $page_data );
559
560 do_action( 'rtsb/core/page_created', $page_id, $page_data );
561 }
562
563 if ( is_array( $options ) ) {
564 if ( ! empty( $options['section_id'] ) && $options['item_id'] && $options['option_id'] ) {
565 $section_items = DataModel::source()->get_option( $options['section_id'], [] );
566 $section_items[ $options['item_id'] ][ $options['option_id'] ] = $page_id;
567 DataModel::source()->set_option( $options['section_id'], $section_items );
568 }
569 } else {
570 if ( $options ) {
571 update_option( $options, $page_id );
572 }
573 }
574
575 return $page_id;
576 }
577
578 public static function get_kses_array() {
579 return [
580 'a' => [
581 'class' => [],
582 'href' => [],
583 'rel' => [],
584 'title' => [],
585 ],
586 'abbr' => [
587 'title' => [],
588 ],
589 'b' => [],
590 'blockquote' => [
591 'cite' => [],
592 ],
593 'cite' => [
594 'title' => [],
595 ],
596 'code' => [],
597 'del' => [
598 'datetime' => [],
599 'title' => [],
600 ],
601 'dd' => [],
602 'div' => [
603 'class' => [],
604 'title' => [],
605 'style' => [],
606 ],
607 'dl' => [],
608 'dt' => [],
609 'em' => [],
610 'h1' => [
611 'class' => [],
612 ],
613 'h2' => [
614 'class' => [],
615 ],
616 'h3' => [
617 'class' => [],
618 ],
619 'h4' => [
620 'class' => [],
621 ],
622 'h5' => [
623 'class' => [],
624 ],
625 'h6' => [
626 'class' => [],
627 ],
628 'i' => [
629 'class' => [],
630 ],
631 'img' => [
632 'alt' => [],
633 'class' => [],
634 'height' => [],
635 'src' => [],
636 'width' => [],
637 ],
638 'li' => [
639 'class' => [],
640 ],
641 'ol' => [
642 'class' => [],
643 ],
644 'p' => [
645 'class' => [],
646 ],
647 'q' => [
648 'cite' => [],
649 'title' => [],
650 ],
651 'span' => [
652 'class' => [],
653 'title' => [],
654 'style' => [],
655 ],
656 'iframe' => [
657 'width' => [],
658 'height' => [],
659 'scrolling' => [],
660 'frameborder' => [],
661 'allow' => [],
662 'src' => [],
663 ],
664 'strike' => [],
665 'br' => [],
666 'strong' => [],
667 'data-wow-duration' => [],
668 'data-wow-delay' => [],
669 'data-wallpaper-options' => [],
670 'data-stellar-background-ratio' => [],
671 'ul' => [
672 'class' => [],
673 ],
674 ];
675 }
676
677
678 /*
679 * Escape output of wishlist icon
680 *
681 * @param string $data Data to escape.
682 * @return string Escaped data
683 */
684 public static function print_icon( $data ) {
685 /**
686 * APPLY_FILTERS: rtsb/core/allowed_icon_html
687 *
688 * Filter the allowed HTML for the icons.
689 *
690 * @param array $allowed_icon_html Allowed HTML
691 *
692 * @return array
693 */
694 $allowed_icon_html = apply_filters(
695 'rtsb/core/allowed_icon_html',
696 [
697 'i' => [
698 'class' => true,
699 ],
700 'img' => [
701 'src' => true,
702 'alt' => true,
703 'width' => true,
704 'height' => true,
705 ],
706 'svg' => [
707 'class' => true,
708 'aria-hidden' => true,
709 'aria-labelledby' => true,
710 'role' => true,
711 'xmlns' => true,
712 'width' => true,
713 'height' => true,
714 'viewbox' => true,
715 'stroke' => true,
716 'fill' => true,
717 ],
718 'g' => [
719 'fill' => true,
720 ],
721 'title' => [
722 'title' => true,
723 ],
724 'path' => [
725 'd' => true,
726 'fill' => true,
727 'stroke' => true,
728 'stroke-width' => true,
729 'stroke-linecap' => true,
730 'stroke-linejoin' => true,
731 'fill-rule' => true,
732 'clip-rule' => true,
733 ],
734 ]
735 );
736
737 echo wp_kses( $data, $allowed_icon_html );
738 }
739
740 /**
741 * Prints HTMl.
742 *
743 * @param string $html HTML.
744 * @param bool $allHtml All HTML.
745 *
746 * @return void
747 */
748 public static function print_html( $html, $allHtml = false ) {
749 if ( ! $html ) {
750 return;
751 }
752 if ( $allHtml ) {
753 echo stripslashes_deep( $html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
754 } else {
755 echo wp_kses_post( stripslashes_deep( $html ) );
756 }
757 }
758
759 /**
760 * Allowed HTML for wp_kses.
761 *
762 * @param string $level Tag level.
763 *
764 * @return mixed
765 */
766 public static function allowedHtml( $level = 'basic' ) {
767 $allowed_html = [];
768 // TODO:: Need Optimize.
769 switch ( $level ) {
770 case 'basic':
771 $allowed_html = [
772 'b' => [
773 'class' => [],
774 'id' => [],
775 ],
776 'i' => [
777 'class' => [],
778 'id' => [],
779 ],
780 'u' => [
781 'class' => [],
782 'id' => [],
783 ],
784 'br' => [
785 'class' => [],
786 'id' => [],
787 ],
788 'em' => [
789 'class' => [],
790 'id' => [],
791 ],
792 'span' => [
793 'class' => [],
794 'id' => [],
795 ],
796 'strong' => [
797 'class' => [],
798 'id' => [],
799 ],
800 'hr' => [
801 'class' => [],
802 'id' => [],
803 ],
804 'div' => [
805 'class' => [],
806 'id' => [],
807 ],
808 'a' => [
809 'href' => [],
810 'title' => [],
811 'class' => [],
812 'id' => [],
813 'target' => [],
814 ],
815 ];
816 break;
817
818 case 'advanced':
819 $allowed_html = [
820 'b' => [
821 'class' => [],
822 'id' => [],
823 ],
824 'i' => [
825 'class' => [],
826 'id' => [],
827 ],
828 'u' => [
829 'class' => [],
830 'id' => [],
831 ],
832 'br' => [
833 'class' => [],
834 'id' => [],
835 ],
836 'em' => [
837 'class' => [],
838 'id' => [],
839 ],
840 'span' => [
841 'class' => [],
842 'id' => [],
843 ],
844 'strong' => [
845 'class' => [],
846 'id' => [],
847 ],
848 'hr' => [
849 'class' => [],
850 'id' => [],
851 ],
852 'a' => [
853 'href' => [],
854 'title' => [],
855 'class' => [],
856 'id' => [],
857 'target' => [],
858 ],
859 'input' => [
860 'type' => [],
861 'name' => [],
862 'class' => [],
863 'value' => [],
864 ],
865 ];
866 break;
867
868 case 'image':
869 $allowed_html = [
870 'img' => [
871 'src' => [],
872 'data-src' => [],
873 'alt' => [],
874 'height' => [],
875 'width' => [],
876 'class' => [],
877 'id' => [],
878 'style' => [],
879 'srcset' => [],
880 'loading' => [],
881 'sizes' => [],
882 ],
883 'div' => [
884 'class' => [],
885 ],
886 ];
887 break;
888
889 case 'anchor':
890 $allowed_html = [
891 'a' => [
892 'href' => [],
893 'title' => [],
894 'class' => [],
895 'id' => [],
896 'style' => [],
897 ],
898 ];
899 break;
900
901 default:
902 // code...
903 break;
904 }
905
906 return $allowed_html;
907 }
908
909 /**
910 * Safe print a validated HTML tag.
911 *
912 * @param string $tag
913 */
914 public static function print_validated_html_tag( $tag ) {
915 $allowed_html_wrapper_tags = [
916 'a',
917 'article',
918 'aside',
919 'button',
920 'div',
921 'footer',
922 'h1',
923 'h2',
924 'h3',
925 'h4',
926 'h5',
927 'h6',
928 'header',
929 'main',
930 'nav',
931 'p',
932 'section',
933 'span',
934 ];
935
936 self::print_html( in_array( strtolower( $tag ), $allowed_html_wrapper_tags, true ) ? $tag : 'div' );
937 }
938
939 /**
940 * Insert Some array element
941 *
942 * @param [type] $key The elements will insert nearby this key.
943 * @param [type] $main_array Original array.
944 * @param [type] $insert_array some element will insert in original array.
945 * @param boolean $is_after array insert position base on the key.
946 *
947 * @return array
948 */
949 public static function insert_controls( $key, $main_array, $insert_array, $is_after = false ) {
950 $index = array_search( $key, array_keys( $main_array ), true );
951 if ( 'integer' === gettype( $index ) ) {
952 if ( $is_after ) {
953 $index++;
954 }
955 $main_array = array_merge(
956 array_slice( $main_array, 0, $index ),
957 $insert_array,
958 array_slice( $main_array, $index )
959 );
960 }
961
962 return $main_array;
963 }
964
965 /**
966 * Image Sizes
967 *
968 * @return array
969 */
970 public static function get_image_sizes() {
971 global $_wp_additional_image_sizes;
972
973 $sizes = [];
974
975 foreach ( get_intermediate_image_sizes() as $_size ) {
976 if ( in_array( $_size, [ 'thumbnail', 'medium', 'large' ], true ) ) {
977 $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
978 $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
979 $sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" );
980 } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
981 $sizes[ $_size ] = [
982 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
983 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
984 'crop' => $_wp_additional_image_sizes[ $_size ]['crop'],
985 ];
986 }
987 }
988
989 $imgSize = [];
990
991 foreach ( $sizes as $key => $img ) {
992 $imgSize[ $key ] = ucfirst( $key ) . " ({$img['width']}*{$img['height']})";
993 }
994
995 $imgSize['full'] = esc_html__( 'Full size', 'shopbuilder' );
996 $imgSize['rtsb_custom'] = esc_html__( 'Custom image size', 'shopbuilder' );
997
998 return $imgSize;
999 }
1000
1001 /**
1002 * Free Layouts
1003 *
1004 * @return array
1005 */
1006 public static function free_layouts() {
1007 $layouts = [
1008 'grid-layout1' => esc_html__( 'Grid Layout 1', 'shopbuilder' ),
1009 'grid-layout2' => esc_html__( 'Grid Layout 2', 'shopbuilder' ),
1010 'list-layout1' => esc_html__( 'List Layout 1', 'shopbuilder' ),
1011 'list-layout2' => esc_html__( 'List Layout 2', 'shopbuilder' ),
1012 'slider-layout1' => esc_html__( 'Slider Layout 1', 'shopbuilder' ),
1013 'slider-layout2' => esc_html__( 'Slider Layout 2', 'shopbuilder' ),
1014 'category-single-layout1' => esc_html__( 'Category Single Layout 1', 'shopbuilder' ),
1015 'category-layout1' => esc_html__( 'Category Layout 1', 'shopbuilder' ),
1016 'category-layout2' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
1017 ];
1018
1019 return apply_filters( 'rtsb/elements/elementor/free_layouts', $layouts );
1020 }
1021
1022 /**
1023 * Get all terms by taxonomy
1024 *
1025 * @param string $taxonomy Taxonomy name.
1026 * @param bool $placeholder Placeholder..
1027 *
1028 * @return array
1029 */
1030 public static function get_all_terms( $taxonomy, $placeholder = false ) {
1031 $terms = [];
1032
1033 if ( empty( $taxonomy ) ) {
1034 return $terms;
1035 }
1036 $cache_key = 'rtsb_get_all_terms_by_tax_name_' . $taxonomy;
1037 if ( isset( self::$cache[ $cache_key ] ) ) {
1038 return self::$cache[ $cache_key ];
1039 }
1040
1041 $termList = get_terms(
1042 [
1043 'taxonomy' => $taxonomy,
1044 'hide_empty' => false,
1045 ]
1046 );
1047
1048 if ( is_array( $termList ) && ! empty( $termList ) && empty( $termList['errors'] ) ) {
1049 if ( $placeholder ) {
1050 $terms = [
1051 'all' => __( 'All Products', 'shopbuilder' ),
1052 ];
1053 }
1054
1055 foreach ( $termList as $term ) {
1056 $terms[ $term->term_id ] = esc_html( $term->name );
1057 }
1058 }
1059 self::$cache[ $cache_key ] = $terms;
1060 return $terms;
1061 }
1062
1063 /**
1064 * Get all terms by attributes
1065 *
1066 * @return array
1067 */
1068 public static function get_all_attributes() {
1069 $terms = [];
1070 $termList = [];
1071
1072 $attributes = wc_get_attribute_taxonomies();
1073
1074 if ( $attributes ) {
1075 foreach ( $attributes as $tax ) {
1076 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1077 $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ) );
1078 }
1079 }
1080 }
1081
1082 if ( ! empty( $termList ) && empty( $termList['errors'] ) && is_array( $termList ) ) {
1083 foreach ( $termList as $name => $atts ) {
1084 foreach ( $atts as $term ) {
1085 $terms[ $term->term_id ] = esc_html( ucwords( str_replace( '-', ' ', $name ) ) . ' - ' . $term->name );
1086 }
1087 }
1088 }
1089
1090 return $terms;
1091 }
1092
1093 /**
1094 * Get all attributes name
1095 *
1096 * @return array
1097 */
1098 public static function get_all_attributes_name() {
1099 $attributes_name = [];
1100
1101 $attributes = wc_get_attribute_taxonomies();
1102
1103 if ( $attributes ) {
1104 foreach ( $attributes as $tax ) {
1105 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1106 $attributes_name[ wc_attribute_taxonomy_name( $tax->attribute_name ) ] = ucwords( $tax->attribute_name );
1107 }
1108 }
1109 }
1110
1111 return $attributes_name;
1112 }
1113
1114 /**
1115 * Get User list
1116 *
1117 * @return array
1118 */
1119 public static function get_users() {
1120 $users = [];
1121 $u = get_users();
1122
1123 if ( ! empty( $u ) ) {
1124 foreach ( $u as $user ) {
1125 $users[ $user->ID ] = $user->display_name;
1126 }
1127 }
1128
1129 return $users;
1130 }
1131
1132 /**
1133 * Get Taxonomy List.
1134 *
1135 * @return array
1136 */
1137 public static function get_tax_list() {
1138 return apply_filters(
1139 'rtsb/elements/tax_list',
1140 [
1141 'product_cat' => esc_html__( 'Product Category', 'shopbuilder' ),
1142 ]
1143 );
1144 }
1145
1146 /**
1147 * Get Category Query List.
1148 *
1149 * @return array
1150 */
1151 public static function get_cat_list() {
1152 return [
1153 'all' => esc_html__( 'All Categories', 'shopbuilder' ),
1154 'specific_parent' => esc_html__( 'Sub-Categories by Parent', 'shopbuilder' ),
1155 'cat_ids' => esc_html__( 'Select by ID', 'shopbuilder' ),
1156 'selection' => esc_html__( 'Manual Selection', 'shopbuilder' ),
1157 ];
1158 }
1159
1160 /**
1161 * Get Term List.
1162 *
1163 * @param string $taxonomy Taxonomy.
1164 * @param bool $first_term First term.
1165 * @param bool $only_parents Only parent terms.
1166 * @param bool $return_slug Return with slug.
1167 *
1168 * @return int|array
1169 */
1170 public static function get_terms( $taxonomy, $first_term = false, $only_parents = false, $return_slug = false ) {
1171 if ( ! is_admin() ) {
1172 return [];
1173 }
1174
1175 // TODO:: Return Slug always true for all settings.
1176 $term_list = [];
1177 $args = [
1178 'taxonomy' => $taxonomy,
1179 'hide_empty' => false,
1180 ];
1181
1182 if ( $only_parents ) {
1183 $args['parent'] = 0;
1184 }
1185
1186 $terms = get_terms( $args );
1187
1188 if ( empty( $terms ) ) {
1189 return [ esc_html__( 'Nothing found', 'shopbuilder' ) ];
1190 }
1191
1192 foreach ( $terms as $term ) {
1193 if ( $return_slug ) {
1194 $term_list[ $term->slug ] = $term->name;
1195 } else {
1196 $term_list[ $term->term_id ] = $term->name;
1197 }
1198 }
1199
1200 if ( $first_term ) {
1201 return array_keys( $term_list )[0];
1202 } else {
1203 return $term_list;
1204 }
1205 }
1206
1207 /**
1208 * Get product rating.
1209 *
1210 * @param array $args Arguments.
1211 *
1212 * @return string|void
1213 */
1214 public static function get_product_rating_html( $args = [] ) {
1215 global $product;
1216
1217 $html = '';
1218 $rating_count = $product->get_rating_count();
1219 $average_rating = $product->get_average_rating();
1220
1221 if ( ! rtsb()->has_pro() || empty( $args ) ) {
1222 if ( ! $rating_count || empty( wc_get_rating_html( $average_rating, $rating_count ) ) ) {
1223 return $html;
1224 }
1225
1226 $html .= '<div class="product-rating">';
1227 $html .= wc_get_rating_html( $average_rating, $rating_count );
1228 $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1229 $html .= '</div>';
1230
1231 self::print_html( $html, true );
1232
1233 return;
1234 }
1235
1236 if ( empty( $rating_count ) && ! $args['show_empty_rating'] ) {
1237 return '';
1238 }
1239
1240 $preset = ! empty( $args['preset'] ) ? $args['preset'] : 'preset1';
1241 $average = $args['show_average_rating'] ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1242 $count = '';
1243
1244 if ( $args['show_rating_count'] ) {
1245 $count .= '<div class="rtsb-count">';
1246 $count .= sprintf(
1247 /* translators: %s is the number of reviews */
1248 _n( '%s Review', '%s Reviews', $rating_count, 'shopbuilder' ),
1249 $rating_count
1250 );
1251 $count .= '</div>';
1252 }
1253
1254 if ( 'preset2' === $preset ) {
1255 $average = $args['show_average_rating'] ? '<div class="inner-wrapper"><span class="star-icon"></span><span class="average-rating">' . $average_rating . '</span></div>' : '';
1256 }
1257
1258 $html .= '<div class="product-rating ' . esc_attr( $preset ) . '">';
1259
1260 if ( 'preset1' === $preset ) {
1261 $html .= wc_get_rating_html( $average_rating, $rating_count );
1262 $html .= $average;
1263 $html .= $count;
1264 } elseif ( 'preset2' === $preset ) {
1265 $html .= $average;
1266 $html .= $count;
1267 }
1268
1269 $html .= '</div>';
1270
1271 self::print_html( $html, true );
1272 }
1273
1274 public static function get_product_simple_rating_html() {
1275 global $product;
1276 $html = null;
1277 $rating_count = $product->get_rating_count();
1278 $average_rating = $product->get_average_rating();
1279 $html .= '<div class="product-rating">';
1280 $html .= wc_get_rating_html( $average_rating, $rating_count );
1281 $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1282 $html .= '</div>';
1283
1284 self::print_html( $html, true );
1285 }
1286
1287
1288
1289 /**
1290 * Text truncation.
1291 *
1292 * @param string $text_to_truncate Text.
1293 * @param int $limit Limit.
1294 * @param string $after After text.
1295 *
1296 * @return string
1297 */
1298 public static function text_truncation( $text_to_truncate, $limit, $after = '&#8230;' ) {
1299 if ( empty( $limit ) ) {
1300 return $text_to_truncate;
1301 }
1302
1303 $limit++;
1304
1305 $text = '';
1306
1307 if ( mb_strlen( $text_to_truncate ) > $limit ) {
1308 $subex = mb_substr( wp_strip_all_tags( $text_to_truncate ), 0, $limit );
1309 $exwords = explode( ' ', $subex );
1310 $excut = - ( mb_strlen( $exwords[ count( $exwords ) - 1 ] ) );
1311
1312 if ( $excut < 0 ) {
1313 $text .= mb_substr( $subex, 0, $excut ) . $after;
1314 } else {
1315 $text .= $subex . $after;
1316 }
1317 } else {
1318 $text .= $text_to_truncate;
1319 }
1320
1321 return $text;
1322 }
1323
1324 /**
1325 * Promo Badge HTML
1326 *
1327 * @param string $text Text.
1328 * @param string $class Class.
1329 *
1330 * @return void
1331 */
1332 public static function get_badge_html( $text, $class = 'fill' ) {
1333 if ( self::is_module_active( 'product_badges' ) && 'rtsb_yes' === $text ) {
1334 do_action( 'rtsb/modules/product_badges/frontend/display' );
1335
1336 return;
1337 }
1338
1339 if ( empty( $text ) ) {
1340 return;
1341 }
1342 ob_start();
1343 ?>
1344
1345 <ul class="rtsb-promotion-list">
1346 <li class="rtsb-promotion-list-item">
1347 <span class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1348 </li>
1349 </ul>
1350
1351 <?php
1352 self::print_html( ob_get_clean() );
1353 }
1354
1355 /**
1356 * Categories HTML
1357 *
1358 * @param int $id Product ID.
1359 * @param string $class Custom class.
1360 *
1361 * @return void|string
1362 */
1363 public static function get_categories_list( $id, $class = 'rtsb-category-outline' ) {
1364 if ( empty( $id ) ) {
1365 return '';
1366 }
1367
1368 ob_start();
1369 ?>
1370
1371 <ul class="rtsb-category-list <?php echo esc_attr( $class ); ?>">
1372 <?php
1373 self::print_html(
1374 wc_get_product_category_list(
1375 $id,
1376 '</li><li class="rtsb-category-list-item">',
1377 '<li class="rtsb-category-list-item">',
1378 '</li>'
1379 )
1380 );
1381 ?>
1382 </ul>
1383
1384 <?php
1385 self::print_html( ob_get_clean() );
1386 }
1387
1388 /**
1389 * Get Featured Image HTML.
1390 *
1391 * @param string $type Image type.
1392 * @param int $post_id Post ID.
1393 * @param string $f_img_size Image size.
1394 * @param null $default_img_id Default image ID.
1395 * @param array $custom_img_size Custom image size.
1396 * @param bool $lazy Lazy load check.
1397 * @param bool $hover Hover image.
1398 * @param bool $gallery Gallery image.
1399 * @param bool $custom_image_id Custom category image ID.
1400 *
1401 * @return string|null
1402 */
1403 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 ) {
1404 $img_html = null;
1405 $attachment_id = null;
1406 $c_size = false;
1407 $hover_class = '';
1408 $post_title = '';
1409
1410 if ( 'rtsb_custom' === $f_img_size ) {
1411 $f_img_size = 'full';
1412 $c_size = true;
1413 }
1414
1415 if ( ! rtsb()->has_pro() ) {
1416 $gallery = false;
1417 }
1418
1419 if ( $hover ) {
1420 $a_id = $post_id;
1421 $hover_class = ' rtsb-img-hover';
1422 } elseif ( $gallery ) {
1423 $a_id = $post_id;
1424 } else {
1425 if ( 'product' === $type ) {
1426 $a_id = get_post_thumbnail_id( $post_id );
1427 $post_title = get_the_title( $post_id );
1428 } elseif ( 'category' === $type ) {
1429 $a_id = $custom_image_id ? $custom_image_id : get_term_meta( $post_id, 'thumbnail_id', true );
1430 $post_title = get_term( $post_id )->name;
1431 } else {
1432 $a_id = $default_img_id;
1433 }
1434 }
1435
1436 $img_alt = trim( wp_strip_all_tags( get_post_meta( $a_id, '_wp_attachment_image_alt', true ) ) );
1437 $alt_tag = ! empty( $img_alt ) ? $img_alt : wp_strip_all_tags( $post_title );
1438 $lazy_class = $lazy ? ' swiper-lazy' : '';
1439 $attr = [
1440 'class' => 'img-responsive rtsb-product-image' . $lazy_class . $hover_class,
1441 'alt' => $alt_tag,
1442 ];
1443
1444 if ( $a_id ) {
1445 $img_html = wp_get_attachment_image( $a_id, $f_img_size, false, $attr );
1446 $attachment_id = $a_id;
1447 }
1448
1449 if ( ! $img_html && $default_img_id ) {
1450 $img_html = wp_get_attachment_image( $default_img_id, $f_img_size, false, $attr );
1451 $attachment_id = $default_img_id;
1452 }
1453
1454 if ( $img_html && $c_size ) {
1455 preg_match( '@src="([^"]+)"@', $img_html, $match );
1456 $img_src = array_pop( $match );
1457 $w = ! empty( $custom_img_size['width'] ) ? absint( $custom_img_size['width'] ) : null;
1458 $h = ! empty( $custom_img_size['height'] ) ? absint( $custom_img_size['height'] ) : null;
1459 $c = ! empty( $custom_img_size['crop'] ) && 'soft' === $custom_img_size['crop'] ? false : true;
1460
1461 if ( $w && $h ) {
1462 $image = self::image_resize( $img_src, $w, $h, $c, false );
1463
1464 if ( ! empty( $image ) ) {
1465 [ $src, $width, $height ] = $image;
1466
1467 $hwstring = image_hwstring( $width, $height );
1468 $attachment = get_post( $attachment_id );
1469 $attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment, $f_img_size );
1470
1471 if ( $lazy ) {
1472 $attr['data-src'] = $src;
1473 } else {
1474 $attr['src'] = $src;
1475 }
1476
1477 $attr = array_map( 'esc_attr', $attr );
1478 $img_html = rtrim( "<img $hwstring" );
1479
1480 foreach ( $attr as $name => $value ) {
1481 $img_html .= " $name=" . '"' . $value . '"';
1482 }
1483
1484 $img_html .= ' />';
1485 }
1486 }
1487 }
1488
1489 if ( ! $img_html ) {
1490 $hwstring = image_hwstring( 160, 160 );
1491 $attr = isset( $attr['src'] ) ? apply_filters( 'wp_get_attachment_image_attributes', $attr, false, $f_img_size ) : [];
1492 $attr['class'] = 'default-img';
1493 $attr['src'] = esc_url( rtsb()->get_assets_uri( 'images/demo.png' ) );
1494 $attr['alt'] = esc_html__( 'Default Image', 'shopbuilder' );
1495 $img_html = rtrim( "<img $hwstring" );
1496
1497 foreach ( $attr as $name => $value ) {
1498 $img_html .= " $name=" . '"' . $value . '"';
1499 }
1500
1501 $img_html .= ' />';
1502 }
1503
1504 if ( $lazy ) {
1505 $img_html = $img_html . '<div class="swiper-lazy-preloader swiper-lazy-preloader"></div>';
1506 }
1507
1508 return $img_html;
1509 }
1510
1511 /**
1512 * Call the Image resize model for resize function
1513 *
1514 * @param string $url URL.
1515 * @param int $width Width.
1516 * @param int $height Height.
1517 * @param string $crop Crop.
1518 * @param bool|true $single Single.
1519 * @param bool|false $upscale Upscale.
1520 *
1521 * @return array|bool|string
1522 */
1523 public static function image_resize( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
1524 $rtResize = new ReSizer();
1525
1526 return $rtResize->process( $url, $width, $height, $crop, $single, $upscale );
1527 }
1528
1529 /**
1530 * Get Product Image
1531 *
1532 * @param string $f_image Featured Image.
1533 * @param string $h_image Hover Image.
1534 *
1535 * @return void
1536 */
1537 public static function get_product_image( $f_image, $h_image = null ) {
1538 if ( empty( $f_image ) ) {
1539 return;
1540 }
1541
1542 echo wp_kses( $f_image, self::allowedHtml( 'image' ) );
1543
1544 if ( ! empty( $h_image ) ) {
1545 echo wp_kses( $h_image, self::allowedHtml( 'image' ) );
1546 }
1547 }
1548
1549 /**
1550 * Post Custom Field value.
1551 *
1552 * @param int $post_id Post id.
1553 * @param string $field_key Custom Field Key.
1554 * @param string $field_fallback FallBack text.
1555 *
1556 * @return string|void
1557 */
1558 public static function get_post_custom_field_value( $post_id, $field_key = '', $field_fallback = '' ) {
1559 if ( ! $post_id || ! $field_key ) {
1560 return;
1561 }
1562 $field_value = get_post_meta( $post_id, $field_key, true );
1563 if ( empty( $field_value ) ) {
1564 $field_value = ! empty( $field_fallback ) ? $field_fallback : $field_value;
1565 }
1566 $field_value = apply_filters( 'rtsb/get_post_custom_field_value/' . $field_key, $field_value, $field_key );
1567
1568 return sprintf( '<span class="rtsb-woo-custom-field">%s</span>', $field_value );
1569 }
1570
1571 /**
1572 * Elementor Icon
1573 *
1574 * @param array $control Elementor Control array.
1575 * @param string $class Custom class.
1576 * @param string $builder Builder name.
1577 *
1578 * @return string
1579 */
1580 public static function icons_manager( $control, $class = '', $builder = 'elementor' ): string {
1581 if ( empty( $control['value'] ) ) {
1582 return '';
1583 }
1584 if ( is_array( $control['value'] ) ) {
1585 $cache_key = 'icons_managet_' . str_replace( ' ', '', md5( serialize( $control['value'] ) ) );
1586 } else {
1587 $cache_key = 'icons_managet_' . str_replace( ' ', '', $control['value'] );
1588 }
1589 if ( isset( self::$cache[ $cache_key ] ) ) {
1590 return self::$cache[ $cache_key ];
1591 }
1592
1593 $attributes = [
1594 'aria-hidden' => 'true',
1595 ];
1596
1597 if ( ! empty( $class ) ) {
1598 $attributes['class'] = esc_attr( $class );
1599 }
1600
1601 ob_start();
1602
1603 if ( defined( 'ELEMENTOR_VERSION' ) && ! empty( $control ) && 'elementor' === $builder ) {
1604 Icons_Manager::render_icon( $control, $attributes );
1605 }
1606
1607 $icons = ob_get_clean();
1608 self::$cache[ $cache_key ] = $icons;
1609 return $icons;
1610 }
1611
1612 /**
1613 * Get product swatches.
1614 *
1615 * @param string $type Swatch type.
1616 *
1617 * @return void
1618 */
1619 public static function get_product_swatches( $type ) {
1620 ?>
1621 <div class="rtsb-swatches <?php echo esc_attr( $type ); ?>-layout">
1622 <?php
1623 do_action( 'rtwpvs_show_archive_variation' );
1624 ?>
1625 </div>
1626 <?php
1627 }
1628
1629 /**
1630 * Get sale badge
1631 *
1632 * @param string $type Badge type.
1633 * @param string $text Badge text.
1634 * @param string $out_of_stock_text Out of stock text.
1635 *
1636 * @return string
1637 */
1638 public static function get_sale_badge( $type, $text, $out_of_stock_text ) {
1639 global $product;
1640
1641 if ( ! $product->is_in_stock() ) {
1642 return $out_of_stock_text;
1643 }
1644
1645 if ( ! $product->is_on_sale() ) {
1646 return '';
1647 }
1648
1649 $badge_text = '';
1650 $percentage = self::calculate_sale_percentage( $product );
1651
1652 if ( $percentage > 0 ) {
1653 $badge_text = '-' . round( $percentage ) . '%';
1654 }
1655
1656 if ( 'text' === $type ) {
1657 $badge_text = $text;
1658 }
1659
1660 return $badge_text;
1661 }
1662
1663 /**
1664 * Get sale badge
1665 *
1666 * @param object $product Product Object.
1667 * @param string $type Badge type.
1668 * @param string $text Badge text.
1669 * @param string $out_of_stock_text Out of stock text.
1670 *
1671 * @return string
1672 */
1673 public static function get_promo_badge( $product, $type, $text, $out_of_stock_text ) {
1674 $disable_badges = self::get_option( 'general', 'guest_user', 'hide_badges', '' );
1675 $badges_visibility = rtsb()->has_pro() && ! is_user_logged_in() && $disable_badges;
1676
1677 if ( $badges_visibility ) {
1678 return '';
1679 }
1680
1681 if ( is_null( $product ) ) {
1682 global $product;
1683 }
1684
1685 if ( ! $product instanceof WC_Product ) {
1686 return '';
1687 }
1688
1689 if ( ! $product->is_in_stock() ) {
1690 return $out_of_stock_text;
1691 }
1692
1693 if ( ! $product->is_on_sale() ) {
1694 return '';
1695 }
1696
1697 $badge_text = '';
1698 $percentage = self::calculate_sale_percentage( $product );
1699
1700 if ( $percentage > 0 ) {
1701 $badge_text = '-' . round( $percentage ) . '%';
1702 }
1703
1704 if ( 'text' === $type ) {
1705 $badge_text = $text;
1706 }
1707
1708 return $badge_text;
1709 }
1710
1711 /**
1712 * Calculate Sale Percentage
1713 *
1714 * @param object $product Product object.
1715 *
1716 * @return float|int|string
1717 */
1718 public static function calculate_sale_percentage( $product = null ) {
1719 if ( is_null( $product ) ) {
1720 global $product;
1721 }
1722
1723 if ( ! $product instanceof WC_Product ) {
1724 return '';
1725 }
1726
1727 if ( ! $product->is_on_sale() ) {
1728 return '';
1729 }
1730
1731 $percentage = null;
1732 $max_percentage = '';
1733
1734 if ( $product->is_type( 'simple' ) ) {
1735 $max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
1736 } elseif ( $product->is_type( 'variable' ) ) {
1737 $max_percentage = 0;
1738
1739 foreach ( $product->get_children() as $child_id ) {
1740
1741 $variation = wc_get_product( $child_id );
1742
1743 $price = $variation->get_regular_price();
1744 $sale = $variation->get_sale_price();
1745
1746 if ( 0 !== $price && ! empty( $sale ) ) {
1747 $percentage = ( $price - $sale ) / $price * 100;
1748 }
1749
1750 if ( $percentage > $max_percentage ) {
1751 $max_percentage = $percentage;
1752 }
1753 }
1754 }
1755
1756 if ( $max_percentage > 0 ) {
1757 return round( $max_percentage );
1758 }
1759
1760 return 0;
1761 }
1762
1763 /**
1764 * Pagination
1765 *
1766 * @param string $pages Pages.
1767 * @param integer $range Range.
1768 * @param boolean $ajax Ajax.
1769 *
1770 * @return string
1771 */
1772 public static function custom_pagination( $pages = '', $range = 4, $ajax = false ) {
1773 $html = null;
1774 $visible_range = apply_filters( 'rtsb/elements/pagination/visible_range', ( $range * 2 ) + 1 );
1775
1776 global $paged;
1777
1778 if ( is_front_page() ) {
1779 $paged = ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1;
1780 } else {
1781 $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
1782 }
1783
1784 if ( empty( $paged ) ) {
1785 $paged = 1;
1786 }
1787
1788 if ( '' === $pages ) {
1789 global $wp_query;
1790
1791 $pages = $wp_query->max_num_pages;
1792
1793 if ( ! $pages ) {
1794 $pages = 1;
1795 }
1796 }
1797
1798 $ajaxClass = null;
1799 $dataAttr = null;
1800
1801 if ( $ajax ) {
1802 $ajaxClass = ' rtsb-pagination-ajax';
1803 $dataAttr = "data-paged='1'";
1804 $dataAttr .= ' data-visible-range="' . esc_attr( $visible_range ) . '"';
1805 }
1806
1807 if ( 1 !== $pages ) {
1808 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
1809 $html .= '<ul class="pagination-list">';
1810
1811 if ( $paged > 2 && $paged > $visible_range + 1 && $visible_range < $pages ) {
1812 $html .= "<li><a data-paged='1' href='" . get_pagenum_link( 1 ) . "' aria-label='First'>&laquo;</a></li>";
1813 }
1814
1815 if ( $paged > 1 && $visible_range < $pages ) {
1816 $p = $paged - 1;
1817 $html .= "<li><a data-paged='{$p}' href='" . get_pagenum_link( $p ) . "' aria-label='Previous'>&lsaquo;</a></li>";
1818 }
1819
1820 for ( $i = 1; $i <= $pages; $i++ ) {
1821 if ( 1 != $pages && ( ! ( $i >= $paged + $visible_range + 1 || $i <= $paged - $visible_range - 1 ) || $pages <= $visible_range ) ) {
1822 $html .= ( $paged == $i ) ? '<li class="active"><span>' . $i . '</span></li>' : "<li><a data-paged='{$i}' href='" . get_pagenum_link( $i ) . "'>" . $i . '</a></li>';
1823 }
1824 }
1825
1826 if ( $paged < $pages && $visible_range < $pages ) {
1827 $p = $paged + 1;
1828 $html .= "<li><a data-paged='{$p}' href=\"" . get_pagenum_link( $paged + 1 ) . "\" aria-label='Next'>&rsaquo;</a></li>";
1829 }
1830
1831 if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $visible_range < $pages ) {
1832 $html .= "<li><a data-paged='{$pages}' href='" . get_pagenum_link( $pages ) . "' aria-label='Last'>&raquo;</a></li>";
1833 }
1834
1835 $html .= '</ul>';
1836 $html .= '</div>';
1837 }
1838
1839 return $html;
1840 }
1841
1842 /**
1843 * Action Buttons HTMl
1844 *
1845 * @param array $items Items.
1846 * @param array $ajax_cart Ajax cart HTML.
1847 * @param string $preset Button preset.
1848 * @param array $position Position.
1849 * @param string $placement Placement.
1850 *
1851 * @return void|string
1852 */
1853 public static function get_formatted_action_buttons( $items, $ajax_cart = '', $preset = 'preset1', $position = 'above', $placement = 'top' ) {
1854 if ( empty( $items ) ) {
1855 return;
1856 }
1857
1858 $html = '';
1859 $class = '';
1860 $args = [ $items, $ajax_cart, $preset, $position, $placement ];
1861
1862 if ( ( 'top' === $placement && 'after' === $position ) || ( 'bottom' === $placement && 'above' === $position ) ) {
1863 return apply_filters( 'rtsb/elements/elementor/formatted_action_buttons', $html, $args );
1864 }
1865
1866 if ( 'preset2' === $preset ) {
1867 $class = 'rtsb-action-buttons-vertical vertical-delay-effect ' . $preset;
1868 } elseif ( 'preset4' === $preset ) {
1869 $class = 'rtsb-action-buttons-vertical rtsb-action-buttons-vertical-left vertical-delay-effect ' . $preset;
1870 } elseif ( 'preset1' === $preset || 'preset3' === $preset ) {
1871 $class = 'rtsb-action-buttons-cart-box-width-auto horizontal-floating-btn ' . $preset;
1872 }
1873
1874 if ( 'after' === $position && 'preset1' === $preset ) {
1875 $class .= ' after-content';
1876 }
1877
1878 if ( 'preset3' === $preset ) {
1879 $html .= '<div class="rtsb-action-buttons top-part ' . esc_attr( $preset ) . '">';
1880 $html .= '<ul class="rtsb-action-button-list">';
1881
1882 ob_start();
1883 /**
1884 * Additional formatted action buttons hook.
1885 */
1886 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1887 $html .= ob_get_clean();
1888
1889 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1890 $html .= self::get_action_button_by_type( $items, 'compare' );
1891 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1892 $html .= '</ul>';
1893 $html .= '</div>';
1894 $html .= '<div class="rtsb-action-buttons bottom-part ' . esc_attr( $preset ) . '">';
1895 $html .= '<ul class="rtsb-action-button-list">';
1896 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1897 $html .= '</ul>';
1898 $html .= '</div>';
1899 } elseif ( 'preset1' === $preset || 'preset2' === $preset || 'preset4' === $preset ) {
1900 $html .= '<div class="rtsb-action-buttons ' . esc_attr( $class ) . '">';
1901 $html .= '<ul class="rtsb-action-button-list">';
1902 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1903
1904 ob_start();
1905 /**
1906 * Additional formatted action buttons hook.
1907 */
1908 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1909 $html .= ob_get_clean();
1910
1911 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1912 $html .= self::get_action_button_by_type( $items, 'compare' );
1913 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1914 $html .= '</ul>';
1915 $html .= '</div>';
1916 }
1917
1918 return apply_filters( 'rtsb/elements/elementor/formatted_action_buttons', $html, $args );
1919 }
1920
1921 /**
1922 * Get Action Button HTML
1923 *
1924 * @param array $items Items.
1925 * @param string $type Button type.
1926 * @param string $cart_html Ajax cart HTML.
1927 * @param string $wrapper Wrapper tag.
1928 *
1929 * @return void|string
1930 */
1931 public static function get_action_button_by_type( $items, $type, $cart_html = '', $wrapper = 'li' ) {
1932 if ( ! in_array( $type, $items, true ) ) {
1933 return;
1934 }
1935
1936 $html = '';
1937 $class = 'rtsb-action-button-item';
1938
1939 if ( 'add_to_cart' === $type ) {
1940 $class .= ' rtsb-cart' . ( empty( $cart_html ) ? esc_attr( ' no-cart-button' ) : '' );
1941 } else {
1942 $class .= ' rtsb-' . esc_attr( str_replace( '_', '-', $type ) );
1943 }
1944
1945 $html .= '<' . esc_attr( $wrapper ) . ' class="' . esc_attr( $class ) . '">';
1946
1947 if ( ( 'add_to_cart' === $type ) && ( ! empty( $cart_html ) ) ) {
1948 $html .= $cart_html;
1949 } else {
1950 $html .= shortcode_exists( 'rtsb_' . $type . '_button' ) ? do_shortcode( '[rtsb_' . $type . '_button]' ) : null;
1951 }
1952
1953 $html .= '</' . esc_attr( $wrapper ) . '>';
1954
1955 return apply_filters( 'rtsb/elements/elementor/get_action_button_by_type', $html );
1956 }
1957
1958 /**
1959 * Social Share Platforms.
1960 *
1961 * @return mixed|null
1962 */
1963 public static function social_share_platforms_list() {
1964 return apply_filters(
1965 'rtsb/settings/social_share/platforms',
1966 [
1967 [
1968 'value' => 'facebook',
1969 'label' => esc_html__( 'Facebook', 'shopbuilder' ),
1970 ],
1971 [
1972 'value' => 'twitter',
1973 'label' => esc_html__( 'Twitter', 'shopbuilder' ),
1974 ],
1975 [
1976 'value' => 'linkedin',
1977 'label' => esc_html__( 'Linkedin', 'shopbuilder' ),
1978 ],
1979 [
1980 'value' => 'pinterest',
1981 'label' => esc_html__( 'Pinterest', 'shopbuilder' ),
1982 ],
1983 [
1984 'value' => 'skype',
1985 'label' => esc_html__( 'Skype', 'shopbuilder' ),
1986 ],
1987 [
1988 'value' => 'whatsapp',
1989 'label' => esc_html__( 'Whatsapp', 'shopbuilder' ),
1990 ],
1991 [
1992 'value' => 'reddit',
1993 'label' => esc_html__( 'Reddit', 'shopbuilder' ),
1994 ],
1995 [
1996 'value' => 'telegram',
1997 'label' => esc_html__( 'Telegram', 'shopbuilder' ),
1998 ],
1999 ]
2000 );
2001 }
2002
2003 /**
2004 * Get Social Share link HTML.
2005 *
2006 * @param int $id Post ID.
2007 * @param array $types Preset type.
2008 * @param string $preset Style type.
2009 * @param boolean $show_icon Show icon.
2010 * @param boolean $show_text Show text.
2011 *
2012 * @return string
2013 */
2014 public static function get_social_share_html( int $id, array $types, string $preset = 'default', $show_icon = true, $show_text = true ) {
2015 $attr = [ 'postid' => $id ];
2016 $output = '';
2017
2018 if ( empty( $types ) ) {
2019 return $output;
2020 }
2021
2022 foreach ( $types as $type ) {
2023 $link = [];
2024 $link['type'] = $type['share_items'];
2025 $link['class'] = '';
2026 $link['img'] = apply_filters( 'rtsb/elements/share/default_img', '', $id, $link );
2027
2028 if ( 'site' === $id ) {
2029 $link['url'] = home_url();
2030 $link['title'] = wp_strip_all_tags( get_bloginfo( 'name' ) );
2031 } elseif ( 0 === strpos( $id, 'http' ) ) {
2032 $link['url'] = $id;
2033 $link['title'] = '';
2034 } else {
2035 $link['url'] = get_permalink( $id );
2036 $link['title'] = wp_strip_all_tags( get_the_title( $id ) );
2037
2038 if ( has_post_thumbnail( $id ) ) {
2039 $link['img'] = wp_get_attachment_image_url( get_post_thumbnail_id( $id ), 'full' );
2040 }
2041
2042 $link['img'] = apply_filters( 'rtsb/elements/share/single_img', $link['img'], $id, $link );
2043 }
2044
2045 $link['url'] = apply_filters( 'rtsb/elements/share/url', $link['url'], $link );
2046
2047 switch ( $type['share_items'] ) {
2048 case 'facebook':
2049 $link['link'] = esc_url( 'https://www.facebook.com/sharer/sharer.php?u=' . $link['url'] . '&display=popup&ref=plugin&src=share_button' );
2050 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="18.8125" height="32" viewBox="0 0 602 1024"><path d="M548 6.857v150.857h-89.714q-49.143 0-66.286 20.571t-17.143 61.714v108h167.429l-22.286 169.143h-145.143v433.714h-174.857v-433.714h-145.714v-169.143h145.714v-124.571q0-106.286 59.429-164.857t158.286-58.571q84 0 130.286 6.857z"></path></svg>';
2051 $link['attr_title'] = esc_html__( 'Share on Facebook', 'shopbuilder' );
2052 $link['social_network'] = 'Facebook';
2053 $link['social_action'] = 'Share';
2054 break;
2055 case 'twitter':
2056 $link['link'] = esc_url( 'https://twitter.com/intent/tweet?text=' . htmlspecialchars( rawurlencode( html_entity_decode( $link['title'], ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' ) . '&url=' . $link['url'] );
2057 $link['icon'] = '<svg width="24" height="24" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" /></svg>';
2058 $link['attr_title'] = esc_html__( 'Share on Twitter', 'shopbuilder' );
2059 $link['social_network'] = 'Twitter';
2060 $link['social_action'] = 'Tweet';
2061 break;
2062 case 'pinterest':
2063 $link['link'] = esc_url( 'https://pinterest.com/pin/create/button/?url=' . $link['url'] . '&media=' . $link['img'] . '&description=' . $link['title'] );
2064 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="22.84375" height="32" viewBox="0 0 731 1024"><path d="M0 341.143q0-61.714 21.429-116.286t59.143-95.143 86.857-70.286 105.714-44.571 115.429-14.857q90.286 0 168 38t126.286 110.571 48.571 164q0 54.857-10.857 107.429t-34.286 101.143-57.143 85.429-82.857 58.857-108 22q-38.857 0-77.143-18.286t-54.857-50.286q-5.714 22.286-16 64.286t-13.429 54.286-11.714 40.571-14.857 40.571-18.286 35.714-26.286 44.286-35.429 49.429l-8 2.857-5.143-5.714q-8.571-89.714-8.571-107.429 0-52.571 12.286-118t38-164.286 29.714-116q-18.286-37.143-18.286-96.571 0-47.429 29.714-89.143t75.429-41.714q34.857 0 54.286 23.143t19.429 58.571q0 37.714-25.143 109.143t-25.143 106.857q0 36 25.714 59.714t62.286 23.714q31.429 0 58.286-14.286t44.857-38.857 32-54.286 21.714-63.143 11.429-63.429 3.714-56.857q0-98.857-62.571-154t-163.143-55.143q-114.286 0-190.857 74t-76.571 187.714q0 25.143 7.143 48.571t15.429 37.143 15.429 26 7.143 17.429q0 16-8.571 41.714t-21.143 25.714q-1.143 0-9.714-1.714-29.143-8.571-51.714-32t-34.857-54-18.571-61.714-6.286-60.857z"></path></svg>';
2065 $link['attr_title'] = esc_html__( 'Share on Pinterest', 'shopbuilder' );
2066 $link['social_network'] = 'Pinterest';
2067 $link['social_action'] = 'Pin';
2068 break;
2069 case 'linkedin':
2070 $link['link'] = esc_url( 'https://www.linkedin.com/shareArticle?url=' . $link['url'] . '&title=' . $link['title'] );
2071 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="27.4375" height="32" viewBox="0 0 878 1024"><path d="M199.429 357.143v566.286h-188.571v-566.286h188.571zM211.429 182.286q0.571 41.714-28.857 69.714t-77.429 28h-1.143q-46.857 0-75.429-28t-28.571-69.714q0-42.286 29.429-70t76.857-27.714 76 27.714 29.143 70zM877.714 598.857v324.571h-188v-302.857q0-60-23.143-94t-72.286-34q-36 0-60.286 19.714t-36.286 48.857q-6.286 17.143-6.286 46.286v316h-188q1.143-228 1.143-369.714t-0.571-169.143l-0.571-27.429h188v82.286h-1.143q11.429-18.286 23.429-32t32.286-29.714 49.714-24.857 65.429-8.857q97.714 0 157.143 64.857t59.429 190z"></path></svg>';
2072 $link['attr_title'] = esc_html__( 'Share on LinkedIn', 'shopbuilder' );
2073 $link['social_network'] = 'LinkedIn';
2074 $link['social_action'] = 'Share';
2075 break;
2076 case 'skype':
2077 $link['link'] = esc_url( 'https://web.skype.com/share?url=' . $link['url'] );
2078 $link['icon'] = '<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" class="eapps-social-share-buttons-item-icon"> <path d="M23.016,13.971c0.111-0.638,0.173-1.293,0.173-1.963 c0-6.213-5.014-11.249-11.199-11.249c-0.704,0-1.393,0.068-2.061,0.193C8.939,0.348,7.779,0,6.536,0C2.926,0,0,2.939,0,6.565 c0,1.264,0.357,2.443,0.973,3.445c-0.116,0.649-0.18,1.316-0.18,1.999c0,6.212,5.014,11.25,11.198,11.25 c0.719,0,1.419-0.071,2.099-0.201C15.075,23.656,16.229,24,17.465,24C21.074,24,24,21.061,24,17.435 C24,16.163,23.639,14.976,23.016,13.971z M12.386,19.88c-3.19,0-6.395-1.453-6.378-3.953c0.005-0.754,0.565-1.446,1.312-1.446 c1.877,0,1.86,2.803,4.85,2.803c2.098,0,2.814-1.15,2.814-1.95c0-2.894-9.068-1.12-9.068-6.563c0-2.945,2.409-4.977,6.196-4.753 c3.61,0.213,5.727,1.808,5.932,3.299c0.102,0.973-0.543,1.731-1.662,1.731c-1.633,0-1.8-2.188-4.613-2.188 c-1.269,0-2.341,0.53-2.341,1.679c0,2.402,9.014,1.008,9.014,6.295C18.441,17.882,16.012,19.88,12.386,19.88z"></path> </svg>';
2079 $link['attr_title'] = esc_html__( 'Share on Skype', 'shopbuilder' );
2080 $link['social_network'] = 'Skype';
2081 $link['social_action'] = 'Skype';
2082 break;
2083 case 'whatsapp':
2084 $link['link'] = esc_url( 'https://wa.me/?text=' . $link['title'] . ' ' . $link['url'] );
2085 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-whatsapp" viewBox="0 0 16 16"> <path d="M13.601 2.326A7.854 7.854 0 0 0 7.994 0C3.627 0 .068 3.558.064 7.926c0 1.399.366 2.76 1.057 3.965L0 16l4.204-1.102a7.933 7.933 0 0 0 3.79.965h.004c4.368 0 7.926-3.558 7.93-7.93A7.898 7.898 0 0 0 13.6 2.326zM7.994 14.521a6.573 6.573 0 0 1-3.356-.92l-.24-.144-2.494.654.666-2.433-.156-.251a6.56 6.56 0 0 1-1.007-3.505c0-3.626 2.957-6.584 6.591-6.584a6.56 6.56 0 0 1 4.66 1.931 6.557 6.557 0 0 1 1.928 4.66c-.004 3.639-2.961 6.592-6.592 6.592zm3.615-4.934c-.197-.099-1.17-.578-1.353-.646-.182-.065-.315-.099-.445.099-.133.197-.513.646-.627.775-.114.133-.232.148-.43.05-.197-.1-.836-.308-1.592-.985-.59-.525-.985-1.175-1.103-1.372-.114-.198-.011-.304.088-.403.087-.088.197-.232.296-.346.1-.114.133-.198.198-.33.065-.134.034-.248-.015-.347-.05-.099-.445-1.076-.612-1.47-.16-.389-.323-.335-.445-.34-.114-.007-.247-.007-.38-.007a.729.729 0 0 0-.529.247c-.182.198-.691.677-.691 1.654 0 .977.71 1.916.81 2.049.098.133 1.394 2.132 3.383 2.992.47.205.84.326 1.129.418.475.152.904.129 1.246.08.38-.058 1.171-.48 1.338-.943.164-.464.164-.86.114-.943-.049-.084-.182-.133-.38-.232z"/> </svg>';
2086 $link['attr_title'] = esc_html__( 'Share on Whatsapp', 'shopbuilder' );
2087 $link['social_network'] = 'Whatsapp';
2088 $link['social_action'] = 'Share';
2089 break;
2090 case 'reddit':
2091 $link['link'] = esc_url( 'https://reddit.com/submit?url=' . $link['url'] . '&title=' . $link['title'] );
2092 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><title>ionicons-v5_logos</title><path d="M324,256a36,36,0,1,0,36,36A36,36,0,0,0,324,256Z"/><circle cx="188" cy="292" r="36" transform="translate(-97.43 94.17) rotate(-22.5)"/><path d="M496,253.77c0-31.19-25.14-56.56-56-56.56a55.72,55.72,0,0,0-35.61,12.86c-35-23.77-80.78-38.32-129.65-41.27l22-79L363.15,103c1.9,26.48,24,47.49,50.65,47.49,28,0,50.78-23,50.78-51.21S441,48,413,48c-19.53,0-36.31,11.19-44.85,28.77l-90-17.89L247.05,168.4l-4.63.13c-50.63,2.21-98.34,16.93-134.77,41.53A55.38,55.38,0,0,0,72,197.21c-30.89,0-56,25.37-56,56.56a56.43,56.43,0,0,0,28.11,49.06,98.65,98.65,0,0,0-.89,13.34c.11,39.74,22.49,77,63,105C146.36,448.77,199.51,464,256,464s109.76-15.23,149.83-42.89c40.53-28,62.85-65.27,62.85-105.06a109.32,109.32,0,0,0-.84-13.3A56.32,56.32,0,0,0,496,253.77ZM414,75a24,24,0,1,1-24,24A24,24,0,0,1,414,75ZM42.72,253.77a29.6,29.6,0,0,1,29.42-29.71,29,29,0,0,1,13.62,3.43c-15.5,14.41-26.93,30.41-34.07,47.68A30.23,30.23,0,0,1,42.72,253.77ZM390.82,399c-35.74,24.59-83.6,38.14-134.77,38.14S157,423.61,121.29,399c-33-22.79-51.24-52.26-51.24-83A78.5,78.5,0,0,1,75,288.72c5.68-15.74,16.16-30.48,31.15-43.79a155.17,155.17,0,0,1,14.76-11.53l.3-.21,0,0,.24-.17c35.72-24.52,83.52-38,134.61-38s98.9,13.51,134.62,38l.23.17.34.25A156.57,156.57,0,0,1,406,244.92c15,13.32,25.48,28.05,31.16,43.81a85.44,85.44,0,0,1,4.31,17.67,77.29,77.29,0,0,1,.6,9.65C442.06,346.77,423.86,376.24,390.82,399Zm69.6-123.92c-7.13-17.28-18.56-33.29-34.07-47.72A29.09,29.09,0,0,1,440,224a29.59,29.59,0,0,1,29.41,29.71A30.07,30.07,0,0,1,460.42,275.1Z"/><path d="M323.23,362.22c-.25.25-25.56,26.07-67.15,26.27-42-.2-66.28-25.23-67.31-26.27h0a4.14,4.14,0,0,0-5.83,0l-13.7,13.47a4.15,4.15,0,0,0,0,5.89h0c3.4,3.4,34.7,34.23,86.78,34.45,51.94-.22,83.38-31.05,86.78-34.45h0a4.16,4.16,0,0,0,0-5.9l-13.71-13.47a4.13,4.13,0,0,0-5.81,0Z"/></svg>';
2093 $link['attr_title'] = esc_html__( 'Share on Reddit', 'shopbuilder' );
2094 $link['social_network'] = 'Reddit';
2095 $link['social_action'] = 'Share';
2096 break;
2097 case 'telegram':
2098 $link['link'] = esc_url( 'https://telegram.me/share/url?text=' . $link['title'] . '&url=' . $link['url'] );
2099 $link['icon'] = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-telegram" viewBox="0 0 16 16"> <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.287 5.906c-.778.324-2.334.994-4.666 2.01-.378.15-.577.298-.595.442-.03.243.275.339.69.47l.175.055c.408.133.958.288 1.243.294.26.006.549-.1.868-.32 2.179-1.471 3.304-2.214 3.374-2.23.05-.012.12-.026.166.016.047.041.042.12.037.141-.03.129-1.227 1.241-1.846 1.817-.193.18-.33.307-.358.336a8.154 8.154 0 0 1-.188.186c-.38.366-.664.64.015 1.088.327.216.589.393.85.571.284.194.568.387.936.629.093.06.183.125.27.187.331.236.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.426 1.426 0 0 0-.013-.315.337.337 0 0 0-.114-.217.526.526 0 0 0-.31-.093c-.3.005-.763.166-2.984 1.09z"/> </svg>';
2100 $link['attr_title'] = esc_html__( 'Share on Telegram', 'shopbuilder' );
2101 $link['social_network'] = 'Telegram';
2102 $link['social_action'] = 'Share';
2103 break;
2104 }
2105
2106 $link['label'] = $type['share_text'];
2107 $link['target'] = '_blank';
2108 $link['rel'] = 'nofollow noopener noreferrer';
2109
2110 $data = '';
2111 $link = apply_filters( 'rtsb/elements/share/share_link', $link, $id, $preset );
2112 $icon = apply_filters( 'rtsb/elements/share/share_icon', $link['icon'], $preset );
2113 $target = ! empty( $link['target'] ) ? ' target="' . esc_attr( $link['target'] ) . '" ' : '';
2114 $rel = ! empty( $link['rel'] ) ? ' rel="' . esc_attr( $link['rel'] ) . '" ' : '';
2115 $attr_title = ! empty( $link['attr_title'] ) ? ' title="' . esc_attr( $link['attr_title'] ) . '" ' : '';
2116 $elements = [];
2117
2118 // Add classes.
2119 $css_classes = [
2120 'rtsb-share-btn',
2121 sanitize_html_class( $link['type'] ),
2122 ];
2123 $css_classes = array_merge( $css_classes, explode( ' ', $link['class'] ) );
2124 $css_classes = array_map( 'sanitize_html_class', $css_classes );
2125 $css_classes = implode( ' ', array_filter( $css_classes ) );
2126
2127 unset( $attr['pin-do'], $attr['action'] );
2128
2129 if ( 'pinterest' === $type['share_items'] ) {
2130 $attr['pin-do'] = 'none';
2131 }
2132
2133 if ( 'whatsapp' === $type['share_items'] ) {
2134 $attr['action'] = 'share/whatsapp/share';
2135 }
2136
2137 $attr = apply_filters( 'rtsb/elements/share/link_data', $attr, $link, $id );
2138
2139 if ( ! empty( $attr ) ) {
2140 foreach ( $attr as $key => $val ) {
2141 $data .= ' data-' . sanitize_html_class( $key ) . '="' . esc_attr( $val ) . '"';
2142 }
2143 }
2144
2145 $additional_attr = apply_filters( 'rtsb/elements/share/additional_attr', [], $link, $id, $preset );
2146
2147 if ( ! empty( $additional_attr ) ) {
2148 $attr_output = join( ' ', $additional_attr );
2149
2150 if ( ! empty( $data ) ) {
2151 $attr_output = ' ' . $attr_output;
2152 }
2153
2154 $data .= $attr_output;
2155 }
2156
2157 $elements['wrapper_start'] = sprintf(
2158 '<li class="rtsb-share-item"><a href="%s"%s%s%s class="%s"%s>',
2159 ! empty( $link['link'] ) ? esc_attr( $link['link'] ) : '',
2160 $attr_title,
2161 $target,
2162 $rel,
2163 $css_classes,
2164 $data
2165 );
2166 $elements['wrapper_end'] = '</a></li>';
2167
2168 $elements['icon'] = $show_icon ? '<span class="rtsb-share-icon">' . ( ! empty( $icon ) ? $icon : null ) . '</span>' : null;
2169 $elements['label'] = $show_text && ! empty( $link['label'] ) ? '<span class="rtsb-share-label">' . $link['label'] . '</span>' : null;
2170 $elements['icon_label'] = '<span class="rtsb-share-icon-label">' . $elements['icon'] . $elements['label'] . '</span>';
2171 $elements = apply_filters( 'rtsb/elements/share/output_elements', $elements, $link, $id );
2172
2173 $output .= $elements['wrapper_start'] . $elements['icon_label'] . $elements['wrapper_end'];
2174 }
2175
2176 return apply_filters( 'rtsb/elements/share/list_output', $output );
2177 }
2178
2179 /**
2180 * Social Share Platforms.
2181 *
2182 * @return mixed|null
2183 */
2184 public static function is_module_active( $module ) {
2185 $modulelist = ModuleList::instance()->get_data();
2186 if ( ! empty( $modulelist[ $module ]['active'] ) ) {
2187 return true;
2188 }
2189 }
2190
2191
2192 /***
2193 * Save Settings data
2194 *
2195 * @param $section_id
2196 * @param $block_id
2197 * @param $rawOptions
2198 *
2199 * @return array
2200 */
2201 public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) {
2202 $section_id = ! empty( $section_id ) ? sanitize_text_field( wp_unslash( $section_id ) ) : '';
2203 $block_id = ! empty( $block_id ) ? sanitize_text_field( wp_unslash( $block_id ) ) : '';
2204 $rawOptions = ! empty( $rawOptions ) ? $rawOptions : [];
2205
2206 $results = [
2207 'status' => true,
2208 'message' => '',
2209 ];
2210 if ( ! $section_id || ! $block_id ) {
2211 $results['status'] = false;
2212 $results['message'] = esc_html__( 'Section , block or options may be empty', 'shopbuilder' );
2213
2214 return $results;
2215 }
2216 $sections = Settings::instance()->get_sections();
2217 if ( empty( $sections[ $section_id ] ) || empty( $sections[ $section_id ]['list'][ $block_id ] ) ) {
2218 $results['status'] = false;
2219 $results['message'] = esc_html__( 'No section or block found with given data', 'shopbuilder' );
2220
2221 return $results;
2222 }
2223
2224 $options = DataModel::source()->get_option( $section_id, [], false );
2225 $changed = false;
2226 $fields = [];
2227 if ( isset( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) && ! empty( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) ) {
2228 $fields = $sections[ $section_id ]['list'][ $block_id ]['fields'];
2229 }
2230
2231 if ( empty( $fields ) ) {
2232 if ( isset( $rawOptions['active'] ) ) {
2233 $changed = true;
2234 $options[ $block_id ]['active'] = 'on' === $rawOptions['active'] ? 'on' : '';
2235 }
2236 } else {
2237 foreach ( $rawOptions as $raw_option_key => $raw_value ) {
2238 if ( $raw_option_key === 'active' ) {
2239 $changed = true;
2240 $options[ $block_id ]['active'] = $sections[ $section_id ]['list'][ $block_id ]['active'] = 'on' === $raw_value ? 'on' : '';
2241 } else {
2242 if ( isset( $fields[ $raw_option_key ] ) ) {
2243 $field = $fields[ $raw_option_key ];
2244 if ( $field['type'] === 'switch' ) {
2245 $value = 'on' === $raw_value ? 'on' : '';
2246 } elseif ( 'repeaters' === $field['type'] ) {
2247 $value = stripslashes_deep( $raw_value );
2248 } else {
2249 if ( ! empty( $field['multiple'] ) || in_array( $field['type'], [ 'checkbox', 'search_and_multi_select' ] ) ) {
2250
2251 if ( isset( $raw_value ) && is_array( $raw_value ) ) {
2252 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2253 $value = array_map( $field['sanitize_fn'], $raw_value );
2254 } else {
2255 $value = array_map( 'sanitize_text_field', $raw_value );
2256 }
2257 } else {
2258 $value = [];
2259 }
2260 } else {
2261 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2262 $value = $field['sanitize_fn']( $raw_value );
2263 } else {
2264 $value = sanitize_text_field( $raw_value );
2265 }
2266 }
2267 }
2268 $options[ $block_id ][ $raw_option_key ] = $sections[ $section_id ]['list'][ $block_id ]['fields'][ $raw_option_key ]['value'] = $value ?? null;
2269 $changed = true;
2270 }
2271 }
2272 }
2273 }
2274 if ( ! $changed ) {
2275 $results['status'] = false;
2276 $results['message'] = esc_html__( 'No changes found for update', 'shopbuilder' );
2277
2278 return $results;
2279 }
2280
2281 DataModel::source()->set_option( $section_id, $options );
2282
2283 $results['status'] = true;
2284 $results['message'] = esc_html__( 'Successfully Saved', 'shopbuilder' );
2285 $results['sections'] = $sections;
2286
2287 return $results;
2288 }
2289
2290 /***
2291 * @param $meta_key
2292 * @param $meta_value
2293 *
2294 * @return mixed|void
2295 *
2296 *
2297 * public static function get_post_id_by_meta_key_and_value( $meta_key, $meta_value ) {
2298 * if( ! $meta_key || ! $meta_value ){
2299 * return;
2300 * }
2301 * global $wpdb;
2302 * $prepare_guery = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '%s' and meta_value = '%d'", $meta_key, $meta_value );
2303 * $the_products = $wpdb->get_col( $prepare_guery );
2304 * if( count( $the_products ) > 0 ){
2305 * return $the_products[0];
2306 * }
2307 * return;
2308 * }
2309 */
2310 public static function count_products_by_taxonomies( $terms, $taxonomy = 'product_cat' ) {
2311 if ( empty( $terms ) || ! is_array( $terms ) ) {
2312 return;
2313 }
2314
2315 $args = [
2316 'limit' => -1,
2317 'return' => 'ids',
2318 ];
2319
2320 if ( 'product_cat' === $taxonomy ) {
2321 $args['product_category_id'] = $terms;
2322 } else {
2323 $args['product_tag_id'] = $terms;
2324 }
2325
2326 $query = new WC_Product_Query( $args );
2327
2328 return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2329 }
2330
2331 /**
2332 * Check if product filters widget has ajax.
2333 *
2334 * @param string $page Page name.
2335 *
2336 * @return bool
2337 */
2338 public static function product_filters_has_ajax( $page ) {
2339 if ( empty( $page ) ) {
2340 return false;
2341 }
2342
2343 if ( 'page' === get_post_type() ) {
2344 return false;
2345 }
2346
2347 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2348 if ( ! $id ) {
2349 return false;
2350 }
2351 $cache_key = 'product_filters_has_ajax_' . $id;
2352 if ( isset( self::$cache[ $cache_key ] ) ) {
2353 return self::$cache[ $cache_key ];
2354 }
2355 $elmap = ElementorDataMap::instance();
2356 $ajax = [];
2357
2358 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2359 $ajax[] = isset( $data['settings']['ajax_mode'] ) ? false : true;
2360 }
2361
2362 self::$cache[ $cache_key ] = ! empty( $ajax[0] );
2363 return ! empty( $ajax[0] );
2364 }
2365
2366 /**
2367 * Check if product has applied filter.
2368 *
2369 * @param string $page Page name.
2370 *
2371 * @return bool
2372 */
2373 public static function product_has_applied_filters( $page ) {
2374 if ( empty( $page ) ) {
2375 return false;
2376 }
2377
2378 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2379 $elmap = ElementorDataMap::instance();
2380 $ajax = [];
2381 if ( ! $id ) {
2382 return false;
2383 }
2384 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2385 $ajax[] = ! isset( $data['settings']['active_filter'] );
2386
2387 }
2388
2389 return ! empty( $ajax[0] );
2390 }
2391
2392 /**
2393 * Get WooCommerce product categories.
2394 *
2395 * @param string|null $search_query The search string for category names.
2396 *
2397 * @return array An array of product categories with 'value' and 'label' keys.
2398 */
2399 public static function products_category_query( $search_query = null ) {
2400 if ( ! is_admin() ) {
2401 return [];
2402 }
2403
2404 $cache_key = 'rtsb_product_categories_' . md5( serialize( $search_query ) );
2405
2406 if ( isset( self::$cache[ $cache_key ] ) ) {
2407 return self::$cache[ $cache_key ];
2408 }
2409
2410 $results = wp_cache_get( $cache_key );
2411
2412 if ( ! $results ) {
2413 global $wpdb;
2414 $sql = "SELECT t.term_id, t.name
2415 FROM {$wpdb->terms} t
2416 JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2417 WHERE tt.taxonomy = 'product_cat'";
2418
2419 if ( $search_query ) {
2420 $sql .= ' AND t.name LIKE %s';
2421 $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2422 } else {
2423 $prepared_sql = $sql; // No need for placeholders.
2424 }
2425
2426 $results = $wpdb->get_results( $prepared_sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
2427 // Cache the results in the object cache for future use.
2428 wp_cache_set( $cache_key, $results ); // Adjust the expiration time as needed.
2429 }
2430
2431 $cats = [];
2432 if ( ! is_array( $results ) && ! count( $results ) ) {
2433 return $cats;
2434 }
2435 foreach ( $results as $row ) {
2436 $category_id = $row->term_id;
2437 $category_title = $row->name;
2438
2439 $cats[] = [
2440 'value' => $category_id,
2441 'label' => $category_title,
2442 ];
2443 }
2444
2445 // Cache the results in a static variable for the next call.
2446 self::$cache[ $cache_key ] = $cats;
2447 return $cats;
2448 }
2449
2450 /**
2451 * @param string $search_query Search query.
2452 *
2453 * @return array
2454 */
2455 public static function get_registered_post_types( $search_query = null ) {
2456 // Get all registered post types.
2457 $registered_post_types = get_post_types(
2458 [
2459 'public' => true,
2460 '_builtin' => false,
2461 ],
2462 'objects'
2463 );
2464
2465 unset( $registered_post_types['elementor_library'] );
2466 unset( $registered_post_types['e-landing-page'] );
2467 unset( $registered_post_types['rtsb_builder'] );
2468
2469 $post_types = [];
2470
2471 // Check if a search query is provided.
2472 if ( ! empty( $search_query ) ) {
2473 // Filter post types based on the search query.
2474 $registered_post_types = array_filter(
2475 $registered_post_types,
2476 function ( $post_type ) use ( $search_query ) {
2477 return stripos( $post_type->labels->singular_name, $search_query ) !== false;
2478 }
2479 );
2480 // Check if 'post' match the search query and include them if they do.
2481 if ( stripos( 'post', $search_query ) !== false ) {
2482 $post_types[] = [
2483 'value' => 'post',
2484 'label' => 'Post',
2485 ];
2486 }
2487 } else {
2488 $post_types[] = [
2489 'value' => 'post',
2490 'label' => 'Post',
2491 ];
2492 }
2493
2494 // Convert the filtered post types to an array.
2495 foreach ( $registered_post_types as $post_type ) {
2496 $post_types[] = [
2497 'value' => $post_type->name,
2498 'label' => $post_type->labels->singular_name,
2499 ];
2500 }
2501
2502 return $post_types;
2503 }
2504
2505 /**
2506 * Get Post Types.
2507 *
2508 * @param string $search_query Search query.
2509 *
2510 * @return array
2511 */
2512 public static function get_post_types( $search_query = null, $args = [] ) {
2513
2514 $args = wp_parse_args(
2515 $args,
2516 [
2517 'post_type' => 'any',
2518 'posts_per_page' => 20,
2519 'orderby' => 'ID',
2520 'order' => 'DESC',
2521 // 'suppress_filters' => false, // WPML Support, Remove Others Languages.
2522 ]
2523 );
2524
2525 if ( 'archive' !== $args['post_type'] ) {
2526 if ( $search_query ) {
2527 $args['s'] = $search_query;
2528 }
2529 $posts = [];
2530 $query_results = get_posts( $args );
2531 foreach ( $query_results as $post ) {
2532 $posts[] = [
2533 'value' => $post->ID,
2534 'label' => $post->post_title . ' (' . $post->ID . ')',
2535 ];
2536 }
2537 if ( $search_query ) {
2538 if ( strpos( '-error 404', $search_query ) ) {
2539 $posts[] = [
2540 'value' => 'error',
2541 'label' => __( '404 Error', 'shopbuilder' ),
2542 ];
2543 }
2544 } else {
2545 $posts[] = [
2546 'value' => 'error',
2547 'label' => __( '404 Error', 'shopbuilder' ),
2548 ];
2549 }
2550 } else {
2551 $posts = self::get_registered_post_types( $search_query );
2552 }
2553
2554 return $posts;
2555 }
2556
2557 /**
2558 * Get all Elementor breakpoints.
2559 *
2560 * @return array
2561 */
2562 public static function get_elementor_breakpoints() {
2563 return ( new \Elementor\Core\Breakpoints\Manager() )->get_breakpoints_config();
2564 }
2565
2566 /**
2567 * Render view.
2568 *
2569 * @param string $viewName View name.
2570 * @param array $args View args.
2571 * @param boolean $return View return.
2572 * @return string|void
2573 */
2574 public static function renderView( $viewName, $args = [], $return = false ) {
2575 $viewName = str_replace( '.', '/', $viewName );
2576
2577 if ( ! empty( $args ) && is_array( $args ) ) {
2578 extract( $args );
2579 }
2580
2581 $view_file = rtsb()->plugin_path() . '/resources/' . $viewName . '.php';
2582
2583 if ( ! file_exists( $view_file ) ) {
2584 _doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', esc_html( $view_file ) ), '1.7.0' );
2585
2586 return;
2587 }
2588
2589 if ( $return ) {
2590 ob_start();
2591 include $view_file;
2592
2593 return ob_get_clean();
2594 } else {
2595 include $view_file;
2596 }
2597 }
2598
2599 /**
2600 * Best selling product query.
2601 *
2602 * @param int $minimum_sale Minimum sale/
2603 * @param int $limit Total limit.
2604 *
2605 * @return array|mixed
2606 */
2607 public static function best_selling_products_ids( $minimum_sale = 1, $limit = 10 ) {
2608 $cache_key = 'rtsb_best_selling_products_' . $minimum_sale . '_' . $limit;
2609 // Check if the data is in the cache.
2610 if ( isset( self::$cache[ $cache_key ] ) ) {
2611 return self::$cache[ $cache_key ];
2612 }
2613 $cached_data = get_transient( $cache_key );
2614 if ( $cached_data ) {
2615 self::$cache[ $cache_key ] = $cached_data;
2616 return $cached_data;
2617 }
2618 global $wpdb;
2619 $sql = $wpdb->prepare(
2620 "
2621 SELECT ID
2622 FROM {$wpdb->posts} AS p
2623 LEFT JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
2624 WHERE p.post_type = 'product'
2625 AND p.post_status = 'publish'
2626 AND pm.meta_key = 'total_sales'
2627 AND pm.meta_value >= %d
2628 ORDER BY pm.meta_value + 0 DESC
2629 LIMIT %d
2630 ",
2631 $minimum_sale,
2632 $limit
2633 );
2634 $best_selling = $wpdb->get_col( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
2635 // Cache the result for future use.
2636 set_transient( $cache_key, $best_selling, DAY_IN_SECONDS );
2637 self::$cache[ $cache_key ] = $best_selling;
2638 return $best_selling;
2639 }
2640
2641 /**
2642 * @param null|Object $product Product Object.
2643 * @param int $days_threshold Date String.
2644 *
2645 * @return bool
2646 */
2647 public static function is_new_product( $product = null, $days_threshold = 30 ) {
2648 if ( is_null( $product ) ) {
2649 global $product;
2650 }
2651 if ( ! $product instanceof WC_Product ) {
2652 return false;
2653 }
2654
2655 $product_id = $product->get_id();
2656 $days_threshold = ! empty( $days_threshold ) ? $days_threshold : 30;
2657 $cache_key = 'is_new_product_' . $product_id . '_' . $days_threshold;
2658
2659 if ( isset( self::$cache[ $cache_key ] ) ) {
2660 return self::$cache[ $cache_key ];
2661 }
2662
2663 // Get the product publish date.
2664 $publish_timestamp = strtotime( $product->get_date_created()->date_i18n( 'Y-m-d H:i:s' ) );
2665
2666 // Calculate the difference in days.
2667 $days_difference = abs( time() - $publish_timestamp ) / ( 60 * 60 * 24 );
2668
2669 // Check if the product is considered new.
2670 $is_new = $days_difference <= $days_threshold;
2671
2672 // Cache the result for a day.
2673 self::$cache[ $cache_key ] = $is_new;
2674
2675 return $is_new;
2676 }
2677
2678 /**
2679 * Checks if a feature is disabled for guest users based on the provided option.
2680 *
2681 * @param string $option The option to retrieve from the item.
2682 * @param mixed $default The default value if the option is not found.
2683 *
2684 * @return bool
2685 */
2686 public static function is_guest_feature_disabled( $option, $default ) {
2687 $disabled = self::get_option( 'general', 'guest_user', $option, $default );
2688
2689 return ! is_user_logged_in() && $disabled;
2690 }
2691
2692 /**
2693 * Checks if a feature is disabled for guest users based on the provided option.
2694 *
2695 * @param string $option The option to retrieve from the item.
2696 * @param mixed $default The default value if the option is not found.
2697 *
2698 * @return bool
2699 */
2700 public static function woocommerce_output_all_notices() {
2701 if ( function_exists( 'wc_print_notices' ) ) :
2702 echo '<div class="rtsb-notice">';
2703 woocommerce_output_all_notices();
2704 echo '</div>';
2705 endif;
2706 }
2707
2708 /**
2709 * @param object $product Product.
2710 * @return bool
2711 */
2712 public static function is_visible_qty_input( $product = null ) {
2713 $is_visible_qty = true;
2714 if ( $product instanceof \WC_Product ) {
2715 if ( $product->is_sold_individually() ) {
2716 $is_visible_qty = false;
2717 } elseif ( $product->managing_stock() ) {
2718 if ( in_array( $product->get_backorders(), [ 'notify' , 'yes' ], true ) ) {
2719 $is_visible_qty = true;
2720 } elseif ( $product->get_stock_quantity() < 2 ) {
2721 $is_visible_qty = false;
2722 }
2723 }
2724 }
2725 return $is_visible_qty;
2726 }
2727
2728 /**
2729 * @param int $object_id Object id.
2730 * @param string $element_type element type.
2731 * @param string|null $current_lang null for current language, default for default languages.
2732 * @return false|mixed|null
2733 */
2734 public static function wpml_object_id( $object_id, $element_type, $current_lang = 'default' ) {
2735 if ( ! defined( 'ICL_SITEPRESS_VERSION' ) ) {
2736 return $object_id;
2737 }
2738 if ( ! $object_id || ! $element_type ) {
2739 return $object_id;
2740 }
2741 if ( 'default' === $current_lang ) {
2742 $lang = apply_filters( 'wpml_default_language', null );
2743 } else {
2744 $lang = null;
2745 }
2746 return apply_filters( 'wpml_object_id', $object_id, $element_type, false, $lang );
2747 }
2748
2749 /**
2750 * @return string
2751 */
2752 public static function wpml_current_language() {
2753 $current = apply_filters( 'wpml_current_language', null );
2754 $default = apply_filters( 'wpml_default_language', null );
2755 return $default !== $current ? $current : null;
2756 }
2757 }
2758