In Woocommerce, I have a Course product that uses WC Fields to collect student[s] names and email addresses, and custom email template that sends an email when this course product is purchased. As of now, based on the answer in this thread, I am able to collect the email addresses added to those custom fields and send one email to each student as a BCC recipient.
I am now trying to have the emails include the student's name and email address in the body of the email, but I do not want all of the names to appear in the same email. For example, if Student A (studenta#example.com) and Student B (studentb#example.com) are both enrolled in the same purchase, the email received by Student A should just contain in the body something like "Dear Student A, Thank you for enrolling. Use your email address studenta#example.com to login." and the email received by Student B should say the same but with Student B's info, and they should not see the other one's.
So I would have to send multiple emails, based on the number of students being enrolled in that purchase (which is set in the order meta, added from the purchased product's meta).
I tried adding a while() outside of the for() to continue checking through the items until it had gone through all of the items, and sending again for each time it found, but I think the foreach($items as $item) ends up starting through the first item again, since that sends two emails to only the first recipient.
***UPDATED****
I changed the custom email code (which I've stored in my plugins) to:
$order = wc_get_order( $order_id );
$items = $order->get_items();
$course_ordered = false;
$cqt = 1;
$emailnum = 0;
foreach ( $items as $item ) {
$product_id = $item['product_id'];
if ( $item['product_id']== 1164 ) {
$course_ordered = true;
$emailnum++;
continue;
}
}
if ( ! $course_ordered )
return;
while( $cqt <= $emailnum ){
// replace variables in the subject/headings
$this->find[] = '{order_date}';
$this->replace[] = date_i18n( woocommerce_date_format(), strtotime( $this->object->order_date ) );
$this->find[] = '{order_number}';
$this->replace[] = $this->object->get_order_number();
if ( ! $this->is_enabled() || ! $this->get_recipient() )
return;
// woohoo, send the email!
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
$cqt++;
}
And the code in my functions.php file, which was from this answer, is:
add_filter( 'woocommerce_email_headers', 'student_email_notification', 20, 3 );
function student_email_notification( $header, $email_id, $order ) {
// Only for 'wc_course_order' notification
if( 'wc_course_order' != $email_id ) return $header;
$student_emails = array();
//$enroll_num = 0;
// Loop though Order IDs
foreach( $order->get_items() as $item_id => $item_data ){
/*$course_qty = $item_data->get_quantity();
$q = 1;
while ( $q <= $course_qty){
$enroll_num++;
// Get the student full Name
$full_name = wc_get_order_item_meta( $item_id, 'First Name - '.$enroll_num, true );
$full_name .= ' ' . wc_get_order_item_meta( $item_id, 'Last Name - '.$enroll_num, true );
// Get the student email
$student_email = wc_get_order_item_meta( $item_id, 'Student Email - '.$enroll_num, true );
if( ! empty($student_email) && $full_name != ' ' )
// Format the name and the email and set it in an array
$student_emails[] = utf8_decode($full_name . ' <' . $student_email . '>'); // Add name + email to the array
$q++;
}*/
// Get the student full Name
$full_name = wc_get_order_item_meta( $item_id, 'First Name - 1', true );
$full_name .= ' ' . wc_get_order_item_meta( $item_id, 'Last Name - 1', true );
// Get the student email
$student_email = wc_get_order_item_meta( $item_id, 'Student Email - 1', true );
if( ! empty($student_email) && $full_name != ' ' )
// Format the name and the email and set it in an array
$student_emails[] = utf8_decode($full_name . ' <' . $student_email . '>'); // Add name + email to the array
}
// If any student email exist we add it
if( count($student_emails) > 0 ){
// Remove duplicates (if there is any)
$student_emails = array_unique($student_emails);
// Add the emails to existing recipients
$headers .= 'Cc: ' . implode(',', $student_emails) . "\r\n";
}
return $headers;
}
(If the section that is commented out is uncommented, it only sends to the first recipient, and not the second, if it is entered as two separate products, if I remove the while() and use the part below it instead, it sends one email to all recipients)
It sends to all recipients (i.e. gets all of the emails from all of the custom fields and uses those as CC or BCC, however I set it) twice, ie sends the same email twice to everyone. How can I get it to send twice but each time to only one of the recipients? Thank you for any suggestions!
I found a solution for the first half of my question - adding the user's names and emails from the product meta to the body of the email - though still have not been able to get it to send a unique email to each email address (email addresses are coming from this meta):
I updated my custom email template (this is only the email template, not the custom function that creates/sends the email) to the following:
<?php
$student_emails = array();
// Loop though Order IDs
foreach( $order->get_items() as $item_id => $item_data ){
// Get the student full Name
$full_name = wc_get_order_item_meta( $item_id, 'First Name - 1', true );
$full_name .= ' ' . wc_get_order_item_meta( $item_id, 'Last Name - 1', true );
// Get the student email
$student_email = wc_get_order_item_meta( $item_id, 'Student Email - 1', true );
print_r('Name: ' . $full_name . ', Login Email: ');
print_r($student_email . '<br/>');
}
?>
This prints the various users and their emails in the email body.
Related
I was wondering to know if it is possible to run a WordPress function with a delay of some seconds or minutes.
I use the following snippet to send an email to admin after a new user is registered on my website
add_action( 'user_register', 'registration_email_to_admin', 10, 1 );
function registration_email_to_admin( $user_id ) {
wp_new_user_notification( $user_id );
}
Then I use the following code to filter the email's content
//Filters the contents of the new user notification email sent to the site admin.
add_filter( 'wp_new_user_notification_email_admin', 'custom_wp_new_user_notification_admin_email', 10, 3 );
function custom_wp_new_user_notification_admin_email( $wp_new_user_notification_email, $user, $blogname ) {
//pass user id into a variable
$user_id = $user->id;
//get teams for that user
$teams = wc_memberships_for_teams_get_teams( $user_id );
$teamNames = [];
foreach($teams as $teamName){
array_push($teamNames, $teamName->get_name());
}
$teamNamesString = implode (',' , $teamNames);
$wp_new_user_notification_email['subject'] = sprintf( '[%s] New user registered. User ID = %s', $blogname, $user->id );
$wp_new_user_notification_email['message'] = sprintf( "%s ( %s ) has registered to the website %s.", $user->user_login, $user->user_email, $blogname ) . "\r\n\r\n";
$wp_new_user_notification_email['message'] .= sprintf(__('Username: %s'), $user->user_login) . "\r\n";
$wp_new_user_notification_email['message'] .= sprintf(__('Email: %s'), $user->user_email) . "\r\n";
$wp_new_user_notification_email['message'] .= sprintf(__('ID: %s'), $user->id) . "\r\n";
$wp_new_user_notification_email['message'] .= sprintf(__('Team Name: %s'), $teamNamesString) . "\r\n";
$wp_new_user_notification_email['headers'] = "From: Members Website <info#test.com> \n\r cc: GeorgeFR <test#yahoo.com>";
return $wp_new_user_notification_email;
}
On all the above, everything works well except the last append to the message
$wp_new_user_notification_email['message'] .= sprintf(__('Team Name: %s'), $teamNamesString) . "\r\n";
Using the WooCommerce Memberships for Teams plugin, it seems that the new user is being assigned to the team that has been invited to after his/her registration. That being said, the variable $teamNamesString is always empty on my email content.
So, is it possible to run all the above with some delay, making sure that the team has been assigned to the new user before I send the email? It is important for me to include the team's name to the email for business management purposes.
I was finally able to find the fix here.
Using the Woo Memberships and Woo Teams for Memberships plugins, we have 3 different team roles on our website. Owners, Managers, and Members. Also, we are using Woo Subscriptions in combination with Woo Memberships. So, every time a new subscription purchase comes, the purchaser will create a team and become the owner of that team.
The codes that worked are the following:
For Owners (used the woocommerce_subscription_status_active action from Woo Subscriptions):
//fires after purchases
function send_admin_email_with_owner_data_after_purchase( $subscription ) {
// Get the user in the order
$user_id = $subscription->get_user_id();
// Get the products in the order
$items = $subscription->get_items();
foreach( $items as $item ) {
$product = $item->get_product();
$product_id = $product->get_id();
if ($product_id == "8865") {
wp_new_user_notification( $user_id );
}
}
}
add_action( 'woocommerce_subscription_status_active', 'send_admin_email_with_owner_data_after_purchase', 10 );
For Managers and Members (used the wc_memberships_user_membership_created action from Woo Memberships):
//fires after acceptance of invitations to teams (in my case)
function send_admin_email_with_member_data( $plan, $args ) {
$user_id = $args['user_id'];
$memberArgs = array (
$status => "any",
$role => "manager,member"
);
//get teams for that user
$teams = wc_memberships_for_teams_get_teams( $user_id, $memberArgs );
if ( $args['is_update'] = true && ! empty( $teams ) ) {
wp_new_user_notification( $user_id );
}
}
add_action( 'wc_memberships_user_membership_created', 'send_admin_email_with_member_data', 20, 2 );
I am glad I found a way here. Thank you all for your help!
How to add a custom text 'per item' based on product category on all WooCommerce emails?
Therefore I tried to modify Add a custom text for a particular product on specific email notification in Woocommerce and Display a custom text based on product category in WooCommerce Email answer code to my needs.
// Setting the email_is as a global variable
add_action('woocommerce_email_before_order_table', 'the_email_id_as_a_global', 1, 4);
function the_email_id_as_a_global($order, $sent_to_admin, $plain_text, $email ){
$GLOBALS['email_id_str'] = $email->id;
}
// Displaying product description in new email notifications
add_action( 'woocommerce_order_item_meta_end', 'product_description_in_new_email_notification', 10, 3 );
function product_description_in_new_email_notification( $product_cat, $cat, $order = null ){
// HERE define your targetted product CAT
$targeted_id = 'x';
// HERE define the text information to be displayed for the targeted product id
$text_information = __("There is an offer in this particular item", "woocommerce");
// Getting the email ID global variable
$refNameGlobalsVar = $GLOBALS;
$email_id = $refNameGlobalsVar['email_id_str'];
// If empty email ID we exit
if(empty($email_id)) return;
// Only for "New Order email notification" for your targeted product ID
if ( 'customer_completed_order' == $email_id && 'new_order' == $email_id
in_array( $targeted_id, array( $cat->get_product_cat(), $item->get_variation_id() ) ) ) {
// Display the text
echo '<div class="product-text" style="margin-top:10px"><p>' . $text_information . '</p></div>';
}
}
Unfortunately without the desired result. I would like to display custom text under an order item name for all products of a particular product category in all email notifications (this is per item, not per email). Any advice?
Some notes on your code attempt:
$product_cat and $cat do not exist as arguments for the woocommerce_order_item_meta_end hook
$cat->get_product_cat() does not exist and is incorrect
Use has_term() WordPress function to check if the current post has any of given terms
So you get:
// Setting global variable
function action_woocommerce_email_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
$GLOBALS['email_data'] = array(
'email_id' => $email->id, // The email ID (to target specific email notification)
'is_email' => true // When it concerns a WooCommerce email notification
);
}
add_action( 'woocommerce_email_before_order_table', 'action_woocommerce_email_before_order_table', 1, 4 );
// Displaying description
function action_woocommerce_order_item_meta_end( $item_id, $item, $order, $plain_text ) {
// Getting the custom 'email_data' global variable
$ref_name_globals_var = $GLOBALS;
// Isset & NOT empty
if ( isset ( $ref_name_globals_var ) && ! empty( $ref_name_globals_var ) ) {
// Isset
$email_data = isset( $ref_name_globals_var['email_data'] ) ? $ref_name_globals_var['email_data'] : '';
// NOT empty
if ( ! empty( $email_data ) ) {
// Specific categories: the term name/term_id/slug. Several could be added, separated by a comma
$categories = array( 108, 1, 'categorie-1' );
// The text information
$text_information = __( 'There is an offer in this particular item', 'woocommerce' );
// Specific email notifications: multiple statuses can be added, separated by a comma
$email_ids = array( 'new_order', 'customer_processing_order', 'customer_completed_order', 'customer_on_hold_order' );
// Targeting specific email notifications AND check if the current post has any of given terms
if ( in_array( $email_data['email_id'], $email_ids ) && has_term( $categories, 'product_cat', $item->get_product_id() ) ) {
// Display the text
echo '<p>' . $text_information . '</p>';
}
}
}
}
add_action( 'woocommerce_order_item_meta_end', 'action_woocommerce_order_item_meta_end', 10, 4 );
I want to send admin new order e-mail to cc, if order has items from a parent product category:
I am using the code below but this doesn't seem to work. The mail is being sent but I receive a notification for an undefined variable. The person in CC does not receive the message either
add_filter( 'woocommerce_email_headers', 'bbloomer_order_completed_email_add_cc_bcc', 9999, 3 );
function bbloomer_order_completed_email_add_cc_bcc( $headers, $email_id, $order ) {
$order = wc_get_order( $order_id ); // The WC_Order object
if ( 'new_order' == $email_id && $orderid['product_cat']== 69) {
$headers .= "Cc: Name <your#email.com>" . "\r\n"; // del if not needed
}
return $headers;
}
Someone who wants to take a closer look at this?
The use of wc_get_order() is not necessary, you can already access the $order object via the parameters
$orderid['product_cat'] does not exist
Explanation via comments added in the code
So you could use
function filter_woocommerce_email_headers( $header, $email_id, $order ) {
// New order
if ( $email_id == 'new_order' ) {
// Set categories
$categories = array( 'categorie-1', 'categorie-2' );
// Flag = false by default
$flag = false;
// Loop trough items
foreach ($order->get_items() as $item ) {
// Product id
$product_id = $item['product_id'];
// Has term (a certain category in this case)
if ( has_term( $categories, 'product_cat', $product_id ) ) {
// Found, flag = true, break loop
$flag = true;
break;
}
}
// True
if ( $flag ) {
// Prepare the the data
$formatted_email = utf8_decode('My test <mytestemail#test.com>');
// Add Cc to headers
$header .= 'Cc: ' . $formatted_email . '\r\n';
}
}
return $header;
}
add_filter( 'woocommerce_email_headers', 'filter_woocommerce_email_headers', 10, 3 );
I am trying to change the sender email for all emails related to two products in WooCommerce, but not for all the others.
I have the code below to change the sender email, but I am not sure how to make it work for only those two products (by product id or category).
function change_sender_email( $original_email_address ) {
return 'admin#example.com';
}
add_filter( 'wp_mail_from', 'change_sender_email' );
Could I somehow use the filter 'woocommerce_email_recipient_customer_completed_order'?
I know how to use that to conditionally change the recipient of the email, but I couldn't get it to work to change the sender email.
You can use: woocommerce_email_from_address
// Change email sender address
function my_email_from_address( $from_email, $wc_email ) {
// Get the WC_Order object instance
$order = $wc_email->object;
// Get items
$items = $order->get_items();
// Loop through
foreach ( $items as $item ) {
// Get product ID
$product_id = $item->get_product_id();
// Compare
if ( $product_id == 30 ) {
$from_email = 'my.email1#stackoverflow.com';
} elseif ( $product_id == 32 ) {
$from_email = 'my.email2#stackoverflow.com';
}
}
return $from_email;
}
add_filter( 'woocommerce_email_from_address', 'my_email_from_address', 20, 2 );
With WooCommerce I use a special theme that handle bookings for motorbikes and scooters rental service. I want to get the order related data. I am trying to send an SMS when an email notification is sent to customer for completed, on hold, pending and **processing** order status.
I have use the code below for instance that output the data I need in SMS:
$order = new WC_Order($order_id);
$status = $order->get_status(); // order status
if( 'completed' == $status || 'processing' == $status || 'pending' == $status || 'on-hold' == $status ){
$user_phone = get_post_meta($order_id, '_billing_phone', true);
foreach ($order->get_items() as $item_id => $item) {
$product_id = $order->get_item_meta($item_id, '_product_id', true); // product ID
$product_name = get_post($product_id)->post_title; // Product description
// Related Booking data to insert in SMS
$book_check_in = $order->get_item_meta( $item_id, '_st_check_in', true );
$book_check_out = $order->get_item_meta( $item_id, '_st_check_out', true );
$book_pick_up = $order->get_item_meta( $item_id, '_st_pick_up', true );
$book_drop_off = $order->get_item_meta( $item_id, '_st_drop_off', true );
}
// Send SMS in SMS API
file_get_contents("http://144.76.39.175/api.php?username=xxxxxxxxxxx&password=xxxxxxxxxxx&route=1&message%5B%5D=The+message&sender=NBWREN&mobile%5B%5D=xxxxxxxxxxx");
}
This is not working. Where should I hook this code? I tried different templates and all I got were some 500 errors or simply nothing happened.
Please give me some help.
Thanks
You can use a custom function hooked in woocommerce_email_order_details hook, using included $order and $email objects.
You will be able to rearrange the message as you like, as this is just an example.
I have commented this code to make you understand how it works:
add_action('woocommerce_email_order_details', 'send_sms_on_email_notifications', 10, 4);
function send_sms_on_email_notifications($order, $sent_to_admin, $plain_text, $email){
$order_id = $order->id; // get the order ID for Order object
$email_id = $email->id; // get the email ID for Email object
$order_status = $order->get_status(); // Get order Status
// Array of Email IDs to avoid Admin email notifications (SMS sent twice on some notifications)
$emails_ids_exceptions = array('new_order', 'failed_order', 'customer_invoice', 'customer_note');
// Your targeted order status
$order_statuses = array('completed', 'processing', 'on-hold', 'pending');
$send_the_sms = false;
// Just for your targeted order statuses
if( in_array( $order_status, $order_statuses ) ):
// iterating in the order items
foreach($order->get_items() as $item_id => $item):
$prod_id = $order->get_item_meta( $item_id, '_product_id', true ); // product ID
$prod_name = get_post($prod_id)->post_title; // Product Name
$mobile = get_post_meta($order_id, '_billing_phone', true); // mobile phone
// Related Booking data to insert in SMS
$check_in = $order->get_item_meta( $item_id, '_st_check_in', true );
$check_out = $order->get_item_meta( $item_id, '_st_check_out', true );
$pick_up = $order->get_item_meta( $item_id, '_st_pick_up', true );
$drop_off = $order->get_item_meta( $item_id, '_st_drop_off', true );
// stoping the loop (just for one item)
break;
endforeach;
// Limiting to customer email notifications
if( !in_array( $email_id, $emails_ids_exceptions ) )
{
// inserting the order data (variables) in the message
$text = "Your order $order_id with $status status, for $prod_name. Your booking details: Check in time: $check_in, Check out Time: $check_out, Pick up $pick_up and drop of Time is $drop_off";
$send_the_sms = true;
}
// TRIGGERING THE SMS
if($send_the_sms)
{
// Replacing spaces by '+' in the message
$message = str_replace(' ', '+', $text);
// Inserting the message and the user number phone in the URL
$url = "http://144.76.39.175/api.php?username=xxxxxxxxxxx&password=xxxxxxxxxxx&route=1&message%5B%5D=$message&sender=NBWREN&mobile%5B%5D=$mobile";
// Triggering the SMS
file_get_contents($url);
}
endif;
}
This code will work for the first item of the order, assuming that people rent one bike or one scooter at the time.
The code is mainly tested, but I can't guaranty the triggered SMS as I can't test it on your SMS API. I hope this will work… Let me know.
Code goes in function.php file of your active child theme (or theme). Or also in any plugin php files.