🤩 File Manager - Mr.X
PHP:
8.3.33
Server:
Apache
OS:
Linux 5.14.0-611.49.1.el9_7.x86_64
User:
websparkit
Navigate
Upload:
Upload
New File
New Folder
Editing:quantity-discounts.php
<?php /** * Sales booster quantity discounts feature * * @package sales_booster_quantity_discounts.php * @since 9.0 * @author Stas * @link http://xstore.8theme.com * @license Themeforest Split Licence */ // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) { exit; } class Etheme_Sales_Booster_Quantity_Discounts { public static $instance = null; public static $option_name = 'quantity_discounts'; public $args = array( 'is_local_product' => false, 'hidden_default' => false, 'product_id' => null, 'should_render' => true ); public $settings = array(); public $product_settings = array(); public function __construct() { } public function init($product = null) { if ( !class_exists('WooCommerce')) return; if ( !get_option('xstore_sales_booster_settings_'.self::$option_name) ) return; $this->args['tab_prefix'] = 'et_'.self::$option_name; $this->args['product'] = $product; if ( $this->args['product'] ) $this->args['product_id'] = $this->args['product']->get_ID(); $this->args['is_admin'] = is_admin(); $this->args['spb'] = !!get_option( 'etheme_single_product_builder', false ); add_action('wp', array($this, 'add_actions')); /** * Fix quantity discount not applied on ajax request. */ add_filter( 'woocommerce_cart_subtotal', array( $this, 'get_cart_subtotal_filter' ), 100, 1 ); add_filter('woocommerce_get_cart_contents', array($this, 'get_cart_contents'), 20, 1); add_filter('woocommerce_cart_get_total', array($this, 'get_cart_total_filter'), 20, 1); add_filter('woocommerce_cart_item_price', array($this, 'calculate_price_discounts_filter'), 20, 3); add_filter('woocommerce_cart_item_subtotal', array($this, 'calculate_price_discounts_subtotal_filter'), 20, 3); add_filter('woocommerce_get_cart_item_from_session', array($this, 'filter_cart_item_from_session'), 20, 3); if ( $this->args['is_admin'] ) { add_action( 'woocommerce_product_write_panel_tabs', array($this, 'panel_tab') ); add_action( 'woocommerce_product_data_panels', array($this, 'panel_data') ); add_action( 'woocommerce_process_product_meta', array($this, 'save_panel_data') ); } } /** * Add global actions/filters. */ public function add_actions() { if( $this->args['product'] ){ $this->args['is_local_product'] = true; } elseif ( is_singular('product') ) { $this->args['product'] = wc_get_product(); $this->args['product_id'] = $this->args['product']->get_ID(); } else { return; } if ( $this->args['product']->is_sold_individually() || !$this->args['product']->is_purchasable() || !$this->args['product']->is_in_stock() ) { $this->args['should_render'] = false; return; } if ( in_array($this->args['product']->get_type(), array('variable'))) { $this->args['hidden_default'] = true; // if ( !get_theme_mod( 'enable_swatch', 1 ) ) { // $this->args['should_render'] = false; // return; // } } // prevent showing quantity discounts on product which does not support ajax add to cart (it means the product is // external/grouped/etc) elseif ( !$this->args['product']->supports( 'ajax_add_to_cart' ) ) { $this->args['should_render'] = false; return; } if ( etheme_is_catalog() ) return; $this->set_settings(array(), $this->args['product_id']); if ( !$this->args['is_local_product']) { $action = 'woocommerce_after_add_to_cart_button'; $priority = 15; $apply_action = true; if ( $this->args['spb'] ) { $action = 'etheme_woocommerce_template_single_excerpt'; $priority = 5; } switch ( $this->settings['position'] ) { case 'before_cart_form': $action = 'woocommerce_before_add_to_cart_form'; $priority = 5; break; case 'after_cart_form': $action = 'woocommerce_after_add_to_cart_form'; $priority = 15; break; case 'before_woocommerce_share': $action = 'woocommerce_share'; $priority = - 999; break; case 'after_woocommerce_share': $action = 'woocommerce_share'; $priority = 999; break; case 'before_product_meta': $this->args['tag'] = 'span'; $action = 'woocommerce_product_meta_start'; $priority = 5; break; case 'after_product_meta': $this->args['tag'] = 'span'; $action = 'woocommerce_product_meta_end'; $priority = 15; break; case 'shortcode': $apply_action = false; break; default: if ( $this->args['spb'] ) { switch ( $this->settings['position'] ) { case 'before_atc': $action = 'etheme_woocommerce_template_single_add_to_cart'; $priority = 5; break; case 'after_atc': $action = 'etheme_woocommerce_template_single_add_to_cart'; $priority = 15; break; case 'before_excerpt': $action = 'etheme_woocommerce_template_single_excerpt'; $priority = 5; break; case 'after_excerpt': $action = 'etheme_woocommerce_template_single_excerpt'; $priority = 15; break; } } break; } if ( $apply_action ) { add_action( $action, array( $this, 'output' ), $priority ); if ( has_action('after_page_wrapper', 'etheme_sticky_add_to_cart') ) { add_action( 'after_page_wrapper', function () use ($action, $priority) { remove_action( $action, array( $this, 'output' ), $priority ); }, -1 ); add_action( 'after_page_wrapper', function () use ($action, $priority) { add_action( $action, array( $this, 'output' ), $priority ); }, 2 ); } } } } /** * Filter product price which are loaded from session (cache) * @param $session_data * @param $values * @param $key * @return mixed */ public function filter_cart_item_from_session($session_data, $values, $key) { $product = $session_data['data']; if ( isset($session_data['et_discount_price']) ) { unset($session_data['et_discount_price']); } if ( isset($session_data['et_discount_price_done']) ) { unset($session_data['et_discount_price_done']); } // $origin_settings = $this->set_settings(); // $local_settings = $origin_settings; // if ( get_post_meta( $session_data['product_id'], $this->args['tab_prefix'].'_settings', true ) != '' ) { $local_settings = $this->set_settings(array(), $session_data['product_id']); // } if (!count($local_settings)) return $session_data; $type = $local_settings['type']; // percentage/fixed price $steps_intervals = $local_settings[$local_settings['rules'].'_ready']; $simple_price = $product->get_price(); switch ($local_settings['rules']) { case 'intervals': foreach ($steps_intervals as $step_interval) { if ( $session_data['quantity'] >= $step_interval['min'] && (empty($step_interval['max']) || $session_data['quantity'] <= $step_interval['max']) ) { if ( $type == 'percentage') $price = $simple_price - ($simple_price / 100 * $step_interval['percentage']); else $price = $simple_price >= $step_interval['percentage'] ? $simple_price - $step_interval['percentage'] : $simple_price; // in case product price is less than discount - leave origin price for product $session_data['et_discount_price'] = $price; $session_data['et_origin_price'] = $simple_price; $product->set_price($price); } } break; case 'steps': foreach ($steps_intervals as $step_interval) { if ( $session_data['quantity'] == $step_interval['every'] ) { if ( $type == 'percentage') $price = $simple_price - ($simple_price / 100 * $step_interval['percentage']); else $price = $simple_price >= $step_interval['percentage'] ? $simple_price - $step_interval['percentage'] : $simple_price; $session_data['et_discount_price'] = $price; $session_data['et_origin_price'] = $simple_price; $product->set_price($price); } } break; } $session_data['data'] = $product; return $session_data; } /** * Filter global total with products have discounts * @param $total * @return float|int */ public function get_cart_total_filter($total) { $new_total = $total; $items = WC()->cart->get_cart_contents(); foreach ( $items as $item => $values ) { /** * @var \WC_Product $product_obj */ $quantity = $values['quantity']; if ( isset($values['et_discount_price'])) $new_total -= ($values['data']->get_price() - $values['et_discount_price']) * $quantity; // else // $new_total += $values['data']->get_price() * $quantity; } return $new_total; } /** * Filter global subtotal with products have discounts * @param $subtotal * @return mixed */ public function get_cart_subtotal_filter( $subtotal ) { $new_subtotal = 0; $items = WC()->cart->get_cart_contents(); $had_taxable = false; foreach ( $items as $item => $values ) { /** * @var \WC_Product $product_obj */ $quantity = $values['quantity']; $local_price = $values['data']->get_price(); if ( isset($values['et_discount_price'])) { if ( $local_price > $values['et_discount_price']) $local_price = $values['et_discount_price']; } if ( $values['data']->is_taxable() ) { $had_taxable = true; if ( WC()->cart->display_prices_including_tax() ) { $row_price = wc_get_price_including_tax( $values['data'], array( 'qty' => $values['quantity'], 'price' => $local_price ) ); $new_subtotal += $row_price; // if ( ! wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { // $new_price .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; // } // return $new_price; } else { $row_price = wc_get_price_excluding_tax( $values['data'], array( 'qty' => $values['quantity'], 'price' => $local_price ) ); $new_subtotal += $row_price; // if ( wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { // $new_price .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; // } // return $new_price; } } else { $new_subtotal += $local_price * $quantity; } } $subtotal = wc_price( $new_subtotal ); if ( $had_taxable ) { if ( WC()->cart->display_prices_including_tax() ) { if ( ! wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; } } else { if ( wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; } } } return $subtotal; } /** * Set product price with discount if all conditions are met * @param $contents * @return mixed */ public function get_cart_contents($contents) { // return $contents; $new_contents = $contents; // $origin_settings = $this->set_settings(); foreach ( $contents as $item => $values ) { if ( isset($values['et_discount_price_done'])) continue; // if ( isset($values['et_discount_price']) ) { // unset($values['et_discount_price']); // } // if ( isset($values['et_discount_price_done']) ) { // unset($values['et_discount_price_done']); // } // $local_settings = $origin_settings; // if ( get_post_meta( $values['product_id'], $this->args['tab_prefix'].'_settings', true ) == 'custom' ) { $local_settings = $this->set_settings(array(), $values['product_id']); // } if (!count($local_settings)) continue; $type = $local_settings['type']; // percentage/fixed price $steps_intervals = $local_settings[$local_settings['rules'].'_ready']; if (!count($steps_intervals)) continue; // Use it to prevent server error "The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later." // Method "get_price" causes server error in "WC_Product_Subscription" object // Can be removed after solution from "WooCommerce Memberships" plugin - By SkyVerge if ( class_exists('WC_Product_Subscription') && $values['data'] instanceof WC_Product_Subscription ) { $values['data'] = new WC_product($values['data']->get_ID()); } $simple_price = isset($values['et_origin_price']) ? $values['et_origin_price'] : $values['data']->get_price(); switch ($local_settings['rules']) { case 'intervals': foreach ($steps_intervals as $step_interval) { if ( $values['quantity'] >= $step_interval['min'] && (empty($step_interval['max']) || $values['quantity'] <= $step_interval['max']) ) { if ( $type == 'percentage') { $price = $simple_price - ($simple_price / 100 * $step_interval['percentage']); } else $price = $simple_price >= $step_interval['percentage'] ? $simple_price - $step_interval['percentage'] : $simple_price; $values['et_discount_price'] = $price; $values['et_origin_price'] = $simple_price; $values['et_discount_price_done'] = true; } } break; case 'steps': foreach ($steps_intervals as $step_interval) { if ( $values['quantity'] == $step_interval['every'] ) { if ( $type == 'percentage') $price = $simple_price - ($simple_price / 100 * $step_interval['percentage']); else $price = $simple_price >= $step_interval['percentage'] ? $simple_price - $step_interval['percentage'] : $simple_price; $values['et_discount_price'] = $price; $values['et_origin_price'] = $simple_price; $values['et_discount_price_done'] = true; } } break; } $new_contents[$item] = $values; } return $new_contents; } /** * Filter product price from discounted if such one exists * @param $price * @param $cart_item * @param $cart_item_key * @return mixed */ public function calculate_price_discounts_filter($price, $cart_item, $cart_item_key) { $local_price = $cart_item['data']->get_price(); if ( isset($cart_item['et_discount_price']) ) { $local_price = $cart_item['et_discount_price']; } if ( $cart_item['data']->is_taxable() ) { if ( WC()->cart->display_prices_including_tax() ) { $row_price = wc_get_price_including_tax( $cart_item['data'], array( 'price' => $local_price ) ); $new_price = wc_price( $row_price ); if ( ! wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $new_price .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; } return $new_price; } else { $row_price = wc_get_price_excluding_tax( $cart_item['data'], array( 'price' => $local_price ) ); $new_price = wc_price( $row_price ); if ( wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $new_price .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; } return $new_price; } } else { return wc_price($local_price); } } /** * Filter product price with discount if all conditions are passed and quantities are in the ones set in options * @param $product_subtotal * @param $cart_item * @param $cart_item_key * @return string */ public function calculate_price_discounts_subtotal_filter($product_subtotal, $cart_item, $cart_item_key) { $local_price = $cart_item['data']->get_price(); if ( isset($cart_item['et_discount_price']) ) $local_price = $cart_item['et_discount_price']; if ( $cart_item['data']->is_taxable() ) { if ( WC()->cart->display_prices_including_tax() ) { $row_price = wc_get_price_including_tax( $cart_item['data'], array( 'qty' => $cart_item['quantity'], 'price' => $local_price ) ); $product_subtotal = wc_price( $row_price ); if ( ! wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $product_subtotal .= ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>'; } } else { $row_price = wc_get_price_excluding_tax( $cart_item['data'], array( 'qty' => $cart_item['quantity'], 'price' => $local_price ) ); $product_subtotal = wc_price( $row_price ); if ( wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) { $product_subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>'; } } } else { $row_price = (float) $local_price * (float) $cart_item['quantity']; $product_subtotal = wc_price( $row_price ); } // } return $product_subtotal; } /** * Setting settings globally or for specific product if $product_id param is set * @param array $custom_settings * @param null $product_id * @return array|mixed */ public function set_settings($custom_settings = array(), $product_id = null) { if ( !$product_id && count($this->settings)) return $this->settings; if ( $product_id && isset($this->product_settings[$product_id]) && count($this->product_settings[$product_id])) return $this->product_settings[$product_id]; $this->settings = array( 'type' => 'percentage', // fixed 'rules' => 'intervals', // steps 'intervals' => [], 'intervals_ready' => [], 'steps' => [], 'steps_ready' => [], 'title' => esc_html__('Buy more save more!', 'xstore'), 'title_tag' => 'h4', 'discount_title_tag' => 'h5', 'button_icon' => 'et_icon-shopping-bag', 'custom_icon' => false, 'icon_position' => 'left', 'button_text' => esc_html__('Add', 'xstore'), 'add_quantity' => false, 'shown_on_quick_view' => false, 'position' => $this->args['spb'] ? 'after_excerpt' : 'after_summary', 'force_load_assets' => true ); $local_settings = $this->settings; $settings = (array)get_option('xstore_sales_booster_settings', array()); if (count($settings) && isset($settings[self::$option_name])) { $local_settings = wp_parse_args( $settings[ self::$option_name ], $this->settings ); } // single product page backend custom options $product_custom_options = array(); $local_product_id = $product_id ? $product_id : $this->args['product_id']; $product_discount_type = get_post_meta( $local_product_id, $this->args['tab_prefix'].'_settings', true ); if ( $product_discount_type == 'none' ) return $this->settings; if ( $product_discount_type == 'custom' ) { $rules = get_post_meta( $local_product_id, $this->args['tab_prefix'].'_rules', true ); $option_value = get_post_meta($local_product_id, $this->args['tab_prefix'] . '_' . $rules, true); if ($option_value) { $local_settings['rules'] = $rules; $local_settings['type'] = get_post_meta( $local_product_id, $this->args['tab_prefix'].'_type', true ); $steps_intervals_ready = array(); $local_settings[$rules.'_ready'] = array(); foreach ($option_value as $option) { $prefixed_rules = $this->args['tab_prefix'] . '_' . $rules; $steps_intervals_ready['percentage'] = $option[$prefixed_rules . '_percentage']; if ( !$steps_intervals_ready['percentage'] ) continue; if ( $rules == 'intervals' ) { $steps_intervals_ready['min'] = $option[$prefixed_rules . '_min']; $steps_intervals_ready['max'] = $option[$prefixed_rules . '_max']; } else { $steps_intervals_ready['every'] = $option[$prefixed_rules . '_every']; } if ( array_filter($steps_intervals_ready ) ) $local_settings[$rules.'_ready'][] = $steps_intervals_ready; } } } else { if ( !isset($local_settings[$local_settings['rules']]) || !$local_settings[$local_settings['rules']] ) return $this->settings; $steps_intervals = explode(',', $local_settings[$local_settings['rules']]); if ( count($steps_intervals) < 1) return $this->settings; $local_settings[$local_settings['rules'].'_ready'] = array(); foreach ($steps_intervals as $steps_interval) { $steps_intervals_ready = array(); $steps_intervals_ready['percentage'] = $local_settings[$steps_interval . '_percentage']; if ( !$steps_intervals_ready['percentage'] ) continue; if ( $local_settings['rules'] == 'intervals' ) { $steps_intervals_ready['min'] = $local_settings[$steps_interval . '_min']; $steps_intervals_ready['max'] = $local_settings[$steps_interval . '_max']; } else { $steps_intervals_ready['every'] = $local_settings[$steps_interval . '_every']; } if ( array_filter($steps_intervals_ready ) ) $local_settings[$local_settings['rules'].'_ready'][] = $steps_intervals_ready; } // empty all values if ( !array_filter($local_settings[$local_settings['rules'].'_ready'])) return $this->settings; } $this->settings = wp_parse_args( $product_custom_options, $local_settings ); $this->settings = wp_parse_args( $custom_settings, $this->settings ); if ( $product_id ) $this->product_settings[$product_id] = $this->settings; return $this->settings; } /** * Output quantity discounts block if all conditions are passed */ public function output() { if ( !apply_filters('etheme_sales_booster_quantity_discounts', true) ) return; if ( !$this->args['should_render']) return; $settings = apply_filters($this->args['tab_prefix'].'_settings', isset($this->product_settings[$this->args['product_id']]) ? $this->product_settings[$this->args['product_id']] : $this->settings, $this->args); if ( !isset($settings[$settings['rules'].'_ready']) || !count($settings[$settings['rules'].'_ready'])) return; if ( !isset($this->args['product_id']) || !$this->args['product_id']) return; ob_start(); $this->calculated_discounts($settings); $discounts = ob_get_clean(); if ( !$discounts) { $this->args['hidden_default'] = true; // return; } if ( $settings['force_load_assets'] ) etheme_enqueue_style( 'sale-booster-quantity-discounts', true ); ?> <div class="sales-booster-quantity-discounts-wrapper<?php echo esc_attr($this->args['hidden_default'] ? ' hidden': ''); ?>"> <?php echo !empty($settings['title']) ? '<'.$settings['title_tag'].' class="quantity-discounts-title">'.$settings['title'].'</'.$settings['title_tag'].'>' : ''; ?> <?php echo '<div class="sales-booster-quantity-discounts">' . $discounts . '</div>'; ?> </div> <?php } /** * Calculated all discount for specific product and output with specific structure * @param $settings */ public function calculated_discounts($settings) { if ( !count($settings[$settings['rules'].'_ready']) ) return; if ( get_query_var('is_mobile', false) ) { $settings['button_text'] = ''; if ( !$settings['custom_icon'] && $settings['button_icon'] == 'none') { switch (get_theme_mod('cart_icon_et-desktop', 'type1')) { case 'type1': $settings['button_icon'] = 'et_icon-shopping-bag'; break; case 'type2': $settings['button_icon'] = 'et_icon-shopping-basket'; break; case 'type4': $settings['button_icon'] = 'et_icon-shopping-cart-2'; break; default: $settings['button_icon'] = 'et_icon-shopping-cart'; break; } } } $stock_quantity = $this->args['product']->get_stock_quantity(); foreach ($settings[$settings['rules'].'_ready'] as $discount) { $intervals = $settings['rules'] == 'intervals'; if ( ($intervals && !$this->args['product']->has_enough_stock( $discount['min'] )) || ( !$intervals && !$this->args['product']->has_enough_stock( $discount['every'] ) ) ) continue; $max_quantity = ''; if ( $intervals ) { if ($discount['max']) { $max_quantity = $discount['max']; if ($stock_quantity && $stock_quantity <= $max_quantity) { $max_quantity = $stock_quantity; } } } else { if ($discount['every']) { $max_quantity = $discount['every']; if ($stock_quantity && $stock_quantity <= $max_quantity) { $max_quantity = $stock_quantity; } } } $should_display_qty = $intervals && !($max_quantity && $max_quantity == $discount['min']); ?> <div class="quantity-discount-item"> <div class="quantity-discount-info"> <?php if ( $settings['rules'] == 'intervals') { if ( $max_quantity && $max_quantity == $discount['min'] ) { echo sprintf(__('%1s Buy %2s items and get %3s OFF %4s %5s on each product %6s', 'xstore'), '<' . $settings['discount_title_tag'] . ' class="quantity-discount-name">', $discount['min'], ($settings['type'] == 'percentage' ? $discount['percentage'] . '%' : wc_price($discount['percentage'])), '</' . $settings['discount_title_tag'] . '>', '<span class="quantity-discount-suggest">', '</span>'); } else { echo sprintf(__('%1s Buy from %2s to %3s items and get %4s OFF %5s %6s on each product %7s', 'xstore'), '<' . $settings['discount_title_tag'] . ' class="quantity-discount-name">', $discount['min'], ($max_quantity ? $max_quantity : '∞'), ($settings['type'] == 'percentage' ? $discount['percentage'] . '%' : wc_price($discount['percentage'])), '</' . $settings['discount_title_tag'] . '>', '<span class="quantity-discount-suggest">', '</span>'); } } else echo sprintf(__('%1s Buy %2s items get %3s OFF %4s %5s on each product %6s', 'xstore'), '<'.$settings['discount_title_tag'].' class="quantity-discount-name">', $discount['every'], ($settings['type'] == 'percentage' ? $discount['percentage'].'%' : wc_price($discount['percentage']) ), '</'.$settings['discount_title_tag'].'>', '<span class="quantity-discount-suggest">', '</span>'); ?> </div> <div class="quantity-discount-add"> <?php if ( $should_display_qty && $settings['rules'] == 'intervals' && $settings['add_quantity'] == 'on') : add_action( 'woocommerce_before_quantity_input_field', 'et_quantity_minus_icon' ); add_action( 'woocommerce_after_quantity_input_field', 'et_quantity_plus_icon' ); add_filter('woocommerce_quantity_input_classes', array($this, 'quantity_input_classes'), 10, 2); woocommerce_quantity_input( array( 'input_value' => $discount['min'], 'max_value' => $max_quantity, 'min_value' => $discount['min'], 'quantity_type' => 'input', ) ); remove_filter('woocommerce_quantity_input_classes', array($this, 'quantity_input_classes'), 10, 2); remove_action( 'woocommerce_before_quantity_input_field', 'et_quantity_minus_icon' ); remove_action( 'woocommerce_after_quantity_input_field', 'et_quantity_plus_icon' ); endif; ?> <button type="submit" class="add_to_cart_button ajax_add_to_cart button btn bordered" data-product_id="<?php echo esc_attr($this->args['product_id']); ?>" data-qty="<?php echo 'intervals' == $settings['rules'] ? $discount['min'] : $discount['every']; ?>"> <?php if ( $settings['icon_position'] == 'left' && $settings['button_icon'] != 'none' ) { echo '<span class="et_b-icon et-icon' . (!$settings['custom_icon'] ? ' ' . str_replace('et_icon-', 'et-', $settings['button_icon']) : '') . '">'. ($settings['custom_icon'] ? $settings['custom_icon'] : '') . '</span>'; } if ( !empty($settings['button_text']) ) echo '<span class="button-text">' . $settings['button_text'] . '</span>'; if ( $settings['icon_position'] == 'right' && $settings['button_icon'] != 'none' ) { echo '<span class="et_b-icon et-icon' . (!$settings['custom_icon'] ? ' ' . str_replace('et_icon-', 'et-', $settings['button_icon']) : '') . '">'. ($settings['custom_icon'] ? $settings['custom_icon'] : '') . '</span>'; } ?> </button> </div> </div> <?php } } public function quantity_input_classes($classes, $product) { $classes[] = 'quantity-discount-qty-input'; return $classes; } /** * Shortcode output functions with custom params (if needed) * @param array $atts * @return false|string */ public function shortcode_output($atts=array()) { $atts = is_array($atts) ? $atts : array(); if ( isset($atts['product_id']) ) { if ( wc_get_product($atts['product_id']) ) { $this->args['product_id'] = $atts['product_id']; if ( count($this->settings) < 1) $this->set_settings(array(), $this->args['product_id']); } else { wc_print_notice( wp_kses_post( sprintf(esc_html__('Product with id "%s" does not exists or was permanently removed.', 'xstore'), $atts['product_id']) ), 'error'); return; } } else { if (count($this->settings) < 1) $this->set_settings(); } $this->settings = wp_parse_args($atts, $this->settings); ob_start(); $this->output(); return ob_get_clean(); } /** * Add settings panel list item on Single product editor */ public function panel_tab() { ?> <li class="<?php echo esc_attr($this->args['tab_prefix']); ?>_options <?php echo esc_attr($this->args['tab_prefix']); ?>_tab hide_if_virtual hide_if_external"> <a href="#<?php echo esc_attr($this->args['tab_prefix']); ?>_data"><span> <?php echo esc_html__( 'Quantity discounts', 'xstore' ); ?> <?php echo '<span class="et-brand-label" style="background: var(--et_admin_dark-color, #222); color: #fff; font-size: 0.65em; line-height: 1; padding: 2px 5px; border-radius: 3px; margin: 0; margin-inline-start: 3px;">'.apply_filters('etheme_theme_label', 'XStore').'</span>'; ?> </span></a> </li> <?php } /** * Add settings panel on Single product editor */ public function panel_data() { ?> <div id="<?php echo esc_attr($this->args['tab_prefix']); ?>_data" class="panel woocommerce_options_panel"> <div class="options_group"> <p class="form-field"> <?php woocommerce_wp_select([ 'id' => $this->args['tab_prefix'].'_settings', 'label' => __( 'Quantity discounts', 'xstore' ), 'options' => [ 'inherit' => __('Inherit', 'xstore'), 'none' => __('None', 'xstore'), 'custom' => __('Custom', 'xstore'), ], ] ); woocommerce_wp_radio([ 'id' => $this->args['tab_prefix'].'_type', 'label' => __( 'Discount Type', 'xstore' ), 'options' => [ 'percentage' => esc_html__('By percentage (example: 10% OFF)', 'xstore'), 'fixed' => str_replace('{{currency_symbol}}', get_woocommerce_currency_symbol( get_woocommerce_currency() ), esc_html__( 'By fixed price (example: 10{{currency_symbol}} OFF)', 'xstore' )), ], ] ); woocommerce_wp_radio([ 'id' => $this->args['tab_prefix'].'_rules', 'label' => __( 'Discount Rules', 'xstore' ), 'options' => [ 'intervals' => __('Intervals', 'xstore'), 'steps' => __('Steps', 'xstore'), ], ]); $this->sortable_options([ 'id' => $this->args['tab_prefix'].'_intervals', 'label' => __( 'Intervals', 'xstore' ), 'option_key' => 'intervals' ]); $this->sortable_options([ 'id' => $this->args['tab_prefix'].'_steps', 'label' => __( 'Steps', 'xstore' ), 'option_key' => 'steps' ]); ?> </p> </div> </div> <?php wp_add_inline_script( 'etheme_admin_js', " jQuery(document).ready(function($) { let prod_quantity_discounts_options = $('#woocommerce-product-data').find('#et_quantity_discounts_settings'); if ( prod_quantity_discounts_options.length ) { setTimeout(function () { prod_quantity_discounts_options.trigger('change'); $('input[name=et_quantity_discounts_type]:checked, input[name=et_quantity_discounts_rules]:checked').trigger('change'); }, 500); $('#woocommerce-product-data') .on( 'change', '#et_quantity_discounts_settings', function () { var wrap = $(this).closest('.panel'); switch (this.value) { case 'none': case 'inherit': wrap .find('.et_quantity_discounts_type_field, .et_quantity_discounts_rules_field, .et_quantity_discounts_intervals_field, .et_quantity_discounts_steps_field') .hide(); setTimeout(function () { $('.et_quantity_discounts_intervals_field, .et_quantity_discounts_steps_field').hide(); }, 300); break; case 'custom': wrap .find('.et_quantity_discounts_type_field, .et_quantity_discounts_rules_field, .et_quantity_discounts_intervals_field, .et_quantity_discounts_steps_field') .show(); $('input[name=et_quantity_discounts_type]:checked, input[name=et_quantity_discounts_rules]:checked').trigger('change'); break; } return false; } ) .on( 'change', 'input[name=et_quantity_discounts_type]', function () { $('.et_quantity_discounts_intervals_field, .et_quantity_discounts_steps_field') .find('.switch-texts-table-col').text($(this).parent().text()); return false; }) .on( 'change', 'input[name=et_quantity_discounts_rules]', function () { switch (this.value) { case 'steps': $('.et_quantity_discounts_intervals_field').hide(); break; case 'intervals': $('.et_quantity_discounts_steps_field').hide(); break; } $('.et_quantity_discounts_'+this.value+'_field').show(); return false; }); // $(document).on( // 'change', // '#woocommerce-product-data .percentage-switcher-input', // function () { // if ($('input[name=et_quantity_discounts_type]:checked').val() == 'fixed') return false; // if ( this.value > 100) // $(this).val(100); // return false; // } // ) } });", 'after' ); } /** * Save settings from quantity discount panel on Single product editor * @param $post_id */ public function save_panel_data( $post_id ) { $quantity_discount_settings = isset( $_POST[$this->args['tab_prefix'].'_settings'] ) ? $_POST[$this->args['tab_prefix'].'_settings'] : ''; if ( $quantity_discount_settings ) update_post_meta( $post_id, $this->args['tab_prefix'].'_settings', $quantity_discount_settings ); else delete_post_meta( $post_id, $this->args['tab_prefix'].'_settings' ); $quantity_discount_type = isset( $_POST[$this->args['tab_prefix'].'_type'] ) ? $_POST[$this->args['tab_prefix'].'_type'] : ''; if ( $quantity_discount_type ) update_post_meta( $post_id, $this->args['tab_prefix'].'_type', $quantity_discount_type ); else delete_post_meta( $post_id, $this->args['tab_prefix'].'_type' ); $quantity_discount_rules = isset( $_POST[$this->args['tab_prefix'].'_rules'] ) ? $_POST[$this->args['tab_prefix'].'_rules'] : ''; if ( $quantity_discount_rules ) update_post_meta( $post_id, $this->args['tab_prefix'].'_rules', $quantity_discount_rules ); else delete_post_meta( $post_id, $this->args['tab_prefix'].'_rules' ); $this->sortable_options_save($post_id, 'intervals'); $this->sortable_options_save($post_id, 'steps'); } /** * New type of field - sortable (adding/removing rows) * @param $field * @param WC_Data|null $data */ public function sortable_options($field, WC_Data $data = null) { global $post; $field['wrapper_class'] = isset( $field['wrapper_class'] ) ? $field['wrapper_class'] : ''; $options = get_post_meta($post->ID, $field['id'], true); echo '<fieldset class="form-field et-repeater ' . esc_attr( $field['id'] ) . '_field ' . esc_attr( $field['wrapper_class'] ) . '"><legend>' . wp_kses_post( $field['label'] ) . '</legend>'; if ( ! empty( $field['description'] ) && false !== $field['desc_tip'] ) { echo wc_help_tip( $field['description'] ); } ?> <script type="text/javascript"> jQuery(document).ready(function($) { $('#add-row-<?php echo esc_js($field['id']); ?>').on('click', function() { var row = $('.empty-row.screen-reader-text[data-type="<?php echo esc_js($field['id']); ?>"]').clone(true); row.removeClass('empty-row screen-reader-text'); row.attr('data-type', null); row.insertBefore('#repeatable-fieldset-<?php echo esc_js($field['id']); ?> tbody>tr:last'); $('#repeatable-fieldset-<?php echo esc_js($field['id']); ?> tbody tr .button').attr('disabled', null); return false; }); $('.remove-row-<?php echo esc_js($field['id']); ?>').on('click', function(e) { e.preventDefault(); if ( $(this).attr('disabled') ) { alert('You cannot remove last item'); return; } var items = $(this).parents('tbody').find('tr:not(.empty-row)').length; if ( items <= 2 ) { $(this).parents('tbody').find('tr:not(.empty-row) td:last-child .button').attr('disabled', '1'); } $(this).parents('tr').remove(); return false; }); }); </script> <table id="repeatable-fieldset-<?php echo esc_attr($field['id']); ?>" width="100%"> <thead> <tr> <?php if ($field['option_key'] == 'intervals') : ?> <td><?php echo esc_html__('Min', 'xstore'); ?></td> <td><?php echo esc_html__('Max', 'xstore'); ?></td> <?php else: ?> <td><?php echo esc_html__('Every X items', 'xstore'); ?></td> <?php endif; ?> <td class="switch-texts-table-col"><?php echo sprintf(esc_html__('By percentage (example: 10%1s OFF)', 'xstore'), '%'); ?></td> <td><?php echo esc_html__('Action', 'xstore'); ?></td> </tr> </thead> <tbody> <?php $empty_table_columns = (($field['option_key'] == 'intervals') ? '<td> <input type="number" placeholder="Enter min value" min="1" name="'.$this->args['tab_prefix'].'_'.$field['option_key'].'_min[]" /> </td> <td> <input type="number" placeholder="Enter max value" min="1" name="'.$this->args['tab_prefix'].'_'.$field['option_key'].'_max[]" /> </td>' : '<td> <input type="number" min="1" name="'.$this->args['tab_prefix'].'_'.$field['option_key'].'_every[]" /> </td>') . '<td> <input type="number" class="percentage-switcher-input" placeholder="Enter percentage value" min="0" max="" name="'.$this->args['tab_prefix'].'_'.$field['option_key'].'_percentage[]" /> </td> <td> <a class="button remove-row-'.$field['id'].'" href="#">'.esc_html__('Remove', 'xstore').'</a> </td>'; if ($options) : foreach ($options as $option) { ?> <tr> <?php if ($field['option_key'] == 'intervals') : ?> <td> <input type="number" placeholder="Enter min value" min="1" name="<?php echo esc_attr($this->args['tab_prefix'] . '_' . $field['option_key']); ?>_min[]" value="<?php if (isset($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_min'])) echo esc_attr($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_min']); ?>" /> </td> <td> <input type="number" placeholder="Enter max value" min="1" name="<?php echo esc_attr($this->args['tab_prefix'] . '_' . $field['option_key']); ?>_max[]" value="<?php if (isset($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_max'])) echo esc_attr($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_max']); ?>" /> </td> <?php else: ?> <td> <input type="number" min="1" name="<?php echo esc_attr($this->args['tab_prefix'] . '_' . $field['option_key']); ?>_every[]" value="<?php if (isset($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_every'])) echo esc_attr($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_every']); ?>" /> </td> <?php endif; ?> <td> <input type="number" class="percentage-switcher-input" placeholder="Enter percentage value" min="0" max="" name="<?php echo esc_attr($this->args['tab_prefix'].'_'.$field['option_key']); ?>_percentage[]" value="<?php if (isset($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_percentage'])) echo esc_attr($option[$this->args['tab_prefix'].'_'.$field['option_key'].'_percentage']); ?>" /> </td> <td> <a class="button remove-row-<?php echo esc_attr($field['id']); ?>" href="#"><?php echo esc_html__('Remove', 'xstore'); ?></a> </td> </tr> <?php } else : // show a blank one echo '<tr>'.$empty_table_columns.'</tr>'; endif; ?> <!-- empty hidden one for jQuery --> <?php echo '<tr class="empty-row screen-reader-text" data-type="'. esc_attr($field['id']). '">'.$empty_table_columns.'</tr>'; ?> </tbody> </table> <p><a id="add-row-<?php echo esc_attr($field['id']); ?>" class="button" href="#"><?php echo esc_html__('Add new', 'xstore'); ?></a></p> <?php echo '</fieldset>'; } /** * Saving action for new sortable field option type * @param $post_id * @param string $type */ public function sortable_options_save($post_id, $type='intervals') { $previous_value = get_post_meta($post_id, $this->args['tab_prefix'].'_'.$type, true); $prefix = $this->args['tab_prefix'] . '_' . $type; if ( $type == 'intervals' ) { $intervals_new = array(); $interval_min = isset($_POST[$prefix . '_min']) ? $_POST[$prefix . '_min'] : 0; $interval_max = isset($_POST[$prefix . '_max']) ? $_POST[$prefix . '_max'] : 0; $interval_percentage = isset($_POST[$prefix . '_percentage']) ? $_POST[$prefix . '_percentage'] : 0; for ($i = 0; $i < count(max($interval_min, $interval_max, $interval_percentage)); $i++) { $_min = stripslashes($interval_min[$i]); // and however you want to sanitize $_max = stripslashes($interval_max[$i]); // and however you want to sanitize $_percentage = stripslashes($interval_percentage[$i]); // and however you want to sanitize if ( $_min || $_max || $_percentage) { $intervals_new[$i][$prefix . '_min'] = $_min; $intervals_new[$i][$prefix . '_max'] = $_max; $intervals_new[$i][$prefix . '_percentage'] = $_percentage; } } if (!empty($intervals_new) && $intervals_new != $previous_value) update_post_meta($post_id, $prefix, $intervals_new); elseif (empty($intervals_new) && $previous_value) delete_post_meta($post_id, $prefix); } else { $steps_new = array(); $steps_every = isset($_POST[$prefix . '_every']) ? $_POST[$prefix . '_every'] : 0; $interval_percentage = isset($_POST[$prefix . '_percentage']) ? $_POST[$prefix . '_percentage'] : 0; for ($i = 0; $i < count(max($steps_every, $interval_percentage)); $i++) { $_every = stripslashes($steps_every[$i]); // and however you want to sanitize $_percentage = stripslashes($interval_percentage[$i]); // and however you want to sanitize if ( $_every || $_percentage ) { $steps_new[$i][$prefix . '_every'] = $_every; $steps_new[$i][$prefix . '_percentage'] = $_percentage; } } if (!empty($steps_new) && $steps_new != $previous_value) update_post_meta($post_id, $prefix, $steps_new); elseif (empty($steps_new) && $previous_value) delete_post_meta($post_id, $prefix); } } /** * Returns the instance. * * @return object * @since 9.0 */ public static function get_instance( $shortcodes = array() ) { if ( null == self::$instance ) { self::$instance = new self( $shortcodes ); } return self::$instance; } }
Save Changes
Cancel
Create New File
Create New Folder