11

In a WordPress website running WooCommerce, the user can login in his (default) personal area and display information like:

  • Orders history
  • Download
  • Addresses
  • Edit info
  • Logout

In the orders tab, a table is presented by default, showing a list of all orders, with a View button which redirects to the full detail page of that order.

What I'm trying to do is showing that table view in a modal window.

I don't have any problem in showing the modal with the target url loaded in it. The real problem is that the targeted url is that of the full page which is showing like in an <iframe>, and is not what I want.

I think there is some shortcode allowing to load just that table, or maybe some woocommerce function like load_order_content_by_id($id)?

Can anybody point me in the right direction?

Thanks

===SOLVED===

Thanks to Raunak Gupta for pointing me to the right function. I override the orders.php template, added Modal window html and edited $actions:

'view'   => array(
    'url'  => 'javascript:;',
    'data' => [
        'order-number' => $order->get_order_number()
    ],
    'name' => __( 'View', 'woocommerce' )
),

and on same file:

foreach ( $actions as $key => $action ) {
    echo '<a href="' . esc_url( $action['url'] ) . '" class="button ' . sanitize_html_class( $key ) . '"';
    if(isset($action['data']) && is_array($action['data'])){
        foreach($action['data'] AS $data_attr=>$data_value){
            echo 'data-' . sanitize_html_class($data_attr) .'="' .esc_html($data_value) . '" ';
        }
    }
    echo '>' . esc_html( $action['name'] ) . '</a>';
}

A little JS

$('.woocommerce-MyAccount-orders .button.view').on('click', function(e){
    e.preventDefault();
    var data = {};
    data.action = 'modal_order';
    data.order_number = $(this).data('order-number');

    $.get( ajax_script.ajax_url, data, function(response) {
        $('#modalOrderDetail').modal('show').find('.modal-body').html(response);
    });
});

and hooked into wordpress by function.php

function modal_order() {
    if(is_user_logged_in()) {
        $order_number = $_GET['order_number'];
        woocommerce_order_details_table($order_number);
    }
}

add_action('wp_ajax_modal_order', 'modal_order');
add_action('wp_ajax_nopriv_modal_order', 'modal_order');
Yuri
  • 3,082
  • 3
  • 28
  • 47
  • I don't know woocommerce, but your question seems very vague. Try to add more detail (perhaps a [https://jsfiddle.net/](https://jsfiddle.net/) ) and you might get more response. – Ruben Pirotte Aug 26 '16 at 07:15
  • I would make a fiddle if only it was possible to load wordpress in it :) I'll try to clarify my post – Yuri Aug 26 '16 at 11:30
  • If anyone feels the need for downvoting, at least, add an explaination for it – Yuri Oct 05 '16 at 08:18
  • @Yuri it seems that you created a security hole in your site if you used that code. Your `functions.php` only checks `is_user_logged_in()` not if the user owns that order. This means that any order id that is requested will be displayed as long as the user is logged in - including other users orders. [Looking at the code](https://github.com/woocommerce/woocommerce/blob/4ea81923da7795c289aba9930855bf45b95f484d/includes/wc-template-functions.php#L2536) it doesn't seem to do any other permission checks. – rtpHarry Dec 11 '19 at 11:20
  • @Yuri also, you dont need to add an action for `wp_ajax_nopriv_modal_order` because this is for ajax requests from not-logged-in users. – rtpHarry Dec 11 '19 at 11:21

3 Answers3

5

woocommerce_order_details_table( $order_id )

This WooCommerce function returns the full order details in HTML form by $order_id

Raunak Gupta
  • 10,412
  • 3
  • 58
  • 97
  • Nice :) And I presume that the template it uses - `order/order-details.php` is overridable in a theme should the structure not be what is needed. +1 – William Patton Aug 30 '16 at 13:59
  • 1
    Yes you can override `order/order-details.php`, `order/order-details-item.php` and `order/order-details-customer.php` files according to your need. – Raunak Gupta Aug 30 '16 at 14:32
  • A part from overriding the original template, what global variables should I include in order to use it? – Yuri Aug 31 '16 at 08:30
  • You don't have to write any global variable as there are already present in that template, you just need to copy them into your active theam and customize it accordingly. _assuming that you know who to override those template_ – Raunak Gupta Aug 31 '16 at 08:39
5

Here is the complete code to display curent customer orders in a modal window. Its based on a classic query to get current user orders and on the template my-account/orders.php (lightly customized)

<?php

if(is_user_logged_in()):

    // The query
    $args = array(
        // WC orders post type
        'post_type'   => 'shop_order',
        'numberposts' => -1,
        // for current user id
        'meta_key'    => '_customer_user',
        'meta_value'  => get_current_user_id(),
        // get orders statuses
        'post_status' => array_keys(wc_get_order_statuses()),
    );

    // Get all customer orders
    $customer_orders = get_posts( $args );
    $count_ord = 0;
    if (!empty($customer_orders))
        foreach ( $customer_orders as $custo_order )
            $count_ord++;

    if ( $count_ord > 0 )
        $has_orders = true;
    else
        $has_orders = false;

    // the template my-account/orders.php  ?>

    <?php do_action( 'woocommerce_before_account_orders', $has_orders ); ?>

    <?php if ( $has_orders ) : ?>

    <table class="woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table">
        <thead>
            <tr>
                <?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?>
                    <th class="<?php echo esc_attr( $column_id ); ?>"><span class="nobr"><?php echo esc_html( $column_name ); ?></span></th>
                <?php endforeach; ?>
            </tr>
        </thead>

        <tbody>
            <?php foreach ( $customer_orders as $customer_order ) :
                $order      = wc_get_order( $customer_order );
                $item_count = $order->get_item_count();
                ?>
                <tr class="order">
                    <?php foreach ( wc_get_account_orders_columns() as $column_id => $column_name ) : ?>
                        <td class="<?php echo esc_attr( $column_id ); ?>" data-title="<?php echo esc_attr( $column_name ); ?>">
                            <?php if ( has_action( 'woocommerce_my_account_my_orders_column_' . $column_id ) ) : ?>
                                <?php do_action( 'woocommerce_my_account_my_orders_column_' . $column_id, $order ); ?>

                            <?php elseif ( 'order-number' === $column_id ) : ?>
                                <a href="<?php echo esc_url( $order->get_view_order_url() ); ?>">
                                    <?php echo _x( '#', 'hash before order number', 'woocommerce' ) . $order->get_order_number(); ?>
                                </a>

                            <?php elseif ( 'order-date' === $column_id ) : ?>
                                <time datetime="<?php echo date( 'Y-m-d', strtotime( $order->order_date ) ); ?>" title="<?php echo esc_attr( strtotime( $order->order_date ) ); ?>"><?php echo date_i18n( get_option( 'date_format' ), strtotime( $order->order_date ) ); ?></time>

                            <?php elseif ( 'order-status' === $column_id ) : ?>
                                <?php echo wc_get_order_status_name( $order->get_status() ); ?>

                            <?php elseif ( 'order-total' === $column_id ) : ?>
                                <?php echo sprintf( _n( '%s for %s item', '%s for %s items', $item_count, 'woocommerce' ), $order->get_formatted_order_total(), $item_count ); ?>

                            <?php elseif ( 'order-actions' === $column_id ) : ?>
                                <?php
                                    $actions = array(
                                        'pay'    => array(
                                            'url'  => $order->get_checkout_payment_url(),
                                            'name' => __( 'Pay', 'woocommerce' )
                                        ),
                                        'view'   => array(
                                            'url'  => $order->get_view_order_url(),
                                            'name' => __( 'View', 'woocommerce' )
                                        ),
                                        'cancel' => array(
                                            'url'  => $order->get_cancel_order_url( wc_get_page_permalink( 'myaccount' ) ),
                                            'name' => __( 'Cancel', 'woocommerce' )
                                        )
                                    );

                                    if ( ! $order->needs_payment() ) {
                                        unset( $actions['pay'] );
                                    }

                                    if ( ! in_array( $order->get_status(), apply_filters( 'woocommerce_valid_order_statuses_for_cancel', array( 'pending', 'failed' ), $order ) ) ) {
                                        unset( $actions['cancel'] );
                                    }

                                    if ( $actions = apply_filters( 'woocommerce_my_account_my_orders_actions', $actions, $order ) ) {
                                        foreach ( $actions as $key => $action ) {
                                            echo '<a href="' . esc_url( $action['url'] ) . '" class="button ' . sanitize_html_class( $key ) . '">' . esc_html( $action['name'] ) . '</a>';
                                        }
                                    }
                                ?>
                            <?php endif; ?>
                        </td>
                    <?php endforeach; ?>
                </tr>
            <?php endforeach; ?>
        </tbody>
    </table>

    <?php do_action( 'woocommerce_before_account_orders_pagination' ); ?>

    <?php if ( 1 < $customer_orders->max_num_pages ) : ?>
        <div class="woocommerce-Pagination">
            <?php if ( 1 !== $current_page ) : ?>
                <a class="woocommerce-Button woocommerce-Button--previous button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page - 1 ) ); ?>"><?php _e( 'Previous', 'woocommerce' ); ?></a>
            <?php endif; ?>

            <?php if ( $current_page !== intval( $customer_orders->max_num_pages ) ) : ?>
                <a class="woocommerce-Button woocommerce-Button--next button" href="<?php echo esc_url( wc_get_endpoint_url( 'orders', $current_page + 1 ) ); ?>"><?php _e( 'Next', 'woocommerce' ); ?></a>
            <?php endif; ?>
        </div>
    <?php endif; ?>

<?php else : ?>
    <div class="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 _e( 'Go Shop', 'woocommerce' ) ?>
        </a>
<?php endif; ?>
        <?php _e( 'No order has been made yet.', 'woocommerce' ); ?>
    </div>

<?php do_action( 'woocommerce_after_account_orders', $has_orders ); ?>

<?php endif; ?>

You will have to add existing css rules to your modal window and/or customized that css rules.

This code is tested and fully functional.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Thank you for the answer, but with `woocommerce_order_details_table( $order_id )` I was able to obtain the desired result with few lines of code. – Yuri Aug 31 '16 at 14:25
  • @Yuri As you didn't mention it really, I have work on this, that is the woocommerce template for current customer orders, with pagination and everything, just as "my account" => "orders" (but without navigation, menu and everything that you don't want)… This is a complete key turn solution (alternative). – LoicTheAztec Aug 31 '16 at 14:30
  • I just wanted the single order table to be shown on a Modal window, not the full orders list. – Yuri Aug 31 '16 at 14:34
2

There isn't a single function that I'm aware of that can get all of the order details that you need in one go however you can call WC_Order class to get what you need. Making some calls methods similar to these would get you the info you need. You will likely need to make calls to more than just the get_items() method depending on the exact info you need. Generally they return objects similar in structure to post objects.

$order = new WC_Order($post->ID);
$_order = $order->get_items();

Look here under the 'inherited methods' section to find the methods you might need to call to get all the info you need. https://docs.woocommerce.com/wc-apidocs/class-WC_Order.html

William Patton
  • 730
  • 9
  • 21