is_woocommerce_active()) {
add_action('admin_notices', array($this, 'woocommerce_missing_notice'));
return;
}
// Initialize parent menu
AboveWP_Admin_Menu::init();
// Add admin settings
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
// Remove WordPress admin notices on our admin pages
add_action('admin_head', array($this, 'remove_admin_notices_on_plugin_pages'));
// Enqueue admin styles
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_styles'));
// Add plugin card to AboveWP dashboard
add_action('abovewp_admin_dashboard_plugins', array($this, 'display_plugin_card'));
// Add AJAX handler for adding to cart
add_action('wp_ajax_abovewp_add_upsell_to_cart', array($this, 'add_upsell_to_cart'));
add_action('wp_ajax_nopriv_abovewp_add_upsell_to_cart', array($this, 'add_upsell_to_cart'));
// Add AJAX handler for getting upsell data
add_action('wp_ajax_abovewp_get_upsell_data', array($this, 'ajax_get_upsell_data'));
add_action('wp_ajax_nopriv_abovewp_get_upsell_data', array($this, 'ajax_get_upsell_data'));
// Add AJAX handler for admin product search (settings page)
add_action('wp_ajax_abovewp_atc_search_products', array($this, 'ajax_search_products'));
// Initialize frontend functionality
$this->init_frontend();
// Add plugin action links
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
}
/**
* Check if WooCommerce is active
*
* @return bool
*/
private function is_woocommerce_active() {
return in_array('woocommerce/woocommerce.php', (array) get_option('active_plugins', array()), true);
}
/**
* Show admin notice if WooCommerce is not active
*/
public function woocommerce_missing_notice() {
?>
' . __('Settings', 'abovewp-atc-popup-upsell') . '';
array_unshift($links, $settings_link);
return $links;
}
/**
* Initialize frontend functionality
*/
private function init_frontend() {
// Only proceed if enabled
if (get_option('abovewp_atc_enabled', 'yes') !== 'yes') {
return;
}
// Enqueue frontend scripts and styles
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
// Add popup HTML to footer
add_action('wp_footer', array($this, 'add_popup_html'));
// Add hooks that will work with AJAX add to cart
// For single product pages - prepare the upsell data
add_action('woocommerce_before_single_product', array($this, 'add_product_upsell_data'));
// For shop/category pages - handle AJAX add to cart
add_filter('woocommerce_loop_add_to_cart_args', array($this, 'add_data_to_add_to_cart_button'), 10, 2);
// Hook into WooCommerce add to cart action for better detection
add_action('woocommerce_add_to_cart', array($this, 'on_product_added_to_cart'), 10, 6);
// Add cart monitoring script
add_action('wp_footer', array($this, 'add_cart_monitoring_script'), 5);
// Debug info in footer - disabled in production
// if (defined('WP_DEBUG') && WP_DEBUG) {
// add_action('wp_footer', array($this, 'debug_info'), 999);
// }
}
/**
* Enqueue admin styles
*/
public function enqueue_admin_styles($hook) {
if (strpos($hook, 'abovewp') !== false) {
wp_enqueue_style(
'abovewp-font-inter',
'https://fonts.bunny.net/css?family=inter:400,500,600,700',
array(),
ABOVEWP_ATC_VERSION
);
wp_enqueue_style(
'abovewp-admin-default',
ABOVEWP_ATC_PLUGIN_URL . 'assets/css/admin-page-default.css',
array('abovewp-font-inter'),
ABOVEWP_ATC_VERSION
);
}
// Enqueue Select2 (selectWoo) on our settings page only
if ($hook === 'abovewp_page_abovewp-atc-popup-upsell') {
wp_enqueue_style('select2', WC()->plugin_url() . '/assets/css/select2.css', array(), WC_VERSION);
wp_enqueue_script('selectWoo', WC()->plugin_url() . '/assets/js/selectWoo/selectWoo.full.min.js', array('jquery'), WC_VERSION, true);
$inline_js = "
jQuery(function($) {
$('#abovewp_atc_global_products').selectWoo({
ajax: {
url: ajaxurl,
dataType: 'json',
delay: 300,
data: function(params) {
return {
term: params.term,
action: 'abovewp_atc_search_products',
nonce: '" . esc_js(wp_create_nonce('abovewp_atc_search_products')) . "'
};
},
processResults: function(data) {
return data;
},
cache: true
},
minimumInputLength: 2,
placeholder: '" . esc_js(__('Search for products...', 'abovewp-atc-popup-upsell')) . "',
allowClear: true,
templateResult: function(item) {
if (item.loading) return item.text;
var img = item.image ? '
' : '';
return $('' + img + '' + item.text + ' — ' + item.price_html + '');
},
templateSelection: function(item) {
return item.text;
}
});
});
";
wp_add_inline_script('selectWoo', $inline_js);
}
}
/**
* Enqueue scripts and styles for the frontend
*/
public function enqueue_scripts() {
// Enhanced page detection - include more pages where add-to-cart might happen
$should_load = false;
// Standard WooCommerce pages
if (is_product() || is_cart() || is_shop() || is_product_category() || is_product_tag()) {
$should_load = true;
}
// Check for WooCommerce-related content on any page
if (!$should_load && (is_page() || is_single() || is_home() || is_front_page())) {
// Check for WooCommerce shortcodes or blocks
global $post;
if ($post && (
has_shortcode($post->post_content, 'product') ||
has_shortcode($post->post_content, 'products') ||
has_shortcode($post->post_content, 'recent_products') ||
has_shortcode($post->post_content, 'featured_products') ||
has_shortcode($post->post_content, 'sale_products') ||
has_shortcode($post->post_content, 'best_selling_products') ||
has_shortcode($post->post_content, 'top_rated_products') ||
strpos($post->post_content, 'wp:woocommerce/') !== false ||
strpos($post->post_content, 'add-to-cart') !== false
)) {
$should_load = true;
}
}
// Check if we're on a page that might have Elementor or other page builder WooCommerce widgets
if (!$should_load && function_exists('wc_get_page_id')) {
// Also load on pages that might have WooCommerce widgets or custom implementations
$should_load = true; // For maximum compatibility
}
if (!$should_load) {
return;
}
wp_enqueue_style(
'abovewp-atc-popup',
ABOVEWP_ATC_PLUGIN_URL . 'assets/css/popup.css',
array(),
ABOVEWP_ATC_VERSION
);
wp_enqueue_script(
'abovewp-atc-popup',
ABOVEWP_ATC_PLUGIN_URL . 'assets/js/popup.js',
array('jquery'),
ABOVEWP_ATC_VERSION,
true
);
// Get custom text settings
$success_message_format = get_option('abovewp_atc_success_message_format', '{product_name} ' . __('added to cart!', 'abovewp-atc-popup-upsell'));
$add_to_cart_text = get_option('abovewp_atc_add_to_cart_text', __('Add to Cart', 'abovewp-atc-popup-upsell'));
$view_cart_text = get_option('abovewp_atc_view_cart_text', __('View Cart', 'abovewp-atc-popup-upsell'));
$checkout_text = get_option('abovewp_atc_checkout_text', __('Checkout', 'abovewp-atc-popup-upsell'));
$continue_shopping_text = get_option('abovewp_atc_continue_shopping_text', __('Continue Shopping', 'abovewp-atc-popup-upsell'));
$popup_title = get_option('abovewp_atc_popup_title', __('You might also like...', 'abovewp-atc-popup-upsell'));
$added_to_cart_text = get_option('abovewp_atc_added_to_cart_text', __('Added to cart!', 'abovewp-atc-popup-upsell'));
// Prepare enhanced JavaScript parameters
$params = array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('abovewp-atc-upsell'),
'wc_ajax_url' => WC_AJAX::get_endpoint('%%endpoint%%'),
'cart_url' => wc_get_cart_url(),
'checkout_url' => wc_get_checkout_url(),
'is_product_page' => is_product(),
'is_shop_page' => is_shop(),
'is_cart_page' => is_cart(),
'current_product_id' => is_product() ? get_the_ID() : 0,
'i18n' => array(
'added_to_cart' => $added_to_cart_text,
'failed_to_add' => __('Failed to add product to cart.', 'abovewp-atc-popup-upsell'),
'view_cart' => $view_cart_text,
'checkout' => $checkout_text,
'continue_shopping' => $continue_shopping_text,
'add_to_cart' => $add_to_cart_text,
'popup_title' => $popup_title,
'success_message_format' => $success_message_format,
)
);
// Add WooCommerce params if available
if (function_exists('wc_get_cart_url')) {
$params['wc_cart_fragments_enabled'] = 'yes' === get_option('woocommerce_enable_ajax_add_to_cart');
$params['wc_cart_hash'] = md5(json_encode(WC()->cart->get_cart()));
}
wp_localize_script(
'abovewp-atc-popup',
'abovewp_atc_params',
$params
);
}
/**
* Add admin menu
*/
public function add_admin_menu() {
add_submenu_page(
'abovewp',
__('AboveWP Add To Cart Upsell Popup', 'abovewp-atc-popup-upsell'),
__('ATC Upsell Popup', 'abovewp-atc-popup-upsell'),
'manage_options',
'abovewp-atc-popup-upsell',
array($this, 'settings_page')
);
}
/**
* Register settings
*/
public function register_settings() {
register_setting('abovewp_atc_settings', 'abovewp_atc_enabled', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_upsell_source', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_global_product_ids', array(
'sanitize_callback' => function ($value) {
if (is_array($value)) {
$value = array_filter($value);
return implode(',', array_map('absint', $value));
}
return sanitize_text_field($value);
},
));
register_setting('abovewp_atc_settings', 'abovewp_atc_upsell_count', array(
'sanitize_callback' => 'absint',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_category_mappings', array(
'sanitize_callback' => function ($value) {
if (!is_array($value)) {
return array();
}
return array_map('sanitize_text_field', $value);
},
));
// Text customization settings
register_setting('abovewp_atc_settings', 'abovewp_atc_popup_title', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_success_message_format', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_add_to_cart_text', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_view_cart_text', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_checkout_text', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_continue_shopping_text', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_added_to_cart_text', array(
'sanitize_callback' => 'sanitize_text_field',
));
// Button visibility settings
register_setting('abovewp_atc_settings', 'abovewp_atc_show_view_cart', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_show_checkout', array(
'sanitize_callback' => 'sanitize_text_field',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_show_continue_shopping', array(
'sanitize_callback' => 'sanitize_text_field',
));
// Color settings
register_setting('abovewp_atc_settings', 'abovewp_atc_price_color', array(
'sanitize_callback' => 'sanitize_hex_color',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_add_to_cart_button_color', array(
'sanitize_callback' => 'sanitize_hex_color',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_view_cart_button_color', array(
'sanitize_callback' => 'sanitize_hex_color',
));
register_setting('abovewp_atc_settings', 'abovewp_atc_checkout_button_color', array(
'sanitize_callback' => 'sanitize_hex_color',
));
}
/**
* Settings page
*/
public function settings_page() {
// Get options
$enabled = get_option('abovewp_atc_enabled', 'yes');
$upsell_source = get_option('abovewp_atc_upsell_source', 'woo_upsells');
$global_product_ids = get_option('abovewp_atc_global_product_ids', '');
$upsell_count = get_option('abovewp_atc_upsell_count', 2);
$category_mappings = get_option('abovewp_atc_category_mappings', array());
// Get all product categories
$product_categories = get_terms(array(
'taxonomy' => 'product_cat',
'hide_empty' => false,
));
?>
base) && strpos($current_screen->base, 'abovewp') !== false) {
remove_all_actions('admin_notices');
remove_all_actions('all_admin_notices');
}
}
/**
* Add product upsell data
*/
public function add_product_upsell_data() {
global $product;
if (!$product) {
return;
}
$upsell_source = get_option('abovewp_atc_upsell_source', 'woo_upsells');
$upsell_count = intval(get_option('abovewp_atc_upsell_count', 2));
// Get upsell products based on selected source
$upsell_ids = array();
switch ($upsell_source) {
case 'woo_upsells':
// Use WooCommerce's built-in upsells
$upsell_ids = $product->get_upsell_ids();
break;
case 'global_products':
// Use globally defined products
$global_product_ids = get_option('abovewp_atc_global_product_ids', '');
if (!empty($global_product_ids)) {
$upsell_ids = array_map('trim', explode(',', $global_product_ids));
}
break;
case 'category_based':
// Use category-based products
$category_mappings = get_option('abovewp_atc_category_mappings', array());
$product_categories = $product->get_category_ids();
foreach ($product_categories as $category_id) {
if (isset($category_mappings[$category_id]) && !empty($category_mappings[$category_id])) {
$category_upsells = array_map('trim', explode(',', $category_mappings[$category_id]));
$upsell_ids = array_merge($upsell_ids, $category_upsells);
}
}
// Remove duplicates
$upsell_ids = array_unique($upsell_ids);
break;
}
// Remove current product from upsells if it's there
$current_product_id = $product->get_id();
$upsell_ids = array_diff($upsell_ids, array($current_product_id));
// Limit the number of upsells
$upsell_ids = array_slice($upsell_ids, 0, $upsell_count);
// Prepare upsell products data
$upsell_products = array();
foreach ($upsell_ids as $upsell_id) {
$upsell_product = wc_get_product($upsell_id);
if (!$upsell_product || !$upsell_product->is_purchasable() || !$upsell_product->is_in_stock()) {
continue;
}
$upsell_products[] = array(
'id' => $upsell_product->get_id(),
'name' => $upsell_product->get_name(),
'price' => $upsell_product->get_price(),
'price_html' => $upsell_product->get_price_html(),
'image' => wp_get_attachment_image_url($upsell_product->get_image_id(), 'woocommerce_thumbnail') ?:
wc_placeholder_img_src('woocommerce_thumbnail'),
'url' => $upsell_product->get_permalink(),
);
}
// Prepare data for JavaScript
$popup_data = array(
'added_product_name' => $product->get_name(),
'upsell_products' => $upsell_products,
);
// Add data to the page for JavaScript
if (!empty($upsell_products)) {
// We need to make sure this script is added after jQuery and our main script
wp_add_inline_script('abovewp-atc-popup', 'var abovewp_atc_upsell_data = ' . wp_json_encode($popup_data) . ';', 'after');
}
// Also store data in a transient for AJAX requests
set_transient('abovewp_product_' . $current_product_id . '_upsell_data', $popup_data, HOUR_IN_SECONDS);
}
/**
* Add popup HTML to footer
*/
public function add_popup_html() {
// Enhanced page detection - same logic as enqueue_scripts
$should_load = false;
// Standard WooCommerce pages
if (is_product() || is_cart() || is_shop() || is_product_category() || is_product_tag()) {
$should_load = true;
}
// Check for WooCommerce-related content on any page
if (!$should_load && (is_page() || is_single() || is_home() || is_front_page())) {
// Check for WooCommerce shortcodes or blocks
global $post;
if ($post && (
has_shortcode($post->post_content, 'product') ||
has_shortcode($post->post_content, 'products') ||
has_shortcode($post->post_content, 'recent_products') ||
has_shortcode($post->post_content, 'featured_products') ||
has_shortcode($post->post_content, 'sale_products') ||
has_shortcode($post->post_content, 'best_selling_products') ||
has_shortcode($post->post_content, 'top_rated_products') ||
strpos($post->post_content, 'wp:woocommerce/') !== false ||
strpos($post->post_content, 'add-to-cart') !== false
)) {
$should_load = true;
}
}
// Check if we're on a page that might have Elementor or other page builder WooCommerce widgets
if (!$should_load && function_exists('wc_get_page_id')) {
// Also load on pages that might have WooCommerce widgets or custom implementations
$should_load = true; // For maximum compatibility
}
if (!$should_load) {
return;
}
$cart_url = wc_get_cart_url();
$checkout_url = wc_get_checkout_url();
// Get text customization settings
$popup_title = get_option('abovewp_atc_popup_title', __('You might also like...', 'abovewp-atc-popup-upsell'));
// Get button visibility settings
$show_view_cart = get_option('abovewp_atc_show_view_cart', 'yes') === 'yes';
$show_checkout = get_option('abovewp_atc_show_checkout', 'yes') === 'yes';
$show_continue_shopping = get_option('abovewp_atc_show_continue_shopping', 'yes') === 'yes';
// Get button text settings
$view_cart_text = get_option('abovewp_atc_view_cart_text', __('View Cart', 'abovewp-atc-popup-upsell'));
$checkout_text = get_option('abovewp_atc_checkout_text', __('Checkout', 'abovewp-atc-popup-upsell'));
$continue_shopping_text = get_option('abovewp_atc_continue_shopping_text', __('Continue Shopping', 'abovewp-atc-popup-upsell'));
// Get color settings
$price_color = get_option('abovewp_atc_price_color', '#0582FF');
$add_to_cart_button_color = get_option('abovewp_atc_add_to_cart_button_color', '#0582FF');
$view_cart_button_color = get_option('abovewp_atc_view_cart_button_color', '#0582FF');
$checkout_button_color = get_option('abovewp_atc_checkout_button_color', '#4CAF50');
// Darken colors for hover states (roughly 15% darker)
$add_to_cart_hover = $this->darken_color($add_to_cart_button_color, 15);
$view_cart_hover = $this->darken_color($view_cart_button_color, 15);
$checkout_hover = $this->darken_color($checkout_button_color, 15);
$inline_css = "
.abovewp-atc-popup-product .price {
color: " . esc_attr($price_color) . ";
}
.abovewp-atc-popup-product button {
background-color: " . esc_attr($add_to_cart_button_color) . ";
}
.abovewp-atc-popup-product button:hover {
background-color: " . esc_attr($add_to_cart_hover) . ";
}
.abovewp-atc-popup-product button.added {
background-color: #4CAF50;
}
.abovewp-atc-popup-actions .view-cart {
background-color: " . esc_attr($view_cart_button_color) . ";
}
.abovewp-atc-popup-actions .view-cart:hover {
background-color: " . esc_attr($view_cart_hover) . ";
}
.abovewp-atc-popup-actions .checkout {
background-color: " . esc_attr($checkout_button_color) . ";
}
.abovewp-atc-popup-actions .checkout:hover {
background-color: " . esc_attr($checkout_hover) . ";
}
";
wp_add_inline_style('abovewp-atc-popup', $inline_css);
?>
cart->add_to_cart($product_id);
if ($added) {
// Get mini cart fragments
$fragments = array();
ob_start();
woocommerce_mini_cart();
$mini_cart = ob_get_clean();
$fragments['div.widget_shopping_cart_content'] = '' . $mini_cart . '
';
wp_send_json_success(array(
'fragments' => $fragments,
));
} else {
wp_send_json_error();
}
wp_die();
}
/**
* Add data attributes to add to cart buttons in product loops
* This helps identify the product when added via AJAX
*/
public function add_data_to_add_to_cart_button($args, $product) {
// Store product ID in a data attribute
$args['attributes']['data-product_id'] = $product->get_id();
// Get and prepare the upsell data for this product
$upsell_source = get_option('abovewp_atc_upsell_source', 'woo_upsells');
$upsell_count = intval(get_option('abovewp_atc_upsell_count', 2));
// Get upsell products based on selected source
$upsell_ids = array();
switch ($upsell_source) {
case 'woo_upsells':
// Use WooCommerce's built-in upsells
$upsell_ids = $product->get_upsell_ids();
break;
case 'global_products':
// Use globally defined products
$global_product_ids = get_option('abovewp_atc_global_product_ids', '');
if (!empty($global_product_ids)) {
$upsell_ids = array_map('trim', explode(',', $global_product_ids));
}
break;
case 'category_based':
// Use category-based products
$category_mappings = get_option('abovewp_atc_category_mappings', array());
$product_categories = $product->get_category_ids();
foreach ($product_categories as $category_id) {
if (isset($category_mappings[$category_id]) && !empty($category_mappings[$category_id])) {
$category_upsells = array_map('trim', explode(',', $category_mappings[$category_id]));
$upsell_ids = array_merge($upsell_ids, $category_upsells);
}
}
// Remove duplicates
$upsell_ids = array_unique($upsell_ids);
break;
}
// Remove current product from upsells if it's there
$current_product_id = $product->get_id();
$upsell_ids = array_diff($upsell_ids, array($current_product_id));
// Limit the number of upsells
$upsell_ids = array_slice($upsell_ids, 0, $upsell_count);
// Store product and upsell IDs for reference
$args['attributes']['data-has-upsells'] = !empty($upsell_ids) ? 'true' : 'false';
// Store data in transient for AJAX requests
if (!empty($upsell_ids)) {
// Prepare upsell products data
$upsell_products = array();
foreach ($upsell_ids as $upsell_id) {
$upsell_product = wc_get_product($upsell_id);
if (!$upsell_product || !$upsell_product->is_purchasable() || !$upsell_product->is_in_stock()) {
continue;
}
$upsell_products[] = array(
'id' => $upsell_product->get_id(),
'name' => $upsell_product->get_name(),
'price' => $upsell_product->get_price(),
'price_html' => $upsell_product->get_price_html(),
'image' => wp_get_attachment_image_url($upsell_product->get_image_id(), 'woocommerce_thumbnail') ?:
wc_placeholder_img_src('woocommerce_thumbnail'),
'url' => $upsell_product->get_permalink(),
);
}
// Prepare data for JavaScript
$popup_data = array(
'added_product_name' => $product->get_name(),
'upsell_products' => $upsell_products,
);
// Store data in a transient for AJAX requests
set_transient('abovewp_product_' . $current_product_id . '_upsell_data', $popup_data, HOUR_IN_SECONDS);
}
return $args;
}
/**
* AJAX handler to get upsell data for a product
*/
public function ajax_get_upsell_data() {
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'abovewp-atc-upsell')) {
wp_send_json_error(array('message' => 'Invalid security token'));
return;
}
// Check if product ID is provided
if (!isset($_POST['product_id'])) {
wp_send_json_error(array('message' => 'No product ID provided'));
return;
}
$product_id = sanitize_text_field(wp_unslash($_POST['product_id']));
$product = null;
// If product_id is 'latest', get the latest added product to cart
if ($product_id === 'latest') {
// Use our enhanced method to get the last added product
$latest_product_id = $this->get_last_added_product();
if ($latest_product_id) {
$product_id = $latest_product_id;
} else {
wp_send_json_error(array('message' => 'No products in cart or recently added'));
return;
}
}
// Get product data
$product = wc_get_product($product_id);
if (!$product) {
wp_send_json_error(array('message' => 'Product not found'));
return;
}
// Try to get data from transient first
$transient_key = 'abovewp_product_' . $product_id . '_upsell_data';
$upsell_data = get_transient($transient_key);
if (!$upsell_data) {
// If no transient data, generate it
$upsell_data = $this->generate_upsell_data_for_product($product);
// Store in transient for future use
set_transient($transient_key, $upsell_data, HOUR_IN_SECONDS);
}
if (empty($upsell_data['upsell_products'])) {
wp_send_json_error(array('message' => 'No upsell products found'));
return;
}
wp_send_json_success($upsell_data);
}
/**
* Generate upsell data for a specific product
*
* @param WC_Product $product The product to generate upsell data for
* @return array The upsell data
*/
private function generate_upsell_data_for_product($product) {
$upsell_source = get_option('abovewp_atc_upsell_source', 'woo_upsells');
$upsell_count = intval(get_option('abovewp_atc_upsell_count', 2));
$global_product_ids = get_option('abovewp_atc_global_product_ids', '');
$category_mappings = get_option('abovewp_atc_category_mappings', array());
$upsell_products = array();
$upsell_ids = array();
// Get upsell products based on the selected source
switch ($upsell_source) {
case 'woo_upsells':
// Use WooCommerce's built-in upsells
$upsell_ids = $product->get_upsell_ids();
break;
case 'global_products':
// Use globally defined products
if (!empty($global_product_ids)) {
$upsell_ids = array_map('trim', explode(',', $global_product_ids));
}
break;
case 'category_based':
// Use category-based products
$product_categories = $product->get_category_ids();
foreach ($product_categories as $category_id) {
if (isset($category_mappings[$category_id]) && !empty($category_mappings[$category_id])) {
$category_upsells = array_map('trim', explode(',', $category_mappings[$category_id]));
$upsell_ids = array_merge($upsell_ids, $category_upsells);
}
}
// Remove duplicates
$upsell_ids = array_unique($upsell_ids);
break;
}
// Remove current product from upsells if it's there
$current_product_id = $product->get_id();
$upsell_ids = array_diff($upsell_ids, array($current_product_id));
// Limit the number of upsells
$upsell_ids = array_slice($upsell_ids, 0, $upsell_count);
// Prepare upsell data
foreach ($upsell_ids as $upsell_id) {
$upsell_product = wc_get_product($upsell_id);
if (!$upsell_product || !$upsell_product->is_purchasable() || !$upsell_product->is_in_stock()) {
continue;
}
$upsell_products[] = array(
'id' => $upsell_product->get_id(),
'name' => $upsell_product->get_name(),
'price' => $upsell_product->get_price(),
'price_html' => $upsell_product->get_price_html(),
'image' => wp_get_attachment_image_url($upsell_product->get_image_id(), 'woocommerce_thumbnail') ?: wc_placeholder_img_src('woocommerce_thumbnail'),
'url' => get_permalink($upsell_product->get_id())
);
}
return array(
'added_product_name' => $product->get_name(),
'upsell_products' => $upsell_products
);
}
/**
* AJAX handler for searching products (admin settings page)
*/
public function ajax_search_products() {
check_ajax_referer('abovewp_atc_search_products', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
$term = isset($_GET['term']) ? sanitize_text_field(wp_unslash($_GET['term'])) : '';
if (empty($term)) {
wp_send_json(array('results' => array()));
}
$products = wc_get_products(array(
's' => $term,
'limit' => 30,
'status' => 'publish',
));
$results = array();
foreach ($products as $product) {
$image_url = '';
$image_id = $product->get_image_id();
if ($image_id) {
$image_src = wp_get_attachment_image_src($image_id, 'thumbnail');
if ($image_src) {
$image_url = $image_src[0];
}
} else {
$image_url = wc_placeholder_img_src('thumbnail');
}
$results[] = array(
'id' => $product->get_id(),
'text' => $product->get_name(),
'image' => $image_url,
'price_html' => wp_strip_all_tags($product->get_price_html()),
);
}
wp_send_json(array('results' => $results));
}
/**
* Hook into WooCommerce add to cart action for better detection
*/
public function on_product_added_to_cart($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart) {
// Store the last added product in session for JavaScript to pick up
WC()->session->set('abovewp_last_added_product', $product_id);
WC()->session->set('abovewp_last_added_time', time());
// Also store in transient as fallback
set_transient('abovewp_last_added_product_' . WC()->session->get_customer_id(), $product_id, 300); // 5 minutes
}
/**
* Add method to retrieve last added product for AJAX calls
*/
public function get_last_added_product() {
// Try session first
$product_id = WC()->session->get('abovewp_last_added_product');
$time = WC()->session->get('abovewp_last_added_time');
// Only return if added recently (within 5 minutes)
if ($product_id && $time && (time() - $time < 300)) {
return $product_id;
}
// Try transient as fallback
$product_id = get_transient('abovewp_last_added_product_' . WC()->session->get_customer_id());
if ($product_id) {
return $product_id;
}
// As last resort, get the most recent item from cart
$cart = WC()->cart->get_cart();
if (!empty($cart)) {
$cart_keys = array_keys($cart);
$last_key = end($cart_keys);
return $cart[$last_key]['product_id'];
}
return null;
}
/**
* Add cart monitoring script
*/
public function add_cart_monitoring_script() {
if (!wp_script_is('abovewp-atc-popup', 'enqueued')) {
return;
}
// Add cart state monitoring
$cart_contents = absint(WC()->cart->get_cart_contents_count());
$cart_hash = esc_js(WC()->cart->get_cart_hash());
$script = "
if (typeof window.abovewp_cart_state === 'undefined') {
window.abovewp_cart_state = {
item_count: {$cart_contents},
cart_hash: '{$cart_hash}'
};
}
";
// Check if a product was just added to cart (non-AJAX flow)
$last_product_id = WC()->session->get('abovewp_last_added_product');
$last_added_time = WC()->session->get('abovewp_last_added_time');
if ($last_product_id && $last_added_time && (time() - $last_added_time < 30)) {
$product = wc_get_product($last_product_id);
if ($product) {
$upsell_data = $this->generate_upsell_data_for_product($product);
if (!empty($upsell_data['upsell_products'])) {
$script .= "\nwindow.abovewp_atc_upsell_data = " . wp_json_encode($upsell_data) . ";\n";
$script .= "window.abovewp_atc_show_popup_on_load = true;\n";
}
}
// Clear the session flag so it doesn't fire again on next page load
WC()->session->set('abovewp_last_added_product', null);
WC()->session->set('abovewp_last_added_time', null);
}
wp_add_inline_script('abovewp-atc-popup', $script, 'before');
}
}
// Initialize the plugin
$abovewp_atc_popup_upsell = new AboveWP_ATC_Popup_Upsell();