I'm using Woocommerce hook woocommerce_email_footer() and inside my function, I need to call $email->get_content() which causes recursion and PHP memory error and WordPress exits with system error
Tried to remove the hook before calling the $email->get_content() and adding the hook back right after this call. However, this may not be a foolproof solution since some other session which hits exactly at the time when my function has removed the action might totally miss the hook custom action
I've written the following code in functions.php of my theme to capture the mail contents (mail body) into a local file just before it is being sent whenever a new order is received
//
// Capture the contents of the Emails sent and save into local files
// These Local files are used for further messaging through different channels
//
function Save_Email_Contents_into_Local_File ( $email ) {
if ( $email->id == 'customer_processing_order' ) {
// Remove the action temporarily so as not to cause Recursion while we refer to $email functions
remove_action( 'woocommerce_email_footer', 'Save_Email_Contents_into_Local_File', 20, 1 );
$TargetFilename = '/home/users/....../Sent_Mail.html' ;
$html_message = $email->get_content();
$formatted_message = $email->style_inline($html_message);
file_put_contents($TargetFilename, $formatted_message);
}
// Put the action back to original state
add_action( 'woocommerce_email_footer', 'Save_Email_Contents_into_Local_File', 20, 1 );
};
// add the action
add_action( 'woocommerce_email_footer', 'Save_Email_Contents_into_Local_File', 20, 1 );
Please note in the above function, I'm referring the $email->get_content() public function.
If I do not do the remove_action( 'woocommerce_email_footer', 'Save_Email_Contents_into_Local_File', 20, 1 ); this function becomes recursive and fails with a PHP memory error.
Although this is a workable solution, Removing the action can potentially cause another instance of the customer_processing_order from another user to miss the action and not come to this function if that session hits exactly at the time when this current session has removed the action and before adding the action again.
I'm sure I'm not doing it right! Is there any better way to accomplish what I need - basically I need the exact formatted mail content to be stored in a local file whenever the order is received. Similarly, I will need local file stored for Order completion and order hold, etc. but at later point of time.
Want to achieve storing formatted email into a local file a) without causing recursion / PHP memory errors b) without having to miss some instances of the execution missing the custom code attached to the hook.
Related
I am creating a plugin for WordPress that sends out a periodic newsletter using WordPress's built-in PHPMailer class. I have everything working correctly, but I want to send individual emails out to a specified number of recipients per hour.
I am pulling a number of email addresses from my database and running them through a foreach loop to send out individual emails and I am attaching each one through the addAddress() function.
To ensure I only send one email per address, I am trying to use clearAllRecipients() at the beginning of the loop, but this is not working. If I have my "email addresses per hour" set at 3, all 3 each email addresses is receiving 3 separate emails.
I also tried to run the list through the foreach loop and just run addAddress for each and use one send statement, but I still get 3 separate emails for each of the three addresses.
Below is my foreach loop. I can't figure out how am doing it wrong. Please help.
foreach ( $list as $e ) {
$phpmailer->clearAllRecipients();
$phpmailer->addAddress($e['email']);
if ( $phpmailer->Send() ) {
// Update the member row in the db. update the "checked" column to 1 so it will be skipped next pass through
$wpdb->query( $wpdb->prepare( "UPDATE {$q_vars['members']} SET checked = %d WHERE id = %d", 1, $e['id'] ) );
}
}
If you’re getting multiple emails with that code, it means your script as a whole is being run multiple times, not that a single run of the script is sending duplicates. You can verify this by appending a random number to your subject line. This is commonly caused by browser plugins that resubmit requests unnecessarily, but it may be because your code is being called by Wordpress for other reasons - adding some logging would reveal that.
Okay, typical PEBCAK error. Sorry! I had inadvertently wrapped my if/else statement to see which mail method to use in an identical foreach loop. So, I was running a foreach from my list, then checking which mail method to use, THEN running an identical foreach within that. I am stupid. Problem solved.
I have written an action that attaches to woocommerce_order_status_completed, and it works fine, adding a bit of meta data to the order. But the email that goes out after order completed seems to go BEFORE this runs, and therefore does not send the meta data in question (it will send it if I rerun the completed order again, but that is because this data is now already in the DB). So what I am looking for is either:
a hook that runs JUST before the completed email sends, OR
a way to have the completed email send AFTER woocommerce_order_status_completed hook
Any ideas or pointers? I looked through the Woocommerce API reference but can't find anything that seems to suit.
UPDATE: found an earlier hook and tried hooking it into
add_action( 'woocommerce_order_status_completed_notification','mysite_woocommerce_order_status_completed',5,1 );
which should run sooner, but STILL the email goes out first (before the meta data is in the DB and can be read. If I "recomplete" the order (putting it back into processing status and then completed again), it will send the meta data (again, this is because it is now in the db)
After much hair pulling, I have come up with a workaround which seems kind of ugly, but hopefully it will help someone else out.
I verified that my hook WAS correctly running before the main email one. (using add_action( 'woocommerce_order_status_completed_notification','mysite_woocommerce_order_status_completed',5,1 );
)
I verified that my meta data WAS correctly inserted into the db BEFORE the email went out
Unfortunately, it still refused to grab my meta data on first send. So I did the following:
I copied the woocommerce/templates/emails/email-order-items.php template into my theme and made the following change:
// Variation
if ( ! empty( $item_meta->meta ) ) {
echo '<br/><small>' . nl2br( $item_meta->display( true, true, '_', "\n" ) ) . '</small>';
// following 5 lines are MY extra code (checking for my meta field 'signup_code')
if (!array_key_exists('signup_code',$item_meta->meta)) {
$suc = wc_get_order_item_meta( $item_id, 'signup_code' );
if ($suc) {
echo '<br/><small>signup_code: ' . $suc . '</small>';
}}
}
It will check for a dupe in the meta array and not output if it already exists. It needs to do this to prevent it showing twice (which it would otherwise do on second send). I can't believe this is all necessary, but I can't find any other pointers anywhere that can address this.
UPDATE:
This was apparently caused by a woo internal caching problem. I had a lengthy discussion with one of the woo devs here:
https://wordpress.org/support/topic/hook-an-action-before-transactional-woocommerce-emails-are-triggeredsent-out/page/2?replies=40#post-8379725
And the upshot is, it will be fixed in a future version, but you can see the changes here:
https://github.com/woothemes/woocommerce/commit/3db3f3796eb28fb79983f605a5a70d61ca114c6d
I'm developing a plugin with Woocommerce and Woosensei
I can't seem to get the process working of creating an order programmatically, mark the order completed and activate the woosensei course for that particular person.
// create a new checkout instance and order id
$checkout = new WC_Checkout();
$this_order_id = $checkout->create_order();
// add some data to the order here //
[ ......... ]
// execute order
$order = new WC_Order($this_order_id);
$order->update_status('completed');
$learning = new WooThemes_Sensei();
$learning->sensei_woocommerce_complete_order($this_order_id);
$cart = new WC_Cart();
$cart->empty_cart();
The order is marked as complete indeed, but the user doesn't have access to the course.
If I manually change the order back to in processing and after that to completed (in the WP admin), then the course is assigned to the user.
This is really annoying, hope somebody has some cues (Woo is poorly documented ....)
Thx in advance!
Hmm, it seems like the sensei_woocommerce_complete_order function checks whether a user is logged in or not.
So I duplicated that function and removed the 'logged in' check, and called that specific function when the user is not logged in.
End to the grey hairs ... ;-)
i was working in a wordpress registration plugin. i stucked in expiry of the user. actually i want to expire the member after one year of his/her registration. and i want to notify them via email before 1 month of their expiry. i am using add_action('init','my function name') to check how many of the user is going to expire after a month and also to send the mail. bt this action hook will run every time a user visits the site which will make my site too slow to load everytime a user will visit. so i want something dat will make this code run once in a day. e.g. when the first user visit the site this code will run and for the whole remaining day this code will not be invoke no matter how many user will visit the website.
Wordpress has a built-in function/API that just do exactly what you want - doing something every day/hour/any interval you specify.
http://codex.wordpress.org/Function_Reference/wp_schedule_event
Taken shamelessly from the above page
add_action( 'wp', 'prefix_setup_schedule' );
/**
* On an early action hook, check if the hook is scheduled - if not, schedule it.
*/
function prefix_setup_schedule() {
if ( ! wp_next_scheduled( 'prefix_daily_event' ) ) {
wp_schedule_event( time(), 'daily', 'prefix_daily_event');
}
}
add_action( 'prefix_daily_event', 'prefix_do_this_daily' );
/**
* On the scheduled action hook, run a function.
*/
function prefix_do_this_daily() {
// check every user and see if their account is expiring, if yes, send your email.
}
prefix_ is presumably to ensure there will be no collision with other plugins, so I suggest you to change this to something unique.
See http://wp.tutsplus.com/articles/insights-into-wp-cron-an-introduction-to-scheduling-tasks-in-wordpress/ if you want to know more.
I am attempting to validate fields on a custom post type in the admin panel Edit Post page.
When the user clicks "Publish" I want to validate fields in the POST data, and change the post_status to "pending" if the data does not pass the tests. When this occurs, I'd also like to add errors to the page in the admin notices area.
I've been trying this with an added hook to the "wp_insert_post" action which also saves our own data. I'm not certain of the order of operations, but I'm assuming that the wp_insert_post events happen first, and then my function gets called via the hook.
The problem is that it's the Wordpress function which is doing the post publish actions, so by the time I get to validate data, Wordpress has already saved the post with a status of "publish". What I need to do is either prevent that update, or change the status back to "pending", but I'm having little success in finding a way to do this within the API.
So, here's an order of operations I'd like to effect:
1. admin user edits post data and clicks "Publish"
2. via wp_insert_post, my data validation and post meta save routine is called
3. If data passes validation, post status is "published"
4. Otherwise, post status set to "pending" & message shown in admin notice area
Surely someone has done this, but extensive Googling just leads me to the same seemingly irrelevant pages. Can someone point me in the right direction here? Thanks in advance-
UPDATE:
So, RichardML was indeed correct, hooking to the wp_insert_post_data filter gave me the right place to validate admin post edit page fields. I'm updating this however to note what the rest of the solution is, specifically getting the reason reported in the admin notice area.
First off, you can't just output data or set a field because the admin page is the result of a redirect, and by the time you get to rendering the admin post page again, the admin_notices action is already gone. The trick was something I picked up from another forum, and it's hackish, but it works.
What you'll need to do is in your validation filter function, if you determine that you will need to display errors, is use set_option() to add a blog option with a unique name (I used 'publish_errors'). This should be HTML code in a div with a class of "error".
You will also need to add an action hook for 'admin_notices', pointing at a function which checks for the existence of the 'publish_errors' option, and if it finds it, prints it to the page and deletes it with delete_option().
You can use the wp_insert_post_data filter to inspect and modify post data before it's inserted into the database.
In response to your update I don't think it's necessary to temporarily add an option to the database. It should be possible to simply add a query string variable to the Wordpress redirect, something like this:
add_filter('wp_insert_post_data', 'my_post_data_validator', '99');
function my_post_data_validator($data) {
if ($data['post_type'] == 'post') {
// If post data is invalid then
$data['post_status'] = 'pending';
add_filter('redirect_post_location', 'my_post_redirect_filter', '99');
}
return $data;
}
function my_post_redirect_filter($location) {
remove_filter('redirect_post_location', __FILTER__, '99');
return add_query_arg('my_message', 1, $location);
}
add_action('admin_notices', 'my_post_admin_notices');
function my_post_admin_notices() {
if (!isset($_GET['my_message'])) return;
switch (absint($_GET['my_message'])) {
case 1:
$message = 'Invalid post data';
break;
default:
$message = 'Unexpected error';
}
echo '<div id="notice" class="error"><p>' . $message . '</p></div>';
}