Create a Cron job after every post publish - wordpress

I have a situation where i want to create a cron job after every WordPress blog post publish. Cron job will run after 7 days of publishing post and check if all the users have viewed the posts and email the list of not viewed post by users list. How can i achieve this. Its a WordPress website and there are around 300+ users, after each blog post publish user gets notification. So want to check who has viewed and who has not viewed the post.

If you want to start the cron job only the first time a post is published, you'll want to use the draft_to_publish hook.
draft_to_publish

Well there are Plugins capable of managing Stats and tracking users activities. I'm not sure which one fit to your needs but I'll try to give a simple approach to do it by yourself.
Tracking
We can set a custom field for those (posts/post-types) where we will store a list of users who read that post. Let's call it readerslist
On the single.php file we will add these lines to Update this field.
$list = explode(',',get_post_meta($post->ID, 'readerslist', true));
$user = get_current_user_id();
if (!in_array($user,$list)){
array_push($list, $user);
update_post_meta( $post->ID, 'readerslist', implode(",", $list) );
}
Now that we can retrieve who viewed the article? We could later find those who didn't read it yet.
Then in functions.php we could set up a hook that will execute when a post get published, to set a single scheduled cron job to send the absent readers.
<?php
function wpdocs_run_on_publish_only( $new_status, $old_status, $post ) {
{
if ($new_status != 'publish') {
return;
}
//You better add limitations to choose specific post_types you are targeting
$postid=$post->ID;
add_cron($postid);
}
add_action( 'transition_post_status', 'wpdocs_run_on_publish_only', 10, 3 );
function add_cron($postid){
$nxtweek = strtotime("+1 week");
add_action('sendemail', 'stats_mail', 10, 1);
wp_schedule_single_event(time() + $nxtweek, 'sendemail',array( $postid);
}
function stats_mail($postid)
{
$readerslist = get_post_meta( $postid, 'readerslist', true );
$users = get_users( array( 'fields' => array( 'ID' ) ) );
$absents = array_diff($users, explode(",",$readerslist));
//Now that you got the list of absent users
$names='';
foreach ($absents as $x){
$names.=$x.' : '.get_user_meta( $x,'first_name' , true ).' '.get_user_meta( $x,'last_name' , true ).'<br>';
}
$title=get_the_title($postid);
$to = 'you#example.com';
$subject = 'Users who didn\'t read '.$title;
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail( $to, $subject, $names, $headers );
}
?>
Notice : I didn't tested the above code ! I made you this in order to show you how to do it. But there isn't much work to do here. Good luck

I tried your code, it works fine, but i am not able to make it working as i wanted, can you look into it. I don't know what i am doing wrong.
function wpdocs_run_on_publish_only( $new_status, $old_status, $post ) {
if ($new_status != 'publish') {
return;
}
$postid=$post->ID;
add_cron($postid);
}
add_action( 'transition_post_status', 'wpdocs_run_on_publish_only', 10, 3 );
function add_cron($postid){
$nxtweek = strtotime("+1 week");
add_action('sendemail', 'stats_mail', 10, 1);
if ( ! wp_next_scheduled( 'sendemail') ) {
wp_schedule_event(time() + $nxtweek, $nxtweek, 'sendemail', array($postid));
// i want to run cron every 7 days from the date of publishing post, until all user reads it, i tried this code but not working.
// wp_mail( 'email#company.com', $postid, 'test', null );
// For testing purpose if i send mail from here, it goes.
}
}
function stats_mail($postid) {
wp_mail( 'email#company.com', $postid, 'test', null );
// this doesnt work.
$readerslist = get_post_meta( $postid, 'readerslist', true );
$users = get_users( array( 'fields' => array( 'ID' ) ) );
$absents = array_diff($users, explode(",",$readerslist));
//Now that you got the list of absent users
$names='';
foreach ($absents as $x){
$names.=$x.' : '.get_user_meta( $x,'first_name' , true ).' '.get_user_meta( $x,'last_name' , true ).'<br>';
}
$title=get_the_title($postid);
$to = 'email#company.com';
$subject = 'Users who didn\'t read '.$title;
$headers = array('Content-Type: text/html; charset=UTF-8');
//wp_mail( $to, $subject, $names, $headers );
}

Related

How to update post author after update post

I'd like to update post_author after update post by getting current user ID, but when I use this function wordpress created new post with correct author by copied current post
function change_author () {
if ( ! wp_is_post_revision( $post_id ) ){
$post= array(
'ID' => $post_id,
'post_author' => get_current_user_id(),
);
wp_update_post( $post );
}
}
add_action('save_post', 'change_author');
I'd use the wp_insert_post_data hook instead of save_post. Rather than updating the post after it's saved, it's probably better to change the data before it gets inserted into the database as part of the save post action. Here's what I'd do:
function change_author ( $data ) {
if ( ! wp_is_post_revision( $data['ID'] ) ){
$data['post_author'] = get_current_user_id();
}
return $data;
}
add_filter( 'wp_insert_post_data', 'change_author', 10, 1 );

Woocommerce ajax call on single product page

I get all my products from an API and those who are variations to each other all share a custom meta key called "api_product_family". The products with the same api_product_family are variants to each other, so on the single page I have a hook where I display the other variants with image and anchor to it's variants.
My code:
function mv_variations() {
global $post;
global $wpdb;
$product_id = $post->ID;
$product_family = get_post_meta( $post->ID, 'api_product_family', true );
if(!empty($product_family)) {
$query = "
SELECT post_id
FROM " . $wpdb->prefix . "postmeta
WHERE meta_value = '" . $product_family . "'
";
$products = $wpdb->get_col($query);
if(count($products) > 0) {
for($i=0; $i<count($products); $i++) {
if($products[$i] == $product_id) {
unset($products[$i]);
}
}
if(count($products) > 0) {
print '<h3>Choose other variants: </h3>';
foreach($products as $product) {
$image = wp_get_attachment_image_src(get_post_thumbnail_id($product));
print '<img src="' . $image[0] . '" alt="img"/> ';
}
}
}
}
}
add_action( 'woocommerce_single_product_summary', 'mv_variations' );
The problem:
I have a LOT of posts, and a lot of post_meta's, so it's taking an eternity to load so I was thinking to move this whole function inside and AJAX call so it's doesn't slow down the initial load. The problem is that I have no idea how to do that with wordpress
Are you simply looking to run a WP function via AJAX?
1) Add ajax actions
This needs to run inside the main plugin file. If you run this only on the public code, it will not work. WP is a little weird and all ajax uses admin-ajax.php
if ( wp_doing_ajax() ){
add_action( 'wp_ajax_yourcustomfunction', array($this, 'yourcustomfunction') );
add_action( 'wp_ajax_nopriv_yourcustomfunction', array($this, 'yourcustomfunction') );
}
function yourcustomfunction(){
echo 'success';
exit();
}
2) In JavaScript
in the backend, you have the global: ajaxurl for the ajax url
BUT in the front end you need to pass this as a variable via wp_localize_script
$datatoBePassed = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
);
wp_localize_script( 'your_javascript_script', 'plugin_display_settings', $datatoBePassed );
In JS:
var datavar = {
action: 'yourcustomfunction',
};
$.post(plugin_display_settings.ajaxurl, datavar, function(response){
//response received here. It will be 'success' which is echoed in PHP
});
3)
If you also want to run a security nonce check (to check the request truly originates from the WP website, prevents some attacks), it gets a little more complicated:
$datatoBePassed should also include 'security' => wp_create_nonce( 'yourplugin_security_nonce' ),
datavar in JS includes security: plugin_display_settings.security,
Finally, your PHP custom function begins with:
// Check security nonce.
if ( ! check_ajax_referer( 'yourplugin_security_nonce', 'security' ) ) {
wp_send_json_error( 'Invalid security token sent.' );
wp_die();
}
// If security check passed, run further
So I think you might get better performance using WP_Query. Below I converted what you have into a custom WP_Query. May need some slight adjustment but should be the right direction.
function mv_variations() {
global $post_id;
// get current post "product family"
$product_family = get_post_meta( $post_id, 'api_product_family', true );
// build related "product family" products query
$products_query_args = array(
'post_type' => 'product', // may need to update this for your case
'posts_per_page' => -1, // return all found
'post__not_in' => array($post_id), // exclude current post
'post_status' => 'publish',
// use a meta query to pull only posts with same "product family" as current post
'meta_query' => array(
array(
'key' => 'api_product_family',
'value' => $product_family,
'compare' => '='
)
)
);
$products_query = new WP_Query($products_query);
// use "the loop" to display your products
if ( $products_query->have_posts() ) :
print '<h3>Choose other variants: </h3>';
while ( $products_query->have_posts() ) : $products_query->the_post();
print ''. wp_get_attachment_image() .'';
endwhile;
// restore global post
wp_reset_postdata();
endif;
}
add_action( 'woocommerce_single_product_summary', 'mv_variations' );

Trying to get contact form 7 post data to debug to screen

I've been trying to get contact form 7 post data to debug the form submission so that I can use it for a plugin I'm trying to work on. However, when I use var_dump or print_r, I can't get the data anywhere.
I've started with this.
add_action( 'wpcf7_before_send_mail', 'my_process_cf7_form_data' );
function my_process_cf7_form_data() {
$submission = WPCF7_Submission::get_instance();
if ( $submission ) {
$posted_data = $submission->get_posted_data();
}
var_dump($posted_data);
}
But I'm not getting any output.
You can't just dump this data to the screen, because it's part of an ajax function. You can however dump it to the error log and tail it in bash, or view the output of the log with FTP.
If you do this instead:
add_action( 'wpcf7_before_send_mail', 'my_process_cf7_form_data' );
function my_process_cf7_form_data() {
$submission = WPCF7_Submission::get_instance();
if ( $submission ) {
$posted_data = $submission->get_posted_data();
}
ob_start();
var_dump($posted_data);
error_log(ob_get_clean());
}
then either view your php_error_log for this domain, or if you have wp-debug enabled and error logging to file (in your wp-config.php).
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
then you can view the debug.log in wp-content folder.
I managed to debug by altering the mail content and sending it to myself, as described here:
How to change data before sending in Contact form 7?
add_action('wpcf7_before_send_mail', 'w2p_on_submit', 10, 3);
function w2p_on_submit( $form, &$abort, $submission )
{
$debug="1";
$mail = $form->prop( 'mail' );
$new_mail = str_replace( '[your-name]', $debug, $mail );
$form->set_properties( array( 'mail' => $new_mail ) );
return $form;
}
(I couldn't get the log to work. )

How to custom user url in BuddyPress and WordPress?

Thanks for support!
I need to custom user url in my page using WordPress and BuddyPress.
This is example:
From: (current)
http://example.com/user/pum_su411
To
http://example.com/user/548234
With 548234 is ID of the user.
I want after completed the custom, all users will have url like above automatically.
Thanks for all solutions!
add this code to your theme functions.php file.
function _bp_core_get_user_domain($domain, $user_id, $user_nicename = false, $user_login = false) {
if ( empty( $user_id ) ){
return;
}
if( isset($user_nicename) ){
$user_nicename = bp_core_get_username($user_id);
}
$after_domain = bp_get_members_root_slug() . '/' . $user_id;
$domain = trailingslashit( bp_get_root_domain() . '/' . $after_domain );
$domain = apply_filters( 'bp_core_get_user_domain_pre_cache', $domain, $user_id, $user_nicename, $user_login );
if ( !empty( $domain ) ) {
wp_cache_set( 'bp_user_domain_' . $user_id, $domain, 'bp' );
}
return $domain;
}
add_filter('bp_core_get_user_domain', '_bp_core_get_user_domain', 10, 4);
function _bp_core_get_userid($userid, $username){
if(is_numeric($username)){
$aux = get_userdata( $username );
if( get_userdata( $username ) )
$userid = $username;
}
return $userid;
}
add_filter('bp_core_get_userid', '_bp_core_get_userid', 10, 2);
function _bp_get_activity_parent_content($content){
global $bp;
$user = get_user_by('slug', $bp->displayed_user->fullname); // 'slug' - user_nicename
return preg_replace('/href=\"(.*?)\"/is', 'href="'.bp_core_get_user_domain($user->ID, $bp->displayed_user->fullname).'"', $content);
}
add_filter( 'bp_get_activity_parent_content','_bp_get_activity_parent_content', 10, 1 );
function _bp_get_activity_action_pre_meta($content){
global $bp;
$fullname = $bp->displayed_user->fullname; // 'slug' - user_nicename
$user = get_user_by('slug', $fullname);
if(!is_numeric($user->ID) || empty($fullname)){
$args = explode(' ', trim(strip_tags($content)));
$fullname = trim($args[0]);
$user = get_user_by('slug', $fullname);
}
return preg_replace('/href=\"(.*?)\"/is', 'href="'.bp_core_get_user_domain($user->ID, $fullname).'"', $content);
}
add_action('bp_get_activity_action_pre_meta', '_bp_get_activity_action_pre_meta');
add_filter('bp_core_get_userid_from_nicename', '_bp_core_get_userid', 10, 2);
Just spent a bit of time going over the documentation, codex and files of BuddyPress and i can find ways of changing the /user/ part of the url but sadly not the /username side of it.
Reading through, it is controlled within the core of BuddyPress and any changes to the core can cause crashes and more than likely to cause problems or overwrites further down the line.
This isn't to say it's not possible though, it is most certainly possible, but it will require a great deal of editing to many many different files, an edit to a number of BuddyPress functions and there are no guarantee's on it working straight out or even working further down the line when files get update.
I would recommend going onto the BuddyPress Trac and putting in a ticket to have the feature added to change user url structure. It would be a cool feature to be able to swap between a username, full name, ID or any other unique identifiable string.
You can access it here: https://buddypress.trac.wordpress.org/
Alternatively, you can try what aSeptik has done above, but make sure to update that file with any changes when BuddyPress updates as well.

wordpress set post_status as "draft" in 'save_post' action

I have a custom function that works with my custom post type. While porocessing save_post action:
add_action( 'save_post', 'my_custom_function' );
I would like to set post status as draft (in case of a problem with getting custom data from outside api).
In my my_custom_function function I have this little block:
if ($error == true) {
$override_post = array();
$override_post['ID'] = $post_id;
$override_post['post_status'] = 'draft';
wp_update_post( $override_post );
}
but it seems, that after save_post is being processed, then post_status is being set again.
Anybody have an idea, where should I hook into, so while saving post data I can modify its post_status, post_date and some other post data informations so they are not being overriten?
You should hook it to wp_insert_post_data. Then you could use a function like this to set your post status to draft:
add_filter( 'wp_insert_post_data', 'set_post_to_draft', 99, 2 );
function set_post_to_draft( $data, $postarr ) {
if ( your_condition ) {
$data['post_status'] = 'draft';
}
return $data;
}
I had to make a post type with only one post_status option, and it seem to fit your needs too, as it is works exactly with the save_post hook.
add_action( 'save_post', 'my_function' );
function my_function( $post_id ){
if ( ! wp_is_post_revision( $post_id ) ){
// avoid endless circle
remove_action('save_post', 'my_function');
// update the data before saving
wp_update_post( wp_slash([
'ID' => $_POST['ID'],
'post_status' => 'draft'
]));
// restore the saving hook
add_action('save_post', 'my_function');
}
}
The original solution found here:
https://wp-kama.ru/function/wp_update_post

Resources