<?php
/**
* View & modify scheduled Updates
*
* @category Scheduling
* @package XPoster Pro
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.xposterpro.com
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Get and display list of scheduled updates
*/
function wpt_get_scheduled_tweets() {
$schedule = wpt_schedule_custom_tweet( $_POST );
$deletions = ( isset( $_POST['delete-tweets'] ) && isset( $_POST['delete-list'] ) ) ? $_POST['delete-list'] : array();
$cron = _get_cron_array();
$schedules = wp_get_schedules();
$post_id = false;
$date_format = _x( 'M j, Y @ G:i', 'Publish box date format', 'wp-tweets-pro' );
$clear_queue = wp_nonce_url( admin_url( 'admin.php?page=wp-to-twitter-schedule&wpt=clear' ) );
$cur_sched = '';
if ( isset( $schedule['message'] ) ) {
echo $schedule['message'];
}
$editing_tweet = array();
?>
<div class="wrap" id="wp-to-twitter" >
<h1><?php _e( 'Scheduled Updates from XPoster Pro', 'wp-tweets-pro' ); ?></h1>
<div class="postbox-container jcd-wide">
<div class="metabox-holder">
<div class="ui-sortable meta-box-sortables">
<div class="postbox">
<h3><?php _e( 'Your Scheduled Status Updates', 'wp-tweets-pro' ); ?></h3>
<div class="inside">
<form method="post" action="<?php echo admin_url( 'admin.php?page=wp-to-twitter-schedule&action=delete' ); ?>">
<table class="widefat fixed">
<thead>
<tr>
<th scope="col"><?php _e( 'Scheduled', 'wp-tweets-pro' ); ?></th>
<th scope="col" style="width:60%;"><?php _e( 'Status', 'wp-tweets-pro' ); ?></th>
<th scope="col"><?php _e( 'Account', 'wp-tweets-pro' ); ?></th>
<th scope="col"><?php _e( 'Delete', 'wp-tweets-pro' ); ?></th>
</tr>
</thead>
<tbody>
<?php
$offset = ( 60 * 60 * get_option( 'gmt_offset' ) );
$class = array();
foreach ( $cron as $ts => $cronhooks ) {
foreach ( (array) $cronhooks as $hook => $events ) {
$i = 0;
foreach ( (array) $events as $event ) {
$class = array();
if ( 'wpt_schedule_tweet_action' === $hook || 'wpt_recurring_tweets' === $hook ) {
++$i;
$cur_sched = '';
if ( 'wpt_recurring_tweets' === $hook ) {
$class[] = 'is_recurring';
$cur_sched = ', ' . $event['schedule'];
}
if ( count( $event['args'] ) ) {
$auth = $event['args']['id'];
$sentence = $event['args']['sentence'];
$rt = $event['args']['rt'];
$post_ID = $event['args']['post_id'];
}
$id = md5( $ts . $auth . $rt . $post_ID . $sentence );
$cron_transient = get_transient( 'wpt' . $id );
if ( $cron_transient ) {
$class[] = 'autoposter';
}
if ( isset( $_GET['tweet_id'] ) && $_GET['tweet_id'] === $id ) {
$editing_tweet = array(
'timestamp' => $ts,
'auth' => $auth,
'rt' => $rt,
'sentence' => $sentence,
'post_id' => $post_ID,
'frequency' => $event['schedule'],
);
$class[] = 'is_editing';
} else {
if ( isset( $class['is_editing'] ) ) {
unset( $class['is_editing'] );
}
}
if ( ( isset( $_GET['wpt'] ) && 'clear' === $_GET['wpt'] ) && ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'] ) ) ) {
wp_unschedule_event(
$ts,
$hook,
array(
'id' => $auth,
'sentence' => $sentence,
'rt' => $rt,
'post_id' => $post_ID,
)
);
// Translators: date and time status update was scheduled for.
echo "<div id='message' class='updated'><p>" . sprintf( __( 'Status update for %1$s has been deleted.', 'wp-tweets-pro' ), date( $date_format, ( $ts + $offset ) ) ) . '</p></div>'; // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
} elseif ( in_array( $id, $deletions, true ) ) {
wp_unschedule_event(
$ts,
$hook,
array(
'id' => $auth,
'sentence' => $sentence,
'rt' => $rt,
'post_id' => $post_ID,
)
);
// Editing works by deleting the event and scheduling a new one. Don't tell users a message is deleted in this case.
if ( ! isset( $_POST['original_tweet'] ) ) {
echo "<div id='message' class='notice notice-success'><p>" . __( 'Scheduled update has been deleted.', 'wp-tweets-pro' ) . '</p></div>';
} else {
echo "<div id='original' class='notice notice-success'><p><strong>" . __( 'Original status:', 'wp-tweets-pro' ) . '</strong>: ' . esc_html( $_POST['original_tweet'] ) . '</p></div>';
}
} else {
$time_diff = human_time_diff( $ts + $offset, time() + $offset );
$image = '';
if ( 1 === (int) get_option( 'wpt_media' ) ) {
if ( 1 !== (int) get_post_meta( $post_ID, '_wpt_image', true ) ) {
$tweet_this_image = wpt_filter_scheduled_media( true, $post_ID, $rt );
if ( $tweet_this_image ) {
$img = wpt_post_attachment( $post_ID );
if ( $img ) {
/**
* Filter the image size sent to Twitter. Default `large`.
*
* @hook wpt_upload_image_size
* @param {string|int[]} $size Registered size name or array of width and height.
*
* @return {string|int[]}
*/
$size = apply_filters( 'wpt_upload_image_size', 'large' );
$img_url = wp_get_attachment_image_src( $img, $size );
$image = "<a href='$img_url[0]' class='wpt_image'>" . __( 'Includes Image', 'wp-tweets-pro' ) . '</a>';
}
}
}
}
$accounts = array();
if ( ! $auth || 'main' === $auth ) {
if ( wpt_check_oauth() ) {
$accounts[] = array(
'text' => '@' . get_option( 'wtt_twitter_username' ),
'link' => 'https://twitter.com/' . get_option( 'wtt_twitter_username' ),
);
}
if ( function_exists( 'wpt_mastodon_connection' ) && wpt_mastodon_connection() ) {
$accounts[] = array(
'text' => '@' . get_option( 'wpt_mastodon_username' ),
'link' => get_option( 'wpt_mastodon_instance' ) . '/@' . get_option( 'wpt_mastodon_username' ),
);
}
} else {
if ( wpt_check_oauth( $auth ) ) {
$accounts[] = array(
'text' => '@' . get_user_meta( $auth, 'wtt_twitter_username', true ),
'link' => 'https://twitter.com/' . get_user_meta( $auth, 'wtt_twitter_username', true ),
);
}
if ( function_exists( 'wpt_mastodon_connection' ) && wpt_mastodon_connection( $auth ) ) {
$accounts[] = array(
'text' => '@' . get_user_meta( $auth, 'wpt_mastodon_username', true ),
'link' => get_user_meta( $auth, 'wpt_mastodon_instance', true ) . '/@' . get_option( $auth, 'wpt_mastodon_username', true ),
);
}
}
$display_accounts = array();
if ( ! empty( $accounts ) ) {
foreach ( $accounts as $account ) {
$display_accounts[] = '<a href="' . esc_url( $account['link'] ) . '">' . $account['text'] . '</a>';
}
}
$display_accounts = implode( ', ', $display_accounts );
?>
<tr class='<?php echo implode( ' ', array_map( 'sanitize_html_class', $class ) ); ?>'>
<th scope="row"><?php echo date_i18n( $date_format, ( $ts + $offset ) ); ?><br /><small>(~<?php echo $time_diff . $cur_sched; ?>)</small></th>
<td id='sentence-<?php echo esc_attr( $id ); ?>'><strong><?php echo wp_kses_post( "$sentence $image" ); ?></strong>
<?php
if ( 'is_recurring' !== $class ) {
?>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wp-to-twitter-schedule&tweet_id=' . $id ) ); ?>"><span class='dashicons dashicons-edit' aria-hidden='true'></span>Edit</a>
<?php
}
if ( in_array( 'autoposter', $class, true ) ) {
echo ' <em>(' . __( 'Generated by autoposter', 'wp-tweets-pro' ) . ')</em>';
}
if ( wpt_has_tags( $sentence ) ) {
$post_info = wpt_post_info( $post_ID );
echo '<p class="rendered-tweet"><strong>' . __( 'Expected:', 'wp-tweets-pro' ) . '</strong> ' . jd_truncate_tweet( $sentence, $post_info, $post_ID, $rt, $auth ) . '</p>';
}
?>
</td>
<td><?php echo $display_accounts; ?></td>
<td><input type='checkbox' id='checkbox-<?php echo esc_attr( $id ); ?>' value='<?php echo esc_attr( $id ); ?>' name='delete-list[]' aria-describedby='sentence-<?php echo esc_attr( $id ); ?>' /> <label for='checkbox-<?php echo esc_attr( $id ); ?>'><?php _e( 'Delete', 'wp-tweets-pro' ); ?></label></td>
</tr>
<?php
}
}
}
}
}
?>
</tbody>
</table>
<p><input type='submit' class='button-primary' name='delete-tweets' value='<?php _e( 'Delete checked updates', 'wp-tweets-pro' ); ?>' /></p>
</form>
<p><a href="<?php echo $clear_queue; ?>"><?php _e( 'Clear Queue', 'wp-tweets-pro' ); ?></a></p>
</div>
</div>
</div>
<div class="ui-sortable meta-box-sortables">
<div class="postbox">
<h3><?php _e( 'Schedule status updates', 'wp-tweets-pro' ); ?></h3>
<div class="inside schedule" id="wp2t">
<form method="post" action="<?php echo admin_url( 'admin.php?page=wp-to-twitter-schedule' ); ?>">
<div>
<input type="hidden" name="submit-type" value="schedule-tweet" />
<input type="hidden" name='author' id='author' value='<?php echo get_current_user_id(); ?>' />
</div>
<?php
$nonce = wp_nonce_field( 'wp-to-twitter-nonce', '_wpnonce', true, false );
echo "<div>$nonce</div>";
$tweet = ( isset( $schedule['tweet'] ) ) ? stripslashes( $schedule['tweet'] ) : '';
$tweet = ( isset( $_GET['tweet'] ) ) ? stripslashes( urldecode( $_GET['tweet'] ) ) : $tweet;
$date = '';
$time = '';
$freq = '';
if ( ! empty( $editing_tweet ) ) {
$tweet = $editing_tweet['sentence'];
$date = gmdate( 'Y-m-d', $editing_tweet['timestamp'] + $offset );
$time = gmdate( 'H:i', $editing_tweet['timestamp'] + $offset );
$post_id = $editing_tweet['post_id'];
$auth = $editing_tweet['auth'];
$freq = $editing_tweet['frequency'];
?>
<input type="hidden" name="delete-tweets" value="true" />
<input type="hidden" name="delete-list[]" value="<?php echo esc_attr( $_GET['tweet_id'] ); ?>" />
<?php
}
if ( isset( $_GET['post'] ) || false !== $post_id ) {
$post_id = ( isset( $_GET['post'] ) ) ? intval( $_GET['post'] ) : $post_id;
$post_title = get_the_title( $post_id );
$edit_link = get_edit_post_link( $post_id );
?>
<div class="notice notice-info inline">
<input type='hidden' name='post_id' value='<?php echo $post_id; ?>' />
<p>
<?php
// Translators: Edit link for post, title of post.
printf( __( 'Scheduling status update for “<a href="%1$s">%2$s</a>”', 'wp-tweets-pro' ), $edit_link, $post_title );
?>
</p>
</div>
<?php
} else {
$post_types = wpt_allowed_post_types();
$last = wp_get_recent_posts(
array(
'numberposts' => 1,
'post_type' => $post_types,
'post_status' => 'publish',
)
);
$last_id = ( ! empty( $last ) ) ? $last['0']['ID'] : '';
$post_title = ( isset( $schedule['post'] ) ) ? get_the_title( $schedule['post'] ) : get_the_title( $last_id );
?>
<p>
<label for='post'><span class="dashicons dashicons-search" aria-hidden="true"></span> <?php _e( 'Select related post', 'wp-tweets-pro' ); ?></label>
<input type="hidden" name="post_id" value="<?php echo intval( ( isset( $schedule['post'] ) ) ? $schedule['post'] : $last_id ); ?>" />
<input type="text" name="post" class="suggest widefat" id="post" value="<?php echo esc_attr( $post_title ); ?>" />
</p>
<?php
}
?>
<p class="jtw">
<label for='wpt_custom_tweet'><?php _e( 'Status Update', 'wp-tweets-pro' ); ?></label>
<textarea id='wpt_custom_tweet' name='tweet' rows='3' cols='70'><?php echo strip_tags( $tweet ); ?></textarea>
<input type="checkbox" value='on' id='filter' name='filter' checked='checked' /><label for='filter'><?php _e( 'Run template filters on this update', 'wp-tweets-pro' ); ?></label>
<input type="hidden" name="original_tweet" value="<?php echo esc_attr( $tweet ); ?>" />
</p>
<div class="wpt-datetime">
<div class='autoschedule'>
<p>
<input type='checkbox' name='autoschedule' id='wpt_autoschedule' size="20" value='true' /> <label for='wpt_autoschedule'><?php _e( 'Auto-schedule', 'wp-tweets-pro' ); ?></label>
</p>
</div>
<div class='date'>
<label for='wpt_date'><?php _e( 'Date', 'wp-tweets-pro' ); ?></label><br />
<?php
$date = ( ! $date ) ? date_i18n( 'Y-m-d', ( current_time( 'timestamp' ) + 300 ) ) : $date; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
?>
<input type='date' class="wpt_date" name='date' id='wpt_date' value='<?php echo $date; ?>' />
</div>
<div class='time'>
<label for='wpt_time'><?php _e( 'Time', 'wp-tweets-pro' ); ?></label><br />
<?php
$time = ( ! $time ) ? date_i18n( 'H:i', ( current_time( 'timestamp' ) + 300 ) ) : $time; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
?>
<input type='time' class="wpt_time" name='time' id='wpt_time' value='<?php echo $time; ?>' />
</div>
<div class='recurrence'>
<label for='wpt_recurrence'><?php _e( 'Frequency', 'wp-tweets-pro' ); ?></label><br />
<select name='wpt_recurrence' id='wpt_recurrence'>
<option value=''><?php _e( 'Once', 'wp-tweets-pro' ); ?></option>
<?php
$schedules = wp_get_schedules();
foreach ( $schedules as $key => $schedule ) {
if ( 'four-hours' !== $key && 'eight-hours' !== $key && 'sixteen-hours' !== $key ) {
echo "<option value='$key'" . selected( $freq, $key ) . ">$schedule[display]</option>";
}
}
?>
</select>
</div>
</div>
<?php
if ( '1' === get_option( 'jd_individual_twitter_users' ) ) {
print(
'<p><label for="alt_author">' . __( 'Post to author', 'wp-tweets-pro' ) . '</label>
<select name="alt_author" id="alt_author">
<option value="main">' . __( 'Main site account', 'wp-tweets-pro' ) . '</option>
<option value="false">' . __( 'Current User\'s account', 'wp-tweets-pro' ) . '</option>'
);
$user_query = get_users(
array(
'role' => 'subscriber',
)
);
// This gets the array of ids of the subscribers.
$subscribers = wp_list_pluck( $user_query, 'ID' );
// Now use the exclude parameter to exclude the subscribers.
$users = get_users(
array(
'exclude' => $subscribers,
)
);
if ( count( $users ) < 1000 ) {
$selected_user = ( isset( $schedule['id'] ) ) ? $schedule['id'] : wp_get_current_user()->ID;
foreach ( $users as $this_user ) {
if ( '' !== get_user_meta( $this_user->ID, 'wtt_twitter_username', true ) ) {
$selected = selected( $this_user->ID, $selected_user, false );
print( '<option value="' . $this_user->ID . '" ' . $selected . '>' . $this_user->display_name . '</option>' );
}
}
}
print( '</select></p>' );
}
if ( isset( $_GET['tweet_id'] ) ) {
$submit_label = __( 'Update status', 'wp-tweets-pro' );
} else {
$submit_label = __( 'Schedule update', 'wp-tweets-pro' );
}
?>
<p><input type="submit" name="submit" value="<?php echo esc_attr( $submit_label ); ?>" class="button-primary" /></p>
</form>
</div>
</div>
</div>
</div>
</div>
<?php
if ( function_exists( 'wpt_sidebar' ) ) {
wpt_sidebar();
} else {
_e( 'Please Activate XPoster!', 'wp-tweets-pro' );
}
?>
</div>
<?php
}
/**
* When autoscheduling a single update, check time and shift if in blackout period.
*
* @param string $time Timestamp.
* @param string $from Time that blackout starts.
* @param string $to Time that blackout ends.
*
* @return timestamp.
*/
function wpt_test_time( $time, $from, $to ) {
$hour = date( 'G', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
if ( $hour > $from || $hour < $to ) {
$time = wpt_increment_time( $time, $from, $to );
}
return $time;
}
/**
* Shift a given time to a new value outside of blackout period.
*
* @param string $time Timestamp.
* @param string $from Time that blackout starts.
* @param string $to Time that blackout ends.
*
* @return timestamp.
*/
function wpt_increment_time( $time, $from, $to ) {
$hour = date( 'G', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
while ( $hour > $from || $hour < $to ) {
$time += 15 * 60 + wp_rand( 0, 6000 );
$hour = date( 'G', $time ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
return $time;
}
/**
* Schedule a custom update
*
* @param array $post Post data.
*
* @return array Message & scheduled post info.
*/
function wpt_schedule_custom_tweet( $post ) {
$offset = ( 60 * 60 * get_option( 'gmt_offset' ) );
if ( isset( $post['submit-type'] ) && 'schedule-tweet' === $post['submit-type'] ) {
if ( isset( $post['alt_author'] ) ) {
$auth = ( isset( $post['author'] ) && '' !== $post['author'] ) ? (int) $post['author'] : false;
$auth = ( isset( $post['alt_author'] ) && 'false' === $post['alt_author'] ) ? $auth : (int) $post['alt_author'];
$auth = ( isset( $post['alt_author'] ) && 'main' === $post['alt_author'] ) ? false : $auth;
if ( $auth && '' === get_user_meta( $auth, 'wtt_twitter_username', true ) ) {
$auth = false;
}
} else {
$auth = false;
}
$encoding = get_option( 'blog_charset' );
if ( '' === $encoding ) {
$encoding = 'UTF-8';
}
$sentence = ( isset( $post['tweet'] ) ) ? html_entity_decode( stripcslashes( $post['tweet'] ), ENT_COMPAT, $encoding ) : '';
$orig_sentence = $sentence;
$post_id = ( isset( $post['post_id'] ) && '' !== $post['post_id'] ) ? (int) $post['post_id'] : false;
if ( isset( $post['autoschedule'] ) && 'true' === $post['autoschedule'] ) {
$max = DAY_IN_SECONDS;
$time = wp_rand( 300, $max );
$blackout = get_option( 'wpt_blackout' );
$from = $blackout['from'];
$to = $blackout['to'];
if ( $from === $to ) {
$time = current_time( 'timestamp' ) + wp_rand( 300, $max ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
} else {
$time = current_time( 'timestamp' ) + wp_rand( 300, $max ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$time = wpt_test_time( $time, $from, $to );
}
} else {
$time = ( isset( $post['time'] ) && isset( $post['date'] ) ) ? ( strtotime( $post['date'] . ' ' . $post['time'] ) ) : '';
$time = ( $time > current_time( 'timestamp' ) ) ? $time : false; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$time = ( $time ) ? $time - $offset : $time;
}
if ( ! $sentence || ! get_post_status( $post_id ) ) {
return array(
'message' => "<div class='error'><p>" . __( 'You must include text to post and a valid post ID to associate the tweet with.', 'wp-tweets-pro' ) . '</p></div>',
'tweet' => $sentence,
'post' => $post_id,
'id' => $auth,
);
} elseif ( ! $time ) {
return array(
'message' => "<div class='error'><p>" . __( 'The time provided was either invalid or in the past.', 'wp-tweets-pro' ) . '</p></div>',
'tweet' => $sentence,
'post' => $post_id,
'id' => $auth,
);
} else {
if ( isset( $post['filter'] ) && 'on' === $post['filter'] ) {
$post_info = wpt_post_info( $post_id );
$sentence = jd_truncate_tweet( $sentence, $post_info, $post_id, false, false );
}
if ( ! isset( $_POST['wpt_recurrence'] ) || '' === $_POST['wpt_recurrence'] ) {
wp_schedule_single_event(
$time,
'wpt_schedule_tweet_action',
array(
'id' => $auth,
'sentence' => $sentence,
'rt' => 0,
'post_id' => $post_id,
)
);
} else {
$recurrence = sanitize_text_field( $_POST['wpt_recurrence'] );
wp_schedule_event(
$time,
$recurrence,
'wpt_recurring_tweets',
array(
'id' => $auth,
'sentence' => $orig_sentence,
'rt' => 0,
'post_id' => $post_id,
)
);
}
return array(
'message' => "<div class='updated'><p>" . __( 'Your custom status update has been scheduled.', 'wp-tweets-pro' ) . '</p></div>',
'tweet' => $sentence,
'post' => $post_id,
'id' => $auth,
);
}
}
}