admin_instance = $admin; } // ──────────────────────────────────────────────────────────────── // Accordion / Section helpers // ──────────────────────────────────────────────────────────────── public function get_accordion_sections_config() { return array( 'connection' => array( 'title' => 'Connection & Authentication', 'description' => 'Manage your API connection and authentication settings', 'icon' => 'admin-network', 'priority' => 10, 'default_open' => true, 'fields' => array( 'searchatlas_api_key', 'apikey' ) ), 'otto_ssr' => array( 'title' => Metasync::get_whitelabel_otto_name() . ' Server-Side Rendering', 'description' => 'Configure ' . Metasync::get_whitelabel_otto_name() . ' rendering and display options', 'icon' => 'performance', 'priority' => 20, 'default_open' => false, 'fields' => array( 'otto_pixel_uuid', 'otto_disable_on_loggedin', 'otto_disable_preview_button', 'otto_wp_rocket_compat', ) ), 'edge_cache' => array( 'title' => 'Edge Cache / CDN', 'description' => 'Configure CDN credentials for cache-tag purging', 'icon' => 'admin-site-alt3', 'priority' => 22, 'default_open' => false, 'render_callback' => array('Metasync_Edge_Cache_Settings', 'render'), ), 'bot_detection' => array( 'title' => 'Bot Detection & Filtering', 'description' => 'Manage bot traffic and reduce unnecessary API calls', 'icon' => 'filter', 'priority' => 25, 'default_open' => false, 'fields' => array( 'otto_disable_for_bots', 'otto_bot_whitelist', 'otto_bot_blacklist', 'otto_bot_statistics_link' ) ), 'breadcrumbs' => array( 'title' => 'Breadcrumbs', 'description' => 'Configure breadcrumb navigation display', 'icon' => 'format-links', 'priority' => 35, 'default_open' => false, 'render_callback' => array($this, 'render_breadcrumbs_section') ), 'open_graph' => array( 'title' => 'Open Graph', 'description' => 'Control which Open Graph and article meta tags are output', 'icon' => 'share', 'priority' => 38, 'default_open' => false, 'fields' => array( 'og_image_dimensions', 'article_timestamps', 'article_author', 'article_section', 'article_tags', 'twitter_image_alt' ) ), 'editor_settings' => array( 'title' => 'Post/Page Editor Settings', 'description' => 'Customize meta boxes and editor functionality', 'icon' => 'edit', 'priority' => 40, 'default_open' => false, 'fields' => array( 'disable_common_robots_metabox', 'disable_advance_robots_metabox', 'disable_redirection_metabox', 'disable_canonical_metabox', 'disable_social_opengraph_metabox', 'disable_schema_markup_metabox', 'disable_seo_metabox', 'open_external_links' ) ), 'user_management' => array( 'title' => 'User Management for Content', 'description' => 'Configure which users are allowed to be authors of content synced.', 'icon' => 'groups', 'priority' => 50, 'default_open' => false, 'fields' => array( 'content_genius_sync_roles' ) ), 'content_rendering' => array( 'title' => 'Content Rendering', 'description' => 'Configure how synced content is stored and rendered by your page builder', 'icon' => 'admin-appearance', 'priority' => 55, 'default_open' => false, 'fields' => array( 'default_page_builder' ) ), 'llms_txt' => array( 'title' => 'LLMs.txt', 'description' => 'Publish a standards-compliant /llms.txt so AI search engines and assistants can discover your content', 'icon' => 'media-text', 'priority' => 56, 'default_open' => false, 'render_callback' => array($this, 'render_llms_txt_section'), ), 'advanced' => array( 'title' => 'Plugin Settings', 'description' => 'System configuration and maintenance options', 'icon' => 'admin-settings', 'priority' => 60, 'default_open' => false, 'fields' => array( 'permalink_structure', 'hide_dashboard_framework', 'show_admin_bar_status', 'enable_auto_updates', 'import_external_data' ) ) ); } public function get_advanced_accordion_config() { $config = array(); $user = wp_get_current_user(); if (in_array('administrator', (array) $user->roles)) { $config['plugin_access'] = array( 'title' => 'User Roles with Plugin Access', 'description' => 'Control which user roles can see and access this plugin', 'icon' => 'admin-users', 'priority' => 5, 'default_open' => false, 'render_callback' => array($this, 'render_plugin_access_roles_section') ); } $config['debug_mode'] = array( 'title' => 'Debug Mode', 'description' => 'Manage debug mode with automatic disable and safety limits', 'icon' => 'info', 'priority' => 8, 'default_open' => false, 'render_callback' => array($this->admin_instance, 'render_debug_mode_section') ); $config['error_logs'] = array( 'title' => 'Error Logs', 'description' => 'View and manage error logs to troubleshoot issues', 'icon' => 'warning', 'priority' => 10, 'default_open' => true, 'render_callback' => array($this->admin_instance, 'render_error_log_content') ); $config['execution_settings'] = array( 'title' => 'Execution Settings', 'description' => 'Configure resource limits and execution parameters', 'icon' => 'controls-play', 'priority' => 15, 'default_open' => false, 'render_callback' => array($this, 'render_execution_settings_section') ); $config['otto_cache'] = array( 'title' => 'Cache Management', 'description' => 'Manage ' . Metasync::get_whitelabel_otto_name() . ' cache and clear all cache plugins', 'icon' => 'database', 'priority' => 20, 'default_open' => false, 'render_callback' => array($this->admin_instance, 'render_otto_cache_management') ); $config['db_cleanup'] = array( 'title' => 'Database Cleanup', 'description' => 'Remove orphaned data and schedule automated weekly cleanup', 'icon' => 'trash', 'priority' => 25, 'default_open' => false, 'render_callback' => array($this->admin_instance, 'render_db_cleanup_section') ); $config['performance'] = array( 'title' => 'Performance & CPU Load', 'description' => 'Configure CPU load thresholds to prevent processing during peak server load', 'icon' => 'chart-line', 'priority' => 27, 'default_open' => false, 'render_callback' => array($this->admin_instance, 'render_cpu_monitor_section') ); $config['reset_settings'] = array( 'title' => 'Reset Plugin Settings', 'description' => 'Reset all plugin settings to default values', 'icon' => 'update', 'priority' => 30, 'default_open' => false, 'render_callback' => array($this, 'render_reset_settings_section') ); return $config; } public function render_advanced_accordion() { $sections_config = $this->get_advanced_accordion_config(); uasort($sections_config, function($a, $b) { return $a['priority'] - $b['priority']; }); echo '
'; foreach ($sections_config as $section_key => $section_data) { $section_id = 'metasync-advanced-section-' . $section_key; $is_open = $section_data['default_open']; $aria_expanded = $is_open ? 'true' : 'false'; $content_state = $is_open ? 'open' : 'closed'; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '

' . esc_html($section_data['title']) . '

'; echo '

' . esc_html($section_data['description']) . '

'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; if (isset($section_data['render_callback']) && is_callable($section_data['render_callback'])) { call_user_func($section_data['render_callback']); } echo '
'; echo '
'; } echo '
'; } public function render_reset_settings_section() { ?>

Important Warning

This action will permanently delete:

You will need to reconfigure the plugin completely after this reset.

get_service_account_info(); $is_configured = !isset($service_info['error']); $saved_json_display = $is_configured ? $google_index->get_redacted_config_json() : ''; include plugin_dir_path(dirname(__FILE__)) . 'views/metasync-google-index-api-settings.php'; // Render post types selection $options = get_option('metasync_options_instant_indexing', ['post_types' => []]); $post_types_settings = isset($options['post_types']) && is_array($options['post_types']) ? $options['post_types'] : []; include plugin_dir_path(dirname(__FILE__)) . 'views/metasync-google-instant-post-types.php'; } public function render_bing_index_section() { require_once plugin_dir_path(dirname(__FILE__)) . 'bing-index/class-metasync-bing-instant-index.php'; $bing_index = new Metasync_Bing_Instant_Index(); $api_key = $bing_index->get_setting('api_key'); $endpoint = $bing_index->get_setting('endpoint', 'indexnow'); $post_types_settings = $bing_index->get_setting('post_types', []); $is_configured = !empty($api_key); $post_types = get_post_types(['public' => true], 'objects'); ?>
About IndexNow Protocol

IndexNow is a simple protocol that allows websites to instantly notify search engines about URL changes. It's supported by Bing, Yandex, Naver, Seznam, and other search engines.

  • ✓ Instant notification to multiple search engines
  • ✓ Simple API key authentication (no OAuth required)
  • ✓ Supports batch URL submissions (up to 10,000 URLs)
  • ✓ Free to use with no quotas
  • ✓ Fire-and-forget protocol (no status checking needed)

Resources: IndexNow Documentation | Bing Webmaster Tools

IndexNow API Configured
API Key:

Configuration Required
Configure your IndexNow API key below to enable instant indexing with Bing and other search engines.


Your IndexNow API key is required to submit URLs for instant indexing. You can generate a random key or use your own (32+ character hexadecimal string). After saving, a verification file will be automatically created at your site root.

Select which endpoint to use for submitting URLs. The IndexNow.org endpoint is recommended as it notifies multiple search engines at once.

Selected post types will be automatically submitted to IndexNow when published or updated.

roles; $all_roles_checked = is_array($selected_roles) && in_array('all', $selected_roles); ?>

Select which user roles can see and access this plugin's menu, settings, and options in the WordPress admin area.


$role_details) { if ($role_key === 'administrator') { continue; } if (!isset($role_details['name'])) { continue; } $is_checked = is_array($selected_roles) && in_array($role_key, $selected_roles); $role_name = translate_user_role($role_details['name']); ?>

How it works:
Administrators always have access to this plugin regardless of settings.
By default, only Administrators can access the plugin (no roles selected).
If "All Roles" is selected, all users will see the plugin.
Users with unchecked roles will not see the plugin in their WordPress admin area.

30, 'max_memory_limit' => 256, 'log_batch_size' => 1000, 'action_scheduler_batches' => 1, 'otto_rate_limit' => 10, 'otto_cache_ttl' => 30, 'queue_cleanup_days' => 31 ); } public function get_execution_setting($key, $default = null) { $settings = get_option('metasync_execution_settings', array()); $defaults = $this->get_default_execution_settings(); if (empty($settings)) { return isset($defaults[$key]) ? $defaults[$key] : $default; } return isset($settings[$key]) ? $settings[$key] : (isset($defaults[$key]) ? $defaults[$key] : $default); } public function get_all_execution_settings() { $settings = get_option('metasync_execution_settings', array()); $defaults = $this->get_default_execution_settings(); return wp_parse_args($settings, $defaults); } public function can_change_memory_limit() { $current_limit = ini_get('memory_limit'); $test_result = @ini_set('memory_limit', $current_limit); return $test_result !== false; } public function get_server_limits() { $max_execution_time = ini_get('max_execution_time'); $memory_limit = ini_get('memory_limit'); // Prefer the server-configured value: WordPress (wp_raise_memory_limit) // or this plugin (apply_memory_limit) may have already changed the // runtime limit via ini_set, so ini_get no longer reflects the server // configuration. get_cfg_var reads the php.ini value untouched. $config_memory_limit = get_cfg_var('memory_limit'); if ($config_memory_limit !== false && $config_memory_limit !== '') { $memory_limit = $config_memory_limit; } $memory_limit_mb = $this->parse_memory_limit_to_mb($memory_limit); $can_change_memory = $this->can_change_memory_limit(); $memory_limit_display = $memory_limit_mb == -1 ? 'Unlimited' : $memory_limit_mb . ' MB'; return array( 'max_execution_time' => $max_execution_time == -1 ? 'Unlimited' : $max_execution_time . ' seconds', 'memory_limit' => $memory_limit_display, 'max_execution_time_raw' => $max_execution_time, 'memory_limit_raw' => $memory_limit_mb, 'can_change_memory' => $can_change_memory ); } /** * Allowed range for the Max Memory Limit setting: 64 MB up to the * server-configured memory limit (null max when the server is unlimited). */ public function get_memory_limit_allowed_range() { $server_limits = $this->get_server_limits(); $max = $server_limits['memory_limit_raw'] == -1 ? null : max(64, (int) $server_limits['memory_limit_raw']); return array( 'min' => 64, 'max' => $max ); } public function apply_memory_limit() { if (!$this->can_change_memory_limit()) { return false; } $memory_limit_mb = $this->get_execution_setting('max_memory_limit'); $result = @ini_set('memory_limit', $memory_limit_mb . 'M'); return $result !== false; } public function parse_memory_limit_to_mb($memory_limit) { if ($memory_limit == -1 || $memory_limit == '-1') { return -1; } $memory_limit = trim($memory_limit); $last = strtolower(substr($memory_limit, -1)); $value = (int) $memory_limit; switch ($last) { case 'g': $value *= 1024; break; case 'm': break; case 'k': $value /= 1024; break; } return $value; } public function render_execution_settings_section() { $settings = $this->get_all_execution_settings(); $server_limits = $this->get_server_limits(); $memory_range = $this->get_memory_limit_allowed_range(); $warnings = array(); if ((int)$server_limits['max_execution_time_raw'] !== -1 && (int)$settings['max_execution_time'] > (int)$server_limits['max_execution_time_raw']) { $warnings['max_execution_time'] = true; } if ($server_limits['memory_limit_raw'] != -1 && $settings['max_memory_limit'] > $server_limits['memory_limit_raw']) { $warnings['max_memory_limit'] = true; } ?>

Configure resource limits and execution parameters for plugin operations. Adjust these settings based on your server capabilities.

Execution & Memory

seconds

Server Limit:

max="" readonly disabled required style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: ; color: ; cursor: ;" /> MB

Server Limit: · Allowed:

Server does not allow changing memory limit. This setting is read-only.

Batch Processing

lines

Higher values increase server load

API Rate Limiting

days
sprintf('Connect your WordPress site to your %s dashboard to retrieve your API key and OTTO UUID. This does not create a WordPress login session.', $plugin_name), 'apikey' => sprintf('Auto-generated authentication token used for secure API communication between your WordPress site and %s services. You can refresh this token if needed for security purposes.', $plugin_name), 'otto_pixel_uuid' => sprintf('Your unique %s tracking pixel identifier. This UUID is used to track %s modifications and analytics on your website pages.', $otto_name, $otto_name), 'otto_disable_on_loggedin' => sprintf('Disable %s modifications when you are logged in to WordPress. This allows you to see and edit the original content without %s\'s enhancements during editing sessions.', $otto_name, $otto_name), 'otto_disable_preview_button' => sprintf('Hide the %s frontend toolbar that displays the status indicator, preview button, and debug button. Enable this for a cleaner frontend experience.', $otto_name), 'otto_wp_rocket_compat' => sprintf('WP Rocket Compatibility Mode: Controls how %s interacts with WP Rocket. "Auto" (recommended) allows both to work together by avoiding DONOTCACHEPAGE constant unless necessary for Brizy pages or SG Optimizer conflicts. This ensures WP Rocket\'s JavaScript delay and optimization features continue working.', $otto_name), 'otto_disable_for_bots' => sprintf('Enable bot detection to automatically skip %s processing for search engine crawlers, SEO tools, and other bots. This reduces unnecessary API calls and improves performance.', $otto_name), 'otto_bot_whitelist' => sprintf('Enter bot names or user-agent patterns (one per line) that should always be processed by %s, even when "Disable for Bots" is enabled. For example: Googlebot, Bingbot. This allows you to ensure specific search engines always see your optimized content.', $otto_name), 'otto_bot_blacklist' => sprintf('Enter bot names or user-agent patterns (one per line) that should always be blocked from %s processing, regardless of other settings. For example: BadBot, MaliciousCrawler. Use this to exclude problematic crawlers or unwanted traffic sources.', $otto_name), 'otto_bot_statistics_link' => 'View detailed bot detection statistics including total hits, API calls saved, breakdown by bot type, and unique bot entries with hit counts.', 'disable_common_robots_metabox' => 'Hide the Common Robots meta box from post and page edit screens. This removes the robots meta tag controls (index/noindex, follow/nofollow) from the editor interface.', 'disable_advance_robots_metabox' => 'Hide the Advanced Robots meta box from post and page edit screens. This removes advanced robots directives like max-snippet, max-image-preview, and max-video-preview settings.', 'disable_redirection_metabox' => 'Hide the Redirection meta box from post and page edit screens. This removes the URL redirect configuration options from the editor interface.', 'disable_canonical_metabox' => 'Hide the Canonical URL meta box from post and page edit screens. This removes the canonical URL override field from the editor interface.', 'disable_social_opengraph_metabox' => 'Hide the Social Media & Open Graph meta box from post and page edit screens. This removes Facebook, Twitter, and other social media meta tag controls from the editor.', 'disable_schema_markup_metabox' => 'Hide the Schema Markup meta box from post and page edit screens. This removes the structured data (Article, FAQ, Product, Recipe, etc.) configuration from the editor interface.', 'disable_seo_metabox' => 'Hide the SEO Title & Meta Description meta box from post and page edit screens. This removes the Classic editor SEO fields (the Gutenberg sidebar is unaffected).', 'open_external_links' => 'Automatically add target="_blank" attribute to external links appearing in your posts, pages, and other post types when rendered by Otto.', 'content_genius_sync_roles' => 'Select which WordPress user roles should be synchronized with Content Genius. This determines which users will have their profiles and permissions synced for content collaboration.', 'permalink_structure' => sprintf('Displays your current WordPress permalink structure. %s works best with pretty permalinks (not "Plain"). If you see a warning, visit Settings > Permalinks to change your structure.', $plugin_name), 'hide_dashboard_framework' => sprintf('Hide the main %s dashboard from the WordPress admin menu. This is useful if you want to reduce menu clutter but still keep the plugin active.', $plugin_name), 'show_admin_bar_status' => sprintf('Display the %s status indicator in the WordPress admin bar at the top of your screen. This provides quick visibility of plugin status and key metrics.', $plugin_name), 'enable_auto_updates' => sprintf('Allow WordPress to automatically update the %s plugin when new versions are released. Recommended for security patches, but you may prefer manual updates for major versions.', $plugin_name), 'import_external_data' => sprintf('Import your existing SEO settings and metadata from other popular SEO plugins like Yoast, Rank Math, or All in One SEO. This makes migration to %s seamless without losing your SEO data.', $plugin_name), 'import_seo_metadata' => 'Migrate your existing SEO titles and meta descriptions from Yoast, Rank Math, or All in One SEO. This one-click import preserves your search rankings by copying your optimized meta data to MetaSync, even if the source plugin is deactivated.', 'default_page_builder' => 'Choose how Content Genius stores synced articles. "Gutenberg" works with all themes and page builders. Selecting a specific builder converts content to that builder\'s widget format, which inherits the builder\'s global typography and layout styles.' ); } public function get_field_section($field_id) { $sections = $this->get_accordion_sections_config(); foreach ($sections as $section_key => $section_data) { if (in_array($field_id, $section_data['fields'])) { return $section_key; } } return null; } public function render_accordion_sections($page) { global $wp_settings_sections, $wp_settings_fields; if (!isset($wp_settings_fields[$page])) { return; } $sections_config = $this->get_accordion_sections_config(); echo '
'; uasort($sections_config, function($a, $b) { return $a['priority'] - $b['priority']; }); foreach ($sections_config as $section_key => $section_data) { $section_id = 'metasync-section-' . $section_key; $is_open = $section_data['default_open']; $aria_expanded = $is_open ? 'true' : 'false'; $content_state = $is_open ? 'open' : 'closed'; echo '
'; echo '
'; echo '
'; echo ''; echo '
'; echo '

' . esc_html($section_data['title']) . '

'; echo '

' . esc_html($section_data['description']) . '

'; echo '
'; echo '
'; echo ''; echo '
'; echo '
'; if (isset($section_data['render_callback']) && is_callable($section_data['render_callback'])) { call_user_func($section_data['render_callback']); } elseif (isset($section_data['fields']) && is_array($section_data['fields'])) { echo ''; $tooltips = $this->get_field_tooltips(); foreach ($section_data['fields'] as $field_id) { if (isset($wp_settings_fields[$page]['metasync_settings'][$field_id])) { $field = $wp_settings_fields[$page]['metasync_settings'][$field_id]; echo ''; echo ''; echo ''; echo ''; } } echo ''; } echo '
'; echo '
'; } echo '
'; } public function render_llms_txt_section() { global $wp_settings_fields; $page = Metasync_Admin::$page_slug . '_general'; $section = Metasync_Admin::SECTION_LLMS_TXT; if (empty($wp_settings_fields[$page][$section])) { return; } echo ''; foreach ($wp_settings_fields[$page][$section] as $field) { echo ''; echo ''; echo ''; echo ''; } echo ''; ?> ', esc_attr($display_value) ); printf(''); printf('

%s %s

', $status_message, $refresh_help); } public function linkgraph_token_callback() { printf( '', isset(Metasync::get_option('general')['linkgraph_token']) ? esc_attr(Metasync::get_option('general')['linkgraph_token']) : '' ); printf( '', isset(Metasync::get_option('general')['linkgraph_customer_id']) ? esc_attr(Metasync::get_option('general')['linkgraph_customer_id']) : '' ); ?> diff($ago); $string = [ 'y' => 'year', 'm' => 'month', 'w' => 'week', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second', ]; foreach ($string as $k => &$v) { if (isset($diff->$k) && $diff->$k) { $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); } else { unset($string[$k]); } } if (!$full) $string = array_slice($string, 0, 1); return $string ? implode(', ', $string) . ' ago' : 'just now'; } public function searchatlas_api_key_callback() { $current_api_key = isset(Metasync::get_option('general')['searchatlas_api_key']) ? esc_attr(Metasync::get_option('general')['searchatlas_api_key']) : ''; $otto_uuid = isset(Metasync::get_option('general')['otto_pixel_uuid']) ? Metasync::get_option('general')['otto_pixel_uuid'] : ''; $has_api_key = !empty($current_api_key); $has_otto_uuid = !empty($otto_uuid); $is_fully_connected = $this->admin_instance->is_heartbeat_connected(); printf('
'); printf('
'); printf('One-Click Authentication'); printf('
'); printf('
'); if ($is_fully_connected) { printf('Your %s account is fully synced with active heartbeat API. You can re-authenticate to refresh your connection or connect a different account.', esc_html(Metasync::get_effective_plugin_name())); } elseif ($has_api_key && !$has_otto_uuid) { printf('Your %s API key is configured, but %s UUID is missing. Please re-authenticate to complete the setup.', esc_html(Metasync::get_effective_plugin_name()), esc_html(Metasync::get_whitelabel_otto_name())); } else { printf('Connect your %s account with one click. This will automatically configure your API key and %s UUID below, enabling all plugin features.', esc_html(Metasync::get_effective_plugin_name()), esc_html(Metasync::get_whitelabel_otto_name())); } printf('
'); ?> '); printf(''); if ($has_api_key) { printf(''); } printf('
'); printf('
'); printf('
'); printf('Authentication Tips'); printf('
'); printf('• Make sure you have a %s account before connecting
', esc_html(Metasync::get_effective_plugin_name())); printf('• The authentication window will open in a popup - please allow popups
'); printf('• The process typically takes 15-30 seconds to complete
'); printf('• Your API key will be automatically filled in the field below
'); printf('• If you encounter issues, try disabling ad blockers temporarily
'); printf('• Contact %s if you need assistance', Metasync::SUPPORT_EMAIL, Metasync::SUPPORT_EMAIL); printf('
'); printf('
'); printf('
'); printf(''); printf('
'); printf(''); printf( '', $current_api_key ); printf('

'); if ($is_fully_connected) { printf('Your %s API key for secure communication with the platform. Use the authentication button above to refresh or change accounts.', esc_html(Metasync::get_effective_plugin_name())); } elseif ($has_api_key && !$has_otto_uuid) { printf('Your API key is configured but %s UUID is missing. Re-authenticate above to complete the setup and enable dashboard access.', esc_html(Metasync::get_whitelabel_otto_name())); } else { printf('This field will be automatically populated when you authenticate using the button above. You can also manually enter your API key if you have one.'); } printf('

'); ?>

Manual Authentication: If you manually enter your API Key and OTTO UUID, you also consent to the same AI-powered automation permissions described above.

'); if( isset(Metasync::get_option('general')['searchatlas_api_key'])&&Metasync::get_option('general')['searchatlas_api_key']!=''){ $timestamp = @Metasync::get_option('general')['send_auth_token_timestamp']; printf( '

%s (%s)

', esc_attr($timestamp), $this->time_elapsed_string($timestamp) ); } } public function bing_site_verification_callback() { printf( '', isset(Metasync::get_option('searchengines')['bing_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['bing_site_verification']) : '' ); printf('
Enter Bing Webmaster Tools verification code: '); printf(' Get from here
'); } public function yandex_site_verification_callback() { printf( '', isset(Metasync::get_option('searchengines')['yandex_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['yandex_site_verification']) : '' ); printf('
Enter Yandex verification code: '); printf(' Get from here
'); } public function google_site_verification_callback() { printf( '', isset(Metasync::get_option('searchengines')['google_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['google_site_verification']) : '' ); printf('
Enter Google Search Console verification code: '); printf(' Get from here
'); } public function pinterest_site_verification_callback() { printf( '', isset(Metasync::get_option('searchengines')['pinterest_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['pinterest_site_verification']) : '' ); printf('
Enter Pinterest verification code: '); printf(' Get from here
'); } public function local_seo_person_organization_callback() { $person_organization = Metasync::get_option('localseo')['local_seo_person_organization'] ?? ''; ?>
Choose whether the site represents a person or an organization. '); } public function local_seo_name_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_name']) ? esc_attr(Metasync::get_option('localseo')['local_seo_name']) : get_bloginfo() ); printf('
Your name or company name '); } public function local_seo_logo_callback() { $local_seo_logo = Metasync::get_option('localseo')['local_seo_logo'] ?? ''; printf( '', isset(Metasync::get_option('localseo')['local_seo_logo']) ? esc_attr(Metasync::get_option('localseo')['local_seo_logo']) : '' ); printf('
'); printf('

Min Size: 160Χ90px, Max Size: 1920X1080px.
A squared image is preferred by the search engines.

'); printf('', wp_get_attachment_image_src($local_seo_logo, 'medium')[0] ?? ''); $button_type = 'hidden'; if ($local_seo_logo) { $button_type = 'button'; } printf('', $button_type); } public function local_seo_url_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_url']) ? esc_attr(Metasync::get_option('localseo')['local_seo_url']) : home_url() ); printf('
URL of the item. '); } public function local_seo_email_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_email']) ? esc_attr(Metasync::get_option('localseo')['local_seo_email']) : '' ); printf('
Search engines display your email address. '); } public function local_seo_phone_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_phone']) ? esc_attr(Metasync::get_option('localseo')['local_seo_phone']) : '' ); printf('
Search engines may prominently display your contact phone number for mobile users. '); } public function local_seo_address_callback() { printf( '
', isset(Metasync::get_option('localseo')['address']['street']) ? esc_attr(Metasync::get_option('localseo')['address']['street']) : '' ); printf( '
', isset(Metasync::get_option('localseo')['address']['locality']) ? esc_attr(Metasync::get_option('localseo')['address']['locality']) : '' ); printf( '
', isset(Metasync::get_option('localseo')['address']['region']) ? esc_attr(Metasync::get_option('localseo')['address']['region']) : '' ); printf( '
', isset(Metasync::get_option('localseo')['address']['postalcode']) ? esc_attr(Metasync::get_option('localseo')['address']['postalcode']) : '' ); printf( '
', isset(Metasync::get_option('localseo')['address']['country']) ? esc_attr(Metasync::get_option('localseo')['address']['country']) : '' ); } public function local_seo_business_type_callback() { $types = self::get_business_types(); sort($types); $business_type = Metasync::get_option('localseo')['local_seo_business_type'] ?? ''; ?> ', count($opening_days)); printf(' '); printf('
Select opening hours. You can add multiple sets if you have different opening or closing hours on some days or if you have a mid-day break. Times are specified using 24:00 time. '); } public function local_seo_phone_numbers_callback() { $number_types = ['Customer Service', 'Technical Support', 'Billing Support', 'Bill Payment', 'Sales', 'Reservations', 'Credit Card Support', 'Emergency', 'Baggage Tracking', 'Roadside Assistance', 'Package Tracking']; $types = isset(Metasync::get_option('localseo')['phonetype']) ? Metasync::get_option('localseo')['phonetype'] : ''; $numbers = isset(Metasync::get_option('localseo')['phonenumber']) ? Metasync::get_option('localseo')['phonenumber'] : ''; ?> ', count($phone_numbers)); printf(' '); printf('
Search engines may prominently display your contact phone number for mobile users. '); } public function local_seo_price_range_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_price_range']) ? esc_attr(Metasync::get_option('localseo')['local_seo_price_range']) : '' ); printf('
The price range of the business, for example $$$. '); } public function local_seo_about_page_callback() { ?> Search engines tag your about us page. '); } public function local_seo_contact_page_callback() { ?> Search engines tag your contact page. '); } public function local_seo_map_key_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_map_key']) ? esc_attr(Metasync::get_option('localseo')['local_seo_map_key']) : '' ); printf('
An API Key is required to display embedded Google Maps on your site. '); } public function local_seo_geo_coordinates_callback() { printf( '', isset(Metasync::get_option('localseo')['local_seo_geo_coordinates']) ? esc_attr(Metasync::get_option('localseo')['local_seo_geo_coordinates']) : '' ); printf('
Latitude and longitude values separated by comma. '); } public function header_snippets_callback() { printf( '', isset(Metasync::get_option('codesnippets')['header_snippet']) ? esc_attr(Metasync::get_option('codesnippets')['header_snippet']) : '' ); } public function footer_snippets_callback() { printf( '', isset(Metasync::get_option('codesnippets')['footer_snippet']) ? esc_attr(Metasync::get_option('codesnippets')['footer_snippet']) : '' ); } public function no_index_posts_callback() { printf( '', isset(Metasync::get_option('optimal_settings')['no_index_posts']) && Metasync::get_option('optimal_settings')['no_index_posts'] == 'true' ? 'checked' : '' ); printf('
Setting empty archives to noindex is useful for avoiding indexation of thin content pages and dilution of page rank. As soon as a post is added, the page is updated to index. '); } public function no_follow_links_callback() { printf( '', isset(Metasync::get_option('optimal_settings')['no_follow_links']) && Metasync::get_option('optimal_settings')['no_follow_links'] == 'true' ? 'checked' : '' ); printf('
Automatically add rel="nofollow" attribute to external links appearing in your posts, pages, and other post types. The attribute is dynamically applied when the url is displayed'); } public function open_external_links_callback() { printf( '', isset(Metasync::get_option('optimal_settings')['open_external_links']) && Metasync::get_option('optimal_settings')['open_external_links'] == 'true' ? 'checked' : '' ); printf('
Automatically add target="_blank" attribute to external links appearing in your posts, pages, and other post types. The attribute is applied when the url is displayed.'); } public function add_alt_image_tags_callback() { printf( '', isset(Metasync::get_option('optimal_settings')['add_alt_image_tags']) && Metasync::get_option('optimal_settings')['add_alt_image_tags'] == 'true' ? 'checked' : '' ); printf('
Automatically add alt attribute to Image Tags appearing in your posts, pages, and other post types. The attribute is applied when the content is displayed.'); } public function add_title_image_tags_callback() { printf( '', isset(Metasync::get_option('optimal_settings')['add_title_image_tags']) && Metasync::get_option('optimal_settings')['add_title_image_tags'] == 'true' ? 'checked' : '' ); printf('
Automatically add title attribute to Image Tags appearing in your posts, pages, and other post types. The attribute is applied when the content is displayed.'); } public function site_type_callback() { $site_type = Metasync::get_option('optimal_settings')['site_info']['type'] ?? ''; $types = [ ['name' => 'Personal Blog', 'value' => 'blog'], ['name' => 'Community Blog/News Site', 'value' => 'news'], ['name' => 'Personal Portfolio', 'value' => 'portfolio'], ['name' => 'Small Business Site', 'value' => 'business'], ['name' => 'Webshop', 'value' => 'webshop'], ['name' => 'Other Personal Website', 'value' => 'otherpersonal'], ['name' => 'Other Business Website', 'value' => 'otherbusiness'], ]; ?> ', $company_name ? $company_name : get_bloginfo('name') ); } public function site_google_logo_callback() { $google_logo = Metasync::get_option('optimal_settings')['site_info']['google_logo'] ?? ''; printf( '', $google_logo ); printf('
'); printf('

Min Size: 160X90px, Max Size: 1920X1080px.
A squared image is preferred by the search engines.

'); printf('', wp_get_attachment_image_src($google_logo, 'medium')[0] ?? ''); $button_type = 'hidden'; if ($google_logo) { $button_type = 'button'; } printf('', $button_type); } public function site_social_share_image_callback() { $social_share_image = Metasync::get_option('optimal_settings')['site_info']['social_share_image'] ?? ''; printf( '', $social_share_image ); printf('
'); printf('

The recommended image size is 1200 x 630 pixels.
When a featured image or an OpenGraph Image is not set for individual posts/pages/CPTs, this image will be used as a fallback thumbnail when your post is shared on Facebook.

'); printf('', wp_get_attachment_image_src($social_share_image, 'medium')[0] ?? ''); $button_type = 'hidden'; if ($social_share_image) { $button_type = 'button'; } printf('', $button_type); } public function common_robot_meta_tags_callback() { $common_robots_meta = Metasync::get_option('common_robots_meta') ?? Metasync::get_option('common_robots_mata') ?? ''; ?> common_robot_meta_tags_callback(); } public function advance_robot_meta_tags_callback() { $advance_robots_meta = Metasync::get_option('advance_robots_meta') ?? Metasync::get_option('advance_robots_mata') ?? ''; $snippet_advance_robots_enable = $advance_robots_meta['max-snippet']['enable'] ?? ''; $snippet_advance_robots_length = $advance_robots_meta['max-snippet']['length'] ?? '-1'; $video_advance_robots_enable = $advance_robots_meta['max-video-preview']['enable'] ?? ''; $video_advance_robots_length = $advance_robots_meta['max-video-preview']['length'] ?? '-1'; $image_advance_robots_enable = $advance_robots_meta['max-image-preview']['enable'] ?? ''; $image_advance_robots_length = $advance_robots_meta['max-image-preview']['length'] ?? ''; ?> advance_robot_meta_tags_callback(); } public function global_twitter_card_type_callback() { $twitter_card_type = Metasync::get_option('twitter_card_type') ?? ''; ?> ', isset(Metasync::get_option('common_meta_settings')['open_graph_meta_tags']) && Metasync::get_option('common_meta_settings')['open_graph_meta_tags'] == 'true' ? 'checked' : '' ); printf('
Automatically add the Open Graph meta tags in a page or post.'); } public function global_facebook_meta_callback() { printf( '', isset(Metasync::get_option('common_meta_settings')['facebook_meta_tags']) && Metasync::get_option('common_meta_settings')['facebook_meta_tags'] == 'true' ? 'checked' : '' ); printf('
Automatically add the Facebook meta tags in a page or post.'); } public function global_twitter_meta_callback() { printf( '', isset(Metasync::get_option('common_meta_settings')['twitter_meta_tags']) && Metasync::get_option('common_meta_settings')['twitter_meta_tags'] == 'true' ? 'checked' : '' ); printf('
Automatically add the Twitter meta tags in a page or post.'); } public function og_image_dimensions_callback() { $val = Metasync::get_option('common_meta_settings')['og_image_dimensions'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output og:image:width, og:image:height, and og:image:type when OG image metadata is available.'); } public function article_timestamps_callback() { $val = Metasync::get_option('common_meta_settings')['article_timestamps'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output article:published_time and article:modified_time from the post publish and modified dates (ISO 8601).'); } public function article_author_callback() { $val = Metasync::get_option('common_meta_settings')['article_author'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output article:author using the post author website URL or archive URL (overridable per-post via _metasync_og_article_author meta).'); } public function article_section_callback() { $val = Metasync::get_option('common_meta_settings')['article_section'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output article:section from the primary category (falls back to first category).'); } public function article_tags_callback() { $val = Metasync::get_option('common_meta_settings')['article_tags'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output one article:tag meta tag per WordPress post tag.'); } public function twitter_image_alt_callback() { $val = Metasync::get_option('common_meta_settings')['twitter_image_alt'] ?? 'true'; printf( '', $val === 'true' ? 'checked' : '' ); printf(' Output twitter:image:alt using the stored alt text or the OG image attachment alt attribute.'); } public function facebook_page_url_callback() { $facebook_page_url = Metasync::get_option('social_meta')['facebook_page_url'] ?? ''; printf('', esc_attr($facebook_page_url)); printf('
Enter your Facebook page URL. eg: https://www.facebook.com/MetaSync/ '); } public function facebook_authorship_callback() { $facebook_authorship = Metasync::get_option('social_meta')['facebook_authorship'] ?? ''; printf('', esc_attr($facebook_authorship)); printf('
Enter Facebook profile URL to show Facebook Authorship when your articles are being shared on Facebook. eg: https://www.facebook.com/shahrukh/ '); } public function facebook_admin_callback() { $facebook_admin = Metasync::get_option('social_meta')['facebook_admin'] ?? ''; printf('', esc_attr($facebook_admin)); printf('
Enter numeric user ID of Facebook. '); } public function facebook_app_callback() { $facebook_app = Metasync::get_option('social_meta')['facebook_app'] ?? ''; printf('', esc_attr($facebook_app)); printf('
Enter numeric app ID of Facebook '); } public function facebook_secret_callback() { $facebook_secret = Metasync::get_option('social_meta')['facebook_secret'] ?? ''; printf('', esc_attr($facebook_secret)); printf('
Enter alphanumeric access token from Facebook. '); } public function twitter_username_callback() { $twitter_username = Metasync::get_option('social_meta')['twitter_username'] ?? ''; printf('', esc_attr($twitter_username)); printf('
Twitter username of the author to add twitter:creator tag to post. eg: MetaSync '); } public static function get_business_types() { $business_type = [ 'Airline', 'Consortium', 'Corporation', 'Educational Organization', 'College Or University', 'Elementary School', 'High School', 'Middle School', 'Preschool', 'School', 'Funding Scheme', 'Government Organization', 'Library System', 'Local Business', 'Animal Shelter', 'Archive Organization', 'Automotive Business', 'Auto Body Shop', 'Auto Dealer', 'Auto Parts Store', 'Auto Rental', 'Auto Repair', 'Auto Wash', 'Gas Station', 'Motorcycle Dealer', 'Motorcycle Repair', 'Child Care', 'Dry Cleaning Or Laundry', 'Emergency Service', 'Fire Station', 'Hospital', 'Police Station', 'Employment Agency', 'Entertainment Business', 'Adult Entertainment', 'Amusement Park', 'Art Gallery', 'Casino', 'Comedy Club', 'Movie Theater', 'Night Club', 'Financial Service', 'Accounting Service', 'Automated Teller', 'Bank Or CreditUnion', 'Insurance Agency', 'Food Establishment', 'Bakery', 'Bar Or Pub', 'Brewery', 'Cafe Or CoffeeShop', 'Distillery', 'Fast Food Restaurant', 'IceCream Shop', 'Restaurant', 'Winery', 'Government Office', 'Post Office', 'Health And Beauty Business', 'Beauty Salon', 'Day Spa', 'Hair Salon', 'Health Club', 'Nail Salon', 'Tattoo Parlor', 'Home And Construction Business', 'Electrician', 'General Contractor', 'HVAC Business', 'House Painter', 'Locksmith', 'Moving Company', 'Plumber', 'Roofing Contractor', 'Internet Cafe', 'Legal Service', 'Attorney', 'Notary', 'Library', 'Lodging Business', 'Bed And Breakfast', 'Campground', 'Hostel', 'Hotel', 'Motel', 'Resort', 'Ski Resort', 'Medical Business', 'Community Health', 'Dentist', 'Dermatology', 'Diet Nutrition', 'Emergency', 'Geriatric', 'Gynecologic', 'Medical Clinic', 'Optician', 'Pharmacy', 'Physician', 'Professional Service', 'Radio Station', 'Real Estate Agent', 'Recycling Center', 'Self Storage', 'Shopping Center', 'Sports Activity Location', 'Bowling Alley', 'Exercise Gym', 'Golf Course', 'Public Swimming Pool', 'Ski Resort', 'Sports Club', 'Stadium Or Arena', 'Tennis Complex', 'Store', 'Bike Store', 'Book Store', 'Clothing Store', 'Computer Store', 'Convenience Store', 'Department Store', 'Electronics Store', 'Florist', 'Furniture Store', 'Garden Store', 'Grocery Store', 'Hardware Store', 'Hobby Shop', 'Home Goods Store', 'Jewelry Store', 'Liquor Store', 'Mens Clothing Store', 'Mobile Phone Store', 'Movie Rental Store', 'Music Store', 'Office Equipment Store', 'Outlet Store', 'Pawn Shop', 'Pet Store', 'Shoe Store', 'Sporting GoodsStore', 'Tire Shop', 'Toy Store', 'Wholesale Store', 'Television Station', 'Tourist Information Center', 'Travel Agency', 'Tree Services', 'Medical Organization', 'Diagnostic Lab', 'Veterinary Care', 'NGO', 'News Media Organization', 'Performing Group', 'Dance Group', 'Music Group', 'Theater Group', 'Project', 'Funding Agency', 'Research Project', 'Sports Organization', 'Sports Team', 'Workers Union', ]; return $business_type; } /** * Render breadcrumbs settings section */ public function render_breadcrumbs_section() { $options = Metasync::get_option('breadcrumbs', array()); $defaults = array( 'enabled' => true, 'separator' => '»', 'home_label' => 'Home', 'home_url' => '', 'show_current_page' => true, 'prefix_text' => '', 'archive_label_format' => '{name}', 'disable_schema' => false, ); $options = wp_parse_args($options, $defaults); ?>

Enable or disable breadcrumb navigation on your site

When checked, MetaSync will not output the BreadcrumbList JSON-LD in <head>. Use this if another SEO plugin already handles breadcrumb schema.

Choose how to separate breadcrumb items

Label for the home/root breadcrumb item

Leave empty to use your site home URL

Include the current page as the last (unlinked) breadcrumb item

Optional text to display before the breadcrumbs (leave empty for none)

Use {name} as a placeholder for the archive name. Example: Category: {name}