Send Emails to Wordpress Users on Post Publish and Update - wordpress

I have managed to send users an email when a post is published using transition_post_status, but it does not send when the post is updated. I have tried using 'new' with both old_status and new_status but no luck. Any direction is greatly appreciated. My reference thus far is from https://wordpress.stackexchange.com/questions/100644/how-to-auto-send-email-when-publishing-a-custom-post-type?rq=1
add_action('transition_post_status', 'send_media_emails', 10, 3);
function send_media_emails($new_status, $old_status, $post){
if ( 'publish' !== $new_status or 'publish' === $old_status)
return;
$the_media = get_users( array ( 'role' => 'media' ) );
$emails = array ();
foreach($the_media as $media)
$emails[] = $media->user_email;
$body = sprintf('There are new bus cancellations or delays in Huron-Perth <%s>', get_permalink($post));
wp_mail($emails, 'New Bus Cancellation or Delay', $body);
}
//Working but sending double emails now. I tried wrapping it all in the function but that didn't work either. just confused as to where to put it.
function send_media_emails($post_id){
$the_media = get_users( array ( 'role' => 'media' ) );
$emails = array ();
if( ! ( wp_is_post_revision( $post_id) && wp_is_post_autosave( $post_id ) ) ) {
return;
}
if(get_post_status($post_id) == 'draft' or get_post_status($post_id) == 'pending' or get_post_status($post_id) == 'trash'){
return;
}
foreach($the_media as $media){
$emails = $media->user_email;
}
$body = sprintf('There are new bus cancellations or delays in Huron-Perth <%s>', get_permalink($post_id));
wp_mail($emails, 'New Bus Cancellation or Delay', $body);
}
add_action('post_updated', 'send_media_emails');

The transition_post_status hook only fires when the post status changes, e.g. from 'Draft' to 'Publish'.
A better hook would be post_updated. This fires on all updates, so you'll need to filter out updates to drafts and comments in your script.
You might be able to do a 'publish_to_publish' action, but I haven't tested this personally.

transition_post_status will also fire when a post is updated (publish > publish for example).
However, a known bug makes transition_post_status sometimes fire twice: https://github.com/WordPress/gutenberg/issues/15094
There's a solution for the double firing of transition_post_status, which I copy from there:
function ghi15094_my_updater( $new_status, $old_status, $post ) {
// run your code but do NOT count on $_POST data being available here!
}
function ghi15094_transition_action( $new_status, $old_status, $post ) {
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
ghi15094_my_updater( $new_status, $old_status, $post );
set_transient( 'my_updater_flag', 'done', 10 );
} else {
if ( false === get_transient( 'my_updater_flag' ) ) {
ghi15094_my_updater( $new_status, $old_status, $post );
}
}
}
add_action( 'transition_post_status', 'ghi15094_transition_action', 10, 3 );

Related

Large WooCommerce query throws fatal memory errors

I have a site that has tens of thousands of orders, which I need to compare the billing and customer/user emails and show a flag if they don't match. One of the stipulations is that I'm unable to add any metadata to the orders. So my solution is to just add a custom column, and compare the emails on the fly when the orders list is rendered. That works just fine.
add_filter( 'manage_edit-shop_order_columns', 'mismatched_orders_column' );
function mismatched_orders_column( $columns ) {
$columns['mismatched'] = 'Mismatched';
return $columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'mismatched_orders_column_data' );
function mismatched_orders_column_data( $column ) {
global $post;
if ( 'mismatched' === $column ) {
$order = new WC_Order( $post->ID );
$customer = $order->get_user();
$result = '';
$billing_email = strtolower ( $order->get_billing_email() );
$customer_email = '';
if ($customer) $customer_email = strtolower ( $customer->user_email );
if ( $customer && ( $billing_email != $customer_email ) ) {
$result = '<span class="mismatched-order" title="Possible order mismatch">Yes</span>';
}
echo $result;
}
}
My issue is when trying to add sorting. Because I'm not accessing any post metadata, I don't have any easy data to sort via the main query. My solution here was originally to hook into pre_get_posts, grab all the orders in a new WP_Query, then loop through them and add the ones that had mismatched emails to an array for use in post__in.
This works/worked fine on my small dev site, but throws fatal memory errors when trying to loop over any more than about 8 or 9 thousand posts (out of a total of 30-40 thousand). Increasing memory isn't really an option.
add_filter( 'manage_edit-shop_order_sortable_columns', 'mismatched_orders_column_sortable');
function mismatched_orders_column_sortable( $columns ) {
$columns['mismatched'] = 'mismatched';
return $columns;
}
add_action( 'pre_get_posts', 'mismatched_emails_posts_orderby' );
function mismatched_emails_posts_orderby( $query ) {
if( ! is_admin() || ! $query->is_main_query() ) {
return;
}
//Remove the pre_get_posts hook so we don't get stuck in a loop
add_action( 'pre_get_posts', 'mismatched_emails_posts_orderby' );
//Make sure we're only looking at our custom column
if ( 'mismatched' === $query->get( 'orderby') ) {
//Set our initial array for 'post__in'
$mismatched = array();
$orders_list = get_posts(array(
'post_type' => 'shop_order',
'posts_per_page' => -1,
'post_status' => 'any',
'fields' => 'ids'
));
//And here is our problem
foreach( $orders_list as $order_post ) :
//Get our order and customer/user object
$order_object = new WC_Order( $order_post );
$customer = $order_object->get_user();
//Check that billing and customer emails don't match, and also that we're not dealing with a guest order
if ( ( $order_object->get_billing_email() != $customer->user_email ) && $order_object->get_user() != false ) {
$mismatched[] = $order_post;
}
endforeach; wp_reset_postdata();
$query->set( 'post__in', $mismatched );
}
}
I would seriously appreciate any insight into how I could either reduce the expense of the query I'm trying to run, or an alternate approach. Again, just for clarification, adding metadata to the orders isn't an option.
Thanks!

How to set a WordPress custom post type visibility to private after editing?

I created a custom post type with a plugin. A registered user can insert a new post from front-end and it is saved as draft. When I edit it in back-end I need it is saved with private visibility.
I found this snippet to set visibility by default:
public function force_dpa_request_private( $data , $postarr ) {
if( empty( $data['post_name'] ) && 'my-cpt' == $postarr['post_type'] )
$data[ 'post_status' ] = 'private';
return $data;
}
but it works only on first insert, when I edit it the visibility change to public...
You can hook to the save_post which is called after the post is created or updated.
<?php
add_action( 'save_post', 'callback_save_post', 10, 3);
function callback_save_post( $post_ID, $post, $update ){
if ( 'my-cpt' === get_post_type( $post_ID) && ! wp_is_post_revision( $post_ID ) ) {
// unhook this function so it doesn't loop infinitely
remove_action('save_post', 'callback_save_post', 10 );
// Make the post private if it is edited else make it draft.
if ( $update ) {
$postarr = array(
'ID' => $post_ID,
'post_status' => 'private'
);
} else {
$postarr = array(
'ID' => $post_ID,
'post_status' => 'draft'
);
}
// Update the post.
wp_update_post( $postarr );
// re-hook this function.
add_action( 'save_post', 'callback_save_post', 10, 3);
}
}
Reference:
https://developer.wordpress.org/reference/hooks/save_post/
https://codex.wordpress.org/Function_Reference/wp_update_post
Slight variation from your question, but you can still edit the post on creation, if you make all your post type private. Thus this works.
function force_type_private($post)
{
if ($post['post_type'] == 'Your Post Type')
$post['post_status'] = 'private';
return $post;
}
add_filter('wp_insert_post_data', 'force_type_private');

Get Post ID of post you're currently editing in WordPress

I'm attempting to get the ID of the post I am editing in functions.php for the purpose of dynamically rewriting a custom post type slug.
This is what I'm working with so far.
function change_post_type_slug( $args, $post_type ) {
if ( 'custom_post' == $post_type ) {
global $post;
$location = get_field('custom_field', $post->ID);
$args['rewrite']['slug'] = $location;
}
return $args;
}
add_filter( 'register_post_type_args', 'change_post_type_slug', 10, 2 );
I am not sure if the hook register_post_type_args is firing before I am able to get the ID, or if this is even the best way to go about what I am trying to accomplish. Can't find much out there on the subject.
I was able to get it to work with the following:
function change_post_type_slug( $args, $post_type ) {
if ( 'lead_page' == $post_type ) {
$post_id = $_GET['post'];
$location = get_field('leadpage_location', $post_id);
$args['rewrite']['slug'] = $location->post_name;
}
return $args;
}
add_filter( 'register_post_type_args', 'change_post_type_slug', 10, 2 );
However it resulted in a notice on the front-end:
Notice: Undefined index: post in /path/to/wordpress/functions.php on line 623
Line 623 is $post_id = $_GET['post'];
You should use the updated_postmeta hook for this, as its run every time you update your custom fields.
Then you can update your post data with wp_update_post() function.
add_action( 'updated_postmeta', function( $meta_id, $object_id, $meta_key, $meta_value ) {
if ( 'location' === $meta_key ) {
wp_update_post([
'ID' => $object_id,
'post_name' => $meta_value,
]);
}
}, 10, 4 );
Update:
Try this:
function change_post_types_slug( $args, $post_type ) {
if ( 'your-custom_post' === $post_type ) {
// Check and get the custom post ID
$id = isset($_GET[ 'post' ]) ? $_GET[ 'post' ] : '' ;
// $location = get_field('leadpage_location', $id);
$args['rewrite']['slug'] = 'new-slug-here';
}
return $args;
}
add_filter( 'register_post_type_args', 'change_post_types_slug', 10, 2 );
Try this out:
function change_post_type_slug( $args, $post_type ) {
if ( 'lead_page' === $post_type && is_admin() && $_GET['action'] === 'edit' ) {
$post_id = $_GET['post'];
$location = get_field('leadpage_location', $post_id);
$args['rewrite']['slug'] = $location->post_name;
}
return $args;
}
add_filter( 'register_post_type_args', 'change_post_type_slug', 10, 2 );
It adds two more conditionals, to check if you're on the admin screen and to check of there is a GET parameter of edit. Probably overkill to do is_admin() as well, but now you're super safe.

Can I turn a WordPress Draft into a published post with a direct link if I know the post ID?

For example, using a function like this:
http://website.com/wp-admin/post.php?post=%%post_id%%&action=publish
P.S. I checked and this does not work, but I'm wondering if there is something similar in spirit that works?
You can paste this code in your theme functions.php file. This will do the trick, now if you change action parameter to draft and sent a get request , it will make that post draft.
add_action( 'admin_init', 'draft_post_status_221' );
function draft_post_status_221(){
// Get current page , so this action will only fire in post.php page.
global $pagenow;
if ( $pagenow != 'post.php' ){
return;
}
$post_id = false;
$action = false;
// get post id
if ( isset($_GET['post']) && !empty($_GET['post']) ){
$post_id = $_GET['post'];
}
// get action
if ( isset($_GET['action']) && !empty($_GET['action']) ){
$action = $_GET['action'];
// for security we only allow draft action
if ( $action != 'draft' ){
$action = false;
}
}
// if $post_id and $action has data than post will be updated.
if ( !empty($post_id) && !empty($action) ){
$args = array(
'ID' => $post_id,
'post_status' => $action
);
wp_update_post( $args );
}
}

Wordpress 4.2.1 pre_get_comments this doesn't work after update

I have this code working all time but, after updating to Wordpress 4.2.1, this doesn't work.
// return just the missing replies in the comment table
add_action( 'pre_get_comments', array( $this, 'return_missing_list' ) );
public function return_missing_list( $comments = array() ) {
// bail on anything not admin
if ( ! is_admin() )
return;
// only run this on the comments table
$current_screen = get_current_screen();
if( 'edit-comments' !== $current_screen->base )
return;
// check for query param
if ( ! isset( $_GET['missing_reply'] ) )
return;
// now run action to show missing
$comments->query_vars['meta_key'] = '_cnrt_missing';
$comments->query_vars['meta_value'] = '1';
$comments->query_vars['date_query'] = array(
'after' => '10 months ago'
);
// Because at this point, the meta query has already been parsed,
// we need to re-parse it to incorporate our changes
$comments->meta_query->parse_query_vars( $comments->query_vars );
} // end missing_reply_list

Resources