I found this thread but it doesn't exactly do what I'm looking for.
My question is, how can I change the default Woocommerce role "customer" to, e.g. "Subscriber" for new registered users.
And then, if user checks out (purchases product), change the role from "Subscriber" to "Customer".
I'm asking this as I want to show different content per user roles: "registered customer" and "subscribed (paid) customer".
The first part of your question is changing the default role of user that is created by WooCommerce. Honestly, I'd probably leave the default role as customer. And then create a new role/capability for people who purchase your specific product.
add_filter( 'woocommerce_new_customer_data', 'so_29647785_default_role' );
function so_29647785_default_role( $data ){
$data['role'] = 'subscriber'; // the new default role
return $data;
}
As you saw in my other answer (well my re-posted answer), you can do things once the user has finished paying, but hooking into the woocommerce_order_status_completed hook.
To adapt that code to do something specific to a particular product, you need to loop through the order items and check them against a product ID. Replace 999 with the ID of the product in question.
add_action( 'woocommerce_order_status_completed', 'so_29647785_convert_customer_role' );
function so_29647785_convert_customer_role( $order_id ) {
$order = new WC_Order( $order_id );
if ( $order->user_id > 0 ) {
foreach ( $order->get_items() as $order_item ) {
if( 999 == $order_item[ 'product_id' ] ) {
$user = new WP_User( $order->user_id );
// Remove existing role
$user->remove_role( 'customer' );
// Add new role
$user->add_role( 'subscriber' );
}
}
}
}
Note: Totally untested, but seems right in theory.
Related
My initial problem is that AJAX add-to-cart button on Woocommerce doesn't seem to work on Private products (which we're only displaying to a selection of customers): the wheel appears on the add-to-cart button, the page reloads, and the product is added to cart but there's no notice that it has, so it's quite confusing.
After lots of research, all I could find is this thread https://wordpress.org/support/topic/add-to-cart-redirecting-only-on-private-products-2/ which led me to believe it's most likely a bug?
I tried to come up with a work-around: disable AJAX add-to-cart and revert to default behaviour on Private products only so we can still display some kind of notification that the product has been added to cart. I've thought of this code as a starting point:
add_action( 'wp_enqueue_scripts', 'bbloomer_disable_woocommerce_cart_fragments', 11 );
function bbloomer_disable_woocommerce_cart_fragments() {
if ( is_front_page() ) wp_dequeue_script( 'wc-cart-fragments' );
}
Would this work? And how would I modify it so the condition is "if is private product"?
You could solve the problem by thinking the other way around. Instead of showing private products only for specific users you could hide them for all other user roles (leaving the products with the "publish" status).
This way the Ajax problem does not arise.
If the user has one of the roles defined in the $user_roles array of the following function, it hides all products in the $product_ids_to_hide array:
// hide products based on user role
add_filter( 'woocommerce_product_is_visible', 'hide_products_by_user_role', 10, 2 );
function hide_products_by_user_role( $visible, $product_id ) {
// only if the user is logged in
if ( ! is_user_logged_in() ) {
return $visible;
}
// set the product ids to hide
$product_ids_to_hide = array( 12, 14 );
// sets the user roles for which products are to be hidden
$user_roles = array( 'custom_role_1', 'custom_role_2' );
$user = wp_get_current_user();
if ( array_intersect( $user_roles, $user->roles ) && in_array( $product_id, $product_ids_to_hide ) ) {
return false;
}
return $visible;
}
To reply to your comment:
If you want to allow the user to modify the product ids to be hidden based on the user role you can create a .csv file to be uploaded to FTP with the list of product ids, one per line.
If your client in the future wants to remove or add a product id, he can do so simply by overwriting the .csv file with an FTP import or directly in the file manager (if the hosting allows it).
The file I created as an example will be called product-ids-to-hide.csv and will need to be uploaded to the same directory as the functions.php file. No header is required. Here is an example:
349
235
456
745
and this will be the new updated function:
// hide products based on user role
add_filter( 'woocommerce_product_is_visible', 'hide_products_by_user_role', 10, 2 );
function hide_products_by_user_role( $visible, $product_id ) {
// only if the user is logged in
if ( ! is_user_logged_in() ) {
return $visible;
}
// sets the user roles for which products are to be hidden
$user_roles = array( 'custom_role_1', 'custom_role_2' );
$user = wp_get_current_user();
// only if the user has at least one role present in the array
if ( empty( array_intersect( $user_roles, $user->roles ) ) ) {
return $visible;
}
// get an array with product ids
$product_ids_to_hide = file( realpath( __DIR__ . '/product-ids-to-hide.csv' ), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
if ( in_array( $product_id, $product_ids_to_hide ) ) {
return false;
}
return $visible;
}
The code has been tested and works. Add it to your active theme's functions.php.
A Customer creates a subscription and tells me what he requires via WooCommerce. The requirements are stored inside of the user profile using the user meta data. When he updates this in the WooCommerce my account area on the front end, it updates it fine within the Wordpress customer profile. No problems there.
The problem i have is, When he updates the requirements via a custom field inside of the WooCommerce account area on the front end, the subscription keeps renewing his old requirements over and over. How do i get it to so that when the subscription renews it uses the latest custom field data found inside the account area?
Example:
https://i.stack.imgur.com/UEMpY.png
This is what i have at the moment. In this example below, I tried to copy the user meta to the order meta and update it when the subscription renews. For some reason it's not updating. Perhaps this is the wrong way all together. My PHP is very limited. Can anyone advise?
// update subscription meta order
add_action('woocommerce_subscription_renewal_payment_complete','subscription_renew');
function subscription_renew( $order_id ) {
$order = wc_get_order( $order_id ); // get the order id
$user_id = $order->get_user_id(); // get the user id
if ( $order->get_total() > 1 ) { // not sure why this is here
$getsend = get_user_meta( $user_id, 'send' ); // grab the user meta field 'send'
$getcategories = get_user_meta( $user_id, 'categories' ); // grab the user meta field 'categories'
// now do the update
$order->update_meta_data( $user_id, 'send', $getsend ); // update user order meta field 'send'
$order->update_meta_data( $user_id, 'categories', $getcategories ); // update user order meta field 'categories'
$order_id = $order->save(); // save the order
return $order_id;
}
}
There are some mistakes in your code. You should remove $user_id argument from:
// now do the update
$order->update_meta_data( $user_id, 'send', $getsend ); // update user order meta field 'send'
$order->update_meta_data( $user_id, 'categories', $getcategories ); // update user order meta field 'categories'
replacing by:
// now do the update
$order->update_meta_data( 'send', $getsend ); // update user order meta field 'send'
$order->update_meta_data( 'categories', $getcategories ); // update user order meta field 'categories'
Now it should better works.
Update related to woocommerce_subscription_renewal_payment_complete hook:
It seems when looking to the docs and to some other code examples, that this hook has 2 arguments: $subscription and $last_order (but not $order_id).
Here is a different code version that should really work, adding to the last related renewal Order, your custom user data as order meta data:
add_action( 'woocommerce_subscription_renewal_payment_complete', 'add_order_meta_from_custom_user_meta', 10, 2 );
function add_order_meta_from_custom_user_meta( $subscription, $last_order ) {
if( ! $subscription )
return;
$user_id = $last_order->get_user_id(); // Get the user id
if( $send = get_user_meta( $user_id, 'send', true ) ) {
$last_order->update_meta_data( 'send', $send );
}
if( $categories = get_user_meta( $user_id, 'categories', true ) ) {
$last_order->update_meta_data( 'categories', $categories );
}
if( isset($send) || isset($categories) ) {
$last_order->save();
}
}
Code goes in function.php file of your active child theme (or active theme). It should better work.
We would like to prevent shop manager from changing order status, we found a help here in the link below Restrict user role to change only some order statuses in Woocommerce
But the issue here that it limits certain role ( shop Manager ) to some order statuses, we need to deny the shop manager from changing the order status completely not limit it to some order statuses.
Also the snippet we mentioned remove the the order statuses from the bulk action drop down & the order details here: https://prnt.sc/mpfl3b, we need to remove the statuses too from the quick action column here https://snipboard.io/B6SYHb.jpg
Simply we try to have the shop manager to when he try to change order status from bulk, order details page, or actions column to find there is no order statuses to select to change it or disable it completely.
Best Regards
As you can see in the example code the conditions of the statuses are determined in the if statement, because you want to apply this without a limit, it's just a matter of removing that if statement and returning empty arrays
p.s; if you mark my answer as the solution, then also vote for #LoicTheAztec original answer if you have not already done this, since his code just about contained the solution.
// Admin orders list: bulk order status change dropdown
function filter_dropdown_bulk_actions_shop_order( $actions ) {
// Targeting shop_manager
if( current_user_can( 'shop_manager' ) ) {
$actions = (array) null;
}
return $actions;
}
add_filter( 'bulk_actions-edit-shop_order', 'filter_dropdown_bulk_actions_shop_order', 20, 1 );
// Admin orders list: quick action
function filter_order_actions( $actions, $order ) {
// Targeting shop_manager
if( current_user_can( 'shop_manager' ) ) {
$actions = (array) null;
}
return $actions;
}
add_filter( 'woocommerce_admin_order_actions', 'filter_order_actions', 10, 2 );
// Admin order pages: order status dropdown
function filter_order_statuses( $order_statuses ) {
global $post, $pagenow;
if( $pagenow === 'post.php' || $pagenow === 'post-new.php' ) {
// Get ID
$order_id = $post->ID;
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// TRUE
if ( $order ) {
// Get current order status
$order_status = 'wc-' . $order->get_status();
// New order status
$new_order_statuses = array();
foreach ($order_statuses as $key => $option ) {
// Targeting "shop_manager"
if( current_user_can('shop_manager') && $key == $order_status ) {
$new_order_statuses[$key] = $option;
}
}
if( sizeof($new_order_statuses) > 0 ) {
return $new_order_statuses;
}
}
}
return $order_statuses;
}
add_filter('wc_order_statuses', 'filter_order_statuses', 10, 1 );
//Since the suggested answer apparently causes some new issues and doesn't solve the original issue in a couple of other cases, there are options to hide elements according to user type, something like the below - which is a bit of kludge, but might serve:
First, to load an admin style sheet applying only to Shop Managers:
/**
* SHOP MANAGER STYLES
* Front (Optional) and Back End stylesheet
* Style interface for users logged in with'shop_manager' role
* Add to theme functions.php
*/
add_action('admin_enqueue_scripts', 'shop_manager_styles');
//if front end stylesheet needs to be added to cover admin bar:
//add_action('wp_enqueue_scripts', 'shop_manager_styles' ) ;
function shop_manager_styles() {
$user = wp_get_current_user() ;
//uncomment following and remove next if not confined to admin
//if ( $user && in_array( 'shop_manager', $user->roles ) ) {
if ( in_array( 'shop_manager', $user->roles ) ) {
//time() as stylesheeet version to help bust caching - may not be necessary but doesn't hurt:
wp_enqueue_style(
'shop_manager_styles', get_stylesheet_directory_uri()
. '/css/shop_manager_styles.css', array(), time()
);
}
}
...and the css to hide the order-status label and menu completely, as well as related columns in shop_order sub-pages:
/** HIDE ORDER STATUS LABEL, SELECTION MENU IN ORDER EDIT
* AND RELATED COLUMNS IN shop_order SUB-PAGE
*/
.wc-order-status,
.column-order_status,
.column-wc_actions {
display: none;
}
You'd save that in your theme css folder in a new shop_manager_styles.css.
Now, you may have some need to show the order status to shop managers without their being able to edit it. That would also be doable with CSS, if also (even more) a kludge. It may be that you have other peculiarities in your installation that will prevent the above code or a minimally customized variation of it from working, but, even if it is a little less clean than removing the option via function, this kind of thing usually will work in a pinch.
(Edited to provide option to add stylesheet on front end - in case relevant options appearing in admin bar, otherwise no need to enqueue extra non-admin script.)
I need to add a new role to my recently registered user (upon buying any of my four specific subscription products). Until now, every recently registered user (those who buy a subscription product) get a Subscriber role. While I want them to be Subscriber + Advertiser if they buy any of my 4 target subscription products.
I have tried to use woocommerce_order_status_completed, woocommerce_order_status_processing and woocommerce_order_status_changed hooks, but none of them are working with my code.
I have modified the function and code inside these hooks several times but I got nothing special.
Until now, I have used this code.
add_action( 'woocommerce_order_status_completed', 'so_29647785_convert_customer_role' );
function so_29647785_convert_customer_role( $order_id ) {
$order = new WC_Order( $order_id );
if ( $order->user_id > 0 ) {
foreach ( $order->get_items() as $order_item ) {
if( 4008 == $order_item[ 'product_id' ] ) {
$user = new WP_User( $order->user_id );
// Add new role
$user->add_role( 'advertiser' );
}
}
}
}
I will appreciate any help or track.
I have also tried this code and it is helpful in creating a user with both Subscriber + Advertiser roles but I can't do so in my case. Because I need users to be registered with both Subscriber + Advertiser roles only if they will buy four of my target subscription products. While this code is adding both Subscriber + Advertiser to every new user regardless of the product they choose.
add_filter('woocommerce_new_customer_data', 'bbloomer_assign_custom_role', 10, 1);
function bbloomer_assign_custom_role($args) {
$args['role'] = 'advertiser';
return $args;
}
Any help will highly be appreciated!
Since Woocommerce 3, your code is outdated and there are some errors and mistakes in your code, like $order_item['product_id'] will not work… Try the following instead:
add_action( 'woocommerce_order_status_processing', 'order_status_change_add_user_role', 10, 2 );
add_action( 'woocommerce_order_status_completed', 'order_status_change_add_user_role', 10, 2 );
function order_status_change_add_user_role( $order_id, $order ) {
if ( $order->get_user_id() > 0 ) {
$user = $order->get_user(); // Get an instance of the WP_User object
foreach ( $order->get_items() as $item ) {
// Check that user role is not set yet and that is matching with a product ID
if( 4008 == $item->get_product_id() && ! in_array('advertiser', $user->roles) ) {
$user->add_role( 'advertiser' ); // Add new role
break; // Stop the loop
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). It should works now.
Order and order items related since Woocommerce 3:
How to get WooCommerce order details
Get Order items and WC_Order_Item_Product in Woocommerce 3
add_action( 'woocommerce_order_status_completed', 'add_advertiser_role' );
function add_advertiser_role( $order_id ) {
$order = new WC_Order( $order_id );
if ( $order->get_user_id() > 0 ) {
foreach ( $order->get_items() as $order_item ) {
if( 4008 == $order_item->get_product_id() ) {
$user = new WP_User( $order->get_user_id() );
// Add new role
$user->add_role( 'advertiser' );
}
}
}
}
Programatically get WooCommerce Order details
I'm working on WooCommerce with two product types. I need some of them ($products_to_check) to create an specific role account (subscriber) and the rest of them another one (customer, which is the default one for WooCommerce).
The thing is, if I have an user who's already a costumer, I don't want him to change its role to subscriber (so I check if he's logged in, which mean he's not a new costumer and have a previous defined role). On the other hand, if a subscriber purchase one of the products not included in $products_to_check, he should get a role upgrade (from subscriber to costumer).
This is my function, which is not working but I don't really know why.
add_action( 'woocommerce_order_status_completed', 'change_role_on_purchase' );
function change_role_on_purchase( $order_id ) {
$order = wc_get_order( $order_id );
$items = $order->get_items();
$products_to_check = array( ... );
$user = new WP_User( $order->user_id );
$user_meta = get_userdata( $order->user_id );
$user_roles = $user_meta->roles;
foreach ( $items as $item ) {
if ( $order->user_id > 0 && in_array( $item['product_id'], $products_to_check ) ) {
if (!is_user_logged_in()){
$user->set_role( 'subscriber' );
}
}
elseif ( $order->user_id > 0 && !(in_array( $item['product_id'], $products_to_check)) ) {
if (is_user_logged_in() && in_array('subscriber', $user_roles)){
$user->set_role( 'customer' );
}
}
}
}
I'd really appreciate any help on this issue. What am I doing wrong?
There are several things you need to take note here.
Make sure you hook is invoked after or override the WooCommerce's or any other plugins' hooks that set user roles.
Do not use your log in condition to check if users are new. user_register is the hook to use for newly registered users.
Your role logic loop here is also flawed. You are iterating through all product orders and attempts to set role on each iteration. This obviously means that each iteration can change the user role if condition satisfies and can override the previous ones. (Though it seems like you are hoping your logged in condition will do the trick, I think that's very error prone even if it could maybe work.)
The WooCommerce subscription plugin actually sets the subscriber role to users that have active subscriptions and customer role for expired subscription users. This is configureable but I think that's a more clear assignment.
So first make sure your hook triggers after or overrides the WooCommerce user role hooks. And I think you can do away completely with the new or old user logic, as the only important thing is whether they purchased a product that's going to upgrade them; if not, they should have the default role.