WooCommerce 预定功能插件

WooCommerce的BDFG预订无缝地为您的WooCommerce商店添加预订功能,使客户在正式发布或可用之前购买产品。允许客户预订尚未发布或可用的产品,并具有可自定义的发布日期,定价选项和自动通知。

***可自定义的发布日期**:设置产品可用性的特定日期和时间
***灵活的定价选项**:提供预订的折扣或保费
***自动状态更新**:产品自动从预订过渡到可用
***电子邮件通知**:预订产品可用时提醒客户
***倒计时计时器**:使用可自定义的倒数计时器建立预期
***订单管理**:用于管理预订的专用仪表板
***客户帐户集成**:客户可以查看和管理其预订

相关文章: WooCommerce 产品比较工具

<?php
/**
* Plugin Name: BDFG Pre-Orders for WooCommerce
* Plugin URI: https://beiduofengou.net/2024/12/05/bdfg-pre-orders-for-woocommerce/
* Description: Allow customers to pre-order products before they are available.
* Version: 2.0.0
* Author: Beiduofengou
* Author URI: https://beiduofengou.net
* Text Domain: bdfg-preorders
* Domain Path: /languages
* Requires at least: 5.6
* Requires PHP: 7.2
* WC requires at least: 5.0.0
* WC tested up to: 7.5.0
*
* @package BDFG_PreOrders
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

// Define constants
define('BDFG_PREORDERS_VERSION', '2.0.0');
define('BDFG_PREORDERS_PATH', plugin_dir_path(__FILE__));
define('BDFG_PREORDERS_URL', plugin_dir_url(__FILE__));
define('BDFG_PREORDERS_BASENAME', plugin_basename(__FILE__));

/**
* Main plugin class
*/
class BDFG_PreOrders {
/**
* Instance
*
* @var BDFG_PreOrders
*/
private static $instance;

/**
* Admin class
*
* @var BDFG_PreOrders_Admin
*/
public $admin;

/**
* Frontend class
*
* @var BDFG_PreOrders_Frontend
*/
public $frontend;

/**
* Orders class
*
* @var BDFG_PreOrders_Orders
*/
public $orders;

/**
* Emails class
*
* @var BDFG_PreOrders_Emails
*/
public $emails;

/**
* Constructor
*/
private function __construct() {
// Load textdomain
add_action('plugins_loaded', array($this, 'load_textdomain'));

// Check if WooCommerce is active
if ($this->check_woocommerce()) {
$this->includes();
$this->init();

// Register activation/deactivation hooks
register_activation_hook(__FILE__, array('BDFG_PreOrders_Install', 'install'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
}
}

/**
* Get instance
*
* @return BDFG_PreOrders
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

/**
* Load textdomain
*/
public function load_textdomain() {
load_plugin_textdomain('bdfg-preorders', false, dirname(plugin_basename(__FILE__)) . '/languages');
}

/**
* Check if WooCommerce is active
*
* @return bool
*/
private function check_woocommerce() {
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
return true;
}

// Check for WooCommerce if in a multisite
if (is_multisite()) {
$plugins = get_site_option('active_sitewide_plugins');
if (isset($plugins['woocommerce/woocommerce.php'])) {
return true;
}
}

// Show admin notice if WooCommerce is not active
add_action('admin_notices', array($this, 'woocommerce_missing_notice'));

return false;
}

/**
* Include required files
*/
private function includes() {
// Helper classes
require_once BDFG_PREORDERS_PATH . 'includes/helpers/class-bdfg-preorders-helper.php';

// Core classes
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-admin.php';
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-frontend.php';
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-product.php';
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-orders.php';
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-emails.php';
require_once BDFG_PREORDERS_PATH . 'includes/class-bdfg-preorders-install.php';

// Template functions
require_once BDFG_PREORDERS_PATH . 'includes/bdfg-preorders-template-functions.php';
}

/**
* Initialize classes
*/
private function init() {
$this->admin = new BDFG_PreOrders_Admin();

if (!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
$this->frontend = new BDFG_PreOrders_Frontend();
}

$this->orders = new BDFG_PreOrders_Orders();
$this->emails = new BDFG_PreOrders_Emails();

// Add settings link
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'add_plugin_action_links'));

// Add plugin row meta
add_filter('plugin_row_meta', array($this, 'add_plugin_row_meta'), 10, 2);
}

/**
* Deactivate plugin
*/
public function deactivate() {
// Remove scheduled events
wp_clear_scheduled_hook('bdfg_preorders_check_availability');
}

/**
* Show admin notice if WooCommerce is not active
*/
public function woocommerce_missing_notice() {
?>
<div class="error">
<p><?php echo sprintf(
esc_html__('%1$s requires WooCommerce to be installed and active. You can download %2$s here.', 'bdfg-preorders'),
'<strong>BDFG Pre-Orders for WooCommerce</strong>',
'<a href="https://woocommerce.com/" target="_blank">WooCommerce</a>'
); ?></p>
</div>
<?php
}

/**
* Add plugin action links
*
* @param array $links
* @return array
*/
public function add_plugin_action_links($links) {
$plugin_links = array(
'<a href="' . admin_url('admin.php?page=bdfg-preorders-settings') . '">' . __('Settings', 'bdfg-preorders') . '</a>',
'<a href="' . admin_url('admin.php?page=bdfg-preorders-management') . '">' . __('Manage Pre-Orders', 'bdfg-preorders') . '</a>'
);

return array_merge($plugin_links, $links);
}

/**
* Add plugin row meta
*
* @param array $links
* @param string $file
* @return array
*/
public function add_plugin_row_meta($links, $file) {
if (BDFG_PREORDERS_BASENAME === $file) {
$row_meta = array(
'docs' => '<a href="https://beiduofengou.net/docs/bdfg-preorders/" target="_blank" aria-label="' . esc_attr__('View BDFG Pre-Orders documentation', 'bdfg-preorders') . '">' . esc_html__('Docs', 'bdfg-preorders') . '</a>',
'support' => '<a href="https://beiduofengou.net/support/" target="_blank" aria-label="' . esc_attr__('Visit customer support', 'bdfg-preorders') . '">' . esc_html__('Support', 'bdfg-preorders') . '</a>'
);

return array_merge($links, $row_meta);
}

return $links;
}
}

// Initialize the plugin
function BDFG_PreOrders() {
return BDFG_PreOrders::get_instance();
}

// Start the plugin
BDFG_PreOrders();

includes/class-bdfg-preorders-admin.php

<?php
/**
* Admin functionality for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Admin class for handling backend operations
*/
class BDFG_PreOrders_Admin {
/**
* Constructor
*/
public function __construct() {
// Add product tab
add_filter('woocommerce_product_data_tabs', array($this, 'add_product_tab'));

// Add product panel
add_action('woocommerce_product_data_panels', array($this, 'add_product_panel'));

// Save product data
add_action('woocommerce_process_product_meta', array($this, 'save_product_data'));

// Add variation specific fields
add_action('woocommerce_product_after_variable_attributes', array($this, 'add_variation_preorder_fields'), 10, 3);

// Save variation fields
add_action('woocommerce_save_product_variation', array($this, 'save_variation_preorder_fields'), 10, 2);

// Add settings page
add_action('admin_menu', array($this, 'add_settings_page'));

// Register settings
add_action('admin_init', array($this, 'register_settings'));

// Add custom order status to WooCommerce
add_filter('wc_order_statuses', array($this, 'add_order_statuses'));

// Add custom column to orders list
add_filter('manage_edit-shop_order_columns', array($this, 'add_order_column'));
add_action('manage_shop_order_posts_custom_column', array($this, 'populate_order_column'), 10, 2);

// Add preorder filter for orders
add_action('restrict_manage_posts', array($this, 'add_preorder_filter'));
add_filter('request', array($this, 'filter_preorders'));

// Admin dashboard widgets
add_action('wp_dashboard_setup', array($this, 'add_dashboard_widgets'));

// Load admin JS and CSS
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));

// Add welcome page redirect after activation
add_action('admin_init', array($this, 'welcome_page_redirect'));
}

/**
* Add product tab
*
* @param array $tabs
* @return array
*/
public function add_product_tab($tabs) {
$tabs['bdfg_preorder'] = array(
'label' => __('Pre-Order', 'bdfg-preorders'),
'target' => 'bdfg_preorder_data',
'class' => array('show_if_simple', 'show_if_variable'),
'priority' => 21
);

return $tabs;
}

/**
* Add product panel
*/
public function add_product_panel() {
global $post;
?>
<div id="bdfg_preorder_data" class="panel woocommerce_options_panel">
<div class="options_group bdfg-preorder-options">
<h4 class="bdfg-section-title"><?php esc_html_e('Pre-Order Settings', 'bdfg-preorders'); ?></h4>

<?php
woocommerce_wp_checkbox(array(
'id' => '_bdfg_preorder_enabled',
'label' => __('Enable Pre-Order', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Enable pre-order functionality for this product.', 'bdfg-preorders')
));

woocommerce_wp_text_input(array(
'id' => '_bdfg_preorder_available_date',
'label' => __('Available Date', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('The date when the product will be available.', 'bdfg-preorders'),
'type' => 'text',
'class' => 'date-picker short',
'custom_attributes' => array(
'placeholder' => _x('YYYY-MM-DD', 'placeholder', 'bdfg-preorders')
)
));

woocommerce_wp_text_input(array(
'id' => '_bdfg_preorder_available_time',
'label' => __('Available Time', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('The time when the product will be available.', 'bdfg-preorders'),
'type' => 'time',
'class' => 'short',
'custom_attributes' => array(
'placeholder' => _x('HH:MM', 'placeholder', 'bdfg-preorders')
)
));
?>
</div>

<div class="options_group bdfg-preorder-pricing">
<h4 class="bdfg-section-title"><?php esc_html_e('Pre-Order Pricing', 'bdfg-preorders'); ?></h4>

<?php
woocommerce_wp_select(array(
'id' => '_bdfg_preorder_pricing_type',
'label' => __('Price Adjustment Type', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Select how the pre-order price should be adjusted.', 'bdfg-preorders'),
'options' => array(
'' => __('No adjustment', 'bdfg-preorders'),
'fixed' => __('Fixed amount', 'bdfg-preorders'),
'percentage' => __('Percentage', 'bdfg-preorders')
)
));

woocommerce_wp_text_input(array(
'id' => '_bdfg_preorder_price_adjustment',
'label' => __('Price Adjustment', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Enter an amount to adjust the price for pre-orders. Use a negative value for a discount.', 'bdfg-preorders'),
'type' => 'number',
'class' => 'short',
'custom_attributes' => array(
'step' => '0.01'
)
));
?>
</div>

<div class="options_group bdfg-preorder-display">
<h4 class="bdfg-section-title"><?php esc_html_e('Display Settings', 'bdfg-preorders'); ?></h4>

<?php
woocommerce_wp_text_input(array(
'id' => '_bdfg_preorder_button_text',
'label' => __('Button Text', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Customize the pre-order button text. Leave empty to use the global setting.', 'bdfg-preorders'),
'type' => 'text',
'placeholder' => get_option('bdfg_preorders_button_text', __('Pre-Order Now', 'bdfg-preorders'))
));

woocommerce_wp_textarea_input(array(
'id' => '_bdfg_preorder_custom_message',
'label' => __('Custom Message', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Add a custom message to display on the product page for pre-orders.', 'bdfg-preorders'),
'placeholder' => __('e.g., "This is a limited-time pre-order!"', 'bdfg-preorders')
));
?>
</div>

<div class="options_group bdfg-preorder-advanced">
<h4 class="bdfg-section-title"><?php esc_html_e('Advanced Settings', 'bdfg-preorders'); ?></h4>

<?php
woocommerce_wp_select(array(
'id' => '_bdfg_preorder_stock_handling',
'label' => __('Stock Handling', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Choose how stock should be handled for pre-orders.', 'bdfg-preorders'),
'options' => array(
'' => __('Use global setting', 'bdfg-preorders'),
'normal' => __('Normal stock management', 'bdfg-preorders'),
'reserved' => __('Reserve stock for pre-orders', 'bdfg-preorders'),
'unlimited' => __('Unlimited pre-orders (ignore stock)', 'bdfg-preorders')
)
));

woocommerce_wp_checkbox(array(
'id' => '_bdfg_preorder_enable_cancellations',
'label' => __('Allow Cancellations', 'bdfg-preorders'),
'desc_tip' => true,
'description' => __('Allow customers to cancel pre-orders before the release date.', 'bdfg-preorders')
));
?>
</div>
</div>
<?php
}

/**
* Save product data
*
* @param int $post_id
*/
public function save_product_data($post_id) {
// Save preorder enabled status
$preorder_enabled = isset($_POST['_bdfg_preorder_enabled']) ? 'yes' : 'no';
update_post_meta($post_id, '_bdfg_preorder_enabled', $preorder_enabled);

// Save available date
if (isset($_POST['_bdfg_preorder_available_date'])) {
update_post_meta($post_id, '_bdfg_preorder_available_date', sanitize_text_field($_POST['_bdfg_preorder_available_date']));
}

// Save available time
if (isset($_POST['_bdfg_preorder_available_time'])) {
update_post_meta($post_id, '_bdfg_preorder_available_time', sanitize_text_field($_POST['_bdfg_preorder_available_time']));
}

// Save pricing type
if (isset($_POST['_bdfg_preorder_pricing_type'])) {
update_post_meta($post_id, '_bdfg_preorder_pricing_type', sanitize_text_field($_POST['_bdfg_preorder_pricing_type']));
}

// Save price adjustment
if (isset($_POST['_bdfg_preorder_price_adjustment'])) {
update_post_meta($post_id, '_bdfg_preorder_price_adjustment', wc_format_decimal($_POST['_bdfg_preorder_price_adjustment']));
}

// Save button text
if (isset($_POST['_bdfg_preorder_button_text'])) {
update_post_meta($post_id, '_bdfg_preorder_button_text', sanitize_text_field($_POST['_bdfg_preorder_button_text']));
}

// Save custom message
if (isset($_POST['_bdfg_preorder_custom_message'])) {
update_post_meta($post_id, '_bdfg_preorder_custom_message', sanitize_textarea_field($_POST['_bdfg_preorder_custom_message']));
}

// Save stock handling
if (isset($_POST['_bdfg_preorder_stock_handling'])) {
update_post_meta($post_id, '_bdfg_preorder_stock_handling', sanitize_text_field($_POST['_bdfg_preorder_stock_handling']));
}

// Save cancellations setting
$enable_cancellations = isset($_POST['_bdfg_preorder_enable_cancellations']) ? 'yes' : 'no';
update_post_meta($post_id, '_bdfg_preorder_enable_cancellations', $enable_cancellations);
}

/**
* Add variation specific preorder fields
*
* @param int $loop
* @param array $variation_data
* @param WP_Post $variation
*/
public function add_variation_preorder_fields($loop, $variation_data, $variation) {
$variation_id = $variation->ID;
$enabled = get_post_meta($variation_id, '_bdfg_preorder_enabled', true);
$date = get_post_meta($variation_id, '_bdfg_preorder_available_date', true);
$time = get_post_meta($variation_id, '_bdfg_preorder_available_time', true);
$adjustment = get_post_meta($variation_id, '_bdfg_preorder_price_adjustment', true);
?>
<div class="bdfg-variation-preorder-fields">
<p class="form-row form-row-full">
<label><?php esc_html_e('Pre-Order Settings', 'bdfg-preorders'); ?></label>
<span class="woocommerce-help-tip" data-tip="<?php esc_attr_e('Configure pre-order settings for this variation.', 'bdfg-preorders'); ?>"></span>
</p>

<p class="form-row form-row-first">
<label>
<input type="checkbox" class="checkbox" name="_bdfg_variation_preorder_enabled[<?php echo esc_attr($loop); ?>]" <?php checked($enabled, 'yes'); ?> />
<?php esc_html_e('Enable Pre-Order', 'bdfg-preorders'); ?>
</label>
</p>

<p class="form-row form-row-last">
<label><?php esc_html_e('Available Date', 'bdfg-preorders'); ?></label>
<input type="text" class="short date-picker" name="_bdfg_variation_preorder_available_date[<?php echo esc_attr($loop); ?>]" value="<?php echo esc_attr($date); ?>" placeholder="<?php esc_attr_e('YYYY-MM-DD', 'bdfg-preorders'); ?>" />
</p>

<p class="form-row form-row-first">
<label><?php esc_html_e('Available Time', 'bdfg-preorders'); ?></label>
<input type="time" class="short" name="_bdfg_variation_preorder_available_time[<?php echo esc_attr($loop); ?>]" value="<?php echo esc_attr($time); ?>" />
</p>

<p class="form-row form-row-last">
<label><?php esc_html_e('Price Adjustment', 'bdfg-preorders'); ?></label>
<input type="number" class="short" name="_bdfg_variation_preorder_price_adjustment[<?php echo esc_attr($loop); ?>]" value="<?php echo esc_attr($adjustment); ?>" step="0.01" />
</p>
</div>
<?php
}

/**
* Save variation preorder fields
*
* @param int $variation_id
* @param int $i
*/
public function save_variation_preorder_fields($variation_id, $i) {
// Save preorder enabled status for variation
$enabled = isset($_POST['_bdfg_variation_preorder_enabled'][$i]) ? 'yes' : 'no';
update_post_meta($variation_id, '_bdfg_preorder_enabled', $enabled);

// Save available date for variation
if (isset($_POST['_bdfg_variation_preorder_available_date'][$i])) {
update_post_meta($variation_id, '_bdfg_preorder_available_date', sanitize_text_field($_POST['_bdfg_variation_preorder_available_date'][$i]));
}

// Save available time for variation
if (isset($_POST['_bdfg_variation_preorder_available_time'][$i])) {
update_post_meta($variation_id, '_bdfg_preorder_available_time', sanitize_text_field($_POST['_bdfg_variation_preorder_available_time'][$i]));
}

// Save price adjustment for variation
if (isset($_POST['_bdfg_variation_preorder_price_adjustment'][$i])) {
update_post_meta($variation_id, '_bdfg_preorder_price_adjustment', wc_format_decimal($_POST['_bdfg_variation_preorder_price_adjustment'][$i]));
}
}

/**
* Add settings page
*/
public function add_settings_page() {
add_submenu_page(
'woocommerce',
__('BDFG Pre-Orders Settings', 'bdfg-preorders'),
__('Pre-Orders', 'bdfg-preorders'),
'manage_woocommerce',
'bdfg-preorders-settings',
array($this, 'render_settings_page')
);

// Add management page for preorders
add_submenu_page(
'woocommerce',
__('Manage Pre-Orders', 'bdfg-preorders'),
__('Manage Pre-Orders', 'bdfg-preorders'),
'manage_woocommerce',
'bdfg-preorders-management',
array($this, 'render_management_page')
);
}

/**
* Render settings page
*/
public function render_settings_page() {
?>
<div class="wrap bdfg-preorders-settings-wrap">
<div class="bdfg-preorders-header">
<h1><?php echo esc_html__('BDFG Pre-Orders Settings', 'bdfg-preorders'); ?></h1>
<div class="bdfg-preorders-logo">
<img src="<?php echo esc_url(BDFG_PREORDERS_URL . 'assets/images/bdfg-logo.png'); ?>" alt="Beiduofengou" />
</div>
</div>

<div class="bdfg-preorders-settings-content">
<form method="post" action="options.php">
<?php settings_fields('bdfg_preorders_settings'); ?>

<div class="bdfg-preorders-settings-tabs">
<ul class="tabs-nav">
<li class="active"><a href="#general"><?php esc_html_e('General', 'bdfg-preorders'); ?></a></li>
<li><a href="#display"><?php esc_html_e('Display', 'bdfg-preorders'); ?></a></li>
<li><a href="#emails"><?php esc_html_e('Emails', 'bdfg-preorders'); ?></a></li>
<li><a href="#advanced"><?php esc_html_e('Advanced', 'bdfg-preorders'); ?></a></li>
</ul>

<div class="tabs-content">
<!-- General Settings Tab -->
<div id="general" class="tab-content active">
<h2><?php esc_html_e('General Settings', 'bdfg-preorders'); ?></h2>
<table class="form-table">
<?php do_settings_fields('bdfg_preorders_settings', 'bdfg_preorders_general_section'); ?>
</table>
</div>

<!-- Display Settings Tab -->
<div id="display" class="tab-content">
<h2><?php esc_html_e('Display Settings', 'bdfg-preorders'); ?></h2>
<table class="form-table">
<?php do_settings_fields('bdfg_preorders_settings', 'bdfg_preorders_appearance_section'); ?>
</table>
</div>

<!-- Email Settings Tab -->
<div id="emails" class="tab-content">
<h2><?php esc_html_e('Email Notification Settings', 'bdfg-preorders'); ?></h2>
<table class="form-table">
<?php do_settings_fields('bdfg_preorders_settings', 'bdfg_preorders_email_section'); ?>
</table>
</div>

<!-- Advanced Settings Tab -->
<div id="advanced" class="tab-content">
<h2><?php esc_html_e('Advanced Settings', 'bdfg-preorders'); ?></h2>
<table class="form-table">
<?php do_settings_fields('bdfg_preorders_settings', 'bdfg_preorders_advanced_section'); ?>
</table>
</div>
</div>
</div>

<?php submit_button(); ?>
</form>
</div>

<div class="bdfg-preorders-settings-sidebar">
<div class="bdfg-preorders-help-box">
<h3><?php esc_html_e('Need Help?', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Check our documentation or contact support if you need assistance.', 'bdfg-preorders'); ?></p>
<a href="https://beiduofengou.net/docs/bdfg-preorders/" class="button button-primary" target="_blank">
<?php esc_html_e('Documentation', 'bdfg-preorders'); ?>
</a>
<a href="https://beiduofengou.net/support/" class="button" target="_blank">
<?php esc_html_e('Contact Support', 'bdfg-preorders'); ?>
</a>
</div>

<div class="bdfg-preorders-version-info">
<p><?php echo sprintf(esc_html__('Version: %s', 'bdfg-preorders'), BDFG_PREORDERS_VERSION); ?></p>
<p><?php echo sprintf(
esc_html__('Made with %s by %s', 'bdfg-preorders'),
'<span class="dashicons dashicons-heart"></span>',
'<a href="https://beiduofengou.net" target="_blank">Beiduofengou</a>'
); ?></p>
</div>
</div>
</div>
<?php
}

/**
* Render management page for preorders
*/
public function render_management_page() {
?>
<div class="wrap bdfg-preorders-management-wrap">
<div class="bdfg-preorders-header">
<h1><?php echo esc_html__('Manage Pre-Orders', 'bdfg-preorders'); ?></h1>
</div>

<?php
// Include the preorder management list table
require_once BDFG_PREORDERS_PATH . 'includes/admin/class-bdfg-preorders-list-table.php';

// Display filters
?>
<div class="bdfg-preorders-filters">
<form method="get">
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />

<?php
// Create an instance of our list table
$preorders_table = new BDFG_PreOrders_List_Table();

// Display the list table
$preorders_table->prepare_items();
$preorders_table->search_box(__('Search Pre-Orders', 'bdfg-preorders'), 'bdfg-preorders-search');
$preorders_table->display();
?>
</form>
</div>
</div>
<?php
}

/**
* Register settings
*/
public function register_settings() {
// General Settings
register_setting('bdfg_preorders_settings', 'bdfg_preorders_stock_handling');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_payment_method');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_deposit_amount');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_enable_cancellations');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_cancellation_limit');

// Display Settings
register_setting('bdfg_preorders_settings', 'bdfg_preorders_button_text');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_button_color');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_availability_text');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_show_countdown');

// Email Settings
register_setting('bdfg_preorders_settings', 'bdfg_preorders_enable_emails');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_email_subject');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_email_heading');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_email_content');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_admin_notification');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_admin_email_subject');

// Advanced Settings
register_setting('bdfg_preorders_settings', 'bdfg_preorders_delete_data_uninstall');
register_setting('bdfg_preorders_settings', 'bdfg_preorders_debug_mode');

// General Settings Section
add_settings_section(
'bdfg_preorders_general_section',
__('General Settings', 'bdfg-preorders'),
array($this, 'render_general_section'),
'bdfg_preorders_settings'
);

// Display Settings Section
add_settings_section(
'bdfg_preorders_appearance_section',
__('Display Settings', 'bdfg-preorders'),
array($this, 'render_appearance_section'),
'bdfg_preorders_settings'
);

// Email Settings Section
add_settings_section(
'bdfg_preorders_email_section',
__('Email Notification Settings', 'bdfg-preorders'),
array($this, 'render_email_section'),
'bdfg_preorders_settings'
);

// Advanced Settings Section
add_settings_section(
'bdfg_preorders_advanced_section',
__('Advanced Settings', 'bdfg-preorders'),
array($this, 'render_advanced_section'),
'bdfg_preorders_settings'
);

// General Settings Fields
add_settings_field(
'bdfg_preorders_stock_handling',
__('Stock Handling', 'bdfg-preorders'),
array($this, 'render_select_field'),
'bdfg_preorders_settings',
'bdfg_preorders_general_section',
array(
'id' => 'bdfg_preorders_stock_handling',
'default' => 'normal',
'options' => array(
'normal' => __('Normal stock management', 'bdfg-preorders'),
'reserved' => __('Reserve stock for pre-orders', 'bdfg-preorders'),
'unlimited' => __('Unlimited pre-orders (ignore stock)', 'bdfg-preorders')
),
'description' => __('Choose how stock should be handled for pre-orders.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_payment_method',
__('Payment Method', 'bdfg-preorders'),
array($this, 'render_select_field'),
'bdfg_preorders_settings',
'bdfg_preorders_general_section',
array(
'id' => 'bdfg_preorders_payment_method',
'default' => 'full',
'options' => array(
'full' => __('Full payment upfront', 'bdfg-preorders'),
'deposit' => __('Deposit only', 'bdfg-preorders'),
'authorize' => __('Authorize payment only (charge on release)', 'bdfg-preorders')
),
'description' => __('Choose how payments should be processed for pre-orders.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_deposit_amount',
__('Deposit Amount (%)', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_general_section',
array(
'id' => 'bdfg_preorders_deposit_amount',
'default' => '50',
'description' => __('Percentage of product price to charge as deposit. Only applies if deposit payment method is selected.', 'bdfg-preorders'),
'type' => 'number',
'min' => '1',
'max' => '99'
)
);

add_settings_field(
'bdfg_preorders_enable_cancellations',
__('Allow Cancellations', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_general_section',
array(
'id' => 'bdfg_preorders_enable_cancellations',
'default' => 'yes',
'description' => __('Allow customers to cancel pre-orders before the release date.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_cancellation_limit',
__('Cancellation Time Limit (hours)', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_general_section',
array(
'id' => 'bdfg_preorders_cancellation_limit',
'default' => '24',
'description' => __('Number of hours before release date after which cancellations are no longer allowed.', 'bdfg-preorders'),
'type' => 'number',
'min' => '0'
)
);

// Display Settings Fields
add_settings_field(
'bdfg_preorders_button_text',
__('Pre-Order Button Text', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_appearance_section',
array(
'id' => 'bdfg_preorders_button_text',
'default' => __('Pre-Order Now', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_button_color',
__('Pre-Order Button Color', 'bdfg-preorders'),
array($this, 'render_color_field'),
'bdfg_preorders_settings',
'bdfg_preorders_appearance_section',
array(
'id' => 'bdfg_preorders_button_color',
'default' => '#3d9cd2'
)
);

add_settings_field(
'bdfg_preorders_availability_text',
__('Availability Text Format', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_appearance_section',
array(
'id' => 'bdfg_preorders_availability_text',
'default' => __('Available on: %date%', 'bdfg-preorders'),
'description' => __('Use %date% as a placeholder for the availability date.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_show_countdown',
__('Show Countdown Timer', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_appearance_section',
array(
'id' => 'bdfg_preorders_show_countdown',
'default' => 'yes',
'description' => __('Display a countdown timer for upcoming pre-order releases.', 'bdfg-preorders')
)
);

// Email Settings Fields
add_settings_field(
'bdfg_preorders_enable_emails',
__('Enable Customer Notifications', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_enable_emails',
'default' => 'yes',
'description' => __('Send email notifications to customers when pre-ordered products become available.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_email_subject',
__('Email Subject', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_email_subject',
'default' => __('Your pre-ordered product is now available!', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_email_heading',
__('Email Heading', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_email_heading',
'default' => __('Pre-order Available', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_email_content',
__('Email Content', 'bdfg-preorders'),
array($this, 'render_textarea_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_email_content',
'default' => __("Hello {customer_name},\n\nYour pre-ordered product '{product_name}' is now available.\n\nThank you for your patience and support!\n\nBeiduofengou Team", 'bdfg-preorders'),
'description' => __('Available placeholders: {customer_name}, {product_name}, {order_id}, {availability_date}', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_admin_notification',
__('Enable Admin Notifications', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_admin_notification',
'default' => 'yes',
'description' => __('Send email notifications to admin when pre-ordered products become available.', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_admin_email_subject',
__('Admin Email Subject', 'bdfg-preorders'),
array($this, 'render_text_field'),
'bdfg_preorders_settings',
'bdfg_preorders_email_section',
array(
'id' => 'bdfg_preorders_admin_email_subject',
'default' => __('Pre-ordered product now available', 'bdfg-preorders')
)
);

// Advanced Settings Fields
add_settings_field(
'bdfg_preorders_delete_data_uninstall',
__('Delete Plugin Data on Uninstall', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_advanced_section',
array(
'id' => 'bdfg_preorders_delete_data_uninstall',
'default' => 'no',
'description' => __('Delete all plugin data when uninstalling the plugin. This cannot be undone!', 'bdfg-preorders')
)
);

add_settings_field(
'bdfg_preorders_debug_mode',
__('Debug Mode', 'bdfg-preorders'),
array($this, 'render_checkbox_field'),
'bdfg_preorders_settings',
'bdfg_preorders_advanced_section',
array(
'id' => 'bdfg_preorders_debug_mode',
'default' => 'no',
'description' => __('Enable debug mode for troubleshooting. Only use when needed.', 'bdfg-preorders')
)
);
}

/**
* Render section headers
*/
public function render_general_section() {
echo '<p>' . esc_html__('Configure general settings for pre-orders.', 'bdfg-preorders') . '</p>';
}

public function render_appearance_section() {
echo '<p>' . esc_html__('Customize the appearance of pre-order elements.', 'bdfg-preorders') . '</p>';
}

public function render_email_section() {
echo '<p>' . esc_html__('Configure email notifications sent to customers when pre-ordered products become available.', 'bdfg-preorders') . '</p>';
}

public function render_advanced_section() {
echo '<p>' . esc_html__('Advanced settings for BDFG Pre-Orders. Only change these settings if you know what you are doing.', 'bdfg-preorders') . '</p>';
}

/**
* Render field types
*/
public function render_text_field($args) {
$id = $args['id'];
$default = isset($args['default']) ? $args['default'] : '';
$value = get_option($id, $default);
$type = isset($args['type']) ? $args['type'] : 'text';
$min = isset($args['min']) ? 'min="' . esc_attr($args['min']) . '"' : '';
$max = isset($args['max']) ? 'max="' . esc_attr($args['max']) . '"' : '';
$description = isset($args['description']) ? $args['description'] : '';

echo '<input type="' . esc_attr($type) . '" id="' . esc_attr($id) . '" name="' . esc_attr($id) . '" value="' . esc_attr($value) . '" class="regular-text" ' . $min . ' ' . $max . ' />';

if ($description) {
echo '<p class="description">' . esc_html($description) . '</p>';
}
}

public function render_color_field($args) {
$id = $args['id'];
$default = isset($args['default']) ? $args['default'] : '#000000';
$value = get_option($id, $default);

echo '<input type="color" id="' . esc_attr($id) . '" name="' . esc_attr($id) . '" value="' . esc_attr($value) . '" class="bdfg-color-picker" />';
}

public function render_checkbox_field($args) {
$id = $args['id'];
$default = isset($args['default']) ? $args['default'] : 'no';
$value = get_option($id, $default);
$description = isset($args['description']) ? $args['description'] : '';

echo '<input type="checkbox" id="' . esc_attr($id) . '" name="' . esc_attr($id) . '" value="yes" ' . checked($value, 'yes', false) . ' />';

if ($description) {
echo '<p class="description">' . esc_html($description) . '</p>';
}
}

public function render_select_field($args) {
$id = $args['id'];
$default = isset($args['default']) ? $args['default'] : '';
$value = get_option($id, $default);
$options = isset($args['options']) ? $args['options'] : array();
$description = isset($args['description']) ? $args['description'] : '';

echo '<select id="' . esc_attr($id) . '" name="' . esc_attr($id) . '">';
foreach ($options as $option_value => $option_label) {
echo '<option value="' . esc_attr($option_value) . '" ' . selected($value, $option_value, false) . '>' . esc_html($option_label) . '</option>';
}
echo '</select>';

if ($description) {
echo '<p class="description">' . esc_html($description) . '</p>';
}
}

public function render_textarea_field($args) {
$id = $args['id'];
$default = isset($args['default']) ? $args['default'] : '';
$value = get_option($id, $default);
$description = isset($args['description']) ? $args['description'] : '';

echo '<textarea id="' . esc_attr($id) . '" name="' . esc_attr($id) . '" rows="5" class="large-text">' . esc_textarea($value) . '</textarea>';

if ($description) {
echo '<p class="description">' . esc_html($description) . '</p>';
}
}

/**
* Add custom order status
*
* @param array $order_statuses
* @return array
*/
public function add_order_statuses($order_statuses) {
$new_statuses = array();

// Add preordered status after processing
foreach ($order_statuses as $key => $status) {
$new_statuses[$key] = $status;

if ($key === 'wc-processing') {
$new_statuses['wc-preordered'] = __('Pre-ordered', 'bdfg-preorders');
}
}

return $new_statuses;
}

/**
* Add custom column to orders list
*
* @param array $columns
* @return array
*/
public function add_order_column($columns) {
$new_columns = array();

foreach ($columns as $key => $column) {
$new_columns[$key] = $column;

if ($key === 'order_status') {
$new_columns['bdfg_preorder'] = __('Pre-Order', 'bdfg-preorders');
}
}

return $new_columns;
}

/**
* Populate custom order column
*
* @param string $column
* @param int $order_id
*/
public function populate_order_column($column, $order_id) {
if ($column === 'bdfg_preorder') {
$order = wc_get_order($order_id);
$has_preorder = false;
$available_date = '';
$closest_date = null;

// Check each line item for preorder products
foreach ($order->get_items() as $item) {
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$target_id = $variation_id ? $variation_id : $product_id;

if (BDFG_PreOrders_Product::is_preorder($target_id)) {
$has_preorder = true;
$date = get_post_meta($target_id, '_bdfg_preorder_available_date', true);
$time = get_post_meta($target_id, '_bdfg_preorder_available_time', true);

if ($date) {
// Format the full date/time
$temp_date = $date;
if ($time) {
$temp_date .= ' ' . $time;
}

// Track the earliest release date
if (is_null($closest_date) || strtotime($temp_date) < strtotime($closest_date)) {
$closest_date = $temp_date;
$available_date = $temp_date;
}
}
}
}

// Display preorder status if applicable
if ($has_preorder) {
echo '<mark class="preorder"><span>' . esc_html__('Pre-Order', 'bdfg-preorders') . '</span></mark>';

if ($available_date) {
$formatted_date = BDFG_PreOrders_Helper::format_date($available_date);
echo '<br><small>' . esc_html__('Available on:', 'bdfg-preorders') . ' ' . esc_html($formatted_date) . '</small>';

// Show status indicator
$current_time = current_time('timestamp');
$release_time = strtotime($available_date);

if ($release_time < $current_time) {
echo ' <span class="bdfg-status-indicator released" title="' . esc_attr__('Released', 'bdfg-preorders') . '"></span>';
} else {
echo ' <span class="bdfg-status-indicator pending" title="' . esc_attr__('Pending Release', 'bdfg-preorders') . '"></span>';
}
}
}
}
}

/**
* Add preorder filter for orders
*
* @param string $post_type
*/
public function add_preorder_filter($post_type) {
global $pagenow;

if ($pagenow === 'edit.php' && $post_type === 'shop_order') {
$current = isset($_GET['bdfg_preorder_filter']) ? sanitize_text_field($_GET['bdfg_preorder_filter']) : '';
?>
<select name="bdfg_preorder_filter">
<option value=""><?php esc_html_e('All Orders', 'bdfg-preorders'); ?></option>
<option value="preorder" <?php selected($current, 'preorder'); ?>><?php esc_html_e('Pre-Orders Only', 'bdfg-preorders'); ?></option>
<option value="pending_release" <?php selected($current, 'pending_release'); ?>><?php esc_html_e('Pending Release', 'bdfg-preorders'); ?></option>
<option value="released" <?php selected($current, 'released'); ?>><?php esc_html_e('Released Pre-Orders', 'bdfg-preorders'); ?></option>
</select>
<?php
}
}

/**
* Filter preorders in the admin order list
*
* @param array $vars
* @return array
*/
public function filter_preorders($vars) {
global $pagenow, $typenow;

if ($pagenow === 'edit.php' && $typenow === 'shop_order' && isset($_GET['bdfg_preorder_filter']) && !empty($_GET['bdfg_preorder_filter'])) {
$filter_value = sanitize_text_field($_GET['bdfg_preorder_filter']);

// Set up meta query
if ($filter_value === 'preorder') {
$vars['meta_key'] = '_bdfg_has_preorder';
$vars['meta_value'] = 'yes';
} elseif ($filter_value === 'pending_release' || $filter_value === 'released') {
$vars['meta_key'] = '_bdfg_has_preorder';
$vars['meta_value'] = 'yes';

// This is simplified - would need custom filtering logic based on release dates in a real implementation
if ($filter_value === 'pending_release') {
$vars['post_status'] = 'wc-preordered';
} elseif ($filter_value === 'released') {
$vars['post_status'] = array('wc-completed', 'wc-processing');
}
}
}

return $vars;
}

/**
* Add dashboard widgets
*/
public function add_dashboard_widgets() {
// Only add if user has WooCommerce permissions
if (current_user_can('manage_woocommerce')) {
wp_add_dashboard_widget(
'bdfg_preorders_dashboard_widget',
__('BDFG Pre-Orders Summary', 'bdfg-preorders'),
array($this, 'render_dashboard_widget')
);
}
}

/**
* Render dashboard widget
*/
public function render_dashboard_widget() {
global $wpdb;

$table_name = $wpdb->prefix . 'bdfg_preorders';
$today = date('Y-m-d');
$thirty_days = date('Y-m-d', strtotime('+30 days'));

// Get counts
$total_preorders = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
$pending_release = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'pending'");
$upcoming_releases = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table_name WHERE status = 'pending' AND available_date BETWEEN %s AND %s",
$today,
$thirty_days
));

// Get upcoming releases
$upcoming = $wpdb->get_results($wpdb->prepare(
"SELECT p.ID, p.post_title, po.available_date
FROM $table_name po
JOIN {$wpdb->posts} p ON p.ID = po.product_id
WHERE po.status = 'pending'
AND po.available_date BETWEEN %s AND %s
ORDER BY po.available_date ASC
LIMIT 5",
$today,
$thirty_days
));

?>
<div class="bdfg-preorders-widget">
<div class="bdfg-preorders-stats">
<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($total_preorders); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Total Pre-Orders', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($pending_release); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Pending Release', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($upcoming_releases); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Upcoming (30 days)', 'bdfg-preorders'); ?></span>
</div>
</div>

<?php if (!empty($upcoming)) : ?>
<h4><?php esc_html_e('Upcoming Releases', 'bdfg-preorders'); ?></h4>
<ul class="bdfg-upcoming-releases">
<?php foreach ($upcoming as $product) : ?>
<li>
<span class="bdfg-release-date">
<?php echo esc_html(BDFG_PreOrders_Helper::format_date($product->available_date)); ?>
</span>
<a href="<?php echo esc_url(get_edit_post_link($product->ID)); ?>" class="bdfg-product-title">
<?php echo esc_html($product->post_title); ?>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>

<p class="bdfg-widget-footer">
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-management')); ?>">
<?php esc_html_e('Manage Pre-Orders', 'bdfg-preorders'); ?> →
</a>
</p>
</div>
<?php
}

/**
* Welcome page redirect
*/
public function welcome_page_redirect() {
// Check if we need to show the welcome page
if (get_transient('bdfg_preorders_activation_redirect')) {
delete_transient('bdfg_preorders_activation_redirect');

// Only redirect if not already on the page and not doing AJAX
if (!isset($_GET['page']) || $_GET['page'] !== 'bdfg-preorders-welcome' && !wp_doing_ajax()) {
wp_safe_redirect(admin_url('admin.php?page=bdfg-preorders-settings&tab=welcome'));
exit;
}
}
}

/**
* Load admin scripts and styles
*
* @param string $hook
*/
public function enqueue_admin_scripts($hook) {
$screen = get_current_screen();

// Only load on product edit page, our settings page, or orders page
if (($hook === 'post.php' || $hook === 'post-new.php') && $screen->post_type === 'product' ||
strpos($hook, 'bdfg-preorders') !== false ||
($screen->base === 'edit' && $screen->post_type === 'shop_order')) {

wp_enqueue_style('bdfg-preorders-admin', BDFG_PREORDERS_URL . 'assets/css/admin.css', array(), BDFG_PREORDERS_VERSION);
wp_enqueue_script('bdfg-preorders-admin', BDFG_PREORDERS_URL . 'assets/js/admin.js', array('jquery', 'jquery-ui-datepicker'), BDFG_PREORDERS_VERSION, true);

// Add localization for datepicker
wp_localize_script('bdfg-preorders-admin', 'bdfg_preorders', array(
'date_format' => _x('yy-mm-dd', 'jQuery date format', 'bdfg-preorders'),
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg_preorders_admin_nonce'),
'i18n' => array(
'confirm_delete' => __('Are you sure you want to delete this pre-order? This cannot be undone.', 'bdfg-preorders'),
'error' => __('An error occurred. Please try again.', 'bdfg-preorders'),
'success' => __('Operation completed successfully.', 'bdfg-preorders')
)
));
}
}
}

includes/class-bdfg-preorders-frontend.php

相关文章: WordPress 智能缓存管理

<?php
/**
* Frontend functionality for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Frontend class for handling customer-facing operations
*/
class BDFG_PreOrders_Frontend {
/**
* Constructor
*/
public function __construct() {
// Modify add to cart button
add_filter('woocommerce_product_single_add_to_cart_text', array($this, 'change_add_to_cart_text'), 10, 2);
add_filter('woocommerce_product_add_to_cart_text', array($this, 'change_add_to_cart_text'), 10, 2);

// Modify product price display
add_filter('woocommerce_get_price_html', array($this, 'modify_price_display'), 10, 2);

// Add availability info
add_action('woocommerce_before_add_to_cart_button', array($this, 'add_availability_info'));
add_action('woocommerce_before_add_to_cart_form', array($this, 'add_preorder_badge'));

// Add custom message
add_action('woocommerce_before_single_product_summary', array($this, 'add_custom_message'), 15);

// Add countdown timer
add_action('woocommerce_before_add_to_cart_form', array($this, 'add_countdown_timer'));

// Modify cart item data
add_filter('woocommerce_add_cart_item_data', array($this, 'add_preorder_cart_item_data'), 10, 3);
add_filter('woocommerce_get_item_data', array($this, 'add_preorder_cart_item_meta'), 10, 2);

// Modify cart and checkout totals
add_action('woocommerce_cart_totals_before_order_total', array($this, 'add_preorder_cart_totals'));
add_action('woocommerce_review_order_before_order_total', array($this, 'add_preorder_cart_totals'));

// Add custom styles and scripts
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));

// Add variation data
add_filter('woocommerce_available_variation', array($this, 'add_variation_data'), 10, 3);

// Add account actions for preorder management
add_filter('woocommerce_my_account_my_orders_actions', array($this, 'add_order_actions'), 10, 2);

// Add my account endpoint for preorder management
add_action('init', array($this, 'add_preorders_endpoint'));
add_filter('woocommerce_account_menu_items', array($this, 'add_preorders_menu_item'));
add_action('woocommerce_account_preorders_endpoint', array($this, 'preorders_content'));

// AJAX handlers for customer actions
add_action('wp_ajax_bdfg_preorders_cancel', array($this, 'cancel_preorder_ajax'));
}

/**
* Modify add to cart button text
*
* @param string $text
* @param WC_Product $product
* @return string
*/
public function change_add_to_cart_text($text, $product) {
if (BDFG_PreOrders_Product::is_preorder($product->get_id())) {
$custom_text = get_post_meta($product->get_id(), '_bdfg_preorder_button_text', true);

if (!empty($custom_text)) {
return $custom_text;
}

return get_option('bdfg_preorders_button_text', __('Pre-Order Now', 'bdfg-preorders'));
}

return $text;
}

/**
* Modify price display
*
* @param string $price_html
* @param WC_Product $product
* @return string
*/
public function modify_price_display($price_html, $product) {
if (BDFG_PreOrders_Product::is_preorder($product->get_id())) {
$pricing_type = get_post_meta($product->get_id(), '_bdfg_preorder_pricing_type', true);
$adjustment = get_post_meta($product->get_id(), '_bdfg_preorder_price_adjustment', true);

if (!empty($pricing_type) && !empty($adjustment) && $adjustment != 0) {
$regular_price = $product->get_regular_price();

if ($pricing_type === 'fixed') {
$adjusted_price = $regular_price + $adjustment;
} else { // percentage
$adjusted_price = $regular_price * (1 + ($adjustment / 100));
}

if ($adjusted_price < 0) {
$adjusted_price = 0;
}

// For preorder products, add custom price label
$price_html = '<span class="bdfg-preorder-price">' . wc_price($adjusted_price) . '</span>';

// Show original price and discount/premium indicators
if ($adjustment < 0) {
// Show discount
$price_html .= ' <span class="bdfg-preorder-original-price"><del>' . wc_price($regular_price) . '</del></span>';

if ($pricing_type === 'fixed') {
$discount_text = wc_price(abs($adjustment)) . ' ' . __('off', 'bdfg-preorders');
} else {
$discount_text = abs($adjustment) . '% ' . __('off', 'bdfg-preorders');
}

$price_html .= ' <span class="bdfg-preorder-discount">(' . $discount_text . ')</span>';
} elseif ($adjustment > 0) {
// Show premium
$price_html .= ' <span class="bdfg-preorder-original-price"><del>' . wc_price($regular_price) . '</del></span>';

if ($pricing_type === 'fixed') {
$premium_text = wc_price($adjustment) . ' ' . __('premium', 'bdfg-preorders');
} else {
$premium_text = $adjustment . '% ' . __('premium', 'bdfg-preorders');
}

$price_html .= ' <span class="bdfg-preorder-premium">(' . $premium_text . ')</span>';
}

return $price_html;
}

// Add preorder label even if no price adjustment
return '<span class="bdfg-preorder-price">' . $price_html . '</span>';
}

return $price_html;
}

/**
* Add preorder badge to product
*/
public function add_preorder_badge() {
global $product;

if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

echo '<span class="bdfg-preorder-badge">' . esc_html__('Pre-Order', 'bdfg-preorders') . '</span>';
}

/**
* Add availability info
*/
public function add_availability_info() {
global $product;

if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

$date = get_post_meta($product->get_id(), '_bdfg_preorder_available_date', true);
$time = get_post_meta($product->get_id(), '_bdfg_preorder_available_time', true);

if (empty($date)) {
return;
}

$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if (!empty($time)) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

$availability_text = get_option('bdfg_preorders_availability_text', __('Available on: %date%', 'bdfg-preorders'));
$availability_text = str_replace('%date%', $formatted_date, $availability_text);

echo '<div class="bdfg-preorder-availability">' . esc_html($availability_text) . '</div>';
}

/**
* Add custom message to product page
*/
public function add_custom_message() {
global $product;

if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

$custom_message = get_post_meta($product->get_id(), '_bdfg_preorder_custom_message', true);

if (!empty($custom_message)) {
echo '<div class="bdfg-preorder-message">' . wp_kses_post(wpautop($custom_message)) . '</div>';
}
}

/**
* Add countdown timer
*/
public function add_countdown_timer() {
global $product;

if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

// Check if countdown timer is enabled
if (get_option('bdfg_preorders_show_countdown', 'yes') !== 'yes') {
return;
}

$date = get_post_meta($product->get_id(), '_bdfg_preorder_available_date', true);
$time = get_post_meta($product->get_id(), '_bdfg_preorder_available_time', true);

if (empty($date)) {
return;
}

$datetime = $date;

if (!empty($time)) {
$datetime .= ' ' . $time;
} else {
$datetime .= ' 00:00:00';
}

$target_time = strtotime($datetime);
$current_time = current_time('timestamp');

// Only show countdown if not yet released
if ($target_time > $current_time) {
$countdown_data = array(
'year' => date('Y', $target_time),
'month' => date('m', $target_time),
'day' => date('d', $target_time),
'hour' => date('H', $target_time),
'minute' => date('i', $target_time),
'second' => date('s', $target_time)
);

echo '<div class="bdfg-preorder-countdown" data-countdown="' . esc_attr(json_encode($countdown_data)) . '">';
echo '<div class="countdown-label">' . esc_html__('Available in:', 'bdfg-preorders') . '</div>';
echo '<div class="countdown-timer">';
echo '<span class="days">00</span><span class="countdown-sep">:</span>';
echo '<span class="hours">00</span><span class="countdown-sep">:</span>';
echo '<span class="minutes">00</span><span class="countdown-sep">:</span>';
echo '<span class="seconds">00</span>';
echo '</div>';
echo '<div class="countdown-labels">';
echo '<span class="days-label">' . esc_html__('Days', 'bdfg-preorders') . '</span> ';
echo '<span class="hours-label">' . esc_html__('Hours', 'bdfg-preorders') . '</span> ';
echo '<span class="minutes-label">' . esc_html__('Min', 'bdfg-preorders') . '</span> ';
echo '<span class="seconds-label">' . esc_html__('Sec', 'bdfg-preorders') . '</span>';
echo '</div>';
echo '</div>';
}
}

/**
* Add preorder data to cart item
*
* @param array $cart_item_data
* @param int $product_id
* @param int $variation_id
* @return array
*/
public function add_preorder_cart_item_data($cart_item_data, $product_id, $variation_id) {
$target_id = $variation_id ? $variation_id : $product_id;

if (BDFG_PreOrders_Product::is_preorder($target_id)) {
$cart_item_data['bdfg_preorder'] = array(
'is_preorder' => true,
'available_date' => BDFG_PreOrders_Product::get_available_date($target_id)
);
}

return $cart_item_data;
}

/**
* Add preorder info to cart item meta
*
* @param array $item_data
* @param array $cart_item
* @return array
*/
public function add_preorder_cart_item_meta($item_data, $cart_item) {
if (isset($cart_item['bdfg_preorder']) && $cart_item['bdfg_preorder']['is_preorder']) {
$available_date = $cart_item['bdfg_preorder']['available_date'];

if ($available_date) {
$formatted_date = BDFG_PreOrders_Helper::format_date($available_date);

$item_data[] = array(
'key' => __('Pre-order', 'bdfg-preorders'),
'value' => sprintf(__('Available on %s', 'bdfg-preorders'), $formatted_date),
'display' => ''
);
} else {
$item_data[] = array(
'key' => __('Pre-order', 'bdfg-preorders'),
'value' => __('This is a pre-order item', 'bdfg-preorders'),
'display' => ''
);
}
}

return $item_data;
}

/**
* Add preorder totals to cart
*/
public function add_preorder_cart_totals() {
$cart = WC()->cart;
$has_preorder = false;
$total_preorder = 0;

// Check if the cart contains preorders
foreach ($cart->get_cart() as $cart_item) {
if (isset($cart_item['bdfg_preorder']) && $cart_item['bdfg_preorder']['is_preorder']) {
$has_preorder = true;
$total_preorder += $cart_item['line_total'];
}
}

if ($has_preorder) {
// Display preorder subtotal
?>
<tr class="bdfg-preorder-total">
<th><?php esc_html_e('Pre-Order Total', 'bdfg-preorders'); ?></th>
<td data-title="<?php esc_attr_e('Pre-Order Total', 'bdfg-preorders'); ?>">
<?php echo wc_price($total_preorder); ?>
</td>
</tr>
<?php
}
}

/**
* Load frontend styles and scripts
*/
public function enqueue_styles() {
// Load CSS for the frontend
wp_enqueue_style('bdfg-preorders-frontend', BDFG_PREORDERS_URL . 'assets/css/frontend.css', array(), BDFG_PREORDERS_VERSION);

// Load JavaScript for the frontend
wp_enqueue_script('bdfg-preorders-frontend', BDFG_PREORDERS_URL . 'assets/js/frontend.js', array('jquery'), BDFG_PREORDERS_VERSION, true);

// Add dynamic styles
$button_color = get_option('bdfg_preorders_button_color', '#3d9cd2');
$custom_css = "
.single_add_to_cart_button.bdfg-preorder-button {
background-color: {$button_color} !important;
border-color: {$button_color} !important;
}
.bdfg-preorder-badge {
background-color: {$button_color};
}
";

wp_add_inline_style('bdfg-preorders-frontend', $custom_css);

// Localize script for the frontend
wp_localize_script('bdfg-preorders-frontend', 'bdfg_preorders_params', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bdfg_preorders_nonce'),
'i18n' => array(
'days' => __('Days', 'bdfg-preorders'),
'hours' => __('Hours', 'bdfg-preorders'),
'minutes' => __('Minutes', 'bdfg-preorders'),
'seconds' => __('Seconds', 'bdfg-preorders')
)
));
}

/**
* Add variation data
*
* @param array $data
* @param WC_Product $product
* @param WC_Product_Variation $variation
* @return array
*/
public function add_variation_data($data, $product, $variation) {
if (BDFG_PreOrders_Product::is_preorder($variation->get_id())) {
$data['is_preorder'] = true;

$date = get_post_meta($variation->get_id(), '_bdfg_preorder_available_date', true);
$time = get_post_meta($variation->get_id(), '_bdfg_preorder_available_time', true);

if (!empty($date)) {
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if (!empty($time)) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

$availability_text = get_option('bdfg_preorders_availability_text', __('Available on: %date%', 'bdfg-preorders'));
$availability_text = str_replace('%date%', $formatted_date, $availability_text);

$data['availability_html'] = '<div class="bdfg-preorder-availability">' . esc_html($availability_text) . '</div>';
}

$custom_text = get_post_meta($variation->get_id(), '_bdfg_preorder_button_text', true);

if (!empty($custom_text)) {
$data['button_text'] = $custom_text;
} else {
$data['button_text'] = get_option('bdfg_preorders_button_text', __('Pre-Order Now', 'bdfg-preorders'));
}
}

return $data;
}

/**
* Add endpoint for my account page
*/
public function add_preorders_endpoint() {
add_rewrite_endpoint('preorders', EP_ROOT | EP_PAGES);
}

/**
* Add menu item to my account page
*
* @param array $items
* @return array
*/
public function add_preorders_menu_item($items) {
// Add the preorders item after the orders item
$new_items = array();

foreach ($items as $key => $value) {
$new_items[$key] = $value;

if ($key === 'orders') {
$new_items['preorders'] = __('Pre-Orders', 'bdfg-preorders');
}
}

return $new_items;
}

/**
* Display preorders content on my account page
*/
public function preorders_content() {
$customer_orders = $this->get_customer_preorders();

if ($customer_orders) {
?>
<table class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table">
<thead>
<tr>
<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-number">
<span class="nobr"><?php esc_html_e('Order', 'bdfg-preorders'); ?></span>
</th>
<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date">
<span class="nobr"><?php esc_html_e('Date', 'bdfg-preorders'); ?></span>
</th>
<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status">
<span class="nobr"><?php esc_html_e('Status', 'bdfg-preorders'); ?></span>
</th>
<th class="woocommerce-orders-table__header woocommerce-orders-table__header-preorder-release">
<span class="nobr"><?php esc_html_e('Release Date', 'bdfg-preorders'); ?></span>
</th>
<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-actions">
<span class="nobr"><?php esc_html_e('Actions', 'bdfg-preorders'); ?></span>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($customer_orders as $customer_order) : ?>
<?php
$order = wc_get_order($customer_order->ID);
$item_count = $order->get_item_count();
$release_date = $this->get_preorder_release_date($order);
?>
<tr class="woocommerce-orders-table__row order">
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-number" data-title="<?php esc_attr_e('Order', 'bdfg-preorders'); ?>">
<a href="<?php echo esc_url($order->get_view_order_url()); ?>">
<?php echo esc_html(_x('#', 'hash before order number', 'bdfg-preorders') . $order->get_order_number()); ?>
</a>
</td>
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" data-title="<?php esc_attr_e('Date', 'bdfg-preorders'); ?>">
<time datetime="<?php echo esc_attr($order->get_date_created()->date('c')); ?>"><?php echo esc_html(wc_format_datetime($order->get_date_created())); ?></time>
</td>
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" data-title="<?php esc_attr_e('Status', 'bdfg-preorders'); ?>">
<?php echo esc_html(wc_get_order_status_name($order->get_status())); ?>
</td>
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-preorder-release" data-title="<?php esc_attr_e('Release Date', 'bdfg-preorders'); ?>">
<?php echo !empty($release_date) ? esc_html(BDFG_PreOrders_Helper::format_date($release_date)) : esc_html__('To be announced', 'bdfg-preorders'); ?>
</td>
<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-actions" data-title="<?php esc_attr_e('Actions', 'bdfg-preorders'); ?>">
<?php
$actions = $this->get_preorder_actions($order);

if (!empty($actions)) {
foreach ($actions as $key => $action) {
echo '<a href="' . esc_url($action['url']) . '" class="woocommerce-button button ' . sanitize_html_class($key) . '">' . esc_html($action['name']) . '</a>';
}
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
} else {
?>
<div class="woocommerce-message woocommerce-message--info woocommerce-Message woocommerce-Message--info woocommerce-info">
<a class="woocommerce-Button button" href="<?php echo esc_url(apply_filters('woocommerce_return_to_shop_redirect', wc_get_page_permalink('shop'))); ?>">
<?php esc_html_e('Browse products', 'bdfg-preorders'); ?>
</a>
<?php esc_html_e('No pre-orders have been placed yet.', 'bdfg-preorders'); ?>
</div>
<?php
}
}

/**
* Get customer preorders
*
* @return array
*/
private function get_customer_preorders() {
$customer_id = get_current_user_id();

if (!$customer_id) {
return array();
}

$args = array(
'meta_key' => '_bdfg_has_preorder',
'meta_value' => 'yes',
'post_type' => 'shop_order',
'post_status' => array_keys(wc_get_order_statuses()),
'numberposts' => -1,
'meta_query' => array(
array(
'key' => '_customer_user',
'value' => $customer_id,
'compare' => '='
)
)
);

return get_posts($args);
}

/**
* Get preorder release date
*
* @param WC_Order $order
* @return string|false
*/
private function get_preorder_release_date($order) {
$earliest_date = false;

foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$date = $item->get_meta('_bdfg_preorder_available_date');
$time = $item->get_meta('_bdfg_preorder_available_time');

if ($date) {
$datetime = $date;
if ($time) {
$datetime .= ' ' . $time;
}

if (!$earliest_date || strtotime($datetime) < strtotime($earliest_date)) {
$earliest_date = $datetime;
}
}
}
}

return $earliest_date;
}

/**
* Add order actions
*
* @param array $actions
* @param WC_Order $order
* @return array
*/
public function add_order_actions($actions, $order) {
// Only process if this is a preorder
if ($order->get_meta('_bdfg_has_preorder') !== 'yes') {
return $actions;
}

// Get custom actions
$custom_actions = $this->get_preorder_actions($order);

// Merge with existing actions
return array_merge($actions, $custom_actions);
}

/**
* Get preorder actions
*
* @param WC_Order $order
* @return array
*/
private function get_preorder_actions($order) {
$actions = array();
$release_date = $this->get_preorder_release_date($order);

// Only allow cancellation if enabled globally and for this product
if (get_option('bdfg_preorders_enable_cancellations', 'yes') === 'yes' && $order->has_status('preordered')) {
// Check if we're before the cancellation limit
if ($release_date) {
$cancellation_limit = get_option('bdfg_preorders_cancellation_limit', '24');
$limit_time = strtotime($release_date) - ($cancellation_limit * HOUR_IN_SECONDS);

if (current_time('timestamp') < $limit_time) {
$actions['cancel-preorder'] = array(
'url' => wp_nonce_url(
add_query_arg(
array(
'action' => 'bdfg_cancel_preorder',
'order_id' => $order->get_id()
),
wc_get_endpoint_url('preorders')
),
'bdfg_cancel_preorder_' . $order->get_id()
),
'name' => __('Cancel Pre-order', 'bdfg-preorders')
);
}
} else {
// If no release date is set, always allow cancellation
$actions['cancel-preorder'] = array(
'url' => wp_nonce_url(
add_query_arg(
array(
'action' => 'bdfg_cancel_preorder',
'order_id' => $order->get_id()
),
wc_get_endpoint_url('preorders')
),
'bdfg_cancel_preorder_' . $order->get_id()
),
'name' => __('Cancel Pre-order', 'bdfg-preorders')
);
}
}

return $actions;
}

/**
* Cancel preorder via AJAX
*/
public function cancel_preorder_ajax() {
check_ajax_referer('bdfg_preorders_nonce', 'nonce');

$order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;

if (!$order_id) {
wp_send_json_error(array('message' => __('Invalid order ID.', 'bdfg-preorders')));
}

$order = wc_get_order($order_id);

if (!$order || $order->get_customer_id() !== get_current_user_id()) {
wp_send_json_error(array('message' => __('You do not have permission to cancel this pre-order.', 'bdfg-preorders')));
}

if ($order->get_meta('_bdfg_has_preorder') !== 'yes' || !$order->has_status('preordered')) {
wp_send_json_error(array('message' => __('This order cannot be cancelled.', 'bdfg-preorders')));
}

// Process cancellation
$order->update_status('cancelled', __('Pre-order cancelled by customer.', 'bdfg-preorders'));

// Update preorder database records
global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

$wpdb->update(
$table_name,
array(
'status' => 'cancelled',
'updated_at' => current_time('mysql')
),
array('order_id' => $order_id),
array('%s', '%s'),
array('%d')
);

wp_send_json_success(array(
'message' => __('Your pre-order has been cancelled.', 'bdfg-preorders'),
'redirect' => wc_get_endpoint_url('preorders')
));
}
}

includes/class-bdfg-preorders-orders.php

<?php
/**
* Order handling for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Orders class for handling order-related operations
*/
class BDFG_PreOrders_Orders {
/**
* Constructor
*/
public function __construct() {
// Add preorder data to order items
add_action('woocommerce_checkout_create_order_line_item', array($this, 'add_preorder_data_to_order_items'), 10, 4);

// Process preorder after order is placed
add_action('woocommerce_checkout_order_processed', array($this, 'process_preorder'), 10, 3);

// Add preorder label to order items
add_action('woocommerce_before_order_itemmeta', array($this, 'add_preorder_order_item_label'), 10, 2);

// Add preorder details to order emails
add_action('woocommerce_email_order_details', array($this, 'add_preorder_details_to_emails'), 10, 4);

// Add preorder details to order page
add_action('woocommerce_order_details_after_order_table', array($this, 'add_preorder_details_to_order_page'));

// Schedule availability check
add_action('init', array($this, 'schedule_availability_check'));
add_action('bdfg_preorders_check_availability', array($this, 'check_preorder_availability'));

// Handle status transitions
add_action('woocommerce_order_status_changed', array($this, 'handle_order_status_change'), 10, 4);

// Register custom order statuses
add_action('init', array($this, 'register_custom_order_statuses'));

// Filter valid order statuses for preorders
add_filter('wc_order_statuses', array($this, 'add_order_statuses'));
add_filter('woocommerce_valid_order_statuses_for_payment', array($this, 'add_valid_order_statuses_for_payment'));
add_filter('woocommerce_payment_complete_order_status', array($this, 'payment_complete_order_status'), 10, 3);
}

/**
* Add preorder data to order items
*
* @param WC_Order_Item_Product $item
* @param string $cart_item_key
* @param array $values
* @param WC_Order $order
*/
public function add_preorder_data_to_order_items($item, $cart_item_key, $values, $order) {
if (isset($values['bdfg_preorder']) && $values['bdfg_preorder']['is_preorder']) {
// Mark as preorder
$item->add_meta_data('_bdfg_is_preorder', 'yes', true);

// Save product and variation IDs
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$target_id = $variation_id ? $variation_id : $product_id;

// Save available date and time
$date = get_post_meta($target_id, '_bdfg_preorder_available_date', true);
$time = get_post_meta($target_id, '_bdfg_preorder_available_time', true);

if ($date) {
$item->add_meta_data('_bdfg_preorder_available_date', $date, true);

if ($time) {
$item->add_meta_data('_bdfg_preorder_available_time', $time, true);
}

// Format date for display
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if ($time) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

$item->add_meta_data(__('Available on', 'bdfg-preorders'), $formatted_date, true);
}

// Save price adjustment
$pricing_type = get_post_meta($target_id, '_bdfg_preorder_pricing_type', true);
$price_adjustment = get_post_meta($target_id, '_bdfg_preorder_price_adjustment', true);

if ($pricing_type && $price_adjustment) {
$item->add_meta_data('_bdfg_preorder_pricing_type', $pricing_type, true);
$item->add_meta_data('_bdfg_preorder_price_adjustment', $price_adjustment, true);
}
}
}

/**
* Process preorder
*
* @param int $order_id
* @param array $posted_data
* @param WC_Order $order
*/
public function process_preorder($order_id, $posted_data, $order) {
$has_preorder = false;

// Check if order contains preorder products
foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$has_preorder = true;
break;
}
}

// If order contains preorder products, update order status and record to database
if ($has_preorder) {
// Add preorder flag to order
$order->add_meta_data('_bdfg_has_preorder', 'yes', true);
$order->save();

// Update order status for preorder if needed
if ($order->has_status(array('processing', 'completed'))) {
$order->update_status('preordered', __('Order contains pre-order products.', 'bdfg-preorders'));
}

// Record preorder items to custom table for tracking
global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$available_date = $item->get_meta('_bdfg_preorder_available_date');
$available_time = $item->get_meta('_bdfg_preorder_available_time');

// Build complete datetime
$datetime = $available_date;
if ($available_time) {
$datetime .= ' ' . $available_time;
}

// Calculate preorder price
$pricing_type = $item->get_meta('_bdfg_preorder_pricing_type');
$price_adjustment = $item->get_meta('_bdfg_preorder_price_adjustment');
$preorder_price = $item->get_total();

// Insert record
$wpdb->insert(
$table_name,
array(
'order_id' => $order_id,
'product_id' => $product_id,
'variation_id' => $variation_id ? $variation_id : null,
'available_date' => $datetime ? date('Y-m-d H:i:s', strtotime($datetime)) : null,
'preorder_price' => $preorder_price,
'status' => 'pending',
'created_at' => current_time('mysql'),
'updated_at' => current_time('mysql')
),
array(
'%d',
'%d',
'%d',
'%s',
'%f',
'%s',
'%s',
'%s'
)
);

// Add preorder meta if needed
$preorder_id = $wpdb->insert_id;
$meta_table_name = $wpdb->prefix . 'bdfg_preorder_meta';

if ($pricing_type && $price_adjustment) {
$wpdb->insert(
$meta_table_name,
array(
'preorder_id' => $preorder_id,
'meta_key' => 'pricing_type',
'meta_value' => $pricing_type
),
array('%d', '%s', '%s')
);

$wpdb->insert(
$meta_table_name,
array(
'preorder_id' => $preorder_id,
'meta_key' => 'price_adjustment',
'meta_value' => $price_adjustment
),
array('%d', '%s', '%s')
);
}
}
}
}
}

/**
* Add preorder label to order items
*
* @param int $item_id
* @param WC_Order_Item $item
*/
public function add_preorder_order_item_label($item_id, $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$date = $item->get_meta('_bdfg_preorder_available_date');
$time = $item->get_meta('_bdfg_preorder_available_time');

echo '<div class="bdfg-preorder-label">' . esc_html__('Pre-Order', 'bdfg-preorders');

if ($date) {
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if ($time) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

echo ' - ' . esc_html__('Available on', 'bdfg-preorders') . ': ' . esc_html($formatted_date);
}

echo '</div>';
}
}

/**
* Add preorder details to emails
*
* @param WC_Order $order
* @param bool $sent_to_admin
* @param bool $plain_text
* @param WC_Email $email
*/
public function add_preorder_details_to_emails($order, $sent_to_admin, $plain_text, $email) {
if ($order->get_meta('_bdfg_has_preorder') === 'yes') {
if ($plain_text) {
echo "\n\n" . __('Pre-Order Information', 'bdfg-preorders') . "\n\n";

foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$product = $item->get_product();
$date = $item->get_meta('_bdfg_preorder_available_date');
$time = $item->get_meta('_bdfg_preorder_available_time');

echo $product->get_name() . ': ' . __('Pre-ordered', 'bdfg-preorders') . "\n";

if ($date) {
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if ($time) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

echo __('Available on', 'bdfg-preorders') . ': ' . $formatted_date . "\n";
}

echo "\n";
}
}
} else {
echo '<h2>' . __('Pre-Order Information', 'bdfg-preorders') . '</h2>';
echo '<div class="bdfg-preorder-info-box">';
echo '<p>' . __('Your order contains pre-order items that will be available at a later date.', 'bdfg-preorders') . '</p>';
echo '<table class="td" cellspacing="0" cellpadding="6" style="width: 100%; margin-bottom: 20px;">';
echo '<thead><tr>';
echo '<th>' . __('Product', 'bdfg-preorders') . '</th>';
echo '<th>' . __('Status', 'bdfg-preorders') . '</th>';
echo '<th>' . __('Availability', 'bdfg-preorders') . '</th>';
echo '</tr></thead><tbody>';

foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$product = $item->get_product();
$date = $item->get_meta('_bdfg_preorder_available_date');
$time = $item->get_meta('_bdfg_preorder_available_time');

echo '<tr>';
echo '<td>' . esc_html($product->get_name()) . '</td>';
echo '<td>' . esc_html__('Pre-ordered', 'bdfg-preorders') . '</td>';

if ($date) {
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if ($time) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

echo '<td>' . esc_html($formatted_date) . '</td>';
} else {
echo '<td>' . esc_html__('To be announced', 'bdfg-preorders') . '</td>';
}

echo '</tr>';
}
}

echo '</tbody></table>';
echo '</div>';
}
}
}

/**
* Add preorder details to order page
*
* @param WC_Order $order
*/
public function add_preorder_details_to_order_page($order) {
if ($order->get_meta('_bdfg_has_preorder') === 'yes') {
echo '<h2>' . esc_html__('Pre-Order Information', 'bdfg-preorders') . '</h2>';
echo '<div class="bdfg-preorder-details">';
echo '<p>' . esc_html__('Your order contains the following pre-order items:', 'bdfg-preorders') . '</p>';
echo '<table class="woocommerce-table shop_table preorder_details">';
echo '<thead>';
echo '<tr>';
echo '<th>' . esc_html__('Product', 'bdfg-preorders') . '</th>';
echo '<th>' . esc_html__('Status', 'bdfg-preorders') . '</th>';
echo '<th>' . esc_html__('Availability', 'bdfg-preorders') . '</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';

$current_time = current_time('timestamp');

foreach ($order->get_items() as $item) {
if ($item->get_meta('_bdfg_is_preorder') === 'yes') {
$product = $item->get_product();
$date = $item->get_meta('_bdfg_preorder_available_date');
$time = $item->get_meta('_bdfg_preorder_available_time');

echo '<tr>';

if ($product) {
echo '<td>' . esc_html($product->get_name()) . '</td>';
} else {
echo '<td>' . esc_html($item->get_name()) . '</td>';
}

$status_class = 'bdfg-preorder-status-pending';
$status_text = __('Pending Release', 'bdfg-preorders');

if ($date) {
$datetime = $date;
if ($time) {
$datetime .= ' ' . $time;
}

$release_time = strtotime($datetime);

if ($release_time <= $current_time) {
$status_class = 'bdfg-preorder-status-released';
$status_text = __('Released', 'bdfg-preorders');
}
}

echo '<td><span class="' . esc_attr($status_class) . '">' . esc_html($status_text) . '</span></td>';

if ($date) {
$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if ($time) {
$formatted_date .= ' ' . date_i18n(get_option('time_format'), strtotime($time));
}

echo '<td>' . esc_html($formatted_date);

// Show countdown if needed
if (get_option('bdfg_preorders_show_countdown', 'yes') === 'yes' && $release_time > $current_time) {
$countdown_data = array(
'year' => date('Y', $release_time),
'month' => date('m', $release_time),
'day' => date('d', $release_time),
'hour' => date('H', $release_time),
'minute' => date('i', $release_time),
'second' => date('s', $release_time)
);

echo '<div class="bdfg-preorder-countdown-small" data-countdown="' . esc_attr(json_encode($countdown_data)) . '"></div>';
}

echo '</td>';
} else {
echo '<td>' . esc_html__('To be announced', 'bdfg-preorders') . '</td>';
}

echo '</tr>';
}
}

echo '</tbody>';
echo '</table>';
echo '</div>';
}
}

/**
* Schedule availability check
*/
public function schedule_availability_check() {
if (!wp_next_scheduled('bdfg_preorders_check_availability')) {
wp_schedule_event(time(), 'hourly', 'bdfg_preorders_check_availability');
}
}

/**
* Check preorder availability
*/
public function check_preorder_availability() {
global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';
$current_datetime = current_time('mysql');

// Get preorders that have reached their availability date but are still pending
$preorders = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table_name
WHERE status = 'pending'
AND available_date IS NOT NULL
AND available_date <= %s",
$current_datetime
)
);

if (!empty($preorders)) {
foreach ($preorders as $preorder) {
// Update preorder status
$wpdb->update(
$table_name,
array(
'status' => 'available',
'updated_at' => current_time('mysql')
),
array('id' => $preorder->id),
array('%s', '%s'),
array('%d')
);

// Get the order
$order = wc_get_order($preorder->order_id);

if ($order) {
// Add order note
$product = wc_get_product($preorder->product_id);
if ($product) {
$order->add_order_note(
sprintf(
__('Pre-ordered product "%s" is now available.', 'bdfg-preorders'),
$product->get_name()
)
);
} else {
$order->add_order_note(__('A pre-ordered product is now available.', 'bdfg-preorders'));
}

// Check if all preordered products are available
$this->maybe_complete_preorder($order);

// Send email notifications if enabled
if (get_option('bdfg_preorders_enable_emails', 'yes') === 'yes') {
// Get product information
$product_id = $preorder->product_id;
$variation_id = $preorder->variation_id;

if ($variation_id) {
$product = wc_get_product($variation_id);
} else {
$product = wc_get_product($product_id);
}

if ($product) {
// Send email to customer
$emails = new BDFG_PreOrders_Emails();
$emails->send_availability_notification($order, $product);

// Send email to admin if enabled
if (get_option('bdfg_preorders_admin_notification', 'yes') === 'yes') {
$emails->send_admin_notification($order, $product);
}
}
}
}
}

// Clear any caches that might be storing the preorders
if (function_exists('wc_delete_shop_order_transients')) {
wc_delete_shop_order_transients();
}
}
}

/**
* Check if all preordered products are available and update order status if needed
*
* @param WC_Order $order
*/
private function maybe_complete_preorder($order) {
if (!$order || !$order->has_status('preordered')) {
return;
}

global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

// Check if any preordered items are still pending
$pending_items = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table_name
WHERE order_id = %d
AND status = 'pending'",
$order->get_id()
));

// If no pending items, update order status to processing
if ($pending_items == 0) {
$order->update_status('processing', __('All pre-ordered products are now available.', 'bdfg-preorders'));
}
}

/**
* Handle order status changes
*
* @param int $order_id
* @param string $from
* @param string $to
* @param WC_Order $order
*/
public function handle_order_status_change($order_id, $from, $to, $order) {
if ($order->get_meta('_bdfg_has_preorder') !== 'yes') {
return;
}

global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

// Handle cancellation
if ($to === 'cancelled') {
$wpdb->update(
$table_name,
array(
'status' => 'cancelled',
'updated_at' => current_time('mysql')
),
array('order_id' => $order_id),
array('%s', '%s'),
array('%d')
);
}

// Handle refunds
if ($to === 'refunded') {
$wpdb->update(
$table_name,
array(
'status' => 'refunded',
'updated_at' => current_time('mysql')
),
array('order_id' => $order_id),
array('%s', '%s'),
array('%d')
);
}
}

/**
* Register custom order statuses
*/
public function register_custom_order_statuses() {
register_post_status('wc-preordered', array(
'label' => __('Pre-ordered', 'bdfg-preorders'),
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop('Pre-ordered <span class="count">(%s)</span>', 'Pre-ordered <span class="count">(%s)</span>', 'bdfg-preorders')
));
}

/**
* Add custom order statuses to WooCommerce
*
* @param array $order_statuses
* @return array
*/
public function add_order_statuses($order_statuses) {
$new_statuses = array();

// Add preordered status after processing
foreach ($order_statuses as $key => $status) {
$new_statuses[$key] = $status;

if ($key === 'wc-processing') {
$new_statuses['wc-preordered'] = __('Pre-ordered', 'bdfg-preorders');
}
}

return $new_statuses;
}

/**
* Add preordered status to valid statuses for payment
*
* @param array $statuses
* @return array
*/
public function add_valid_order_statuses_for_payment($statuses) {
$statuses[] = 'preordered';
return $statuses;
}

/**
* Modify payment complete order status for preorders
*
* @param string $status
* @param int $order_id
* @param WC_Order $order
* @return string
*/
public function payment_complete_order_status($status, $order_id, $order) {
if ($order && $order->get_meta('_bdfg_has_preorder') === 'yes') {
return 'preordered';
}

return $status;
}
}

includes/class-bdfg-preorders-product.php

相关文章: WooCommerce 产品过滤器

<?php
/**
* Product helper class for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Product helper class
*/
class BDFG_PreOrders_Product {
/**
* Check if product is a preorder
*
* @param int $product_id
* @return bool
*/
public static function is_preorder($product_id) {
return get_post_meta($product_id, '_bdfg_preorder_enabled', true) === 'yes';
}

/**
* Get preorder available date
*
* @param int $product_id
* @return string|false
*/
public static function get_available_date($product_id) {
$date = get_post_meta($product_id, '_bdfg_preorder_available_date', true);
$time = get_post_meta($product_id, '_bdfg_preorder_available_time', true);

if (empty($date)) {
return false;
}

$datetime = $date;

if (!empty($time)) {
$datetime .= ' ' . $time;
}

return $datetime;
}

/**
* Check if product is available (release date has passed)
*
* @param int $product_id
* @return bool
*/
public static function is_available($product_id) {
$datetime = self::get_available_date($product_id);

if (!$datetime) {
return true; // If no date set, consider it available
}

$timestamp = strtotime($datetime);
$current_time = current_time('timestamp');

return $timestamp <= $current_time;
}

/**
* Get adjusted price
*
* @param WC_Product $product
* @return float
*/
public static function get_adjusted_price($product) {
$product_id = $product->get_id();
$pricing_type = get_post_meta($product_id, '_bdfg_preorder_pricing_type', true);
$adjustment = get_post_meta($product_id, '_bdfg_preorder_price_adjustment', true);

if (empty($pricing_type) || empty($adjustment) || $adjustment == 0) {
return $product->get_price();
}

$price = $product->get_regular_price();

if ($pricing_type === 'fixed') {
$adjusted_price = $price + $adjustment;
} else { // percentage
$adjusted_price = $price * (1 + ($adjustment / 100));
}

return max(0, $adjusted_price);
}

/**
* Get all preorder products
*
* @param array $args Additional query args
* @return array
*/
public static function get_preorder_products($args = array()) {
$query_args = wp_parse_args($args, array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_bdfg_preorder_enabled',
'value' => 'yes',
'compare' => '='
)
)
));

$products = get_posts($query_args);

return $products;
}

/**
* Get upcoming preorder products
*
* @param int $limit Number of products to return
* @return array
*/
public static function get_upcoming_preorders($limit = 5) {
global $wpdb;

$products = $wpdb->get_results($wpdb->prepare(
"SELECT p.ID, p.post_title, pm1.meta_value AS date, pm2.meta_value AS time
FROM {$wpdb->posts} p
JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_bdfg_preorder_enabled' AND pm.meta_value = 'yes'
JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_bdfg_preorder_available_date'
LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_bdfg_preorder_available_time'
WHERE p.post_type = 'product'
AND p.post_status = 'publish'
AND STR_TO_DATE(CONCAT(pm1.meta_value, ' ', IFNULL(pm2.meta_value, '00:00:00')), '%%Y-%%m-%%d %%H:%%i:%%s') > NOW()
ORDER BY STR_TO_DATE(CONCAT(pm1.meta_value, ' ', IFNULL(pm2.meta_value, '00:00:00')), '%%Y-%%m-%%d %%H:%%i:%%s') ASC
LIMIT %d",
$limit
));

return $products;
}

/**
* Get preorder statistics
*
* @return array
*/
public static function get_preorder_stats() {
global $wpdb;

$stats = array(
'total_products' => 0,
'pending_products' => 0,
'active_preorders' => 0,
'revenue' => 0
);

// Count total preorder products
$stats['total_products'] = $wpdb->get_var(
"SELECT COUNT(post_id)
FROM {$wpdb->postmeta}
WHERE meta_key = '_bdfg_preorder_enabled'
AND meta_value = 'yes'"
);

// Count pending release products
$current_date = date('Y-m-d');
$stats['pending_products'] = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(p.ID)
FROM {$wpdb->posts} p
JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_bdfg_preorder_enabled' AND pm.meta_value = 'yes'
JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_bdfg_preorder_available_date'
WHERE p.post_type = 'product'
AND p.post_status = 'publish'
AND pm1.meta_value >= %s",
$current_date
));

// Count active preorders and revenue
$table_name = $wpdb->prefix . 'bdfg_preorders';

if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name) {
$stats['active_preorders'] = $wpdb->get_var(
"SELECT COUNT(id) FROM $table_name WHERE status = 'pending'"
);

$stats['revenue'] = $wpdb->get_var(
"SELECT SUM(preorder_price) FROM $table_name WHERE status IN ('pending', 'available')"
);
}

return $stats;
}
}

includes/class-bdfg-preorders-emails.php

<?php
/**
* Email handling for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Emails class for handling email notifications
*/
class BDFG_PreOrders_Emails {
/**
* Constructor
*/
public function __construct() {
// Add new emails to WooCommerce
add_filter('woocommerce_email_classes', array($this, 'add_emails'));
}

/**
* Add new emails
*
* @param array $email_classes
* @return array
*/
public function add_emails($email_classes) {
// Add custom email classes here if needed
return $email_classes;
}

/**
* Send availability notification to customer
*
* @param WC_Order $order
* @param WC_Product $product
* @return bool
*/
public function send_availability_notification($order, $product) {
if (!$order || !$product) {
return false;
}

// Get settings
$subject = get_option('bdfg_preorders_email_subject', __('Your pre-ordered product is now available!', 'bdfg-preorders'));
$heading = get_option('bdfg_preorders_email_heading', __('Pre-order Available', 'bdfg-preorders'));
$content = get_option('bdfg_preorders_email_content', __("Hello {customer_name},\n\nYour pre-ordered product '{product_name}' is now available.\n\nThank you for your patience and support!\n\nBeiduofengou Team", 'bdfg-preorders'));

// Replace placeholders
$customer_name = $order->get_billing_first_name();
$product_name = $product->get_name();
$order_id = $order->get_id();
$available_date = date_i18n(get_option('date_format'));

$content = str_replace('{customer_name}', $customer_name, $content);
$content = str_replace('{product_name}', $product_name, $content);
$content = str_replace('{order_id}', $order_id, $content);
$content = str_replace('{availability_date}', $available_date, $content);

// Prepare email
$email_content = $this->get_email_template($heading, $content, $order);

// Send email
$customer_email = $order->get_billing_email();

if (!empty($customer_email)) {
$headers = array('Content-Type: text/html; charset=UTF-8');
$result = wp_mail($customer_email, $subject, $email_content, $headers);

if ($result) {
$order->add_order_note(
sprintf(
__('Pre-order availability notification sent to customer for product: %s', 'bdfg-preorders'),
$product->get_name()
)
);
}

return $result;
}

return false;
}

/**
* Send notification to admin
*
* @param WC_Order $order
* @param WC_Product $product
* @return bool
*/
public function send_admin_notification($order, $product) {
if (!$order || !$product) {
return false;
}

// Get admin email
$admin_email = get_option('admin_email');

if (empty($admin_email)) {
return false;
}

// Get subject
$subject = get_option('bdfg_preorders_admin_email_subject', __('Pre-ordered product now available', 'bdfg-preorders'));

// Prepare message
$message = sprintf(
__('Pre-ordered product "%1$s" (ID: %2$s) is now available. Order #%3$s.', 'bdfg-preorders'),
$product->get_name(),
$product->get_id(),
$order->get_order_number()
);

// Add order link
$message .= "\n\n";
$message .= sprintf(
__('View order: %s', 'bdfg-preorders'),
admin_url('post.php?post=' . $order->get_id() . '&action=edit')
);

// Send email
$headers = array('Content-Type: text/html; charset=UTF-8');

$email_content = '<p>' . nl2br(esc_html($message)) . '</p>';

return wp_mail($admin_email, $subject, $email_content, $headers);
}

/**
* Get email template
*
* @param string $heading
* @param string $content
* @param WC_Order $order
* @return string
*/
private function get_email_template($heading, $content, $order) {
ob_start();

// Get email template
$template = WC()->mailer()->get_template('customer-completed-order');

// Create WooCommerce email instance
$email = new WC_Email();

// Prepare content
$email_heading = $heading;
$plain_text = false;
$sent_to_admin = false;

// Convert newlines to HTML
$content = wpautop($content);

// Add BDFG branding
$content .= '<div class="bdfg-preorder-email-branding" style="margin-top: 20px; text-align: center; padding-top: 10px; border-top: 1px solid #f2f2f2;">';
$content .= '<a href="https://beiduofengou.net" target="_blank" style="text-decoration: none; color: #3d9cd2;">';
$content .= '<img src="' . esc_url(BDFG_PREORDERS_URL . 'assets/images/bdfg-logo-email.png') . '" alt="Beiduofengou" style="max-width: 150px; margin-bottom: 10px;" />';
$content .= '</a>';
$content .= '<p style="font-size: 12px; color: #999;">' . esc_html__('Powered by BDFG Pre-Orders for WooCommerce', 'bdfg-preorders') . '</p>';
$content .= '</div>';

// Load WooCommerce email template
wc_get_template('emails/email-header.php', array('email_heading' => $email_heading));

echo wp_kses_post($content);

wc_get_template('emails/email-order-details.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email));
wc_get_template('emails/email-customer-details.php', array('order' => $order, 'sent_to_admin' => $sent_to_admin, 'plain_text' => $plain_text, 'email' => $email));

wc_get_template('emails/email-footer.php');

return ob_get_clean();
}
}

includes/helpers/class-bdfg-preorders-helper.php

相关文章: WooCommerce 高级产品定价和折扣管理

<?php
/**
* Helper functions for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Helper class with utility functions
*/
class BDFG_PreOrders_Helper {
/**
* Format date for display
*
* @param string $date Date or datetime string
* @param string $time Time string (optional)
* @return string
*/
public static function format_date($date, $time = '') {
if (empty($date)) {
return '';
}

// Check if date contains time
if (strpos($date, ' ') !== false) {
list($date_part, $time_part) = explode(' ', $date);

$formatted_date = date_i18n(get_option('date_format'), strtotime($date_part));
$formatted_time = date_i18n(get_option('time_format'), strtotime($time_part));

return $formatted_date . ' ' . $formatted_time;
}

$formatted_date = date_i18n(get_option('date_format'), strtotime($date));

if (!empty($time)) {
$formatted_time = date_i18n(get_option('time_format'), strtotime($time));
return $formatted_date . ' ' . $formatted_time;
}

return $formatted_date;
}

/**
* Log debug information
*
* @param mixed $message
* @return void
*/
public static function log($message) {
// Only log if debug mode is enabled
if (get_option('bdfg_preorders_debug_mode') !== 'yes') {
return;
}

$log_file = BDFG_PREORDERS_PATH . 'logs/debug.log';

// Create logs directory if it doesn't exist
if (!file_exists(dirname($log_file))) {
wp_mkdir_p(dirname($log_file));
}

// Format message
if (is_array($message) || is_object($message)) {
$message = print_r($message, true);
}

// Add timestamp
$message = '[' . date('Y-m-d H:i:s') . '] ' . $message . "\n";

// Write to log file
file_put_contents($log_file, $message, FILE_APPEND);
}

/**
* Get formatted price adjustment
*
* @param string $pricing_type Fixed or percentage
* @param float $adjustment Amount
* @return string
*/
public static function get_price_adjustment_text($pricing_type, $adjustment) {
if (empty($pricing_type) || empty($adjustment) || $adjustment == 0) {
return __('No adjustment', 'bdfg-preorders');
}

if ($pricing_type === 'fixed') {
if ($adjustment > 0) {
return sprintf(__('Add %s', 'bdfg-preorders'), wc_price($adjustment));
} else {
return sprintf(__('Subtract %s', 'bdfg-preorders'), wc_price(abs($adjustment)));
}
} else { // percentage
if ($adjustment > 0) {
return sprintf(__('Add %d%%', 'bdfg-preorders'), $adjustment);
} else {
return sprintf(__('Subtract %d%%', 'bdfg-preorders'), abs($adjustment));
}
}
}

/**
* Check if an order contains preorder products
*
* @param int|WC_Order $order Order ID or object
* @return bool
*/
public static function order_has_preorders($order) {
if (!$order instanceof WC_Order) {
$order = wc_get_order($order);
}

if (!$order) {
return false;
}

return $order->get_meta('_bdfg_has_preorder') === 'yes';
}

/**
* Get remaining time until release as a human-readable string
*
* @param string $date
* @return string
*/
public static function get_time_until_release($date) {
if (empty($date)) {
return '';
}

$timestamp = strtotime($date);
$current = current_time('timestamp');

if ($timestamp <= $current) {
return __('Released', 'bdfg-preorders');
}

$diff = $timestamp - $current;

if ($diff < DAY_IN_SECONDS) {
$hours = floor($diff / HOUR_IN_SECONDS);
return sprintf(_n('%d hour', '%d hours', $hours, 'bdfg-preorders'), $hours);
} elseif ($diff < WEEK_IN_SECONDS) {
$days = floor($diff / DAY_IN_SECONDS);
return sprintf(_n('%d day', '%d days', $days, 'bdfg-preorders'), $days);
} else {
$weeks = floor($diff / WEEK_IN_SECONDS);
return sprintf(_n('%d week', '%d weeks', $weeks, 'bdfg-preorders'), $weeks);
}
}

/**
* Get preorder payment method label
*
* @param string $method
* @return string
*/
public static function get_payment_method_label($method) {
$methods = array(
'full' => __('Full payment upfront', 'bdfg-preorders'),
'deposit' => __('Deposit only', 'bdfg-preorders'),
'authorize' => __('Authorize payment only', 'bdfg-preorders')
);

return isset($methods[$method]) ? $methods[$method] : $method;
}
}

includes/bdfg-preorders-template-functions.php

<?php
/**
* Template functions for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Get template part
*
* @param string $slug
* @param string $name
* @param array $args
*/
function bdfg_preorders_get_template_part($slug, $name = '', $args = array()) {
$template = '';

// Look in yourtheme/bdfg-preorders/slug-name.php and yourtheme/bdfg-preorders/slug.php
if ($name) {
$template = locate_template(array("bdfg-preorders/{$slug}-{$name}.php", "bdfg-preorders/{$slug}.php"));
}

// Get default template
if (!$template) {
if ($name) {
$template = BDFG_PREORDERS_PATH . "templates/{$slug}-{$name}.php";
} else {
$template = BDFG_PREORDERS_PATH . "templates/{$slug}.php";
}
}

// Allow third party plugins to filter template file
$template = apply_filters('bdfg_preorders_get_template_part', $template, $slug, $name);

if (file_exists($template)) {
load_template($template, false, $args);
}
}

/**
* Get template
*
* @param string $template_name
* @param array $args
* @param string $template_path
* @param string $default_path
*/
function bdfg_preorders_get_template($template_name, $args = array(), $template_path = '', $default_path = '') {
if ($args && is_array($args)) {
extract($args);
}

$located = bdfg_preorders_locate_template($template_name, $template_path, $default_path);

if (!file_exists($located)) {
return;
}

// Allow third party plugins to filter template file
$located = apply_filters('bdfg_preorders_get_template', $located, $template_name, $args, $template_path, $default_path);

include $located;
}

/**
* Locate template
*
* @param string $template_name
* @param string $template_path
* @param string $default_path
* @return string
*/
function bdfg_preorders_locate_template($template_name, $template_path = '', $default_path = '') {
if (!$template_path) {
$template_path = 'bdfg-preorders/';
}

if (!$default_path) {
$default_path = BDFG_PREORDERS_PATH . 'templates/';
}

// Look within passed path within the theme - this is priority
$template = locate_template(array(
trailingslashit($template_path) . $template_name,
$template_name
));

// Get default template
if (!$template) {
$template = trailingslashit($default_path) . $template_name;
}

// Return what we found
return apply_filters('bdfg_preorders_locate_template', $template, $template_name, $template_path);
}

/**
* Display preorder badge
*
* @param WC_Product $product
*/
function bdfg_preorders_display_badge($product) {
if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

echo '<span class="bdfg-preorder-badge">' . esc_html__('Pre-Order', 'bdfg-preorders') . '</span>';
}

/**
* Display preorder availability
*
* @param WC_Product $product
*/
function bdfg_preorders_display_availability($product) {
if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

$date = get_post_meta($product->get_id(), '_bdfg_preorder_available_date', true);
$time = get_post_meta($product->get_id(), '_bdfg_preorder_available_time', true);

if (empty($date)) {
return;
}

$formatted_date = BDFG_PreOrders_Helper::format_date($date, $time);

$availability_text = get_option('bdfg_preorders_availability_text', __('Available on: %date%', 'bdfg-preorders'));
$availability_text = str_replace('%date%', $formatted_date, $availability_text);

echo '<div class="bdfg-preorder-availability">' . esc_html($availability_text) . '</div>';
}

/**
* Display countdown timer
*
* @param WC_Product $product
*/
function bdfg_preorders_display_countdown($product) {
if (!$product || !BDFG_PreOrders_Product::is_preorder($product->get_id())) {
return;
}

// Check if countdown timer is enabled
if (get_option('bdfg_preorders_show_countdown', 'yes') !== 'yes') {
return;
}

$date = get_post_meta($product->get_id(), '_bdfg_preorder_available_date', true);
$time = get_post_meta($product->get_id(), '_bdfg_preorder_available_time', true);

if (empty($date)) {
return;
}

$datetime = $date;

if (!empty($time)) {
$datetime .= ' ' . $time;
} else {
$datetime .= ' 00:00:00';
}

$target_time = strtotime($datetime);
$current_time = current_time('timestamp');

// Only show countdown if not yet released
if ($target_time > $current_time) {
$countdown_data = array(
'year' => date('Y', $target_time),
'month' => date('m', $target_time),
'day' => date('d', $target_time),
'hour' => date('H', $target_time),
'minute' => date('i', $target_time),
'second' => date('s', $target_time)
);

echo '<div class="bdfg-preorder-countdown" data-countdown="' . esc_attr(json_encode($countdown_data)) . '">';
echo '<div class="countdown-label">' . esc_html__('Available in:', 'bdfg-preorders') . '</div>';
echo '<div class="countdown-timer">';
echo '<span class="days">00</span><span class="countdown-sep">:</span>';
echo '<span class="hours">00</span><span class="countdown-sep">:</span>';
echo '<span class="minutes">00</span><span class="countdown-sep">:</span>';
echo '<span class="seconds">00</span>';
echo '</div>';
echo '<div class="countdown-labels">';
echo '<span class="days-label">' . esc_html__('Days', 'bdfg-preorders') . '</span> ';
echo '<span class="hours-label">' . esc_html__('Hours', 'bdfg-preorders') . '</span> ';
echo '<span class="minutes-label">' . esc_html__('Min', 'bdfg-preorders') . '</span> ';
echo '<span class="seconds-label">' . esc_html__('Sec', 'bdfg-preorders') . '</span>';
echo '</div>';
echo '</div>';
}
}

includes/admin/class-bdfg-preorders-list-table.php

<?php
/**
* Pre-Orders List Table
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

// Load WP_List_Table if not loaded
if (!class_exists('WP_List_Table')) {
require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
}

/**
* Pre-Orders List Table class
*/
class BDFG_PreOrders_List_Table extends WP_List_Table {
/**
* Constructor
*/
public function __construct() {
parent::__construct(array(
'singular' => 'preorder',
'plural' => 'preorders',
'ajax' => false
));
}

/**
* Get columns
*
* @return array
*/
public function get_columns() {
return array(
'cb' => '<input type="checkbox" />',
'order_id' => __('Order', 'bdfg-preorders'),
'product' => __('Product', 'bdfg-preorders'),
'customer' => __('Customer', 'bdfg-preorders'),
'available_date' => __('Available Date', 'bdfg-preorders'),
'status' => __('Status', 'bdfg-preorders'),
'actions' => __('Actions', 'bdfg-preorders')
);
}

/**
* Get sortable columns
*
* @return array
*/
public function get_sortable_columns() {
return array(
'order_id' => array('order_id', true),
'available_date' => array('available_date', false),
'status' => array('status', false)
);
}

/**
* Get bulk actions
*
* @return array
*/
public function get_bulk_actions() {
return array(
'release' => __('Release Now', 'bdfg-preorders'),
'cancel' => __('Cancel', 'bdfg-preorders')
);
}

/**
* Process bulk actions
*/
public function process_bulk_action() {
// Security check
if (isset($_REQUEST['_wpnonce']) && !empty($_REQUEST['_wpnonce'])) {
$nonce = filter_input(INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING);
$action = 'bulk-' . $this->_args['plural'];

if (!wp_verify_nonce($nonce, $action)) {
wp_die('Security check failed');
}
}

$action = $this->current_action();

if ($action) {
global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

if (isset($_REQUEST['preorder'])) {
$preorder_ids = array_map('intval', (array) $_REQUEST['preorder']);

if (!empty($preorder_ids)) {
if ($action === 'release') {
// Release preorders
foreach ($preorder_ids as $id) {
$wpdb->update(
$table_name,
array(
'status' => 'available',
'updated_at' => current_time('mysql')
),
array('id' => $id),
array('%s', '%s'),
array('%d')
);

$preorder = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $id));

if ($preorder) {
$order = wc_get_order($preorder->order_id);

if ($order) {
$order->add_order_note(__('Pre-order manually released by admin.', 'bdfg-preorders'));
}
}
}

$message = __('Pre-orders released successfully.', 'bdfg-preorders');
echo '<div class="updated"><p>' . esc_html($message) . '</p></div>';
} elseif ($action === 'cancel') {
// Cancel preorders
foreach ($preorder_ids as $id) {
$wpdb->update(
$table_name,
array(
'status' => 'cancelled',
'updated_at' => current_time('mysql')
),
array('id' => $id),
array('%s', '%s'),
array('%d')
);

$preorder = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $id));

if ($preorder) {
$order = wc_get_order($preorder->order_id);

if ($order) {
$order->add_order_note(__('Pre-order cancelled by admin.', 'bdfg-preorders'));
}
}
}

$message = __('Pre-orders cancelled successfully.', 'bdfg-preorders');
echo '<div class="updated"><p>' . esc_html($message) . '</p></div>';
}
}
}
}
}

/**
* Prepare items
*/
public function prepare_items() {
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();

$this->_column_headers = array($columns, $hidden, $sortable);

$this->process_bulk_action();

$per_page = 20;
$current_page = $this->get_pagenum();
$offset = ($current_page - 1) * $per_page;

global $wpdb;
$table_name = $wpdb->prefix . 'bdfg_preorders';

// Build query
$where = array('1=1');

// Filter by status
if (isset($_REQUEST['status']) && !empty($_REQUEST['status'])) {
$status = sanitize_text_field($_REQUEST['status']);
$where[] = $wpdb->prepare('status = %s', $status);
}

// Filter by date range
if (isset($_REQUEST['date_from']) && !empty($_REQUEST['date_from'])) {
$date_from = sanitize_text_field($_REQUEST['date_from']);
$where[] = $wpdb->prepare('available_date >= %s', $date_from . ' 00:00:00');
}

if (isset($_REQUEST['date_to']) && !empty($_REQUEST['date_to'])) {
$date_to = sanitize_text_field($_REQUEST['date_to']);
$where[] = $wpdb->prepare('available_date <= %s', $date_to . ' 23:59:59');
}

// Search query
if (isset($_REQUEST['s']) && !empty($_REQUEST['s'])) {
$search = sanitize_text_field($_REQUEST['s']);

// Join product and order tables for search
$where[] = $wpdb->prepare(
"(p.ID = %d OR p.post_title LIKE %s OR o.ID = %d OR om.meta_value LIKE %s)",
is_numeric($search) ? (int)$search : 0,
'%' . $wpdb->esc_like($search) . '%',
is_numeric($search) ? (int)$search : 0,
'%' . $wpdb->esc_like($search) . '%'
);
}

// Build WHERE clause
$where_clause = implode(' AND ', $where);

// Order by
$orderby = !empty($_REQUEST['orderby']) ? sanitize_sql_orderby($_REQUEST['orderby']) : 'id';
$order = !empty($_REQUEST['order']) ? sanitize_text_field($_REQUEST['order']) : 'DESC';

if (!in_array($orderby, array('id', 'order_id', 'available_date', 'status'))) {
$orderby = 'id';
}

$order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC';

// Get total items count
$count_query = "SELECT COUNT(po.id) FROM $table_name po
LEFT JOIN {$wpdb->posts} p ON po.product_id = p.ID
LEFT JOIN {$wpdb->posts} o ON po.order_id = o.ID
LEFT JOIN {$wpdb->postmeta} om ON o.ID = om.post_id AND om.meta_key = '_billing_email'
WHERE $where_clause";

$total_items = $wpdb->get_var($count_query);

// Get items
$query = "SELECT po.*, p.post_title as product_name, o.post_status as order_status
FROM $table_name po
LEFT JOIN {$wpdb->posts} p ON po.product_id = p.ID
LEFT JOIN {$wpdb->posts} o ON po.order_id = o.ID
LEFT JOIN {$wpdb->postmeta} om ON o.ID = om.post_id AND om.meta_key = '_billing_email'
WHERE $where_clause
ORDER BY $orderby $order
LIMIT $per_page OFFSET $offset";

$this->items = $wpdb->get_results($query);

// Set pagination arguments
$this->set_pagination_args(array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => ceil($total_items / $per_page)
));
}

/**
* Column default
*
* @param object $item
* @param string $column_name
* @return string
*/
public function column_default($item, $column_name) {
switch ($column_name) {
case 'id':
return $item->id;
case 'status':
return $this->get_status_label($item->status);
default:
return print_r($item, true); // Show the whole array for troubleshooting purposes
}
}

/**
* Get status label
*
* @param string $status
* @return string
*/
private function get_status_label($status) {
$statuses = array(
'pending' => __('Pending Release', 'bdfg-preorders'),
'available' => __('Released', 'bdfg-preorders'),
'cancelled' => __('Cancelled', 'bdfg-preorders'),
'refunded' => __('Refunded', 'bdfg-preorders')
);

return isset($statuses[$status]) ? $statuses[$status] : $status;
}

/**
* Column cb
*
* @param object $item
* @return string
*/
public function column_cb($item) {
return sprintf('<input type="checkbox" name="preorder[]" value="%s" />', $item->id);
}

/**
* Column order_id
*
* @param object $item
* @return string
*/
public function column_order_id($item) {
$order = wc_get_order($item->order_id);

if ($order) {
$edit_link = get_edit_post_link($item->order_id);
return '<a href="' . esc_url($edit_link) . '">#' . esc_html($order->get_order_number()) . '</a>';
} else {
return '#' . esc_html($item->order_id) . ' (' . __('Deleted', 'bdfg-preorders') . ')';
}
}

/**
* Column product
*
* @param object $item
* @return string
*/
public function column_product($item) {
if (empty($item->product_name)) {
$product = wc_get_product($item->product_id);

if ($product) {
$product_name = $product->get_name();
$edit_link = get_edit_post_link($item->product_id);

// Show variation info if available
if ($item->variation_id && $variation = wc_get_product($item->variation_id)) {
$attributes = $variation->get_variation_attributes();
$attribute_info = array();

foreach ($attributes as $attr_name => $attr_value) {
$label = wc_attribute_label(str_replace('attribute_', '', $attr_name));
$value = $attr_value ? $attr_value : __('Any', 'bdfg-preorders');
$attribute_info[] = $label . ': ' . $value;
}

$variation_text = implode(', ', $attribute_info);
return '<a href="' . esc_url($edit_link) . '">' . esc_html($product_name) . '</a><br><small>' . esc_html($variation_text) . '</small>';
}

return '<a href="' . esc_url($edit_link) . '">' . esc_html($product_name) . '</a>';
} else {
return __('Product not found', 'bdfg-preorders') . ' (ID: ' . esc_html($item->product_id) . ')';
}
} else {
$edit_link = get_edit_post_link($item->product_id);
return '<a href="' . esc_url($edit_link) . '">' . esc_html($item->product_name) . '</a>';
}
}

/**
* Column customer
*
* @param object $item
* @return string
*/
public function column_customer($item) {
$order = wc_get_order($item->order_id);

if ($order) {
$email = $order->get_billing_email();
$name = $order->get_formatted_billing_full_name();

if ($email) {
return esc_html($name) . '<br><a href="mailto:' . esc_attr($email) . '">' . esc_html($email) . '</a>';
} else {
return esc_html($name);
}
}

return __('Unknown', 'bdfg-preorders');
}

/**
* Column available_date
*
* @param object $item
* @return string
*/
public function column_available_date($item) {
if (!empty($item->available_date)) {
$formatted_date = BDFG_PreOrders_Helper::format_date($item->available_date);

// Show countdown for future dates
$current_time = current_time('timestamp');
$release_time = strtotime($item->available_date);

if ($release_time > $current_time) {
$time_until = BDFG_PreOrders_Helper::get_time_until_release($item->available_date);
return sprintf(
'%s<br><small>%s</small>',
esc_html($formatted_date),
esc_html($time_until)
);
}

return esc_html($formatted_date);
}

return __('Not specified', 'bdfg-preorders');
}

/**
* Column status
*
* @param object $item
* @return string
*/
public function column_status($item) {
$status_label = $this->get_status_label($item->status);

$status_class = 'bdfg-preorder-status-' . sanitize_html_class($item->status);

return '<span class="' . esc_attr($status_class) . '">' . esc_html($status_label) . '</span>';
}

/**
* Column actions
*
* @param object $item
* @return string
*/
public function column_actions($item) {
$actions = array();

// View order link
$actions['view-order'] = sprintf(
'<a href="%s" title="%s">%s</a>',
esc_url(get_edit_post_link($item->order_id)),
esc_attr__('View Order', 'bdfg-preorders'),
__('View Order', 'bdfg-preorders')
);

// Release action (only for pending preorders)
if ($item->status === 'pending') {
$release_url = wp_nonce_url(
add_query_arg(
array(
'action' => 'release',
'preorder' => $item->id
)
),
'release-preorder-' . $item->id
);

$actions['release'] = sprintf(
'<a href="%s" title="%s" class="bdfg-action-release">%s</a>',
esc_url($release_url),
esc_attr__('Release Now', 'bdfg-preorders'),
__('Release Now', 'bdfg-preorders')
);
}

// Cancel action (only for pending preorders)
if ($item->status === 'pending') {
$cancel_url = wp_nonce_url(
add_query_arg(
array(
'action' => 'cancel',
'preorder' => $item->id
)
),
'cancel-preorder-' . $item->id
);

$actions['cancel'] = sprintf(
'<a href="%s" title="%s" class="bdfg-action-cancel">%s</a>',
esc_url($cancel_url),
esc_attr__('Cancel', 'bdfg-preorders'),
__('Cancel', 'bdfg-preorders')
);
}

return implode(' | ', $actions);
}

/**
* Extra table navigation
*
* @param string $which
*/
public function extra_tablenav($which) {
if ($which === 'top') {
// Status filter
$current_status = isset($_REQUEST['status']) ? sanitize_text_field($_REQUEST['status']) : '';
?>
<div class="alignleft actions">
<label for="filter-by-status" class="screen-reader-text"><?php esc_html_e('Filter by status', 'bdfg-preorders'); ?></label>
<select name="status" id="filter-by-status">
<option value=""><?php esc_html_e('All statuses', 'bdfg-preorders'); ?></option>
<option value="pending" <?php selected($current_status, 'pending'); ?>><?php esc_html_e('Pending Release', 'bdfg-preorders'); ?></option>
<option value="available" <?php selected($current_status, 'available'); ?>><?php esc_html_e('Released', 'bdfg-preorders'); ?></option>
<option value="cancelled" <?php selected($current_status, 'cancelled'); ?>><?php esc_html_e('Cancelled', 'bdfg-preorders'); ?></option>
<option value="refunded" <?php selected($current_status, 'refunded'); ?>><?php esc_html_e('Refunded', 'bdfg-preorders'); ?></option>
</select>

<?php
// Date filter
$date_from = isset($_REQUEST['date_from']) ? sanitize_text_field($_REQUEST['date_from']) : '';
$date_to = isset($_REQUEST['date_to']) ? sanitize_text_field($_REQUEST['date_to']) : '';
?>

<span class="date-filter">
<input type="text" name="date_from" id="date-from" class="date-picker" placeholder="<?php esc_attr_e('From date', 'bdfg-preorders'); ?>" value="<?php echo esc_attr($date_from); ?>" />
<span>&ndash;</span>
<input type="text" name="date_to" id="date-to" class="date-picker" placeholder="<?php esc_attr_e('To date', 'bdfg-preorders'); ?>" value="<?php echo esc_attr($date_to); ?>" />
</span>

<?php submit_button(__('Filter', 'bdfg-preorders'), '', 'filter_action', false); ?>
</div>
<?php
}
}
}

includes/class-bdfg-preorders-install.php

<?php
/**
* Installation and database schema for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Installation class
*/
class BDFG_PreOrders_Install {
/**
* Database version
*
* @var string
*/
private static $db_version = '2.0.0';

/**
* Install the plugin
*/
public static function install() {
self::create_tables();
self::create_pages();
self::update_version();

// Set transient for welcome page redirect
set_transient('bdfg_preorders_activation_redirect', 1, 30);
}

/**
* Create database tables
*/
private static function create_tables() {
global $wpdb;

require_once(ABSPATH . 'wp-admin/includes/upgrade.php');

$charset_collate = $wpdb->get_charset_collate();

// Create preorders table
$table_name = $wpdb->prefix . 'bdfg_preorders';

$sql = "CREATE TABLE $table_name (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
order_id bigint(20) unsigned NOT NULL,
product_id bigint(20) unsigned NOT NULL,
variation_id bigint(20) unsigned DEFAULT NULL,
available_date datetime DEFAULT NULL,
preorder_price decimal(19,4) DEFAULT NULL,
status varchar(50) NOT NULL DEFAULT 'pending',
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
PRIMARY KEY (id),
KEY order_id (order_id),
KEY product_id (product_id),
KEY status (status),
KEY available_date (available_date)
) $charset_collate;";

dbDelta($sql);

// Create preorder meta table
$meta_table_name = $wpdb->prefix . 'bdfg_preorder_meta';

$sql = "CREATE TABLE $meta_table_name (
meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
preorder_id bigint(20) unsigned NOT NULL,
meta_key varchar(255) DEFAULT NULL,
meta_value longtext,
PRIMARY KEY (meta_id),
KEY preorder_id (preorder_id),
KEY meta_key (meta_key(191))
) $charset_collate;";

dbDelta($sql);
}

/**
* Create custom pages
*/
private static function create_pages() {
// No custom pages needed for now, but can add in the future if needed
}

/**
* Update version
*/
private static function update_version() {
update_option('bdfg_preorders_db_version', self::$db_version);
update_option('bdfg_preorders_version', BDFG_PREORDERS_VERSION);
}

/**
* Uninstall the plugin
*/
public static function uninstall() {
// Only delete data if setting is enabled
if (get_option('bdfg_preorders_delete_data_uninstall', 'no') === 'yes') {
self::delete_tables();
self::delete_options();
}
}

/**
* Delete database tables
*/
private static function delete_tables() {
global $wpdb;

$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}bdfg_preorder_meta");
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}bdfg_preorders");
}

/**
* Delete options
*/
private static function delete_options() {
// Delete all plugin options
$options = array(
'bdfg_preorders_db_version',
'bdfg_preorders_version',
'bdfg_preorders_stock_handling',
'bdfg_preorders_payment_method',
'bdfg_preorders_deposit_amount',
'bdfg_preorders_enable_cancellations',
'bdfg_preorders_cancellation_limit',
'bdfg_preorders_button_text',
'bdfg_preorders_button_color',
'bdfg_preorders_availability_text',
'bdfg_preorders_show_countdown',
'bdfg_preorders_enable_emails',
'bdfg_preorders_email_subject',
'bdfg_preorders_email_heading',
'bdfg_preorders_email_content',
'bdfg_preorders_admin_notification',
'bdfg_preorders_admin_email_subject',
'bdfg_preorders_delete_data_uninstall',
'bdfg_preorders_debug_mode'
);

foreach ($options as $option) {
delete_option($option);
}

// Delete post meta
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_bdfg_preorder%'");
}
}

assets/js/frontend.js

/**
* BDFG Pre-Orders frontend JavaScript
*
* @package BDFG_PreOrders
*/
(function($) {
'use strict';

// Initialize countdown timers
function initCountdownTimers() {
$('.bdfg-preorder-countdown').each(function() {
var $this = $(this);
var countdownData = $this.data('countdown');

if (!countdownData) return;

var targetDate = new Date(
countdownData.year,
countdownData.month - 1,
countdownData.day,
countdownData.hour,
countdownData.minute,
countdownData.second
);

// Update countdown every second
var interval = setInterval(function() {
var now = new Date();
var diff = Math.max(0, targetDate - now) / 1000;

if (diff <= 0) {
clearInterval(interval);
$this.html('<div class="countdown-complete">' + bdfg_preorders_params.i18n.available_now + '</div>');
// Refresh the page after a slight delay
setTimeout(function() {
location.reload();
}, 3000);
return;
}

var days = Math.floor(diff / 86400);
diff -= days * 86400;

var hours = Math.floor(diff / 3600);
diff -= hours * 3600;

var minutes = Math.floor(diff / 60);
diff -= minutes * 60;

var seconds = Math.floor(diff);

// Update countdown elements
$this.find('.days').text(padZero(days));
$this.find('.hours').text(padZero(hours));
$this.find('.minutes').text(padZero(minutes));
$this.find('.seconds').text(padZero(seconds));
}, 1000);
});
}

// Helper function to pad with zero
function padZero(num) {
return (num < 10 ? '0' : '') + num;
}

// Handle variation changes for preorders
function handleVariationChanges() {
if ($('form.variations_form').length) {
$('form.variations_form').on('found_variation', function(event, variation) {
if (variation.is_preorder) {
// Update button text if provided
if (variation.button_text) {
$('.single_add_to_cart_button').text(variation.button_text);
$('.single_add_to_cart_button').addClass('bdfg-preorder-button');
}

// Update availability text if provided
if (variation.availability_html) {
$('.woocommerce-variation-availability').html(variation.availability_html);
}

// Add preorder class to form
$(this).addClass('is-preorder-variation');
} else {
// Restore default button text
$('.single_add_to_cart_button').text($('.single_add_to_cart_button').data('original-text') || bdfg_preorders_params.i18n.add_to_cart);
$('.single_add_to_cart_button').removeClass('bdfg-preorder-button');

// Remove preorder class from form
$(this).removeClass('is-preorder-variation');
}
});

// Reset when no variation is selected
$('form.variations_form').on('reset_data', function() {
$('.single_add_to_cart_button').text($('.single_add_to_cart_button').data('original-text') || bdfg_preorders_params.i18n.add_to_cart);
$('.single_add_to_cart_button').removeClass('bdfg-preorder-button');
});
}
}

// Handle cancel preorder actions
function handleCancelPreorder() {
$('.bdfg-cancel-preorder').on('click', function(e) {
if (!confirm(bdfg_preorders_params.i18n.confirm_cancel)) {
e.preventDefault();
return false;
}

var $button = $(this);
var orderId = $button.data('order-id');

if (orderId) {
e.preventDefault();

$button.prop('disabled', true);
$button.text(bdfg_preorders_params.i18n.processing);

$.ajax({
type: 'POST',
url: bdfg_preorders_params.ajax_url,
data: {
action: 'bdfg_preorders_cancel',
order_id: orderId,
nonce: bdfg_preorders_params.nonce
},
success: function(response) {
if (response.success) {
if (response.data && response.data.redirect) {
window.location.href = response.data.redirect;
} else {
window.location.reload();
}
} else {
alert(response.data.message || bdfg_preorders_params.i18n.error);
$button.prop('disabled', false);
$button.text(bdfg_preorders_params.i18n.cancel_preorder);
}
},
error: function() {
alert(bdfg_preorders_params.i18n.error);
$button.prop('disabled', false);
$button.text(bdfg_preorders_params.i18n.cancel_preorder);
}
});
}
});
}

// Store original button text on page load
function storeOriginalButtonText() {
$('.single_add_to_cart_button').each(function() {
$(this).data('original-text', $(this).text());

// Add preorder button class if needed
if ($(this).closest('form').hasClass('is-preorder')) {
$(this).addClass('bdfg-preorder-button');
}
});
}

// Initialize everything when document is ready
$(document).ready(function() {
initCountdownTimers();
handleVariationChanges();
handleCancelPreorder();
storeOriginalButtonText();
});

})(jQuery);

assets/js/admin.js

/**
* BDFG Pre-Orders admin JavaScript
*
* @package BDFG_PreOrders
*/
(function($) {
'use strict';

// Initialize datepicker
function initDatepicker() {
$('.bdfg-datepicker').datepicker({
dateFormat: bdfg_preorders.date_format,
changeMonth: true,
changeYear: true
});
}

// Initialize timepicker
function initTimepicker() {
if ($.fn.timepicker) {
$('.bdfg-timepicker').timepicker({
timeFormat: 'HH:mm',
interval: 15,
dynamic: false,
dropdown: true,
scrollbar: true
});
}
}

// Handle preorder tab visibility
function handlePreorderTab() {
var $enablePreorder = $('#_bdfg_preorder_enabled');
var $preorderTab = $('.bdfg_preorder_options');

function togglePreorderTab() {
if ($enablePreorder.is(':checked')) {
$preorderTab.show();
} else {
$preorderTab.hide();
}
}

togglePreorderTab();
$enablePreorder.on('change', togglePreorderTab);
}

// Handle pricing type changes
function handlePricingType() {
var $pricingType = $('#_bdfg_preorder_pricing_type');
var $priceAdjustment = $('#_bdfg_preorder_price_adjustment');
var $priceLabel = $('.bdfg-price-adjustment-label');

function updatePriceLabel() {
var type = $pricingType.val();
if (type === 'percentage') {
$priceLabel.text(bdfg_preorders.i18n.percentage);
$priceAdjustment.attr('step', '0.01');
} else {
$priceLabel.text(bdfg_preorders.i18n.fixed_amount);
$priceAdjustment.attr('step', '0.01');
}
}

updatePriceLabel();
$pricingType.on('change', updatePriceLabel);
}

// Confirm delete action
function confirmDelete() {
$('.bdfg-delete-preorder').on('click', function(e) {
if (!confirm(bdfg_preorders.i18n.confirm_delete)) {
e.preventDefault();
return false;
}
});
}

// Clear cache button handler
function handleClearCache() {
$('#bdfg-clear-cache').on('click', function(e) {
e.preventDefault();

var $button = $(this);
$button.prop('disabled', true);
$button.text(bdfg_preorders.i18n.clearing_cache);

$.ajax({
url: bdfg_preorders.ajax_url,
type: 'POST',
data: {
action: 'bdfg_clear_cache',
nonce: bdfg_preorders.nonce
},
success: function(response) {
if (response.success) {
$button.text(bdfg_preorders.i18n.cache_cleared);
setTimeout(function() {
$button.prop('disabled', false);
$button.text(bdfg_preorders.i18n.clear_cache);
}, 2000);
} else {
$button.text(bdfg_preorders.i18n.error);
setTimeout(function() {
$button.prop('disabled', false);
$button.text(bdfg_preorders.i18n.clear_cache);
}, 2000);
}
},
error: function() {
$button.text(bdfg_preorders.i18n.error);
setTimeout(function() {
$button.prop('disabled', false);
$button.text(bdfg_preorders.i18n.clear_cache);
}, 2000);
}
});
});
}

// Initialize everything when document is ready
$(document).ready(function() {
initDatepicker();
initTimepicker();
handlePreorderTab();
handlePricingType();
confirmDelete();
handleClearCache();
});

})(jQuery);

assets/css/frontend.css

/**
* BDFG Pre-Orders frontend styles
*
* @package BDFG_PreOrders
*/

/* Pre-order badge */
.bdfg-preorder-badge {
display: inline-block;
background-color: #3d9cd2;
color: #fff;
padding: 5px 10px;
font-size: 12px;
font-weight: bold;
border-radius: 3px;
margin-bottom: 15px;
text-transform: uppercase;
}

/* Pre-order availability */
.bdfg-preorder-availability {
margin-bottom: 15px;
font-weight: 500;
color: #555;
}

/* Pre-order custom message */
.bdfg-preorder-message {
background-color: #f7f6f7;
border-left: 3px solid #3d9cd2;
padding: 10px 15px;
margin-bottom: 20px;
}

/* Preorder button */
.single_add_to_cart_button.bdfg-preorder-button {
background-color: #3d9cd2 !important;
border-color: #3d9cd2 !important;
}

/* Countdown timer */
.bdfg-preorder-countdown {
margin: 15px 0;
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 4px;
background-color: #f9f9f9;
}

.bdfg-preorder-countdown .countdown-label {
font-weight: bold;
margin-bottom: 5px;
color: #333;
}

.bdfg-preorder-countdown .countdown-timer {
font-size: 24px;
font-weight: bold;
text-align: center;
color: #3d9cd2;
}

.bdfg-preorder-countdown .countdown-labels {
display: flex;
justify-content: space-around;
font-size: 12px;
color: #777;
margin-top: 5px;
}

/* Smaller countdown timer for order details */
.bdfg-preorder-countdown-small {
font-size: 14px;
margin-top: 5px;
color: #3d9cd2;
}

/* Price styling */
.bdfg-preorder-price {
font-weight: bold;
}

.bdfg-preorder-original-price {
opacity: 0.7;
}

.bdfg-preorder-discount {
color: #77a464;
}

.bdfg-preorder-premium {
color: #e2401c;
}

/* Preorder status indicators */
.bdfg-preorder-status-pending {
color: #ffba00;
font-weight: bold;
}

.bdfg-preorder-status-released {
color: #77a464;
font-weight: bold;
}

.bdfg-preorder-status-cancelled {
color: #e2401c;
font-weight: bold;
}

/* Preorder details on order page */
.bdfg-preorder-details {
margin-bottom: 30px;
}

.preorder_details {
width: 100%;
margin-bottom: 20px;
}

.preorder_details th {
text-align: left;
padding: 10px;
background-color: #f8f8f8;
}

.preorder_details td {
padding: 10px;
border-bottom: 1px solid #f8f8f8;
}

/* Responsive styles */
@media (max-width: 768px) {
.bdfg-preorder-countdown .countdown-timer {
font-size: 20px;
}

.bdfg-preorder-countdown .countdown-labels {
font-size: 10px;
}
}

assets/css/admin.css

/**
* BDFG Pre-Orders admin styles
*
* @package BDFG_PreOrders
*/

/* Tabs for product data panel */
#bdfg_preorder_data .panel-wrap {
overflow: hidden;
}

/* Options visibility */
.bdfg_preorder_options.hide {
display: none;
}

/* Preorder settings fields */
.bdfg-preorder-field {
margin-bottom: 12px;
}

.bdfg-preorder-field label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

.bdfg-preorder-field .description {
color: #777;
font-style: italic;
font-size: 12px;
margin-top: 5px;
}

.bdfg-preorder-field .bdfg-field-inline {
display: flex;
align-items: center;
}

.bdfg-preorder-field .bdfg-field-inline label {
margin-right: 10px;
margin-bottom: 0;
}

/* Date and time fields */
.bdfg-datepicker,
.bdfg-timepicker {
width: 150px;
}

/* Status indicators */
.bdfg-status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-left: 5px;
}

.bdfg-status-indicator.pending {
background-color: #ffba00;
}

.bdfg-status-indicator.released {
background-color: #77a464;
}

.bdfg-status-indicator.cancelled {
background-color: #e2401c;
}

/* Dashboard widget */
.bdfg-preorders-widget {
padding: 0 12px;
}

.bdfg-preorders-stats {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding-top: 12px;
}

.bdfg-stat-item {
text-align: center;
flex: 1;
padding: 10px;
background-color: #f9f9f9;
border: 1px solid #eee;
border-radius: 3px;
margin: 0 5px;
}

.bdfg-stat-item:first-child {
margin-left: 0;
}

.bdfg-stat-item:last-child {
margin-right: 0;
}

.bdfg-stat-value {
display: block;
font-size: 24px;
font-weight: bold;
color: #3d9cd2;
margin-bottom: 5px;
}

.bdfg-stat-label {
display: block;
color: #777;
font-size: 12px;
}

.bdfg-upcoming-releases {
margin-bottom: 15px;
padding-left: 0;
list-style: none;
}

.bdfg-upcoming-releases li {
border-bottom: 1px solid #f0f0f0;
padding: 8px 0;
}

.bdfg-upcoming-releases li:last-child {
border-bottom: none;
}

.bdfg-release-date {
font-size: 12px;
color: #777;
margin-right: 10px;
}

.bdfg-product-title {
font-weight: 500;
}

.bdfg-widget-footer {
margin-top: 15px;
text-align: right;
padding-bottom: 12px;
}

/* Order list styling */
.column-bdfg_preorder mark.preorder {
background: #e8f4fa;
color: #3d9cd2;
padding: 4px 8px;
border-radius: 3px;
}

/* Pre-orders management page */
.bdfg-preorders-management {
margin-top: 20px;
}

.bdfg-preorders-management .date-filter {
margin: 0 10px;
}

.bdfg-preorders-management .date-filter .date-picker {
width: 120px;
}

.bdfg-preorder-status-pending {
color: #ffba00;
font-weight: bold;
}

.bdfg-preorder-status-available {
color: #77a464;
font-weight: bold;
}

.bdfg-preorder-status-cancelled {
color: #e2401c;
font-weight: bold;
}

.bdfg-preorder-status-refunded {
color: #999;
font-weight: bold;
}

/* Settings page */
.bdfg-settings-section {
margin-bottom: 20px;
padding: 15px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 3px;
}

.bdfg-settings-section h3 {
margin-top: 0;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}

.form-table th {
padding-left: 0;
}

.bdfg-color-picker {
width: 80px;
}

.bdfg-welcome-panel {
padding: 20px;
margin-bottom: 20px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 3px;
}

.bdfg-welcome-panel h2 {
margin-top: 0;
color: #3d9cd2;
}

.bdfg-features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 20px;
}

.bdfg-feature-card {
border: 1px solid #eee;
border-radius: 3px;
padding: 15px;
background-color: #f9f9f9;
}

.bdfg-feature-card h3 {
margin-top: 0;
color: #3d9cd2;
}

@media screen and (max-width: 782px) {
.bdfg-features-grid {
grid-template-columns: repeat(1, 1fr);
}

.bdfg-preorders-stats {
flex-direction: column;
}

.bdfg-stat-item {
margin: 5px 0;
}
}

/* Action buttons in list table */
.bdfg-action-release {
color: #77a464;
}

.bdfg-action-cancel {
color: #e2401c;
}

templates/preorder-info.php

<?php
/**
* Product preorder information template
*
* This template can be overridden by copying it to yourtheme/bdfg-preorders/preorder-info.php.
*
* @package BDFG_PreOrders
* @version 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* @var WC_Product $product
*/
?>

<div class="bdfg-preorder-info">
<?php if (BDFG_PreOrders_Product::is_preorder($product->get_id())) : ?>
<?php bdfg_preorders_display_badge($product); ?>

<?php bdfg_preorders_display_availability($product); ?>

<?php if (get_option('bdfg_preorders_show_countdown', 'yes') === 'yes') : ?>
<?php bdfg_preorders_display_countdown($product); ?>
<?php endif; ?>

<?php
// Display custom message if available
$custom_message = get_post_meta($product->get_id(), '_bdfg_preorder_custom_message', true);
if (!empty($custom_message)) :
?>
<div class="bdfg-preorder-message"><?php echo wp_kses_post(wpautop($custom_message)); ?></div>
<?php endif; ?>
<?php endif; ?>
</div>

includes/admin/views/settings.php

<?php
/**
* Admin settings view for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}
?>

<div class="wrap woocommerce">
<h1><?php esc_html_e('BDFG Pre-Orders Settings', 'bdfg-preorders'); ?></h1>

<?php settings_errors(); ?>

<nav class="nav-tab-wrapper woo-nav-tab-wrapper">
<?php
$tabs = array(
'general' => __('General', 'bdfg-preorders'),
'display' => __('Display', 'bdfg-preorders'),
'emails' => __('Emails', 'bdfg-preorders'),
'advanced' => __('Advanced', 'bdfg-preorders'),
'license' => __('License', 'bdfg-preorders'),
'welcome' => __('About', 'bdfg-preorders'),
);

$current_tab = isset($_GET['tab']) ? sanitize_title($_GET['tab']) : 'general';

foreach ($tabs as $tab_id => $tab_name) {
$active = $current_tab === $tab_id ? 'nav-tab-active' : '';
echo '<a href="' . esc_url(admin_url('admin.php?page=bdfg-preorders-settings&tab=' . $tab_id)) . '" class="nav-tab ' . esc_attr($active) . '">' . esc_html($tab_name) . '</a>';
}
?>
</nav>

<div class="bdfg-settings-container">
<form method="post" action="options.php">
<?php
if ($current_tab === 'welcome') {
include BDFG_PREORDERS_PATH . 'includes/admin/views/welcome.php';
} else {
// Output nonce, action, and option_page fields
settings_fields('bdfg_preorders_' . $current_tab);

// Output settings sections and fields
do_settings_sections('bdfg_preorders_' . $current_tab);

// Output save button
submit_button();
}
?>
</form>
</div>
</div>

includes/admin/views/welcome.php

<?php
/**
* Welcome page for BDFG Pre-Orders admin
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}
?>

<div class="bdfg-welcome-panel">
<h2><?php esc_html_e('Welcome to BDFG Pre-Orders for WooCommerce', 'bdfg-preorders'); ?></h2>

<p class="about-description">
<?php esc_html_e('Version 2.0.0 brings powerful new features and improvements to help you manage pre-orders more effectively.', 'bdfg-preorders'); ?>
</p>

<div class="bdfg-features-grid">
<div class="bdfg-feature-card">
<h3><?php esc_html_e('Enhanced Management Dashboard', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('The new dedicated dashboard makes it easy to track, filter, and manage all your pre-orders in one place.', 'bdfg-preorders'); ?></p>
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-management')); ?>" class="button button-primary"><?php esc_html_e('Go to Dashboard', 'bdfg-preorders'); ?></a>
</div>

<div class="bdfg-feature-card">
<h3><?php esc_html_e('Flexible Pricing Options', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Offer special pricing for pre-orders with fixed or percentage-based discounts or premiums.', 'bdfg-preorders'); ?></p>
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-settings&tab=general')); ?>" class="button"><?php esc_html_e('Configure Settings', 'bdfg-preorders'); ?></a>
</div>

<div class="bdfg-feature-card">
<h3><?php esc_html_e('Automated Notifications', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Customers automatically receive emails when their pre-ordered products become available.', 'bdfg-preorders'); ?></p>
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-settings&tab=emails')); ?>" class="button"><?php esc_html_e('Email Settings', 'bdfg-preorders'); ?></a>
</div>

<div class="bdfg-feature-card">
<h3><?php esc_html_e('Countdown Timers', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Build excitement with countdown timers showing when products will be available.', 'bdfg-preorders'); ?></p>
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-settings&tab=display')); ?>" class="button"><?php esc_html_e('Display Settings', 'bdfg-preorders'); ?></a>
</div>

<div class="bdfg-feature-card">
<h3><?php esc_html_e('Documentation', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Comprehensive guides and tutorials to help you get the most from the plugin.', 'bdfg-preorders'); ?></p>
<a href="https://beiduofengou.net/docs/bdfg-preorders/" target="_blank" class="button"><?php esc_html_e('View Docs', 'bdfg-preorders'); ?></a>
</div>

<div class="bdfg-feature-card">
<h3><?php esc_html_e('Support', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('Need help? Our support team is ready to assist with any questions or issues.', 'bdfg-preorders'); ?></p>
<a href="https://beiduofengou.net/support/" target="_blank" class="button"><?php esc_html_e('Get Support', 'bdfg-preorders'); ?></a>
</div>
</div>

<div class="bdfg-system-info">
<h3><?php esc_html_e('System Information', 'bdfg-preorders'); ?></h3>

<table class="widefat" cellspacing="0">
<tbody>
<tr>
<td><?php esc_html_e('BDFG Pre-Orders Version:', 'bdfg-preorders'); ?></td>
<td><?php echo BDFG_PREORDERS_VERSION; ?></td>
</tr>
<tr>
<td><?php esc_html_e('WordPress Version:', 'bdfg-preorders'); ?></td>
<td><?php echo get_bloginfo('version'); ?></td>
</tr>
<tr>
<td><?php esc_html_e('WooCommerce Version:', 'bdfg-preorders'); ?></td>
<td><?php echo defined('WC_VERSION') ? WC_VERSION : __('Not Active', 'bdfg-preorders'); ?></td>
</tr>
<tr>
<td><?php esc_html_e('PHP Version:', 'bdfg-preorders'); ?></td>
<td><?php echo phpversion(); ?></td>
</tr>
<tr>
<td><?php esc_html_e('Database Version:', 'bdfg-preorders'); ?></td>
<td><?php echo get_option('bdfg_preorders_db_version', '1.0.0'); ?></td>
</tr>
<tr>
<td><?php esc_html_e('Last Updated:', 'bdfg-preorders'); ?></td>
<td><?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime('2025-03-05 14:23:48')); ?></td>
</tr>
</tbody>
</table>
</div>
</div>

includes/admin/views/management.php

<?php
/**
* Pre-Orders management page
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

// Include the list table class
require_once BDFG_PREORDERS_PATH . 'includes/admin/class-bdfg-preorders-list-table.php';

// Create an instance of the list table
$list_table = new BDFG_PreOrders_List_Table();
$list_table->prepare_items();

// Get preorder statistics
$stats = BDFG_PreOrders_Product::get_preorder_stats();
?>

<div class="wrap bdfg-preorders-management">
<h1 class="wp-heading-inline"><?php esc_html_e('Pre-Orders Management', 'bdfg-preorders'); ?></h1>

<hr class="wp-header-end">

<div class="bdfg-preorders-stats">
<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($stats['active_preorders']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Active Pre-Orders', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($stats['pending_products']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Pending Release Products', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($stats['total_products']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Total Pre-Order Products', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo wc_price($stats['revenue']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Pre-Order Revenue', 'bdfg-preorders'); ?></span>
</div>
</div>

<div class="bdfg-preorders-list">
<form method="post">
<?php $list_table->search_box(__('Search Pre-Orders', 'bdfg-preorders'), 'search_id'); ?>
<?php $list_table->display(); ?>
</form>
</div>

<div class="bdfg-management-help">
<h3><?php esc_html_e('Pre-Order Management Help', 'bdfg-preorders'); ?></h3>
<p><?php esc_html_e('This page allows you to manage all pre-orders in your store. You can:', 'bdfg-preorders'); ?></p>
<ul>
<li><?php esc_html_e('Filter pre-orders by status or date', 'bdfg-preorders'); ?></li>
<li><?php esc_html_e('Search for specific pre-orders', 'bdfg-preorders'); ?></li>
<li><?php esc_html_e('Manually release pre-orders before their scheduled date', 'bdfg-preorders'); ?></li>
<li><?php esc_html_e('Cancel pending pre-orders if needed', 'bdfg-preorders'); ?></li>
</ul>
<p>
<?php
echo wp_kses(
sprintf(
__('Need more help? Check our <a href="%s" target="_blank">documentation</a>.', 'bdfg-preorders'),
esc_url('https://beiduofengou.net/docs/bdfg-preorders/')
),
array('a' => array('href' => array(), 'target' => array()))
);
?>
</p>
</div>
</div>

includes/admin/class-bdfg-preorders-dashboard.php

<?php
/**
* Dashboard widgets for BDFG Pre-Orders
*
* @package BDFG_PreOrders
* @since 2.0.0
*/

// Prevent direct file access
if (!defined('ABSPATH')) {
exit;
}

/**
* Dashboard widget class
*/
class BDFG_PreOrders_Dashboard {
/**
* Constructor
*/
public function __construct() {
// Add dashboard widget
add_action('wp_dashboard_setup', array($this, 'add_dashboard_widget'));
}

/**
* Add dashboard widget
*/
public function add_dashboard_widget() {
wp_add_dashboard_widget(
'bdfg_preorders_dashboard_widget',
__('BDFG Pre-Orders Overview', 'bdfg-preorders'),
array($this, 'dashboard_widget_content')
);
}

/**
* Dashboard widget content
*/
public function dashboard_widget_content() {
// Get statistics
$stats = BDFG_PreOrders_Product::get_preorder_stats();

// Get upcoming releases
$upcoming = BDFG_PreOrders_Product::get_upcoming_preorders(5);
?>

<div class="bdfg-preorders-widget">
<div class="bdfg-preorders-stats">
<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo esc_html($stats['active_preorders']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Active Pre-Orders', 'bdfg-preorders'); ?></span>
</div>

<div class="bdfg-stat-item">
<span class="bdfg-stat-value"><?php echo wc_price($stats['revenue']); ?></span>
<span class="bdfg-stat-label"><?php esc_html_e('Pre-Order Revenue', 'bdfg-preorders'); ?></span>
</div>
</div>

<?php if (!empty($upcoming)) : ?>
<h4><?php esc_html_e('Upcoming Releases', 'bdfg-preorders'); ?></h4>
<ul class="bdfg-upcoming-releases">
<?php foreach ($upcoming as $product) : ?>
<?php
$date = !empty($product->date) ? BDFG_PreOrders_Helper::format_date($product->date, $product->time) : '';
$edit_link = get_edit_post_link($product->ID);
?>
<li>
<span class="bdfg-release-date"><?php echo esc_html($date); ?></span>
<a href="<?php echo esc_url($edit_link); ?>" class="bdfg-product-title"><?php echo esc_html($product->post_title); ?></a>
</li>
<?php endforeach; ?>
</ul>
<?php else : ?>
<p><?php esc_html_e('No upcoming pre-order releases.', 'bdfg-preorders'); ?></p>
<?php endif; ?>

<div class="bdfg-widget-footer">
<a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-preorders-management')); ?>" class="button button-primary"><?php esc_html_e('Manage Pre-Orders', 'bdfg-preorders'); ?></a>
</div>
</div>
<?php
}
}

// Initialize the dashboard widget
new BDFG_PreOrders_Dashboard();

 

Leave a Comment