tigerstyle-heat/includes/modules/class-facebook.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

744 lines
29 KiB
PHP

<?php
/**
* Facebook Integration Module
*
* Handles Facebook Open Graph optimization, domain verification,
* and sharing optimization features.
*
* @package TigerStyleSEO
* @subpackage Modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Facebook Integration Class
*/
class TigerStyleSEO_Facebook {
/**
* Single instance of the class
*/
private static $instance = null;
/**
* Option name for Facebook settings
*/
private $option_name = 'tigerstyle_heat_facebook';
/**
* Default settings
*/
private $defaults = array(
'enable_open_graph' => 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(
'<meta name="facebook-domain-verification" content="%s" />' . "\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(
'<meta property="%s" content="%s" />' . "\n",
esc_attr($property),
esc_attr($content)
);
}
}
// Facebook-specific tags
if (!empty($settings['app_id'])) {
printf(
'<meta property="fb:app_id" content="%s" />' . "\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(
'<meta property="fb:admins" content="%s" />' . "\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();
?>
<div class="seo-info-box">
<h3><?php _e('Facebook Integration & Open Graph Optimization', 'tigerstyle-heat'); ?></h3>
<p><?php _e('Configure Facebook Open Graph tags, domain verification, and sharing optimization for better social media engagement.', 'tigerstyle-heat'); ?></p>
<div class="seo-setup-steps">
<h4><?php _e('Quick Setup Steps:', 'tigerstyle-heat'); ?></h4>
<ol>
<li><?php _e('Enable Open Graph tags below', 'tigerstyle-heat'); ?></li>
<li><?php _e('Set a default sharing image (1200x630px recommended)', 'tigerstyle-heat'); ?></li>
<li><?php _e('Add your Facebook App ID and admin IDs', 'tigerstyle-heat'); ?></li>
<li><?php _e('Configure domain verification meta tag', 'tigerstyle-heat'); ?></li>
<li><?php _e('Test your pages using Facebook\'s Sharing Debugger', 'tigerstyle-heat'); ?></li>
</ol>
</div>
<div style="margin-top: 20px;">
<strong><?php _e('Facebook Webmaster Tools:', 'tigerstyle-heat'); ?></strong>
<ul style="margin-left: 20px;">
<li><a href="<?php echo esc_url($webmaster_urls['sharing_debugger']); ?>" target="_blank"><?php _e('Sharing Debugger', 'tigerstyle-heat'); ?></a> - <?php _e('Test and refresh your Open Graph data', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($webmaster_urls['webmaster_tool']); ?>" target="_blank"><?php _e('Webmaster Tool', 'tigerstyle-heat'); ?></a> - <?php _e('Monitor domain crawling and sharing metrics', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($webmaster_urls['domain_verification']); ?>" target="_blank"><?php _e('Domain Verification', 'tigerstyle-heat'); ?></a> - <?php _e('Verify your domain ownership', 'tigerstyle-heat'); ?></li>
<li><a href="<?php echo esc_url($webmaster_urls['best_practices']); ?>" target="_blank"><?php _e('Best Practices Guide', 'tigerstyle-heat'); ?></a> - <?php _e('Official Facebook sharing guidelines', 'tigerstyle-heat'); ?></li>
</ul>
</div>
</div>
<form method="post" action="">
<?php wp_nonce_field('facebook_settings', 'facebook_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Enable Open Graph Tags', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_open_graph" value="1" <?php checked($settings['enable_open_graph']); ?> />
<?php _e('Generate Open Graph meta tags for Facebook sharing', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Automatically adds og:title, og:description, og:image, and other Open Graph tags to your pages.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Default Sharing 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/image.jpg" />
<p class="description">
<?php _e('Fallback image when no featured image is set. Recommended: 1200x630px, under 8MB.', 'tigerstyle-heat'); ?><br>
<strong><?php _e('Facebook Image Requirements:', 'tigerstyle-heat'); ?></strong>
<ul style="margin: 5px 0 0 20px;">
<li><?php _e('Minimum: 200x200px', 'tigerstyle-heat'); ?></li>
<li><?php _e('Recommended: 1200x630px (1.91:1 ratio)', 'tigerstyle-heat'); ?></li>
<li><?php _e('Maximum file size: 8MB', 'tigerstyle-heat'); ?></li>
<li><?php _e('Supported formats: JPG, PNG, GIF', 'tigerstyle-heat'); ?></li>
</ul>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Image Dimensions', 'tigerstyle-heat'); ?></th>
<td>
<input type="number" name="image_width" value="<?php echo esc_attr($settings['image_width']); ?>" min="200" max="2048" style="width: 80px;" /> x
<input type="number" name="image_height" value="<?php echo esc_attr($settings['image_height']); ?>" min="200" max="2048" style="width: 80px;" /> px
<p class="description"><?php _e('Default dimensions for your sharing images. Facebook recommends 1200x630px.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Site Name', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="site_name" value="<?php echo esc_attr($settings['site_name']); ?>" class="regular-text" placeholder="<?php echo esc_attr(get_bloginfo('name')); ?>" />
<p class="description"><?php _e('The name of your website. Leave blank to use your site title.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Facebook App ID', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="app_id" value="<?php echo esc_attr($settings['app_id']); ?>" class="regular-text" placeholder="1234567890123456" />
<p class="description">
<?php _e('Your Facebook App ID for analytics and insights.', 'tigerstyle-heat'); ?>
<a href="https://developers.facebook.com/apps/" target="_blank"><?php _e('Create an app', 'tigerstyle-heat'); ?></a>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Facebook Admin IDs', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="admins" value="<?php echo esc_attr($settings['admins']); ?>" class="regular-text" placeholder="123456789,987654321" />
<p class="description">
<?php _e('Comma-separated list of Facebook user IDs who can moderate your content.', 'tigerstyle-heat'); ?>
<a href="https://findmyfbid.com/" target="_blank"><?php _e('Find your Facebook ID', 'tigerstyle-heat'); ?></a>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Domain Verification', 'tigerstyle-heat'); ?></th>
<td>
<input type="text" name="domain_verification" value="<?php echo esc_attr($settings['domain_verification']); ?>" class="regular-text" placeholder="abc123def456ghi789" />
<p class="description">
<?php _e('Domain verification meta tag content from Facebook Business Manager.', 'tigerstyle-heat'); ?><br>
<?php _e('Go to Business Settings > Brand Safety > Domains to get your verification code.', 'tigerstyle-heat'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Article Tags', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_article_tags" value="1" <?php checked($settings['enable_article_tags']); ?> />
<?php _e('Add article-specific Open Graph tags (author, publish date, categories)', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Includes article:author, article:published_time, article:section, and article:tag for blog posts.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Crawler Optimization', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="enable_crawler_optimization" value="1" <?php checked($settings['enable_crawler_optimization']); ?> />
<?php _e('Optimize for Facebook crawlers (FacebookExternalHit)', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Ensures Open Graph tags are placed within the first 1MB of your page content as recommended by Facebook.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
</table>
<div style="margin-top: 20px; padding: 20px; background: #f0f8ff; border: 1px solid #cce7ff; border-radius: 5px;">
<h4><?php _e('Facebook Crawler Information', 'tigerstyle-heat'); ?></h4>
<p><?php _e('Facebook uses several crawlers to index your content:', 'tigerstyle-heat'); ?></p>
<ul style="margin-left: 20px;">
<li><strong>facebookexternalhit/1.1</strong> - <?php _e('Main sharing crawler for Open Graph data', 'tigerstyle-heat'); ?></li>
<li><strong>meta-webindexer/1.1</strong> - <?php _e('AI search indexing crawler', 'tigerstyle-heat'); ?></li>
<li><strong>meta-externalads/1.1</strong> - <?php _e('Advertising and business products', 'tigerstyle-heat'); ?></li>
</ul>
<h4 style="margin-top: 15px;"><?php _e('Testing Your Setup', 'tigerstyle-heat'); ?></h4>
<p><?php _e('After saving these settings, test your pages:', 'tigerstyle-heat'); ?></p>
<ol style="margin-left: 20px;">
<li><?php _e('Visit the', 'tigerstyle-heat'); ?> <a href="<?php echo esc_url($webmaster_urls['sharing_debugger']); ?>" target="_blank"><?php _e('Facebook Sharing Debugger', 'tigerstyle-heat'); ?></a></li>
<li><?php _e('Enter your page URL to see how it will appear when shared', 'tigerstyle-heat'); ?></li>
<li><?php _e('Click "Scrape Again" to refresh the data after making changes', 'tigerstyle-heat'); ?></li>
</ol>
</div>
<?php submit_button(__('Save Facebook Settings', 'tigerstyle-heat'), 'primary', 'submit_facebook_settings'); ?>
</form>
<?php
}
/**
* Handle settings update
*/
private function handle_settings_update() {
// Validate and save settings
$new_settings = array();
// Boolean settings
$new_settings['enable_open_graph'] = !empty($_POST['enable_open_graph']);
$new_settings['enable_article_tags'] = !empty($_POST['enable_article_tags']);
$new_settings['enable_crawler_optimization'] = !empty($_POST['enable_crawler_optimization']);
// Text settings
$new_settings['default_image'] = esc_url_raw($_POST['default_image'] ?? '');
$new_settings['app_id'] = sanitize_text_field($_POST['app_id'] ?? '');
$new_settings['admins'] = sanitize_text_field($_POST['admins'] ?? '');
$new_settings['domain_verification'] = sanitize_text_field($_POST['domain_verification'] ?? '');
$new_settings['site_name'] = sanitize_text_field($_POST['site_name'] ?? '');
// Numeric settings
$new_settings['image_width'] = absint($_POST['image_width'] ?? 1200);
$new_settings['image_height'] = absint($_POST['image_height'] ?? 630);
// 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', 'facebook_updated', wp_get_referer()));
exit;
}
}