';
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:
All API keys and authentication tokens
White label branding settings
Plugin configuration and preferences
Instant indexing settings
Media optimization settings and cache
Code minification settings
All cached data and crawl information
You will need to reconfigure the plugin completely after this reset.
Delete data on uninstall
By default, deleting the plugin keeps your data: sitemap and redirection settings, per-post SEO values, and the plugin's database tables stay in place in case you reinstall. Enable this to also remove all of that when the plugin itself is deleted from the Plugins screen.
The Google service account and the IndexNow API key are always deleted on uninstall, regardless of this setting.
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)
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.
Select which user roles can see and access this plugin's menu, settings, and options in the 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); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
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'); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- runtime PHP setting with no WordPress API
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.
sprintf('Connect your WordPress site to your %1$s dashboard to retrieve your API key and %2$s UUID. This does not create a WordPress login session.', $plugin_name, $otto_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),
'seo_priority' => sprintf('Choose which value appears first on a page when both your custom SEO value and an approved %s suggestion are available. This changes what visitors see; it does not delete or overwrite saved values.', $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_sg_optimizer_compat' => sprintf('SiteGround Optimizer Compatibility Mode: Controls how %s renders on SiteGround hosting. "Auto" (recommended) uses the internal HTTP fetch, which protects themes that defer inline CSS to the footer (e.g. Divi). "Buffer Mode" renders in-process with no internal fetch — faster on sites whose pages can never be host-cached, for example when a plugin starts a PHP session on every request (BookingPress sessions are removed automatically, so this does not apply to them).', $otto_name),
'bookingpress_session_compat' => 'BookingPress Session Compatibility: Controls whether MetaSync removes the PHP session BookingPress starts on every public page, so hosts such as SiteGround can page-cache the site again.',
'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' => sprintf('Hide the Common Robots meta box and stop %1$s emitting index/noindex and follow/nofollow directives on the front end. %1$s also stops overriding WordPress core or another SEO plugin\'s robots tag. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'disable_advance_robots_metabox' => sprintf('Hide the Advanced Robots meta box and stop %1$s emitting max-snippet, max-image-preview and max-video-preview directives, including the site-wide advanced defaults. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'disable_redirection_metabox' => 'Hide the Redirection meta box and stop redirects configured through it from running. Rules added by hand on the Redirections screen keep working. Saved redirects are kept and resume if you re-enable this.',
'disable_canonical_metabox' => sprintf('Hide the Canonical meta box and stop %1$s emitting or overriding the canonical URL on every render path. WordPress core and third-party canonicals are left in place. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'disable_social_opengraph_metabox' => sprintf('Hide the Social Media & Open Graph meta box and stop %1$s emitting any Open Graph, Twitter or article tags. Tags from another SEO plugin are left in place. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'disable_schema_markup_metabox' => sprintf('Hide the Schema Markup meta box and stop %1$s emitting any JSON-LD, including the site-wide Local SEO markup and OTTO structured data. Breadcrumb markup has its own setting. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'disable_language_alternates_metabox' => sprintf('Hide the Language Alternates (hreflang) panel in the block editor and stop %1$s emitting any hreflang tags on the front end, including the auto-detected WPML entries. While disabled, %1$s does not manage or override this data — another SEO plugin, or WPML itself, is free to handle it. Saved values are kept and restored if you re-enable this.', Metasync::get_effective_plugin_name('MetaSync')),
'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' => sprintf('Automatically add target="_blank" attribute to external links appearing in your posts, pages, and other post types when rendered by %s.', $otto_name),
'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),
'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' => sprintf('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 %s, even if the source plugin is deactivated.', $plugin_name),
'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.',
'og_image_dimensions' => 'Adds your share image\'s width, height, and file type to the Open Graph tags so Facebook, LinkedIn, and others render your preview instantly at the right size instead of guessing. Leave on unless another plugin already outputs these.',
'article_timestamps' => 'Tells social platforms and search engines when each post was first published and last updated, so shares and rich results show accurate dates. Safe to leave enabled.',
'article_author' => 'Adds a link to the author\'s profile or archive page to each post\'s social tags, so shared articles can show author attribution. You can override the author URL per-post. Turn off if you\'d rather not expose author pages.',
'article_section' => 'Labels each post with its main topic (its primary category) in the social tags, helping platforms categorize your content. Falls back to the first category if no primary one is set.',
'article_tags' => 'Adds each of your WordPress post tags as a separate social "tag" so platforms understand the topics a post covers. Turn off if you don\'t want your tags exposed in share metadata.',
'twitter_image_alt' => 'Adds descriptive alt text to the image shown when your page is shared on X/Twitter, improving accessibility for screen-reader users. Uses your saved alt text or the image\'s own alt attribute.'
);
}
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 '
';
}
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 '
';
$llms_tooltips = array(
'llms_txt_enabled' => 'Publishes a file at yoursite.com/llms.txt listing your key pages so AI assistants like ChatGPT and Perplexity can find and cite your content. Safe to leave off if you don\'t want AI crawlers guided to your pages.',
'llms_full_enabled' => 'Also publishes a larger file with the full text of your posts (not just links). This exposes complete article content and can be large — keep the post limit low on smaller hosting plans.',
);
foreach ($wp_settings_fields[$page][$section] as $field) {
echo '
', esc_html($status_message), esc_html($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()
{
// Whether a key is stored at rest (encrypted or legacy plaintext).
$stored_api_key = Metasync::get_option('general')['searchatlas_api_key'] ?? '';
$has_api_key = ($stored_api_key !== '');
// Decrypted key for masking only. Never rendered in cleartext.
// false => an encrypted key exists but cannot be decrypted (salts changed).
$decrypted_api_key = Metasync::get_searchatlas_api_key();
$decrypt_failed = ($decrypted_api_key === false);
// Build a masked representation: bullets + last 4 chars. The cleartext
// key is never emitted into the page.
$masked_api_key = '';
if (!$decrypt_failed && $decrypted_api_key !== '') {
$masked_api_key = str_repeat('*', 8) . substr($decrypted_api_key, -4);
}
$otto_uuid = isset(Metasync::get_option('general')['otto_pixel_uuid']) ? Metasync::get_option('general')['otto_pixel_uuid'] : '';
$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('
');
?>
AI-Powered SEO Automation: By authenticating, you authorize Brain (our AI assistant) to access your WordPress admin capabilities through the Model Context Protocol (MCP). This enables intelligent automation for SEO optimizations, content enhancements, and performance improvements.
');
printf('');
if ($has_api_key) {
printf('');
}
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 stored securely and shown masked below ');
printf('• If you encounter issues, try disabling ad blockers temporarily ');
printf('• Contact %s if you need assistance', esc_attr(Metasync::SUPPORT_EMAIL), esc_html(Metasync::SUPPORT_EMAIL));
printf('
');
printf('');
printf('
');
printf('');
printf('
');
printf('');
// Read-only masked display. The API key is managed via one-click
// authentication only; the cleartext key is never rendered into the
// page HTML (View Source / DOM). No form input is emitted, so saving
// settings cannot overwrite or expose the stored key.
$display_has_key = (!$decrypt_failed && $masked_api_key !== '') ? '1' : '0';
printf(
'
',
esc_attr($display_has_key)
);
if ($decrypt_failed) {
printf('Stored API key could not be read. Please re-authenticate above.');
} elseif ($masked_api_key !== '') {
echo esc_html($masked_api_key);
} else {
printf('Not configured');
}
printf('
');
printf('
');
if ($decrypt_failed) {
printf('Your stored API key could not be decrypted (your site security keys may have changed). Please re-authenticate above to reconnect.');
} elseif ($is_fully_connected) {
printf('Your %s API key is stored securely and used for 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('Use the authentication button above to securely connect your account. Your API key is stored encrypted and shown masked here.');
}
printf('
',
esc_attr($timestamp),
esc_html($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'] ?? '';
$valid_person_organizations = array('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']) : esc_attr(get_bloginfo())
);
printf(' Your name or company name ');
}
public function local_seo_logo_callback()
{
$local_seo_logo = Metasync::get_option('localseo')['local_seo_logo'] ?? '';
// Rows saved by the legacy sanitizer hold the corrupted form of
// an attachment ID ("http://45589"). Repair it before anything reads
// the value — both so the preview resolves and so the hidden input
// re-submits the recovered ID instead of the corrupted string, which
// would otherwise survive every subsequent Save Settings.
$local_seo_logo = metasync_repair_scheme_prefixed_media_id($local_seo_logo);
printf(
'',
esc_attr((string) $local_seo_logo)
);
printf(' ');
printf('
Min Size: 160Χ90px, Max Size: 1920X1080px. A squared image is preferred by the search engines.
');
printf('');
// The stored value may be an attachment ID or a raw URL, so resolve
// both the way the front-end schema output already does. Anything that
// resolves to nothing — a deleted attachment, a legacy value mangled by
// an earlier save — must render no preview at all, rather than an empty
// src next to an orphaned remove button.
$logo_url = $this->resolve_media_url($local_seo_logo, 'medium');
printf(
'',
esc_url($logo_url),
$logo_url ? '' : ' style="display:none"'
);
$button_type = $logo_url ? 'button' : 'hidden';
printf('', esc_attr($button_type));
printf('');
}
/**
* Resolve a stored media setting to a displayable URL.
*
* These settings accept either a media-library attachment ID or a raw URL;
* returns an empty string when the value resolves to neither, so callers
* can distinguish "no image" from "image we could not resolve".
*
* @param mixed $value Attachment ID or URL as stored in the options.
* @param string $size Registered image size to request for an attachment.
* @return string Resolved URL, or '' when there is nothing to show.
*/
private function resolve_media_url($value, $size = 'medium')
{
if (empty($value)) {
return '';
}
// A row saved by the legacy sanitizer stores a corrupted attachment ID ("http://45589"),
// which is neither resolvable as an ID nor a usable image URL.
$value = metasync_repair_scheme_prefixed_media_id($value);
if (empty($value)) {
return '';
}
if (is_numeric($value)) {
$url = wp_get_attachment_image_url((int) $value, $size);
return $url ? $url : '';
}
return (string) $value;
}
public function local_seo_url_callback()
{
printf(
'',
isset(Metasync::get_option('localseo')['local_seo_url']) ? esc_attr(Metasync::get_option('localseo')['local_seo_url']) : esc_attr(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'] ?? '';
?>
$day_time) {
?>
', 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'] : '';
?>
$phone_number) {
?>
', 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 common_robot_meta_tags_callback()
{
$common_robots_meta = Metasync::get_option('common_robots_meta') ?? Metasync::get_option('common_robots_mata') ?? '';
?>