HEX
Server: nginx/1.27.1
System: Linux in-3 5.15.0-161-generic #171-Ubuntu SMP Sat Oct 11 08:17:01 UTC 2025 x86_64
User: ivenus-clone (3297)
PHP: 7.4.33
Disabled: exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source
Upload Files
File: /storage/v4513/tepnot/public_html/wp-content/plugins/dokan-pro/includes/Ajax.php
<?php

namespace WeDevs\DokanPro;

use WC_Countries;
use WC_Meta_Box_Product_Data;
use WC_Product_Variable;
use WC_Tax;
use WeDevs\DokanPro\Shipping\ShippingZone;

/**
 * Dokan Pro Ajax class
 *
 * @since   2.4
 *
 * @package dokan
 */
class Ajax {

    /**
     * Loading automatically when class initiate
     *
     * @since 2.4
     *
     * @uses  action hook
     * @uses  filter hook
     */
    public function __construct() {

        // Shipping ajax hanlding
        add_action( 'wp_ajax_dps_select_state_by_country', [ $this, 'load_state_by_country' ] );
        add_action( 'wp_ajax_nopriv_dps_select_state_by_country', [ $this, 'load_state_by_country' ] );

        // shipping state ajax
        add_action( 'wp_ajax_nopriv_dokan_shipping_country_select', [ $this, 'get_state_by_shipping_country' ] );
        add_action( 'wp_ajax_dokan_shipping_country_select', [ $this, 'get_state_by_shipping_country' ] );

        // shipping calculation ajax
        add_action( 'wp_ajax_nopriv_dokan_shipping_calculator', [ $this, 'get_calculated_shipping_cost' ] );
        add_action( 'wp_ajax_dokan_shipping_calculator', [ $this, 'get_calculated_shipping_cost' ] );

        // Variation Handle for Vendor frontend
        add_action( 'wp_ajax_dokan_add_variation', [ $this, 'add_variation' ] );
        add_action( 'wp_ajax_dokan_link_all_variations', [ $this, 'link_all_variations' ] );
        add_action( 'wp_ajax_dokan_pre_define_attribute', [ $this, 'dokan_pre_define_attribute' ] );
        add_action( 'wp_ajax_dokan_save_attributes', [ $this, 'save_attributes' ] );
        add_action( 'wp_ajax_dokan_remove_variation', [ $this, 'remove_variations' ] );
        add_action( 'wp_ajax_dokan_load_variations', [ $this, 'load_variations' ] );
        add_action( 'wp_ajax_dokan_save_variations', [ $this, 'save_variations' ] );
        add_action( 'wp_ajax_dokan_bulk_edit_variations', [ $this, 'bulk_edit_variations' ] );

        // Single product Design ajax
        add_action( 'wp_ajax_dokan_get_pre_attribute', [ $this, 'add_attr_predefined_attribute' ] );
        add_action( 'wp_ajax_nopriv_dokan_get_pre_attribute', [ $this, 'add_attr_predefined_attribute' ] );
        add_action( 'wp_ajax_dokan_add_new_attribute', [ $this, 'add_new_attribute' ] );
        add_action( 'wp_ajax_nopriv_dokan_add_new_attribute', [ $this, 'add_new_attribute' ] );
        add_action( 'wp_ajax_dokan_load_order_items', [ $this, 'load_order_items' ] );
        add_action( 'wp_ajax_nopriv_dokan_load_order_items', [ $this, 'load_order_items' ] );

        add_action( 'wp_ajax_dokan_toggle_seller', [ $this, 'toggle_seller_status' ] );

        // Shipping Zone
        add_action( 'wp_ajax_dokan-get-shipping-zone', [ $this, 'get_shipping_zone' ] );
        add_action( 'wp_ajax_dokan-update-shipping-method-settings', [ $this, 'update_shipping_methods_settings' ] );
        add_action( 'wp_ajax_dokan-toggle-shipping-method-enabled', [ $this, 'toggle_shipping_method' ] );
        add_action( 'wp_ajax_dokan-save-zone-settings', [ $this, 'save_zone_settings' ] );
        add_action( 'wp_ajax_dokan-add-shipping-method', [ $this, 'add_shipping_method' ] );
        add_action( 'wp_ajax_dokan-delete-shipping-method', [ $this, 'delete_shipping_method' ] );
        add_action( 'wp_ajax_dokan-save-shipping-settings', [ $this, 'save_shipping_settings' ] );
        add_action( 'wp_ajax_dokan-get-shipping-settings', [ $this, 'get_shipping_settings' ] );

        // Profile Progressbar
        add_action( 'wp_ajax_dokan_user_closed_progressbar', [ $this, 'user_closed_progressbar' ] );
        add_action( 'wp_ajax_dokan_dashboard_dismiss_profile_progressbar', [ $this, 'dismiss_profile_progressbar' ] );
    }

    /**
     * Get shipping zone
     *
     * @since 2.8.0
     * @since 3.7.23 Shipping zone sorting support added.
     *
     * @return void
     */
    public function get_shipping_zone() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        if ( isset( $_POST['zoneID'] ) ) {
            $zones = ShippingZone::get_zone( absint( $_POST['zoneID'] ) );
        } else {
            $zones = ShippingZone::get_zones();
            // we are sorting by `zone_order` key of the zone.
            usort(
                $zones,
                function ( $zone1, $zone2 ) {
                    // handle the `Locations not covered by your other zones`
                    // here zone id = 0 is the zone "not covered by your other zones";.
                    if ( 0 === $zone1['id'] ) {
                        return 1;
                    } elseif ( 0 === $zone2['id'] ) {
                        return -1;
                    }

                    return $zone1['zone_order'] <=> $zone2['zone_order'];
                }
            );
        }

        wp_send_json_success( $zones );
    }

    /**
     * Get shipping methods
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function get_shipping_methods() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        if ( ! isset( $_POST['zoneID'] ) ) {
            wp_send_json_error( __( 'Zone not found', 'dokan' ) );
        }

        $methods = ShippingZone::get_shipping_methods( absint( $_POST['zoneID'] ) );

        wp_send_json_success( $methods );
    }

    /**
     * Update shipping methods settings
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function update_shipping_methods_settings() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $zone_id = isset( $_POST['zoneID'] ) ? absint( $_POST['zoneID'] ) : '';

        if ( '' === $zone_id ) {
            wp_send_json_error( __( 'Shipping zone not found', 'dokan' ) );
        }

        $defaults = [
            'instance_id' => '',
            'method_id'   => '',
            'zone_id'     => $zone_id,
            'settings'    => [],
        ];

        $args = dokan_parse_args( $_POST['data'], $defaults ); // phpcs:ignore

        if ( empty( $args['settings']['title'] ) ) {
            wp_send_json_error( __( 'Shipping title must be required', 'dokan' ) );
        }

        $result = ShippingZone::update_shipping_method( $args );

        wp_send_json_success( $result );
    }

    /**
     * Toggle shipping method
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function toggle_shipping_method() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $zone_id = isset( $_POST['zoneID'] ) ? absint( $_POST['zoneID'] ) : '';
        if ( '' === $zone_id ) {
            wp_send_json_error( __( 'Shipping zone not found', 'dokan' ) );
        }

        $instance_id = ! empty( $_POST['instance_id'] ) ? absint( $_POST['instance_id'] ) : 0;
        if ( ! $instance_id ) {
            wp_send_json_error( __( 'Shipping method not found', 'dokan' ) );
        }

        $data = [
            'instance_id' => $instance_id,
            'zone_id'     => $zone_id,
            'checked'     => isset( $_POST['checked'] ) && $_POST['checked'] === 'true' ? 1 : 0,
        ];

        $result = ShippingZone::toggle_shipping_method( $data );

        if ( is_wp_error( $result ) ) {
            wp_send_json_error( $result->get_error_message() );
        }

        $message = $data['checked'] ? __( 'Shipping method enabled successfully', 'dokan' ) : __( 'Shipping method disabled successfully', 'dokan' );
        wp_send_json_success( $message );
    }

    /**
     * Add new shipping method for a zone
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function add_shipping_method() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $zone_id = isset( $_POST['zoneID'] ) ? absint( $_POST['zoneID'] ) : '';
        if ( '' === $zone_id ) {
            wp_send_json_error( __( 'Shipping zone not found', 'dokan' ) );
        }

        if ( empty( $_POST['method'] ) ) {
            wp_send_json_error( __( 'Please select a shipping method', 'dokan' ) );
        }

        $data = [
            'zone_id'   => $zone_id,
            'method_id' => sanitize_text_field( $_POST['method'] ),
            'settings'  => $_POST['settings'], // phpcs:ignore
        ];

        $result = ShippingZone::add_shipping_methods( $data );
        if ( is_wp_error( $result ) ) {
            wp_send_json_error( $result->get_error_message(), 'dokan' );
        }

        wp_send_json_success( __( 'Shipping method added successfully', 'dokan' ) );
    }

    /**
     * Delete shipping method
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function delete_shipping_method() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $zone_id = isset( $_POST['zoneID'] ) ? $_POST['zoneID'] : '';

        if ( $zone_id === '' ) {
            wp_send_json_error( __( 'Shipping zone not found', 'dokan' ) );
        }

        if ( empty( $_POST['instance_id'] ) ) {
            wp_send_json_error( __( 'Shipping method not found', 'dokan' ) );
        }

        $data = [
            'zone_id'     => $zone_id,
            'instance_id' => $_POST['instance_id'],
        ];

        $result = ShippingZone::delete_shipping_methods( $data );

        if ( is_wp_error( $result ) ) {
            wp_send_json_error( $result->get_error_message(), 'dokan' );
        }

        wp_send_json_success( __( 'Shipping method deleted', 'dokan' ) );
    }

    /**
     * Save shipping Settings
     *
     * @since 2.9.2
     *
     * @return void
     */
    public function save_shipping_settings() {
        if ( ! wp_verify_nonce( $_POST['nonce'], 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $settings = $_POST['settings'];
        $user_id  = dokan_get_current_user_id();

        if ( isset( $settings['processing_time'] ) ) {
            update_user_meta( $user_id, '_dps_pt', $settings['processing_time'] );
        }

        if ( isset( $settings['shipping_policy'] ) ) {
            update_user_meta( $user_id, '_dps_ship_policy', $settings['shipping_policy'] );
        }

        if ( isset( $settings['refund_policy'] ) ) {
            update_user_meta( $user_id, '_dps_refund_policy', $settings['refund_policy'] );
        }

        update_user_meta( $user_id, '_dps_zone_wise_settings', 'yes' );

        wp_send_json_success( __( 'Settings save successfully', 'dokan' ) );
    }

    /**
     * Get shipping settings
     *
     * @since 2.9.2
     *
     * @return void
     */
    public function get_shipping_settings() {
        if ( ! wp_verify_nonce( $_POST['nonce'], 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $user_id = dokan_get_current_user_id();

        $dps_processing  = get_user_meta( $user_id, '_dps_pt', true );
        $shipping_policy = get_user_meta( $user_id, '_dps_ship_policy', true );
        $refund_policy   = get_user_meta( $user_id, '_dps_refund_policy', true );

        $shipping_data = [
            'processing_time' => $dps_processing,
            'shipping_policy' => $shipping_policy,
            'refund_policy'   => $refund_policy,
        ];

        wp_send_json_success( $shipping_data );
    }

    /**
     * Save zone settings
     *
     * @since 2.8.0
     *
     * @return void
     */
    public function save_zone_settings() {
        if ( ! wp_verify_nonce( $_POST['nonce'], 'dokan_reviews' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $zone_id = isset( $_POST['zoneID'] ) ? $_POST['zoneID'] : '';

        if ( $zone_id === '' ) {
            wp_send_json_error( __( 'Shipping zone not found', 'dokan' ) );
        }

        $location = [];

        if ( ! empty( $_POST['continent'] ) && is_array( $_POST['continent'] ) ) {
            $continent_array = [];

            foreach ( $_POST['continent'] as $continent ) {
                $continent_array[] = [
                    'code' => $continent['code'],
                    'type' => 'continent',
                ];
            }

            $location = array_merge( $location, $continent_array );
        }

        if ( ! empty( $_POST['country'] ) && is_array( $_POST['country'] ) ) {
            $country_array = [];

            foreach ( $_POST['country'] as $country ) {
                $country_array[] = [
                    'code' => $country['code'],
                    'type' => 'country',
                ];
            }

            $location = array_merge( $location, $country_array );
        }

        if ( ! empty( $_POST['state'] ) && is_array( $_POST['state'] ) ) {
            $state_array = [];

            foreach ( $_POST['state'] as $state ) {
                $state_array[] = [
                    'code' => $state['code'],
                    'type' => 'state',
                ];
            }

            $location = array_merge( $location, $state_array );
        }

        if ( ! empty( $_POST['postcode'] ) ) {
            $postcodes      = explode( ',', $_POST['postcode'] );
            $postcode_array = [];

            foreach ( $postcodes as $postcode ) {
                if ( false !== strpos( $postcode, '...' ) ) {
                    $postcode = implode( '...', array_map( 'trim', explode( '...', $postcode ) ) );
                }

                $postcode_array[] = [
                    'code' => trim( $postcode ),
                    'type' => 'postcode',
                ];
            }

            $location = array_merge( $location, $postcode_array );
        }

        $result = ShippingZone::save_location( $location, $zone_id );

        wp_send_json_success( __( 'Zone settings save successfully', 'dokan' ) );
    }

    /**
     * Load variations
     *
     * @return void
     */
    public function load_variations() {
        ob_start();

        check_ajax_referer( 'load-variations', 'security' );

        // Check permissions again and make sure we have what we need
        if ( ! current_user_can( 'dokandar' ) || empty( $_POST['product_id'] ) || empty( $_POST['attributes'] ) ) {
            die( -1 );
        }

        global $post;

        $product_id = absint( $_POST['product_id'] );
        $post       = get_post( $product_id ); // Set $post global so its available like within the admin screens
        $per_page   = ! empty( $_POST['per_page'] ) ? absint( $_POST['per_page'] ) : 10;
        $page       = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;

        // Get attributes
        $attributes        = [];
        $posted_attributes = json_decode( wp_unslash( $_POST['attributes'] ) );

        foreach ( $posted_attributes as $key => $value ) {
            $attributes[ $key ] = array_map( 'wc_clean', (array) $value );
        }

        // Get tax classes
        $tax_classes           = WC_Tax::get_tax_classes();
        $tax_class_options     = [];
        $tax_class_options[''] = __( 'Standard', 'dokan' );

        if ( ! empty( $tax_classes ) ) {
            foreach ( $tax_classes as $class ) {
                $tax_class_options[ sanitize_title( $class ) ] = esc_attr( $class );
            }
        }

        // Set backorder options
        $backorder_options = [
            'no'     => __( 'Do not allow', 'dokan' ),
            'notify' => __( 'Allow, but notify customer', 'dokan' ),
            'yes'    => __( 'Allow', 'dokan' ),
        ];

        // set stock status options
        $stock_status_options = [
            'instock'     => __( 'In stock', 'dokan' ),
            'outofstock'  => __( 'Out of stock', 'dokan' ),
            'onbackorder' => __( 'On backorder', 'dokan' ),
        ];

        $parent_data = [
            'id'                   => $product_id,
            'attributes'           => $attributes,
            'tax_class_options'    => $tax_class_options,
            'sku'                  => get_post_meta( $product_id, '_sku', true ),
            'weight'               => wc_format_localized_decimal( get_post_meta( $product_id, '_weight', true ) ),
            'length'               => wc_format_localized_decimal( get_post_meta( $product_id, '_length', true ) ),
            'width'                => wc_format_localized_decimal( get_post_meta( $product_id, '_width', true ) ),
            'height'               => wc_format_localized_decimal( get_post_meta( $product_id, '_height', true ) ),
            'tax_class'            => get_post_meta( $product_id, '_tax_class', true ),
            'backorder_options'    => $backorder_options,
            'stock_status_options' => $stock_status_options,
        ];

        if ( ! $parent_data['weight'] ) {
            $parent_data['weight'] = wc_format_localized_decimal( 0 );
        }

        if ( ! $parent_data['length'] ) {
            $parent_data['length'] = wc_format_localized_decimal( 0 );
        }

        if ( ! $parent_data['width'] ) {
            $parent_data['width'] = wc_format_localized_decimal( 0 );
        }

        if ( ! $parent_data['height'] ) {
            $parent_data['height'] = wc_format_localized_decimal( 0 );
        }

        // Get variations
        $args = apply_filters(
            'woocommerce_ajax_admin_get_variations_args', [
				'post_type'      => 'product_variation',
				'post_status'    => [ 'private', 'publish' ],
				'posts_per_page' => $per_page,
				'paged'          => $page,
				'orderby'        => [
					'menu_order' => 'ASC',
					'ID'         => 'ASC',
				],
				'post_parent'    => $product_id,
			], $product_id
        );

        $variations = get_posts( $args );
        $loop       = 0;

        if ( $variations ) {
            foreach ( $variations as $variation ) {
                $variation_id     = absint( $variation->ID );
                $variation_meta   = get_post_meta( $variation_id );
                $variation_data   = [];
                $shipping_classes = get_the_terms( $variation_id, 'product_shipping_class' );
                $variation_fields = [
                    '_sku'                   => '',
                    '_stock'                 => '',
                    '_regular_price'         => '',
                    '_sale_price'            => '',
                    '_weight'                => '',
                    '_length'                => '',
                    '_width'                 => '',
                    '_height'                => '',
                    '_download_limit'        => '',
                    '_download_expiry'       => '',
                    '_downloadable_files'    => '',
                    '_downloadable'          => '',
                    '_virtual'               => '',
                    '_thumbnail_id'          => '',
                    '_sale_price_dates_from' => '',
                    '_sale_price_dates_to'   => '',
                    '_manage_stock'          => '',
                    '_stock_status'          => '',
                    '_backorders'            => null,
                    '_tax_class'             => null,
                    '_variation_description' => '',
                    '_low_stock_amount'      => '',
                ];

                foreach ( $variation_fields as $field => $value ) {
                    $variation_data[ $field ] = isset( $variation_meta[ $field ][0] ) ? maybe_unserialize( $variation_meta[ $field ][0] ) : $value;
                }

                // Add the variation attributes
                $variation_data = array_merge( $variation_data, wc_get_product_variation_attributes( $variation_id ) );

                // Formatting
                $variation_data['_regular_price']    = wc_format_localized_price( $variation_data['_regular_price'] );
                $variation_data['_sale_price']       = wc_format_localized_price( $variation_data['_sale_price'] );
                $variation_data['_weight']           = wc_format_localized_decimal( $variation_data['_weight'] );
                $variation_data['_length']           = wc_format_localized_decimal( $variation_data['_length'] );
                $variation_data['_width']            = wc_format_localized_decimal( $variation_data['_width'] );
                $variation_data['_height']           = wc_format_localized_decimal( $variation_data['_height'] );
                $variation_data['_thumbnail_id']     = absint( $variation_data['_thumbnail_id'] );
                $variation_data['image']             = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url( $variation_data['_thumbnail_id'] ) : '';
                $variation_data['shipping_class']    = $shipping_classes && ! is_wp_error( $shipping_classes ) ? current( $shipping_classes )->term_id : '';
                $variation_data['menu_order']        = $variation->menu_order;
                $variation_data['_stock']            = '' === $variation_data['_stock'] ? '' : wc_stock_amount( $variation_data['_stock'] );
                $variation_data['_low_stock_amount'] = '' === $variation_data['_low_stock_amount'] ? '' : wc_format_decimal( $variation_data['_low_stock_amount'] );

                dokan_get_template_part(
                    'products/edit/html-product-variation', '', [
                        'pro'            => true,
                        'loop'           => $loop,
                        'variation_id'   => $variation_id,
                        'parent_data'    => $parent_data,
                        'variation_data' => $variation_data,
                        'variation'      => $variation,
                    ]
                );

                ++$loop;
            }
        }

        die();
    }

    /**
     * Save variations via AJAX.
     */
    public static function save_variations() {
        // Checking nonce and security.
        check_ajax_referer( 'save-variations', 'security' );

        // Check permissions again and make sure we have what we need
        if ( ! current_user_can( 'dokandar' ) || empty( $_POST ) || empty( $_POST['product_id'] ) ) {
            die( -1 );
        }

        ob_start();

        $product_id   = absint( $_POST['product_id'] );
        $product_type = empty( $_POST['product_type'] ) ? 'simple' : sanitize_title( stripslashes( $_POST['product_type'] ) );

        $product_type_terms = wp_get_object_terms( $product_id, 'product_type' );

        // If the product type hasn't been set or it has changed, update it before saving variations
        if ( empty( $product_type_terms ) || $product_type !== sanitize_title( current( $product_type_terms )->name ) ) {
            wp_set_object_terms( $product_id, $product_type, 'product_type' );
        }

        WC_Meta_Box_Product_Data::save_variations( $product_id, get_post( $product_id ) );

        do_action( 'dokan_ajax_save_product_variations', $product_id );

        // Clear cache/transients
        wc_delete_product_transients( $product_id );
        die();
    }


    /**
     * Bulk action - Toggle Enabled.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_toggle_enabled( $variations, $data ) {
        global $wpdb;

        foreach ( $variations as $variation_id ) {
            $post_status = get_post_status( $variation_id );
            $new_status  = 'private' === $post_status ? 'publish' : 'private';
            $wpdb->update( $wpdb->posts, [ 'post_status' => $new_status ], [ 'ID' => $variation_id ] );
        }
    }

    /**
     * Bulk action - Toggle Downloadable Checkbox.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_toggle_downloadable( $variations, $data ) {
        foreach ( $variations as $variation_id ) {
            $_downloadable   = get_post_meta( $variation_id, '_downloadable', true );
            $is_downloadable = 'no' === $_downloadable ? 'yes' : 'no';
            update_post_meta( $variation_id, '_downloadable', $is_downloadable );
        }
    }

    /**
     * Bulk action - Toggle Virtual Checkbox.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_toggle_virtual( $variations, $data ) {
        foreach ( $variations as $variation_id ) {
            $_virtual   = get_post_meta( $variation_id, '_virtual', true );
            $is_virtual = 'no' === $_virtual ? 'yes' : 'no';
            update_post_meta( $variation_id, '_virtual', $is_virtual );
        }
    }

    /**
     * Bulk action - Toggle Manage Stock Checkbox.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_toggle_manage_stock( $variations, $data ) {
        foreach ( $variations as $variation_id ) {
            $_manage_stock   = get_post_meta( $variation_id, '_manage_stock', true );
            $is_manage_stock = 'no' === $_manage_stock || '' === $_manage_stock ? 'yes' : 'no';
            update_post_meta( $variation_id, '_manage_stock', $is_manage_stock );
        }
    }

    /**
     * Bulk action - Set Regular Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_regular_price( $variations, $data ) {
        if ( ! isset( $data['value'] ) ) {
            return;
        }

        foreach ( $variations as $variation_id ) {
            // Price fields
            $regular_price = wc_clean( $data['value'] );
            $sale_price    = get_post_meta( $variation_id, '_sale_price', true );

            // Date fields
            $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true );
            $date_to   = get_post_meta( $variation_id, '_sale_price_dates_to', true );
            $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : '';
            $date_to   = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : '';

            dokan_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to );
        }
    }

    /**
     * Bulk action - Set Sale Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_sale_price( $variations, $data ) {
        if ( ! isset( $data['value'] ) ) {
            return;
        }

        foreach ( $variations as $variation_id ) {
            // Price fields
            $regular_price = get_post_meta( $variation_id, '_regular_price', true );
            $sale_price    = wc_clean( $data['value'] );

            // Date fields
            $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true );
            $date_to   = get_post_meta( $variation_id, '_sale_price_dates_to', true );
            $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : '';
            $date_to   = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : '';

            dokan_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to );
        }
    }

    /**
     * Bulk action - Set Stock.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_stock( $variations, $data ) {
        if ( ! isset( $data['value'] ) ) {
            return;
        }

        $value = wc_clean( $data['value'] );

        foreach ( $variations as $variation_id ) {
            if ( 'yes' === get_post_meta( $variation_id, '_manage_stock', true ) ) {
                wc_update_product_stock( $variation_id, wc_stock_amount( $value ) );
            } else {
                delete_post_meta( $variation_id, '_stock' );
            }
        }
    }

    /**
     * Bulk action - Set Weight.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_weight( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_weight', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Length.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_length( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_length', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Width.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_width( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_width', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Height.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_height( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_height', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Download Limit.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_download_limit( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_download_limit', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Download Expiry.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_download_expiry( $variations, $data ) {
        self::variation_bulk_set_meta( $variations, '_download_expiry', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Delete all.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_delete_all( $variations, $data ) {
        if ( isset( $data['allowed'] ) && 'true' === $data['allowed'] ) {
            foreach ( $variations as $variation_id ) {
                wp_delete_post( $variation_id );
            }
        }
    }

    /**
     * Bulk action - Sale Schedule.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_sale_schedule( $variations, $data ) {
        if ( ! isset( $data['date_from'] ) && ! isset( $data['date_to'] ) ) {
            return;
        }

        foreach ( $variations as $variation_id ) {
            // Price fields
            $regular_price = get_post_meta( $variation_id, '_regular_price', true );
            $sale_price    = get_post_meta( $variation_id, '_sale_price', true );

            // Date fields
            $date_from = get_post_meta( $variation_id, '_sale_price_dates_from', true );
            $date_to   = get_post_meta( $variation_id, '_sale_price_dates_to', true );

            if ( 'false' === $data['date_from'] ) {
                $date_from = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : '';
            } else {
                $date_from = $data['date_from'];
            }

            if ( 'false' === $data['date_to'] ) {
                $date_to = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : '';
            } else {
                $date_to = $data['date_to'];
            }

            dokan_save_product_price( $variation_id, $regular_price, $sale_price, $date_from, $date_to );
        }
    }

    /**
     * Bulk action - Increase Regular Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_regular_price_increase( $variations, $data ) {
        self::variation_bulk_adjust_price( $variations, '_regular_price', '+', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Decrease Regular Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_regular_price_decrease( $variations, $data ) {
        self::variation_bulk_adjust_price( $variations, '_regular_price', '-', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Increase Sale Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_sale_price_increase( $variations, $data ) {
        self::variation_bulk_adjust_price( $variations, '_sale_price', '+', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Decrease Sale Prices.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array $variations
     * @param array $data
     */
    private static function variation_bulk_action_variable_sale_price_decrease( $variations, $data ) {
        self::variation_bulk_adjust_price( $variations, '_sale_price', '-', wc_clean( $data['value'] ) );
    }

    /**
     * Bulk action - Set Price.
     *
     * @since   2.6
     *
     * @used-by bulk_edit_variations
     *
     * @param array  $variations
     * @param string $operator + or -
     * @param string $field    price being adjusted
     * @param string $value    Price or Percent
     */
    private static function variation_bulk_adjust_price( $variations, $field, $operator, $value ) {
        foreach ( $variations as $variation_id ) {
            // Get existing data
            $_regular_price = get_post_meta( $variation_id, '_regular_price', true );
            $_sale_price    = get_post_meta( $variation_id, '_sale_price', true );
            $date_from      = get_post_meta( $variation_id, '_sale_price_dates_from', true );
            $date_to        = get_post_meta( $variation_id, '_sale_price_dates_to', true );
            $date_from      = ! empty( $date_from ) ? date( 'Y-m-d', $date_from ) : '';
            $date_to        = ! empty( $date_to ) ? date( 'Y-m-d', $date_to ) : '';

            if ( '%' === substr( $value, -1 ) ) {
                $percent = wc_format_decimal( substr( $value, 0, -1 ) );
                $$field  += ( ( $$field / 100 ) * $percent ) * "{$operator}1";
            } else {
                $$field += $value * "{$operator}1";
            }
            dokan_save_product_price( $variation_id, $_regular_price, $_sale_price, $date_from, $date_to );
        }
    }

    /**
     * Bulk action - Set Meta.
     *
     * @since 2.6
     *
     * @param array  $variations
     * @param string $field
     * @param string $value
     */
    private static function variation_bulk_set_meta( $variations, $field, $value ) {
        foreach ( $variations as $variation_id ) {
            update_post_meta( $variation_id, $field, $value );
        }
    }

    public static function bulk_edit_variations() {
        ob_start();

        check_ajax_referer( 'bulk-edit-variations', 'security' );

        // Check permissions again and make sure we have what we need
        if ( ! current_user_can( 'dokandar' ) || empty( $_POST['product_id'] ) || empty( $_POST['bulk_action'] ) ) {
            die( -1 );
        }

        $product_id  = absint( $_POST['product_id'] );
        $bulk_action = wc_clean( $_POST['bulk_action'] );
        $data        = ! empty( $_POST['data'] ) ? array_map( 'wc_clean', $_POST['data'] ) : [];
        $variations  = [];

        if ( apply_filters( 'dokan_bulk_edit_variations_need_children', true ) ) {
            $variations = get_posts(
                [
                    'post_parent'    => $product_id,
                    'posts_per_page' => -1,
                    'post_type'      => 'product_variation',
                    'fields'         => 'ids',
                    'post_status'    => [ 'publish', 'private' ],
                ]
            );
        }

        if ( method_exists( __CLASS__, "variation_bulk_action_$bulk_action" ) ) {
            call_user_func( [ __CLASS__, "variation_bulk_action_$bulk_action" ], $variations, $data );
        } else {
            do_action( 'dokan_bulk_edit_variations_default', $bulk_action, $data, $product_id, $variations );
        }

        do_action( 'dokan_bulk_edit_variations', $bulk_action, $data, $product_id, $variations );

        // Sync and update transients
        WC_Product_Variable::sync( $product_id );
        wc_delete_product_transients( $product_id );
        die();
    }

    /**
     * Delete variations via ajax function.
     */
    public static function remove_variations() {
        check_ajax_referer( 'delete-variations', 'security' );

        if ( ! current_user_can( 'dokandar' ) ) {
            die( -1 );
        }

        $variation_ids = (array) $_POST['variation_ids'];

        foreach ( $variation_ids as $variation_id ) {
            $variation = get_post( $variation_id );

            if ( $variation && 'product_variation' === $variation->post_type ) {
                wp_delete_post( $variation_id );
            }
        }

        die();
    }

    /**
     * Enable/disable seller selling capability from admin seller listing page
     *
     * @return type
     */
    public function toggle_seller_status() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( $_POST['nonce'], 'dokan-admin-nonce' ) ) {
            return;
        }

        $user_id = isset( $_POST['user_id'] ) ? intval( $_POST['user_id'] ) : 0;
        $status  = sanitize_text_field( $_POST['type'] );

        if ( in_array( $status, [ 'yes', 'no' ] ) ) {
            if ( 'yes' === $status ) {
                $user = dokan()->vendor->get( $user_id )->make_active();
            } else {
                $user = dokan()->vendor->get( $user_id )->make_inactive();
            }
        }

        wp_send_json_success( $user );
        exit;
    }

    /**
     * Load State via ajax for refund
     *
     * @since 2.4.11
     *
     * @return html Set of states
     */
    public function load_order_items() {
        check_ajax_referer( 'order-item', 'security' );

        if ( ! current_user_can( 'edit_shop_orders' ) ) {
            die( -1 );
        }

        // Return HTML items
        $order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0;
        $order    = wc_get_order( $order_id );
        $data     = $order->get_meta_data();

        dokan_get_template_part(
            'orders/views/html-order-items',
            '',
            [
                'pro'   => true,
                'data'  => $data,
                'order' => $order,
            ]
        );

        die();
    }

    /**
     * Load State via ajax for shipping
     *
     * @since 2.4
     *
     * @return html Set of states
     */
    public function load_state_by_country() {
        $country_id  = $_POST['country_id'];
        $country_obj = new WC_Countries();
        $states      = $country_obj->states;

        ob_start();
        if ( ! empty( $states[ $country_id ] ) ) {
            ?>
            <tr>
                <td>
                    <label for=""><?php _e( 'State', 'dokan' ); ?></label>
                    <select name="dps_state_to[<?php echo $country_id; ?>][]" class="dokan-form-control dps_state_selection" id="dps_state_selection">
                        <?php dokan_state_dropdown( $states[ $country_id ], '', true ); ?>
                    </select>
                </td>
                <td>
                    <label for=""><?php _e( 'Cost', 'dokan' ); ?></label>
                    <div class="dokan-input-group">
                        <span class="dokan-input-group-addon"><?php echo get_woocommerce_currency_symbol(); ?></span>
                        <input type="text" placeholder="0.00" class="dokan-form-control" name="dps_state_to_price[<?php echo $country_id; ?>][]">
                    </div>
                </td>
                <td width="15%">
                    <label for=""></label>
                    <div>
                        <a class="dps-add" href="#"><i class="fas fa-plus"></i></a>
                        <a class="dps-remove" href="#"><i class="fas fa-minus"></i></a>
                    </div>
                </td>
            </tr>
            <?php
            // }
        } else {
            ?>
            <tr>
                <td>
                    <label for=""><?php _e( 'State', 'dokan' ); ?></label>
                    <input type="text" name="dps_state_to[<?php echo $country_id; ?>][]" class="dokan-form-control dps_state_selection" placeholder="State name">
                </td>
                <td>
                    <label for=""><?php _e( 'Cost', 'dokan' ); ?></label>
                    <div class="dokan-input-group">
                        <span class="dokan-input-group-addon"><?php echo get_woocommerce_currency_symbol(); ?></span>
                        <input type="text" placeholder="0.00" class="dokan-form-control" name="dps_state_to_price[<?php echo $country_id; ?>][]">
                    </div>
                </td>
                <td width="15%">
                    <label for=""></label>
                    <div>
                        <a class="dps-add" href="#"><i class="fas fa-plus"></i></a>
                        <a class="dps-remove" href="#"><i class="fas fa-minus"></i></a>
                    </div>
                </td>
            </tr>
            <?php
        }

        $data = ob_get_clean();

        wp_send_json_success( $data );
    }

    /**
     * get state by shipping country
     *
     * @since 2.4
     *
     * @return json
     */
    public function get_state_by_shipping_country() {
        global $post;
        $dps_state_rates = get_user_meta( $_POST['author_id'], '_dps_state_rates', true );
        $country_obj     = new WC_Countries();
        $states          = $country_obj->states;

        $country = $_POST['country_id'];
        ob_start();
        ?>
        <?php
        if ( isset( $dps_state_rates[ $country ] ) && count( $dps_state_rates[ $country ] ) ) {
            ?>
            <label for="dokan-shipping-state" class="dokan-control-label"><?php _e( 'State', 'dokan' ); ?></label>
            <select name="dokan-shipping-state" class="dokan-shipping-state dokan-form-control" id="dokan-shipping-state">
                <option value=""><?php _e( '--Select State--', 'dokan' ); ?></option>
                <?php foreach ( $dps_state_rates[ $country ] as $state_code => $state_cost ) : ?>
                    <option value="<?php echo $state_code; ?>">
                        <?php
                        if ( $state_code === 'everywhere' ) {
                            _e( 'Other States', 'dokan' );
                        } elseif ( isset( $states[ $country ][ $state_code ] ) ) {
                            echo $states[ $country ][ $state_code ];
                        } else {
                            echo $state_code;
                        }
                        ?>
                    </option>
                <?php endforeach ?>
            </select>
            <?php
        }
        $content = ob_get_clean();

        wp_send_json_success( $content );
    }

    /**
     * calculate shipping rate in single product page
     *
     * @since 2.4
     *
     * @return json
     */
    public function get_calculated_shipping_cost() {
        global $post;

        if ( ! isset( $_POST['product_id'] ) || ! isset( $_POST['author_id'] ) ) {
            wp_send_json_error();
        }

        $product_id = absint( wp_unslash( $_POST['product_id'] ) );
        $author_id  = absint( wp_unslash( $_POST['author_id'] ) );

        $_overwrite_shipping = get_post_meta( $product_id, '_overwrite_shipping', true );

        $dps_country_rates = get_user_meta( $author_id, '_dps_country_rates', true );
        $dps_state_rates   = get_user_meta( $author_id, '_dps_state_rates', true );

        $store_shipping_type_price    = (float) get_user_meta( $author_id, '_dps_shipping_type_price', true );
        $additional_product_cost      = (float) get_post_meta( $product_id, '_additional_price', true );
        $base_shipping_type_price     = $store_shipping_type_price;
        $additional_qty_product_price = (float) wc_format_decimal( get_post_meta( $product_id, '_additional_qty', true ) );
        $dps_additional_qty           = (float) wc_format_decimal( get_user_meta( $author_id, '_dps_additional_qty', true ) );
        $additional_qty_price         = $dps_additional_qty;

        if ( $_overwrite_shipping === 'yes' ) {
            $base_shipping_type_price = $store_shipping_type_price + $additional_product_cost;
            $additional_qty_price     = $additional_qty_product_price ? $additional_qty_product_price : $dps_additional_qty;
        }

        if ( isset( $_POST['country_id'] ) || ! empty( $_POST['country_id'] ) ) {
            $country = $_POST['country_id'];
        } else {
            $country = '';
        }

        if ( isset( $_POST['quantity'] ) && $_POST['quantity'] > 0 ) {
            $quantity = $_POST['quantity'];
        } else {
            $quantity = 1;
        }

        $additional_quantity_cost = ( $quantity - 1 ) * $additional_qty_price;
        $flag                     = '';
        ob_start();
        if ( $country !== '' ) {
            if ( isset( $dps_state_rates[ $country ] ) && count( $dps_state_rates[ $country ] ) && empty( $_POST['state'] ) ) {
                esc_html_e( 'Please select a State from the dropdown', 'dokan' );
            } elseif ( ! isset( $dps_state_rates[ $country ] ) && empty( $_POST['state'] ) ) {
                echo __( 'Shipping Cost : ', 'dokan' ) . '<h4>' . wc_price( $dps_country_rates[ $country ] + $base_shipping_type_price + $additional_quantity_cost ) . '</h4>';
            } elseif ( isset( $_POST['state'] ) && ! empty( $_POST['state'] ) ) {
                $state = $_POST['state'];
                echo __( 'Shipping Cost : ', 'dokan' ) . '<strong>' . wc_price( $dps_state_rates[ $country ][ $state ] + $base_shipping_type_price + $additional_quantity_cost ) . '</strong>';
            }
        } else {
            esc_html_e( 'Please select a country from the dropdown', 'dokan' );
        }
        $content = ob_get_clean();

        wp_send_json_success( $content );
    }

    /**
     * Save attributes from edit product page
     *
     * @return void
     */
    public function save_attributes() {
        // Get post data
        parse_str( $_POST['data'], $data );
        $post_id = absint( $_POST['post_id'] );

        // Save Attributes
        $attributes = [];

        if ( isset( $data['attribute_names'] ) ) {
            $attribute_names  = array_map( 'stripslashes', $data['attribute_names'] );
            $attribute_values = isset( $data['attribute_values'] ) ? $data['attribute_values'] : [];

            if ( isset( $data['attribute_visibility'] ) ) {
                $attribute_visibility = $data['attribute_visibility'];
            }

            if ( isset( $data['attribute_variation'] ) ) {
                $attribute_variation = $data['attribute_variation'];
            }

            $attribute_is_taxonomy   = $data['attribute_is_taxonomy'];
            $attribute_position      = $data['attribute_position'];
            $attribute_names_max_key = max( array_keys( $attribute_names ) );

            for ( $i = 0; $i <= $attribute_names_max_key; $i++ ) {
                if ( empty( $attribute_names[ $i ] ) ) {
                    continue;
                }

                $is_visible   = isset( $attribute_visibility[ $i ] ) ? 1 : 0;
                $is_variation = isset( $attribute_variation[ $i ] ) ? 1 : 0;
                $is_taxonomy  = $attribute_is_taxonomy[ $i ] ? 1 : 0;

                if ( $is_taxonomy ) {
                    if ( isset( $attribute_values[ $i ] ) ) {

                        // Select based attributes - Format values (posted values are slugs)
                        if ( is_array( $attribute_values[ $i ] ) ) {
                            $values = $attribute_values[ $i ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
                            // Text based attributes - Posted values are term names, wp_set_object_terms wants ids or slugs.
                        } else {
                            $values     = [];
                            $raw_values = explode( WC_DELIMITER, $attribute_values[ $i ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
                            foreach ( $raw_values as $value ) {
                                $term = get_term_by( 'name', $value, $attribute_names[ $i ] );
                                if ( ! $term ) {
                                    $term = wp_insert_term( $value, $attribute_names[ $i ] );

                                    if ( $term && ! is_wp_error( $term ) ) {
                                        $values[] = $term['term_id'];
                                    }
                                } else {
                                    $values[] = $term->term_id;
                                }
                            }
                        }

                        // Remove empty items in the array
                        $values = array_filter( $values, 'strlen' );
                    } else {
                        $values = [];
                    }

                    $values = array_map( 'strval', $values );

                    // Update post terms
                    if ( taxonomy_exists( $attribute_names[ $i ] ) ) {
                        wp_set_object_terms( $post_id, $values, $attribute_names[ $i ] );
                    }

                    if ( ! empty( $values ) ) {
                        // Add attribute to array, but don't set values
                        $attributes[ sanitize_title( $attribute_names[ $i ] ) ] = [
                            'name'         => wc_clean( $attribute_names[ $i ] ),
                            'value'        => '',
                            'position'     => $attribute_position[ $i ],
                            'is_visible'   => $is_visible,
                            'is_variation' => $is_variation,
                            'is_taxonomy'  => $is_taxonomy,
                        ];
                    }
                } elseif ( isset( $attribute_values[ $i ] ) ) {

                    // Text based, possibly separated by pipes (WC_DELIMITER). Preserve line breaks in non-variation attributes.
                    $values = implode( ' ' . WC_DELIMITER . ' ', array_map( 'wc_clean', array_map( 'stripslashes', $attribute_values[ $i ] ) ) );

                    // Custom attribute - Add attribute to array and set the values
                    $attributes[ sanitize_title( $attribute_names[ $i ] ) ] = [
                        'name'         => wc_clean( $attribute_names[ $i ] ),
                        'value'        => $values,
                        'position'     => $attribute_position[ $i ],
                        'is_visible'   => $is_visible,
                        'is_variation' => $is_variation,
                        'is_taxonomy'  => $is_taxonomy,
                    ];
                }
            }
        }

        uasort( $attributes, 'wc_product_attribute_uasort_comparison' );
        update_post_meta( $post_id, '_product_attributes', $attributes );
        die();
    }

    /**
     * Delete variations via ajax function
     */
    public function remove_variation() {
        if ( ! current_user_can( 'dokandar' ) ) {
            die( -1 );
        }

        $variation_ids = (array) $_POST['variation_ids'];

        foreach ( $variation_ids as $variation_id ) {
            $variation = get_post( $variation_id );

            if ( $variation && 'product_variation' === $variation->post_type ) {
                wp_delete_post( $variation_id );
            }
        }

        die();
    }

    public function add_attr_predefined_attribute() {
        check_ajax_referer( 'dokan_reviews' );

        global $wc_product_attributes;

        $thepostid = 0;
        $taxonomy  = sanitize_text_field( $_POST['taxonomy'] );
        $i         = absint( $_POST['i'] );
        $attribute = [
            'name'         => $taxonomy,
            'value'        => '',
            'is_visible'   => apply_filters( 'dokan_attribute_default_visibility', 1 ),
            'is_variation' => apply_filters( 'dokan_attribute_default_is_variation', 0 ),
            'is_taxonomy'  => $taxonomy ? 1 : 0,
        ];

        if ( $taxonomy ) {
            $attribute_taxonomy = $wc_product_attributes[ $taxonomy ];
            $metabox_class[]    = 'taxonomy';
            $metabox_class[]    = $taxonomy;
            $attribute_label    = wc_attribute_label( $taxonomy );
        } else {
            $attribute_label    = '';
            $attribute_taxonomy = [];
            $metabox_class[]    = '';
        }
        ob_start();
        dokan_get_template_part(
            'products/edit/html-product-attribute', '', [
                'pro'                => true,
                'i'                  => $i,
                'thepostid'          => $thepostid,
                'taxonomy'           => $taxonomy,
                'attribute_taxonomy' => $attribute_taxonomy,
                'attribute_label'    => $attribute_label,
                'attribute'          => $attribute,
                'metabox_class'      => $metabox_class,
                'position'           => 0,
            ]
        );
        $content = ob_get_clean();
        wp_send_json_success( $content );
    }

    /**
     * Add new attribute from predifined attribute
     *
     * @since 2.5
     *
     * @return void
     */
    public function add_new_attribute() {
        check_ajax_referer( 'dokan_reviews' );

        if ( ! current_user_can( 'dokandar' ) ) {
            die( -1 );
        }

        $taxonomy = esc_attr( $_POST['taxonomy'] );
        $term     = wc_clean( $_POST['term'] );

        if ( taxonomy_exists( $taxonomy ) ) {
            $result = wp_insert_term( $term, $taxonomy );

            if ( is_wp_error( $result ) ) {
                wp_send_json(
                    [
                        'error' => $result->get_error_message(),
                    ]
                );
            } else {
                $term = get_term_by( 'id', $result['term_id'], $taxonomy );
                wp_send_json(
                    [
                        'term_id' => $term->term_id,
                        'name'    => $term->name,
                        'slug'    => $term->slug,
                    ]
                );
            }
        }
    }

    /**
     * Add Predefined Attribute
     *
     * @since 2.3
     *
     * @return json success|$content (array)
     */
    public function add_predefined_attribute() {
        $attr_name               = $_POST['name'];
        $single                  = ( isset( $_POST['from'] ) && $_POST['from'] === 'popup' ) ? 'single-' : '';
        $remove_btn              = ( isset( $_POST['from'] ) && $_POST['from'] === 'popup' ) ? 'single_' : '';
        $attribute_taxonomy_name = wc_attribute_taxonomy_name( $attr_name );
        $tax                     = get_taxonomy( $attribute_taxonomy_name );
        $options                 = get_terms( $attribute_taxonomy_name, 'orderby=name&hide_empty=0' );
        $att_val                 = wp_list_pluck( $options, 'name' );
        ob_start();
        ?>
        <tr class="dokan-<?php echo $single; ?>attribute-options">
            <td width="20%">
                <input type="text" disabled="disabled" value="<?php echo $attr_name; ?>" class="dokan-form-control dokan-<?php echo $single; ?>attribute-option-name-label"
                       data-attribute_name="<?php echo wc_sanitize_taxonomy_name( str_replace( 'pa_', '', $attribute_taxonomy_name ) ); ?>">
                <input type="hidden" name="attribute_names[]" value="<?php echo esc_attr( $attribute_taxonomy_name ); ?>" class="dokan-<?php echo $single; ?>attribute-option-name">
                <input type="hidden" name="attribute_is_taxonomy[]" value="1">
            </td>
            <td colspan="3"><input type="text" name="attribute_values[]" value="<?php echo implode( ',', $att_val ); ?>" data-preset_attr="<?php echo implode( ',', $att_val ); ?>"
                                   class="dokan-form-control dokan-<?php echo $single; ?>attribute-option-values"></td>
            <td>
                <button title="<?php _e( 'Clear All', 'dokan' ); ?>" class="dokan-btn dokan-btn-theme clear_attributes"><?php _e( 'Clear', 'dokan' ); ?></button>
                <button title="Delete" class="dokan-btn dokan-btn-theme remove_<?php echo $remove_btn; ?>attribute"><i class="far fa-trash-alt"></i></button>
            </td>
        </tr>
        <?php
        $content = ob_get_clean();
        wp_send_json_success( $content );
    }

    /**
     * Add variation via ajax function
     *
     * @since 2.3
     *
     * @return void
     */
    public static function add_variation() {
        check_ajax_referer( 'add-variation', 'security' );

        if ( ! current_user_can( 'dokandar' ) ) {
            die( -1 );
        }

        global $post;

        $post_id = intval( $_POST['post_id'] );
        $post    = get_post( $post_id ); // Set $post global so its available like within the admin screens
        $loop    = intval( $_POST['loop'] );

        $variation = [
            'post_title'   => 'Product #' . $post_id . ' Variation',
            'post_content' => '',
            'post_status'  => 'publish',
            'post_author'  => dokan_get_current_user_id(),
            'post_parent'  => $post_id,
            'post_type'    => 'product_variation',
            'menu_order'   => -1,
        ];

        $variation_id = wp_insert_post( $variation );

        do_action( 'dokan_create_product_variation', $variation_id );

        if ( $variation_id ) {
            $variation        = get_post( $variation_id );
            $variation_meta   = get_post_meta( $variation_id );
            $variation_data   = [];
            $shipping_classes = get_the_terms( $variation_id, 'product_shipping_class' );
            $variation_fields = [
                '_sku'                   => '',
                '_stock'                 => '',
                '_regular_price'         => '',
                '_sale_price'            => '',
                '_weight'                => '',
                '_length'                => '',
                '_width'                 => '',
                '_height'                => '',
                '_download_limit'        => '',
                '_download_expiry'       => '',
                '_downloadable_files'    => '',
                '_downloadable'          => '',
                '_virtual'               => '',
                '_thumbnail_id'          => '',
                '_sale_price_dates_from' => '',
                '_sale_price_dates_to'   => '',
                '_manage_stock'          => '',
                '_stock_status'          => '',
                '_backorders'            => null,
                '_tax_class'             => null,
                '_variation_description' => '',
            ];

            foreach ( $variation_fields as $field => $value ) {
                $variation_data[ $field ] = isset( $variation_meta[ $field ][0] ) ? maybe_unserialize( $variation_meta[ $field ][0] ) : $value;
            }

            // Add the variation attributes
            $variation_data = array_merge( $variation_data, wc_get_product_variation_attributes( $variation_id ) );

            // Formatting
            $variation_data['_regular_price']    = wc_format_localized_price( $variation_data['_regular_price'] );
            $variation_data['_sale_price']       = wc_format_localized_price( $variation_data['_sale_price'] );
            $variation_data['_weight']           = wc_format_localized_decimal( $variation_data['_weight'] );
            $variation_data['_length']           = wc_format_localized_decimal( $variation_data['_length'] );
            $variation_data['_width']            = wc_format_localized_decimal( $variation_data['_width'] );
            $variation_data['_height']           = wc_format_localized_decimal( $variation_data['_height'] );
            $variation_data['_thumbnail_id']     = absint( $variation_data['_thumbnail_id'] );
            $variation_data['image']             = $variation_data['_thumbnail_id'] ? wp_get_attachment_thumb_url( $variation_data['_thumbnail_id'] ) : '';
            $variation_data['shipping_class']    = $shipping_classes && ! is_wp_error( $shipping_classes ) ? current( $shipping_classes )->term_id : '';
            $variation_data['menu_order']        = $variation->menu_order;
            $variation_data['_stock']            = wc_stock_amount( $variation_data['_stock'] );
            $variation_data['_low_stock_amount'] = wc_format_localized_decimal( $variation_data['_low_stock_amount'] );

            // Get tax classes
            $tax_classes           = WC_Tax::get_tax_classes();
            $tax_class_options     = [];
            $tax_class_options[''] = __( 'Standard', 'dokan' );

            if ( ! empty( $tax_classes ) ) {
                foreach ( $tax_classes as $class ) {
                    $tax_class_options[ sanitize_title( $class ) ] = esc_attr( $class );
                }
            }

            // Set backorder options
            $backorder_options = [
                'no'     => __( 'Do not allow', 'dokan' ),
                'notify' => __( 'Allow, but notify customer', 'dokan' ),
                'yes'    => __( 'Allow', 'dokan' ),
            ];

            // set stock status options
            $stock_status_options = [
                'instock'     => __( 'In stock', 'dokan' ),
                'outofstock'  => __( 'Out of stock', 'dokan' ),
                'onbackorder' => __( 'On backorder', 'dokan' ),
            ];

            // Get attributes
            $attributes = (array) maybe_unserialize( get_post_meta( $post_id, '_product_attributes', true ) );

            $parent_data = [
                'id'                   => $post_id,
                'attributes'           => $attributes,
                'tax_class_options'    => $tax_class_options,
                'sku'                  => get_post_meta( $post_id, '_sku', true ),
                'weight'               => wc_format_localized_decimal( get_post_meta( $post_id, '_weight', true ) ),
                'length'               => wc_format_localized_decimal( get_post_meta( $post_id, '_length', true ) ),
                'width'                => wc_format_localized_decimal( get_post_meta( $post_id, '_width', true ) ),
                'height'               => wc_format_localized_decimal( get_post_meta( $post_id, '_height', true ) ),
                'tax_class'            => get_post_meta( $post_id, '_tax_class', true ),
                'backorder_options'    => $backorder_options,
                'stock_status_options' => $stock_status_options,
            ];

            if ( ! $parent_data['weight'] ) {
                $parent_data['weight'] = wc_format_localized_decimal( 0 );
            }

            if ( ! $parent_data['length'] ) {
                $parent_data['length'] = wc_format_localized_decimal( 0 );
            }

            if ( ! $parent_data['width'] ) {
                $parent_data['width'] = wc_format_localized_decimal( 0 );
            }

            if ( ! $parent_data['height'] ) {
                $parent_data['height'] = wc_format_localized_decimal( 0 );
            }

            dokan_get_template_part(
                'products/edit/html-product-variation', '', [
                    'pro'            => true,
                    'loop'           => $loop,
                    'variation_id'   => $variation_id,
                    'parent_data'    => $parent_data,
                    'variation_data' => $variation_data,
                    'variation'      => $variation,
                ]
            );
        }

        die();
    }

    /**
     * Link all variations via ajax function
     *
     * @since 2.3
     *
     * @return void
     */
    public function link_all_variations() {
        if ( ! defined( 'WC_MAX_LINKED_VARIATIONS' ) ) {
            define( 'WC_MAX_LINKED_VARIATIONS', 49 );
        }

        check_ajax_referer( 'link-variations', 'security' );

        wc_set_time_limit( 0 );

        $post_id = intval( $_POST['post_id'] );

        if ( ! $post_id ) {
            die();
        }

        $variations = [];

        $_product = wc_get_product( $post_id );

        // Put variation attributes into an array
        foreach ( $_product->get_attributes() as $attribute ) {
            if ( ! $attribute['is_variation'] ) {
                continue;
            }

            $attribute_field_name = 'attribute_' . sanitize_title( $attribute['name'] );

            if ( $attribute['is_taxonomy'] ) {
                $options = wc_get_product_terms( $post_id, $attribute['name'], [ 'fields' => 'slugs' ] );
            } else {
                $options = explode( WC_DELIMITER, $attribute['value'] );
            }

            $options = array_map( 'trim', $options );

            $variations[ $attribute_field_name ] = $options;
        }

        // Quit out if none were found
        if ( sizeof( $variations ) === 0 ) {
            die();
        }

        // Get existing variations so we don't create duplicates
        $available_variations = [];

        foreach ( $_product->get_children() as $child_id ) {
            $child = wc_get_product( $child_id );

            if ( ! empty( $child->variation_id ) ) {
                $available_variations[] = $child->get_variation_attributes();
            }
        }

        // Created posts will all have the following data
        $variation_post_data = [
            'post_title'   => 'Product #' . $post_id . ' Variation',
            'post_content' => '',
            'post_status'  => 'publish',
            'post_author'  => dokan_get_current_user_id(),
            'post_parent'  => $post_id,
            'post_type'    => 'product_variation',
        ];

        $variation_ids       = [];
        $added               = 0;
        $possible_variations = wc_array_cartesian( $variations );

        foreach ( $possible_variations as $variation ) {

            // Check if variation already exists
            if ( in_array( $variation, $available_variations ) ) {
                continue;
            }

            $variation_id = wp_insert_post( $variation_post_data );

            $variation_ids[] = $variation_id;

            foreach ( $variation as $key => $value ) {
                update_post_meta( $variation_id, $key, $value );
            }

            // Save stock status
            update_post_meta( $variation_id, '_stock_status', 'instock' );

            ++$added;

            do_action( 'dokan_product_variation_linked', $variation_id );

            if ( $added > WC_MAX_LINKED_VARIATIONS ) {
                break;
            }
        }

        delete_transient( 'wc_product_children_' . $post_id );

        echo $added;

        die();
    }

    /**
     * Dokan Pre Define Attribute Render
     *
     * @since 2.0
     *
     * @return void
     */
    public function dokan_pre_define_attribute() {
        $attribute               = $_POST;
        $attribute_taxonomy_name = wc_attribute_taxonomy_name( $attribute['name'] );
        $tax                     = get_taxonomy( $attribute_taxonomy_name );
        $options                 = get_terms( $attribute_taxonomy_name, 'orderby=name&hide_empty=0' );
        $i                       = $_POST['row'];
        ob_start();
        ?>
        <div class="inputs-box woocommerce_attribute" data-count="<?php echo $i; ?>">
            <div class="box-header">
                <input type="text" disabled="disabled" value="<?php echo $attribute['name']; ?>">
                <input type="hidden" name="attribute_names[<?php echo $i; ?>]" value="<?php echo esc_attr( $attribute_taxonomy_name ); ?>">
                <input type="hidden" name="attribute_is_taxonomy[<?php echo $i; ?>]" value="1">
                <input type="hidden" name="attribute_position[<?php echo $i; ?>]" class="attribute_position" value="<?php echo esc_attr( $i ); ?>" />
                <span class="actions">
                    <button class="row-remove btn pull-right btn-danger btn-sm"><?php _e( 'Remove', 'dokan' ); ?></button>
                </span>
            </div>
            <div class="box-inside clearfix">
                <div class="attribute-config">
                    <ul class="list-unstyled ">
                        <li>
                            <label class="checkbox-inline">
                                <input type="checkbox" class="checkbox"
                                    <?php
                                    $tax = '';
                                    checked( apply_filters( 'default_attribute_visibility', false, $tax ), true );
                                    ?>
                                       name="attribute_visibility[<?php echo $i; ?>]" value="1" /> <?php _e( 'Visible on the product page', 'dokan' ); ?>
                            </label>
                        </li>
                        <li class="enable_variation" <?php echo ( $_POST['type'] === 'simple' ) ? 'style="display:none;"' : ''; ?>>
                            <label class="checkbox-inline">
                                <input type="checkbox" class="checkbox"
                                    <?php
                                    checked( apply_filters( 'default_attribute_variation', false, $tax ), true );
                                    ?>
                                       name="attribute_variation[<?php echo $i; ?>]" value="1" /> <?php _e( 'Used for variations', 'dokan' ); ?></label>
                        </li>
                    </ul>
                </div>
                <div class="attribute-options">
                    <ul class="option-couplet list-unstyled ">
                        <?php
                        if ( $options ) {
                            foreach ( $options as $count => $option ) {
                                ?>
                                <li>
                                    <input type="text" class="option" placeholder="<?php _e( 'Option...', 'dokan' ); ?>" name="attribute_values[<?php echo $i; ?>][<?php echo $count; ?>]" value="<?php echo esc_attr( $option->name ); ?>">
                                    <span class="item-action actions">
                                        <a href="#" class="row-add">+</a>
                                        <a href="#" class="row-remove">-</a>
                                    </span>
                                </li>
                                <?php
                            }
                        } else {
                            ?>
                            <li>
                                <input type="text" class="option" name="attribute_values[<?php echo $i; ?>][0]" placeholder="<?php _e( 'Option...', 'dokan' ); ?>">
                                <span class="item-action actions">
                                    <a href="#" class="row-add">+</a>
                                    <a href="#" class="row-remove">-</a>
                                </span>
                            </li>
                            <?php
                        }
                        ?>
                    </ul>
                </div> <!-- .attribute-options -->
            </div> <!-- .box-inside -->
        </div> <!-- .input-box -->
        <?php
        $response = ob_get_clean();

        return wp_send_json_success( $response );
    }

    /**
     * Save the action of user closed the progressbar
     *
     * @since 3.5.2
     *
     * @return void
     */
    public function user_closed_progressbar() {
        if ( ! wp_verify_nonce( $_POST['nonce'], 'dokan_user_closed_progressbar' ) ) {
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }

        $profile_settings = get_user_meta( get_current_user_id(), 'dokan_profile_settings', true );
        $progress         = isset( $profile_settings['profile_completion']['progress'] ) ? $profile_settings['profile_completion']['progress'] : 0;

        if ( $progress >= 100 ) {
            $profile_settings['profile_completion']['closed_by_user'] = true;
            update_user_meta( get_current_user_id(), 'dokan_profile_settings', $profile_settings );
            wp_send_json_success( __( 'Successfully closed', 'dokan' ) );
        } else {
            wp_send_json_error( __( 'Profile is not 100% complete', 'dokan' ) );
        }
    }
    /**
     * Check if the progressbar is closed by user
     *
     * @since 4.0.6
     *
     * @return void
     */
    public function dismiss_profile_progressbar() {
        if ( ! wp_verify_nonce( $_POST['nonce'], 'dokan_user_closed_progressbar' ) ) { // phpcs:ignore
            wp_send_json_error( __( 'Invalid nonce', 'dokan' ) );
        }
        // set transient for 1 day based on user id
        $transient_key = 'dokan_dashboard_closed_progressbar_' . get_current_user_id();
        set_transient( $transient_key, true, DAY_IN_SECONDS );
        wp_send_json_success( __( 'Successfully closed', 'dokan' ) );
    }
}