如何将文件上传添加到WooCommerce结账页面?

huangapple go评论108阅读模式
英文:

How to add file upload to WooCommerce checkout?

问题

WooCommerce结账页面基于安全原因并不原生支持添加文件上传字段作为可能的字段。我想要在结账页面上添加一个文件上传字段,以便客户可以提供一个文档,然后将其附加到他们的订单,并且如果需要的话,管理员可以在将来通过订单仪表板引用它。

我尝试了两种不同的方法,但都没有成功。我尝试过的两种不同解决方案分别是:

  1. Gravity Forms上传表单 - 尝试通过挂钩在结账页面上显示一个Gravity Forms表单,但文件字段数据永远不会显示在$_FILES或$_POST中。

  2. 使用ajax的上传字段 - 尝试创建一个上传字段,然后使用ajax将上传字段的数据发送到WordPress的ajax函数,但这种方法的问题是您无法验证文件大小或文件是否已经上传。因此,用户有可能上传非常大的文件,或者他们可以修改文件的存储位置,因为文件上传路径是在文件上传元素的HTML中添加的,如下所示:

add_action( 'wp_ajax_mishaupload', 'misha_file_upload' );
add_action( 'wp_ajax_nopriv_mishaupload', 'misha_file_upload' );

function misha_file_upload(){

	$upload_dir = wp_upload_dir();

	if ( isset( $_FILES[ 'misha_file' ] ) ) {
		$path = $upload_dir[ 'path' ] . '/' . basename( $_FILES[ 'misha_file' ][ 'name' ] );

		if( move_uploaded_file( $_FILES[ 'misha_file' ][ 'tmp_name' ], $path ) ) {
			echo $upload_dir[ 'url' ] . '/' . basename( $_FILES[ 'misha_file' ][ 'name' ] );
		}
	}
	die;
}

其中的这行 echo $upload_dir[ 'url' ] . '/' . basename( $_FILES[ 'misha_file' ][ 'name' ] ); 将文件目录添加到输入元素的value=部分,这在安全方面不理想。

然后我到了这一步,现在我可以使用以下代码添加'file'类型字段:

/**
 * `woocommerce_form_field` 过滤挂钩的函数。
 *
 * @param  $field
 * @param  $key
 * @param  $args
 * @param  $value
 *
 * @return
 */
function wp_kama_woocommerce_form_field_filter( $field, $key, $args, $value ){

    // 检查字段是否是文件字段
    if( $args['type'] == 'file' ){
            // 添加自定义HTML到字段
            $field = '<div class="woocommerce-additional-fields__field-wrapper">';
            $field .= '<p class="form-row notes woocommerce-validated" id="certificate_file_upload_field" data-priority="">';
            $field .= '<label for="certificate_file_upload" class=""></label>';
            $field .= '<span class="woocommerce-input-wrapper">';

            $field .= sprintf(
                '<input type="file" class="%s" name="%s" id="%s"/>',
                esc_attr( implode( ' ', $args['class'] ) ),
                esc_attr( $key ),
                esc_attr( $args['id'] ),
            );

            $field .= '</span>';
            $field .= '</p>';
            $field .= '</div>';
    }

	return $field;
}
add_filter( 'woocommerce_form_field', 'wp_kama_woocommerce_form_field_filter', 10, 4 );

上面的代码允许我在另一个挂钩中执行以下操作:

function add_custom_checkout_field($checkout) {
	woocommerce_form_field('certificate_file_upload', array(
		'type' => 'file',
		'class' => array('form-row-wide'),
		'label' => __('Upload Certificate'),
		'required' => false,
	), $checkout->get_value('certificate_file_upload'));
}
add_action( 'woocommerce_after_order_notes', 'add_custom_checkout_field' );

这段代码会在结账页面上添加一个文件字段。但此时的问题是,无论是$_FILES还是$_POST都没有与键"certificate_file_upload"相关的文件数据,这在尝试处理实际文件数据时是一个问题。

我尝试搜索WooCommerce如何处理默认结账字段的方法,以查看如何可能将我的文件数据添加到$_FILES/$_POST,但我所找到的所有信息都表明它们通过WooCommerce插件来管理数据,可能是通过 "woocommerce->assets->js->frontend->checkout.js" 进行管理。但我不知道如何在不修改他们的文件的情况下继续添加文件支持,因为如果这甚至是正确的文件,一旦更新插件,我的修改就会被覆盖。

checkout.js是我应该首先查看的文件吗?如果checkout.js是我应该查看的正确文件,是否有一种方法可以在不修改他们的文件的情况下允许我的文件数据添加到$_FILES?如果没有其他方法,我希望避免下载插件以使结账页面上的文件上传成为可能,因为我试图避免过多的内容,但如果没有其他方法,我猜我会选择这样做。

英文:

The WooCommerce checkout page doesn't natively support adding a file upload field as a possible field due to security reasons. I'm looking to add a file upload field on the checkout page so that the customer can give us a document that can then be attached to their order and be referenced again by admins through the orders dashboard in the future if we need to.

I've tried 2 different methods but both have led to dead ends. The 2 different solutions I've tried were:

  1. Gravity Forms Upload Form - tried displaying a gravity forms form on the checkout page via a hook and the file field data would never show up in $_FILES or $_POST.
  2. Upload field with ajax - tried making an upload field and then using ajax to send the upload field's data to a wordpress ajax function but the issue with this approach is that you cannot validate the file size / if the file's been uploaded already. So a user could potentially upload very large files or they could mess with where the file is stored due to the file upload path being added in the HTML of the file upload element like so:
add_action( &#39;wp_ajax_mishaupload&#39;, &#39;misha_file_upload&#39; );
add_action( &#39;wp_ajax_nopriv_mishaupload&#39;, &#39;misha_file_upload&#39; );

function misha_file_upload(){

	$upload_dir = wp_upload_dir();

	if ( isset( $_FILES[ &#39;misha_file&#39; ] ) ) {
		$path = $upload_dir[ &#39;path&#39; ] . &#39;/&#39; . basename( $_FILES[ &#39;misha_file&#39; ][ &#39;name&#39; ] );

		if( move_uploaded_file( $_FILES[ &#39;misha_file&#39; ][ &#39;tmp_name&#39; ], $path ) ) {
			echo $upload_dir[ &#39;url&#39; ] . &#39;/&#39; . basename( $_FILES[ &#39;misha_file&#39; ][ &#39;name&#39; ] );
		}
	}
	die;
}

where the line echo $upload_dir[ 'url' ] . '/' . basename( $_FILES[ 'misha_file' ][ 'name' ] ); adds the file directory to the value= part of the input element which isn't ideal in terms of security.

I then got to this point where I can now add 'file' type fields with the following code:

/**
 * Function for `woocommerce_form_field` filter-hook.
 *
 * @param  $field
 * @param  $key
 * @param  $args
 * @param  $value
 *
 * @return
 */
function wp_kama_woocommerce_form_field_filter( $field, $key, $args, $value ){

    // check if field is a file field
    if( $args[&#39;type&#39;] == &#39;file&#39; ){
            // add custom HTML to the field
            $field = &#39;&lt;div class=&quot;woocommerce-additional-fields__field-wrapper&quot;&gt;&#39;;
            $field .= &#39;&lt;p class=&quot;form-row notes woocommerce-validated&quot; id=&quot;certificate_file_upload_field&quot; data-priority=&quot;&quot;&gt;&#39;;
            $field .= &#39;&lt;label for=&quot;certificate_file_upload&quot; class=&quot;&quot;&gt;Upload Certificate&lt;/label&gt;&#39;;
            $field .= &#39;&lt;span class=&quot;woocommerce-input-wrapper&quot;&gt;&#39;;

            $field .= sprintf(
                &#39;&lt;input type=&quot;file&quot; class=&quot;%s&quot; name=&quot;%s&quot; id=&quot;%s&quot;/&gt;&#39;,
                esc_attr( implode( &#39; &#39;, $args[&#39;class&#39;] ) ),
                esc_attr( $key ),
                esc_attr( $args[&#39;id&#39;] ),
            );

            $field .= &#39;&lt;/span&gt;&#39;;
            $field .= &#39;&lt;/p&gt;&#39;;
            $field .= &#39;&lt;/div&gt;&#39;;
    }

	return $field;
}
add_filter( &#39;woocommerce_form_field&#39;, &#39;wp_kama_woocommerce_form_field_filter&#39;, 10, 4 );

The code above lets me then do the following in another hook:

function add_custom_checkout_field($checkout) {
	woocommerce_form_field(&#39;certificate_file_upload&#39;, array(
		&#39;type&#39; =&gt; &#39;file&#39;,
		&#39;class&#39; =&gt; array(&#39;form-row-wide&#39;),
		&#39;label&#39; =&gt; __(&#39;Upload Certificate&#39;),
		&#39;required&#39; =&gt; false,
	), $checkout-&gt;get_value(&#39;certificate_file_upload&#39;));
}
add_action( &#39;woocommerce_after_order_notes&#39;, &#39;add_custom_checkout_field&#39; );

which this code then adds a file field to the checkout page. The issue at this point is that neither $_FILES nor $_POST has any file data related to the key "certificate_file_upload" which is an issue when trying to do anything with the actual file data itself.

I've tried searching around for how WooCommerce deals with the default checkout fields to see how I could possibly add my file data to $_FILES/$_POST but all I've come up with is that they manage the data possibly through the WooCommerce plugin:
woocommerce->assets->js->frontend->checkout.js
but I don't know how I could go about adding file support to the checkout page past this point without modifying their files (which will be overriden whenever they update the plugin) if this is even the correct file to be doing this in.

Is checkout.js the file that I should be looking at in the first place to add my file data to $_FILES or should I be looking somewhere else? If checkout.js is the correct file I should be looking at, is there a way around modifying their file to allow my file data to be added to $_FILES?

I would like to avoid having to download a plugin just to make file uploading on the checkout page possible as I'm trying to avoid bloat but if that's the only solution I guess I'll go with that if nothing is posssible to fix this.

答案1

得分: 0

以下是该插件的翻译部分:

以下的超轻量级插件以非常安全的方式使用Ajax,以允许在WooCommerce结帐页面上传文件。

使用Ajax时,您可以:

  • 限制/检查文件大小,
  • 仅限于接受的文件类型,
  • 检查文件是否已经上传过(但这对于某人可以上传一个文件并重新上传一个具有相同名称的更新文件而不检查出来来说并不是真正有用的)。
  • 完全隐藏所有敏感数据,如上传路径,使用WC Session来安全存储它。

所有上传的文件都将存储在名为“wc_checkout_uploads”的文件夹中,该文件夹位于WordPress主“uploads”目录中。它们将包含在以用户ID为名称的子文件夹中(长度为6位数)。对于访客用户,如果启用了结帐,上传将具有相同的上传目录000000以及基于账单电子邮件的子目录。

请参阅“完整使用示例”部分(插件代码后面):

  • 显示上传字段,
  • 在需要时验证字段,
  • 将文件URL和名称保存为自定义订单元数据并在管理界面中显示,
  • 在需要的任何地方使用该自定义订单元数据。

以下是这个轻量级插件的代码:

主PHP文件(将其添加到您喜欢的文件夹中):checkout_uploads.php

<?php
/*
Plugin Name: WooCommerce Checkout upload
Plugin URI: https://stackoverflow.com/a/76691778/3730754
Description: Add a input field type "file" for checkout (Ajax securely powered), and save the downloaded file URL and name as custom order metadata.
Version: 1.0
Author: LoicTheAztec
Author URI: https://stackoverflow.com/users/3730754/loictheaztec
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit; // 如果直接访问,则退出。
}

register_activation_hook(__FILE__, 'wcu_plugin_activation');
function wcu_plugin_activation() {
    // 确保WooCommerce插件处于活动状态
    if ( ! in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
        $message = '需要激活WooCommerce插件。';
        echo $message;
        trigger_error($message, E_USER_NOTICE);
    }
}

// Enqueue JavaScript file and localize it
add_action( 'wp_enqueue_scripts', 'checkout_uploads_enqueue_scripts' );
function checkout_uploads_enqueue_scripts() {
   if ( is_checkout() && ! is_wc_endpoint_url() ) {
        wp_enqueue_script( 
            'checkout-uploads',  
            plugins_url( 'js/checkout_upload.js', __FILE__ ), 
            array('jquery'), false, true 
        );

        wp_localize_script(
            'checkout-uploads',
            'checkout_uploads_params',
            array(
                'ajax_url' => admin_url( 'admin-ajax.php?action=checkout_upload&security=' . wp_create_nonce('checkout_upload') ),
            )
        );
    }
}

// 将Input File类型添加到WooCommerce表单字段
add_filter( 'woocommerce_form_field', 'woocommerce_form_input_field_type_file', 10, 4 );
function woocommerce_form_input_field_type_file( $field, $key, $args, $value ){
    if( $args['type'] == 'file' ){
        if ( $args['required'] ) {
            $args['class'][] = 'validate-required';
            $required        = '&nbsp;<abbr class="required" title="' . esc_attr__( 'required', 'woocommerce' ) . '">*</abbr>';
        } else {
            $required = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        }
        $field           = '';
        $label_id        = $args['id'];
        $sort            = $args['priority'] ? $args['priority'] : '';
        $field_container = '<p class="form-row %1$s" id="%2$s" data-priority="' . esc_attr( $sort ) . '">%3$s</p>';
        $max_size        = isset($args['max_size']) ? 'data-max_size="' . intval( $args['max_size'] ) . '" ' : '';
        $accept          = isset($args['accept']) ? 'accept="' . esc_attr( $args['accept'] ) . '" ' : '';
        
        $field .= sprintf( '<input type="%s" class="input-file %s" name="%s" id="%s" %s/>', esc_attr( $args['type'] ), 
            esc_attr( implode( ' ', $args['input_class'] ) ), esc_attr( $key ), esc_attr( $args['id'] ), $max_size . $accept );

        if ( ! empty( $field ) ) {
            $field_html = '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . wp_kses_post( $args['label'] ) . $required . '</label>';
            $field_html .= '<span class="woocommerce-input-wrapper">' . $field;

            if ( $args['description'] ) {
                $field_html .= '<span class="description" id="' . esc_attr( $args['id'] ) . '-description" aria-hidden="true">' . wp_kses_post( $args['description'] ) . '</span>';
            }

            $field_html .= '<span class="upload-response" style="display:none"></span></span>';

            $container_class = esc_attr( implode( ' ', $args['class'] ) );
            $container_id    = esc_attr( $args['id'] ) . '_field';
            $field           = sprintf( $field_container, $container_class, $container_id, $field_html );
        }
    }
    if ( $args['return'] ) {
        return $field;
    } else {
        echo $field;
    }
}

// PHP Ajax响应器
add_action( 'wp_ajax_checkout_upload', 'checkout_ajax_file_upload' );
add_action( 'wp_ajax_nopriv_checkout_upload', 'checkout_ajax_file_upload' );
function checkout_ajax_file_upload(){
    check_ajax_referer('checkout_upload', 'security'); 

    global $current_user;

    if ( isset($_FILES['uploads']) ) {
        if ( ! $current_user->ID && isset($_POST['email']) && ! empty($_POST['email']) ) {
            // 从账单电子邮件中生成子/子文件夹(路径)的Guest目录中的‘000000’
            $user_path = '000000/'.substr(s

<details>
<summary>英文:</summary>

The following ultra lightweight plugin uses Ajax in a very secure way, to allow uploads in WooCommerce checkout page.

When using Ajax, you can:

 - Restrict/check the file size,
 - Restrict to accepted file types only,
 - check if the file has already been uploaded (but this is not really helpful as someone can upload a file, and re-upload an update file with the same name, without checking out yet).
 - Hide completely all the sensitive data like the upload path, using WC Session to store it securely.

All uploaded files will go to a folder named &quot;wc_checkout_uploads&quot; located in WordPress main &quot;uploads&quot; directory. They will be included in a subfolder with the user ID as name (in 6 digits length). &lt;br&gt;
For guests users, if checkout is enabled, the uploads have the same upload directory `000000` and a subdirectory based on the billing email.

See the **&quot;Complete Usage Examples&quot;** section *(below after the plugin code)* to:

 - Display an upload field,
 - Validate the field when it is required,
 - Save the file URL and name as custom order meta data and displayed in the admin,
 - Use that custom order meta data everywhere you like.

### Here is the code of this lightweight plugin:

**Main PHP file** *(add it to a folder named as you like)*: ***checkout_uploads.php***
```php
&lt;?php
/*
Plugin Name: WooCommerce Checkout upload
Plugin URI: https://stackoverflow.com/a/76691778/3730754
Description: Add a input field type &quot;file&quot; for checkout (Ajax securely powered), and save the downloaded file URL and name as custom order metadata.
Version: 1.0
Author: LoicTheAztec
Author URI: https://stackoverflow.com/users/3730754/loictheaztec
*/

if ( ! defined( &#39;ABSPATH&#39; ) ) {
	exit; // Exit if accessed directly.
}

register_activation_hook(__FILE__, &#39;wcu_plugin_activation&#39;);
function wcu_plugin_activation() {
    // Make sure that WooCommerce plugin is active
    if ( ! in_array( &#39;woocommerce/woocommerce.php&#39;, apply_filters( &#39;active_plugins&#39;, get_option( &#39;active_plugins&#39; ) ) ) ) {
        $message = &#39;Requires WooCommerce plugin activated.&#39;;
        echo $message;
        trigger_error($message, E_USER_NOTICE);
    }
}

// Enqueue JavaScript file and localize it
add_action( &#39;wp_enqueue_scripts&#39;, &#39;checkout_uploads_enqueue_scripts&#39; );
function checkout_uploads_enqueue_scripts() {
   if ( is_checkout() &amp;&amp; ! is_wc_endpoint_url() ) {
        wp_enqueue_script( 
            &#39;checkout-uploads&#39;,  
            plugins_url( &#39;js/checkout_upload.js&#39;, __FILE__ ), 
            array(&#39;jquery&#39;), false, true 
        );

        wp_localize_script(
            &#39;checkout-uploads&#39;,
            &#39;checkout_uploads_params&#39;,
            array(
                &#39;ajax_url&#39; =&gt; admin_url( &#39;admin-ajax.php?action=checkout_upload&amp;security=&#39;.wp_create_nonce(&#39;checkout_upload&#39;) ),
            )
        );
    }
}

// ADd Input File type to WooCommerce form fields
add_filter( &#39;woocommerce_form_field&#39;, &#39;woocommerce_form_input_field_type_file&#39;, 10, 4 );
function woocommerce_form_input_field_type_file( $field, $key, $args, $value ){
    if( $args[&#39;type&#39;] == &#39;file&#39; ){
		if ( $args[&#39;required&#39;] ) {
			$args[&#39;class&#39;][] = &#39;validate-required&#39;;
			$required        = &#39;&amp;nbsp;&lt;abbr class=&quot;required&quot; title=&quot;&#39; . esc_attr__( &#39;required&#39;, &#39;woocommerce&#39; ) . &#39;&quot;&gt;*&lt;/abbr&gt;&#39;;
		} else {
			$required = &#39;&amp;nbsp;&lt;span class=&quot;optional&quot;&gt;(&#39; . esc_html__( &#39;optional&#39;, &#39;woocommerce&#39; ) . &#39;)&lt;/span&gt;&#39;;
		}
        $field           = &#39;&#39;;
        $label_id        = $args[&#39;id&#39;];
		$sort            = $args[&#39;priority&#39;] ? $args[&#39;priority&#39;] : &#39;&#39;;
		$field_container = &#39;&lt;p class=&quot;form-row %1$s&quot; id=&quot;%2$s&quot; data-priority=&quot;&#39; . esc_attr( $sort ) . &#39;&quot;&gt;%3$s&lt;/p&gt;&#39;;
        $max_size        = isset($args[&#39;max_size&#39;]) ? &#39;data-max_size=&quot;&#39; . intval( $args[&#39;max_size&#39;] ) . &#39;&quot; &#39; : &#39;&#39;;
        $accept          = isset($args[&#39;accept&#39;]) ? &#39;accept=&quot;&#39; . esc_attr( $args[&#39;accept&#39;] ) . &#39;&quot; &#39; : &#39;&#39;;
        
        $field .= sprintf( &#39;&lt;input type=&quot;%s&quot; class=&quot;input-file %s&quot; name=&quot;%s&quot; id=&quot;%s&quot; %s/&gt;&#39;, esc_attr( $args[&#39;type&#39;] ), 
            esc_attr( implode( &#39; &#39;, $args[&#39;input_class&#39;] ) ), esc_attr( $key ), esc_attr( $args[&#39;id&#39;] ), $max_size . $accept );

        if ( ! empty( $field ) ) {
			$field_html = &#39;&lt;label for=&quot;&#39; . esc_attr( $label_id ) . &#39;&quot; class=&quot;&#39; . esc_attr( implode( &#39; &#39;, $args[&#39;label_class&#39;] ) ) . &#39;&quot;&gt;&#39; . wp_kses_post( $args[&#39;label&#39;] ) . $required . &#39;&lt;/label&gt;&#39;;
			$field_html .= &#39;&lt;span class=&quot;woocommerce-input-wrapper&quot;&gt;&#39; . $field;

			if ( $args[&#39;description&#39;] ) {
				$field_html .= &#39;&lt;span class=&quot;description&quot; id=&quot;&#39; . esc_attr( $args[&#39;id&#39;] ) . &#39;-description&quot; aria-hidden=&quot;true&quot;&gt;&#39; . wp_kses_post( $args[&#39;description&#39;] ) . &#39;&lt;/span&gt;&#39;;
			}

			$field_html .= &#39;&lt;span class=&quot;upload-response&quot; style=&quot;display:none&quot;&gt;&lt;/span&gt;&lt;/span&gt;&#39;;

			$container_class = esc_attr( implode( &#39; &#39;, $args[&#39;class&#39;] ) );
			$container_id    = esc_attr( $args[&#39;id&#39;] ) . &#39;_field&#39;;
			$field           = sprintf( $field_container, $container_class, $container_id, $field_html );
		}
    }
    if ( $args[&#39;return&#39;] ) {
        return $field;
    } else {
        echo $field;
    }
}

// PHP Ajax responder
add_action( &#39;wp_ajax_checkout_upload&#39;, &#39;checkout_ajax_file_upload&#39; );
add_action( &#39;wp_ajax_nopriv_checkout_upload&#39;, &#39;checkout_ajax_file_upload&#39; );
function checkout_ajax_file_upload(){
    check_ajax_referer(&#39;checkout_upload&#39;, &#39;security&#39;); 

    global $current_user;

    if ( isset($_FILES[&#39;uploads&#39;]) ) {
        if ( ! $current_user-&gt;ID &amp;&amp; isset($_POST[&#39;email&#39;]) &amp;&amp; ! empty($_POST[&#39;email&#39;]) ) {
            // Generating a sub / subfolder (path) from billing email in &#39;000000&#39; guest directory
            $user_path = &#39;000000/&#39;.substr(sanitize_title($_POST[&#39;email&#39;]), 0, 10); // For Guests
        } else {
            $user_path = str_pad($current_user-&gt;ID, 6, &#39;0&#39;, STR_PAD_LEFT); // For logged in users
        }
        $upload_dir  = wp_upload_dir();
        $user_path   = &#39;/wc_checkout_uploads/&#39; . $user_path;
        $user_folder = $upload_dir[&#39;basedir&#39;]  . $user_path;
        $user_url    = $upload_dir[&#39;baseurl&#39;]  . $user_path;

        if ( ! is_dir( $user_folder ) ) {
            wp_mkdir_p( $user_folder );
            chmod( $user_folder, 0777 );
        }
        $file_path = $user_folder . &#39;/&#39; . basename($_FILES[&#39;uploads&#39;][&#39;name&#39;]);
        $file_url  = $user_url . &#39;/&#39; . basename( $_FILES[&#39;uploads&#39;][&#39;name&#39;]);

        if( move_uploaded_file($_FILES[&#39;uploads&#39;][&#39;tmp_name&#39;], $file_path)) {
            // Save the file URL and the file name to WC Session
            WC()-&gt;session-&gt;set(&#39;checkout_upload&#39;, array(
                &#39;file_url&#39;  =&gt; $file_url, 
                &#39;file_name&#39; =&gt; $_FILES[&#39;uploads&#39;][&#39;name&#39;]
            ));
            
            echo &#39;&lt;span style=&quot;color:green&quot;&gt;&#39; . __(&#39;Upload completed&#39;, &#39;woocommerce&#39;) . &#39;&lt;/span&gt;&lt;br&gt;&#39;;
        } else {
            echo &#39;&lt;span style=&quot;color:red&quot;&gt;&#39; . __(&#39;Upload failed.&#39;) . &#39;&lt;/span&gt;&#39;;
        }
    }
    wp_die();
}

The Javascript file located in a "js" subfolder: checkout_upload.js

jQuery( function($) {
    if (typeof checkout_uploads_params === &#39;undefined&#39;) {
        return false;
    }

    $(&#39;form.checkout&#39;).on( &#39;change&#39;, &#39;input[type=file]&#39;, function() {
        const files = $(this).prop(&#39;files&#39;);
        const email = $(&#39;input#billing_email&#39;).val();

        if ( files.length ) {
            const file = files[0];
            const maxSize = $(this).data(&#39;max_size&#39;);
            const formData = new FormData();
            formData.append( &#39;uploads&#39;, file );
            formData.append( &#39;email&#39;, email );

            if ( maxSize &gt; 0 &amp;&amp; file.size &gt; ( maxSize * 1024 ) ) {
                const maxSizeText = &#39;This file is to heavy (&#39; + parseInt(file.size / 1024) + &#39; ko)&#39;;
                $( &#39;.upload-response&#39; ).html( maxSizeText ).css(&#39;color&#39;,&#39;red&#39;).fadeIn().delay(2000).fadeOut();
                return;
            }
            $(&#39;form.checkout&#39;).block({message: null, overlayCSS:{background:&quot;#fff&quot;,opacity: .6}});

            $.ajax({
                url: checkout_uploads_params.ajax_url,
                type: &#39;POST&#39;,
                data: formData,
                contentType: false,
                enctype: &#39;multipart/form-data&#39;,
                processData: false,
                success: function ( response ) {
                    $(&#39;form.checkout&#39;).unblock();
                    $( &#39;.upload-response&#39; ).html( response ).fadeIn().delay(2000).fadeOut();
                },
                error: function ( error ) {
                    $(&#39;form.checkout&#39;).unblock();
                    $( &#39;.upload-response&#39; ).html( error ).css(&#39;color&#39;,&#39;red&#39;).fadeIn().delay(2000).fadeOut();
                }
            });
        }
    });
});

End of the plugin code.

> A new input field type "file" is now available for woocommerce form fields and has 2 additional optional arguments:
>
> - The File max allowed size: &#39;max_size&#39; (in ko),
> - The accepted files extensions for upload: &#39;accept&#39;.


Complete Usage Examples:

1) Adding an upload field:

A) After the order notes (accepting only text / pdf files and limitting the download size).

add_action( &#39;woocommerce_after_order_notes&#39;, &#39;add_custom_checkout_field&#39; );
function add_custom_checkout_field($checkout) {

    echo &#39;&lt;div class=&quot;woocommerce-additional-fields__field-wrapper&quot;&gt;&#39;;

    woocommerce_form_field(&#39;certificate&#39;, array(
        &#39;type&#39;      =&gt; &#39;file&#39;,
        &#39;class&#39;     =&gt; array(&#39;form-row-wide&#39;),
        &#39;label&#39;     =&gt; __(&#39;Upload Certificate&#39;, &#39;woocommerce&#39;),
        &#39;required&#39;  =&gt; false,
        &#39;max_size&#39;  =&gt; &#39;3072&#39;, // in ko (here 3 Mo size limit)
        &#39;accept&#39;    =&gt; &#39;.img,.doc,.docx,.rtf,.txt&#39;, // text documents and pdf
    ), &#39;&#39;);

    echo &#39;&lt;/div&gt;&#39;;
}

This code goes in functions.php file of your active child theme (or active theme) or using Code Snippets plugin (recommended by WooCommerce).

B) In the billing (or shipping) address section for a specific user role (accepting only text / pdf files and limitting the download size).

Note: Here the field has "required" option enabled.

add_filter( &#39;woocommerce_checkout_fields&#39;, &#39;add_custom_billing_field&#39; );
function add_custom_billing_field( $fields ) {
    // Only for &#39;wholesale_customer&#39; user role
    if( ! current_user_can( &#39;wholesale_customer&#39; ) ) return $fields;

    $fields[&#39;billing&#39;][&#39;billing_image&#39;] = array(
        &#39;type&#39; =&gt; &#39;file&#39;,
        &#39;label&#39; =&gt; __(&#39;Upload your image&#39;, &#39;woocommerce&#39;),
        &#39;class&#39; =&gt; array(&#39;form-row-wide&#39;),
        &#39;required&#39; =&gt; true,
        &#39;max_size&#39;  =&gt; &#39;5120&#39;, // in ko (here 5 Mo size limit)
        &#39;accept&#39;    =&gt; &#39;image/*&#39;, // Image files only
        &#39;priority&#39; =&gt; 200,
    );    
    
    return $fields;
}

For logged users only, you can use:

    // Only for logged in users
    if( ! is_user_logged_in() ) return $fields;

Important: When the field is required, and located in billing or shipping fields sections, add the following code, to avoid woocommerce stopping checkout (when the file has been uploaded):

// On billing or shipping section, when &quot;upload&quot; field is required
add_action( &#39;woocommerce_after_checkout_validation&#39;, &#39;custom_after_checkout_validation&#39;, 20, 2 );
function custom_after_checkout_validation($data, $errors) {
    $field_key = &#39;billing_image&#39;; // Here define the field key (or field ID)

    $errors-&gt;remove($field_key.&#39;_required&#39;); // Remove unwanted error for input file
}

This code goes in functions.php file of your active child theme (or active theme) or using Code Snippets plugin (recommended by WooCommerce).

2) Validation to be used when the file is required:

// Required upload field validation
add_action( &#39;woocommerce_checkout_process&#39;, &#39;checkout_required_upload_validation&#39; );
function checkout_required_upload_validation() {
    $checkout_upload = WC()-&gt;session-&gt;get(&#39;checkout_upload&#39;);
	if( empty( $checkout_upload ) ) {
        wc_add_notice( __(&#39;Uploading your file is required in order to checkout.&#39;, &#39;woocommerce&#39;), &#39;error&#39; ); // Displays an error notice
    }
}

This code goes in functions.php file of your active child theme (or active theme) or using Code Snippets plugin (recommended by WooCommerce).

3) Save the uploaded file URL and name:

// Save the uploaded file URL and name (array
add_action( &#39;woocommerce_checkout_create_order&#39;, &#39;save_checkout_uploaded_file&#39;, 10, 2 );
function save_checkout_uploaded_file( $order, $data ){
	if( $checkout_upload = WC()-&gt;session-&gt;get(&#39;checkout_upload&#39;) ) {
		$order-&gt;update_meta_data( &#39;_checkout_upload&#39;, $checkout_upload ); // Save 
	}
    WC()-&gt;session-&gt;__unset(&#39;checkout_upload&#39;); // Remove session variable
}

4) Display the uploaded file URL and name:

A) In the admin, on order edit pages, after billing address:

// Display the uploaded file in admin orders
add_action(&#39;woocommerce_admin_order_data_after_billing_address&#39;, &#39;display_uploaded_file_in_admin_orders&#39;);
function display_uploaded_file_in_admin_orders( $order ){
	if( $checkout_upload = $order-&gt;get_meta( &#39;_checkout_upload&#39; ) ) {
		printf( &#39;&lt;p&gt;%s &lt;br&gt;&lt;a href=&quot;%s&quot;&gt;%s&lt;/a&gt;&lt;/p&gt;&#39;, 
            __(&quot;File Upload:&quot;, &#39;woocommerce&#39;), 
            $checkout_upload[&#39;file_url&#39;], 
            $checkout_upload[&#39;file_name&#39;] 
        );
	}
}

This code goes in functions.php file of your active child theme (or active theme) or using Code Snippets plugin (recommended by WooCommerce).

B) Everywhere needed with $order variable (the WC_Order object):

First, if needed, you can get the WC_Order object from the order ID like:

$order = wc_get_order( $order_id );

Then you will get the data using:

$upload = $order-&gt;get_meta(&#39;_checkout_upload&#39;);

Then, for a file, you can display it as a link like:

$upload = $order-&gt;get_meta(&#39;_checkout_upload&#39;);

printf( &#39;&lt;p&gt;%s &lt;br&gt;&lt;a href=&quot;%s&quot;&gt;%s&lt;/a&gt;&lt;/p&gt;&#39;, 
    __(&quot;File Upload:&quot;, &#39;woocommerce&#39;), 
    $upload[&#39;file_url&#39;], 
    $upload[&#39;file_name&#39;] 
);

Or for an image, you can display the image like:

$upload = $order-&gt;get_meta(&#39;_checkout_upload&#39;);
printf( &#39;&lt;p&gt;%s &lt;br&gt;&lt;img src=&quot;%s&quot; alt=&quot;%s&quot; /&gt;&lt;br&gt;&lt;a href=&quot;%s&quot;&gt;%s&lt;/a&gt;&lt;/p&gt;&#39;, 
__(&quot;Image Uploaded:&quot;, &#39;woocommerce&#39;), 
$upload[&#39;file_url&#39;], 
$upload[&#39;file_name&#39;], 
$upload[&#39;file_url&#39;], 
$upload[&#39;file_name&#39;] 
);

huangapple
  • 本文由 发表于 2023年7月14日 04:17:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76682973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定