PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.1.0
ShopBuilder – WooCommerce Builder For Elementor v2.1.0
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.0, at app/Helpers/Fns.php

2,474 lines 73.6 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 /**
49 * @param $plugin_slug
50 *
51 * @return bool
52 */
53 public static function check_plugin_installed( $plugin_slug ): bool {
54 $installed_plugins = get_plugins();
55
56 return array_key_exists( $plugin_slug, $installed_plugins ) || in_array( $plugin_slug, $installed_plugins, true );
57 }
58
59 /**
60 * @param $plugin_slug
61 *
62 * @return bool
63 */
64 public static function check_plugin_active( $plugin_slug ): bool {
65 if ( is_plugin_active( $plugin_slug ) ) {
66 return true;
67 }
68
69 return false;
70 }
71
72 /**
73 * @param $data
74 *
75 * @return mixed|string
76 */
77 public static function stripslashes_value( $data ) {
78 if ( is_string( $data ) && strpos( $data, '\\' ) !== false ) {
79 return stripslashes( $data );
80 } elseif ( is_array( $data ) ) {
81 foreach ( $data as $key => $value ) {
82 $data[ $key ] = self::stripslashes_value( $value );
83 }
84 }
85
86 return $data;
87 }
88
89 /**
90 * @param $string
91 *
92 * @return array
93 */
94 public static function multiselect_settings_field_value( $string ) {
95
96 $values = [];
97 if ( empty( $string ) ) {
98 return $values;
99 }
100
101 $stripslashes_data = self::stripslashes_value( $string );
102 if ( is_array( $stripslashes_data ) && count( $stripslashes_data ) ) {
103 foreach ( $stripslashes_data as $item ) {
104 $item = is_array( $item ) ? $item : json_decode( $item, true );
105 $values[] = $item['value'];
106 }
107 }
108
109 return $values;
110 }
111
112 /**
113 * @return bool
114 */
115 public static function check_is_block_theme(): bool {
116 global $wp_version;
117
118 return version_compare( $wp_version, '5.9', '>=' ) && function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
119 }
120
121 /**
122 * @param string $template_name Template name.
123 * @param string $template_path Template path. (default: '').
124 * @param string $plugin_path Plugin path. (default: ''). fallback file from plugin
125 *
126 * @return mixed|void
127 */
128 public static function locate_template( $template_name, $template_path = '', $plugin_path = '' ) {
129 $template_name = $template_name . '.php';
130 if ( ! $template_path ) {
131 $template_path = rtsb()->get_template_path();
132 }
133 if ( ! $plugin_path ) {
134 $plugin_path = RTSB_ABSPATH . 'templates/';
135 }
136
137 $template_rtsb_path = trailingslashit( $template_path ) . $template_name;
138 $template_path = '/' . $template_name;
139 $plugin_path = $plugin_path . $template_name;
140 $pro_plugin_path = rtsb()->has_pro() ? RTSBPRO_PATH . 'templates/' . $template_name : '';
141
142 $located = locate_template(
143 apply_filters(
144 'rtsb/core/locate_template_files',
145 [
146 $template_rtsb_path, // Search in <theme>/shopbuilder/.
147 $template_path, // Search in <theme>/.
148 ]
149 )
150 );
151
152 if ( ! $located ) {
153 if ( file_exists( $plugin_path ) ) {
154 return apply_filters( 'rtsb/core/locate_template', $plugin_path, $template_name );
155 } elseif ( $pro_plugin_path && rtsb()->has_pro() && file_exists( $pro_plugin_path ) ) {
156 return apply_filters( 'rtsb/core/locate_template', $pro_plugin_path, $template_name );
157 }
158 }
159
160 /**
161 * APPLY_FILTERS: rtsb/core/locate_template
162 *
163 * Filter the location of the templates.
164 *
165 * @param string $located Template found
166 * @param string $path Template path
167 *
168 * @return string
169 */
170 return apply_filters( 'rtsb/core/locate_template', $located, $template_name );
171 }
172
173 /**
174 * Template Content
175 *
176 * @param string $template_name Template name.
177 * @param array $args Arguments. (default: array).
178 * @param bool $return Whether to return or print the template.
179 * @param string $template_path Template path. (default: '').
180 * @param string $plugin_path Fallback path from where file will load if fail to load from template. (default: '').
181 *
182 * @return false|string|void
183 */
184 public static function load_template( $template_name, array $args = null, $return = false, $template_path = '', $plugin_path = '' ) {
185 $located = self::locate_template( $template_name, $template_path, $plugin_path );
186
187 if ( ! file_exists( $located ) ) {
188 // translators: %s template.
189 self::doing_it_wrong( __FUNCTION__, sprintf( __( '%s does not exist.', 'shopbuilder' ), '<code>' . $located . '</code>' ), '1.0' );
190
191 return;
192 }
193
194 if ( ! empty( $args ) && is_array( $args ) ) {
195 $atts = $args;
196 extract( $args ); // @codingStandardsIgnoreLine
197 }
198
199 // Allow 3rd party plugin filter template file from their plugin.
200 $located = apply_filters( 'rtsb/core/get_template', $located, $template_name, $args );
201
202 if ( $return ) {
203 ob_start();
204 }
205
206 do_action( 'rtsb/core/before_template_part', $template_name, $located, $args );
207 include $located;
208 do_action( 'rtsb/core/after_template_part', $template_name, $located, $args );
209
210 if ( $return ) {
211 return ob_get_clean();
212 }
213 }
214
215 /**
216 * Verify nonce.
217 *
218 * @return string
219 */
220 public static function is_woocommerce() {
221 return is_woocommerce() || is_cart() || is_checkout() || is_account_page();
222 }
223
224 /**
225 * Page builder
226 *
227 * @param int $post_id post id.
228 *
229 * @return string
230 */
231 public static function page_edit_with( $post_id ) {
232 if ( ! $post_id ) {
233 return '';
234 }
235
236 $edit_with = get_post_meta( $post_id, '_elementor_edit_mode', true );
237
238 if ( 'builder' === $edit_with ) {
239 $edit_by = 'elementor';
240 } else {
241 $edit_by = 'gutenberg';
242 }
243
244 return $edit_by;
245 }
246
247 /**
248 * Returns default expiration for wishlist cookie
249 *
250 * @return int Number of seconds the cookie should last.
251 */
252 public static function get_cookie_expiration() {
253 return intval( apply_filters( 'rtsb/cookie_expiration', 60 * 60 * 24 * 30 ) );
254 }
255
256 /**
257 * Create a cookie.
258 *
259 * @param string $name Cookie name.
260 * @param mixed $value Cookie value.
261 * @param int $time Cookie expiration time.
262 * @param bool $secure Whether cookie should be available to secured connection only.
263 * @param bool $httponly Whether cookie should be available to HTTP request only (no js handling).
264 *
265 * @return bool
266 * @since 1.0.0
267 */
268 public static function setcookie( $name, $value = [], $time = null, $secure = false, $httponly = false ) {
269
270 if ( ! apply_filters( 'rtsb/set_cookie', true ) || empty( $name ) ) {
271 return false;
272 }
273
274 $time = ! empty( $time ) ? $time : time() + self::get_cookie_expiration();
275
276 $value = wp_json_encode( stripslashes_deep( $value ) );
277 $expiration = apply_filters( 'rtsb/cookie_expiration_time', $time ); // Default 30 days.
278
279 $_COOKIE[ $name ] = $value;
280 wc_setcookie( $name, $value, $expiration, $secure, $httponly );
281
282 return true;
283 }
284
285 /**
286 * Retrieve the value of a cookie.
287 *
288 * @param string $name Cookie name.
289 *
290 * @return mixed
291 * @since 1.0.0
292 */
293 public static function getcookie( $name ) {
294 if ( isset( $_COOKIE[ $name ] ) ) {
295 return json_decode( sanitize_text_field( wp_unslash( $_COOKIE[ $name ] ) ), true );
296 }
297
298 return [];
299 }
300
301 /**
302 * Woocommerce Last product id return
303 */
304 public static function get_prepared_product_id() {
305 if ( is_singular( 'product' ) ) {
306 return get_the_ID();
307 }
308 // Return the Builder Template id.
309
310 if ( get_post_type( get_the_ID() ) == BuilderFns::$post_type_tb ) {
311 $product_id = get_post_meta( get_the_ID(), BuilderFns::$product_template_meta, true );
312 if ( $product_id ) {
313 return $product_id;
314 }
315 }
316
317 global $wpdb;
318 $cache_key = 'rtsb_prepared_product_id';
319 $_post_id = wp_cache_get( $cache_key );
320
321 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
322 $_post_id = $wpdb->get_var(
323 $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status IN('publish', 'draft')", 'product' )
324 );
325 wp_cache_set( $cache_key, $_post_id, '', 12 * HOUR_IN_SECONDS );
326 }
327
328 return $_post_id;
329 }
330
331 /**
332 * Get the product function. Only used in single page widgets.
333 *
334 * @return object
335 */
336 public static function get_product() {
337 global $product;
338
339 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
340 return $product;
341 }
342 $cache_key = 'prepared_product_for_preview';
343 if ( isset( self::$cache[ $cache_key ] ) ) {
344 return self::$cache[ $cache_key ];
345 }
346
347 $product = wc_get_product( self::get_prepared_product_id() );
348 self::$cache[ $cache_key ] = $product;
349
350 return $product;
351 }
352
353 /**
354 * Error function
355 *
356 * @param [type] $function function name.
357 * @param [type] $message message.
358 * @param [type] $version version.
359 *
360 * @return void
361 */
362 public static function doing_it_wrong( $function, $message, $version ) {
363 $message .= ' Backtrace: ' . wp_debug_backtrace_summary();
364 _doing_it_wrong( $function, $message, $version );
365 }
366
367 /**
368 * @return array
369 */
370 public static function get_pages() {
371 $pages = [];
372 $rawPages = get_pages();
373 if ( ! empty( $rawPages ) ) {
374 foreach ( $rawPages as $page ) {
375 $pages[ $page->ID ] = $page->post_title;
376 }
377 }
378
379 return $pages;
380 }
381
382 public static function get_section_items( $section_id ) {
383 if ( ! $section_id ) {
384 return [];
385 }
386
387 return DataModel::source()->get_option( $section_id, [] );
388 }
389
390 public static function get_options( $section_id, $item_id ) {
391 if ( ! $section_id || ! $item_id ) {
392 return [];
393 }
394 $sections = self::get_section_items( $section_id );
395
396 return isset( $sections[ $item_id ] ) ? $sections[ $item_id ] : [];
397 }
398
399 /**
400 * Get options value with default value if options doesn't exist.
401 *
402 * @param $group
403 * @param $option_key
404 * @param $default_value
405 *
406 * @return mixed|string
407 */
408 public static function get_options_by_default_val( $group, $option_key, $default_value = '' ) {
409
410 if ( ! $option_key || ! isset( $group[ $option_key ] ) ) {
411 return $default_value;
412 }
413
414 return $group[ $option_key ];
415 }
416
417 /**
418 * @param string $section_id
419 * @param string $item_id
420 * @param string $option_id
421 * @param null $default EXCEPT multi_checkbox you can provide default value if given option does not set any value
422 * @param null $type checkbox, multi_checkbox, number
423 *
424 * @return bool|int|mixed|null
425 */
426 public static function get_option( $section_id, $item_id, $option_id, $default = null, $type = null ) {
427 $options = self::get_options( $section_id, $item_id );
428
429 if ( $type === 'checkbox' ) {
430 if ( isset( $options[ $option_id ] ) ) {
431 return $options[ $option_id ] === 'on';
432 }
433
434 return $default;
435 } elseif ( $type === 'multi_checkbox' ) {
436 return isset( $options[ $option_id ] ) && is_array( $options[ $option_id ] ) && in_array( $default, $options[ $option_id ] );
437 } elseif ( $type === 'number' ) {
438 return isset( $options[ $option_id ] ) ? absint( $options[ $option_id ] ) : absint( $default );
439 }
440
441 return isset( $options[ $option_id ] ) && ! empty( $options[ $option_id ] ) ? $options[ $option_id ] : $default;
442 }
443
444
445 /**
446 * Create a page and store the ID in an option.
447 *
448 * @param mixed $slug Slug for the new page.
449 * @param array $options ['section_id', 'item_id', 'option_id']Option name to store the page's ID.
450 * @param string $page_title (default: '') Title for the new page.
451 * @param string $page_content (default: '') Content for the new page.
452 * @param int $post_parent (default: 0) Parent for the new page.
453 * @param string $post_status (default: publish) The post status of the new page.
454 *
455 * @return int page ID.
456 */
457 public static function create_page( $slug, $options = '', $page_title = '', $page_content = '', $post_parent = 0, $post_status = 'publish' ) {
458 global $wpdb;
459
460 $option_value = 0;
461 if ( ! empty( $options ) ) {
462 if ( is_array( $options ) ) {
463 $options = wp_parse_args(
464 $options,
465 [
466 'section_id' => '',
467 'item_id' => '',
468 'option_id' => '',
469 ]
470 );
471 $option_value = self::get_option( $options['section_id'], $options['item_id'], $options['option_id'], 0, 'number' );
472 } else {
473 $option_value = absint( get_option( $options ) );
474 }
475 }
476
477 if ( $option_value > 0 ) {
478 $page_object = get_post( $option_value );
479
480 if ( $page_object && 'page' === $page_object->post_type && ! in_array(
481 $page_object->post_status,
482 [
483 'pending',
484 'trash',
485 'future',
486 'auto-draft',
487 ],
488 true
489 ) ) {
490 // Valid page is already in place.
491 return $page_object->ID;
492 }
493 }
494
495 if ( strlen( $page_content ) > 0 ) {
496 // Search for an existing page with the specified page content (typically a shortcode).
497 $shortcode = str_replace( [ '<!-- wp:shortcode -->', '<!-- /wp:shortcode -->' ], '', $page_content );
498 $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}%" ) );
499 } else {
500 // Search for an existing page with the specified page slug.
501 $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 ) );
502 }
503
504 $valid_page_found = apply_filters( 'rtsb/core/create_page_id', $valid_page_found, $slug, $page_content, $options );
505
506 if ( $valid_page_found ) {
507 if ( is_array( $options ) ) {
508 if ( ! empty( $options['section_id'] ) && $options['item_id'] && $options['option_id'] ) {
509 $section_items = DataModel::source()->get_option( $options['section_id'], [] );
510 $section_items[ $options['item_id'] ][ $options['option_id'] ] = $valid_page_found;
511 DataModel::source()->set_option( $options['section_id'], $section_items );
512 }
513 } else {
514 if ( $options ) {
515 update_option( $options, $valid_page_found );
516 }
517 }
518
519 return $valid_page_found;
520 }
521
522 // Search for a matching valid trashed page.
523 if ( strlen( $page_content ) > 0 ) {
524 // Search for an existing page with the specified page content (typically a shortcode).
525 $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}%" ) );
526 } else {
527 // Search for an existing page with the specified page slug.
528 $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 ) );
529 }
530
531 if ( $trashed_page_found ) {
532 $page_id = $trashed_page_found;
533 $page_data = [
534 'ID' => $page_id,
535 'post_status' => $post_status,
536 ];
537 wp_update_post( $page_data );
538 } else {
539 $page_data = [
540 'post_status' => $post_status,
541 'post_type' => 'page',
542 'post_author' => 1,
543 'post_name' => $slug,
544 'post_title' => $page_title,
545 'post_content' => $page_content,
546 'post_parent' => $post_parent,
547 'comment_status' => 'closed',
548 ];
549 $page_id = wp_insert_post( $page_data );
550
551 do_action( 'rtsb/core/page_created', $page_id, $page_data );
552 }
553
554 if ( is_array( $options ) ) {
555 if ( ! empty( $options['section_id'] ) && $options['item_id'] && $options['option_id'] ) {
556 $section_items = DataModel::source()->get_option( $options['section_id'], [] );
557 $section_items[ $options['item_id'] ][ $options['option_id'] ] = $page_id;
558 DataModel::source()->set_option( $options['section_id'], $section_items );
559 }
560 } else {
561 if ( $options ) {
562 update_option( $options, $page_id );
563 }
564 }
565
566 return $page_id;
567 }
568
569 public static function get_kses_array() {
570 return [
571 'a' => [
572 'class' => [],
573 'href' => [],
574 'rel' => [],
575 'title' => [],
576 ],
577 'abbr' => [
578 'title' => [],
579 ],
580 'b' => [],
581 'blockquote' => [
582 'cite' => [],
583 ],
584 'cite' => [
585 'title' => [],
586 ],
587 'code' => [],
588 'del' => [
589 'datetime' => [],
590 'title' => [],
591 ],
592 'dd' => [],
593 'div' => [
594 'class' => [],
595 'title' => [],
596 'style' => [],
597 ],
598 'dl' => [],
599 'dt' => [],
600 'em' => [],
601 'h1' => [
602 'class' => [],
603 ],
604 'h2' => [
605 'class' => [],
606 ],
607 'h3' => [
608 'class' => [],
609 ],
610 'h4' => [
611 'class' => [],
612 ],
613 'h5' => [
614 'class' => [],
615 ],
616 'h6' => [
617 'class' => [],
618 ],
619 'i' => [
620 'class' => [],
621 ],
622 'img' => [
623 'alt' => [],
624 'class' => [],
625 'height' => [],
626 'src' => [],
627 'width' => [],
628 ],
629 'li' => [
630 'class' => [],
631 ],
632 'ol' => [
633 'class' => [],
634 ],
635 'p' => [
636 'class' => [],
637 ],
638 'q' => [
639 'cite' => [],
640 'title' => [],
641 ],
642 'span' => [
643 'class' => [],
644 'title' => [],
645 'style' => [],
646 ],
647 'iframe' => [
648 'width' => [],
649 'height' => [],
650 'scrolling' => [],
651 'frameborder' => [],
652 'allow' => [],
653 'src' => [],
654 ],
655 'strike' => [],
656 'br' => [],
657 'strong' => [],
658 'data-wow-duration' => [],
659 'data-wow-delay' => [],
660 'data-wallpaper-options' => [],
661 'data-stellar-background-ratio' => [],
662 'ul' => [
663 'class' => [],
664 ],
665 ];
666 }
667
668
669 /*
670 * Escape output of wishlist icon
671 *
672 * @param string $data Data to escape.
673 * @return string Escaped data
674 */
675 public static function print_icon( $data ) {
676 /**
677 * APPLY_FILTERS: rtsb/core/allowed_icon_html
678 *
679 * Filter the allowed HTML for the icons.
680 *
681 * @param array $allowed_icon_html Allowed HTML
682 *
683 * @return array
684 */
685 $allowed_icon_html = apply_filters(
686 'rtsb/core/allowed_icon_html',
687 [
688 'i' => [
689 'class' => true,
690 ],
691 'img' => [
692 'src' => true,
693 'alt' => true,
694 'width' => true,
695 'height' => true,
696 ],
697 'svg' => [
698 'class' => true,
699 'aria-hidden' => true,
700 'aria-labelledby' => true,
701 'role' => true,
702 'xmlns' => true,
703 'width' => true,
704 'height' => true,
705 'viewbox' => true,
706 'stroke' => true,
707 'fill' => true,
708 ],
709 'g' => [
710 'fill' => true,
711 ],
712 'title' => [
713 'title' => true,
714 ],
715 'path' => [
716 'd' => true,
717 'fill' => true,
718 'stroke' => true,
719 'stroke-width' => true,
720 'stroke-linecap' => true,
721 'stroke-linejoin' => true,
722 'fill-rule' => true,
723 'clip-rule' => true,
724 ],
725 ]
726 );
727
728 echo wp_kses( $data, $allowed_icon_html );
729 }
730
731 /**
732 * Prints HTMl.
733 *
734 * @param string $html HTML.
735 * @param bool $allHtml All HTML.
736 *
737 * @return void
738 */
739 public static function print_html( $html, $allHtml = false ) {
740 if ( ! $html ) {
741 return;
742 }
743 if ( $allHtml ) {
744 echo stripslashes_deep( $html ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
745 } else {
746 echo wp_kses_post( stripslashes_deep( $html ) );
747 }
748 }
749
750 /**
751 * Allowed HTML for wp_kses.
752 *
753 * @param string $level Tag level.
754 *
755 * @return mixed
756 */
757 public static function allowedHtml( $level = 'basic' ) {
758 $allowed_html = [];
759 // TODO:: Need Optimize.
760 switch ( $level ) {
761 case 'basic':
762 $allowed_html = [
763 'b' => [
764 'class' => [],
765 'id' => [],
766 ],
767 'i' => [
768 'class' => [],
769 'id' => [],
770 ],
771 'u' => [
772 'class' => [],
773 'id' => [],
774 ],
775 'br' => [
776 'class' => [],
777 'id' => [],
778 ],
779 'em' => [
780 'class' => [],
781 'id' => [],
782 ],
783 'span' => [
784 'class' => [],
785 'id' => [],
786 ],
787 'strong' => [
788 'class' => [],
789 'id' => [],
790 ],
791 'hr' => [
792 'class' => [],
793 'id' => [],
794 ],
795 'div' => [
796 'class' => [],
797 'id' => [],
798 ],
799 'a' => [
800 'href' => [],
801 'title' => [],
802 'class' => [],
803 'id' => [],
804 'target' => [],
805 ],
806 ];
807 break;
808
809 case 'advanced':
810 $allowed_html = [
811 'b' => [
812 'class' => [],
813 'id' => [],
814 ],
815 'i' => [
816 'class' => [],
817 'id' => [],
818 ],
819 'u' => [
820 'class' => [],
821 'id' => [],
822 ],
823 'br' => [
824 'class' => [],
825 'id' => [],
826 ],
827 'em' => [
828 'class' => [],
829 'id' => [],
830 ],
831 'span' => [
832 'class' => [],
833 'id' => [],
834 ],
835 'strong' => [
836 'class' => [],
837 'id' => [],
838 ],
839 'hr' => [
840 'class' => [],
841 'id' => [],
842 ],
843 'a' => [
844 'href' => [],
845 'title' => [],
846 'class' => [],
847 'id' => [],
848 'target' => [],
849 ],
850 'input' => [
851 'type' => [],
852 'name' => [],
853 'class' => [],
854 'value' => [],
855 ],
856 ];
857 break;
858
859 case 'image':
860 $allowed_html = [
861 'img' => [
862 'src' => [],
863 'data-src' => [],
864 'alt' => [],
865 'height' => [],
866 'width' => [],
867 'class' => [],
868 'id' => [],
869 'style' => [],
870 'srcset' => [],
871 'loading' => [],
872 'sizes' => [],
873 ],
874 'div' => [
875 'class' => [],
876 ],
877 ];
878 break;
879
880 case 'anchor':
881 $allowed_html = [
882 'a' => [
883 'href' => [],
884 'title' => [],
885 'class' => [],
886 'id' => [],
887 'style' => [],
888 ],
889 ];
890 break;
891
892 default:
893 // code...
894 break;
895 }
896
897 return $allowed_html;
898 }
899
900 /**
901 * Insert Some array element
902 *
903 * @param [type] $key The elements will insert nearby this key.
904 * @param [type] $main_array Original array.
905 * @param [type] $insert_array some element will insert in original array.
906 * @param boolean $is_after array insert position base on the key.
907 *
908 * @return array
909 */
910 public static function insert_controls( $key, $main_array, $insert_array, $is_after = false ) {
911 $index = array_search( $key, array_keys( $main_array ), true );
912 if ( 'integer' === gettype( $index ) ) {
913 if ( $is_after ) {
914 $index++;
915 }
916 $main_array = array_merge(
917 array_slice( $main_array, 0, $index ),
918 $insert_array,
919 array_slice( $main_array, $index )
920 );
921 }
922
923 return $main_array;
924 }
925
926 /**
927 * Image Sizes
928 *
929 * @return array
930 */
931 public static function get_image_sizes() {
932 global $_wp_additional_image_sizes;
933
934 $sizes = [];
935
936 foreach ( get_intermediate_image_sizes() as $_size ) {
937 if ( in_array( $_size, [ 'thumbnail', 'medium', 'large' ], true ) ) {
938 $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
939 $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
940 $sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" );
941 } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
942 $sizes[ $_size ] = [
943 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
944 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
945 'crop' => $_wp_additional_image_sizes[ $_size ]['crop'],
946 ];
947 }
948 }
949
950 $imgSize = [];
951
952 foreach ( $sizes as $key => $img ) {
953 $imgSize[ $key ] = ucfirst( $key ) . " ({$img['width']}*{$img['height']})";
954 }
955
956 $imgSize['full'] = esc_html__( 'Full size', 'shopbuilder' );
957 $imgSize['rtsb_custom'] = esc_html__( 'Custom image size', 'shopbuilder' );
958
959 return $imgSize;
960 }
961
962 /**
963 * Free Layouts
964 *
965 * @return array
966 */
967 public static function free_layouts() {
968 $layouts = [
969 'grid-layout1' => esc_html__( 'Grid Layout 1', 'shopbuilder' ),
970 'grid-layout2' => esc_html__( 'Grid Layout 2', 'shopbuilder' ),
971 'list-layout1' => esc_html__( 'List Layout 1', 'shopbuilder' ),
972 'list-layout2' => esc_html__( 'List Layout 2', 'shopbuilder' ),
973 'slider-layout1' => esc_html__( 'Slider Layout 1', 'shopbuilder' ),
974 'slider-layout2' => esc_html__( 'Slider Layout 2', 'shopbuilder' ),
975 'category-single-layout1' => esc_html__( 'Category Single Layout 1', 'shopbuilder' ),
976 'category-layout1' => esc_html__( 'Category Layout 1', 'shopbuilder' ),
977 'category-layout2' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
978 ];
979
980 return apply_filters( 'rtsb/elements/elementor/free_layouts', $layouts );
981 }
982
983 /**
984 * Get all terms by taxonomy
985 *
986 * @param string $taxonomy Taxonomy name.
987 * @param bool $placeholder Placeholder..
988 *
989 * @return array
990 */
991 public static function get_all_terms( $taxonomy, $placeholder = false ) {
992 $terms = [];
993
994 if ( empty( $taxonomy ) ) {
995 return $terms;
996 }
997 $cache_key = 'rtsb_get_all_terms_by_tax_name_' . $taxonomy;
998 if ( isset( self::$cache[ $cache_key ] ) ) {
999 return self::$cache[ $cache_key ];
1000 }
1001
1002 $termList = get_terms( [ $taxonomy ], [ 'hide_empty' => 0 ] );
1003
1004 if ( is_array( $termList ) && ! empty( $termList ) && empty( $termList['errors'] ) ) {
1005 if ( $placeholder ) {
1006 $terms = [
1007 'all' => __( 'All Products', 'shopbuilder' ),
1008 ];
1009 }
1010
1011 foreach ( $termList as $term ) {
1012 $terms[ $term->term_id ] = esc_html( $term->name );
1013 }
1014 }
1015 self::$cache[ $cache_key ] = $terms;
1016 return $terms;
1017 }
1018
1019 /**
1020 * Get all terms by attributes
1021 *
1022 * @return array
1023 */
1024 public static function get_all_attributes() {
1025 $terms = [];
1026 $termList = [];
1027
1028 $attributes = wc_get_attribute_taxonomies();
1029
1030 if ( $attributes ) {
1031 foreach ( $attributes as $tax ) {
1032 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1033 $termList[ $tax->attribute_name ] = get_terms( wc_attribute_taxonomy_name( $tax->attribute_name ), 'orderby=name&hide_empty=0' );
1034 }
1035 }
1036 }
1037
1038 if ( ! empty( $termList ) && empty( $termList['errors'] ) && is_array( $termList ) ) {
1039 foreach ( $termList as $name => $atts ) {
1040 foreach ( $atts as $term ) {
1041 $terms[ $term->term_id ] = esc_html( ucwords( str_replace( '-', ' ', $name ) ) . ' - ' . $term->name );
1042 }
1043 }
1044 }
1045
1046 return $terms;
1047 }
1048
1049 /**
1050 * Get all attributes name
1051 *
1052 * @return array
1053 */
1054 public static function get_all_attributes_name() {
1055 $attributes_name = [];
1056
1057 $attributes = wc_get_attribute_taxonomies();
1058
1059 if ( $attributes ) {
1060 foreach ( $attributes as $tax ) {
1061 if ( taxonomy_exists( wc_attribute_taxonomy_name( $tax->attribute_name ) ) ) {
1062 $attributes_name[ wc_attribute_taxonomy_name( $tax->attribute_name ) ] = ucwords( $tax->attribute_name );
1063 }
1064 }
1065 }
1066
1067 return $attributes_name;
1068 }
1069
1070 /**
1071 * Get User list
1072 *
1073 * @return array
1074 */
1075 public static function get_users() {
1076 $users = [];
1077 $u = get_users();
1078
1079 if ( ! empty( $u ) ) {
1080 foreach ( $u as $user ) {
1081 $users[ $user->ID ] = $user->display_name;
1082 }
1083 }
1084
1085 return $users;
1086 }
1087
1088 /**
1089 * Get Taxonomy List.
1090 *
1091 * @return array
1092 */
1093 public static function get_tax_list() {
1094 return apply_filters(
1095 'rtsb/elements/tax_list',
1096 [
1097 'product_cat' => esc_html__( 'Product Category', 'shopbuilder' ),
1098 ]
1099 );
1100 }
1101
1102 /**
1103 * Get Category Query List.
1104 *
1105 * @return array
1106 */
1107 public static function get_cat_list() {
1108 return [
1109 'all' => esc_html__( 'All Categories', 'shopbuilder' ),
1110 'specific_parent' => esc_html__( 'Sub-Categories by Parent', 'shopbuilder' ),
1111 'cat_ids' => esc_html__( 'Select by ID', 'shopbuilder' ),
1112 'selection' => esc_html__( 'Manual Selection', 'shopbuilder' ),
1113 ];
1114 }
1115
1116 /**
1117 * Get Term List.
1118 *
1119 * @param string $taxonomy Taxonomy.
1120 * @param bool $first_term First term.
1121 * @param bool $only_parents Only parent terms.
1122 * @param bool $return_slug Return with slug.
1123 *
1124 * @return int|array
1125 */
1126 public static function get_terms( $taxonomy, $first_term = false, $only_parents = false, $return_slug = false ) {
1127 if ( ! is_admin() ) {
1128 return [];
1129 }
1130
1131 // TODO:: Return Slug always true for all settings.
1132 $term_list = [];
1133 $args = [
1134 'taxonomy' => $taxonomy,
1135 'hide_empty' => false,
1136 ];
1137
1138 if ( $only_parents ) {
1139 $args['parent'] = 0;
1140 }
1141
1142 $terms = get_terms( $args );
1143
1144 if ( empty( $terms ) ) {
1145 return [ esc_html__( 'Nothing found', 'shopbuilder' ) ];
1146 }
1147
1148 foreach ( $terms as $term ) {
1149 if ( $return_slug ) {
1150 $term_list[ $term->slug ] = $term->name;
1151 } else {
1152 $term_list[ $term->term_id ] = $term->name;
1153 }
1154 }
1155
1156 if ( $first_term ) {
1157 return array_keys( $term_list )[0];
1158 } else {
1159 return $term_list;
1160 }
1161 }
1162
1163 /**
1164 * Get product rating.
1165 *
1166 * @param array $args Arguments.
1167 *
1168 * @return string|void
1169 */
1170 public static function get_product_rating_html( $args = [] ) {
1171 global $product;
1172
1173 $html = '';
1174 $rating_count = $product->get_rating_count();
1175 $average_rating = $product->get_average_rating();
1176
1177 if ( ! rtsb()->has_pro() || empty( $args ) ) {
1178 if ( ! $rating_count || empty( wc_get_rating_html( $average_rating, $rating_count ) ) ) {
1179 return $html;
1180 }
1181
1182 $html .= '<div class="product-rating">';
1183 $html .= wc_get_rating_html( $average_rating, $rating_count );
1184 $html .= ! empty( $html ) ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1185 $html .= '</div>';
1186
1187 self::print_html( $html, true );
1188
1189 return;
1190 }
1191
1192 if ( empty( $rating_count ) && ! $args['show_empty_rating'] ) {
1193 return '';
1194 }
1195
1196 $preset = ! empty( $args['preset'] ) ? $args['preset'] : 'preset1';
1197 $average = $args['show_average_rating'] ? '<span class="rtsb-count">(' . $average_rating . ')</span>' : '';
1198 $count = '';
1199
1200 if ( $args['show_rating_count'] ) {
1201 $count .= '<div class="rtsb-count">';
1202 $count .= sprintf(
1203 /* translators: %s is the number of reviews */
1204 _n( '%s Review', '%s Reviews', $rating_count, 'shopbuilder' ),
1205 $rating_count
1206 );
1207 $count .= '</div>';
1208 }
1209
1210 if ( 'preset2' === $preset ) {
1211 $average = $args['show_average_rating'] ? '<div class="inner-wrapper"><span class="star-icon"></span><span class="average-rating">' . $average_rating . '</span></div>' : '';
1212 }
1213
1214 $html .= '<div class="product-rating ' . esc_attr( $preset ) . '">';
1215
1216 if ( 'preset1' === $preset ) {
1217 $html .= wc_get_rating_html( $average_rating, $rating_count );
1218 $html .= $average;
1219 $html .= $count;
1220 } elseif ( 'preset2' === $preset ) {
1221 $html .= $average;
1222 $html .= $count;
1223 }
1224
1225 $html .= '</div>';
1226
1227 self::print_html( $html, true );
1228 }
1229
1230 /**
1231 * Text truncation.
1232 *
1233 * @param string $text_to_truncate Text.
1234 * @param int $limit Limit.
1235 * @param string $after After text.
1236 *
1237 * @return string
1238 */
1239 public static function text_truncation( $text_to_truncate, $limit, $after = '&#8230;' ) {
1240 if ( empty( $limit ) ) {
1241 return $text_to_truncate;
1242 }
1243
1244 $limit++;
1245
1246 $text = '';
1247
1248 if ( mb_strlen( $text_to_truncate ) > $limit ) {
1249 $subex = mb_substr( wp_strip_all_tags( $text_to_truncate ), 0, $limit );
1250 $exwords = explode( ' ', $subex );
1251 $excut = - ( mb_strlen( $exwords[ count( $exwords ) - 1 ] ) );
1252
1253 if ( $excut < 0 ) {
1254 $text .= mb_substr( $subex, 0, $excut ) . $after;
1255 } else {
1256 $text .= $subex . $after;
1257 }
1258 } else {
1259 $text .= $text_to_truncate;
1260 }
1261
1262 return $text;
1263 }
1264
1265 /**
1266 * Promo Badge HTML
1267 *
1268 * @param string $text Text.
1269 * @param string $class Class.
1270 *
1271 * @return void
1272 */
1273 public static function get_badge_html( $text, $class = 'fill' ) {
1274 if ( empty( $text ) ) {
1275 return;
1276 }
1277 ob_start();
1278 ?>
1279
1280 <ul class="rtsb-promotion-list">
1281 <li class="rtsb-promotion-list-item">
1282 <span
1283 class="rtsb-tag-<?php echo ! empty( $class ) ? esc_attr( $class ) : ''; ?>"><?php echo esc_html( $text ); ?></span>
1284 </li>
1285 </ul>
1286
1287 <?php
1288 self::print_html( ob_get_clean() );
1289 }
1290
1291 /**
1292 * Categories HTML
1293 *
1294 * @param int $id Product ID.
1295 * @param string $class Custom class.
1296 *
1297 * @return void|string
1298 */
1299 public static function get_categories_list( $id, $class = 'rtsb-category-outline' ) {
1300 if ( empty( $id ) ) {
1301 return '';
1302 }
1303
1304 ob_start();
1305 ?>
1306
1307 <ul class="rtsb-category-list <?php echo esc_attr( $class ); ?>">
1308 <?php
1309 self::print_html(
1310 wc_get_product_category_list(
1311 $id,
1312 '</li><li class="rtsb-category-list-item">',
1313 '<li class="rtsb-category-list-item">',
1314 '</li>'
1315 )
1316 );
1317 ?>
1318 </ul>
1319
1320 <?php
1321 self::print_html( ob_get_clean() );
1322 }
1323
1324
1325 /**
1326 * Get Featured Image HTML.
1327 *
1328 * @param string $type Image type.
1329 * @param int $post_id Post ID.
1330 * @param string $f_img_size Image size.
1331 * @param null $default_img_id Default image ID.
1332 * @param array $custom_img_size Custom image size.
1333 * @param bool $lazy Lazy load check.
1334 * @param bool $hover Hover image.
1335 * @param bool $gallery Gallery image.
1336 *
1337 * @return string|null
1338 */
1339 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 ) {
1340 $img_html = null;
1341 $attachment_id = null;
1342 $c_size = false;
1343 $hover_class = '';
1344 $post_title = '';
1345
1346 if ( 'rtsb_custom' === $f_img_size ) {
1347 $f_img_size = 'full';
1348 $c_size = true;
1349 }
1350
1351 if ( ! rtsb()->has_pro() ) {
1352 $gallery = false;
1353 }
1354
1355 if ( $hover ) {
1356 $a_id = $post_id;
1357 $hover_class = ' rtsb-img-hover';
1358 } elseif ( $gallery ) {
1359 $a_id = $post_id;
1360 } else {
1361 if ( 'product' === $type ) {
1362 $a_id = get_post_thumbnail_id( $post_id );
1363 $post_title = get_the_title( $post_id );
1364 } elseif ( 'category' === $type ) {
1365 $a_id = get_term_meta( $post_id, 'thumbnail_id', true );
1366 $post_title = get_term( $post_id )->name;
1367 } else {
1368 $a_id = $default_img_id;
1369 }
1370 }
1371
1372 $img_alt = trim( wp_strip_all_tags( get_post_meta( $a_id, '_wp_attachment_image_alt', true ) ) );
1373 $alt_tag = ! empty( $img_alt ) ? $img_alt : wp_strip_all_tags( $post_title );
1374 $lazy_class = $lazy ? ' swiper-lazy' : '';
1375 $attr = [
1376 'class' => 'img-responsive rtsb-product-image' . $lazy_class . $hover_class,
1377 'alt' => $alt_tag,
1378 ];
1379
1380 if ( $a_id ) {
1381 $img_html = wp_get_attachment_image( $a_id, $f_img_size, false, $attr );
1382 $attachment_id = $a_id;
1383 }
1384
1385 if ( ! $img_html && $default_img_id ) {
1386 $img_html = wp_get_attachment_image( $default_img_id, $f_img_size, false, $attr );
1387 $attachment_id = $default_img_id;
1388 }
1389
1390 if ( $img_html && $c_size ) {
1391 preg_match( '@src="([^"]+)"@', $img_html, $match );
1392 $img_src = array_pop( $match );
1393 $w = ! empty( $custom_img_size['width'] ) ? absint( $custom_img_size['width'] ) : null;
1394 $h = ! empty( $custom_img_size['height'] ) ? absint( $custom_img_size['height'] ) : null;
1395 $c = ! empty( $custom_img_size['crop'] ) && 'soft' === $custom_img_size['crop'] ? false : true;
1396
1397 if ( $w && $h ) {
1398 $image = self::image_resize( $img_src, $w, $h, $c, false );
1399
1400 if ( ! empty( $image ) ) {
1401 [ $src, $width, $height ] = $image;
1402
1403 $hwstring = image_hwstring( $width, $height );
1404 $attachment = get_post( $attachment_id );
1405 $attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment, $f_img_size );
1406
1407 if ( $lazy ) {
1408 $attr['data-src'] = $src;
1409 } else {
1410 $attr['src'] = $src;
1411 }
1412
1413 $attr = array_map( 'esc_attr', $attr );
1414 $img_html = rtrim( "<img $hwstring" );
1415
1416 foreach ( $attr as $name => $value ) {
1417 $img_html .= " $name=" . '"' . $value . '"';
1418 }
1419
1420 $img_html .= ' />';
1421 }
1422 }
1423 }
1424
1425 if ( ! $img_html ) {
1426 $hwstring = image_hwstring( 160, 160 );
1427 $attr = isset( $attr['src'] ) ? apply_filters( 'wp_get_attachment_image_attributes', $attr, false, $f_img_size ) : [];
1428 $attr['class'] = 'default-img';
1429 $attr['src'] = esc_url( rtsb()->get_assets_uri( 'images/demo.png' ) );
1430 $attr['alt'] = esc_html__( 'Default Image', 'shopbuilder' );
1431 $img_html = rtrim( "<img $hwstring" );
1432
1433 foreach ( $attr as $name => $value ) {
1434 $img_html .= " $name=" . '"' . $value . '"';
1435 }
1436
1437 $img_html .= ' />';
1438 }
1439
1440 if ( $lazy ) {
1441 $img_html = $img_html . '<div class="swiper-lazy-preloader swiper-lazy-preloader"></div>';
1442 }
1443
1444 return $img_html;
1445 }
1446
1447 /**
1448 * Call the Image resize model for resize function
1449 *
1450 * @param string $url URL.
1451 * @param int $width Width.
1452 * @param int $height Height.
1453 * @param string $crop Crop.
1454 * @param bool|true $single Single.
1455 * @param bool|false $upscale Upscale.
1456 *
1457 * @return array|bool|string
1458 */
1459 public static function image_resize( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
1460 $rtResize = new ReSizer();
1461
1462 return $rtResize->process( $url, $width, $height, $crop, $single, $upscale );
1463 }
1464
1465 /**
1466 * Get Product Image
1467 *
1468 * @param string $f_image Featured Image.
1469 * @param string $h_image Hover Image.
1470 *
1471 * @return void
1472 */
1473 public static function get_product_image( $f_image, $h_image = null ) {
1474 if ( empty( $f_image ) ) {
1475 return;
1476 }
1477
1478 echo wp_kses( $f_image, self::allowedHtml( 'image' ) );
1479
1480 if ( ! empty( $h_image ) ) {
1481 echo wp_kses( $h_image, self::allowedHtml( 'image' ) );
1482 }
1483 }
1484
1485 /**
1486 * Post Custom Field value.
1487 *
1488 * @param int $post_id Post id.
1489 * @param string $field_key Custom Field Key.
1490 * @param string $field_fallback FallBack text.
1491 *
1492 * @return string|void
1493 */
1494 public static function get_post_custom_field_value( $post_id, $field_key = '', $field_fallback = '' ) {
1495 if ( ! $post_id || ! $field_key ) {
1496 return;
1497 }
1498 $field_value = get_post_meta( $post_id, $field_key, true );
1499 if ( empty( $field_value ) ) {
1500 $field_value = ! empty( $field_fallback ) ? $field_fallback : $field_value;
1501 }
1502 $field_value = apply_filters( 'rtsb/get_post_custom_field_value/' . $field_key, $field_value, $field_key );
1503
1504 return sprintf( '<span class="rtsb-woo-custom-field">%s</span>', $field_value );
1505 }
1506
1507 /**
1508 * Elementor Icon
1509 *
1510 * @param array $control Elementor Control array.
1511 * @param string $class Custom class.
1512 * @param string $builder Builder name.
1513 *
1514 * @return string
1515 */
1516 public static function icons_manager( $control, $class = '', $builder = 'elementor' ): string {
1517 if( empty( $control['value'] ) ){
1518 return '';
1519 }
1520 if( is_array( $control['value'] )){
1521 $cache_key = 'icons_managet_' . str_replace(' ', '', md5( serialize( $control['value'] ) ) );
1522 } else {
1523 $cache_key = 'icons_managet_' . str_replace(' ', '', $control['value'] );
1524 }
1525 if ( isset( self::$cache[ $cache_key ] ) ) {
1526 return self::$cache[ $cache_key ];
1527 }
1528
1529 $attributes = [
1530 'aria-hidden' => 'true',
1531 ];
1532
1533 if ( ! empty( $class ) ) {
1534 $attributes['class'] = esc_attr( $class );
1535 }
1536
1537 ob_start();
1538
1539 if ( defined( 'ELEMENTOR_VERSION' ) && ! empty( $control ) && 'elementor' === $builder ) {
1540 Icons_Manager::render_icon( $control, $attributes );
1541 }
1542
1543 $icons = ob_get_clean();
1544 self::$cache[ $cache_key ] = $icons;
1545 return $icons;
1546 }
1547
1548 /**
1549 * Get product swatches.
1550 *
1551 * @param string $type Swatch type.
1552 *
1553 * @return void
1554 */
1555 public static function get_product_swatches( $type ) {
1556 ?>
1557 <div class="rtsb-swatches <?php echo esc_attr( $type ); ?>-layout">
1558 <?php
1559 do_action( 'rtwpvs_show_archive_variation' );
1560 ?>
1561 </div>
1562 <?php
1563 }
1564
1565 /**
1566 * Get sale badge
1567 *
1568 * @param string $type Badge type.
1569 * @param string $text Badge text.
1570 * @param string $out_of_stock_text Out of stock text.
1571 *
1572 * @return string
1573 */
1574 public static function get_sale_badge( $type, $text, $out_of_stock_text ) {
1575 global $product;
1576
1577 if ( ! $product->is_in_stock() ) {
1578 return $out_of_stock_text;
1579 }
1580
1581 if ( ! $product->is_on_sale() ) {
1582 return '';
1583 }
1584
1585 $badge_text = '';
1586 $percentage = null;
1587 $max_percentage = '';
1588
1589 if ( $product->is_type( 'simple' ) ) {
1590 $max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
1591 } elseif ( $product->is_type( 'variable' ) ) {
1592 $max_percentage = 0;
1593
1594 foreach ( $product->get_children() as $child_id ) {
1595
1596 $variation = wc_get_product( $child_id );
1597
1598 $price = $variation->get_regular_price();
1599 $sale = $variation->get_sale_price();
1600
1601 if ( 0 !== $price && ! empty( $sale ) ) {
1602 $percentage = ( $price - $sale ) / $price * 100;
1603 }
1604
1605 if ( $percentage > $max_percentage ) {
1606 $max_percentage = $percentage;
1607 }
1608 }
1609 }
1610
1611 if ( $max_percentage > 0 ) {
1612 $badge_text = '-' . round( $max_percentage ) . '%';
1613 }
1614
1615 if ( 'text' === $type ) {
1616 $badge_text = $text;
1617 }
1618
1619 return $badge_text;
1620 }
1621
1622 /**
1623 * Get sale badge
1624 *
1625 * @param object $product Product Object.
1626 * @param string $type Badge type.
1627 * @param string $text Badge text.
1628 * @param string $out_of_stock_text Out of stock text.
1629 *
1630 * @return string
1631 */
1632 public static function get_promo_badge( $product, $type, $text, $out_of_stock_text ) {
1633 if ( ! $product->is_in_stock() ) {
1634 return $out_of_stock_text;
1635 }
1636
1637 if ( ! $product->is_on_sale() ) {
1638 return '';
1639 }
1640
1641 $badge_text = '';
1642 $percentage = null;
1643 $max_percentage = '';
1644
1645 if ( $product->is_type( 'simple' ) ) {
1646 $max_percentage = ( ( $product->get_regular_price() - $product->get_sale_price() ) / $product->get_regular_price() ) * 100;
1647 } elseif ( $product->is_type( 'variable' ) ) {
1648 $max_percentage = 0;
1649
1650 foreach ( $product->get_children() as $child_id ) {
1651
1652 $variation = wc_get_product( $child_id );
1653
1654 $price = $variation->get_regular_price();
1655 $sale = $variation->get_sale_price();
1656
1657 if ( 0 !== $price && ! empty( $sale ) ) {
1658 $percentage = ( $price - $sale ) / $price * 100;
1659 }
1660
1661 if ( $percentage > $max_percentage ) {
1662 $max_percentage = $percentage;
1663 }
1664 }
1665 }
1666
1667 if ( $max_percentage > 0 ) {
1668 $badge_text = '-' . round( $max_percentage ) . '%';
1669 }
1670
1671 if ( 'text' === $type ) {
1672 $badge_text = $text;
1673 }
1674
1675 return $badge_text;
1676 }
1677
1678 /**
1679 * Pagination
1680 *
1681 * @param string $pages Pages.
1682 * @param integer $range Range.
1683 * @param boolean $ajax Ajax.
1684 *
1685 * @return string
1686 */
1687 public static function custom_pagination( $pages = '', $range = 4, $ajax = false ) {
1688 $html = null;
1689 $showitems = ( $range * 2 ) + 1;
1690
1691 global $paged;
1692
1693 if ( is_front_page() ) {
1694 $paged = ( get_query_var( 'page' ) ) ? get_query_var( 'page' ) : 1;
1695 } else {
1696 $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
1697 }
1698
1699 if ( empty( $paged ) ) {
1700 $paged = 1;
1701 }
1702
1703 if ( '' === $pages ) {
1704 global $wp_query;
1705
1706 $pages = $wp_query->max_num_pages;
1707
1708 if ( ! $pages ) {
1709 $pages = 1;
1710 }
1711 }
1712
1713 $ajaxClass = null;
1714 $dataAttr = null;
1715
1716 if ( $ajax ) {
1717 $ajaxClass = ' rtsb-pagination-ajax';
1718 $dataAttr = "data-paged='1'";
1719 }
1720
1721 if ( 1 !== $pages ) {
1722 $html .= '<div class="rtsb-pagination' . $ajaxClass . '" ' . $dataAttr . '>';
1723 $html .= '<ul class="pagination-list">';
1724
1725 if ( $paged > 2 && $paged > $range + 1 && $showitems < $pages ) {
1726 $html .= "<li><a data-paged='1' href='" . get_pagenum_link( 1 ) . "' aria-label='First'>&laquo;</a></li>";
1727 }
1728
1729 if ( $paged > 1 && $showitems < $pages ) {
1730 $p = $paged - 1;
1731 $html .= "<li><a data-paged='{$p}' href='" . get_pagenum_link( $p ) . "' aria-label='Previous'>&lsaquo;</a></li>";
1732 }
1733
1734 for ( $i = 1; $i <= $pages; $i++ ) {
1735 if ( 1 != $pages && ( ! ( $i >= $paged + $range + 1 || $i <= $paged - $range - 1 ) || $pages <= $showitems ) ) {
1736 $html .= ( $paged == $i ) ? '<li class="active"><span>' . $i . '</span></li>' : "<li><a data-paged='{$i}' href='" . get_pagenum_link( $i ) . "'>" . $i . '</a></li>';
1737 }
1738 }
1739
1740 if ( $paged < $pages && $showitems < $pages ) {
1741 $p = $paged + 1;
1742 $html .= "<li><a data-paged='{$p}' href=\"" . get_pagenum_link( $paged + 1 ) . "\" aria-label='Next'>&rsaquo;</a></li>";
1743 }
1744
1745 if ( $paged < $pages - 1 && $paged + $range - 1 < $pages && $showitems < $pages ) {
1746 $html .= "<li><a data-paged='{$pages}' href='" . get_pagenum_link( $pages ) . "' aria-label='Last'>&raquo;</a></li>";
1747 }
1748
1749 $html .= '</ul>';
1750 $html .= '</div>';
1751 }
1752
1753 return $html;
1754 }
1755
1756 /**
1757 * Action Buttons HTMl
1758 *
1759 * @param array $items Items.
1760 * @param array $ajax_cart Ajax cart HTML.
1761 * @param string $preset Button preset.
1762 * @param array $position Position.
1763 * @param string $placement Placement.
1764 *
1765 * @return void|string
1766 */
1767 public static function get_formatted_action_buttons( $items, $ajax_cart = '', $preset = 'preset1', $position = 'above', $placement = 'top' ) {
1768 if ( empty( $items ) ) {
1769 return;
1770 }
1771
1772 $html = '';
1773 $class = '';
1774 $args = [ $items, $ajax_cart, $preset, $position, $placement ];
1775
1776 if ( ( 'top' === $placement && 'after' === $position ) || ( 'bottom' === $placement && 'above' === $position ) ) {
1777 return apply_filters( 'rtsb/elements/elementor/formatted_action_buttons', $html, $args );
1778 }
1779
1780 if ( 'preset2' === $preset ) {
1781 $class = 'rtsb-action-buttons-vertical vertical-delay-effect ' . $preset;
1782 } elseif ( 'preset4' === $preset ) {
1783 $class = 'rtsb-action-buttons-vertical rtsb-action-buttons-vertical-left vertical-delay-effect ' . $preset;
1784 } elseif ( 'preset1' === $preset || 'preset3' === $preset ) {
1785 $class = 'rtsb-action-buttons-cart-box-width-auto horizontal-floating-btn ' . $preset;
1786 }
1787
1788 if ( 'after' === $position && 'preset1' === $preset ) {
1789 $class .= ' after-content';
1790 }
1791
1792 if ( 'preset3' === $preset ) {
1793 $html .= '<div class="rtsb-action-buttons top-part ' . esc_attr( $preset ) . '">';
1794 $html .= '<ul class="rtsb-action-button-list">';
1795
1796 ob_start();
1797 /**
1798 * Additional formatted action buttons hook.
1799 */
1800 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1801 $html .= ob_get_clean();
1802
1803 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1804 $html .= self::get_action_button_by_type( $items, 'compare' );
1805 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1806 $html .= '</ul>';
1807 $html .= '</div>';
1808 $html .= '<div class="rtsb-action-buttons bottom-part ' . esc_attr( $preset ) . '">';
1809 $html .= '<ul class="rtsb-action-button-list">';
1810 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1811 $html .= '</ul>';
1812 $html .= '</div>';
1813 } elseif ( 'preset1' === $preset || 'preset2' === $preset || 'preset4' === $preset ) {
1814 $html .= '<div class="rtsb-action-buttons ' . esc_attr( $class ) . '">';
1815 $html .= '<ul class="rtsb-action-button-list">';
1816 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1817
1818 ob_start();
1819 /**
1820 * Additional formatted action buttons hook.
1821 */
1822 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1823 $html .= ob_get_clean();
1824
1825 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1826 $html .= self::get_action_button_by_type( $items, 'compare' );
1827 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1828 $html .= '</ul>';
1829 $html .= '</div>';
1830 }
1831
1832 return apply_filters( 'rtsb/elements/elementor/formatted_action_buttons', $html, $args );
1833 }
1834
1835 /**
1836 * Get Action Button HTML
1837 *
1838 * @param array $items Items.
1839 * @param string $type Button type.
1840 * @param string $cart_html Ajax cart HTML.
1841 * @param string $wrapper Wrapper tag.
1842 *
1843 * @return void|string
1844 */
1845 public static function get_action_button_by_type( $items, $type, $cart_html = '', $wrapper = 'li' ) {
1846 if ( ! in_array( $type, $items, true ) ) {
1847 return;
1848 }
1849
1850 $html = '';
1851 $class = 'rtsb-action-button-item';
1852
1853 if ( 'add_to_cart' === $type ) {
1854 $class .= ' rtsb-cart' . ( empty( $cart_html ) ? esc_attr( ' no-cart-button' ) : '' );
1855 } else {
1856 $class .= ' rtsb-' . esc_attr( str_replace( '_', '-', $type ) );
1857 }
1858
1859 $html .= '<' . esc_attr( $wrapper ) . ' class="' . esc_attr( $class ) . '">';
1860
1861 if ( ( 'add_to_cart' === $type ) && ( ! empty( $cart_html ) ) ) {
1862 $html .= $cart_html;
1863 } else {
1864 $html .= shortcode_exists( 'rtsb_' . $type . '_button' ) ? do_shortcode( '[rtsb_' . $type . '_button]' ) : null;
1865 }
1866
1867 $html .= '</' . esc_attr( $wrapper ) . '>';
1868
1869 return apply_filters( 'rtsb/elements/elementor/get_action_button_by_type', $html );
1870 }
1871
1872 /**
1873 * Social Share Platforms.
1874 *
1875 * @return mixed|null
1876 */
1877 public static function social_share_platforms_list() {
1878 return apply_filters(
1879 'rtsb/settings/social_share/platforms',
1880 [
1881 [
1882 'value' => 'facebook',
1883 'label' => esc_html__( 'Facebook', 'shopbuilder' ),
1884 ],
1885 [
1886 'value' => 'twitter',
1887 'label' => esc_html__( 'Twitter', 'shopbuilder' ),
1888 ],
1889 [
1890 'value' => 'linkedin',
1891 'label' => esc_html__( 'Linkedin', 'shopbuilder' ),
1892 ],
1893 [
1894 'value' => 'pinterest',
1895 'label' => esc_html__( 'Pinterest', 'shopbuilder' ),
1896 ],
1897 [
1898 'value' => 'skype',
1899 'label' => esc_html__( 'Skype', 'shopbuilder' ),
1900 ],
1901 [
1902 'value' => 'whatsapp',
1903 'label' => esc_html__( 'Whatsapp', 'shopbuilder' ),
1904 ],
1905 [
1906 'value' => 'reddit',
1907 'label' => esc_html__( 'Reddit', 'shopbuilder' ),
1908 ],
1909 [
1910 'value' => 'telegram',
1911 'label' => esc_html__( 'Telegram', 'shopbuilder' ),
1912 ],
1913 ]
1914 );
1915 }
1916
1917 /**
1918 * Get Social Share link HTML.
1919 *
1920 * @param int $id Post ID.
1921 * @param array $types Preset type.
1922 * @param string $preset Style type.
1923 * @param boolean $show_icon Show icon.
1924 * @param boolean $show_text Show text.
1925 *
1926 * @return string
1927 */
1928 public static function get_social_share_html( int $id, array $types, string $preset = 'default', $show_icon = true, $show_text = true ) {
1929 $attr = [ 'postid' => $id ];
1930 $output = '';
1931
1932 if ( empty( $types ) ) {
1933 return $output;
1934 }
1935
1936 foreach ( $types as $type ) {
1937 $link = [];
1938 $link['type'] = $type['share_items'];
1939 $link['class'] = '';
1940 $link['img'] = apply_filters( 'rtsb/elements/share/default_img', '', $id, $link );
1941
1942 if ( 'site' === $id ) {
1943 $link['url'] = home_url();
1944 $link['title'] = wp_strip_all_tags( get_bloginfo( 'name' ) );
1945 } elseif ( 0 === strpos( $id, 'http' ) ) {
1946 $link['url'] = $id;
1947 $link['title'] = '';
1948 } else {
1949 $link['url'] = get_permalink( $id );
1950 $link['title'] = wp_strip_all_tags( get_the_title( $id ) );
1951
1952 if ( has_post_thumbnail( $id ) ) {
1953 $link['img'] = wp_get_attachment_image_url( get_post_thumbnail_id( $id ), 'full' );
1954 }
1955
1956 $link['img'] = apply_filters( 'rtsb/elements/share/single_img', $link['img'], $id, $link );
1957 }
1958
1959 $link['url'] = apply_filters( 'rtsb/elements/share/url', $link['url'], $link );
1960
1961 switch ( $type['share_items'] ) {
1962 case 'facebook':
1963 $link['link'] = esc_url( 'https://www.facebook.com/sharer/sharer.php?u=' . $link['url'] . '&display=popup&ref=plugin&src=share_button' );
1964 $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>';
1965 $link['attr_title'] = esc_html__( 'Share on Facebook', 'shopbuilder' );
1966 $link['social_network'] = 'Facebook';
1967 $link['social_action'] = 'Share';
1968 break;
1969 case 'twitter':
1970 $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'] );
1971 $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>';
1972 $link['attr_title'] = esc_html__( 'Share on Twitter', 'shopbuilder' );
1973 $link['social_network'] = 'Twitter';
1974 $link['social_action'] = 'Tweet';
1975 break;
1976 case 'pinterest':
1977 $link['link'] = esc_url( 'https://pinterest.com/pin/create/button/?url=' . $link['url'] . '&media=' . $link['img'] . '&description=' . $link['title'] );
1978 $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>';
1979 $link['attr_title'] = esc_html__( 'Share on Pinterest', 'shopbuilder' );
1980 $link['social_network'] = 'Pinterest';
1981 $link['social_action'] = 'Pin';
1982 break;
1983 case 'linkedin':
1984 $link['link'] = esc_url( 'https://www.linkedin.com/shareArticle?url=' . $link['url'] . '&title=' . $link['title'] );
1985 $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>';
1986 $link['attr_title'] = esc_html__( 'Share on LinkedIn', 'shopbuilder' );
1987 $link['social_network'] = 'LinkedIn';
1988 $link['social_action'] = 'Share';
1989 break;
1990 case 'skype':
1991 $link['link'] = esc_url( 'https://web.skype.com/share?url=' . $link['url'] );
1992 $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>';
1993 $link['attr_title'] = esc_html__( 'Share on Skype', 'shopbuilder' );
1994 $link['social_network'] = 'Skype';
1995 $link['social_action'] = 'Skype';
1996 break;
1997 case 'whatsapp':
1998 $link['link'] = esc_url( 'https://wa.me/?text=' . $link['title'] . ' ' . $link['url'] );
1999 $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>';
2000 $link['attr_title'] = esc_html__( 'Share on Whatsapp', 'shopbuilder' );
2001 $link['social_network'] = 'Whatsapp';
2002 $link['social_action'] = 'Share';
2003 break;
2004 case 'reddit':
2005 $link['link'] = esc_url( 'https://reddit.com/submit?url=' . $link['url'] . '&title=' . $link['title'] );
2006 $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>';
2007 $link['attr_title'] = esc_html__( 'Share on Reddit', 'shopbuilder' );
2008 $link['social_network'] = 'Reddit';
2009 $link['social_action'] = 'Share';
2010 break;
2011 case 'telegram':
2012 $link['link'] = esc_url( 'https://telegram.me/share/url?text=' . $link['title'] . '&url=' . $link['url'] );
2013 $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>';
2014 $link['attr_title'] = esc_html__( 'Share on Telegram', 'shopbuilder' );
2015 $link['social_network'] = 'Telegram';
2016 $link['social_action'] = 'Share';
2017 break;
2018 }
2019
2020 $link['label'] = $type['share_text'];
2021 $link['target'] = '_blank';
2022 $link['rel'] = 'nofollow noopener noreferrer';
2023
2024 $data = '';
2025 $link = apply_filters( 'rtsb/elements/share/share_link', $link, $id, $preset );
2026 $icon = apply_filters( 'rtsb/elements/share/share_icon', $link['icon'], $preset );
2027 $target = ! empty( $link['target'] ) ? ' target="' . esc_attr( $link['target'] ) . '" ' : '';
2028 $rel = ! empty( $link['rel'] ) ? ' rel="' . esc_attr( $link['rel'] ) . '" ' : '';
2029 $attr_title = ! empty( $link['attr_title'] ) ? ' title="' . esc_attr( $link['attr_title'] ) . '" ' : '';
2030 $elements = [];
2031
2032 // Add classes.
2033 $css_classes = [
2034 'rtsb-share-btn',
2035 sanitize_html_class( $link['type'] ),
2036 ];
2037 $css_classes = array_merge( $css_classes, explode( ' ', $link['class'] ) );
2038 $css_classes = array_map( 'sanitize_html_class', $css_classes );
2039 $css_classes = implode( ' ', array_filter( $css_classes ) );
2040
2041 unset( $attr['pin-do'], $attr['action'] );
2042
2043 if ( 'pinterest' === $type['share_items'] ) {
2044 $attr['pin-do'] = 'none';
2045 }
2046
2047 if ( 'whatsapp' === $type['share_items'] ) {
2048 $attr['action'] = 'share/whatsapp/share';
2049 }
2050
2051 $attr = apply_filters( 'rtsb/elements/share/link_data', $attr, $link, $id );
2052
2053 if ( ! empty( $attr ) ) {
2054 foreach ( $attr as $key => $val ) {
2055 $data .= ' data-' . sanitize_html_class( $key ) . '="' . esc_attr( $val ) . '"';
2056 }
2057 }
2058
2059 $additional_attr = apply_filters( 'rtsb/elements/share/additional_attr', [], $link, $id, $preset );
2060
2061 if ( ! empty( $additional_attr ) ) {
2062 $attr_output = join( ' ', $additional_attr );
2063
2064 if ( ! empty( $data ) ) {
2065 $attr_output = ' ' . $attr_output;
2066 }
2067
2068 $data .= $attr_output;
2069 }
2070
2071 $elements['wrapper_start'] = sprintf(
2072 '<li class="rtsb-share-item"><a href="%s"%s%s%s class="%s"%s>',
2073 ! empty( $link['link'] ) ? esc_attr( $link['link'] ) : '',
2074 $attr_title,
2075 $target,
2076 $rel,
2077 $css_classes,
2078 $data
2079 );
2080 $elements['wrapper_end'] = '</a></li>';
2081
2082 $elements['icon'] = $show_icon ? '<span class="rtsb-share-icon">' . ( ! empty( $icon ) ? $icon : null ) . '</span>' : null;
2083 $elements['label'] = $show_text && ! empty( $link['label'] ) ? '<span class="rtsb-share-label">' . $link['label'] . '</span>' : null;
2084 $elements['icon_label'] = '<span class="rtsb-share-icon-label">' . $elements['icon'] . $elements['label'] . '</span>';
2085 $elements = apply_filters( 'rtsb/elements/share/output_elements', $elements, $link, $id );
2086
2087 $output .= $elements['wrapper_start'] . $elements['icon_label'] . $elements['wrapper_end'];
2088 }
2089
2090 return apply_filters( 'rtsb/elements/share/list_output', $output );
2091 }
2092
2093 /**
2094 * Social Share Platforms.
2095 *
2096 * @return mixed|null
2097 */
2098 public static function is_module_active( $module ) {
2099 $modulelist = ModuleList::instance()->get_data();
2100 if ( ! empty( $modulelist[ $module ]['active'] ) ) {
2101 return true;
2102 }
2103 }
2104
2105
2106 /***
2107 * Save Settings data
2108 *
2109 * @param $section_id
2110 * @param $block_id
2111 * @param $rawOptions
2112 *
2113 * @return array
2114 */
2115 public static function set_options( $section_id = '', $block_id = '', $rawOptions = [] ) {
2116 $section_id = ! empty( $section_id ) ? sanitize_text_field( wp_unslash( $section_id ) ) : '';
2117 $block_id = ! empty( $block_id ) ? sanitize_text_field( wp_unslash( $block_id ) ) : '';
2118 $rawOptions = ! empty( $rawOptions ) ? $rawOptions : [];
2119
2120 $results = [
2121 'status' => true,
2122 'message' => '',
2123 ];
2124 if ( ! $section_id || ! $block_id ) {
2125 $results['status'] = false;
2126 $results['message'] = esc_html__( 'Section , block or options may be empty', 'shopbuilder' );
2127
2128 return $results;
2129 }
2130 $sections = Settings::instance()->get_sections();
2131 if ( empty( $sections[ $section_id ] ) || empty( $sections[ $section_id ]['list'][ $block_id ] ) ) {
2132 $results['status'] = false;
2133 $results['message'] = esc_html__( 'No section or block found with given data', 'shopbuilder' );
2134
2135 return $results;
2136 }
2137
2138 $options = DataModel::source()->get_option( $section_id, [] );
2139 $changed = false;
2140 $fields = [];
2141 if ( isset( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) && ! empty( $sections[ $section_id ]['list'][ $block_id ]['fields'] ) ) {
2142 $fields = $sections[ $section_id ]['list'][ $block_id ]['fields'];
2143 }
2144
2145 if ( empty( $fields ) ) {
2146 if ( isset( $rawOptions['active'] ) ) {
2147 $changed = true;
2148 $options[ $block_id ]['active'] = 'on' === $rawOptions['active'] ? 'on' : '';
2149 }
2150 } else {
2151 foreach ( $rawOptions as $raw_option_key => $raw_value ) {
2152 if ( $raw_option_key === 'active' ) {
2153 $changed = true;
2154 $options[ $block_id ]['active'] = $sections[ $section_id ]['list'][ $block_id ]['active'] = 'on' === $raw_value ? 'on' : '';
2155 } else {
2156 if ( isset( $fields[ $raw_option_key ] ) ) {
2157 $field = $fields[ $raw_option_key ];
2158 if ( $field['type'] === 'switch' ) {
2159 $value = 'on' === $raw_value ? 'on' : '';
2160 } elseif ( 'repeaters' === $field['type'] ) {
2161 $value = stripslashes_deep( $raw_value );
2162 } else {
2163 if ( ! empty( $field['multiple'] ) || in_array( $field['type'], [ 'checkbox', 'search_and_multi_select' ] ) ) {
2164
2165 if ( isset( $raw_value ) && is_array( $raw_value ) ) {
2166 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2167 $value = array_map( $field['sanitize_fn'], $raw_value );
2168 } else {
2169 $value = array_map( 'sanitize_text_field', $raw_value );
2170 }
2171 } else {
2172 $value = [];
2173 }
2174 } else {
2175 if ( ! empty( $field['sanitize_fn'] ) && is_callable( $field['sanitize_fn'] ) ) {
2176 $value = $field['sanitize_fn']( $raw_value );
2177 } else {
2178 $value = sanitize_text_field( $raw_value );
2179 }
2180 }
2181 }
2182 $options[ $block_id ][ $raw_option_key ] = $sections[ $section_id ]['list'][ $block_id ]['fields'][ $raw_option_key ]['value'] = $value ?? null;
2183 $changed = true;
2184 }
2185 }
2186 }
2187 }
2188 if ( ! $changed ) {
2189 $results['status'] = false;
2190 $results['message'] = esc_html__( 'No changes found for update', 'shopbuilder' );
2191
2192 return $results;
2193 }
2194
2195 DataModel::source()->set_option( $section_id, $options );
2196
2197 $results['status'] = true;
2198 $results['message'] = esc_html__( 'Successfully Saved', 'shopbuilder' );
2199 $results['sections'] = $sections;
2200
2201 return $results;
2202 }
2203
2204 /***
2205 * @param $meta_key
2206 * @param $meta_value
2207 *
2208 * @return mixed|void
2209 *
2210 *
2211 * public static function get_post_id_by_meta_key_and_value( $meta_key, $meta_value ) {
2212 * if( ! $meta_key || ! $meta_value ){
2213 * return;
2214 * }
2215 * global $wpdb;
2216 * $prepare_guery = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '%s' and meta_value = '%d'", $meta_key, $meta_value );
2217 * $the_products = $wpdb->get_col( $prepare_guery );
2218 * if( count( $the_products ) > 0 ){
2219 * return $the_products[0];
2220 * }
2221 * return;
2222 * }
2223 */
2224
2225 public static function count_products_by_taxonomies( $terms, $taxonomy = 'product_cat' ) {
2226 if ( empty( $terms ) || ! is_array( $terms ) ) {
2227 return;
2228 }
2229
2230 $args = [
2231 'limit' => -1,
2232 'return' => 'ids',
2233 ];
2234
2235 if ( 'product_cat' === $taxonomy ) {
2236 $args['product_category_id'] = $terms;
2237 } else {
2238 $args['product_tag_id'] = $terms;
2239 }
2240
2241 $query = new WC_Product_Query( $args );
2242
2243 return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2244 }
2245
2246 /**
2247 * Check if product filters widget has ajax.
2248 *
2249 * @param string $page Page name.
2250 *
2251 * @return bool
2252 */
2253 public static function product_filters_has_ajax( $page ) {
2254 if ( empty( $page ) ) {
2255 return false;
2256 }
2257
2258 if ( 'page' === get_post_type() ) {
2259 return false;
2260 }
2261
2262 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2263 $cache_key = 'product_filters_has_ajax_' . $id;
2264 if ( isset( self::$cache[ $cache_key ] ) ) {
2265 return self::$cache[ $cache_key ];
2266 }
2267 $elmap = ElementorDataMap::instance();
2268 $ajax = [];
2269
2270 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2271 $ajax[] = isset( $data['settings']['ajax_mode'] ) ? false : true;
2272 }
2273 self::$cache[ $cache_key ] = ! empty( $ajax[0] );
2274 return ! empty( $ajax[0] );
2275 }
2276
2277 /**
2278 * Check if product has applied filter.
2279 *
2280 * @param string $page Page name.
2281 *
2282 * @return bool
2283 */
2284 public static function product_has_applied_filters( $page ) {
2285 if ( empty( $page ) ) {
2286 return false;
2287 }
2288
2289 $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2290 $elmap = ElementorDataMap::instance();
2291 $ajax = [];
2292
2293 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2294 $ajax[] = ! isset( $data['settings']['active_filter'] );
2295
2296 }
2297
2298 return ! empty( $ajax[0] );
2299 }
2300
2301 /**
2302 * Get WooCommerce product categories.
2303 *
2304 * @param string|null $search_query The search string for category names.
2305 *
2306 * @return array An array of product categories with 'value' and 'label' keys.
2307 */
2308 public static function products_category_query( $search_query = null ) {
2309 if ( ! is_admin() ) {
2310 return [];
2311 }
2312 // Check if the results are already cached
2313 static $cached_results = null;
2314
2315 if ( null !== $cached_results ) {
2316 return $cached_results;
2317 }
2318
2319 $cache_key = 'product_categories_' . md5( serialize( $search_query ) );
2320 $results = wp_cache_get( $cache_key );
2321
2322 if ( ! $results ) {
2323 global $wpdb;
2324 $sql = "SELECT t.term_id, t.name
2325 FROM {$wpdb->terms} t
2326 JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2327 WHERE tt.taxonomy = 'product_cat'";
2328 if ( $search_query ) {
2329 $sql .= ' AND t.name LIKE %s';
2330 $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // Escaping and wildcard
2331 } else {
2332 $prepared_sql = $sql; // No need for placeholders
2333 }
2334 $results = $wpdb->get_results( $prepared_sql );
2335 // Cache the results in the object cache for future use
2336 wp_cache_set( $cache_key, $results ); // Adjust the expiration time as needed
2337 }
2338
2339 $cats = [];
2340 if ( ! is_array( $results ) && ! count( $results ) ) {
2341 return $cats;
2342 }
2343 foreach ( $results as $row ) {
2344 $category_id = $row->term_id;
2345 $category_title = $row->name;
2346
2347 $cats[] = [
2348 'value' => $category_id,
2349 'label' => $category_title,
2350 ];
2351 }
2352
2353 // Cache the results in a static variable for the next call
2354 $cached_results = $cats;
2355
2356 return $cats;
2357 }
2358
2359 /**
2360 * @param $search_query
2361 *
2362 * @return array
2363 */
2364 public static function get_registered_post_types( $search_query = null ) {
2365 // Get all registered post types
2366 $registered_post_types = get_post_types(
2367 [
2368 'public' => true,
2369 '_builtin' => false,
2370 ],
2371 'objects'
2372 );
2373
2374 unset( $registered_post_types['elementor_library'] );
2375 unset( $registered_post_types['e-landing-page'] );
2376 unset( $registered_post_types['rtsb_builder'] );
2377
2378 $post_types = [];
2379
2380 // Check if a search query is provided
2381 if ( ! empty( $search_query ) ) {
2382 // Filter post types based on the search query
2383 $registered_post_types = array_filter(
2384 $registered_post_types,
2385 function ( $post_type ) use ( $search_query ) {
2386 return stripos( $post_type->labels->singular_name, $search_query ) !== false;
2387 }
2388 );
2389 // Check if 'post' match the search query and include them if they do
2390 if ( stripos( 'post', $search_query ) !== false ) {
2391 $post_types[] = [
2392 'value' => 'post',
2393 'label' => 'Post',
2394 ];
2395 }
2396 } else {
2397 $post_types[] = [
2398 'value' => 'post',
2399 'label' => 'Post',
2400 ];
2401 }
2402
2403 // Convert the filtered post types to an array
2404 foreach ( $registered_post_types as $post_type ) {
2405 $post_types[] = [
2406 'value' => $post_type->name,
2407 'label' => $post_type->labels->singular_name,
2408 ];
2409 }
2410
2411 return $post_types;
2412 }
2413
2414 /**
2415 * Get Post Types.
2416 *
2417 * @param $search_query
2418 *
2419 * @return array
2420 */
2421 public static function get_post_types( $search_query = null, $args = [] ) {
2422
2423 $args = wp_parse_args(
2424 $args,
2425 [
2426 'post_type' => 'any',
2427 'posts_per_page' => 20,
2428 'orderby' => 'ID',
2429 'order' => 'DESC',
2430 ]
2431 );
2432
2433 if ( 'archive' !== $args['post_type'] ) {
2434 if ( $search_query ) {
2435 $args['s'] = $search_query;
2436 }
2437 $posts = [];
2438 $query_results = get_posts( $args );
2439 foreach ( $query_results as $post ) {
2440 $posts[] = [
2441 'value' => $post->ID,
2442 'label' => $post->post_title,
2443 ];
2444 }
2445 if ( $search_query ) {
2446 if ( strpos( '-error 404', $search_query ) ) {
2447 $posts[] = [
2448 'value' => 'error',
2449 'label' => __( '404 Error', 'shopbuilder' ),
2450 ];
2451 }
2452 } else {
2453 $posts[] = [
2454 'value' => 'error',
2455 'label' => __( '404 Error', 'shopbuilder' ),
2456 ];
2457 }
2458 } else {
2459 $posts = self::get_registered_post_types( $search_query );
2460 }
2461
2462 return $posts;
2463 }
2464
2465 /**
2466 * Get all Elementor breakpoints.
2467 *
2468 * @return array
2469 */
2470 public static function get_elementor_breakpoints() {
2471 return ( new \Elementor\Core\Breakpoints\Manager() )->get_breakpoints_config();
2472 }
2473 }
2474