newInstanceWithoutConstructor();
}
/**
* Class construct.
*
* @param array $settings widget settings.
* @param array $widget widget object.
* @since 1.1.0
*/
public function __construct( $settings, $widget ) {
$this->settings = $settings;
$this->widget = $widget;
$this->actions();
}
/**
* Actions.
* required actions to apply changes to woocommerce checkout shortcode.
*
* @return void
* @since 1.1.0
*/
private function actions() {
// Empty cart notice.
add_action( 'woocommerce_before_checkout_form_cart_notices', [ $this, 'empty_cart_notice' ] );
// sellkit wrapper for checkout widget.
add_action( 'woocommerce_before_checkout_form', [ $this, 'open_multistep_wrap' ] );
add_action( 'woocommerce_after_checkout_form', [ $this, 'close_multistep_wrap' ], 999 );
// Add express section to widget.
$this->add_express_checkout();
// Replace Woocommerce templates that require huge changes.
add_filter( 'woocommerce_locate_template', [ $this, 'template_replace' ], 1, 3 );
// Remove coupon from it's default location. will add custom coupon form based on design at desired location.
remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10, 1 );
// Filter all woocommerce field right before printing them in our widget.
add_filter( 'woocommerce_checkout_fields', [ $this, 'woocommerce_checkout_fields' ] );
// Hide shipping fields section title.
add_filter( 'sellkit-checkout-disable-shipping-fields-title', [ $this, 'check_to_disable_shipping_title' ] );
// Customize shipping fields based on user desire.
add_action( 'sellkit_checkout_shipping_fields', [ $this, 'sellkit_checkout_shipping_field' ], 10, 2 );
// Customize billing fields based on user desire.
add_action( 'sellkit_checkout_billing_fields', [ $this, 'sellkit_checkout_billing_field' ], 10, 2 );
// Rename Shipping text to Shipping Method.
add_filter( 'woocommerce_shipping_package_name', [ $this, 'rename_shipping_text' ] );
// Remove login form from default location.
remove_action( 'woocommerce_before_checkout_form', 'woocommerce_checkout_login_form', 10 );
// Append widget settings to checkout form 2 hidden fields form_id post_id.
add_action( 'woocommerce_checkout_before_customer_details', [ $this, 'widget_settings' ] );
// Apply custom messages.
$this->custom_messages();
// Enable or Disable Sections based on user selection.
add_action( 'woocommerce_before_checkout_form', function() {
self::enable_disable_sections( $this->settings, [] );
} );
// Inject required scripts for google address autocomplete.
add_action( 'sellkit-after-checkout-content', [ $this, 'inject_google_autocomplete' ] );
// Order bump html.
add_action( 'woocommerce_before_checkout_form', [ $this, 'order_bump' ] );
// Bundle products.
$this->bundled_products();
}
/**
* Add custom notice if cart is empty.
*
* @return void
* @since 1.1.0
*/
public function empty_cart_notice() {
global $woocommerce;
$count = $woocommerce->cart->get_cart_contents_count();
if ( 0 === $count ) {
/* translators: %s: Empty cart message. */
echo sprintf(
'
%s
',
$this->settings['empty_cart']
);
}
}
/**
* Opens a wrapper around widget checkout form.
*
* @return void
* @since 1.1.0
*/
public function open_multistep_wrap() {
?>
';
}
/**
* Inject express checkout section into the checkout form based on layout.
*
* @return void
* @since 1.1.0
*/
private function add_express_checkout() {
if ( ! sellkit()->has_pro ) {
return;
}
$show_express = true;
// Disable express checkout.
if ( 'yes' !== $this->settings['show_express_checkout'] ) {
$show_express = false;
}
$gateways = [
'Paypal_For_Woocommerce',
'Woo_Payment_Gateway',
'Stripe_For_Woocommerce',
'Klarma_Checkout_Woocommerce',
'Amazon_Pay_Woocommerce',
'Stripe_Woocommerce_Official',
];
foreach ( $gateways as $gateway ) {
$class = 'Sellkit\Elementor\Modules\Checkout\Integrations\\' . $gateway;
$class = new $class();
$class->run();
}
if ( false === $show_express ) {
return;
}
if ( 'one-page' === $this->settings['layout-type'] ) {
add_action( 'sellkit_checkout_one_page_express_methods', [ $this, 'express_checkout_html' ] );
return;
}
add_action( 'sellkit-checkout-step-a-begins', [ $this, 'express_checkout_html' ], 20 );
}
/**
* Replace Woocommerce templates that needs to be changed, locally.
* this template_replace does not affect default checkout page.
*
* @param string $template template name.
* @param string $template_name name.
* @param string $template_path path of template.
* @return string
* @since 1.1.0
*/
public function template_replace( $template, $template_name, $template_path ) {
$basename = basename( $template );
$our_path = sellkit()->plugin_dir() . 'includes/elementor/modules/checkout/';
switch ( $basename ) {
case 'form-checkout.php':
$template = $our_path . 'templates/form-checkout.php';
break;
case 'form-login.php':
$template = $our_path . 'templates/form-login.php';
break;
case 'form-shipping.php':
$template = $our_path . 'templates/form-shipping.php';
break;
case 'form-billing.php':
$template = $our_path . 'templates/form-billing.php';
break;
case 'payment.php':
$template = $our_path . 'templates/payment.php';
break;
case 'payment-method.php':
$template = $our_path . 'templates/payment-method.php';
break;
case 'review-order.php':
$template = $our_path . 'templates/review-order.php';
break;
case 'cart-shipping.php':
$template = $our_path . 'templates/cart-shipping.php';
break;
case 'cart-item-data.php':
$template = $our_path . 'templates/cart-item-data.php';
break;
}
return $template;
}
/**
* Customize shipping fields parameters based on user needs through widget options.
*
* @param array $default_fields woocommerce fields.
* @return array
* @since 1.1.0
*/
public function customize_shipping_field( $default_fields ) {
$widget_shipping_fields = $this->settings['shipping_list'];
$widget_field_slug = Helper::instance()->get_user_defined_fields_slug( $widget_shipping_fields, 'shipping_list_field' );
// Unset default fields.
foreach ( $default_fields as $key => $field ) {
if ( ! in_array( $key, $widget_field_slug, true ) ) {
unset( $default_fields[ $key ] );
}
}
return $default_fields;
}
/**
* Check to disable shipping fields section title.
*
* @since 1.2.1
*/
public function check_to_disable_shipping_title() {
$widget_shipping_fields = $this->settings['shipping_list'];
if ( 0 === count( $widget_shipping_fields ) ) {
return false;
}
return true;
}
/**
* Print shipping field in frontend using our customized fields.
*
* @param array $fields : shipping fields.
* @param object $checkout : checkout object.
* @since 1.1.0
*/
public function sellkit_checkout_shipping_field( $fields, $checkout ) {
$default_text = [
'shipping_first_name',
'shipping_last_name',
'shipping_company',
'shipping_address_1',
'shipping_address_2',
'shipping_postcode',
'shipping_city',
];
$default_select = [
'shipping_country',
'shipping_state',
];
foreach ( $fields as $key => $details ) {
if ( in_array( $key, $default_text, true ) ) {
$fields[ $key ]['type'] = 'text';
}
if ( in_array( $key, $default_select, true ) ) {
$fields[ $key ]['type'] = 'select';
}
}
$widget_shipping_fields = $this->settings['shipping_list'];
$default_fields = Helper::instance()->assign_settings_per_field( $fields, $widget_shipping_fields, 'shipping', $this->settings );
$priority = array_column( $default_fields, 'priority' );
array_multisort( $priority, SORT_ASC, $default_fields );
foreach ( $default_fields as $key => $details ) {
if ( ! array_key_exists( 'type', $details ) ) {
continue;
}
$class = $details['type'];
$class = '\Sellkit\Elementor\Modules\Checkout\Fields\\' . ucfirst( $class );
$class = new $class();
$field = '';
$class->final_html_structure( $field, $details, $key );
}
}
/**
* Customize billing fields parameters based on user needs through widget options.
*
* @param array $default_fields woocommerce fields.
* @return array
* @since 1.1.0
*/
public function customize_billing_field( $default_fields ) {
$widget_billing_fields = $this->settings['billing_list'];
$widget_field_slug = Helper::instance()->get_user_defined_fields_slug( $widget_billing_fields, 'billing_list_field' );
// Unset default fields.
foreach ( $default_fields as $key => $field ) {
if ( ! in_array( $key, $widget_field_slug, true ) && 'billing_email' !== $field ) {
unset( $default_fields[ $key ] );
}
}
return $default_fields;
}
/**
* Print billing fields in frontend using our customized fields.
*
* @param array $fields : billing fields.
* @param object $checkout : checkout object.
* @since 1.1.0
*/
public function sellkit_checkout_billing_field( $fields, $checkout ) {
$default_text = [
'billing_first_name',
'billing_last_name',
'billing_company',
'billing_address_1',
'billing_address_2',
'billing_postcode',
'billing_city',
];
$default_select = [
'billing_country',
'billing_state',
];
$default_tel = [
'billing_phone',
];
foreach ( $fields as $key => $details ) {
if ( in_array( $key, $default_text, true ) ) {
$fields[ $key ]['type'] = 'text';
}
if ( in_array( $key, $default_select, true ) ) {
$fields[ $key ]['type'] = 'select';
}
if ( in_array( $key, $default_tel, true ) ) {
$fields[ $key ]['type'] = 'tel';
}
}
$widget_billing_fields = $this->settings['billing_list'];
$default_fields = Helper::instance()->assign_settings_per_field( $fields, $widget_billing_fields, 'billing', $this->settings );
$priority = array_column( $default_fields, 'priority' );
array_multisort( $priority, SORT_ASC, $default_fields );
foreach ( $default_fields as $key => $details ) {
// Never shot field that it's type is not defined.
if ( ! array_key_exists( 'type', $details ) ) {
continue;
}
// Billing email is already defined at top of page.
if ( 'billing_email' === $key ) {
continue;
}
$class = $details['type'];
$class = '\Sellkit\Elementor\Modules\Checkout\Fields\\' . ucfirst( $class );
$class = new $class();
$field = '';
$field = $class->final_html_structure( $field, $details, $key );
}
}
/**
* Hook to fields before shortcode.
*
* @param array $default_fields woocommerce fields.
* @return array
* @since 1.1.0
*/
public function woocommerce_checkout_fields( $default_fields ) {
$widget_billing_fields = $this->settings['billing_list'];
$widget_shipping_fields = $this->settings['shipping_list'];
// Unset fields.
$default_shipping_fields = $default_fields['shipping'];
$default_fields['shipping'] = $this->customize_shipping_field( $default_shipping_fields );
// Unset fields.
$default_billing_fields = $default_fields['billing'];
$default_fields['billing'] = $this->customize_billing_field( $default_billing_fields );
foreach ( $widget_billing_fields as $data ) {
$slug = $data['billing_list_field'];
if ( 'yes' !== $data['billing_list_required'] && array_key_exists( $slug, $default_fields['billing'] ) ) {
$default_fields['billing'][ $slug ]['required'] = false;
unset( $default_fields['billing'][ $slug ]['required'] );
}
}
foreach ( $widget_shipping_fields as $data ) {
$slug = $data['shipping_list_field'];
if ( 'yes' !== $data['shipping_list_required'] && array_key_exists( $slug, $default_fields['shipping'] ) ) {
$default_fields['shipping'][ $slug ]['required'] = false;
unset( $default_fields['shipping'][ $slug ]['required'] );
}
}
return $default_fields;
}
/**
* Rename Shipping text
*
* @param string $name title of shipping methods.
* @return string
* @since 1.1.0
*/
public function rename_shipping_text( $name ) {
return '';
}
/**
* Adds widget settings as hidden input to checkout form, to be used in various places.
*
* @return void
* @since 1.1.0
*/
public function widget_settings() {
?>
documents ) && ! empty( Elementor::$instance->documents->get_current() ) ) {
return Elementor::$instance->documents->get_current()->get_main_id();
}
return get_the_ID();
}
/**
* Express checkout html.
*
* @return void
* @since 1.1.0
*/
public function express_checkout_html() {
ob_start();
?>
settings['already_have_account_text'];
} );
add_filter( 'sellkit_core/widgets/checkout/custom_message/login_toggle_text', function() {
return $this->settings['login_toggle_text'];
} );
add_action( 'sellkit_core/widgets/checkout/custom_message/create_website_account', function() {
$site = get_bloginfo( 'name' );
$text = $this->settings['create_website_account'];
echo str_replace( '{{website}}', $site, $text );
} );
add_filter( 'sellkit_core/widgets/checkout/custom_message/secure_transaction_text', function() {
return $this->settings['secure_transaction_text'];
} );
add_filter( 'sellkit_core/widgets/checkout/custom_message/select_address_text', function() {
return $this->settings['select_address_text'];
} );
add_filter( 'sellkit-checkout-place-order-btn-text', function( $default ) {
if ( ! empty( $this->settings['place_order_btn_txt'] ) ) {
return $this->settings['place_order_btn_txt'];
}
return $default;
}, 10 );
}
/**
* Enable or disable some of checkout sections.
*
* @param array $settings widget settings.
* @param array $posted_data checkout form data.
* @return void
* @since 1.1.0
*/
public static function enable_disable_sections( $settings, $posted_data = [] ) {
if ( Elementor::$instance->editor->is_edit_mode() || array_key_exists( 'sellkit-elementor-editor-mode', $posted_data ) ) {
add_action( 'sellkit-checkout-after-coupon-form-ajax', [ Helper::instance(), 'editor_mode_extra_js' ] );
// Let coupon form to be viewable in editor mode.
add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
Global_Hooks::instance()->coupon_form( $settings );
} );
return;
}
if ( wp_doing_ajax() ) {
// Hide order item images.
add_filter( 'sellkit/includes/elementor/modules/checkout/product-image', function( $img_html ) use ( $settings ) {
if ( ! array_key_exists( 'order_summary_show_image', $settings ) ) {
return $img_html;
}
return '';
} );
add_filter( 'sellkit-checkout-cart-item', function( $cart_item ) use ( $settings ) {
if ( ! array_key_exists( 'show_cart_items', $settings ) ) {
return $cart_item;
}
return '';
} );
// Disable order item quantity input.
if ( array_key_exists( 'show_cart_edit', $settings ) || array_key_exists( 'bundle-products-force', $posted_data ) ) {
add_action( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
}
add_filter( 'woocommerce_shipping_package_name', function() {
return '';
} );
add_filter( 'woocommerce_cart_ready_to_calc_shipping', function() use ( $settings ) {
if ( ! array_key_exists( 'show_shipping_method', $settings ) ) {
return true;
}
return false;
} );
add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
Global_Hooks::instance()->coupon_form( $settings );
} );
return;
}
// Normal mode when page is loading.
add_filter( 'woocommerce_cart_ready_to_calc_shipping', function() use ( $settings ) {
if ( 'yes' === $settings['show_shipping_method'] ) {
return true;
}
return false;
}, 10 );
add_filter( 'sellkit/includes/elementor/modules/checkout/product-image', function( $img_html ) use ( $settings ) {
if ( 'yes' === $settings['order_summary_show_image'] ) {
return $img_html;
}
return '';
} );
add_filter( 'sellkit-checkout-cart-item', function( $cart_item ) use ( $settings ) {
if ( 'yes' === $settings['show_cart_items'] ) {
return $cart_item;
}
return '';
}, 10 );
// Disable order item quantity input.
if ( array_key_exists( 'show_cart_edit', $settings ) ) {
add_action( 'sellkit-checkout-widget-disable-quantity', [ Helper::instance(), 'checkout_order_hidden_quantity' ], 10, 2 );
add_filter( 'sellkit/includes/elementor/modules/checkout/quanity/class', function( $classes ) {
return $classes .= ' sellkit-checkout-widget-d-block';
} );
}
add_filter( 'sellkit-checkout-place-order-btn-text', function( $place_order_btn ) {
if ( ! empty( $settings['place_order_btn_txt'] ) ) {
return $settings['place_order_btn_txt'];
}
return $place_order_btn;
}, 10 );
add_action( 'sellkit-checkout-widget-custom-coupon-form', function() use ( $settings ) {
if ( 'yes' !== $settings['show_coupon_field'] ) {
return;
}
self::coupon_form( $settings );
} );
}
/**
* Inject google autocomplete address script if enabled for shipping/billing section.
*
* @return void
* @since 1.1.0
*/
public function inject_google_autocomplete() {
if ( ! sellkit()->has_pro ) {
return;
}
\Sellkit_Elementor_Checkout_Pro_Module::checkout_google_autocomplete_address( $this->settings );
}
/**
* Place custom coupon form in checkout order-review based design.
* ! we have used same form in Global_Hooks. but this one for first load of page.
*
* @param array $settings widget settings.
* @see sellkit_Core\Raven\Modules\Checkout\Classes\Global_Hooks::coupon_form.
* @return void
* @since 1.1.0
*/
public static function coupon_form( $settings ) {
echo '
| ';
self::form( $settings );
echo ' |
';
}
/**
* New custom coupon form HTML.
*
* @param array $settings widget settings.
* @return void
* @since 1.1.0
*/
public static function form( $settings ) {
$class = 'sellkit-custom-coupon-form-d-none';
if ( ! array_key_exists( 'coupon_field_type', $settings ) || 'normal' === $settings['coupon_field_type'] ) {
$class = 'sellkit-normal-coupon-form';
}
?>
editor_mode_extra_js(); ?>
query_vars;
$option = 'allow-buyers';
if ( array_key_exists( 'funnel_product_settings', $query ) ) {
$option = $query['funnel_product_settings'];
}
if ( 'allow-buyers' === $option ) {
return;
}
add_action( 'sellkit_checkout_required_hidden_fields', function() use ( $option ) {
?>
cart->get_cart();
$default = null;
$default_q = null;
ob_start();
?>
cart->empty_cart();
if ( null !== $default ) {
WC()->cart->add_to_cart( $default, $default_q );
}
}
}
/**
* Trigger to activate order bumb or not for this checkout.
*
* @param array $data checkout form data.
* @since 1.1.0
* @return void
*/
public static function order_bump( $data = [] ) {
if ( 'woocommerce_before_checkout_form' === current_action() ) {
self::order_bump_frontend_manager();
return;
}
if ( ! array_key_exists( 'sellkit_current_page_id', $data ) ) {
return;
}
$bumps_in_cart = [];
$simple_bumps = [];
$page = $data['sellkit_current_page_id'];
$funnel = new Sellkit_Funnel( $page );
if ( ! array_key_exists( 'bump', $funnel->current_step_data ) ) {
return;
}
$bumps = $funnel->current_step_data['bump'];
// Get added bump products in cart.
foreach ( $data as $key => $value ) {
if ( strpos( $key, 'sellkit_bump_data' ) !== false ) {
$bumps_in_cart[] = $value;
}
}
if ( empty( $bumps_in_cart ) ) {
return;
}
foreach ( $bumps as $bump ) {
// Check if product is set for bump.
if ( ! array_key_exists( 'products', $bump['data'] ) ) {
continue;
}
$products = $bump['data']['products'];
$product_id = array_key_first( $products['list'] );
$product_details = current( $products['list'] );
// Check if product has discount.
if ( empty( $product_details['discount'] ) ) {
continue;
}
$simple_bumps[ $product_id ] = [
'type' => $product_details['discountType'],
'discount' => $product_details['discount'],
];
}
if ( empty( $simple_bumps ) ) {
return;
}
$fee = 0;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
if ( ! array_key_exists( $product_id, $simple_bumps ) ) {
continue;
}
$discount_data = $simple_bumps[ $product_id ];
$quantity = $cart_item['quantity'];
$price = $cart_item['data']->get_price();
$total = $price * $quantity;
if ( 'percentage' === $discount_data['type'] ) {
$discount = ( $total * $discount_data['discount'] ) / 100;
} else {
$discount = $discount_data['discount'];
}
$fee = $fee + $discount;
}
if ( $fee <= 0 ) {
return;
}
add_action( 'woocommerce_cart_calculate_fees', function() use ( $fee ) {
WC()->cart->add_fee( esc_html__( 'Bump Discount', 'sellkit' ), -$fee, true, 'standard' );
} );
}
/**
* Order bump to manage frontend.
*
* @since 1.1.0
* @return void
*/
private static function order_bump_frontend_manager() {
global $wp_query;
$query = $wp_query->query_vars;
if ( ! array_key_exists( 'bump_data', $query ) ) {
return;
}
$bumps = $query['bump_data'];
foreach ( $bumps as $bump ) {
if ( ! array_key_exists( 'products', $bump['data'] ) ) {
continue;
}
$design = $bump['data']['design'];
$products = $bump['data']['products'];
add_action( $design['sellkit_funnels_bump_position'], function() use ( $design, $products ) {
self::bump_html( $design, $products );
}, 5 );
}
}
/**
* Order bump html.
*
* @param array $design design properties.
* @param array $products products details.
* @since 1.1.0
* @return void
*/
public static function bump_html( $design, $products ) {
$list = $products['list'];
$ids = [];
foreach ( $list as $id => $details ) {
$ids[] = $id;
$qty = $details['quantity'];
$discount = $details['discount'];
$type = $details['discountType'];
}
$ids_string = implode( '|', $ids );
$product = wc_get_product( $ids_string );
$qty = ( empty( $qty ) ) ? 1 : $qty;
if ( empty( $product ) || ! $product->get_id() ) {
return;
}
$unique_id = 'bump-title-' . $ids_string;
$class = '';
if (
'sellkit-checkout-before-order-summary' === current_action() ||
'sellkit-checkout-after-order-summary' === current_action()
) {
$class = 'sellkit-bump-review-order';
}
?>