Source: includes/metabox.php

<?php
/**
 * Metabox rendering functions XPoster
 *
 * @category Metabox
 * @package  XPoster
 * @author   Joe Dolson
 * @license  GPLv2 or later
 * @link     https://www.joedolson.com/wp-to-twitter/
 */

/**
 * Set up post meta box.
 */
function wpt_add_twitter_outer_box() {
	// add X.com panel to post types where it's enabled.
	$wpt_post_types = get_option( 'wpt_post_types' );
	if ( is_array( $wpt_post_types ) ) {
		foreach ( $wpt_post_types as $key => $value ) {
			if ( '1' === (string) $value['post-published-update'] || '1' === (string) $value['post-edited-update'] ) {
				if ( current_user_can( 'wpt_can_tweet' ) ) {
					add_meta_box( 'wp2t', 'XPoster', 'wpt_add_twitter_inner_box', $key, 'side' );
				}
			}
		}
	}
}
add_action( 'admin_menu', 'wpt_add_twitter_outer_box' );

/**
 * Print post meta box
 *
 * @param  object $post Post object.
 */
function wpt_add_twitter_inner_box( $post ) {
	$nonce = wp_create_nonce( 'wp-to-twitter-nonce' );
	?>
	<div>
		<input type="hidden" name="wp_to_twitter_nonce" value="<?php echo esc_attr( $nonce ); ?>">
		<input type="hidden" name="wp_to_twitter_meta" value="true">
	</div>
	<?php
	if ( current_user_can( 'wpt_can_tweet' ) ) {
		$is_pro = ( function_exists( 'wpt_pro_exists' ) ) ? 'pro' : 'free';
		?>
		<div class='wp-to-twitter <?php echo esc_attr( $is_pro ); ?>'>
		<?php
		$options = get_option( 'wpt_post_types' );
		$status  = $post->post_status;
		wpt_show_metabox_message( $post, $options );
		// Show switch to flip update status.
		wpt_show_post_switch( $post, $options );
		echo '<div class="wpt-options-metabox">';
		$user_tweet = apply_filters( 'wpt_user_text', '', $status );
		// Formulate Template display.
		$template = wpt_display_status_template( $post, $options );
		if ( $user_tweet ) {
			// If a user template is defined, replace the existing template.
			$template = $user_tweet;
		}
		if ( 'publish' === $status && ( current_user_can( 'wpt_tweet_now' ) || current_user_can( 'manage_options' ) ) ) {
			// Show metabox status buttons.
			wpt_display_metabox_status_buttons( $is_pro );
		}
		if ( current_user_can( 'wpt_twitter_custom' ) || current_user_can( 'manage_options' ) ) {
			$custom_update = get_post_meta( $post->ID, '_jd_twitter', true );
			?>
			<p class='jtw'>
				<label for="wpt_custom_tweet"><?php esc_html_e( 'Custom Status Update', 'wp-to-twitter' ); ?></label><br/>
				<textarea class="wpt_tweet_box widefat" name="_jd_twitter" id="wpt_custom_tweet" placeholder="<?php echo esc_attr( $template ); ?>" rows="2" cols="60"><?php echo esc_textarea( stripslashes( $custom_update ) ); ?></textarea>
			</p>
			<div role="alert" class="x-notification notice inline notice-info hidden"><p><?php esc_html_e( 'X length limit reached:', 'wp-to-twitter' ); ?> <span></span></p></div>
			<div role="alert" class="bluesky-notification notice inline notice-info hidden"><p><?php esc_html_e( 'Bluesky length limit reached:', 'wp-to-twitter' ); ?> <span></span></p></div>
			<div role="alert" class="mastodon-notification notice inline notice-info hidden"><p><?php esc_html_e( 'Mastodon length limit reached:', 'wp-to-twitter' ); ?> <span></span></p></div>
			<div class="wpt-template-resources wpt-flex">
				<p class='wpt-template'>
					<?php esc_html_e( 'Default template:', 'wp-to-twitter' ); ?><br /><code><?php echo esc_html( stripcslashes( $template ) ); ?></code>
				</p>
				<div class='wptab' id='notes'>
					<h3><?php esc_html_e( 'Template Tags', 'wp-to-twitter' ); ?></h3>
					<ul class="inline-list">
					<?php
					$tags = wpt_tags();
					foreach ( $tags as $tag ) {
						$pressed = ( false === stripos( $template, '#' . $tag . '#' ) ) ? 'false' : 'true';
						echo '<li><button type="button" class="button-secondary" aria-pressed="' . esc_attr( $pressed ) . '">#' . esc_html( $tag ) . '#</button></li>';
					}
					do_action( 'wpt_notes_tab', $post->ID );
					?>
					</ul>
				</div>
			</div>
			<?php
			/**
			 * Generate fields after the custom template box in the meta box.
			 *
			 * @hook wpt_after_meta_template_box
			 *
			 * @param {int} $post_ID Post ID.
			 */
			do_action( 'wpt_after_meta_template_box', $post->ID );
			if ( get_option( 'jd_keyword_format' ) === '2' ) {
				$custom_keyword = get_post_meta( $post->ID, '_yourls_keyword', true );
				?>
				<label for='yourls_keyword'><?php esc_html_e( 'YOURLS Custom Keyword', 'wp-to-twitter' ); ?></label> <input type='text' name='_yourls_keyword' id='yourls_keyword' value='<?php echo esc_attr( $custom_keyword ); ?>' />
				<?php
			}
		} else {
			?>
			<input type="hidden" name='_jd_twitter' value='<?php echo esc_attr( $template ); ?>' />
			<p class='wpt-template'>
				<?php esc_html_e( 'Template:', 'wp-to-twitter' ); ?> <code><?php echo esc_html( stripcslashes( $template ) ); ?></code>
			</p>
			<?php
		}
		?>
		<div class='wpt-options'>
			<div class='wptab' id='custom'>
			<?php
			// XPoster Pro.
			if ( 'pro' === $is_pro && ( current_user_can( 'wpt_twitter_custom' ) || current_user_can( 'manage_options' ) ) ) {
				wpt_schedule_values( $post->ID );
				do_action( 'wpt_custom_tab', $post->ID, 'visible' );
				if ( current_user_can( 'edit_others_posts' ) ) {
					if ( '1' === get_option( 'jd_individual_twitter_users' ) ) {
						$selected = ( get_post_meta( $post->ID, '_wpt_authorized_users', true ) ) ? get_post_meta( $post->ID, '_wpt_authorized_users', true ) : array();
						if ( function_exists( 'wpt_authorized_users' ) ) {
							wpt_authorized_users( $selected );
							do_action( 'wpt_authors_tab', $post->ID, $selected );
						}
					}
				}
			}
			if ( ! current_user_can( 'wpt_twitter_custom' ) && ! current_user_can( 'manage_options' ) ) {
				?>
				<p><?php esc_html_e( 'Customizing XPoster options is not allowed for your user role.', 'wp-to-twitter' ); ?></p>
				<?php
				if ( 'pro' === $is_pro ) {
					wpt_schedule_values( $post->ID, 'hidden' );
					do_action( 'wpt_custom_tab', $post->ID, 'hidden' );
				}
			}
			?>
			</div>
		</div>
		<?php wpt_show_history( $post->ID ); ?>
		<?php wpt_meta_box_support( $is_pro ); ?>
		</div>
		</div>
		<?php
	} else {
		// permissions: this user isn't allowed to post status updates.
		esc_html_e( 'Your role does not have the ability to post status updates from this site.', 'wp-to-twitter' );
		?>
		<input type='hidden' name='_wpt_post_this' value='no' />
		<?php
	}
}

/**
 * Format history of status updates attempted on current post.
 *
 * @param array $post_id Post ID to fetch status updates on.
 */
function wpt_show_history( $post_id ) {
	$previous_tweets = get_post_meta( $post_id, '_jd_wp_twitter', true );
	$failed_tweets   = get_post_meta( $post_id, '_wpt_failed' );

	if ( ! is_array( $previous_tweets ) && '' !== $previous_tweets ) {
		$previous_tweets = array( 0 => $previous_tweets );
	}
	if ( ! empty( $previous_tweets ) || ! empty( $failed_tweets ) ) {
		?>
	<p class='panel-toggle'>
		<button type="button" aria-expanded="false" class='history-toggle button-secondary'><span class='dashicons dashicons-plus' aria-hidden="true"></span><?php esc_html_e( 'View Update History', 'wp-to-twitter' ); ?></button>
	</p>
	<div class='history'>
	<h4 class='wpt-past-updates'><?php esc_html_e( 'Previous Updates', 'wp-to-twitter' ); ?>:</h4>
	<ul class="striped">
		<?php
		$has_history = false;
		if ( is_array( $previous_tweets ) ) {
			foreach ( $previous_tweets as $previous_tweet ) {
				if ( '' !== $previous_tweet ) {
					$has_history = true;
					$intents     = array();
					if ( wtt_oauth_test() ) {
						$intents['x'] = "<a class='wpt-x' href='https://x.com/intent/tweet?text=" . urlencode( $previous_tweet ) . "'>" . __( 'X', 'wp-to-twitter' ) . '<span class="dashicons dashicons-external" aria-hidden="true"></span></a>';
					}
					if ( wpt_mastodon_connection() ) {
						$mastodon            = get_option( 'wpt_mastodon_instance' );
						$intents['mastodon'] = "<a class='wpt-mastodon' href='" . esc_url( $mastodon ) . '/statuses/new?text=' . urlencode( $previous_tweet ) . "'>" . __( 'Mastodon', 'wp-to-twitter' ) . '<span class="dashicons dashicons-external" aria-hidden="true"></span></a>';
					}
					if ( wpt_bluesky_connection() ) {
						$intents['bluesky'] = "<a class='wpt-bluesky' href='https://bsky.app/intent/compose?text=" . urlencode( $previous_tweet ) . "'>" . __( 'Bluesky', 'wp-to-twitter' ) . '<span class="dashicons dashicons-external" aria-hidden="true"></span></a>';
					}
					$intent_links = implode( ', ', $intents );
					?>
					<li><input type='hidden' name='_jd_wp_twitter[]' value='<?php echo esc_attr( $previous_tweet ); ?>' />
					<?php
					echo wp_kses_post( "<p class='wpt-previous-tweet'>$previous_tweet</p>$intent_links" );
					?>
					</li>
					<?php
				}
			}
		}
		?>
	</ul>
		<?php
		$list       = false;
		$error_list = '';
		if ( is_array( $failed_tweets ) ) {
			foreach ( $failed_tweets as $failed_tweet ) {
				if ( ! empty( $failed_tweet ) ) {
					$ft     = $failed_tweet['sentence'];
					$reason = $failed_tweet['code'];
					$error  = $failed_tweet['error'];
					$list   = true;

					$twitter_intent  = '';
					$mastodon_intent = '';
					$bluesky_intent  = '';
					if ( wtt_oauth_test() ) {
						$twitter_intent = "<a href='https://x.com/intent/tweet?text=" . urlencode( $ft ) . "'>" . __( 'Send to X.com', 'wp-to-twitter' ) . '</a>';
					}
					if ( wpt_mastodon_connection() ) {
						$mastodon        = get_option( 'wpt_mastodon_instance' );
						$mastodon_intent = "<a href='" . esc_url( $mastodon ) . '/statuses/new?text=' . urlencode( $ft ) . "'>" . __( 'Send to Mastodon', 'wp-to-twitter' ) . '</a>';
					}
					if ( wpt_bluesky_connection() ) {
						$bluesky_intent = "<a href='https://bsky.app/intent/compose?text=" . urlencode( $ft ) . "'>" . __( 'Send to Bluesky', 'wp-to-twitter' ) . '</a>';
					}
					$error_list .= "<li><code>Error: $reason</code> $ft $twitter_intent $mastodon_intent $bluesky_intent <br /><em>$error</em></li>";
				}
			}
			if ( true === $list ) {
				?>
				<h4 class='wpt-failed-updates'><?php esc_html_e( 'Failed Status Updates', 'wp-to-twitter' ); ?></h4>
				<ul>
					<?php echo wp_kses_post( $error_list ); ?>
				</ul>
				<?php
			}
		}
		if ( $has_history || $list ) {
			?>
			<p><input type='checkbox' name='wpt_clear_history' id='wptch' value='clear' /> <label for='wptch'><?php esc_html_e( 'Delete Status History', 'wp-to-twitter' ); ?></label></p>
			<?php
		}
		?>
	</div>
		<?php
	}
}

/**
 * Render support link or sales link inside metabox.
 *
 * @param string $is_pro 'pro' if is pro.
 */
function wpt_meta_box_support( $is_pro = 'free' ) {
	?>
	<div class="wpt-support">
	<?php
	if ( 'pro' === $is_pro ) {
		?>
		<p>
			<a href="<?php echo esc_url( add_query_arg( 'tab', 'support', admin_url( 'admin.php?page=wp-tweets-pro' ) ) ); ?>#get-support"><?php esc_html_e( 'Get Support', 'wp-to-twitter' ); ?></a>
		</p>
		<?php
	} else {
		?>
		<p class="link-highlight">
			<a href="https://xposterpro.com/awesome/xposter-pro/"><?php esc_html_e( 'Buy XPoster Pro', 'wp-to-twitter' ); ?></a>
		</p>
		<?php
	}
	?>
	</div>
	<?php
}

/**
 * Display metabox status messages when page is loaded.
 *
 * @param WP_Post $post Post object.
 * @param array   $options Posting options.
 */
function wpt_show_metabox_message( $post, $options ) {
	$type      = $post->post_type;
	$status    = $post->post_status;
	$post_this = wpt_get_post_update_status( $post, $options );
	if ( isset( $_REQUEST['message'] ) && '10' !== $_REQUEST['message'] ) {
		// don't display when draft is updated or if no message.
		if ( ! ( ( '1' === $_REQUEST['message'] ) && ( 'publish' === $status && '1' !== $options[ $type ]['post-edited-update'] ) ) && 'no' !== $post_this ) {
			$log = wpt_get_log( 'wpt_status_message', $post->ID );
			if ( is_array( $log ) ) {
				$message = $log['message'];
				$http    = $log['http'];
			} else {
				$message = $log;
				$http    = '200';
			}
			$class = ( '200' !== (string) $http ) ? 'error' : 'success';
			if ( '' !== trim( $message ) ) {
				wp_admin_notice(
					$message,
					array(
						'type'               => $class,
						'additional_classes' => 'inline',
					)
				);
			}
		}
	}
}

/**
 * Check whether a post is supposed to be posted based on settings.
 *
 * @param WP_Post $post Post object.
 * @param array   $options Status update options.
 *
 * @return string
 */
function wpt_get_post_update_status( $post, $options ) {
	$status    = $post->post_status;
	$type      = $post->post_type;
	$post_this = get_post_meta( $post->ID, '_wpt_post_this', true );
	if ( ! $post_this ) {
		$post_this = ( '1' === get_option( 'jd_tweet_default' ) ) ? 'no' : 'yes';
	}
	$is_edit               = ( 'publish' === $status ) ? true : false;
	$status_update_on_edit = ( '1' === $options[ $type ]['post-edited-update'] && '1' !== get_option( 'jd_tweet_default_edit' ) ) ? true : false;
	if ( $is_edit && ! $status_update_on_edit ) {
		$post_this = 'no';
	}

	return $post_this;
}

/**
 * Test whether the metabox should load with a 'yes' or 'no' preset for posting status and display toggle to update.
 *
 * @param WP_Post $post Post object.
 * @param array   $options Status update options.
 */
function wpt_show_post_switch( $post, $options ) {
	$post_this = wpt_get_post_update_status( $post, $options );

	if ( current_user_can( 'wpt_twitter_switch' ) || current_user_can( 'manage_options' ) ) {
		// "no" means 'Don't Post' (is checked)
		$nochecked  = ( 'no' === $post_this ) ? ' checked="checked"' : '';
		$yeschecked = ( 'yes' === $post_this ) ? ' checked="checked"' : '';
		?>
		<p class='toggle-btn-group'>
			<input type='radio' name='_wpt_post_this' value='no' id='jtn'<?php echo esc_attr( $nochecked ); ?> /><label for='jtn'><?php esc_html_e( "Don't Post", 'wp-to-twitter' ); ?></label>
			<input type='radio' name='_wpt_post_this' value='yes' id='jty'<?php echo esc_attr( $yeschecked ); ?> /><label for='jty'><?php esc_html_e( 'Post', 'wp-to-twitter' ); ?></label>
		</p>
		<?php
	} else {
		?>
		<input type='hidden' name='_wpt_post_this' value='<?php echo esc_attr( $post_this ); ?>' />
		<?php
	}
}

/**
 * Generate a status update template for display.
 *
 * @param WP_Post $post Post object.
 * @param array   $options Post status options.
 *
 * @return string
 */
function wpt_display_status_template( $post, $options ) {
	$status   = $post->post_status;
	$type     = $post->post_type;
	$template = ( 'publish' === $status ) ? $options[ $type ]['post-edited-text'] : $options[ $type ]['post-published-text'];
	$expanded = $template;
	if ( '' !== get_option( 'jd_twit_prepend', '' ) ) {
		$expanded = '<em>' . stripslashes( get_option( 'jd_twit_prepend' ) ) . '</em> ' . $expanded;
	}
	if ( '' !== get_option( 'jd_twit_append', '' ) ) {
		$expanded = $expanded . ' <em>' . stripslashes( get_option( 'jd_twit_append' ) ) . '</em>';
	}

	return $expanded;
}

/**
 * Generate post now and schedule update buttons.
 *
 * @param string $is_pro 'pro' if Pro.
 */
function wpt_display_metabox_status_buttons( $is_pro ) {
	?>
	<div class='tweet-buttons'>
		<div class="wpt-buttons">
			<button type='button' class='tweet button-primary' data-action='tweet'><span class='dashicons dashicons-share' aria-hidden='true'></span><?php esc_html_e( 'Share Now', 'wp-to-twitter' ); ?></button>
		<?php
		if ( 'pro' === $is_pro ) {
			?>
			<button type='button' class='tweet schedule button-secondary' data-action='schedule' disabled><?php esc_html_e( 'Schedule', 'wp-to-twitter' ); ?></button>
			<button type='button' class='time button-secondary'><span class='dashicons dashicons-clock' aria-hidden='true'></span><span class='screen-reader-text'><?php esc_html_e( 'Set Date/Time', 'wp-to-twitter' ); ?></span></button>
			<?php
		}
		?>
		</div>
	</div>
	<div class='wpt_log' aria-live='assertive'></div>
	<?php
	if ( 'pro' === $is_pro ) {
		$datavalue = gmdate( 'Y-m-d', current_time( 'timestamp' ) ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
		$timevalue = date_i18n( 'h:s a', current_time( 'timestamp' ) + 3600 ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
		?>
		<div id="wpt_set_tweet_time">
			<div class="wpt-date-field">
				<label for="wpt_date"><?php esc_html_e( 'Date', 'wp-to-twitter' ); ?></label>
				<input type="date" value="" class="wpt_date date" name="wpt_datetime" id="wpt_date" data-value="<?php echo esc_attr( $datavalue ); ?>" /><br/>
			</div>
			<div class="wpt-time-field">
				<label for="wpt_time"><?php esc_html_e( 'Time', 'wp-to-twitter' ); ?></label>
				<input type="time" value="<?php echo esc_attr( $timevalue ); ?>" class="wpt_time time" name="wpt_datetime" id="wpt_time" />
			</div>
		</div>
		<?php
	}
}