1

I am making a plugin to hide specific product category only for a user role.

add_action( 'woocommerce_product_query', 'wcpp_hide_product' );
function wcpp_hide_product( $q ) {
    //if main query, skip
    if ( ! $q->is_main_query() ) return;
    //jika admin, skip
    if(is_admin()) return;
    //if user is not logged in, or dont have PRIVATE_MEMBER roles hide the product
    $current_user = wp_get_current_user();
    if(!isset($current_user->roles) || !in_array('PRIVATE_MEMBER', $current_user->roles)){ 
        $tax_query = (array) $q->get( 'tax_query' );
        $tax_query[] = array(
           'taxonomy' => 'product_cat',
           'field' => 'id',
           'terms' => 'PRIVATE_CATEGORY',
           'operator' => 'NOT IN'
        );
        $q->set( 'tax_query', $tax_query );
    }

}

Thats code will hide product from search. But other users still can access the product detail page from direct link.

Here my code that hide the product single pages (which is not works):

add_action( 'woocommerce_before_single_product', 'wcpp_hide_product_detail' );
//add_action( 'pre_get_posts', 'wcpp_hide_product_detail' );

function wcpp_hide_product_detail() {
    if(is_admin()) return;
    global $post;
    $terms = get_the_terms( $post->ID, 'product_cat' );
    if(empty($terms)) return;
    $is_private=false;
    foreach($terms as $term){
        if('PRIVATE_CATEGORY'==$term->term_id){
            $is_private = true;
            break;
        } 
    }
    if(!$is_private) return;
    $current_user = wp_get_current_user();
    if(!isset($current_user->roles) || !in_array('PRIVATE_MEMBER', $current_user->roles)){ 
        //need do something here, but not works
        //maybe redirect
        //wp_redirect(home_url());
        /* or like set 404
        global $wp_query;
        $wp_query->set_404();
        status_header(404);
        */
    }
}

If I set a redirect script, it says: "Cannot modify header information - headers already sent by ..."

I think this will works if I can modify the product query to negative ID number which will trigger the "product not found" page. But I dont know which is the action hook name to do it. Then I may be my code could works using wp action hook like add_action( 'wp', 'wcpp_hide_product_detail' );. But I dont know the right hook.

Any help is appreciated.

1

I have revisited your code as there was some mistakes (everything is commented in the code). Also:

  • You can use current_user_can() to check a user role for the current user.

  • You can use has_term() conditional function to check for post terms of any taxonomy.

The following code will hide products that belongs to a specific product category from user that have not a specific user role. If they try to access that specific products, they will be redirected to shop page with a custom notice…

Additionally the ajax add to cart button or the "Read more" button is replaced on all products loop for that specific products by a disable greyed button (in front end only).

Here is the code:

// Hide specific products from shop and archives pages
add_action( 'woocommerce_product_query', 'wcpp_hide_product' );
function wcpp_hide_product( $q ) {
    // skip for main query and  on admin
    if ( ! $q->is_main_query() || is_admin() )
        return;

    // Hide specific products for unlogged users or users without PRIVATE_MEMBER user role
    if ( is_user_logged_in() || ! current_user_can('PRIVATE_MEMBER') ) {
        $tax_query = (array) $q->get( 'tax_query' );
        $tax_query[] = array(
           'taxonomy' => 'product_cat',
           'field' => 'term_id', // it's not 'id' but 'term_id' (or 'name' or 'slug')
           'terms' => 'PRIVATE_CATEGORY',
           'operator' => 'NOT IN'
        );
        $q->set( 'tax_query', $tax_query );
    }
}

// Replace add to cart button by a disabled button on specific products in Shop and archives pages
add_filter( 'woocommerce_loop_add_to_cart_link', 'wcpp_replace_loop_add_to_cart_button', 10, 2 );
function wcpp_replace_loop_add_to_cart_button( $button, $product  ) {
    if ( ( is_user_logged_in() || ! current_user_can('PRIVATE_MEMBER') )
    && has_term( 'PRIVATE_CATEGORY', 'product_cat', $product->get_id() ) ) {;
        $button = '<a class="button alt disabled">' . __( "Private", "woocommerce" ) . '</a>';
    }
    return $button;
}

// On single product pages, redirect to shop and display a custom notice for specific products
add_action( 'template_redirect', 'wcpp_redirect_hiding_product_detail' );
function wcpp_redirect_hiding_product_detail() {
    if ( ( is_user_logged_in() || ! current_user_can('PRIVATE_MEMBER') )
    && has_term( 'PRIVATE_CATEGORY', 'product_cat' ) && is_product() ) {
        // Add a notice (optional)
        wc_add_notice(__("Sorry, but you are not allowed yet to see this product."), 'notice' );

        // Shop redirection url
        $redirect_url = get_permalink( get_option('woocommerce_shop_page_id') );
        wp_redirect($redirect_url); // Redirect to shop

        exit(); // Always exit
    }
}

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

Tested and works.

  • wow, i have a lot of bonus answer. this really great. thank you so much – plonknimbuzz Apr 14 at 5:53
  • for anyone who hard to find the answer. using template_redirect hook and change id to term_id is the answer. the rest is just fixing my bad code – plonknimbuzz Apr 14 at 5:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.