Source: wpt-scheduled-tweets.php

<?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&amp;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 &ldquo;<a href="%1$s">%2$s</a>&rdquo;', '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,
			);
		}
	}
}