PluginProbe
ShopBuilder – WooCommerce Builder For Elementor / 2.0.2
ShopBuilder – WooCommerce Builder For Elementor v2.0.2
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
← All changes | app/Helpers/Fns.php +57 -346 2.1.12.0.2 View file →
@@ -11,15 +11,14 @@
11 11 if ( ! defined( 'ABSPATH' ) ) {
12 12 exit( 'This script cannot be accessed directly.' );
13 13 }
14 14
15 -use WC_Product;
16 -use WC_Product_Query;
17 15 use Elementor\Icons_Manager;
18 16 use RadiusTheme\SB\Models\ReSizer;
19 17 use RadiusTheme\SB\Models\DataModel;
20 18 use RadiusTheme\SB\Models\ModuleList;
21 19 use RadiusTheme\SB\Models\Settings;
20 +use WC_Product;
22 21
23 22 /**
24 23 * Fns class
25 24 */
@@ -25,13 +24,8 @@
25 24 */
26 25 class Fns {
27 26
28 27 /**
29 - * @var array
30 - */
31 - private static $cache = [];
32 -
33 - /**
34 28 * Verify nonce.
35 29 *
36 30 * @return bool
37 31 */
@@ -204,9 +198,8 @@
204 198 }
205 199
206 200 do_action( 'rtsb/core/before_template_part', $template_name, $located, $args );
207 201 include $located;
208 -
209 202 do_action( 'rtsb/core/after_template_part', $template_name, $located, $args );
210 203
211 204 if ( $return ) {
212 205 return ob_get_clean();
@@ -320,9 +313,9 @@
320 313 $_post_id = wp_cache_get( $cache_key );
321 314
322 315 if ( false === $_post_id || 'publish' !== get_post_status( $_post_id ) ) {
323 316 $_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' )
317 + $wpdb->prepare( "SELECT MAX(ID) FROM {$wpdb->prefix}posts WHERE post_type = %s AND post_status = %s", 'product', 'publish' )
325 318 );
326 319 wp_cache_set( $cache_key, $_post_id, '', 12 * HOUR_IN_SECONDS );
327 320 }
328 321
@@ -329,28 +322,19 @@
329 322 return $_post_id;
330 323 }
331 324
332 325 /**
333 - * Get the product function. Only used in single page widgets.
326 + * Get the product function.
334 327 *
335 328 * @return object
336 329 */
337 330 public static function get_product() {
338 - global $product;
339 -
331 + global $post, $product;
340 332 if ( is_singular( 'product' ) && $product instanceof WC_Product ) {
341 - do_action( 'rtsb_before_product_template_render' );
342 333 return $product;
343 334 }
344 - $cache_key = 'prepared_product_for_preview';
345 - if ( isset( self::$cache[ $cache_key ] ) ) {
346 - return self::$cache[ $cache_key ];
347 - }
348 335
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;
336 + return wc_get_product( self::get_prepared_product_id() );
353 337 }
354 338
355 339 /**
356 340 * Error function
@@ -899,38 +883,8 @@
899 883 return $allowed_html;
900 884 }
901 885
902 886 /**
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 887 * Insert Some array element
934 888 *
935 889 * @param [type] $key The elements will insert nearby this key.
936 890 * @param [type] $main_array Original array.
@@ -1006,8 +960,9 @@
1006 960 'slider-layout2' => esc_html__( 'Slider Layout 2', 'shopbuilder' ),
1007 961 'category-single-layout1' => esc_html__( 'Category Single Layout 1', 'shopbuilder' ),
1008 962 'category-layout1' => esc_html__( 'Category Layout 1', 'shopbuilder' ),
1009 963 'category-layout2' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
964 + 'category-layout3' => esc_html__( 'Category Layout 2', 'shopbuilder' ),
1010 965 ];
1011 966
1012 967 return apply_filters( 'rtsb/elements/elementor/free_layouts', $layouts );
1013 968 }
@@ -1025,12 +980,8 @@
1025 980
1026 981 if ( empty( $taxonomy ) ) {
1027 982 return $terms;
1028 983 }
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 984
1034 985 $termList = get_terms( [ $taxonomy ], [ 'hide_empty' => 0 ] );
1035 986
1036 987 if ( is_array( $termList ) && ! empty( $termList ) && empty( $termList['errors'] ) ) {
@@ -1043,9 +994,9 @@
1043 994 foreach ( $termList as $term ) {
1044 995 $terms[ $term->term_id ] = esc_html( $term->name );
1045 996 }
1046 997 }
1047 - self::$cache[ $cache_key ] = $terms;
998 +
1048 999 return $terms;
1049 1000 }
1050 1001
1051 1002 /**
@@ -1155,12 +1106,8 @@
1155 1106 *
1156 1107 * @return int|array
1157 1108 */
1158 1109 public static function get_terms( $taxonomy, $first_term = false, $only_parents = false, $return_slug = false ) {
1159 - if ( ! is_admin() ) {
1160 - return [];
1161 - }
1162 -
1163 1110 // TODO:: Return Slug always true for all settings.
1164 1111 $term_list = [];
1165 1112 $args = [
1166 1113 'taxonomy' => $taxonomy,
@@ -1194,67 +1141,25 @@
1194 1141
1195 1142 /**
1196 1143 * Get product rating.
1197 1144 *
1198 - * @param array $args Arguments.
1199 - *
1200 1145 * @return string|void
1201 1146 */
1202 - public static function get_product_rating_html( $args = [] ) {
1147 + public static function get_product_rating_html() {
1203 1148 global $product;
1204 1149
1205 - $html = '';
1206 - $rating_count = $product->get_rating_count();
1207 - $average_rating = $product->get_average_rating();
1150 + $html = '';
1151 + $rating_count = $product->get_rating_count();
1152 + $average = $product->get_average_rating();
1208 1153
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;
1154 + if ( ! $rating_count || empty( wc_get_rating_html( $average, $rating_count ) ) ) {
1155 + return $html;
1222 1156 }
1223 1157
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 -
1158 + $html .= '<div class="product-rating">';
1159 + $html .= wc_get_rating_html( $average, $rating_count );
1160 + // TODO:: Disable Rating Count option need to apply.
1161 + $html .= ! empty( $html ) ? '<span class="count">(' . $average . ')</span>' : '';
1257 1162 $html .= '</div>';
1258 1163
1259 1164 self::print_html( $html, true );
1260 1165 }
@@ -1545,20 +1450,8 @@
1545 1450 *
1546 1451 * @return string
1547 1452 */
1548 1453 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 1454 $attributes = [
1562 1455 'aria-hidden' => 'true',
1563 1456 ];
1564 1457
@@ -1571,11 +1464,9 @@
1571 1464 if ( defined( 'ELEMENTOR_VERSION' ) && ! empty( $control ) && 'elementor' === $builder ) {
1572 1465 Icons_Manager::render_icon( $control, $attributes );
1573 1466 }
1574 1467
1575 - $icons = ob_get_clean();
1576 - self::$cache[ $cache_key ] = $icons;
1577 - return $icons;
1468 + return ob_get_clean();
1578 1469 }
1579 1470
1580 1471 /**
1581 1472 * Get product swatches.
@@ -1613,79 +1504,9 @@
1613 1504 if ( ! $product->is_on_sale() ) {
1614 1505 return '';
1615 1506 }
1616 1507
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 -
1508 + $badge_text = '';
1688 1509 $percentage = null;
1689 1510 $max_percentage = '';
1690 1511
1691 1512 if ( $product->is_type( 'simple' ) ) {
@@ -1710,12 +1531,16 @@
1710 1531 }
1711 1532 }
1712 1533
1713 1534 if ( $max_percentage > 0 ) {
1714 - return round( $max_percentage );
1535 + $badge_text = '-' . round( $max_percentage ) . '%';
1715 1536 }
1716 1537
1717 - return 0;
1538 + if ( 'text' === $type ) {
1539 + $badge_text = $text;
1540 + }
1541 +
1542 + return $badge_text;
1718 1543 }
1719 1544
1720 1545 /**
1721 1546 * Pagination
@@ -1841,8 +1666,9 @@
1841 1666 */
1842 1667 do_action( 'rtsb/elements/elementor/additional_action_buttons', $items );
1843 1668 $html .= ob_get_clean();
1844 1669
1670 + // $html .= self::get_action_button_by_type( $items, 'quick_checkout' );
1845 1671 $html .= self::get_action_button_by_type( $items, 'wishlist' );
1846 1672 $html .= self::get_action_button_by_type( $items, 'compare' );
1847 1673 $html .= self::get_action_button_by_type( $items, 'quick_view' );
1848 1674 $html .= '</ul>';
@@ -1855,8 +1681,9 @@
1855 1681 } elseif ( 'preset1' === $preset || 'preset2' === $preset || 'preset4' === $preset ) {
1856 1682 $html .= '<div class="rtsb-action-buttons ' . esc_attr( $class ) . '">';
1857 1683 $html .= '<ul class="rtsb-action-button-list">';
1858 1684 $html .= self::get_action_button_by_type( $items, 'add_to_cart', $ajax_cart );
1685 + // $html .= self::get_action_button_by_type( $items, 'quick_checkout' );
1859 1686
1860 1687 ob_start();
1861 1688 /**
1862 1689 * Additional formatted action buttons hook.
@@ -2268,22 +2095,30 @@
2268 2095 if ( empty( $terms ) || ! is_array( $terms ) ) {
2269 2096 return;
2270 2097 }
2271 2098
2099 + $category_ids = array_map(
2100 + function ( $name ) {
2101 + return sanitize_title( $name );
2102 + },
2103 + $terms
2104 + );
2105 +
2272 2106 $args = [
2273 - 'limit' => -1,
2274 - 'return' => 'ids',
2107 + 'post_type' => 'product',
2108 + 'posts_per_page' => - 1,
2109 + 'tax_query' => [
2110 + [
2111 + 'taxonomy' => $taxonomy,
2112 + 'field' => 'term_id',
2113 + 'terms' => $category_ids,
2114 + ],
2115 + ],
2275 2116 ];
2276 2117
2277 - if ( 'product_cat' === $taxonomy ) {
2278 - $args['product_category_id'] = $terms;
2279 - } else {
2280 - $args['product_tag_id'] = $terms;
2281 - }
2118 + $query = new \WP_Query( $args );
2282 2119
2283 - $query = new WC_Product_Query( $args );
2284 -
2285 - return ! empty( $query->get_products() ) ? count( $query->get_products() ) : 0;
2120 + return $query->found_posts;
2286 2121 }
2287 2122
2288 2123 /**
2289 2124 * Check if product filters widget has ajax.
@@ -2296,17 +2131,9 @@
2296 2131 if ( empty( $page ) ) {
2297 2132 return false;
2298 2133 }
2299 2134
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 - }
2135 + $id = BuilderFns::is_builder_preview() ? get_the_ID() : BuilderFns::builder_page_id_by_type( $page );
2309 2136 $elmap = ElementorDataMap::instance();
2310 2137 $ajax = [];
2311 2138
2312 2139 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
@@ -2311,9 +2138,9 @@
2311 2138
2312 2139 foreach ( $elmap->get_widget_data( 'rtsb-ajax-product-filters', [], $id ) as $data ) {
2313 2140 $ajax[] = isset( $data['settings']['ajax_mode'] ) ? false : true;
2314 2141 }
2315 - self::$cache[ $cache_key ] = ! empty( $ajax[0] );
2142 +
2316 2143 return ! empty( $ajax[0] );
2317 2144 }
2318 2145
2319 2146 /**
@@ -2347,37 +2174,24 @@
2347 2174 *
2348 2175 * @return array An array of product categories with 'value' and 'label' keys.
2349 2176 */
2350 2177 public static function products_category_query( $search_query = null ) {
2351 - if ( ! is_admin() ) {
2352 - return [];
2353 - }
2178 + global $wpdb;
2354 2179
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
2180 + $sql = "SELECT t.term_id, t.name
2366 2181 FROM {$wpdb->terms} t
2367 2182 JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
2368 2183 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
2184 +
2185 + if ( $search_query ) {
2186 + $sql .= ' AND t.name LIKE %s';
2187 + $prepared_sql = $wpdb->prepare( $sql, '%' . $wpdb->esc_like( $search_query ) . '%' ); // Escaping and wildcard
2188 + } else {
2189 + $prepared_sql = $sql; // No need for placeholders
2378 2190 }
2379 2191
2192 + $results = $wpdb->get_results( $prepared_sql );
2193 +
2380 2194 $cats = [];
2381 2195 if ( ! is_array( $results ) && ! count( $results ) ) {
2382 2196 return $cats;
2383 2197 }
@@ -2390,10 +2204,8 @@
2390 2204 'label' => $category_title,
2391 2205 ];
2392 2206 }
2393 2207
2394 - // Cache the results in a static variable for the next call
2395 - self::$cache[ $cache_key ] = $cats;
2396 2208 return $cats;
2397 2209 }
2398 2210
2399 2211 /**
@@ -2508,107 +2320,6 @@
2508 2320 * @return array
2509 2321 */
2510 2322 public static function get_elementor_breakpoints() {
2511 2323 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 2324 }
2614 2325 }