<?php
/**
* Schedule automatically sent Tweets in XPoster Pro
*
* @category Scheduling
* @package XPoster Pro
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.xposterpro.com
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
register_deactivation_hook( __FILE__, 'wpt_deactivate_cron' );
/**
* Clear schedule hook on deactivation.
*/
function wpt_deactivate_cron() {
wp_clear_scheduled_hook( 'wptcron' );
}
add_action( 'wptcron', 'wpt_auto_schedule' );
/**
* On cron schedule, set up a new post to Tweet and insert into scheduler.
*/
function wpt_auto_schedule() {
$notify_user = get_option( 'wpt_autopost_notification' );
$template = ( '' !== get_option( 'wpt_schedule_template', '' ) ) ? get_option( 'wpt_schedule_template' ) : '#title# #url#';
$post_ID = wpt_select_post();
if ( ! $post_ID ) {
if ( $notify_user && is_email( $notify_user ) ) {
wp_mail( $notify_user, __( 'Failed to select any post for Tweeting', 'wp-tweets-pro' ), __( 'XPoster Pro did not find a valid post to repost. This may mean that there are no posts between the selected minimum and maximum ages.', 'wp-tweets-pro' ) );
}
die;
}
// If a custom template is set on this post & the option is enabled, use custom template instead of settings.
$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
$post_info = wpt_post_info( $post_ID );
$template = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== $custom_template ) ? $custom_template : $template;
$sentence = jd_truncate_tweet( $template, $post_info, $post_ID );
$auth = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
update_option( 'wpt_last_autopost_tweet', $sentence );
// For new updates, send current job, then select a new post & continue.
$wpt_cron = get_option( 'wpt_cron_system_updated' );
if ( 'true' !== $wpt_cron ) {
$media = wpt_post_with_media( $post_ID, $post_info );
$tweet = wpt_post_to_service( $sentence, $auth, $post_ID, $media );
update_option( 'wpt_cron_system_updated', 'true' );
$post_ID = wpt_select_post();
$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
$post_info = wpt_post_info( $post_ID );
$template = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== $custom_template ) ? $custom_template : $template;
$sentence = jd_truncate_tweet( $template, $post_info, $post_ID );
$auth = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
do_action( 'wpt_autopost', $tweet, $post_ID );
}
// Get the next schedule for this job, then randomize it over a +/- 5 minute range.
$timestamp = wp_next_scheduled( 'wptcron' );
// `wp_rand()` only returns positive integers, so this needs to stay.
$variable = rand( -300, 300 );
wp_schedule_single_event(
$timestamp + $variable,
'wpt_schedule_tweet_action',
array(
'id' => $auth,
'sentence' => $sentence,
'rt' => 0,
'post_id' => $post_ID,
)
);
// Notify admin about scheduled post.
if ( $notify_user && is_email( $notify_user ) ) {
$post = get_post( $post_ID );
$url = admin_url( 'admin.php?page=wp-to-twitter-schedule' );
// Translators: Tweet title.
wp_mail( $notify_user, sprintf( __( 'Post auto scheduled for Tweeting: %s', 'wp-tweets-pro' ), $post->post_title ), __( 'This Tweet can be viewed, edited, and re-scheduled at XPoster Pro > Scheduled Tweets', 'wp-tweets-pro' ) . "\n\n" . $url . "\n\n" . "“$sentence”" );
}
$id = md5( $timestamp . $auth . 0 . $post_ID . $sentence );
set_transient( 'wpt' . $id, true, $timestamp );
}
/**
* Select a post for auto posting.
*
* @param string $return_type type of data to return.
*
* @return object|array Single post object or array of data about available posts.
*/
function wpt_select_post( $return_type = 'post' ) {
global $wpdb;
$value = false;
$minimum_age = get_option( 'wpt_minimum_age' );
$maximum_age = get_option( 'wpt_maximum_age' );
if ( ! $minimum_age ) {
$minimum_age = 0;
}
if ( ! $maximum_age ) {
$maximum_age = 60 * 60 * 24 * 365 * 5;
}
if ( $maximum_age < $minimum_age ) {
$maximum_age = 60 * 60 * 24 * 365 * 5;
}
$post_types = get_option( 'wpt_autoretweet_post_types' );
$after = apply_filters( 'wpt_custom_cron_maximum', current_time( 'timestamp' ) - $maximum_age ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$before = apply_filters( 'wpt_custom_cron_minimum', current_time( 'timestamp' ) - $minimum_age ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$after = array(
'year' => date( 'Y', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
'month' => date( 'n', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
'day' => date( 'j', $after ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
);
$before = array(
'year' => date( 'Y', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
'month' => date( 'n', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
'day' => date( 'j', $before ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
);
$args = array(
'post_type' => $post_types,
'date_query' => array(
'after' => $after,
'before' => $before,
'inclusive' => true,
),
'fields' => 'ids',
'post_status' => 'publish',
'posts_per_page' => '-1',
);
if ( 'true' === get_option( 'wpt_only_custom' ) ) {
$args['meta_query'] = array(
array(
'key' => '_jd_twitter',
'compare' => 'EXISTS',
),
array(
'key' => '_jd_twitter',
'compare' => '!=',
'value' => '',
),
'relation' => 'AND',
);
}
/**
* Filter the arguments to select posts eligible for Tweets.
*
* @hook wpt_autopost_args
*
* @param {array} $args Original query arguments.
*
* @return {array}
*/
$args = apply_filters( 'wpt_autopost_args', $args );
$posts = new WP_Query( $args );
$posted_args = array(
'post_type' => $post_types,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => '_wpt_autoposted',
'value' => '1',
'compare' => '=',
),
array(
'key' => '_wpt_noautopost',
'value' => '1',
'compare' => '=',
),
'relation' => 'OR',
),
'fields' => 'ids',
'posts_per_page' => '-1',
);
$posted = new WP_Query( $posted_args );
$posts = $posts->posts;
$posted = $posted->posts;
// get all posts from result that aren't already posted.
$diff = array_diff( $posts, $posted );
if ( empty( $diff ) && ! empty( $posted ) ) {
// If the diff leaves no remaining posts, then all eligible posts have already been Tweeted.
$wpdb->query( 'DELETE from ' . $wpdb->prefix . "postmeta WHERE meta_key = '_wpt_autoposted'" );
} else {
$posts = $diff;
}
if ( ! empty( $posts ) ) {
// array_values ensures that the keys are in numeric order after the diff.
$posts = array_values( $posts );
$max = count( $posts );
$rand = wp_rand( 0, $max - 1 );
$post = $posts[ $rand ];
$value = $post;
}
if ( 'post' === $return_type ) {
return $value;
} else {
return array(
'post' => $value,
'posts' => $posts,
'posted' => $posted,
);
}
}
/**
* Set up a schedule to Tweet. Generate first Tweet directly and set up schedule.
*
* @param string $schedule Chosen schedule.
* @param mixed boolean/string $start_time Defined start time for schedules.
*/
function wpt_setup_schedules( $schedule, $start_time = false ) {
update_option( 'wpt_schedule', $schedule );
if ( $start_time ) {
$timestamp = strtotime( get_gmt_from_date( date( 'Y-m-d H:i:s', $start_time ) ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
if ( $timestamp < time() ) {
$timestamp = $timestamp + DAY_IN_SECONDS;
}
} else {
$timestamp = time() + 600; // first auto schedule in 10 minutes.
}
$notify_user = get_option( 'wpt_autopost_notification' );
$template = ( '' !== (string) get_option( 'wpt_schedule_template' ) ) ? get_option( 'wpt_schedule_template' ) : '#title# #url#';
$post_ID = wpt_select_post();
if ( ! $post_ID ) {
if ( $notify_user && is_email( $notify_user ) ) {
wp_mail( $notify_user, __( 'Failed to select any post for Tweeting', 'wp-tweets-pro' ), __( 'XPoster Pro did not find a valid post to repost. This may mean that there are no posts between the selected minimum and maximum ages.', 'wp-tweets-pro' ) );
}
die;
}
/**
* If a custom template is set on this post & the option to use them is enabled, use the custom template instead of the setting.
*/
$custom_template = get_post_meta( $post_ID, '_jd_twitter', true );
$post_info = wpt_post_info( $post_ID );
$template = ( 'true' === get_option( 'wpt_schedule_custom' ) && '' !== (string) $custom_template ) ? $custom_template : $template;
$sentence = jd_truncate_tweet( $template, $post_info, $post_ID );
$auth = apply_filters( 'wpt_auto_repost_authors', false, array( $sentence, $post_ID ) );
wp_schedule_single_event(
$timestamp,
'wpt_schedule_tweet_action',
array(
'id' => $auth,
'sentence' => $sentence,
'rt' => 0,
'post_id' => $post_ID,
)
);
wp_schedule_event( $timestamp, $schedule, 'wptcron' );
$id = md5( $timestamp . $auth . 0 . $post_ID . $sentence );
set_transient( 'wpt' . $id, true, $timestamp );
}
/**
* Add a variety of custom schedules for increased flexibility.
*
* @param array $schedules Array of defined cron scheduls.
*
* @return array
*/
function wpt_custom_schedules( $schedules ) {
$schedules = apply_filters( 'wpt_cron_schedules', $schedules );
$schedules['four-hours'] = array(
'interval' => 14400,
'display' => __( 'Every 4 hours', 'wp-tweets-pro' ),
);
$schedules['eight-hours'] = array(
'interval' => 28800,
'display' => __( 'Every 8 hours', 'wp-tweets-pro' ),
);
$schedules['sixteen-hours'] = array(
'interval' => 57600,
'display' => __( 'Every 16 hours', 'wp-tweets-pro' ),
);
$schedules['fortyeight-hours'] = array(
'interval' => 172800,
'display' => __( 'Every other day', 'wp-tweets-pro' ),
);
$schedules['ninetysix-hours'] = array(
'interval' => 345600,
'display' => __( 'Every four days', 'wp-tweets-pro' ),
);
$schedules['weekly'] = array(
'interval' => 604800,
'display' => __( 'Once Weekly', 'wp-tweets-pro' ),
);
$schedules['monthly'] = array(
'interval' => 2635200,
'display' => __( 'Once Monthly', 'wp-tweets-pro' ),
);
return $schedules;
}
add_filter( 'cron_schedules', 'wpt_custom_schedules' );
add_shortcode( 'wpt_test_autoschedule_arrays', 'wpt_test_shortcode' );
/**
* This exists for testing. You can use this if you want to examine what is currently in the Tweetable arrays
*
* @return string
*/
function wpt_test_shortcode() {
$object = wpt_select_post( 'test' );
$post = $object['post'];
$posted = $object['posted'];
$posts = $object['posts'];
return '<pre>Post: ' . print_r( $post, 1 ) . ' / Posted: ' . print_r( $posted, 1 ) . ' / Posts: ' . print_r( $posts, 1 ) . '</pre>';
}