hooked) { return; } $this->hooked = true; if ( ! defined( 'AIEO_RUNTIME_OWNS_DISPLAY' ) ) { if ( ! class_exists( 'AIEO_Side_Cart_Display' ) && file_exists( AIEO_DIR . 'includes/class-aieo-side-cart-display.php' ) ) { require_once AIEO_DIR . 'includes/class-aieo-side-cart-display.php'; } if ( class_exists( 'AIEO_Side_Cart_Display' ) ) { AIEO_Side_Cart_Display::instance()->register_display(); } } if ( ! defined( 'AIEO_RUNTIME_OWNS_AJAX' ) ) { $actions = array('get', 'update_qty', 'remove', 'apply_coupon', 'quick_view_add'); foreach ($actions as $a) { add_action('wp_ajax_aieo_side_cart_' . $a, array($this, 'ajax_' . $a)); add_action('wp_ajax_nopriv_aieo_side_cart_' . $a, array($this, 'ajax_' . $a)); } } if ( ! defined( 'AIEO_RUNTIME_OWNS_AJAX' ) ) { add_filter('woocommerce_add_to_cart_fragments', array($this, 'add_cart_fragments')); } if ( ! defined( 'AIEO_RUNTIME_OWNS_AJAX' ) ) { $clear_hooks = array( 'woocommerce_add_to_cart', 'woocommerce_cart_item_removed', 'woocommerce_cart_item_restored', 'woocommerce_applied_coupon', 'woocommerce_removed_coupon', 'woocommerce_cart_emptied', ); foreach ($clear_hooks as $h) { add_action($h, array($this, 'clear_session_cache')); } } } public function render_subtab() { $settings = AIEO_DMM_Settings::get_all(); $this->render_template('side-cart.php', array('settings' => $settings)); } private function cart_row_unit_price_html($product) { if (!$product || !is_a($product, 'WC_Product')) { return ''; } if ($product->is_on_sale() && function_exists('wc_format_sale_price')) { $regular = $product->get_regular_price(); $sale = $product->get_sale_price(); if ($regular !== '' && $sale !== '' && $regular !== $sale) { return (string) wc_format_sale_price(wc_price($regular), wc_price($sale)); } } return (string) wc_price($product->get_price()); } public function render_cart_items() { if (!function_exists('WC') || !WC()->cart) { return ''; } $cached = $this->read_session_cache(); if ($cached !== null) { return $cached; } $cart = WC()->cart->get_cart(); if (empty($cart)) { return '

' . esc_html__('No products in the cart.', 'ai-eshop-optimizer') . '

'; } ob_start(); ?> write_session_cache($html); return $html; } private function gift_sections() { if (!class_exists('AIEO_DMM_Brand_Gifts')) { return array(); } return array( 'gifts_html' => (string) AIEO_DMM_Brand_Gifts::instance()->render_side_cart_section(), 'rewards_html' => (string) AIEO_DMM_Brand_Gifts::instance()->render_cart_reward_cards(), ); } public function ajax_get() { check_ajax_referer('aieo_side_cart', 'nonce'); $content = $this->render_cart_items(); $count = function_exists('WC') && WC()->cart ? (int) WC()->cart->get_cart_contents_count() : 0; $gifts = $this->gift_sections() + array('gifts_html' => '', 'rewards_html' => ''); wp_send_json_success(array( 'content' => $content, 'gifts_html' => $gifts['gifts_html'], 'rewards_html' => $gifts['rewards_html'], 'count' => $count, 'subtotal' => function_exists('WC') && WC()->cart ? (string) WC()->cart->get_cart_subtotal() : '', 'subtotal_raw' => (float) $this->get_cart_subtotal_raw(), 'cart_ids' => $this->get_cart_product_ids(), 'cart_variations' => self::get_cart_variations_map(), )); } public function ajax_update_qty() { check_ajax_referer('aieo_side_cart', 'nonce'); $key = isset($_POST['key']) ? sanitize_text_field(wp_unslash($_POST['key'])) : ''; $qty = isset($_POST['qty']) ? (int) $_POST['qty'] : 0; if ($key === '' || $qty < 0) { wp_send_json_error(array('message' => __('Bad request.', 'ai-eshop-optimizer'))); } if (!function_exists('WC') || !WC()->cart) { wp_send_json_error(array('message' => __('Cart unavailable.', 'ai-eshop-optimizer'))); } WC()->cart->set_quantity($key, $qty, false); $gift_changed = $this->sync_gifts_check(); $response = array( 'count' => (int) WC()->cart->get_cart_contents_count(), 'subtotal_raw' => (float) $this->get_cart_subtotal_raw(), 'gifts_changed' => $gift_changed, 'cart_ids' => $this->get_cart_product_ids(), 'cart_variations' => self::get_cart_variations_map(), ); if ($gift_changed) { $this->clear_session_cache(); $response['content'] = $this->render_cart_items(); } wp_send_json_success($response + $this->gift_sections()); } public function ajax_remove() { check_ajax_referer('aieo_side_cart', 'nonce'); $key = isset($_POST['key']) ? sanitize_text_field(wp_unslash($_POST['key'])) : ''; if ($key === '' || !function_exists('WC') || !WC()->cart) { wp_send_json_error(array('message' => __('Bad request.', 'ai-eshop-optimizer'))); } WC()->cart->remove_cart_item($key); $this->sync_gifts_check(); $this->clear_session_cache(); wp_send_json_success(array( 'content' => $this->render_cart_items(), 'count' => (int) WC()->cart->get_cart_contents_count(), 'subtotal' => (string) WC()->cart->get_cart_subtotal(), 'subtotal_raw' => (float) $this->get_cart_subtotal_raw(), 'cart_ids' => $this->get_cart_product_ids(), 'cart_variations' => self::get_cart_variations_map(), ) + $this->gift_sections()); } public static function get_cart_variations_map() { $out = array(); if (function_exists('WC') && WC()->cart) { foreach (WC()->cart->get_cart() as $cart_item) { $vid = isset($cart_item['variation_id']) ? (int) $cart_item['variation_id'] : 0; if ($vid > 0) { $out[$vid] = ($out[$vid] ?? 0) + (int) ($cart_item['quantity'] ?? 0); } } } return (object) $out; } public function ajax_quick_view_add() { check_ajax_referer('aieo_side_cart', 'nonce'); $product_id = isset($_POST['product_id']) ? absint(wp_unslash($_POST['product_id'])) : 0; $variation_id = isset($_POST['variation_id']) ? absint(wp_unslash($_POST['variation_id'])) : 0; $quantity = isset($_POST['quantity']) ? max(1, absint(wp_unslash($_POST['quantity']))) : 1; $variation = array(); if (isset($_POST['variation']) && is_array($_POST['variation'])) { $variation_raw = map_deep(wp_unslash($_POST['variation']), 'sanitize_text_field'); foreach ($variation_raw as $k => $v) { $variation[ sanitize_text_field((string) $k) ] = (string) $v; } } if ($product_id <= 0 || !function_exists('WC') || !WC()->cart) { wp_send_json_error(array('message' => __('Invalid request.', 'ai-eshop-optimizer'))); } $product = wc_get_product($product_id); if (!$product) { wp_send_json_error(array('message' => __('Product not found.', 'ai-eshop-optimizer'))); } if ($product->is_type('variable') && $variation_id <= 0) { wp_send_json_error(array( 'message' => __('Please choose options first.', 'ai-eshop-optimizer'), 'product_url' => $product->get_permalink(), )); } $cart_item_key = WC()->cart->add_to_cart($product_id, $quantity, $variation_id, $variation); if (!$cart_item_key) { wp_send_json_error(array( 'message' => __('Could not add to cart.', 'ai-eshop-optimizer'), 'product_url' => $product->get_permalink(), )); } if (class_exists('AIEO_Attribution')) { $source = isset($_POST['aieo_source']) ? sanitize_key((string) wp_unslash($_POST['aieo_source'])) : 'sidecart_quickview'; AIEO_Attribution::instance()->log_event(array( 'event_id' => isset($_POST['aieo_event_id']) ? sanitize_text_field((string) wp_unslash($_POST['aieo_event_id'])) : '', 'product_id' => $product_id, 'variation_id' => $variation_id, 'quantity' => $quantity, 'unit_price' => (float) $product->get_price(), 'source' => $source, 'source_pid' => isset($_POST['aieo_source_pid']) ? absint(wp_unslash($_POST['aieo_source_pid'])) : 0, 'source_meta' => isset($_POST['aieo_source_meta']) ? (is_array($_POST['aieo_source_meta']) ? map_deep(wp_unslash($_POST['aieo_source_meta']), 'sanitize_text_field') : sanitize_textarea_field(wp_unslash($_POST['aieo_source_meta']))) : null, )); } $this->clear_session_cache(); wp_send_json_success(array( 'count' => (int) WC()->cart->get_cart_contents_count(), 'subtotal' => (string) WC()->cart->get_cart_subtotal(), 'subtotal_raw' => (float) $this->get_cart_subtotal_raw(), 'cart_ids' => $this->get_cart_product_ids(), )); } public function ajax_apply_coupon() { check_ajax_referer('aieo_side_cart', 'nonce'); $code = isset($_POST['coupon_code']) ? sanitize_text_field(wp_unslash($_POST['coupon_code'])) : ''; if ($code === '' || !function_exists('WC') || !WC()->cart) { wp_send_json_error(array('message' => __('Enter a coupon code.', 'ai-eshop-optimizer'))); } $ok = WC()->cart->apply_coupon($code); $this->clear_session_cache(); wp_send_json_success(array( 'applied' => (bool) $ok, 'content' => $this->render_cart_items(), 'count' => (int) WC()->cart->get_cart_contents_count(), 'subtotal' => (string) WC()->cart->get_cart_subtotal(), 'subtotal_raw' => (float) $this->get_cart_subtotal_raw(), 'cart_ids' => $this->get_cart_product_ids(), )); } public function ajax_save_settings() { AIEO_DMM::verify_ajax_request(); $bar_changed = false; $patch = array( 'side_cart_enabled' => !empty($_POST['side_cart_enabled']), 'side_cart_open_on_add' => !empty($_POST['side_cart_open_on_add']), 'side_cart_quick_view_enabled' => !empty($_POST['side_cart_quick_view_enabled']), 'side_cart_rows_qty' => !empty($_POST['side_cart_rows_qty']), 'side_cart_heading' => isset($_POST['side_cart_heading']) ? sanitize_text_field(wp_unslash($_POST['side_cart_heading'])) : '', 'side_cart_left_heading' => isset($_POST['side_cart_left_heading']) ? sanitize_text_field(wp_unslash($_POST['side_cart_left_heading'])) : '', 'side_cart_card_border_enabled' => !empty($_POST['side_cart_card_border_enabled']), 'side_cart_card_border_color' => isset($_POST['side_cart_card_border_color']) ? sanitize_hex_color(wp_unslash($_POST['side_cart_card_border_color'])) : '', 'side_cart_card_border_hover' => isset($_POST['side_cart_card_border_hover']) ? sanitize_hex_color(wp_unslash($_POST['side_cart_card_border_hover'])) : '', 'side_cart_progress_text' => (isset($_POST['side_cart_progress_text']) && 'above' === sanitize_key(wp_unslash($_POST['side_cart_progress_text']))) ? 'above' : 'on_bar', 'side_cart_accent_color' => (!empty($_POST['side_cart_accent_custom']) && isset($_POST['side_cart_accent_color'])) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_accent_color'])) : '', 'side_cart_reco_title_color' => (!empty($_POST['side_cart_accent_custom']) && isset($_POST['side_cart_reco_title_color'])) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_reco_title_color'])) : '', 'side_cart_mbar_custom' => !empty($_POST['side_cart_mbar_custom']), 'side_cart_mbar_height' => max(40, min(96, isset($_POST['side_cart_mbar_height']) ? (int) $_POST['side_cart_mbar_height'] : 58)), 'side_cart_mbar_bg' => isset($_POST['side_cart_mbar_bg']) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_mbar_bg'])) : '', 'side_cart_mbar_icon' => isset($_POST['side_cart_mbar_icon']) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_mbar_icon'])) : '', 'side_cart_mbar_icon_active' => isset($_POST['side_cart_mbar_icon_active']) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_mbar_icon_active'])) : '', 'side_cart_mbar_badge_bg' => isset($_POST['side_cart_mbar_badge_bg']) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_mbar_badge_bg'])) : '', 'side_cart_mbar_badge_fg' => isset($_POST['side_cart_mbar_badge_fg']) ? (string) sanitize_hex_color(wp_unslash($_POST['side_cart_mbar_badge_fg'])) : '', 'side_cart_mbar_cart_bubble' => !empty($_POST['side_cart_mbar_cart_bubble']), 'side_cart_mbar_glass' => !empty($_POST['side_cart_mbar_glass']), 'side_cart_mbar_opacity' => max(0, min(100, isset($_POST['side_cart_mbar_opacity']) ? (int) $_POST['side_cart_mbar_opacity'] : 70)), 'side_cart_mbar_blur' => max(0, min(40, isset($_POST['side_cart_mbar_blur']) ? (int) $_POST['side_cart_mbar_blur'] : 12)), 'side_cart_mbar_sel_enable' => !empty($_POST['side_cart_mbar_sel_enable']), ); foreach (array('nav', 'search', 'chat', 'consent', 'cart') as $sel_slug) { foreach (array('bg', 'fg', 'bc') as $sel_part) { $sel_key = 'side_cart_mbar_' . $sel_slug . '_sel_' . $sel_part; $patch[$sel_key] = isset($_POST[$sel_key]) ? (string) sanitize_hex_color(wp_unslash($_POST[$sel_key])) : ''; } } if (isset($_POST['button_settings_form'])) { $patch['side_cart_show_cart_btn'] = !empty($_POST['side_cart_show_cart_btn']); $patch['side_cart_show_save_btn'] = !empty($_POST['side_cart_show_save_btn']); $patch['side_cart_btn_save'] = isset($_POST['side_cart_btn_save']) ? sanitize_text_field(wp_unslash($_POST['side_cart_btn_save'])) : ''; $valid_borders = array('none','solid','dashed','dotted','double'); foreach (array('cart', 'checkout', 'save') as $which) { $bg = isset($_POST['side_cart_btn_' . $which . '_bg']) ? sanitize_hex_color(wp_unslash($_POST['side_cart_btn_' . $which . '_bg'])) : ''; $color = isset($_POST['side_cart_btn_' . $which . '_color']) ? sanitize_hex_color(wp_unslash($_POST['side_cart_btn_' . $which . '_color'])) : ''; $bcolor = isset($_POST['side_cart_btn_' . $which . '_border_color']) ? sanitize_hex_color(wp_unslash($_POST['side_cart_btn_' . $which . '_border_color'])) : ''; $bstyle = isset($_POST['side_cart_btn_' . $which . '_border_style']) ? sanitize_key(wp_unslash($_POST['side_cart_btn_' . $which . '_border_style'])) : 'none'; if (!in_array($bstyle, $valid_borders, true)) { $bstyle = 'none'; } $patch['side_cart_btn_' . $which . '_bg'] = $bg; $patch['side_cart_btn_' . $which . '_color'] = $color; $patch['side_cart_btn_' . $which . '_border_style'] = $bstyle; $patch['side_cart_btn_' . $which . '_border_color'] = $bcolor; $patch['side_cart_btn_' . $which . '_border_width'] = max(0, min(8, isset($_POST['side_cart_btn_' . $which . '_border_width']) ? (int) $_POST['side_cart_btn_' . $which . '_border_width'] : 0)); $patch['side_cart_btn_' . $which . '_radius'] = max(0, min(48, isset($_POST['side_cart_btn_' . $which . '_radius']) ? (int) $_POST['side_cart_btn_' . $which . '_radius'] : 24)); } } if (isset($_POST['side_cart_bar_items'])) { $raw = wp_unslash((string) sanitize_text_field( wp_unslash( $_POST['side_cart_bar_items'] ) )); $rows = json_decode($raw, true); if (is_array($rows)) { if ( ! class_exists( 'AIEO_Side_Cart_Display' ) && file_exists( AIEO_DIR . 'includes/class-aieo-side-cart-display.php' ) ) { require_once AIEO_DIR . 'includes/class-aieo-side-cart-display.php'; } $catalog_slugs = array(); foreach (AIEO_Side_Cart_Display::bar_items_catalog() as $g) { $catalog_slugs[$g['slug']] = true; } $clean = array(); $seen = array(); foreach ($rows as $r) { if (!is_array($r) || empty($r['slug'])) { continue; } $slug = sanitize_key($r['slug']); if ('' === $slug || isset($seen[$slug]) || (!isset($catalog_slugs[$slug]) && count($clean) >= 12)) { continue; } $clean[] = array('slug' => $slug, 'enabled' => !empty($r['enabled'])); $seen[$slug] = true; } $bar_changed = $clean !== AIEO_DMM_Settings::get('side_cart_bar_items', null); $patch['side_cart_bar_items'] = $clean; } } if (isset($_POST['side_cart_chat_desktop_position'])) { $pos = sanitize_key(wp_unslash((string) $_POST['side_cart_chat_desktop_position'])); $patch['side_cart_chat_desktop_position'] = in_array($pos, array('left', 'above'), true) ? $pos : 'left'; } if (empty($patch['side_cart_card_border_color'])) { $patch['side_cart_card_border_color'] = '#c0a37b'; } if (empty($patch['side_cart_card_border_hover'])) { $patch['side_cart_card_border_hover'] = '#a88a63'; } $label_keys = array( 'side_cart_tab_bag', 'side_cart_tab_viewed', 'side_cart_continue', 'side_cart_maylike_title', 'side_cart_subtotal_label', 'side_cart_coupon_link', 'side_cart_btn_cart', 'side_cart_btn_checkout', 'side_cart_btn_addbag', 'side_cart_rv_empty', 'side_cart_quick_view_label', 'side_cart_qv_view_product', 'side_cart_qv_tab', 'side_cart_tab_favorites', 'side_cart_fav_empty', 'side_cart_rv_atc', 'side_cart_in_cart_label', 'side_cart_fav_brands_title', 'side_cart_fav_brand_products', ); foreach ($label_keys as $k) { $patch[$k] = isset($_POST[$k]) ? sanitize_text_field(wp_unslash($_POST[$k])) : ''; } AIEO_DMM_Settings::update($patch); $this->clear_session_cache(); if ($bar_changed) { do_action('aieo_purge_page_cache'); } wp_send_json_success(array('message' => __('Side cart settings saved.', 'ai-eshop-optimizer'))); } private function get_cart_product_ids() { if (!function_exists('WC') || !WC()->cart) { return array(); } $ids = array(); foreach (WC()->cart->get_cart() as $item) { if (!empty($item['product_id'])) { $ids[] = (int) $item['product_id']; } } return array_values(array_unique($ids)); } private function get_cart_subtotal_raw() { if (!function_exists('WC') || !WC()->cart) { return 0.0; } if (class_exists('AIEO_DMM_Pricing_Engine')) { $cart_ids = array(); foreach (WC()->cart->get_cart() as $item) { if (!empty($item['product_id'])) { $cart_ids[] = (int) $item['product_id']; } if (!empty($item['variation_id'])) { $cart_ids[] = (int) $item['variation_id']; } } $cart_ids = array_unique(array_filter($cart_ids)); if (!empty($cart_ids)) { AIEO_DMM_Pricing_Engine::instance()->preload_role_prices($cart_ids); } } $total = 0.0; foreach (WC()->cart->get_cart() as $item) { if (!empty($item['_aieo_dmm_gift_offer_id'])) { continue; } $product = isset($item['data']) ? $item['data'] : null; if (!$product || !is_a($product, 'WC_Product')) { continue; } $total += ((float) $product->get_price()) * max(0, (int) $item['quantity']); } return $total; } private function sync_gifts_check() { if (!function_exists('WC') || !WC()->cart) { return false; } $before = array(); foreach (WC()->cart->get_cart() as $k => $item) { if (!empty($item['_aieo_dmm_gift_offer_id'])) { $before[$k] = true; } } if (class_exists('AIEO_DMM_Brand_Gifts')) { do_action('woocommerce_cart_item_restored'); } $after = array(); foreach (WC()->cart->get_cart() as $k => $item) { if (!empty($item['_aieo_dmm_gift_offer_id'])) { $after[$k] = true; } } return array_keys($before) !== array_keys($after); } private function session_cache_key() { if (!function_exists('WC') || !WC()->session || !WC()->cart) { return ''; } $id = (string) WC()->session->get_customer_id(); if ($id === '') { return ''; } $locale = function_exists('determine_locale') ? determine_locale() : get_locale(); return 'sc|' . md5($id . '|' . (string) WC()->cart->get_cart_hash() . '|' . (int) WC()->cart->get_cart_contents_count() . '|' . $locale); } private function read_session_cache() { $key = $this->session_cache_key(); if ($key === '') { return null; } $html = wp_cache_get($key, self::CACHE_GROUP); return is_string($html) ? $html : null; } private function write_session_cache($html) { $key = $this->session_cache_key(); if ($key !== '') { wp_cache_set($key, (string) $html, self::CACHE_GROUP, self::SESSION_CACHE_TTL); } } public function clear_session_cache() { $key = $this->session_cache_key(); if ($key !== '') { wp_cache_delete($key, self::CACHE_GROUP); } } public function add_cart_fragments($fragments) { if (!is_array($fragments)) { $fragments = array(); } $count = function_exists('WC') && WC()->cart ? (int) WC()->cart->get_cart_contents_count() : 0; $c11 = class_exists('AIEO_Side_Cart_Display') && AIEO_Side_Cart_Display::theme_c11(); $fragments['.aieo-sc-trigger-count'] = '' . esc_html((string) $count) . ''; return $fragments; } }