true, 'default_image' => '', 'app_id' => '', 'admins' => '', 'domain_verification' => '', 'site_name' => '', 'enable_article_tags' => true, 'enable_video_tags' => false, 'image_width' => 1200, 'image_height' => 630, 'enable_crawler_optimization' => true ); /** * 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_open_graph_tags'), 5); add_action('wp_head', array($this, 'output_facebook_domain_verification'), 1); // 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 Facebook settings */ public function get_settings() { $settings = get_option($this->option_name, array()); return wp_parse_args($settings, $this->defaults); } /** * Update Facebook 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 Facebook domain verification meta tag */ public function output_facebook_domain_verification() { if (!is_home() && !is_front_page()) { return; } $settings = $this->get_settings(); if (!empty($settings['domain_verification'])) { printf( '' . "\n", esc_attr($settings['domain_verification']) ); } } /** * Output Open Graph meta tags */ public function output_open_graph_tags() { $settings = $this->get_settings(); if (!$settings['enable_open_graph']) { return; } // Basic Open Graph tags $og_data = $this->get_open_graph_data(); // Output Open Graph namespace add_filter('language_attributes', array($this, 'add_opengraph_namespace')); // Output tags foreach ($og_data as $property => $content) { if (!empty($content)) { printf( '' . "\n", esc_attr($property), esc_attr($content) ); } } // Facebook-specific tags if (!empty($settings['app_id'])) { printf( '' . "\n", esc_attr($settings['app_id']) ); } if (!empty($settings['admins'])) { $admins = explode(',', $settings['admins']); foreach ($admins as $admin) { $admin = trim($admin); if (!empty($admin)) { printf( '' . "\n", esc_attr($admin) ); } } } } /** * Add Open Graph namespace to html tag */ public function add_opengraph_namespace($output) { if (strpos($output, 'xmlns:og') === false) { $output .= ' xmlns:og="http://ogp.me/ns#" xmlns:fb="http://www.facebook.com/2008/fbml"'; } return $output; } /** * Get Open Graph data for current page */ private function get_open_graph_data() { $settings = $this->get_settings(); $og_data = array(); // Basic required tags $og_data['og:title'] = $this->get_og_title(); $og_data['og:description'] = $this->get_og_description(); $og_data['og:type'] = $this->get_og_type(); $og_data['og:url'] = $this->get_og_url(); $og_data['og:site_name'] = $this->get_og_site_name(); // Image tags $image_data = $this->get_og_image(); if ($image_data) { $og_data['og:image'] = $image_data['url']; if (!empty($image_data['width'])) { $og_data['og:image:width'] = $image_data['width']; } if (!empty($image_data['height'])) { $og_data['og:image:height'] = $image_data['height']; } if (!empty($image_data['alt'])) { $og_data['og:image:alt'] = $image_data['alt']; } } // Article-specific tags if ($settings['enable_article_tags'] && is_single()) { $article_data = $this->get_article_data(); $og_data = array_merge($og_data, $article_data); } return apply_filters('tigerstyle_heat_facebook_og_data', $og_data); } /** * Get Open Graph title */ private function get_og_title() { if (is_single() || is_page()) { return get_the_title(); } elseif (is_category()) { return single_cat_title('', false); } elseif (is_tag()) { return single_tag_title('', false); } elseif (is_author()) { return get_the_author(); } elseif (is_search()) { return sprintf(__('Search Results for: %s', 'tigerstyle-heat'), get_search_query()); } elseif (is_archive()) { return get_the_archive_title(); } else { return get_bloginfo('name'); } } /** * Get Open Graph description */ private function get_og_description() { if (is_single() || is_page()) { $excerpt = get_the_excerpt(); if ($excerpt) { return wp_trim_words($excerpt, 30); } } elseif (is_category()) { $description = category_description(); if ($description) { return wp_trim_words(strip_tags($description), 30); } } elseif (is_tag()) { $description = tag_description(); if ($description) { return wp_trim_words(strip_tags($description), 30); } } return get_bloginfo('description'); } /** * Get Open Graph type */ private function get_og_type() { if (is_single()) { return 'article'; } elseif (is_page()) { return 'website'; } else { return 'website'; } } /** * Get Open Graph URL */ private function get_og_url() { if (is_singular()) { return get_permalink(); } else { global $wp; return home_url(add_query_arg(array(), $wp->request)); } } /** * Get Open Graph site name */ private function get_og_site_name() { $settings = $this->get_settings(); return !empty($settings['site_name']) ? $settings['site_name'] : get_bloginfo('name'); } /** * Get Open Graph image data */ private function get_og_image() { $settings = $this->get_settings(); // Try featured image first if (is_singular() && has_post_thumbnail()) { $image_id = get_post_thumbnail_id(); $image = wp_get_attachment_image_src($image_id, 'full'); if ($image) { $metadata = wp_get_attachment_metadata($image_id); return array( 'url' => $image[0], 'width' => $image[1], 'height' => $image[2], 'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true) ); } } // Fallback to default image if (!empty($settings['default_image'])) { $image_id = attachment_url_to_postid($settings['default_image']); if ($image_id) { $image = wp_get_attachment_image_src($image_id, 'full'); if ($image) { return array( 'url' => $image[0], 'width' => $image[1], 'height' => $image[2], 'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true) ); } } else { // Direct URL return array( 'url' => $settings['default_image'], 'width' => $settings['image_width'], 'height' => $settings['image_height'], 'alt' => get_bloginfo('name') . ' - Default Image' ); } } return false; } /** * Get article-specific Open Graph data */ private function get_article_data() { $data = array(); if (is_single()) { $post = get_post(); // Article author $data['article:author'] = get_author_posts_url($post->post_author); // Article published time $data['article:published_time'] = get_the_date('c'); // Article modified time if (get_the_modified_date('c') !== get_the_date('c')) { $data['article:modified_time'] = get_the_modified_date('c'); } // Article section (category) $categories = get_the_category(); if (!empty($categories)) { $data['article:section'] = $categories[0]->name; } // Article tags $tags = get_the_tags(); if (!empty($tags)) { foreach ($tags as $tag) { $data['article:tag'] = $tag->name; } } } return $data; } /** * Validate Facebook 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]; $size = strlen(file_get_contents($image_url)); // Check minimum dimensions if ($width < 200 || $height < 200) { $errors[] = __('Image must be at least 200x200 pixels.', 'tigerstyle-heat'); } // Check recommended dimensions if ($width < 600 || $height < 315) { $errors[] = __('Warning: Image should be at least 600x315 pixels for optimal display.', 'tigerstyle-heat'); } // Check file size if ($size > 8 * 1024 * 1024) { // 8MB $errors[] = __('Image file size must be under 8MB.', 'tigerstyle-heat'); } // Check aspect ratio $ratio = $width / $height; if ($ratio < 1.5 || $ratio > 2.5) { $errors[] = __('Warning: Recommended aspect ratio is 1.91:1 (1200x630 pixels).', 'tigerstyle-heat'); } return $errors; } /** * Register admin settings */ public function register_settings() { register_setting( 'tigerstyle_heat_facebook', $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_open_graph', 'enable_article_tags', 'enable_video_tags', 'enable_crawler_optimization'); foreach ($boolean_fields as $field) { $clean_settings[$field] = !empty($settings[$field]); } // Text settings $text_fields = array('app_id', 'admins', 'domain_verification', 'site_name', 'default_image'); foreach ($text_fields as $field) { $clean_settings[$field] = sanitize_text_field($settings[$field] ?? ''); } // Numeric settings $clean_settings['image_width'] = absint($settings['image_width'] ?? 1200); $clean_settings['image_height'] = absint($settings['image_height'] ?? 630); return $clean_settings; } /** * Register REST API routes for testing */ public function register_rest_routes() { register_rest_route('tigerstyle-heat/v1', '/facebook/debug', array( 'methods' => 'GET', 'callback' => array($this, 'debug_open_graph'), 'permission_callback' => function() { return current_user_can('manage_options'); } )); } /** * Debug Open Graph data (REST endpoint) */ public function debug_open_graph($request) { $url = $request->get_param('url'); if (empty($url)) { $url = home_url(); } // Get Open Graph data for the URL $og_data = $this->get_open_graph_data(); return rest_ensure_response(array( 'url' => $url, 'open_graph_data' => $og_data, 'facebook_debugger_url' => 'https://developers.facebook.com/tools/debug/?q=' . urlencode($url), 'validation_notes' => $this->get_validation_notes($og_data) )); } /** * Get validation notes for Open Graph data */ private function get_validation_notes($og_data) { $notes = array(); // Check required fields $required_fields = array('og:title', 'og:description', 'og:image', 'og:url'); foreach ($required_fields as $field) { if (empty($og_data[$field])) { $notes[] = sprintf(__('Missing required field: %s', 'tigerstyle-heat'), $field); } } // Check image if (!empty($og_data['og:image'])) { $image_errors = $this->validate_image($og_data['og:image']); $notes = array_merge($notes, $image_errors); } return $notes; } /** * Get Facebook webmaster tools URLs */ public function get_webmaster_tools_urls() { return array( 'sharing_debugger' => 'https://developers.facebook.com/tools/debug/', 'webmaster_tool' => 'https://developers.facebook.com/webmaster', 'domain_verification' => 'https://business.facebook.com/settings/brand-safety', 'best_practices' => 'https://developers.facebook.com/docs/sharing/best-practices', 'open_graph_docs' => 'https://developers.facebook.com/docs/sharing/webmasters' ); } /** * Render admin page */ public function render_admin_page() { // Handle form submission if (isset($_POST['submit_facebook_settings']) && wp_verify_nonce($_POST['facebook_nonce'], 'facebook_settings')) { $this->handle_settings_update(); } $settings = $this->get_settings(); $webmaster_urls = $this->get_webmaster_tools_urls(); ?>


x px


Brand Safety > Domains to get your verification code.', 'tigerstyle-heat'); ?>

validate_image($new_settings['default_image']); if (!empty($image_errors)) { // Show validation warnings add_action('admin_notices', function() use ($image_errors) { echo '

' . __('Image Validation Warnings:', 'tigerstyle-heat') . '

'; }); } } // Save settings $this->update_settings($new_settings); // Redirect with success message wp_redirect(add_query_arg('message', 'facebook_updated', wp_get_referer())); exit; } }