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

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