I'm using the code below to add three buttons to the WooCommerce checkout page so customers can leave a donation. It's working as it should be, but it would be great if I can highlight one of the buttons when a customer adds a donation to their cart and disable the other two.
Any thoughts on how I can get this to work?
add_action( 'woocommerce_review_order_before_submit', 'charity_checkout_add_on', 9999 );
function charity_checkout_add_on() {
$product_ids = array( 59355 );
$in_cart = false;
foreach( WC()->cart->get_cart() as $cart_item ) {
$product_in_cart = $cart_item['variation_id'];
if ( in_array( $product_in_cart, $product_ids ) ) {
$in_cart = true;
break;
}
}
if ( ! $in_cart ) {
echo '<div id="charity-donation">';
echo '<h6>Heading text</h6>';
echo '<p>Explanation</p>';
echo '<a class="charity-button" href="?add-to-cart=59356">€0,50</a><a class="charity-button" href="?add-to-cart=59357">€1,00</a><a class="charity-button last" href="?add-to-cart=59358">€2,50</a>';
echo '</div>';
}
}
Instead of hard-coding the HTML for the buttons you can loop over the $product_ids array to fetch and build up the HTML dynamically. This will work for both simple and variable products. You should always escape these values where possible: see https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/
You could then simply add a css class to the button if the product is in the cart by comparing its ID against any of the IDs in the cart.
Issues with add to cart in URL
If you don't redirect to the cart page after the product has been added, when the checkout refreshes the ?add-to-cart=ID query parameter will remain in the URL. This means that if you refresh the checkout page, the product will be added again. To solve this, either tick "Redirect to the basket page after successful addition" in Woocommerce Settings > Products > General or change the add-t-cart url to something like yourdomain.com/cart/?add-to-cart=ID
Try:
add_action( 'woocommerce_review_order_before_submit', 'charity_checkout_add_on', 20 );
function charity_checkout_add_on() {
$product_ids = array( 59356, 59357, 59358 );
$products_in_cart = array();
$button_data = array();
// Gather product cart items in array
foreach( WC()->cart->get_cart() as $cart_item ) {
$cart_product = $cart_item['data'];
$products_in_cart[] = $cart_product->get_id();
}
// Dynamically generate buttons
$button_html = '';
$last_button = end( $product_ids );
foreach( $product_ids as $product_id ) {
if( $product = wc_get_product( $product_id ) ) {
$in_cart = ( in_array( $product->get_id(), $products_in_cart ) ) ? true : false;
$button_classes = 'charity-button';
if( $in_cart ) {
$button_classes.= ' disabled';
}
if( $product_id === $last_button ) {
$button_classes.= ' last';
}
$button_html .= sprintf(
'<a class="%s"%s>%s</a>',
esc_attr( $button_classes ),
( $in_cart ) ? '' : esc_url( 'href="?add-to-cart=' . $product->get_id() . '"' ),
esc_html( get_woocommerce_currency_symbol() . $product->get_price() )
);
};
}
?>
<h4>Make a Donation?</h4>
<div id="charity-donation">
<h6>Heading text</h6>
<p>Explanation</p>
<?php echo $button_html; ?>
</div>
<?php
}
Your css class then could be something like:
.charity-button.disabled {
cursor: not-allowed;
opacity: 0.5;
}
Related
The context:
On my website users can customize clothing products. Along the customer funnel, it is asked to the user to input a quantity. This quantity is then used when our team receives the order to generate a quote. Nevertheless, it appears that the quantity is not stored as it always returns 1, even when the user inputs another number e.g. 100. I cannot find a way to solve this.
Below you will find code parts that include the quantity dimension. If you see any corrections to be made feel free to mention them.
add_filter( 'wc_add_to_cart_message_html', 'epopey_wc_add_to_cart_message_html', 10, 3);
function epopey_wc_add_to_cart_message_html ( $message, $products, $show_qty ){
$titles = array();
if ( ! is_array( $products ) ) {
$products = array( $products => 1 );
}
if ( ! $show_qty ) {
$products = array_fill_keys( array_keys( $products ), 1 );
}
foreach ( $products as $product_id => $qty ) {
/* translators: %s: product name */
$titles[] = apply_filters( 'woocommerce_add_to_cart_qty_html', ( $qty > 1 ? absint( $qty ) . ' × ' : '' ), $product_id ) . apply_filters( 'woocommerce_add_to_cart_item_name_in_quotes', sprintf( _x( '“%s”', 'Item name in quotes', 'woocommerce' ), strip_tags( get_the_title( $product_id ) ) ), $product_id );
$count += $qty;
}
$titles = array_filter( $titles );
Backorder notification
if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
echo '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'norebro' ) . '</p>';
}
?>
</h4>
</td>
<td class="product-quantity" data-title="<?php esc_html_e( 'Quantity', 'norebro' ); ?>">
<?php
if ( $_product->is_sold_individually() ) {
$product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
} else {
$product_quantity = woocommerce_quantity_input( array(
'input_name' => "cart[{$cart_item_key}][qty]",
'input_value' => $cart_item['quantity'],
'max_value' => $_product->backorders_allowed() ? '' : $_product->get_stock_quantity(),
'min_value' => '0'
), $_product, false );
}
echo apply_filters( 'woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item );
?>
</td>
email details to customer
<td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; vertical-align:middle; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;">
<?php
$qty = $item->get_quantity();
$refunded_qty = $order->get_qty_refunded_for_item( $item_id );
if ( $refunded_qty ) {
$qty_display = '<del>' . esc_html( $qty ) . '</del> <ins>' . esc_html( $qty - ( $refunded_qty * -1 ) ) . '</ins>';
} else {
$qty_display = esc_html( $qty );
}
echo wp_kses_post( apply_filters( 'woocommerce_email_order_item_quantity', $qty_display, $item ) );
?>
</td>
order details
<td class="product-quantity">
<?php echo apply_filters( 'woocommerce_order_item_quantity_html', ' <strong class="product-quantity">' . sprintf( '%s', $item->get_quantity() ) . '</strong>', $item ); ?>
</td>
</tr>
<?php if ( $show_purchase_note && $purchase_note ) : ?>
<tr class="product-purchase-note">
<td colspan="2"><?php echo wpautop( do_shortcode( wp_kses_post( $purchase_note ) ) ); ?></td>
Table with order details
<table class="woo-my-orders-table woo-details woo-my-content">
<thead>
<tr>
<th class="product-name"><?php esc_html_e( 'Product', 'norebro' ); ?></th>
<th class="product-quantity"><?php esc_html_e( 'Quantity', 'norebro' ); ?></th>
</tr>
</thead>
<tbody>
<?php
foreach( $order->get_items() as $item_id => $item ) {
$product = apply_filters( 'woocommerce_order_item_product', $order->get_product_from_item( $item ), $item );
wc_get_template( 'order/order-details-item.php', array(
'order' => $order,
'item_id' => $item_id,
'item' => $item,
'show_purchase_note' => $show_purchase_note,
'purchase_note' => $product ? get_post_meta( $product->get_id(), '_purchase_note', true ) : '',
'product' => $product,
) );
}
?>
<?php do_action( 'woocommerce_order_items_table', $order ); ?>
</tbody>
</table>
Congrats if you read until here, hope there is enough details. Thanks for your time and help.
Edit #1
Could the below code lines be a starting point to solve the problem ?
HTML
<div type="submit" class="wc-proceed-to-checkout" onClick="quantitySubmit(this.form)">
Valider le devis
JS
function quantitySubmit(form){
return (form.submit()){
window.location.href = "https://epopey.co/validation-du-devis/"
}
else {
text = "Please input a number"
}
}
The page that allows a change in quantity isn't updating the cart when it moves to the validate quote page. The button that was added to link to that page doesn't first perform the woo commerce cart update. See below:
The input of type submit must be "clicked" so the page can post back to itself and update the cart before the browser is redirected to the new page.
I'd recommend a simple javascript function in the onclick attribute of the div (class = wc-proceed-to-checkout) that submits the form and returns true/false on success/failure and allows redirection to "https://epopey.co/validation-du-devis/" for success and gives whatever error message you decide on failure.
To redirect in JS: https://www.w3schools.com/howto/howto_js_redirect_webpage.asp
If you need help writing the javascript to submit your form and handle the return value, search for answers on this site, I know they exist/also on Google.
There are numerous other ways to solve this as well - hard to give a best practice answer without knowing your code base/the structure of the theme. Hopefully identifying the problem is enough for you to solve on your own.
TLDR; your validate quote button overrides a default submission button to update the cart with increased/decreased quantities. Make sure the form on this page posts before the page is redirected to the next, or you'll lose the changed quantities.
I have a custom taxonomy in WordPress called Case studies.
Home.php is a page. Within home.php I have ran a loop to get all the case studies:
<?php
// only display 5 posts at first, then load 5 more on button click
$args = array('post_type' => 'case-studies', 'posts_per_page' => 5 );
$the_query = new WP_Query($args);
if ( $the_query->have_posts() ) {
echo "<div id='customers' class='case-studies-container'>"; // If there are results, create a single div in which all case studies will sit.
// run a loop to get data from all the posts that exist
while ($the_query->have_posts() ) {
$the_query->the_post();
tp_get_part( 'templates/snippets/case-study-card',
array(
'heading' => get_the_title(),
'subheading' => get_field( 'case_study_subheading'),
)
);
}
echo "<div><input type='button' id='loadmore' value='Load more'/></div>
</div>" // case-studies-container closed;
wp_reset_postdata();
} else {
// no posts found
}
?>
On #loadmore button click, I want it to load 5 more posts from the DB and display them. To accomodate this, I have the following JS within *home.php*
<script type="text/javascript">
$(document).ready(function(){
$('#loadmore').click(function(){
// alert('Hello world');
// load more case studies here
jQuery(function($){
var column_width = $(this).data('column-width');
var max_num_pages = $(this).data('max-num-pages');
var ignore = $(this).data('featured');
var post_type = $(this).data('type');
var button = $(this),
data = {
action:'loadmore',
query: loadmore_params.posts, // that's how we get params from wp_localize_script() function
page : loadmore_params.current_page,
security : loadmore_params.security,
columns : column_width,
exclude : ignore,
max_num_pages : max_num_pages,
post_type : post_type
};
$.ajax({
url : loadmore_params.ajaxurl, // AJAX handler
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
button.text('Loading...'); // change the button text, you can also add a preloader image
},
success : function( data ){
if( data ) {
button.text( 'Load More' ).prev().before(data); // insert new posts
loadmore_params.current_page++;
$('.case-studies-container').find('.case-card').last().after( data ); // where to insert posts
if ( loadmore_params.current_page == max_num_pages )
button.remove(); // if last page, remove the button
// you can also fire the "post-load" event here if you use a plugin that requires it
// $( document.body ).trigger( 'post-load' );
} else {
button.remove(); // if no data, remove the button as well
}
},
error : function(error){ console.log(error) }
});
});
});
});
</script>
And the following AJAX written in ajax-loaders.php:
<?php
function ajax_handler(){
check_ajax_referer('load_more', 'security');
// prepare our arguments for the query
$args = json_decode( stripslashes( $_POST['query'] ), true );
$args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
$args['post_status'] = 'publish';
$args['post__not_in'] = explode(',',$_POST['exclude']);
$args['max_num_pages'] = $_POST['max_num_pages'];
$cols = $_POST['columns'];
$type = $_POST['post_type'];
query_posts($args );
if( have_posts() ) :
// run the loop
while( have_posts() ): the_post(); ?>
<div class="<?php echo ($cols ? 'col-'.$cols : 'col-3') .' '. ($type === 'case-study' ? 'case-studies' : 'article__item'); ?> grid__item" id="<?php echo get_the_id(); ?>">
<?php
tp_get_part( 'templates/snippets/case-study-card',
array(
'filterable' => true,
'post' => $post,
'type' => get_cpt_term(get_the_id(), 'case-studies')
)
);
?>
</div>
<?php
endwhile;
endif;
die;
}
add_action('wp_ajax_loadmore', 'ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'ajax_handler'); // wp_ajax_nopriv_{action}
?>
With all these files set up, on button click, nothing happens? If I uncomment out the alert, the alert occurs which means the trigger is occurring, but unsure on where my load more functionality is going wrong?
Here is the flow:
Customer adds product to cart.
Customer adds coupon "smile" at checkout.
When customer places order the function will run before the Order
Details page loads. Function will check for "smile" coupon and if it
has been applied it redirects to a new page where they will be
offered additional products for free. If not, then it continues on
as normal.
I have been referencing two solutions I found through a Google search similar to parts of my problem. Individually I get them to work but together I cannot seem to get them to work correctly.
Here is my code:
add_action( 'woocommerce_thankyou' , 'sq_checkout_custom_redirect' );
function sq_checkout_custom_redirect($order_id) {
global $woocommerce;
$order = new WC_Order( $order_id );
$coupon_id = 'smile';
$applied_coupon = $woocommerce->cart->applied_coupons;
$url = 'https://site.mysite.org/score-you-win/';
if( $applied_coupon[0] === $coupon_id ) {
echo "<script type=\"text/javascript\">window.location.replace('".$url."');</script>";
} else {
echo '<h3 style="font-size:200px; z-index:30000; color:#000 !important;">Coupon not applied</h3>';
}
}
No matter what coupon I apply I get the message "Coupon not applied." and no redirect happens.
The two solutions that I am referencing are:
Find applied coupon_id in cart
Redirect with JS
This code runs successfully:
add_action( 'woocommerce_thankyou', function ($order_id){
$order = new WC_Order( $order_id );
$coupon_id = "smile";
$url = 'https://site.mysite.org/score-you-win/';
if ($order->status != 'failed') {
echo "<script type=\"text/javascript\">window.location.replace('".$url."');</script>";
}
});
And this runs successfully:
function product_checkout_custom_content() {
global $woocommerce;
$coupon_id = 'smile';
$applied_coupon = $woocommerce->cart->applied_coupons;
if( $applied_coupon[0] === $coupon_id ) {
echo '<span style="font-size:200px; z-index:30000; color:#red !important;">We are happy you bought this product =)</span> ';
} else {
echo '<h3 style="font-size:200px; z-index:30000; color:#000 !important;">Coupon not applied</h3>';
}
}
add_action( 'woocommerce_thankyou' , 'sq_checkout_custom_redirect' );
Updated: In woocommerce "Order Received" page (thankyou), there is no more WC_Cart object available. Instead you need to target the WC_Order object this way:
add_action( 'woocommerce_thankyou', 'thankyou_custom_redirect', 20, 1 );
function thankyou_custom_redirect( $order_id ) {
// Your settings below:
$coupon_id = 'smile';
$url = 'https://site.mysite.org/score-you-win/';
// Get an instance of the WC_order object
$order = wc_get_order($order_id);
$found = false;
// Loop through the order coupon items
foreach( $order->get_items('coupon') as $coupon_item ){
if( $coupon_item->get_code() == strtolower($coupon_id) ){
$found = true; // Coupon is found
break; // We stop the loop
}
}
if( $found )
echo "<script type=\"text/javascript\">window.location.replace('".$url."');</script>";
else
echo '<h3 style="font-size:200px; z-index:30000; color:#000 !important;">Coupon not applied</h3>';
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I am working on gravity forms add-on. I am saving all the values of gform fields in my database successfully after form submission using this hook gform_after_submission. Now I want to save the total pricing value but I don't get it I also see that the total pricing value coming from span.
So i search a lot but nothing found except some java script code:
<script type="text/javascript">
gform.addFilter( 'gform_product_total', function(total, formId) {
return total;
} );
</script>
But i don't get that how can i get this value in gform_after_submission hook
So please give me some suggestions
check bellow code . it's meet your requirement .
function saiful_gf_remove_money_symbol( $s ) {
return GFCommon::to_number( $s );
}
add_filter( 'saiful_gf_remove_money_symbol', 'saiful_gf_remove_money_symbol' );
add_filter( 'gform_product_info', 'add_fee', 10, 3 );
function add_fee( $product_info, $form, $lead ) {
$loop_price = 0;
if( isset( $product_info['products'] ) && count( $product_info['products'] ) > 0 ){
foreach ( $product_info['products'] as $data ){
if( isset( $data['options'] ) && count( $data['options'] ) > 0 ){
foreach ( $data['options'] as $key => $option ) {
$loop_price += floatval( $option['price'] );
}
}
// Save From Here total amount
$new_price = apply_filters( 'saiful_gf_remove_money_symbol',$data['price']) + $loop_price ;
}
}
return $product_info;
}
I'm currently in a problem where I have to add option in Cart with checkbox (for each item in Cart) which will change item's price with one from custom attribute.
This is an illustration of it (I have already created custom field, just need price updating function when button "Update cart" is clicked)
Code for displaying checkbox for each item (/woocommerce/templates/cart/cart.php):
<td class="product-url">
<?php
$html = sprintf( '<div class="lorem"><input type="checkbox" name="cart[%s][lorem]" value="%s" size="4" class="input-text url text" /> Lorem price</div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
Here I'm assuming that lorem price is stored in custom meta field associated with meta_key your_custom_meta_field
Use following code in your theme's function.php file
add_action( 'woocommerce_before_calculate_totals', 'my_custom_calculate_totals' );
function my_custom_calculate_totals( $cart ) {
if ( ! empty( $cart->cart_contents ) ) {
$lorem_price = array();
if ( ! empty( $_REQUEST['cart'] ) ) { // check if any of the checkboxes is checked
WC()->session->set( 'my_lorem_price', $_REQUEST['cart'] ); // set all checkboxes information in session
$lorem_price = $_REQUEST['cart'];
}
if ( empty( $lorem_price ) ) {
$lorem_price = WC()->session->get( 'my_lorem_price' ); // fetch all checkboxes information from session
}
if ( empty( $lorem_price ) ) {
return; // don't do anything if any of the checkboxes is not checked
}
foreach ( $cart->cart_contents as $cart_item_key => $cart_item ) {
if ( isset( $lorem_price[ $cart_item_key ]['lorem'] ) ) {
// Use following line if lorem price is set at variation level
$id = ( ! empty( $cart_item['variation_id'] ) && $cart_item['variation_id'] > 0 ) ? $cart_item['variation_id'] : $cart_item['product_id'];
// Use following line if lorem price is set at product level
// $id = $cart_item['product_id'];
$new_price = get_post_meta( $id, 'your_custom_meta_field', true ); // fetch price from custom field
$cart_item['data']->price = $new_price;
}
}
}
}