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

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