Customize content in WooCommerce admin quick order

2020-03-30 01:39发布

问题:

I am new to WordPress and I am using WooCommerce plugin in my project where I need to modify some of template files and some of core files of WooCommerce.

I have created child theme and created woocommerce folder inside my child theme and hence I can create/modify template files there which customizations won't be lost during WooCommerce plugin updates. But as I have read on some articles, modifying core files of WooCommerce is not recommended.

I have customized in WooCommerce plugin this core file includes\admin\meta-boxes\views\html-order-item and I am a little worried about future plugin updates (Are my custom changes will stay preserved or not). I have tried to copy this file in my child theme inside the woocommerce folder, but it's not seems to override the original one, so it has no effect.

In this "html-order-item.php" file, I have just added a small piece of code which has a download button to downloads product's original image.

How can I achieve this using a "clean recommended way ( using hooks/filters or templates)?
Please suggest any way to achieve it.

Here is my code snippet (my customization starts with /* Custom code added by Amol */ ):

<?php
/**
 * Shows an order item
 *
 * @var object $item The item being displayed
 * @var int $item_id The id of the item being displayed
 */
  if (!defined('ABSPATH')) {
  exit;
 }
$product_link = $_product ? admin_url('post.php?post=' . absint($_product->id) . '&action=edit') : '';
$thumbnail = $_product ? apply_filters('woocommerce_admin_order_item_thumbnail', $_product->get_image('thumbnail', array('title' => ''), false), $item_id, $item) : '';
$tax_data = empty($legacy_order) && wc_tax_enabled() ? maybe_unserialize(isset($item['line_tax_data']) ? $item['line_tax_data'] : '' ) : false;
$item_total = ( isset($item['line_total']) ) ? esc_attr(wc_format_localized_price($item['line_total'])) : '';
$item_subtotal = ( isset($item['line_subtotal']) ) ? esc_attr(wc_format_localized_price($item['line_subtotal'])) : '';

<tr class="item <?php echo apply_filters('woocommerce_admin_html_order_item_class', (!empty($class) ? $class : ''), $item, $order); ?>" data-order_item_id="<?php echo $item_id; ?>">
 <td class="thumb">
    <?php
    echo '<div class="wc-order-item-thumbnail">' . wp_kses_post($thumbnail) . '</div>';
    ?>
</td>
 <td class="name" data-sort-value="<?php echo esc_attr($item['name']); ?>">
    <?php
    echo $product_link ? '<a href="' . esc_url($product_link) . '" class="wc-order-item-name">' . esc_html($item['name']) . '</a>' : '<div class="class="wc-order-item-name"">' . esc_html($item['name']) . '</div>';   

    if ($_product && $_product->get_sku()) {
        echo '<div class="wc-order-item-sku"><strong>' . __('SKU:', 'woocommerce') . '</strong> ' . esc_html($_product->get_sku()) . '</div>';
    }

    if (!empty($item['variation_id'])) {
        echo '<div class="wc-order-item-variation"><strong>' . __('Variation ID:', 'woocommerce') . '</strong> ';
        if (!empty($item['variation_id']) && 'product_variation' === get_post_type($item['variation_id'])) {
            echo esc_html($item['variation_id']);
        } elseif (!empty($item['variation_id'])) {
            echo esc_html($item['variation_id']) . ' (' . __('No longer exists', 'woocommerce') . ')';
        }
        echo '</div>';
    }
    ?>
    <input type="hidden" class="order_item_id" name="order_item_id[]" value="<?php echo esc_attr($item_id); ?>" />
    <input type="hidden" name="order_item_tax_class[<?php echo absint($item_id); ?>]" value="<?php echo isset($item['tax_class']) ? esc_attr($item['tax_class']) : ''; ?>" />

    <?php do_action('woocommerce_before_order_itemmeta', $item_id, $item, $_product) ?>
    <?php include( 'html-order-item-meta.php' ); ?>
    <?php do_action('woocommerce_after_order_itemmeta', $item_id, $item, $_product) ?>

    <?php echo $_product->get_categories(', ', '<div class="wc-order-item-variation"><strong>' . _n('Category:', 'woocommerce:', sizeof(get_the_terms($item['product_id'], 'product_cat')), 'woocommerce') . ' ', '</strong></div>'); ?> 
    <div  style="float: left;" >
        <?php
        $image_link = wp_get_attachment_image_src(get_post_thumbnail_id($_product->id), 'large');

        echo isset($image_link[0]) ? '<img src = "' . $image_link[0] . '" width = "200">' : '';
        ?>
    </div>
    <?php
    /* Custom code added by Amol */
    $post = $_product->post;
    $attachment_count = count($_product->get_gallery_attachment_ids());
    $gallery = $attachment_count > 0 ? '[product-gallery]' : '';
    $props = wc_get_product_attachment_props(get_post_thumbnail_id(), $post);
    $image = get_the_post_thumbnail($post->ID, apply_filters('single_product_large_thumbnail_size', 'shop_single'), array(
        'title' => $props['title'],
        'alt' => $props['alt'],
    ));
    echo apply_filters(
            'woocommerce_single_product_image_html', sprintf(
                    '<a style="text-decoration: none;clear:both;float: left;margin-top: 5px;" href="%s" download = "Order#' . $order->id . '-' . $item['variation_id'] . '"><input type = "button" value="Download image"/></a>', esc_url($props['url'])
            ), $post->ID
    );
    //End of custom code by Amol
    ?>
</td>

<?php do_action('woocommerce_admin_order_item_values', $_product, $item, absint($item_id)); ?>

<td class="item_cost" width="1%" data-sort-value="<?php echo esc_attr($order->get_item_subtotal($item, false, true)); ?>">
    <div class="view">
        <?php
        if (isset($item['line_total'])) {
            echo wc_price($order->get_item_total($item, false, true), array('currency' => $order->get_order_currency()));

            if (isset($item['line_subtotal']) && $item['line_subtotal'] != $item['line_total']) {
                echo '<span class="wc-order-item-discount">-' . wc_price(wc_format_decimal($order->get_item_subtotal($item, false, false) - $order->get_item_total($item, false, false), ''), array('currency' => $order->get_order_currency())) . '</span>';
            }
        }
        ?>
    </div>
</td>
<td class="quantity" width="1%">
    <div class="view">
        <?php
        echo '<small class="times">&times;</small> ' . ( isset($item['qty']) ? esc_html($item['qty']) : '1' );

        if ($refunded_qty = $order->get_qty_refunded_for_item($item_id)) {
            echo '<small class="refunded">' . ( $refunded_qty * -1 ) . '</small>';
        }
        ?>
    </div>
    <div class="edit" style="display: none;">
        <?php $item_qty = esc_attr($item['qty']); ?>
        <input type="number" step="<?php echo apply_filters('woocommerce_quantity_input_step', '1', $_product); ?>" min="0" autocomplete="off" name="order_item_qty[<?php echo absint($item_id); ?>]" placeholder="0" value="<?php echo $item_qty; ?>" data-qty="<?php echo $item_qty; ?>" size="4" class="quantity" />
    </div>
    <div class="refund" style="display: none;">
        <input type="number" step="<?php echo apply_filters('woocommerce_quantity_input_step', '1', $_product); ?>" min="0" max="<?php echo $item['qty']; ?>" autocomplete="off" name="refund_order_item_qty[<?php echo absint($item_id); ?>]" placeholder="0" size="4" class="refund_order_item_qty" />
    </div>
</td>
<td class="line_cost" width="1%" data-sort-value="<?php echo esc_attr(isset($item['line_total']) ? $item['line_total'] : '' ); ?>">
    <div class="view">
        <?php
        if (isset($item['line_total'])) {
            echo wc_price($item['line_total'], array('currency' => $order->get_order_currency()));
        }

        if (isset($item['line_subtotal']) && $item['line_subtotal'] !== $item['line_total']) {
            echo '<span class="wc-order-item-discount">-' . wc_price(wc_format_decimal($item['line_subtotal'] - $item['line_total'], ''), array('currency' => $order->get_order_currency())) . '</span>';
        }

        if ($refunded = $order->get_total_refunded_for_item($item_id)) {
            echo '<small class="refunded">' . wc_price($refunded, array('currency' => $order->get_order_currency())) . '</small>';
        }
        ?>
    </div>
    <div class="edit" style="display: none;">
        <div class="split-input">
            <div class="input">
                <label><?php esc_attr_e('Pre-discount:', 'woocommerce'); ?></label>
                <input type="text" name="line_subtotal[<?php echo absint($item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" value="<?php echo $item_subtotal; ?>" class="line_subtotal wc_input_price" data-subtotal="<?php echo $item_subtotal; ?>" />
            </div>
            <div class="input">
                <label><?php esc_attr_e('Total:', 'woocommerce'); ?></label>
                <input type="text" name="line_total[<?php echo absint($item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" value="<?php echo $item_total; ?>" class="line_total wc_input_price" data-tip="<?php esc_attr_e('After pre-tax discounts.', 'woocommerce'); ?>" data-total="<?php echo $item_total; ?>" />
            </div>
        </div>
    </div>
    <div class="refund" style="display: none;">
        <input type="text" name="refund_line_total[<?php echo absint($item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" class="refund_line_total wc_input_price" />
    </div>
</td>

<?php
    if (!empty($tax_data)) {
        foreach ($order_taxes as $tax_item) {
            $tax_item_id = $tax_item['rate_id'];
            $tax_item_total = isset($tax_data['total'][$tax_item_id]) ? $tax_data['total'][$tax_item_id] : '';
            $tax_item_subtotal = isset($tax_data['subtotal'][$tax_item_id]) ? $tax_data['subtotal'][$tax_item_id] : '';
            ?>
            <td class="line_tax" width="1%">
                <div class="view">
                    <?php
                    if ('' != $tax_item_total) {
                        echo wc_price(wc_round_tax_total($tax_item_total), array('currency' => $order->get_order_currency()));
                    } else {
                        echo '&ndash;';
                    }

                    if (isset($item['line_subtotal']) && $item['line_subtotal'] !== $item['line_total']) {
                        echo '<span class="wc-order-item-discount">-' . wc_price(wc_round_tax_total($tax_item_subtotal - $tax_item_total), array('currency' => $order->get_order_currency())) . '</span>';
                    }

                    if ($refunded = $order->get_tax_refunded_for_item($item_id, $tax_item_id)) {
                        echo '<small class="refunded">' . wc_price($refunded, array('currency' => $order->get_order_currency())) . '</small>';
                    }
                    ?>
                </div>
                <div class="edit" style="display: none;">
                    <div class="split-input">
                        <div class="input">
                            <label><?php esc_attr_e('Pre-discount:', 'woocommerce'); ?></label>
                            <input type="text" name="line_subtotal_tax[<?php echo absint($item_id); ?>][<?php echo esc_attr($tax_item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" value="<?php echo esc_attr(wc_format_localized_price($tax_item_subtotal)); ?>" class="line_subtotal_tax wc_input_price" data-subtotal_tax="<?php echo esc_attr(wc_format_localized_price($tax_item_subtotal)); ?>" data-tax_id="<?php echo esc_attr($tax_item_id); ?>" />
                        </div>
                        <div class="input">
                            <label><?php esc_attr_e('Total:', 'woocommerce'); ?></label>
                            <input type="text" name="line_tax[<?php echo absint($item_id); ?>][<?php echo esc_attr($tax_item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" value="<?php echo esc_attr(wc_format_localized_price($tax_item_total)); ?>" class="line_tax wc_input_price" data-total_tax="<?php echo esc_attr(wc_format_localized_price($tax_item_total)); ?>" data-tax_id="<?php echo esc_attr($tax_item_id); ?>" />
                        </div>
                    </div>
                </div>
                <div class="refund" style="display: none;">
                    <input type="text" name="refund_line_tax[<?php echo absint($item_id); ?>][<?php echo esc_attr($tax_item_id); ?>]" placeholder="<?php echo wc_format_localized_price(0); ?>" class="refund_line_tax wc_input_price" data-tax_id="<?php echo esc_attr($tax_item_id); ?>" />
                </div>
            </td>
            <?php
        }
    }
    ?>

    <td class="wc-order-edit-line-item" width="1%">
        <div class="wc-order-edit-line-item-actions">
            <?php if ($order->is_editable()) : ?>
                <a class="edit-order-item tips" href="#" data-tip="<?php esc_attr_e('Edit item', 'woocommerce'); ?>"></a><a class="delete-order-item tips" href="#" data-tip="<?php esc_attr_e('Delete item', 'woocommerce'); ?>"></a>
                <?php endif; ?>
        </div>
    </td>
</tr>

Thanks in advance.

回答1:

— Code updated 2 — (FOUND THE PROBLEMS, see comments in code)

Just under your custom code in html-order-item.php core file, if you take a look to existing code, you have woocommerce_admin_order_item_values hook that you can use for your code, instead overriding a WooCommerce core file.

But you will also need to use woocommerce_admin_order_item_headers as you will see below in my code snippets…

Because YES you are going to loose your code customization in that PHP file when WooCommerce will be updated. That's why you will find everywhere in WordPress and WooCommerce core files a lot of different hooks to use, for customizations.

You can NOT copy this core files to your active child theme (or active theme) and doing this will not have any effect on it.

What you can do is copy the templates folder located in WooCommerce plugin, to your active child theme (or active theme) and rename it woocommerce. This way you can override the WooCommerce templates via your theme, as explained here in this documentation.

Now, first you are going to replace back the original core file in the plugin.

In your child theme, you normally have a function.php file where you are going to add the code below (a hooked function):

add_action( 'woocommerce_admin_order_item_headers', 'download_image_admin_order_item_headers', 10, 0 );
function download_image_admin_order_item_headers(){
    echo '<th class="item sortable" colspan="1" data-sort="string-ins">' . __( 'Downloads', 'woocommerce' ) .'</th>';
}

add_action( 'woocommerce_admin_order_item_values', 'download_image_order_item_values', 10, 3 );
function download_image_order_item_values( $_product, $item, $item_id ){
    // Calling global $post to get the order ID
    global $post;
    // The Order ID
    $order_id = $post->ID;

    // the Product ID and variation ID (if different of zero for variations)
    $product_id = $item['product_id'];
    $variation_id = $item['variation_id'];

    // If is not a variable product we replace the variation ID by the product ID
    if (empty($variation_id)) $variation_id = $product_id;

    // HERE ==> Getting an instance of product object, Avoiding an error:
    // "Fatal error: Call to a member function get_gallery_attachment_ids()"
    $product = new WC_Product($product_id);
    // the Product post object
    $post_product = $product->post;

    $attachment_count = count($product->get_gallery_attachment_ids());
    $gallery = $attachment_count > 0 ? '[product-gallery]' : '';

    // CODE ERROR ===> This was returning empty before. You need to put
    // the product ID in get_post_thumbnail_id() function to get something
    $props = wc_get_product_attachment_props(get_post_thumbnail_id($product_id), $post_product);

    // Testing $props output (array not empty) => comment/uncomment line below
    // echo '<br>ITEM ID: ' . $item_id . '<br><pre>'; var_dump($props);  echo '</pre><br>';

    $image = get_the_post_thumbnail( $product->id, apply_filters('single_product_large_thumbnail_size', 'shop_single' ), array(
        'title' => $props['title'],
        'alt' => $props['alt'],
    ));



    // Added a condition to avoid other line items than products (like shipping line)
    if(!empty($product_id))
        echo apply_filters(
                'woocommerce_single_product_image_html', sprintf(
                        '<td class="name" colspan="1" ><a style="text-decoration: none;clear:both;float: left;margin-top: 5px;" href="%s" download = "Order#' . $order_id . '-' . $variation_id . '"><input type = "button" value="Download image"/></a></td>', esc_url($props['url'])
                ), $product->id
        );

 }

In Admin Order edit pages on item metabox you will see:

Code goes in function.php file of your active child theme (active theme or in any plugin file).

This code is tested and works.