Woocommerce强大的购物车管理扩展

功能包括储蓄,共享,自定义结帐字段和高级分析。

*具有可自定义位置的浮动迷你手推车
*购物车节省和共享功能
*自定义结帐字段经理
*详细的购物车分析和报告
*购物车放弃跟踪
*实时购物车更新
*针对客户的购物车笔记
*多个购物车模板
*移动友好的设计

相关文章: WooCommerce 自定义”加入购物车”按钮

<?php
/**
* Plugin Name: BDFG Advanced Cart
* Plugin URI: https://beiduofengou.net/2024/03/15/bdfg-advanced-cart/
* Description: A premium WooCommerce extension that enhances your store's cart experience with features like cart saving, sharing, real-time analytics, and a beautiful floating mini-cart.
* Version: 2.2.1
* Author: beiduofengou
* Author URI: https://beiduofengou.net
* Text Domain: bdfg-advanced-cart
* Domain Path: /languages
* Requires at least: 5.8
* Requires PHP: 7.4
* WC requires at least: 5.0
* WC tested up to: 8.0
* License: GPL v2 or later
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @link https://beiduofengou.net
*/

if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}

// Define BDFG_CART_VERSION
define('BDFG_CART_VERSION', '2.2.1');
define('BDFG_CART_FILE', __FILE__);
define('BDFG_CART_PATH', plugin_dir_path(BDFG_CART_FILE));
define('BDFG_CART_URL', plugin_dir_url(BDFG_CART_FILE));

if (!class_exists('BDFG_Advanced_Cart')):

class BDFG_Advanced_Cart {

/**
* Single instance of the class
*
* @var BDFG_Advanced_Cart
*/
private static $instance = null;

/**
* Main plugin path
*
* @var string
*/
private $plugin_path;

/**
* Plugin URL for asset loading
*
* @var string
*/
private $plugin_url;

/**
* Plugin components
*
* @var array
*/
private $components = array();

/**
* Returns the singleton instance of this class
*
* @return BDFG_Advanced_Cart
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}

/**
* Protected constructor to prevent direct object creation
*/
protected function __construct() {
$this->plugin_path = BDFG_CART_PATH;
$this->plugin_url = BDFG_CART_URL;

// Initialize early hooks
add_action('plugins_loaded', array($this, 'init'), 0);

// Register activation and deactivation hooks
register_activation_hook(BDFG_CART_FILE, array($this, 'activate'));
register_deactivation_hook(BDFG_CART_FILE, array($this, 'deactivate'));
}

/**
* Initialize plugin components and hooks
*/
public function init() {
// First check if WooCommerce is active
if (!$this->check_woocommerce()) {
return;
}

// Load text domain for translations
$this->load_textdomain();

// Initialize components
$this->init_components();

// Set up hooks
$this->init_hooks();

// Let other plugins know we're ready
do_action('bdfg_cart_loaded');
}

/**
* Check if WooCommerce is active and compatible
*
* @return bool
*/
private function check_woocommerce() {
if (!class_exists('WooCommerce')) {
add_action('admin_notices', array($this, 'woocommerce_missing_notice'));
return false;
}

// Add version compatibility check if needed
return true;
}

/**
* Load plugin text domain
*/
private function load_textdomain() {
load_plugin_textdomain(
'bdfg-advanced-cart',
false,
dirname(plugin_basename(BDFG_CART_FILE)) . '/languages'
);
}

/**
* Initialize plugin components
*/
private function init_components() {
// Include core files
require_once $this->plugin_path . 'includes/class-bdfg-cart-manager.php';
require_once $this->plugin_path . 'includes/class-bdfg-checkout-fields.php';
require_once $this->plugin_path . 'includes/class-bdfg-mini-cart.php';
require_once $this->plugin_path . 'includes/class-bdfg-cart-analytics.php';

// Initialize components
$this->components['cart_manager'] = new BDFG_Cart_Manager();
$this->components['checkout_fields'] = new BDFG_Checkout_Fields();
$this->components['mini_cart'] = new BDFG_Mini_Cart();
$this->components['analytics'] = new BDFG_Cart_Analytics();

// Load admin if needed
if (is_admin()) {
require_once $this->plugin_path . 'admin/class-bdfg-admin.php';
$this->components['admin'] = new BDFG_Admin();
}
}

/**
* Set up plugin hooks
*/
private function init_hooks() {
// Assets
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));

// Add settings link to plugins page
add_filter('plugin_action_links_' . plugin_basename(BDFG_CART_FILE), array($this, 'add_plugin_links'));
}

/**
* Plugin activation tasks
*/
public function activate() {
// Create necessary database tables
if (isset($this->components['analytics'])) {
$this->components['analytics']->create_tables();
}

// Set default options
$this->set_default_options();

// Clear transients
$this->clear_transients();

// Let other plugins know we've activated
do_action('bdfg_cart_activated');
}

/**
* Plugin deactivation tasks
*/
public function deactivate() {
// Clear any scheduled hooks
wp_clear_scheduled_hooks('bdfg_daily_cleanup');

// Clear transients
$this->clear_transients();

// Let other plugins know we're deactivating
do_action('bdfg_cart_deactivated');
}

/**
* Set default plugin options
*/
private function set_default_options() {
$defaults = array(
'bdfg_mini_cart_position' => 'right',
'bdfg_cart_expiry_days' => 30,
'bdfg_enable_cart_sharing' => 'yes',
'bdfg_popup_cart_template' => 'default',
'bdfg_analytics_enabled' => 'yes',
'bdfg_cart_threshold' => 0
);

foreach ($defaults as $key => $value) {
if (false === get_option($key)) {
update_option($key, $value);
}
}
}

/**
* Clear plugin transients
*/
private function clear_transients() {
global $wpdb;

$wpdb->query(
"DELETE FROM $wpdb->options
WHERE option_name LIKE '_transient_bdfg_%'
OR option_name LIKE '_transient_timeout_bdfg_%'"
);
}

/**
* Enqueue frontend assets
*/
public function enqueue_frontend_assets() {
if (is_cart() || is_checkout()) {
wp_enqueue_style(
'bdfg-frontend',
$this->plugin_url . 'assets/css/frontend.min.css',
array(),
BDFG_CART_VERSION
);

wp_enqueue_script(
'bdfg-frontend',
$this->plugin_url . 'assets/js/frontend.min.js',
array('jquery'),
BDFG_CART_VERSION,
true
);

wp_localize_script('bdfg-frontend', 'bdfgCart', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg-cart-nonce'),
'i18n' => array(
'cartSaved' => __('Cart saved successfully!', 'bdfg-advanced-cart'),
'cartRestored' => __('Cart restored successfully!', 'bdfg-advanced-cart'),
'error' => __('An error occurred. Please try again.', 'bdfg-advanced-cart')
)
));
}
}

/**
* Enqueue admin assets
*/
public function enqueue_admin_assets($hook) {
// Only load on our settings page
if ('woocommerce_page_bdfg-settings' !== $hook) {
return;
}

wp_enqueue_style(
'bdfg-admin',
$this->plugin_url . 'assets/css/admin.min.css',
array(),
BDFG_CART_VERSION
);

wp_enqueue_script(
'bdfg-admin',
$this->plugin_url . 'assets/js/admin.min.js',
array('jquery'),
BDFG_CART_VERSION,
true
);
}

/**
* Add plugin action links
*
* @param array $links
* @return array
*/
public function add_plugin_links($links) {
$plugin_links = array(
'<a href="' . admin_url('admin.php?page=bdfg-settings') . '">' .
__('Settings', 'bdfg-advanced-cart') . '</a>',
'<a href="https://beiduofengou.net/docs/bdfg-advanced-cart" target="_blank">' .
__('Documentation', 'bdfg-advanced-cart') . '</a>'
);
return array_merge($plugin_links, $links);
}

/**
* Display WooCommerce missing notice
*/
public function woocommerce_missing_notice() {
?>
<div class="error">
<p>
<?php
printf(
__('%s requires WooCommerce to be installed and activated. You can download %s here.', 'bdfg-advanced-cart'),
'<strong>BDFG Advanced Cart</strong>',
'<a href="https://wordpress.org/plugins/woocommerce/" target="_blank">WooCommerce</a>'
);
?>
</p>
</div>
<?php
}

/**
* Get plugin path
*
* @return string
*/
public function get_plugin_path() {
return $this->plugin_path;
}

/**
* Get plugin URL
*
* @return string
*/
public function get_plugin_url() {
return $this->plugin_url;
}

/**
* Get a component instance
*
* @param string $component
* @return object|null
*/
public function get_component($component) {
return isset($this->components[$component]) ? $this->components[$component] : null;
}
}

endif;

/**
* Returns the main instance of BDFG_Advanced_Cart
*
* @return BDFG_Advanced_Cart
*/
function BDFG_Advanced_Cart() {
return BDFG_Advanced_Cart::get_instance();
}

// Initialize the plugin
BDFG_Advanced_Cart();

includes/class-bdfg-cart-manager.php

<?php
/**
* Cart Manager Class
*
* Handles advanced cart management features including saving, sharing,
* and restoring cart states.
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @link https://beiduofengou.net
*/

if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}

class BDFG_Cart_Manager {

/**
* Database table name for saved carts
*
* @var string
*/
private $table_name;

/**
* Cart expiry period in days
*
* @var int
*/
private $expiry_days;

/**
* Constructor
*/
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'bdfg_saved_carts';
$this->expiry_days = get_option('bdfg_cart_expiry_days', 30);

$this->init_hooks();
}

/**
* Initialize hooks
*/
private function init_hooks() {
// AJAX handlers
add_action('wp_ajax_bdfg_save_cart', array($this, 'ajax_save_cart'));
add_action('wp_ajax_bdfg_restore_cart', array($this, 'ajax_restore_cart'));
add_action('wp_ajax_bdfg_share_cart', array($this, 'ajax_share_cart'));
add_action('wp_ajax_bdfg_delete_saved_cart', array($this, 'ajax_delete_saved_cart'));

// Frontend display
add_action('woocommerce_after_cart_table', array($this, 'add_cart_actions'));
add_action('woocommerce_before_cart', array($this, 'handle_cart_actions'));

// Cleanup
add_action('bdfg_daily_cleanup', array($this, 'cleanup_expired_carts'));

// Integration hooks
add_filter('bdfg_cart_save_data', array($this, 'prepare_cart_data'), 10, 2);
add_action('bdfg_after_cart_restore', array($this, 'trigger_cart_restored'), 10, 2);
}

/**
* Add cart action buttons
*/
public function add_cart_actions() {
if (!is_user_logged_in()) {
return;
}

$saved_carts = $this->get_user_saved_carts(get_current_user_id());

// Include template
include BDFG_CART_PATH . 'templates/cart/save-cart-buttons.php';
}

/**
* Handle cart actions from POST requests
*/
public function handle_cart_actions() {
if (!isset($_POST['bdfg_cart_action']) || !wp_verify_nonce($_POST['bdfg_cart_nonce'], 'bdfg_cart_action')) {
return;
}

$action = sanitize_text_field($_POST['bdfg_cart_action']);

switch ($action) {
case 'save':
$this->save_current_cart();
break;
case 'restore':
if (isset($_POST['cart_key'])) {
$this->restore_cart(sanitize_text_field($_POST['cart_key']));
}
break;
case 'delete':
if (isset($_POST['cart_key'])) { $this->delete_saved_cart(sanitize_text_field($_POST['cart_key']));
}
break;
}
}

/**
* AJAX handler for saving cart
*/
public function ajax_save_cart() {
check_ajax_referer('bdfg-cart-nonce', 'nonce');

if (!is_user_logged_in()) {
wp_send_json_error(array(
'message' => __('Please log in to save your cart.', 'bdfg-advanced-cart')
));
}

$cart_name = isset($_POST['cart_name']) ? sanitize_text_field($_POST['cart_name']) : '';
$result = $this->save_current_cart($cart_name);

if (is_wp_error($result)) {
wp_send_json_error(array(
'message' => $result->get_error_message()
));
}

wp_send_json_success(array(
'message' => __('Cart saved successfully!', 'bdfg-advanced-cart'),
'cart_key' => $result
));
}

/**
* Save current cart state
*
* @param string $cart_name Optional name for the saved cart
* @return string|WP_Error Cart key if successful, WP_Error on failure
*/
private function save_current_cart($cart_name = '') {
global $wpdb;

if (WC()->cart->is_empty()) {
return new WP_Error(
'empty_cart',
__('Cannot save an empty cart.', 'bdfg-advanced-cart')
);
}

$user_id = get_current_user_id();
$cart_key = $this->generate_cart_key();

// Prepare cart data
$cart_data = array(
'items' => WC()->cart->get_cart_for_session(),
'applied_coupons' => WC()->cart->get_applied_coupons(),
'cart_totals' => WC()->cart->get_totals(),
'name' => $cart_name,
'timestamp' => current_time('mysql')
);

// Allow plugins to modify cart data before saving
$cart_data = apply_filters('bdfg_cart_save_data', $cart_data, $user_id);

$result = $wpdb->insert(
$this->table_name,
array(
'user_id' => $user_id,
'cart_key' => $cart_key,
'cart_data' => wp_json_encode($cart_data),
'created_at' => current_time('mysql'),
'expires_at' => date('Y-m-d H:i:s', strtotime("+{$this->expiry_days} days")),
'cart_name' => $cart_name
),
array(
'%d',
'%s',
'%s',
'%s',
'%s',
'%s'
)
);

if (!$result) {
return new WP_Error(
'save_failed',
__('Failed to save cart. Please try again.', 'bdfg-advanced-cart')
);
}

do_action('bdfg_cart_saved', $cart_key, $user_id);

return $cart_key;
}

/**
* Get saved cart by key
*
* @param string $cart_key
* @return object|null Cart data if found, null if not
*/
private function get_saved_cart($cart_key) {
global $wpdb;

$cart = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$this->table_name}
WHERE cart_key = %s
AND expires_at > NOW()",
$cart_key
));

if (!$cart) {
return null;
}

$cart->cart_data = json_decode($cart->cart_data, true);
return $cart;
}

/**
* Get all saved carts for a user
*
* @param int $user_id
* @return array
*/
public function get_user_saved_carts($user_id) {
global $wpdb;

$carts = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$this->table_name}
WHERE user_id = %d
AND expires_at > NOW()
ORDER BY created_at DESC",
$user_id
));

foreach ($carts as &$cart) {
$cart->cart_data = json_decode($cart->cart_data, true);
}

return $carts;
}

/**
* Restore a saved cart
*
* @param string $cart_key
* @return bool|WP_Error True on success, WP_Error on failure
*/
public function restore_cart($cart_key) {
$saved_cart = $this->get_saved_cart($cart_key);

if (!$saved_cart) {
return new WP_Error(
'invalid_cart',
__('Cart not found or expired.', 'bdfg-advanced-cart')
);
}

// Clear current cart
WC()->cart->empty_cart();

// Restore cart items
foreach ($saved_cart->cart_data['items'] as $cart_item_key => $cart_item) {
WC()->cart->add_to_cart(
$cart_item['product_id'],
$cart_item['quantity'],
$cart_item['variation_id'],
$cart_item['variation'],
$cart_item
);
}

// Restore coupons
foreach ($saved_cart->cart_data['applied_coupons'] as $coupon) {
WC()->cart->apply_coupon($coupon);
}

do_action('bdfg_cart_restored', $cart_key, $saved_cart);

return true;
}

/**
* Delete a saved cart
*
* @param string $cart_key
* @return bool
*/
public function delete_saved_cart($cart_key) {
global $wpdb;

$result = $wpdb->delete(
$this->table_name,
array('cart_key' => $cart_key),
array('%s')
);

if ($result) {
do_action('bdfg_cart_deleted', $cart_key);
return true;
}

return false;
}

/**
* Generate a unique cart key
*
* @return string
*/
private function generate_cart_key() {
return wp_generate_password(12, false);
}

/**
* Clean up expired carts
*/
public function cleanup_expired_carts() {
global $wpdb;

$wpdb->query(
"DELETE FROM {$this->table_name}
WHERE expires_at < NOW()"
);

do_action('bdfg_expired_carts_cleaned');
}

/**
* Share cart via email
*
* @param string $cart_key
* @param string $email
* @return bool|WP_Error
*/
public function share_cart($cart_key, $email) {
$saved_cart = $this->get_saved_cart($cart_key);

if (!$saved_cart) {
return new WP_Error(
'invalid_cart',
__('Cart not found or expired.', 'bdfg-advanced-cart')
);
}

$share_url = add_query_arg(array(
'bdfg_restore_cart' => $cart_key
), wc_get_cart_url());

$mailer = WC()->mailer();
$recipient = sanitize_email($email);
$subject = sprintf(
__('[%s] Shared Shopping Cart', 'bdfg-advanced-cart'),
get_bloginfo('name')
);

// Get email template
ob_start();
include BDFG_CART_PATH . 'templates/emails/shared-cart.php';
$content = ob_get_clean();

$headers = "Content-Type: text/html\r\n";

$result = $mailer->send($recipient, $subject, $content, $headers);

if ($result) {
do_action('bdfg_cart_shared', $cart_key, $email);
return true;
}

return new WP_Error(
'share_failed',
__('Failed to share cart. Please try again.', 'bdfg-advanced-cart')
);
}
}

includes/class-bdfg-checkout-fields.php

相关文章: WooCommerce 快速下单插件

<?php
/**
* Checkout Fields Manager Class
*
* Handles custom checkout fields functionality with dynamic field creation,
* validation, and storage.
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @link https://beiduofengou.net
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}

class BDFG_Checkout_Fields {

/**
* Custom fields configuration
*
* @var array
*/
private $custom_fields;

/**
* Field types supported
*
* @var array
*/
private $supported_types = array(
'text' => 'Text',
'textarea' => 'Textarea',
'select' => 'Select',
'radio' => 'Radio',
'checkbox' => 'Checkbox',
'date' => 'Date',
'phone' => 'Phone',
'email' => 'Email'
);

/**
* Constructor
*/
public function __construct() {
$this->custom_fields = $this->get_stored_fields();
$this->init_hooks();
}

/**
* Initialize hooks
*/
private function init_hooks() {
// Checkout form modifications
add_filter('woocommerce_checkout_fields', array($this, 'modify_checkout_fields'), 20);
add_action('woocommerce_checkout_process', array($this, 'validate_custom_fields'));
add_action('woocommerce_checkout_update_order_meta', array($this, 'save_custom_fields'));

// Admin order display
add_action('woocommerce_admin_order_data_after_billing_address', array($this, 'display_custom_fields_in_admin'));
add_action('woocommerce_admin_order_data_after_shipping_address', array($this, 'display_custom_fields_in_admin'));

// Email display
add_action('woocommerce_email_after_order_table', array($this, 'display_custom_fields_in_email'), 10, 4);

// AJAX handlers for admin
add_action('wp_ajax_bdfg_save_custom_field', array($this, 'ajax_save_custom_field'));
add_action('wp_ajax_bdfg_delete_custom_field', array($this, 'ajax_delete_custom_field'));
}

/**
* Get stored custom fields
*
* @return array
*/
private function get_stored_fields() {
$fields = get_option('bdfg_custom_checkout_fields', array());
return is_array($fields) ? $fields : array();
}

/**
* Modify checkout fields
*
* @param array $fields
* @return array
*/
public function modify_checkout_fields($fields) {
foreach ($this->custom_fields as $field) {
$section = $field['section']; // billing, shipping, or order

if (!isset($fields[$section])) {
continue;
}

$field_id = sanitize_title($field['name']);

$fields[$section][$field_id] = $this->prepare_field_config($field);
}

return $fields;
}

/**
* Prepare field configuration
*
* @param array $field
* @return array
*/
private function prepare_field_config($field) {
$config = array(
'type' => $field['type'],
'label' => wp_kses_post($field['label']),
'placeholder' => isset($field['placeholder']) ? wp_kses_post($field['placeholder']) : '',
'required' => !empty($field['required']),
'class' => isset($field['class']) ? (array) $field['class'] : array('form-row-wide'),
'priority' => isset($field['priority']) ? absint($field['priority']) : 100
);

// Add options for select and radio types
if (in_array($field['type'], array('select', 'radio')) && !empty($field['options'])) {
$config['options'] = $this->parse_field_options($field['options']);
}

// Add custom validation if specified
if (!empty($field['validation'])) {
$config['validate'] = (array) $field['validation'];
}

return apply_filters('bdfg_checkout_field_config', $config, $field);
}

/**
* Parse field options from string to array
*
* @param string $options
* @return array
*/
private function parse_field_options($options) {
$result = array();
$options = explode("\n", $options);

foreach ($options as $option) {
$option = trim($option);
if (strpos($option, '|') !== false) {
list($key, $value) = explode('|', $option);
$result[trim($key)] = trim($value);
} else {
$result[trim($option)] = trim($option);
}
}

return $result;
}

/**
* Validate custom fields during checkout
*/
public function validate_custom_fields() {
foreach ($this->custom_fields as $field) {
$field_id = sanitize_title($field['name']);

if (!empty($field['required']) && empty($_POST[$field_id])) {
wc_add_notice(
sprintf(
__('%s is a required field.', 'bdfg-advanced-cart'),
$field['label']
),
'error'
);
continue;
}

if (!empty($_POST[$field_id]) && !empty($field['validation'])) {
$this->validate_field_value($_POST[$field_id], $field);
}
}
}

/**
* Validate individual field value
*
* @param mixed $value
* @param array $field
*/
private function validate_field_value($value, $field) {
switch ($field['validation']) {
case 'email':
if (!is_email($value)) {
wc_add_notice(
sprintf(
__('%s is not a valid email address.', 'bdfg-advanced-cart'),
$field['label']
),
'error'
);
}
break;

case 'phone':
if (!preg_match('/^[0-9\-\(\)\/\+\s]*$/', $value)) {
wc_add_notice(
sprintf(
__('%s is not a valid phone number.', 'bdfg-advanced-cart'),
$field['label']
),
'error'
);
}
break;

case 'numeric':
if (!is_numeric($value)) {
wc_add_notice(
sprintf(
__('%s must be a number.', 'bdfg-advanced-cart'),
$field['label']
),
'error'
);
}
break;

default:
do_action('bdfg_validate_custom_field', $value, $field);
break;
}
}

/**
* Save custom field values to order
*
* @param int $order_id
*/
public function save_custom_fields($order_id) {
foreach ($this->custom_fields as $field) {
$field_id = sanitize_title($field['name']);

if (isset($_POST[$field_id])) {
$value = $this->sanitize_field_value($_POST[$field_id], $field['type']);
update_post_meta($order_id, '_' . $field_id, $value);
}
}
}

/**
* Sanitize field value based on type
*
* @param mixed $value
* @param string $type
* @return mixed
*/
private function sanitize_field_value($value, $type) {
switch ($type) {
case 'textarea':
return sanitize_textarea_field($value);

case 'email':
return sanitize_email($value);

case 'checkbox':
return wc_string_to_bool($value);

default:
return sanitize_text_field($value);
}
}

/**
* Display custom fields in admin order page
*
* @param WC_Order $order
*/
public function display_custom_fields_in_admin($order) {
foreach ($this->custom_fields as $field) {
$field_id = sanitize_title($field['name']);
$value = get_post_meta($order->get_id(), '_' . $field_id, true);

if ($value) {
echo '<p><strong>' . esc_html($field['label']) . ':</strong> ' .
$this->format_field_value($value, $field) . '</p>';
}
}
}

/**
* Format field value for display
*
* @param mixed $value
* @param array $field
* @return string
*/
private function format_field_value($value, $field) {
switch ($field['type']) {
case 'checkbox':
return $value ? __('Yes', 'bdfg-advanced-cart') : __('No', 'bdfg-advanced-cart');

case 'select':
case 'radio':
$options = $this->parse_field_options($field['options']);
return isset($options[$value]) ? esc_html($options[$value]) : esc_html($value);

default:
return esc_html($value);
}
}

/**
* Display custom fields in order emails
*
* @param WC_Order $order
* @param bool $sent_to_admin
* @param bool $plain_text
* @param WC_Email $email
*/
public function display_custom_fields_in_email($order, $sent_to_admin, $plain_text, $email) {
$has_fields = false;

foreach ($this->custom_fields as $field) {
$field_id = sanitize_title($field['name']);
$value = get_post_meta($order->get_id(), '_' . $field_id, true);

if ($value) {
if (!$has_fields) {
echo '<h2>' . __('Additional Information', 'bdfg-advanced-cart') . '</h2>';
$has_fields = true;
}

echo '<p><strong>' . esc_html($field['label']) . ':</strong> ' .
$this->format_field_value($value, $field) . '</p>';
}
}
}

/**
* Add a new custom field
*
* @param array $field_data
* @return bool|WP_Error
*/
public function add_custom_field($field_data) {
if (empty($field_data['name']) || empty($field_data['label'])) {
return new WP_Error(
'invalid_field',
__('Field name and label are required.', 'bdfg-advanced-cart')
);
}

$field_id = sanitize_title($field_data['name']);

// Check for duplicate field names
foreach ($this->custom_fields as $field) {
if (sanitize_title($field['name']) === $field_id) {
return new WP_Error(
'duplicate_field',
__('A field with this name already exists.', 'bdfg-advanced-cart')
);
}
}

$this->custom_fields[] = wp_parse_args($field_data, array(
'type' => 'text',
'placeholder' => '',
'required' => false,
'class' => array('form-row-wide'),
'priority' => 100,
'validation' => '',
'section' => 'billing'
));

return $this->save_fields();
}

/**
* Save fields configuration
*
* @return bool
*/
private function save_fields() {
return update_option('bdfg_custom_checkout_fields', $this->custom_fields);
}
}

includes/class-bdfg-mini-cart.php

<?php
/**
* Mini Cart Class
*
* Handles mini cart and popup cart functionality with enhanced features
* and responsive design.
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @link https://beiduofengou.net
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}

class BDFG_Mini_Cart {
/**
* Cart settings
*
* @var array
*/
private $settings;

/**
* Constructor
*/
public function __construct() {
$this->settings = array(
'position' => get_option('bdfg_mini_cart_position', 'right'),
'template' => get_option('bdfg_popup_cart_template', 'default'),
'auto_open' => get_option('bdfg_auto_open_cart', 'yes'),
'cart_threshold' => absint(get_option('bdfg_cart_threshold', 0)),
'enable_cart_notes' => get_option('bdfg_enable_cart_notes', 'yes')
);

$this->init_hooks();
}

/**
* Initialize hooks
*/
private function init_hooks() {
// Cart display
add_action('wp_footer', array($this, 'render_mini_cart'));
add_filter('woocommerce_add_to_cart_fragments', array($this, 'cart_fragments'));

// AJAX handlers
add_action('wp_ajax_bdfg_get_mini_cart', array($this, 'get_mini_cart_content'));
add_action('wp_ajax_nopriv_bdfg_get_mini_cart', array($this, 'get_mini_cart_content'));
add_action('wp_ajax_bdfg_update_cart_note', array($this, 'update_cart_note'));

// Assets
add_action('wp_enqueue_scripts', array($this, 'enqueue_assets'));

// Cart modifications
add_filter('woocommerce_cart_item_name', array($this, 'add_product_image'), 10, 3);
add_action('woocommerce_after_mini_cart', array($this, 'add_cart_notes'));
}

/**
* Enqueue required assets
*/
public function enqueue_assets() {
if (is_cart() || is_checkout()) {
return;
}

wp_enqueue_style(
'bdfg-mini-cart',
BDFG_CART_URL . 'assets/css/mini-cart.min.css',
array(),
BDFG_CART_VERSION
);

wp_enqueue_script(
'bdfg-mini-cart',
BDFG_CART_URL . 'assets/js/mini-cart.min.js',
array('jquery', 'wc-cart-fragments'),
BDFG_CART_VERSION,
true
);

wp_localize_script('bdfg-mini-cart', 'bdfgMiniCart', array(
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg-mini-cart'),
'position' => $this->settings['position'],
'autoOpen' => $this->settings['auto_open'],
'cartThreshold' => $this->settings['cart_threshold'],
'i18n' => array(
'empty' => __('Your cart is empty', 'bdfg-advanced-cart'),
'countdown' => __('Checkout within %s minutes to secure your items', 'bdfg-advanced-cart'),
'noteUpdated' => __('Cart note updated', 'bdfg-advanced-cart')
)
));
}

/**
* Render mini cart in footer
*/
public function render_mini_cart() {
if (is_cart() || is_checkout()) {
return;
}

$template = 'mini-cart-' . $this->settings['template'] . '.php';
$template_path = $this->locate_template($template);

if ($template_path) {
include $template_path;
}
}

/**
* Locate template file
*
* @param string $template_name
* @return string
*/
private function locate_template($template_name) {
$template_path = locate_template('bdfg-advanced-cart/' . $template_name);

if (!$template_path) {
$template_path = BDFG_CART_PATH . 'templates/' . $template_name;
}

return file_exists($template_path) ? $template_path : false;
}

/**
* Update cart fragments via AJAX
*
* @param array $fragments
* @return array
*/
public function cart_fragments($fragments) {
ob_start();
$this->get_cart_content_html();
$fragments['.bdfg-mini-cart-content'] = ob_get_clean();

ob_start();
?>
<div class="bdfg-cart-count">
<?php echo WC()->cart->get_cart_contents_count(); ?>
</div>
<?php
$fragments['.bdfg-cart-count'] = ob_get_clean();

ob_start();
?>
<div class="bdfg-cart-total">
<?php echo WC()->cart->get_cart_total(); ?>
</div>
<?php
$fragments['.bdfg-cart-total'] = ob_get_clean();

return $fragments;
}

/**
* Get mini cart content via AJAX
*/
public function get_mini_cart_content() {
check_ajax_referer('bdfg-mini-cart', 'nonce');

ob_start();
$this->get_cart_content_html();
$cart_html = ob_get_clean();

wp_send_json_success(array(
'html' => $cart_html,
'count' => WC()->cart->get_cart_contents_count(),
'total' => WC()->cart->get_cart_total()
));
}

/**
* Generate cart content HTML
*/
private function get_cart_content_html() {
$cart_items = WC()->cart->get_cart();

if (empty($cart_items)) {
?>
<div class="bdfg-mini-cart-empty">
<p><?php _e('Your cart is empty', 'bdfg-advanced-cart'); ?></p>
<a href="<?php echo esc_url(wc_get_page_permalink('shop')); ?>" class="button">
<?php _e('Continue Shopping', 'bdfg-advanced-cart'); ?>
</a>
</div>
<?php
return;
}

foreach ($cart_items as $cart_item_key => $cart_item) {
$_product = apply_filters('woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key);

if (!$_product || !$_product->exists() || $cart_item['quantity'] == 0) {
continue;
}

?>
<div class="bdfg-mini-cart-item" data-key="<?php echo esc_attr($cart_item_key); ?>">
<div class="bdfg-cart-item-image">
<?php echo $_product->get_image(); ?>
</div>
<div class="bdfg-cart-item-details">
<h4><?php echo wp_kses_post($_product->get_name()); ?></h4>
<div class="bdfg-cart-item-price">
<?php echo WC()->cart->get_product_price($_product); ?>
</div>
<div class="bdfg-cart-item-quantity">
<input type="number"
min="0"
max="<?php echo esc_attr($_product->get_max_purchase_quantity()); ?>"
value="<?php echo esc_attr($cart_item['quantity']); ?>"
class="bdfg-qty-input">
</div>
</div>
<a href="#" class="bdfg-remove-item" data-key="<?php echo esc_attr($cart_item_key); ?>">
<span class="dashicons dashicons-no-alt"></span>
</a>
</div>
<?php
}

// Cart subtotal and buttons
?>
<div class="bdfg-mini-cart-footer">
<div class="bdfg-cart-subtotal">
<strong><?php _e('Subtotal:', 'bdfg-advanced-cart'); ?></strong>
<?php echo WC()->cart->get_cart_subtotal(); ?>
</div>
<div class="bdfg-cart-buttons">
<a href="<?php echo esc_url(wc_get_cart_url()); ?>" class="button">
<?php _e('View Cart', 'bdfg-advanced-cart'); ?>
</a>
<a href="<?php echo esc_url(wc_get_checkout_url()); ?>" class="button checkout">
<?php _e('Checkout', 'bdfg-advanced-cart'); ?>
</a>
</div>
</div>
<?php
}

/**
* Add product image to cart item name
*
* @param string $name
* @param array $cart_item
* @param string $cart_item_key
* @return string
*/
public function add_product_image($name, $cart_item, $cart_item_key) {
if (!is_cart() && !is_checkout()) {
$_product = apply_filters('woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key);
if ($_product) {
$thumbnail = $_product->get_image(array(50, 50));
$name = '<div class="bdfg-cart-item-thumb">' . $thumbnail . '</div>' . $name;
}
}
return $name;
}

/**
* Add cart notes section
*/
public function add_cart_notes() {
if ('yes' !== $this->settings['enable_cart_notes']) {
return;
}

$note = WC()->session->get('cart_note');
?>
<div class="bdfg-cart-notes">
<textarea placeholder="<?php esc_attr_e('Add a note about your order', 'bdfg-advanced-cart'); ?>"
class="bdfg-cart-note"><?php echo esc_textarea($note); ?></textarea>
</div>
<?php
}

/**
* Update cart note via AJAX
*/
public function update_cart_note() {
check_ajax_referer('bdfg-mini-cart', 'nonce');

$note = isset($_POST['note']) ? sanitize_textarea_field($_POST['note']) : '';
WC()->session->set('cart_note', $note);

wp_send_json_success(__('Cart note updated', 'bdfg-advanced-cart'));
}
}

includes/class-bdfg-cart-analytics.php

<?php
/**
* Cart Analytics Class
*
* Handles advanced cart analytics, tracking, and reporting functionality.
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @link https://beiduofengou.net
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}

class BDFG_Cart_Analytics {
/**
* Database table name
*
* @var string
*/
private $table_name;

/**
* Constructor
*/
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'bdfg_cart_analytics';

$this->init_hooks();
}

/**
* Initialize hooks
*/
private function init_hooks() {
// Track cart actions
add_action('woocommerce_add_to_cart', array($this, 'track_add_to_cart'), 10, 6);
add_action('woocommerce_remove_cart_item', array($this, 'track_remove_from_cart'), 10, 2);
add_action('woocommerce_cart_item_restored', array($this, 'track_cart_restore'), 10, 2);
add_action('woocommerce_cart_updated', array($this, 'track_cart_update'));

// Track cart abandonment
add_action('wp_login', array($this, 'track_user_return'), 10, 2);
add_action('woocommerce_cart_emptied', array($this, 'track_cart_cleared'));

// Admin reports
add_action('admin_menu', array($this, 'add_analytics_menu'));

// Cleanup
add_action('bdfg_daily_cleanup', array($this, 'cleanup_old_records'));
}

/**
* Create analytics table
*/
public function create_tables() {
global $wpdb;

$charset_collate = $wpdb->get_charset_collate();

$sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
id bigint(20) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NULL,
session_id varchar(32) NOT NULL,
action varchar(50) NOT NULL,
cart_total decimal(10,2) NOT NULL,
items_count int(11) NOT NULL,
product_id bigint(20) NULL,
variation_id bigint(20) NULL,
quantity int(11) NULL,
timestamp datetime NOT NULL,
meta longtext NULL,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY session_id (session_id),
KEY action (action),
KEY timestamp (timestamp)
) $charset_collate;";

require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}

/**
* Track item added to cart
*/
public function track_add_to_cart($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) {
$this->log_action('add_to_cart', array(
'product_id' => $product_id,
'variation_id' => $variation_id,
'quantity' => $quantity,
'cart_item_data' => $cart_item_data
));
}

/**
* Track item removed from cart
*/
public function track_remove_from_cart($cart_item_key, $cart) {
$cart_item = $cart->get_cart_item($cart_item_key);
if ($cart_item) {
$this->log_action('remove_from_cart', array(
'product_id' => $cart_item['product_id'],
'variation_id' => $cart_item['variation_id'],
'quantity' => $cart_item['quantity']
));
}
}

/**
* Log cart action
*/
private function log_action($action, $meta = array()) {
global $wpdb;

$user_id = get_current_user_id();
$session_id = WC()->session->get_customer_id();

$data = array(
'user_id' => $user_id ? $user_id : null,
'session_id' => $session_id,
'action' => $action,
'cart_total' => WC()->cart->get_cart_contents_total(),
'items_count' => WC()->cart->get_cart_contents_count(),
'timestamp' => current_time('mysql'),
'meta' => wp_json_encode($meta)
);

if (isset($meta['product_id'])) {
$data['product_id'] = $meta['product_id'];
}
if (isset($meta['variation_id'])) {
$data['variation_id'] = $meta['variation_id'];
}
if (isset($meta['quantity'])) {
$data['quantity'] = $meta['quantity'];
}

$wpdb->insert($this->table_name, $data);
}

/**
* Get cart abandonment rate
*/
public function get_cart_abandonment_rate($days = 30) {
global $wpdb;

$start_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));

$total_carts = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(DISTINCT session_id)
FROM {$this->table_name}
WHERE timestamp >= %s
AND action IN ('add_to_cart', 'remove_from_cart')",
$start_date
));

$completed_carts = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(DISTINCT session_id)
FROM {$this->table_name} t
JOIN {$wpdb->prefix}wc_order_stats o ON t.session_id = o.customer_id
WHERE t.timestamp >= %s",
$start_date
));

if ($total_carts == 0) {
return 0;
}

return (($total_carts - $completed_carts) / $total_carts) * 100;
}

/**
* Get popular abandoned products
*/
public function get_popular_abandoned_products($limit = 10) {
global $wpdb;

return $wpdb->get_results($wpdb->prepare(
"SELECT p.ID, p.post_title,
COUNT(DISTINCT t.session_id) as abandonment_count,
SUM(t.quantity) as total_quantity,
AVG(t.cart_total) as average_cart_total
FROM {$this->table_name} t
JOIN {$wpdb->posts} p ON t.product_id = p.ID
WHERE t.action = 'add_to_cart'
AND NOT EXISTS (
SELECT 1 FROM {$wpdb->prefix}wc_order_stats o
WHERE t.session_id = o.customer_id
)
GROUP BY p.ID
ORDER BY abandonment_count DESC, total_quantity DESC
LIMIT %d",
$limit
));
}

/**
* Get cart conversion data
*
* @param int $days Number of days to analyze
* @return array
*/
public function get_cart_conversion_data($days = 30) {
global $wpdb;

$start_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));

return array(
'total_carts' => $this->get_total_carts($start_date),
'converted_carts' => $this->get_converted_carts($start_date),
'average_value' => $this->get_average_cart_value($start_date),
'peak_hours' => $this->get_peak_shopping_hours($start_date),
'top_products' => $this->get_top_cart_products($start_date)
);
}

/**
* Get total number of unique carts
*
* @param string $start_date
* @return int
*/
private function get_total_carts($start_date) {
global $wpdb;

return (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(DISTINCT session_id)
FROM {$this->table_name}
WHERE timestamp >= %s",
$start_date
));
}

/**
* Get number of converted carts
*
* @param string $start_date
* @return int
*/
private function get_converted_carts($start_date) {
global $wpdb;

return (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(DISTINCT t.session_id)
FROM {$this->table_name} t
JOIN {$wpdb->prefix}wc_order_stats o ON t.session_id = o.customer_id
WHERE t.timestamp >= %s",
$start_date
));
}

/**
* Get average cart value
*
* @param string $start_date
* @return float
*/
private function get_average_cart_value($start_date) {
global $wpdb;

return (float) $wpdb->get_var($wpdb->prepare(
"SELECT AVG(cart_total)
FROM {$this->table_name}
WHERE timestamp >= %s
AND action = 'add_to_cart'",
$start_date
));
}

/**
* Get peak shopping hours
*
* @param string $start_date
* @return array
*/
private function get_peak_shopping_hours($start_date) {
global $wpdb;

return $wpdb->get_results($wpdb->prepare(
"SELECT HOUR(timestamp) as hour,
COUNT(*) as count
FROM {$this->table_name}
WHERE timestamp >= %s
AND action = 'add_to_cart'
GROUP BY HOUR(timestamp)
ORDER BY count DESC
LIMIT 5",
$start_date
));
}

/**
* Get top products added to carts
*
* @param string $start_date
* @return array
*/
private function get_top_cart_products($start_date) {
global $wpdb;

return $wpdb->get_results($wpdb->prepare(
"SELECT p.ID, p.post_title,
COUNT(*) as add_count,
SUM(t.quantity) as total_quantity
FROM {$this->table_name} t
JOIN {$wpdb->posts} p ON t.product_id = p.ID
WHERE t.timestamp >= %s
AND t.action = 'add_to_cart'
GROUP BY p.ID
ORDER BY add_count DESC
LIMIT 10",
$start_date
));
}

/**
* Add analytics menu to admin
*/
public function add_analytics_menu() {
add_submenu_page(
'woocommerce',
__('Cart Analytics', 'bdfg-advanced-cart'),
__('Cart Analytics', 'bdfg-advanced-cart'),
'manage_woocommerce',
'bdfg-cart-analytics',
array($this, 'render_analytics_page')
);
}

/**
* Render analytics admin page
*/
public function render_analytics_page() {
// Verify user permissions
if (!current_user_can('manage_woocommerce')) {
wp_die(__('You do not have sufficient permissions to access this page.', 'bdfg-advanced-cart'));
}

// Get time period from request
$period = isset($_GET['period']) ? absint($_GET['period']) : 30;
$valid_periods = array(7, 30, 90, 365);
$period = in_array($period, $valid_periods) ? $period : 30;

// Get analytics data
$analytics_data = $this->get_cart_conversion_data($period);

// Include template
include BDFG_CART_PATH . 'admin/views/analytics.php';
}

/**
* Cleanup old records
*/
public function cleanup_old_records() {
global $wpdb;

// Keep records for 1 year by default
$retention_days = apply_filters('bdfg_analytics_retention_days', 365);
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$retention_days} days"));

$wpdb->query($wpdb->prepare(
"DELETE FROM {$this->table_name}
WHERE timestamp < %s",
$cutoff_date
));

do_action('bdfg_analytics_cleanup_completed');
}

/**
* Export analytics data to CSV
*/
public function export_analytics_data() {
if (!current_user_can('manage_woocommerce')) {
wp_die(__('You do not have sufficient permissions to export data.', 'bdfg-advanced-cart'));
}

$start_date = isset($_POST['start_date']) ? sanitize_text_field($_POST['start_date']) : date('Y-m-d', strtotime('-30 days'));
$end_date = isset($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : date('Y-m-d');

global $wpdb;

$results = $wpdb->get_results($wpdb->prepare(
"SELECT *
FROM {$this->table_name}
WHERE DATE(timestamp) BETWEEN %s AND %s
ORDER BY timestamp DESC",
$start_date,
$end_date
));

if (!empty($results)) {
$filename = 'bdfg-cart-analytics-' . date('Y-m-d') . '.csv';
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '"');

$output = fopen('php://output', 'w');

// Add headers
fputcsv($output, array(
'ID',
'User ID',
'Session ID',
'Action',
'Cart Total',
'Items Count',
'Product ID',
'Variation ID',
'Quantity',
'Timestamp',
'Meta'
));

// Add data
foreach ($results as $row) {
fputcsv($output, (array) $row);
}

fclose($output);
exit;
}
}

/**
* Get real-time cart activity
*
* @param int $minutes Minutes to look back
* @return array
*/
public function get_realtime_activity($minutes = 15) {
global $wpdb;

$cutoff = date('Y-m-d H:i:s', strtotime("-{$minutes} minutes"));

return $wpdb->get_results($wpdb->prepare(
"SELECT t.*, p.post_title as product_name
FROM {$this->table_name} t
LEFT JOIN {$wpdb->posts} p ON t.product_id = p.ID
WHERE t.timestamp >= %s
ORDER BY t.timestamp DESC
LIMIT 50",
$cutoff
));
}
}

admin/views/analytics.php

<?php
/**
* Admin Analytics View
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit;
}
?>

<div class="wrap bdfg-analytics-wrap">
<h1><?php _e('BDFG Cart Analytics', 'bdfg-advanced-cart'); ?></h1>

<div class="bdfg-period-selector">
<form method="get">
<input type="hidden" name="page" value="bdfg-cart-analytics">
<select name="period" onchange="this.form.submit()">
<option value="7" <?php selected($period, 7); ?>><?php _e('Last 7 days', 'bdfg-advanced-cart'); ?></option>
<option value="30" <?php selected($period, 30); ?>><?php _e('Last 30 days', 'bdfg-advanced-cart'); ?></option>
<option value="90" <?php selected($period, 90); ?>><?php _e('Last 90 days', 'bdfg-advanced-cart'); ?></option>
<option value="365" <?php selected($period, 365); ?>><?php _e('Last year', 'bdfg-advanced-cart'); ?></option>
</select>
</form>
</div>

<div class="bdfg-analytics-grid">
<!-- Summary Cards -->
<div class="bdfg-card">
<h3><?php _e('Cart Conversion Rate', 'bdfg-advanced-cart'); ?></h3>
<div class="bdfg-card-value">
<?php
$conversion_rate = $analytics_data['converted_carts'] / $analytics_data['total_carts'] * 100;
echo number_format($conversion_rate, 2) . '%';
?>
</div>
</div>

<div class="bdfg-card">
<h3><?php _e('Average Cart Value', 'bdfg-advanced-cart'); ?></h3>
<div class="bdfg-card-value">
<?php echo wc_price($analytics_data['average_value']); ?>
</div>
</div>

<div class="bdfg-card">
<h3><?php _e('Total Carts', 'bdfg-advanced-cart'); ?></h3>
<div class="bdfg-card-value">
<?php echo number_format($analytics_data['total_carts']); ?>
</div>
</div>

<!-- Peak Hours Chart -->
<div class="bdfg-chart-container">
<h3><?php _e('Peak Shopping Hours', 'bdfg-advanced-cart'); ?></h3>
<canvas id="peakHoursChart"></canvas>
</div>

<!-- Top Products Table -->
<div class="bdfg-table-container">
<h3><?php _e('Top Products Added to Cart', 'bdfg-advanced-cart'); ?></h3>
<table class="widefat">
<thead>
<tr>
<th><?php _e('Product', 'bdfg-advanced-cart'); ?></th>
<th><?php _e('Add Count', 'bdfg-advanced-cart'); ?></th>
<th><?php _e('Total Quantity', 'bdfg-advanced-cart'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($analytics_data['top_products'] as $product): ?>
<tr>
<td>
<a href="<?php echo get_edit_post_link($product->ID); ?>">
<?php echo esc_html($product->post_title); ?>
</a>
</td>
<td><?php echo number_format($product->add_count); ?></td>
<td><?php echo number_format($product->total_quantity); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>

<!-- Export Section -->
<div class="bdfg-export-section">
<h3><?php _e('Export Data', 'bdfg-advanced-cart'); ?></h3>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="bdfg_export_analytics">
<?php wp_nonce_field('bdfg_export_analytics', 'bdfg_export_nonce'); ?>

<div class="date-range">
<label>
<?php _e('Start Date:', 'bdfg-advanced-cart'); ?>
<input type="date" name="start_date" value="<?php echo date('Y-m-d', strtotime('-30 days')); ?>">
</label>
<label>
<?php _e('End Date:', 'bdfg-advanced-cart'); ?>
<input type="date" name="end_date" value="<?php echo date('Y-m-d'); ?>">
</label>
</div>

<button type="submit" class="button button-primary">
<?php _e('Export to CSV', 'bdfg-advanced-cart'); ?>
</button>
</form>
</div>
</div>

templates/mini-cart-default.php

<?php
/**
* Mini Cart Template - Default Style
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit;
}
?>

<div class="bdfg-mini-cart-wrapper <?php echo esc_attr($this->settings['position']); ?>">
<div class="bdfg-mini-cart-trigger">
<span class="bdfg-cart-count"><?php echo WC()->cart->get_cart_contents_count(); ?></span>
<span class="dashicons dashicons-cart"></span>
</div>

<div class="bdfg-mini-cart-content">
<div class="bdfg-mini-cart-header">
<h3><?php _e('Your Cart', 'bdfg-advanced-cart'); ?></h3>
<button class="bdfg-close-cart">
<span class="dashicons dashicons-no-alt"></span>
</button>
</div>

<div class="bdfg-mini-cart-items">
<?php $this->get_cart_content_html(); ?>
</div>

<?php if ('yes' === $this->settings['enable_cart_notes']): ?>
<div class="bdfg-cart-notes">
<textarea placeholder="<?php esc_attr_e('Add a note about your order', 'bdfg-advanced-cart'); ?>"
class="bdfg-cart-note"><?php echo esc_textarea(WC()->session->get('cart_note')); ?></textarea>
</div>
<?php endif; ?>
</div>
</div>

assets/js/frontend.min.js

/* BDFG Advanced Cart Frontend Scripts */
(function($) {
'use strict';

const BDFG_Cart = {
init: function() {
this.miniCart = $('.bdfg-mini-cart-wrapper');
this.trigger = $('.bdfg-mini-cart-trigger');
this.content = $('.bdfg-mini-cart-content');
this.closeBtn = $('.bdfg-close-cart');
this.qtyInputs = $('.bdfg-qty-input');
this.removeButtons = $('.bdfg-remove-item');
this.cartNote = $('.bdfg-cart-note');

this.bindEvents();
this.initAutoOpen();
},

bindEvents: function() {
// Toggle mini cart
this.trigger.on('click', this.toggleCart.bind(this));
this.closeBtn.on('click', this.closeCart.bind(this));

// Update quantity
this.qtyInputs.on('change', this.updateQuantity.bind(this));

// Remove item
this.removeButtons.on('click', this.removeItem.bind(this));

// Cart note
this.cartNote.on('change', this.updateNote.bind(this));

// Close on outside click
$(document).on('click', (e) => {
if (!$(e.target).closest('.bdfg-mini-cart-wrapper').length) {
this.closeCart();
}
});
},

toggleCart: function(e) {
e.preventDefault();
this.miniCart.toggleClass('active');
},

closeCart: function() {
this.miniCart.removeClass('active');
},

updateQuantity: function(e) {
const input = $(e.target);
const qty = input.val();
const key = input.closest('.bdfg-mini-cart-item').data('key');

this.blockCart();

$.ajax({
url: bdfgMiniCart.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_update_quantity',
nonce: bdfgMiniCart.nonce,
key: key,
qty: qty
},
success: (response) => {
if (response.success) {
this.refreshCart();
}
},
complete: () => {
this.unblockCart();
}
});
},

removeItem: function(e) {
e.preventDefault();
const button = $(e.target).closest('.bdfg-remove-item');
const key = button.data('key');

this.blockCart();

$.ajax({
url: bdfgMiniCart.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_remove_item',
nonce: bdfgMiniCart.nonce,
key: key
},
success: (response) => {
if (response.success) {
this.refreshCart();
}
},
complete: () => {
this.unblockCart();
}
});
},

updateNote: function(e) {
const note = $(e.target).val();

$.ajax({
url: bdfgMiniCart.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_update_cart_note',
nonce: bdfgMiniCart.nonce,
note: note
},
success: (response) => {
if (response.success) {
this.showMessage(bdfgMiniCart.i18n.noteUpdated);
}
}
});
},

refreshCart: function() {
$.ajax({
url: bdfgMiniCart.ajaxUrl,
type: 'POST',
data: {
action: 'bdfg_get_mini_cart',
nonce: bdfgMiniCart.nonce
},
success: (response) => {
if (response.success) {
this.content.find('.bdfg-mini-cart-items').html(response.data.html);
this.updateCartCount(response.data.count);
}
}
});
},

blockCart: function() {
this.content.addClass('loading');
},

unblockCart: function() {
this.content.removeClass('loading');
},

showMessage: function(message) {
const messageEl = $('<div class="bdfg-message"></div>')
.text(message)
.appendTo(this.content);

setTimeout(() => {
messageEl.fadeOut(() => messageEl.remove());
}, 3000);
},

updateCartCount: function(count) {
this.trigger.find('.bdfg-cart-count').text(count);
},

initAutoOpen: function() {
if (bdfgMiniCart.autoOpen === 'yes' &&
WC()->cart.cart_contents_count >= bdfgMiniCart.cartThreshold) {
setTimeout(() => {
this.miniCart.addClass('active');
}, 1000);
}
}
};

$(document).ready(() => BDFG_Cart.init());

})(jQuery);

assets/css/frontend.min.css

/* BDFG Advanced Cart Styles */

.bdfg-mini-cart-wrapper {
position: fixed;
z-index: 999999;
}

.bdfg-mini-cart-wrapper.right {
right: 20px;
top: 50%;
transform: translateY(-50%);
}

.bdfg-mini-cart-wrapper.left {
left: 20px;
top: 50%;
transform: translateY(-50%);
}

.bdfg-mini-cart-trigger {
background: #2c2d33;
color: #fff;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
transition: all 0.3s ease;
}

.bdfg-mini-cart-trigger:hover {
background: #0073aa;
}

.bdfg-cart-count {
position: absolute;
top: -8px;
right: -8px;
background: #e94b35;
color: #fff;
font-size: 11px;
padding: 2px 6px;
border-radius: 10px;
min-width: 18px;
text-align: center;
}

.bdfg-mini-cart-content {
position: absolute;
top: 0;
background: #fff;
width: 320px;
padding: 20px;
box-shadow: 0 0 20px rgba(0,0,0,0.15);
border-radius: 8px;
display: none;
max-height: 80vh;
overflow-y: auto;
}

.right .bdfg-mini-cart-content {
right: 70px;
}

.left .bdfg-mini-cart-content {
left: 70px;
}

.bdfg-mini-cart-wrapper.active .bdfg-mini-cart-content {
display: block;
}

.bdfg-mini-cart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}

.bdfg-mini-cart-header h3 {
margin: 0;
font-size: 18px;
}

.bdfg-close-cart {
background: none;
border: none;
padding: 0;
cursor: pointer;
color: #666;
}

.bdfg-mini-cart-item {
display: flex;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}

.bdfg-cart-item-image {
width: 60px;
margin-right: 15px;
}

.bdfg-cart-item-image img {
width: 100%;
height: auto;
border-radius: 4px;
}

.bdfg-cart-item-details {
flex: 1;
}

.bdfg-cart-item-details h4 {
margin: 0 0 5px;
font-size: 14px;
font-weight: 500;
}

.bdfg-cart-item-price {
color: #0073aa;
font-weight: 600;
margin-bottom: 5px;
}

.bdfg-cart-item-quantity {
display: flex;
align-items: center;
}

.bdfg-qty-input {
width: 50px;
text-align: center;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}

.bdfg-remove-item {
color: #e94b35;
text-decoration: none;
margin-left: 10px;
}

.bdfg-mini-cart-footer {
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}

.bdfg-cart-subtotal {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
font-weight: 600;
}

.bdfg-cart-buttons {
display: flex;
gap: 10px;
}

.bdfg-cart-buttons .button {
flex: 1;
text-align: center;
padding: 12px;
border-radius: 4px;
text-decoration: none;
font-weight: 600;
}

.bdfg-cart-buttons .button:not(.checkout) {
background: #f5f5f5;
color: #333;
}

.bdfg-cart-buttons .checkout {
background: #0073aa;
color: #fff;
}

.bdfg-cart-notes {
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}

.bdfg-cart-note {
width: 100%;
min-height: 60px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}

.bdfg-message {
position: fixed;
bottom: 20px;
right: 20px;
background: #333;
color: #fff;
padding: 10px 20px;
border-radius: 4px;
z-index: 999999;
}

/* Loading State */
.bdfg-mini-cart-content.loading {
opacity: 0.7;
pointer-events: none;
}

/* Empty Cart State */
.bdfg-mini-cart-empty {
text-align: center;
padding: 30px 0;
}

.bdfg-mini-cart-empty p {
color: #666;
margin-bottom: 15px;
}

assets/css/admin.min.css

/* BDFG Advanced Cart Admin Styles */

.bdfg-analytics-wrap {
margin: 20px;
}

.bdfg-period-selector {
margin-bottom: 30px;
}

.bdfg-analytics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}

.bdfg-card {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.bdfg-card h3 {
margin: 0 0 15px;
color: #23282d;
font-size: 16px;
}

.bdfg-card-value {
font-size: 24px;
font-weight: 600;
color: #0073aa;
}

.bdfg-chart-container {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
grid-column: 1 / -1;
}

.bdfg-table-container {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
grid-column: 1 / -1;
}

.bdfg-export-section {
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-top: 20px;
}

.bdfg-export-section .date-range {
display: flex;
gap: 20px;
margin-bottom: 15px;
}

.bdfg-export-section label {
display: block;
margin-bottom: 5px;
}

admin/class-bdfg-admin.php

<?php
/**
* Admin Class
*
* Handles all admin-related functionality
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit;
}

class BDFG_Admin {
/**
* Constructor
*/
public function __construct() {
add_action('admin_menu', array($this, 'add_menu_items'));
add_action('admin_init', array($this, 'register_settings'));
add_filter('plugin_action_links_' . plugin_basename(BDFG_CART_FILE), array($this, 'add_plugin_links'));
}

/**
* Add menu items
*/
public function add_menu_items() {
add_submenu_page(
'woocommerce',
__('BDFG Cart Settings', 'bdfg-advanced-cart'),
__('BDFG Cart', 'bdfg-advanced-cart'),
'manage_woocommerce',
'bdfg-settings',
array($this, 'render_settings_page')
);
}

/**
* Register plugin settings
*/
public function register_settings() {
register_setting('bdfg_cart_settings', 'bdfg_mini_cart_position');
register_setting('bdfg_cart_settings', 'bdfg_cart_expiry_days');
register_setting('bdfg_cart_settings', 'bdfg_enable_cart_sharing');
register_setting('bdfg_cart_settings', 'bdfg_popup_cart_template');
register_setting('bdfg_cart_settings', 'bdfg_analytics_enabled');
register_setting('bdfg_cart_settings', 'bdfg_cart_threshold');
register_setting('bdfg_cart_settings', 'bdfg_enable_cart_notes');
}

/**
* Render settings page
*/
public function render_settings_page() {
// Check user capabilities
if (!current_user_can('manage_woocommerce')) {
return;
}

// Save settings if posted
if (isset($_POST['bdfg_save_settings']) && check_admin_referer('bdfg_cart_settings')) {
$this->save_settings();
}

include BDFG_CART_PATH . 'admin/views/settings.php';
}

/**
* Save settings
*/
private function save_settings() {
$fields = array(
'bdfg_mini_cart_position',
'bdfg_cart_expiry_days',
'bdfg_enable_cart_sharing',
'bdfg_popup_cart_template',
'bdfg_analytics_enabled',
'bdfg_cart_threshold',
'bdfg_enable_cart_notes'
);

foreach ($fields as $field) {
if (isset($_POST[$field])) {
update_option($field, sanitize_text_field($_POST[$field]));
}
}

WC_Admin_Settings::add_message(__('Settings saved successfully.', 'bdfg-advanced-cart'));
}

/**
* Add plugin action links
*
* @param array $links
* @return array
*/
public function add_plugin_links($links) {
$plugin_links = array(
'<a href="' . admin_url('admin.php?page=bdfg-settings') . '">' .
__('Settings', 'bdfg-advanced-cart') . '</a>',
'<a href="https://beiduofengou.net/docs/bdfg-advanced-cart" target="_blank">' .
__('Documentation', 'bdfg-advanced-cart') . '</a>'
);
return array_merge($plugin_links, $links);
}
}

admin/views/settings.php

<?php
/**
* Admin Settings View
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit;
}
?>

<div class="wrap">
<h1><?php _e('BDFG Advanced Cart Settings', 'bdfg-advanced-cart'); ?></h1>

<form method="post" action="">
<?php wp_nonce_field('bdfg_cart_settings'); ?>

<table class="form-table">
<tr>
<th scope="row">
<label for="bdfg_mini_cart_position">
<?php _e('Mini Cart Position', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<select name="bdfg_mini_cart_position" id="bdfg_mini_cart_position">
<option value="right" <?php selected(get_option('bdfg_mini_cart_position'), 'right'); ?>>
<?php _e('Right', 'bdfg-advanced-cart'); ?>
</option>
<option value="left" <?php selected(get_option('bdfg_mini_cart_position'), 'left'); ?>>
<?php _e('Left', 'bdfg-advanced-cart'); ?>
</option>
</select>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_cart_expiry_days">
<?php _e('Cart Expiry Days', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<input type="number"
name="bdfg_cart_expiry_days"
id="bdfg_cart_expiry_days"
value="<?php echo esc_attr(get_option('bdfg_cart_expiry_days', 30)); ?>"
min="1"
max="365">
<p class="description">
<?php _e('Number of days before saved carts expire', 'bdfg-advanced-cart'); ?>
</p>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_enable_cart_sharing">
<?php _e('Enable Cart Sharing', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<select name="bdfg_enable_cart_sharing" id="bdfg_enable_cart_sharing">
<option value="yes" <?php selected(get_option('bdfg_enable_cart_sharing'), 'yes'); ?>>
<?php _e('Yes', 'bdfg-advanced-cart'); ?>
</option>
<option value="no" <?php selected(get_option('bdfg_enable_cart_sharing'), 'no'); ?>>
<?php _e('No', 'bdfg-advanced-cart'); ?>
</option>
</select>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_popup_cart_template">
<?php _e('Popup Cart Template', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<select name="bdfg_popup_cart_template" id="bdfg_popup_cart_template">
<option value="default" <?php selected(get_option('bdfg_popup_cart_template'), 'default'); ?>>
<?php _e('Default', 'bdfg-advanced-cart'); ?>
</option>
<option value="modern" <?php selected(get_option('bdfg_popup_cart_template'), 'modern'); ?>>
<?php _e('Modern', 'bdfg-advanced-cart'); ?>
</option>
<option value="minimal" <?php selected(get_option('bdfg_popup_cart_template'), 'minimal'); ?>>
<?php _e('Minimal', 'bdfg-advanced-cart'); ?>
</option>
</select>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_analytics_enabled">
<?php _e('Enable Analytics', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<select name="bdfg_analytics_enabled" id="bdfg_analytics_enabled">
<option value="yes" <?php selected(get_option('bdfg_analytics_enabled'), 'yes'); ?>>
<?php _e('Yes', 'bdfg-advanced-cart'); ?>
</option>
<option value="no" <?php selected(get_option('bdfg_analytics_enabled'), 'no'); ?>>
<?php _e('No', 'bdfg-advanced-cart'); ?>
</option>
</select>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_cart_threshold">
<?php _e('Auto-open Cart Threshold', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<input type="number"
name="bdfg_cart_threshold"
id="bdfg_cart_threshold"
value="<?php echo esc_attr(get_option('bdfg_cart_threshold', 0)); ?>"
min="0">
<p class="description">
<?php _e('Number of items in cart before auto-opening the mini cart (0 to disable)', 'bdfg-advanced-cart'); ?>
</p>
</td>
</tr>

<tr>
<th scope="row">
<label for="bdfg_enable_cart_notes">
<?php _e('Enable Cart Notes', 'bdfg-advanced-cart'); ?>
</label>
</th>
<td>
<select name="bdfg_enable_cart_notes" id="bdfg_enable_cart_notes">
<option value="yes" <?php selected(get_option('bdfg_enable_cart_notes'), 'yes'); ?>>
<?php _e('Yes', 'bdfg-advanced-cart'); ?>
</option>
<option value="no" <?php selected(get_option('bdfg_enable_cart_notes'), 'no'); ?>>
<?php _e('No', 'bdfg-advanced-cart'); ?>
</option>
</select>
<p class="description">
<?php _e('Allow customers to add notes to their cart', 'bdfg-advanced-cart'); ?>
</p>
</td>
</tr>
</table>

<p class="submit">
<input type="submit"
name="bdfg_save_settings"
class="button button-primary"
value="<?php _e('Save Changes', 'bdfg-advanced-cart'); ?>">
</p>
</form>

<div class="bdfg-admin-footer">
<p>
<?php
printf(
__('BDFG Advanced Cart v%s by %s', 'bdfg-advanced-cart'),
BDFG_CART_VERSION,
'<a href="https://beiduofengou.net" target="_blank">beiduofengou</a>'
);
?>
</p>
</div>
</div>

templates/emails/shared-cart.php

<?php
/**
* Shared Cart Email Template
*
* @package BDFG_Advanced_Cart
* @author beiduofengou
* @since 2.2.1
*/

if (!defined('ABSPATH')) {
exit;
}
?>

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo get_bloginfo('name'); ?></title>
</head>
<body style="background-color: #f7f7f7; margin: 0; padding: 0; font-family: Arial, sans-serif;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="padding: 40px 0;">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" width="600" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<tr>
<td style="padding: 40px;">
<h1 style="color: #333333; margin: 0 0 20px;">
<?php _e('Shared Shopping Cart', 'bdfg-advanced-cart'); ?>
</h1>

<p style="color: #666666; font-size: 16px; line-height: 1.5; margin-bottom: 30px;">
<?php _e('Someone has shared their shopping cart with you! Click the button below to view and restore the shared cart.', 'bdfg-advanced-cart'); ?>
</p>

<table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom: 30px;">
<tr>
<td align="center">
<a href="<?php echo esc_url($share_url); ?>"
style="background-color: #0073aa; color: #ffffff; display: inline-block; padding: 15px 30px; text-decoration: none; border-radius: 4px; font-weight: bold;">
<?php _e('View Shared Cart', 'bdfg-advanced-cart'); ?>
</a>
</td>
</tr>
</table>

<p style="color: #666666; font-size: 14px; margin: 0;">
<?php
printf(
__('This cart will expire in %d days.', 'bdfg-advanced-cart'),
get_option('bdfg_cart_expiry_days', 30)
);
?>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

languages/bdfg-advanced-cart.pot

# Copyright (C) 2025 beiduofengou
# This file is distributed under the GPL v2 or later.
msgid ""
msgstr ""
"Project-Id-Version: BDFG Advanced Cart 2.2.1\n"
"Report-Msgid-Bugs-To: https://beiduofengou.net/support\n"
"POT-Creation-Date: 2024-03-15 10:39:22+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2024-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"

#: admin/class-bdfg-admin.php:31
msgid "BDFG Cart Settings"
msgstr ""

#: admin/class-bdfg-admin.php:32
msgid "BDFG Cart"
msgstr ""

#: admin/class-bdfg-admin.php:89
msgid "Settings saved successfully."
msgstr ""

#: admin/views/settings.php:38
msgid "Mini Cart Position"
msgstr ""

#: admin/views/settings.php:43
msgid "Right"
msgstr ""

#: admin/views/settings.php:46
msgid "Left"
msgstr ""

#: admin/views/settings.php:54
msgid "Cart Expiry Days"
msgstr ""

#: admin/views/settings.php:65
msgid "Number of days before saved carts expire"
msgstr ""

#: admin/views/settings.php:72
msgid "Enable Cart Sharing"
msgstr ""

#: admin/views/settings.php:77 admin/views/settings.php:116
msgid "Yes"
msgstr ""

#: admin/views/settings.php:80 admin/views/settings.php:119
msgid "No"
msgstr ""

#: includes/class-bdfg-cart-manager.php:156
msgid "Cart saved successfully!"
msgstr ""

#: includes/class-bdfg-cart-manager.php:189
msgid "Cart restored successfully!"
msgstr ""

#: includes/class-bdfg-mini-cart.php:98
msgid "Your cart is empty"
msgstr ""

#: includes/class-bdfg-mini-cart.php:99
msgid "Continue Shopping"
msgstr ""

#: includes/class-bdfg-checkout-fields.php:167
msgid "%s is a required field."
msgstr ""

#: templates/emails/shared-cart.php:34
msgid "Shared Shopping Cart"
msgstr ""

#: templates/emails/shared-cart.php:38
msgid "Someone has shared their shopping cart with you! Click the button below to view and restore the shared cart."
msgstr ""

#: templates/emails/shared-cart.php:46
msgid "View Shared Cart"
msgstr ""

#: templates/emails/shared-cart.php:53
msgid "This cart will expire in %d days."
msgstr ""

==常见问题==

=此插件是否需要WooCommerce? =

是的,BDFG高级购物车需要WooCommerce 5.0或更高的安装和激活。

=我可以自定义迷你购物车设计吗? =

是的,您可以从多个预构建的模板中进行选择,也可以通过将模板文件复制到主题来创建自己的。

Leave a Comment