My goal is to allow certain role to view any order in shop. While displaying all orders in “My Account” page is relatively easy, accessing particular order created by different user always ends up with “Invalid order. My account” error.
So far I have added custom query on woocommerce_my_account_my_orders_query
filter, so that one can view all orders made by accounts with the same role. I set role permissions to full (as per this link, same permissions as for Shop Manager and Administrator, however those roles can’t view all orders by default either: https://github.com/woocommerce/woocommerce/blob/ee01d4219282387c2975ef4594677453c1dd7a0e/includes/class-wc-install.php#L1052
I was thinking of creating custom view-order.php
template, however I would rather use wc_get_order()
to get entire order object at once. Has anyone came across such challenge?
Here’s how I added new role:
function lf_wc_role_custom() { add_role( 'custom_role', 'Custom Role', [ 'level_9' => true, 'level_8' => true, 'level_7' => true, 'level_6' => true, 'level_5' => true, 'level_4' => true, 'level_3' => true, 'level_2' => true, 'level_1' => true, 'level_0' => true, 'read' => true, 'read_private_pages' => true, 'read_private_posts' => true, 'edit_posts' => true, 'edit_pages' => true, 'edit_published_posts' => true, 'edit_published_pages' => true, 'edit_private_pages' => true, 'edit_private_posts' => true, 'edit_others_posts' => true, 'edit_others_pages' => true, 'publish_posts' => true, 'publish_pages' => true, 'delete_posts' => true, 'delete_pages' => true, 'delete_private_pages' => true, 'delete_private_posts' => true, 'delete_published_pages' => true, 'delete_published_posts' => true, 'delete_others_posts' => true, 'delete_others_pages' => true, 'manage_categories' => true, 'manage_links' => true, 'moderate_comments' => true, 'upload_files' => true, 'export' => true, 'import' => true, 'list_users' => true, 'edit_theme_options' => true, ] ); } add_action('admin_init', 'lf_wc_role_custom');
Here’s how I modified orders query so far:
function lf_modify_my_order_query( $query ) { global $wpdb; $current_user_id = get_current_user_id(); $user = wp_get_current_user(); $allowed_roles = ['administrator', 'custom_role']; if ( array_intersect( $allowed_roles, $user->roles ) ) { $args = array( 'role' => $user->roles[0], ); $all_users_with_role = get_users( $args ); $user_ids = []; foreach ($all_users_with_role as $user_with_role) { array_push($user_ids, $user_with_role->data->ID); } $user_ids_query = implode("', '", $user_ids); $prepare_query = $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND meta_value IN ('$user_ids_query');", '_customer_user', $user_ids ); $results = $wpdb->get_results( $prepare_query, ARRAY_A ); $main_post_ids = wp_list_pluck( $results, 'post_id' ); unset( $query['customer'] ); $query['post__in'] = $main_post_ids; } return $query; } add_filter( 'woocommerce_my_account_my_orders_query', 'lf_modify_my_order_query', 20, 1 );```
Advertisement
Answer
Okay so here’s how I solved it. I ended up creating a copy of view-order.php
template that I would serve to users that belong to particular role. In my child theme directory I created woocommerce/myaccount/view-my-order.php
file, which contains the following before any content:
$user = wp_get_current_user(); $allowed_roles = ['custom_role']; if ( array_intersect( $allowed_roles, $user->roles ) ) { $order = wc_get_order($order_id); $customer_id = $order->customer_id; $customer_meta = get_userdata($customer_id); $customer_roles = $customer_meta->roles; if (!array_intersect($allowed_roles, $customer_roles)) { echo 'You do not have permission to view this order.'; die(); } } else { die(); }
The above is to make sure that logged user can view orders only when customer had the same role.
Then in functions.php
I put the following:
// Creates new query var so we can access new page function lf_custom_query_vars( $vars ) { $vars['view-my-order'] = 'view-my-order'; return $vars; } add_filter( 'woocommerce_get_query_vars', 'lf_custom_query_vars', 0 ); // Flushes rewrite rules (required with custom WC pages?) function lf_custom_flush_rewrite_rules() { flush_rewrite_rules(); } add_action( 'wp_loaded', 'lf_custom_flush_rewrite_rules' ); // Replaces native "View" button button next to each order on the list add_filter('woocommerce_my_account_my_orders_actions', function($actions, $order) { $user = wp_get_current_user(); $allowed_roles = ['custom_role']; if ( array_intersect( $allowed_roles, $user->roles ) ) { $actions['view'] = [ 'url' => wc_get_endpoint_url( 'view-my-order', $order->get_id() ), 'name' => __( 'View', 'txtdomain' ) ]; } return $actions; }, 10, 2); // Renders new template function lf_view_my_order_endpoint_content() { $order_id = get_query_var('view-my-order'); wc_get_template('myaccount/view-my-order.php', [ 'order_id' => $order_id, ]); } add_action( 'woocommerce_account_view-my-order_endpoint', 'lf_view_my_order_endpoint_content' );
It all can be further optimized, but you get the idea.