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

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

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