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.
880 lines
37 KiB
PHP
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;
|
|
}
|
|
}
|