PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.1.13
ShopBuilder – WooCommerce Builder For Elementor v2.1.13
3.4.2 3.4.1 3.4.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 All 63 releases
shopbuilder / app / Helpers / Fns.php

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

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