修改WooCommerce订单发票上传界面:替换媒体上传器并指定存储目录
解决方案:自定义WooCommerce订单发票上传至私有目录
以下是修改后的完整代码,替换原有的媒体库上传逻辑,实现直接将PDF发票上传至受.htaccess保护的私有目录,且不进入媒体库:
<?php defined( 'ABSPATH' ) || exit; class AttachFilesToWooCommerceOrderEmail { private static $instance; private $privateUploads; private $uploadDir; // 自定义上传目录服务器绝对路径 public static function get_instance() { if ( null == self::$instance ) { self::$instance = new self; } return self::$instance; } private function __construct() { $this->privateUploads = true; // 启用私有上传模式 $this->uploadDir = WP_CONTENT_DIR . '/woocommerce_upload/invoices'; // 自动创建目录及保护文件 if ( !file_exists( $this->uploadDir ) ) { wp_mkdir_p( $this->uploadDir ); // 写入.htaccess禁止直接访问 file_put_contents( $this->uploadDir . '/.htaccess', "deny from all\n" ); } $this->init(); } private function init() { add_action( 'admin_notices', array( $this, 'verify_cmb2_active' ) ); add_action( 'cmb2_admin_init', array( $this, 'order_files_metabox' ) ); add_filter( 'woocommerce_email_attachments', array( $this, 'conditionally_attach_order_files_to_order_email' ), 10, 4 ); // 注册AJAX上传处理 add_action( 'wp_ajax_order_invoice_upload', array( $this, 'handle_invoice_upload' ) ); // 加载后台上传脚本 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); } function verify_cmb2_active() { if ( ! defined( 'CMB2_LOADED' ) ) { $plugin_data = get_plugin_data( __FILE__ ); $plugin_name = $plugin_data['Name']; ?> <div class="notice notice-warning is-dismissible"><p>插件 <strong><?php echo $plugin_name; ?></strong> 需要 CMB2 插件。</p></div> <?php } } function order_files_metabox() { $cmb = new_cmb2_box( array( 'id' => 'order_attachments', 'title' => '订单发票附件', 'object_types' => array( 'shop_order', ), 'context' => 'side', 'priority' => 'high', 'show_names' => true, ) ); // 自定义上传字段:只读文本框+上传按钮 $cmb->add_field( array( 'desc' => '上传PDF发票,文件将附加到「订单完成」邮件中。<br/><br/>* 文件仅存储在私有目录,不会进入媒体库', 'id' => 'order_invoice_file', 'type' => 'text', 'attributes' => array( 'readonly' => 'readonly', 'class' => 'order-invoice-path', ), 'after_field' => '<button type="button" class="button order-invoice-upload-btn">上传发票</button>', ) ); } function enqueue_admin_scripts( $hook ) { // 仅在订单编辑页面加载上传脚本 if ( 'post.php' !== $hook || get_post_type() !== 'shop_order' ) { return; } wp_enqueue_script( 'plupload' ); wp_add_inline_script( 'plupload', " jQuery(document).ready(function($) { $('.order-invoice-upload-btn').on('click', function(e) { e.preventDefault(); var uploader = new plupload.Uploader({ runtimes: 'html5,flash,silverlight,html4', browse_button: this, url: ajaxurl + '?action=order_invoice_upload', filters: { mime_types: [{title: 'PDF文件', extensions: 'pdf'}], max_file_size: '10mb' }, multipart_params: { order_id: $('#post_ID').val(), security: '".wp_create_nonce('order_invoice_upload_nonce')."' } }); uploader.init(); uploader.bind('FilesAdded', function(up, files) { up.start(); }); uploader.bind('FileUploaded', function(up, file, response) { var res = JSON.parse(response.response); if (res.success) { $('.order-invoice-path').val(res.data.file_path); alert('上传成功'); } else { alert('上传失败:' + res.data.message); } up.destroy(); }); }); }); " ); } function handle_invoice_upload() { // 权限验证 if ( !current_user_can( 'edit_shop_orders' ) || !wp_verify_nonce( $_POST['security'], 'order_invoice_upload_nonce' ) ) { wp_send_json_error( array( 'message' => '权限不足' ) ); } $order_id = intval( $_POST['order_id'] ); if ( !$order_id || get_post_type( $order_id ) !== 'shop_order' ) { wp_send_json_error( array( 'message' => '无效订单ID' ) ); } // 文件上传验证 if ( !isset( $_FILES['file'] ) || $_FILES['file']['error'] !== UPLOAD_ERR_OK ) { wp_send_json_error( array( 'message' => '文件上传错误' ) ); } $file = $_FILES['file']; // 仅允许PDF文件 $mime_type = wp_check_filetype( $file['name'] )['type']; if ( $mime_type !== 'application/pdf' ) { wp_send_json_error( array( 'message' => '仅支持PDF文件' ) ); } // 生成唯一文件名,避免重复 $file_ext = pathinfo( $file['name'], PATHINFO_EXTENSION ); $file_name = 'invoice-order-' . $order_id . '-' . time() . '.' . $file_ext; $target_path = $this->uploadDir . '/' . $file_name; // 保存文件到私有目录 if ( move_uploaded_file( $file['tmp_name'], $target_path ) ) { update_post_meta( $order_id, 'order_invoice_file', $target_path ); wp_send_json_success( array( 'file_path' => $target_path ) ); } else { wp_send_json_error( array( 'message' => '无法保存文件' ) ); } } function conditionally_attach_order_files_to_order_email( $attachments, $email_id, $object, $email_obj ) { if ( 'customer_completed_order' != $email_id ) { return $attachments; } $invoice_path = get_post_meta( $object->get_id(), 'order_invoice_file', true ); if ( $invoice_path && file_exists( $invoice_path ) ) { $attachments[] = $invoice_path; } return $attachments; } } $AttachFilesToWooCommerceOrderEmail = AttachFilesToWooCommerceOrderEmail::get_instance();
核心修改说明
私有目录自动创建与保护
- 启用私有上传模式,自动创建
/wp-content/woocommerce_upload/invoices目录 - 自动生成
.htaccess文件禁止外部直接访问该目录
- 启用私有上传模式,自动创建
替换媒体库上传逻辑
- 移除原CMB2的
file_list字段,改用自定义上传按钮+只读文本框 - 利用WordPress内置的Plupload库实现文件上传,限制仅支持PDF格式
- 移除原CMB2的
AJAX上传处理
- 注册AJAX接口处理文件上传,验证权限、文件类型后保存到私有目录
- 生成包含订单ID和时间戳的唯一文件名,避免文件覆盖
邮件附件逻辑调整
- 直接从订单元数据读取私有目录的文件路径,添加到邮件附件列表
内容的提问来源于stack exchange,提问作者Karnak
相关产品推荐
相关产品推荐

