So I'm trying to hide certain ship methods in Woocommerce based on a product tag. The main problem I face is my own lack PHP knowledge so I frankensteined the following code together with the help of some very friendly folks:
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_tag' , 10, 1 );
function check_cart_for_share() {
// load the contents of the cart into an array.
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$found = false;
// loop through the array looking for the tag you set. Switch to true if the tag is found.
foreach ($cart as $array_item) {
if (isset($array_item['product_tag']) && $array_item['product_tag'] == "CHOSEN_TAG") { // Replace "CHOSEN_TAG" with what ever tag you want
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_tag( $available_methods ) {
// use the function abve to check the cart for the tag.
if ( check_cart_for_share() ) {
// remove the rate you want
unset( $available_methods['flat_rate'] ); // Replace "flar_rate" with the shipping option that yu want to remove.
}
// return the available methods without the one you unset.
return $available_methods;
}
I understand that this code is by no means universal and thus the variables will be different from case to case but perhaps someone can tell me if something looks off in the code. Much appreciated
No doubt you have sorted this by now but your code was a good start for me ... and since I sorted it I have published it below. Your problem was that woocommerce doesn't have the product_tag in the cart array so you have to go and get it.
/* !Hide Shipping Options Woocommerce */
add_filter( 'woocommerce_available_shipping_methods', 'hide_shipping_based_on_tag' , 10, 1 );
function check_cart_for_share() {
// load the contents of the cart into an array.
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$found = false;
// loop through the array looking for the tag you set. Switch to true if the tag is found.
foreach ($cart as $array_item) {
$term_list = wp_get_post_terms( $array_item['product_id'], 'product_tag', array( "fields" => "names" ) );
if (in_array("Heavy",$term_list)) { // Replace "Heavy" with what ever tag you want
$found = true;
break;
}
}
return $found;
}
function hide_shipping_based_on_tag( $available_methods ) {
// use the function above to check the cart for the tag.
if ( check_cart_for_share() ) {
// remove the rate you want
unset( $available_methods['flat_rate'] ); // Replace "flat_rate" with the shipping option that you want to remove.
}
// return the available methods without the one you unset.
return $available_methods;
}
You can use shipping class of Woocommerce to achieve this.
Here is the step by step instructions.
Create a shipping class (woocommerce->settings->shipping->shipping classes).
Associate this shipping class with products (that you wand to hide shipping methods for)
Use this snippet. (copy and pate to function.php).
For more information about the snippet is available here.
Related
I need to make a specific field, in this case the VAT number, mandatory and not based on the country that the user selects.
In my case, the VAT number must be made optional only in Great Britain and Switzerland.
Here is the code I entered, which works immediately when the page loads, but then when the country changes it no longer executes the conditions.
It just does one interaction, it's like it does the if statement only once.
add_filter('woocommerce_checkout_fields', 'custom_billing_fields', 1000, 1);
function custom_billing_fields( $fields ) {
$user = wp_get_current_user();
global $woocommerce;
$country = $woocommerce->customer->get_country();
//Check user role
//If a company, so professionista role
if ( in_array( 'professionista', (array) $user->roles && $country !== 'GB')) {
//get mandatory VAT field
$fields['billing']['billing_vatcode']['required'] = true;
}
elseif($country == 'GB'){
//remove mandatory VAT field
$fields['billing']['billing_vatcode']['required'] = false;
}
//if is a classic client
else {
//remove other field
unset($fields['billing']['billing_vatcode']);
unset($fields['billing']['billing_pecaddress']);
unset($fields['billing']['billing_recipientcode']);
}
return $fields;
}
I realized that maybe the problem is due to the fact that only with Javascript you could do something like this and I tried:
add_action( 'woocommerce_after_checkout_form', 'bbloomer_shows_notice_shipping' );
function bbloomer_shows_notice_shipping(){
?>
<script>
jQuery(document).ready(function($){
// Set the country code (That will display the message)
var countryCode = 'GB';
$('select#billing_country').change(function(){
selectedCountry = $('select#billing_country').val();
if( selectedCountry == countryCode ){
$("p#billing_vatcode_field").removeClass("validate-required");
$("p#billing_vatcode_field").removeClass("woocommerce-validated");
}
else {
$("p#billing_vatcode_field").addClass("validate-required");
$("p#billing_vatcode_field").addClass("woocommerce-validated");
}
});
});
</script>
<?php
}
But it seems that, even reading around, it is not possible to remove the validation, by removing the class, at the time of checkout.
I need help.
Thanks!
foreach( $cart as $cart_item_id => $citem ) {
if ( $citem['product_id'] == $gift ) {
$item_id = $cart_item_id;
$grp = $citem['data']->get_regular_price();
//$woocommerce->cart->cart_contents[$cart_item_id]['line_subtotal'] = 1;
$woocommerce->cart->line_subtotal($cart_item_id, '0');
$woocommerce->cart->cart_contents[$cart_item_id]['line_subtotal'] = 1;
//WC()->cart->set_session();
//$woocommerce->cart->set_session();
}
}
I can get access to cart details in "woocommerce_before_calculate_totals" hook.
But I can not manage to change the line_subtotal of a specific product.
Is there any way to do it using "woocommerce_before_calculate_totals" hook?
Thanks in advance.
There is a way doing this using woocommerce_before_calculate_totals, but as you do that it would also affect the individual price of the product.
So if you choose to do this, you would have to reset the individual price of the product by then filtering woocommerce_cart_item_price.
But this is kind of wierd so I would try using woocommerce_cart_item_subtotal, if it works in your usecase.
add_filter('woocommerce_cart_item_subtotal','new_cart_item_subtotal_filter', 10, 3);
function new_cart_item_subtotal_filter( $oldTotal, $cart_item, $cart_item_key){
//filter...
return $oldTotal;
}
I have two plugins installed, which has generated two "Events" submenu in my toolbar. I would like to rename one of them to be able to make a difference.
I have found this topic:
how to rename plugin title
With this I can achieve to rename BOTH "Events" to any other title:
function my_text_strings( $translated_text, $text, $domain ) {
switch ( $translated_text ) {
case 'Event' :
$translated_text = __( '3rd party Events', 'Event' );
break;
}
return $translated_text;
}
add_filter( 'gettext', 'my_text_strings', 20, 3 );
The problem with this is that it renames both of my Event menu and I only want to rename the first.
With this code I can target the admin menu to get the complete tree in the source code and get the plugin's unique name, but this only gets me admin menu, not the toolbar.
add_action( 'admin_menu', 'myRenamedPlugin' );
function myRenamedPlugin() {
global $menu;
$searchPlugin = "pgl_wp_files"; // Use the unique plugin name
$replaceName = "New Name for Plugin";
$menuItem = "";
foreach($menu as $key => $item){
if ( $item[2] === $searchPlugin ){
$menuItem = $key;
}
}
$menu[$menuItem][0] = $replaceName; // Position 0 stores the menu title
}
There is another topic discussing the question here, but this also renames ALL with the same name.
How do I target the exact menu item (not just the ones with matching name) in the Toolbar to rename?
I've looked at the code and there isn't any filters that I can see. So you basically will need to do a hack.
Option 1
Get at the node you want to change (by id). Remove that node, then change the title and add it back
add_action('wp_before_admin_bar_render', function () {
global $wp_admin_bar;
// Get the node
$node = $wp_admin_bar->get_node('some-event-id');
// Remove the node
$wp_admin_bar->remove_node('some-event-id');
// Change the node title
$node->title = 'Events #2';
// Add the node back to the menu
$wp_admin_bar->add_menu($node);
}, 9999999, 0);
Option 2
Capture the output buffer of the admin menu, do some string or regex find and replace, then output the buffer
add_action('wp_before_admin_bar_render', function () {
ob_start();
add_action('wp_after_admin_bar_render', function () {
$buffer = ob_get_clean();
// do some find/replace
echo $buffer;
}, 1, 0);
}, 9999999, 0);
There are pros/cons to each, and neither would be perfect. But as far as I can tell, that is your best shot.
I have a function where I duplicate a user to all subsite when they registered.
I achieved that by doing this:
function sync_user( $user_id )
{
$list_ids = get_sites();
$current_site = get_current_site();
$info = get_userdata($user_id);
foreach( $list_ids as $list )
{
if ( $list->blog_id != $current_site->id )
{
add_user_to_blog($list->id, $info->ID, 'subscriber');
}
}
// quick fix for: above somehow doesn't add to main site. add to main site here.
add_user_to_blog(1, $info->ID, 'subscriber');
}
Now, I want to "unsyc" the user when I removed the user from the site. I tried to hook it by using 'remove_user_from_blog', but it caused infinite loop.
Where can I hook the following code so that I can remove all those users that I added previously using above code?
function unsync_user( $user_id )
{
$list_ids = get_sites();
foreach( $list_ids as $list )
{
remove_user_from_blog( $user_id, $list->ID );
}
}
edited the title for clarity
AbdulRahman was correct about that. When user click 'remove' from the user list, the action not fire 'delete_user' or 'deleted_user' hook. I tested it.
I think it is tricky. So, here is how to add custom removed_user action. Add these lines below into your plugin.
add_action('remove_user_from_blog', function($user_id, $blog_id) {
// checking current action
// refer: wp-admin/users.php:99
$wp_list_table = _get_list_table( 'WP_Users_List_Table' );
if( $wp_list_table->current_action() != 'doremove' ) {
return; // only proceed for specific user list action
}
$fire_removed_user_hook = null; // closure reference
$fire_removed_user_hook = function() use ($user_id, $blog_id, &$fire_removed_user_hook) {
do_action( 'removed_user', $user_id, $blog_id );
// remove the hook back
remove_action('switch_blog', $fire_removed_user_hook);
};
// restore_current_blog called at the last line in the remove_user_from_blog function
// so action switch_blog fired
add_action('switch_blog', $fire_removed_user_hook);
}, 10, 2);
add_action('removed_user', function($user_id, $blog_id) {
// the user removed from be blog at this point
}, 10, 2);
The hook "deleted_user" runs after a user is deleted ("delete_user" runs before the deletion occurs):
https://codex.wordpress.org/Plugin_API/Action_Reference/deleted_user
I'm trying to set / show a label for the billing_address_2 field on the Woocommerce checkout page, but can't find a way to do this. Does anyone know a solution?
The code below (which works fine on other fields) does not do the job.
add_filter( 'woocommerce_checkout_fields' , 'custom_rename_wc_checkout_fields' );
function custom_rename_wc_checkout_fields( $fields ) {
$fields['billing']['billing_address_2']['label'] = 'Building number';
return $fields;
}
Updating this question with an answer for the sake of future readers.
As of WooCommerce 3.5.1, commit 87054ece9a4c05db72e139730ed1764c63fab635 adds the 'screen-reader-text' label_class to both Billing & Shipping Address 2 fields for accessibility purposes as per Issue #21182. This class will keep the label hidden unless it is also changed (e.g. make it blank, like the Address 1 field).
Here's how I updated my functions.php to get the billing/shipping_address_2 labels back (I'm using the separate billing/shipping filters as I have made changes to other fields I didn't include in the code for brevity).
// Billing Fields.
add_filter( 'woocommerce_billing_fields', 'custom_woocommerce_billing_fields' );
function custom_woocommerce_billing_fields( $fields ) {
$fields['billing_address_2']['label'] = 'Address 2';
$fields['billing_address_2']['label_class'] = '';
return $fields;
}
// Shipping Fields.
add_filter( 'woocommerce_shipping_fields', 'custom_woocommerce_shipping_fields' );
function custom_woocommerce_shipping_fields( $fields ) {
$fields['shipping_address_2']['label'] = 'Address 2';
$fields['shipping_address_2']['label_class'] = '';
return $fields;
}