tigerstyle-heat/includes/modules/class-twitter.php
Ryan Malloy 0028738e33 Initial commit: TigerStyle Heat v2.0.0
Make your WordPress site irresistible. Natural SEO attraction with:
- robots.txt management
- sitemap.xml generation
- LLMs.txt support
- Google integration (Analytics, Search Console, Tag Manager)
- Schema.org structured data
- Open Graph / Twitter Card meta tags
- AMP support
- Visual elements gallery
- Built-in backup/restore module

Includes build.sh and .distignore for WordPress-installable release ZIPs.
2026-05-27 13:41:35 -06:00

880 lines
37 KiB
PHP

<?php
/**
* Twitter/X Integration Module
*
* Handles Twitter Cards optimization, meta tags generation,
* and social sharing optimization for X platform.
*
* @package TigerStyleSEO
* @subpackage Modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Twitter/X Integration Class
*/
class TigerStyleSEO_Twitter {
/**
* Single instance of the class
*/
private static $instance = null;
/**
* Option name for Twitter settings
*/
private $option_name = 'tigerstyle_heat_twitter';
/**
* Default settings
*/
private $defaults = array(
'enable_twitter_cards' => true,
'default_card_type' => 'summary_large_image',
'site_username' => '',
'creator_username' => '',
'default_image' => '',
'image_alt_text' => '',
'enable_creator_tags' => true,
'enable_fallback_to_og' => true,
'card_title_length' => 70,
'card_description_length' => 200,
'optimize_for_engagement' => true
);
/**
* Available Twitter Card types
*/
private $card_types = array(
'summary' => 'Summary Card',
'summary_large_image' => 'Summary Card with Large Image',
'app' => 'App Card',
'player' => 'Player Card'
);
/**
* Get single instance
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init();
}
/**
* Initialize the module
*/
private function init() {
// Frontend hooks
add_action('wp_head', array($this, 'output_twitter_card_tags'), 6);
// Admin hooks
if (is_admin()) {
add_action('admin_init', array($this, 'register_settings'));
}
// REST API endpoints for testing
add_action('rest_api_init', array($this, 'register_rest_routes'));
}
/**
* Get Twitter settings
*/
public function get_settings() {
$settings = get_option($this->option_name, array());
return wp_parse_args($settings, $this->defaults);
}
/**
* Update Twitter settings
*/
public function update_settings($new_settings) {
$settings = $this->get_settings();
$settings = wp_parse_args($new_settings, $settings);
return update_option($this->option_name, $settings);
}
/**
* Output Twitter Card meta tags
*/
public function output_twitter_card_tags() {
$settings = $this->get_settings();
if (!$settings['enable_twitter_cards']) {
return;
}
// Get Twitter Card data
$card_data = $this->get_twitter_card_data();
// Output Twitter Card tags
foreach ($card_data as $name => $content) {
if (!empty($content)) {
printf(
'<meta name="%s" content="%s" />' . "\n",
esc_attr($name),
esc_attr($content)
);
}
}
}
/**
* Get Twitter Card data for current page
*/
private function get_twitter_card_data() {
$settings = $this->get_settings();
$card_data = array();
// Basic required tags
$card_data['twitter:card'] = $this->get_card_type();
$card_data['twitter:site'] = $this->get_site_username();
// Content tags
$card_data['twitter:title'] = $this->get_twitter_title();
$card_data['twitter:description'] = $this->get_twitter_description();
// Image tags
$image_data = $this->get_twitter_image();
if ($image_data) {
$card_data['twitter:image'] = $image_data['url'];
if (!empty($image_data['alt'])) {
$card_data['twitter:image:alt'] = $image_data['alt'];
}
}
// Creator tag
if ($settings['enable_creator_tags']) {
$creator = $this->get_creator_username();
if ($creator) {
$card_data['twitter:creator'] = $creator;
}
}
// App card specific tags
if ($card_data['twitter:card'] === 'app') {
$app_data = $this->get_app_card_data();
$card_data = array_merge($card_data, $app_data);
}
// Player card specific tags
if ($card_data['twitter:card'] === 'player') {
$player_data = $this->get_player_card_data();
$card_data = array_merge($card_data, $player_data);
}
return apply_filters('tigerstyle_heat_twitter_card_data', $card_data);
}
/**
* Get Twitter Card type
*/
private function get_card_type() {
$settings = $this->get_settings();
// Check for post-specific card type
if (is_singular()) {
$post_card_type = get_post_meta(get_the_ID(), '_twitter_card_type', true);
if ($post_card_type && isset($this->card_types[$post_card_type])) {
return $post_card_type;
}
}
return $settings['default_card_type'];
}
/**
* Get site username (with @ prefix)
*/
private function get_site_username() {
$settings = $this->get_settings();
$username = $settings['site_username'];
if (!empty($username)) {
// Ensure @ prefix
return (strpos($username, '@') === 0) ? $username : '@' . $username;
}
return '';
}
/**
* Get creator username (with @ prefix)
*/
private function get_creator_username() {
$settings = $this->get_settings();
// Check for post-specific creator
if (is_singular()) {
$post_creator = get_post_meta(get_the_ID(), '_twitter_creator', true);
if ($post_creator) {
return (strpos($post_creator, '@') === 0) ? $post_creator : '@' . $post_creator;
}
// Try to get author's Twitter handle
$author_twitter = get_the_author_meta('twitter');
if ($author_twitter) {
return (strpos($author_twitter, '@') === 0) ? $author_twitter : '@' . $author_twitter;
}
}
$username = $settings['creator_username'];
if (!empty($username)) {
return (strpos($username, '@') === 0) ? $username : '@' . $username;
}
return '';
}
/**
* Get Twitter title
*/
private function get_twitter_title() {
$settings = $this->get_settings();
$max_length = $settings['card_title_length'];
// Check for custom Twitter title
if (is_singular()) {
$custom_title = get_post_meta(get_the_ID(), '_twitter_title', true);
if ($custom_title) {
return $this->truncate_text($custom_title, $max_length);
}
}
// Fallback to OpenGraph or default title logic
if (is_single() || is_page()) {
$title = get_the_title();
} elseif (is_category()) {
$title = single_cat_title('', false);
} elseif (is_tag()) {
$title = single_tag_title('', false);
} elseif (is_author()) {
$title = get_the_author();
} elseif (is_search()) {
$title = sprintf(__('Search Results for: %s', 'tigerstyle-heat'), get_search_query());
} elseif (is_archive()) {
$title = get_the_archive_title();
} else {
$title = get_bloginfo('name');
}
return $this->truncate_text($title, $max_length);
}
/**
* Get Twitter description
*/
private function get_twitter_description() {
$settings = $this->get_settings();
$max_length = $settings['card_description_length'];
// Check for custom Twitter description
if (is_singular()) {
$custom_description = get_post_meta(get_the_ID(), '_twitter_description', true);
if ($custom_description) {
return $this->truncate_text($custom_description, $max_length);
}
}
// Fallback logic
if (is_single() || is_page()) {
$excerpt = get_the_excerpt();
if ($excerpt) {
return $this->truncate_text($excerpt, $max_length);
}
} elseif (is_category()) {
$description = category_description();
if ($description) {
return $this->truncate_text(strip_tags($description), $max_length);
}
} elseif (is_tag()) {
$description = tag_description();
if ($description) {
return $this->truncate_text(strip_tags($description), $max_length);
}
}
$site_description = get_bloginfo('description');
return $this->truncate_text($site_description, $max_length);
}
/**
* Get Twitter image data
*/
private function get_twitter_image() {
$settings = $this->get_settings();
// Check for custom Twitter image
if (is_singular()) {
$custom_image = get_post_meta(get_the_ID(), '_twitter_image', true);
if ($custom_image) {
return array(
'url' => $custom_image,
'alt' => get_post_meta(get_the_ID(), '_twitter_image_alt', true) ?: $settings['image_alt_text']
);
}
}
// Try featured image
if (is_singular() && has_post_thumbnail()) {
$image_id = get_post_thumbnail_id();
$image = wp_get_attachment_image_src($image_id, 'full');
if ($image) {
return array(
'url' => $image[0],
'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true) ?: get_the_title()
);
}
}
// Fallback to default image
if (!empty($settings['default_image'])) {
return array(
'url' => $settings['default_image'],
'alt' => $settings['image_alt_text'] ?: get_bloginfo('name') . ' - Default Image'
);
}
return false;
}
/**
* Get App Card specific data
*/
private function get_app_card_data() {
$app_data = array();
if (is_singular()) {
$post_id = get_the_ID();
// iOS app data
$ios_id = get_post_meta($post_id, '_twitter_app_id_iphone', true);
if ($ios_id) {
$app_data['twitter:app:id:iphone'] = $ios_id;
$app_data['twitter:app:id:ipad'] = $ios_id;
$ios_url = get_post_meta($post_id, '_twitter_app_url_iphone', true);
if ($ios_url) {
$app_data['twitter:app:url:iphone'] = $ios_url;
$app_data['twitter:app:url:ipad'] = $ios_url;
}
$ios_name = get_post_meta($post_id, '_twitter_app_name_iphone', true);
if ($ios_name) {
$app_data['twitter:app:name:iphone'] = $ios_name;
$app_data['twitter:app:name:ipad'] = $ios_name;
}
}
// Android app data
$android_id = get_post_meta($post_id, '_twitter_app_id_googleplay', true);
if ($android_id) {
$app_data['twitter:app:id:googleplay'] = $android_id;
$android_url = get_post_meta($post_id, '_twitter_app_url_googleplay', true);
if ($android_url) {
$app_data['twitter:app:url:googleplay'] = $android_url;
}
$android_name = get_post_meta($post_id, '_twitter_app_name_googleplay', true);
if ($android_name) {
$app_data['twitter:app:name:googleplay'] = $android_name;
}
}
}
return $app_data;
}
/**
* Get Player Card specific data
*/
private function get_player_card_data() {
$player_data = array();
if (is_singular()) {
$post_id = get_the_ID();
$player_url = get_post_meta($post_id, '_twitter_player_url', true);
if ($player_url) {
$player_data['twitter:player'] = $player_url;
$player_width = get_post_meta($post_id, '_twitter_player_width', true);
if ($player_width) {
$player_data['twitter:player:width'] = $player_width;
}
$player_height = get_post_meta($post_id, '_twitter_player_height', true);
if ($player_height) {
$player_data['twitter:player:height'] = $player_height;
}
$player_stream = get_post_meta($post_id, '_twitter_player_stream', true);
if ($player_stream) {
$player_data['twitter:player:stream'] = $player_stream;
}
}
}
return $player_data;
}
/**
* Truncate text to specified length
*/
private function truncate_text($text, $max_length) {
if (strlen($text) <= $max_length) {
return $text;
}
return substr($text, 0, $max_length - 3) . '...';
}
/**
* Validate Twitter image requirements
*/
public function validate_image($image_url) {
$errors = array();
// Check if image exists and get dimensions
$image_info = getimagesize($image_url);
if (!$image_info) {
$errors[] = __('Unable to access image or invalid image format.', 'tigerstyle-heat');
return $errors;
}
$width = $image_info[0];
$height = $image_info[1];
$mime_type = $image_info['mime'];
// Get file size
$headers = get_headers($image_url, 1);
$size = isset($headers['Content-Length']) ? $headers['Content-Length'] : 0;
// Check file size (5MB limit)
if ($size > 5 * 1024 * 1024) {
$errors[] = __('Image file size must be under 5MB for Twitter Cards.', 'tigerstyle-heat');
}
// Check supported formats
$supported_formats = array('image/jpeg', 'image/png', 'image/webp', 'image/gif');
if (!in_array($mime_type, $supported_formats)) {
$errors[] = __('Image format not supported. Use JPG, PNG, WEBP, or GIF.', 'tigerstyle-heat');
}
// Check minimum dimensions for summary_large_image
if ($width < 300 || $height < 157) {
$errors[] = __('Warning: For best results, images should be at least 300x157 pixels.', 'tigerstyle-heat');
}
// Check recommended dimensions
if ($width < 1200 || $height < 628) {
$errors[] = __('Recommendation: Use 1200x628 pixels for optimal display.', 'tigerstyle-heat');
}
// Check aspect ratio for large image cards
$ratio = $width / $height;
if ($ratio < 1.8 || $ratio > 2.1) {
$errors[] = __('Warning: Recommended aspect ratio is 1.91:1 for large image cards.', 'tigerstyle-heat');
}
return $errors;
}
/**
* Register admin settings
*/
public function register_settings() {
register_setting(
'tigerstyle_heat_twitter',
$this->option_name,
array(
'sanitize_callback' => array($this, 'sanitize_settings')
)
);
}
/**
* Sanitize settings
*/
public function sanitize_settings($settings) {
$clean_settings = array();
// Boolean settings
$boolean_fields = array('enable_twitter_cards', 'enable_creator_tags', 'enable_fallback_to_og', 'optimize_for_engagement');
foreach ($boolean_fields as $field) {
$clean_settings[$field] = !empty($settings[$field]);
}
// Text settings
$clean_settings['default_card_type'] = sanitize_text_field($settings['default_card_type'] ?? 'summary_large_image');
$clean_settings['site_username'] = sanitize_text_field($settings['site_username'] ?? '');
$clean_settings['creator_username'] = sanitize_text_field($settings['creator_username'] ?? '');
$clean_settings['default_image'] = esc_url_raw($settings['default_image'] ?? '');
$clean_settings['image_alt_text'] = sanitize_text_field($settings['image_alt_text'] ?? '');
// Numeric settings
$clean_settings['card_title_length'] = min(70, max(10, absint($settings['card_title_length'] ?? 70)));
$clean_settings['card_description_length'] = min(200, max(50, absint($settings['card_description_length'] ?? 200)));
return $clean_settings;
}
/**
* Register REST API routes for testing
*/
public function register_rest_routes() {
register_rest_route('tigerstyle-heat/v1', '/twitter/debug', array(
'methods' => 'GET',
'callback' => array($this, 'debug_twitter_cards'),
'permission_callback' => function() {
return current_user_can('manage_options');
}
));
}
/**
* Debug Twitter Card data (REST endpoint)
*/
public function debug_twitter_cards($request) {
$url = $request->get_param('url');
if (empty($url)) {
$url = home_url();
}
// Get Twitter Card data for the URL
$card_data = $this->get_twitter_card_data();
return rest_ensure_response(array(
'url' => $url,
'twitter_card_data' => $card_data,
'card_validator_urls' => $this->get_card_validator_urls(),
'validation_notes' => $this->get_validation_notes($card_data)
));
}
/**
* Get validation notes for Twitter Card data
*/
private function get_validation_notes($card_data) {
$notes = array();
// Check required fields
$required_fields = array('twitter:card', 'twitter:title', 'twitter:description');
foreach ($required_fields as $field) {
if (empty($card_data[$field])) {
$notes[] = sprintf(__('Missing required field: %s', 'tigerstyle-heat'), $field);
}
}
// Check site username
if (empty($card_data['twitter:site'])) {
$notes[] = __('Missing twitter:site - highly recommended for attribution', 'tigerstyle-heat');
}
// Check image
if (!empty($card_data['twitter:image'])) {
$image_errors = $this->validate_image($card_data['twitter:image']);
$notes = array_merge($notes, $image_errors);
} else {
$notes[] = __('No Twitter image specified - cards may not display optimally', 'tigerstyle-heat');
}
return $notes;
}
/**
* Get Twitter Card validator URLs
*/
public function get_card_validator_urls() {
return array(
'twitter_official' => 'https://cards-dev.twitter.com/validator',
'threadcreator' => 'https://threadcreator.com/tools/twitter-card-validator',
'tweetpik' => 'https://tweethunter.io/tweetpik/twitter-card-validator',
'boilerplate' => 'https://boilerplatehq.com/tools/twitter-card-validator',
'brandbird' => 'https://www.brandbird.app/tools/twitter-card-validator',
'typefully' => 'https://typefully.com/tools/twitter-card-validator'
);
}
/**
* Get Twitter/X developer documentation URLs
*/
public function get_twitter_docs_urls() {
return array(
'cards_overview' => 'https://developer.x.com/en/docs/x-for-websites/cards/overview/markup',
'troubleshooting' => 'https://developer.x.com/en/docs/x-for-websites/cards/guides/troubleshooting-cards',
'getting_started' => 'https://developer.x.com/en/docs/x-for-websites/cards/guides/getting-started',
'card_types' => 'https://developer.x.com/en/docs/x-for-websites/cards/overview/abouts-cards'
);
}
/**
* Render admin page
*/
public function render_admin_page() {
// Handle form submission
if (isset($_POST['submit_twitter_settings']) && wp_verify_nonce($_POST['twitter_nonce'], 'twitter_settings')) {
$this->handle_settings_update();
}
$settings = $this->get_settings();
$validator_urls = $this->get_card_validator_urls();
$docs_urls = $this->get_twitter_docs_urls();
?>
<div class="seo-info-box">
<h3><?php _e('Twitter/✖️ Cards & Social Optimization', 'tigerstyle-heat'); ?></h3>
<p><?php _e('Configure Twitter Cards for optimal sharing on X (formerly Twitter). Enhance your social media presence with rich media previews.', 'tigerstyle-heat'); ?></p>
<div class="seo-setup-steps">
<h4><?php _e('Quick Setup Steps:', 'tigerstyle-heat'); ?></h4>
<ol>
<li><?php _e('Enable Twitter Cards below', 'tigerstyle-heat'); ?></li>
<li><?php _e('Add your @username for site attribution', 'tigerstyle-heat'); ?></li>
<li><?php _e('Set a default card image (1200x628px recommended)', 'tigerstyle-heat'); ?></li>
<li><?php _e('Configure creator username for content attribution', 'tigerstyle-heat'); ?></li>
<li><?php _e('Test your cards using Twitter Card validators', 'tigerstyle-heat'); ?></li>
</ol>
</div>
<div style="margin-top: 20px;">
<strong><?php _e('Twitter Card Validators & Tools:', 'tigerstyle-heat'); ?></strong>
<ul style="margin-left: 20px;">
<li><a href="<?php echo esc_url($validator_urls['threadcreator']); ?>" target="_blank"><?php _e('ThreadCreator Validator', 'tigerstyle-heat'); ?></a> - <?php _e('Preview how your website will look when shared', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($validator_urls['tweetpik']); ?>" target="_blank"><?php _e('TweetPik Validator', 'tigerstyle-heat'); ?></a> - <?php _e('Validate how your site appears in tweets', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($validator_urls['brandbird']); ?>" target="_blank"><?php _e('BrandBird Validator', 'tigerstyle-heat'); ?></a> - <?php _e('Test and preview Twitter Cards online', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($validator_urls['typefully']); ?>" target="_blank"><?php _e('Typefully Validator', 'tigerstyle-heat'); ?></a> - <?php _e('Optimize tweets with card validation', 'tigerstyle-heat'); ?></li>
</ul>
<strong style="margin-top: 15px; display: block;"><?php _e('Official X/Twitter Documentation:', 'tigerstyle-heat'); ?></strong>
<ul style="margin-left: 20px;">
<li><a href="<?php echo esc_url($docs_urls['cards_overview']); ?>" target="_blank"><?php _e('Twitter Cards Markup Guide', 'tigerstyle-heat'); ?></a></li>
<li><a href="<?php echo esc_url($docs_urls['troubleshooting']); ?>" target="_blank"><?php _e('Troubleshooting Cards', 'tigerstyle-heat'); ?></a></li>
<li><a href="<?php echo esc_url($docs_urls['getting_started']); ?>" target="_blank"><?php _e('Getting Started Guide', 'tigerstyle-heat'); ?></a></li>
</ul>
</div>
</div>
<form method="post" action="">
<?php wp_nonce_field('twitter_settings', 'twitter_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Enable Twitter Cards', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_twitter_cards" value="1" <?php checked($settings['enable_twitter_cards']); ?> />
<?php _e('Generate Twitter Card meta tags for X/Twitter sharing', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Automatically adds twitter:card, twitter:title, twitter:description, and other Twitter Card tags.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Default Card Type', 'tigerstyle-heat'); ?></th>
<td>
<select name="default_card_type">
<?php foreach ($this->card_types as $value => $label): ?>
<option value="<?php echo esc_attr($value); ?>" <?php selected($settings['default_card_type'], $value); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description">
<?php _e('Default Twitter Card type for your content.', 'tigerstyle-heat'); ?><br>
<strong><?php _e('Card Types:', 'tigerstyle-heat'); ?></strong>
<ul style="margin: 5px 0 0 20px;">
<li><strong><?php _e('Summary:', 'tigerstyle-heat'); ?></strong> <?php _e('Default card with small image', 'tigerstyle-heat'); ?></li>
<li><strong><?php _e('Summary Large Image:', 'tigerstyle-heat'); ?></strong> <?php _e('Card with prominent image (recommended)', 'tigerstyle-heat'); ?></li>
<li><strong><?php _e('App:', 'tigerstyle-heat'); ?></strong> <?php _e('Mobile app download card', 'tigerstyle-heat'); ?></li>
<li><strong><?php _e('Player:', 'tigerstyle-heat'); ?></strong> <?php _e('Video/audio content card', 'tigerstyle-heat'); ?></li>
</ul>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Site Username', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="site_username" value="<?php echo esc_attr($settings['site_username']); ?>" class="regular-text" placeholder="@yoursite" />
<p class="description">
<?php _e('Your website\'s Twitter/X username (with or without @). This appears as attribution when content is shared.', 'tigerstyle-heat'); ?><br>
<strong><?php _e('Example:', 'tigerstyle-heat'); ?></strong> @yourcompany
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Creator Username', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="creator_username" value="<?php echo esc_attr($settings['creator_username']); ?>" class="regular-text" placeholder="@creator" />
<p class="description">
<?php _e('Default content creator\'s Twitter/X username. Can be overridden per post.', 'tigerstyle-heat'); ?><br>
<?php _e('This is used for individual content attribution alongside the site attribution.', 'tigerstyle-heat'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Default Card Image', 'tigerstyle-heat'); ?></th>
<td>
<input type="url" name="default_image" value="<?php echo esc_attr($settings['default_image']); ?>" class="regular-text" placeholder="https://example.com/twitter-card.jpg" />
<p class="description">
<?php _e('Fallback image when no featured image is set. Used for Twitter Card previews.', 'tigerstyle-heat'); ?><br>
<strong><?php _e('Twitter Image Requirements:', 'tigerstyle-heat'); ?></strong>
<ul style="margin: 5px 0 0 20px;">
<li><?php _e('Recommended: 1200x628px (1.91:1 ratio)', 'tigerstyle-heat'); ?></li>
<li><?php _e('Minimum: 300x157px', 'tigerstyle-heat'); ?></li>
<li><?php _e('Maximum file size: 5MB', 'tigerstyle-heat'); ?></li>
<li><?php _e('Supported formats: JPG, PNG, WEBP, GIF', 'tigerstyle-heat'); ?></li>
<li><?php _e('SVG not supported', 'tigerstyle-heat'); ?></li>
</ul>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Image Alt Text', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="image_alt_text" value="<?php echo esc_attr($settings['image_alt_text']); ?>" class="regular-text" placeholder="Your site logo" />
<p class="description"><?php _e('Default alt text for your Twitter Card images. Improves accessibility.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Text Length Limits', 'tigerstyle-heat'); ?></th>
<td>
<label><?php _e('Title:', 'tigerstyle-heat'); ?>
<input type="number" name="card_title_length" value="<?php echo esc_attr($settings['card_title_length']); ?>" min="10" max="70" style="width: 60px;" /> <?php _e('characters (max 70)', 'tigerstyle-heat'); ?>
</label><br><br>
<label><?php _e('Description:', 'tigerstyle-heat'); ?>
<input type="number" name="card_description_length" value="<?php echo esc_attr($settings['card_description_length']); ?>" min="50" max="200" style="width: 60px;" /> <?php _e('characters (max 200)', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Twitter automatically truncates content that exceeds these limits.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Creator Attribution', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_creator_tags" value="1" <?php checked($settings['enable_creator_tags']); ?> />
<?php _e('Include twitter:creator tags for content attribution', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Adds individual content creator attribution in addition to site attribution.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('OpenGraph Fallback', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_fallback_to_og" value="1" <?php checked($settings['enable_fallback_to_og']); ?> />
<?php _e('Allow Twitter to use OpenGraph tags as fallback', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('If Twitter-specific tags are missing, X/Twitter may use og:title, og:description, and og:image tags.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Engagement Optimization', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="optimize_for_engagement" value="1" <?php checked($settings['optimize_for_engagement']); ?> />
<?php _e('Optimize card content for maximum engagement', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Ensures optimal card formatting and content structure for better click-through rates.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
</table>
<div style="margin-top: 20px; padding: 20px; background: #f0f9ff; border: 1px solid #bfdbfe; border-radius: 5px;">
<h4><?php _e('Twitter/X Platform Updates', 'tigerstyle-heat'); ?></h4>
<p><?php _e('X (formerly Twitter) continues to support Twitter Cards for rich media previews. Key features:', 'tigerstyle-heat'); ?></p>
<ul style="margin-left: 20px;">
<li><?php _e('Cards display rich previews when URLs are shared', 'tigerstyle-heat'); ?></li>
<li><?php _e('Images, titles, and descriptions improve engagement', 'tigerstyle-heat'); ?></li>
<li><?php _e('Proper attribution helps with brand recognition', 'tigerstyle-heat'); ?></li>
<li><?php _e('Works across all X/Twitter applications and platforms', 'tigerstyle-heat'); ?></li>
</ul>
<h4 style="margin-top: 15px;"><?php _e('Testing Your Twitter Cards', 'tigerstyle-heat'); ?></h4>
<p><?php _e('After saving these settings, validate your cards:', 'tigerstyle-heat'); ?></p>
<ol style="margin-left: 20px;">
<li><?php _e('Use any of the validator tools listed above', 'tigerstyle-heat'); ?></li>
<li><?php _e('Enter your page URL to see the card preview', 'tigerstyle-heat'); ?></li>
<li><?php _e('Share a test post on X/Twitter to verify appearance', 'tigerstyle-heat'); ?></li>
<li><?php _e('Monitor engagement metrics to optimize content', 'tigerstyle-heat'); ?></li>
</ol>
</div>
<?php submit_button(__('Save Twitter Settings', 'tigerstyle-heat'), 'primary', 'submit_twitter_settings'); ?>
</form>
<?php
}
/**
* Handle settings update
*/
private function handle_settings_update() {
// Validate and save settings
$new_settings = array();
// Boolean settings
$new_settings['enable_twitter_cards'] = !empty($_POST['enable_twitter_cards']);
$new_settings['enable_creator_tags'] = !empty($_POST['enable_creator_tags']);
$new_settings['enable_fallback_to_og'] = !empty($_POST['enable_fallback_to_og']);
$new_settings['optimize_for_engagement'] = !empty($_POST['optimize_for_engagement']);
// Text settings
$new_settings['default_card_type'] = sanitize_text_field($_POST['default_card_type'] ?? 'summary_large_image');
$new_settings['site_username'] = sanitize_text_field($_POST['site_username'] ?? '');
$new_settings['creator_username'] = sanitize_text_field($_POST['creator_username'] ?? '');
$new_settings['default_image'] = esc_url_raw($_POST['default_image'] ?? '');
$new_settings['image_alt_text'] = sanitize_text_field($_POST['image_alt_text'] ?? '');
// Numeric settings
$new_settings['card_title_length'] = min(70, max(10, absint($_POST['card_title_length'] ?? 70)));
$new_settings['card_description_length'] = min(200, max(50, absint($_POST['card_description_length'] ?? 200)));
// Validate image if provided
if (!empty($new_settings['default_image'])) {
$image_errors = $this->validate_image($new_settings['default_image']);
if (!empty($image_errors)) {
// Show validation warnings
add_action('admin_notices', function() use ($image_errors) {
echo '<div class="notice notice-warning"><p><strong>' . __('Image Validation Warnings:', 'tigerstyle-heat') . '</strong></p><ul>';
foreach ($image_errors as $error) {
echo '<li>' . esc_html($error) . '</li>';
}
echo '</ul></div>';
});
}
}
// Save settings
$this->update_settings($new_settings);
// Redirect with success message
wp_redirect(add_query_arg('message', 'twitter_updated', wp_get_referer()));
exit;
}
}