PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | includes/class-metasync-settings-registration.php +195 -190 2.7.0 → 2.7.1 View file →
@@ -180,12 +180,13 @@
180 180 */
181 181 private function handle_plugin_access_roles_save()
182 182 {
183 183 if (isset($_POST['save_plugin_access_roles']) && $_POST['save_plugin_access_roles'] === 'yes') {
184 - if (isset($_POST['plugin_access_roles_nonce']) && wp_verify_nonce($_POST['plugin_access_roles_nonce'], 'metasync_plugin_access_roles_nonce')) {
184 + $access_roles_nonce = isset($_POST['plugin_access_roles_nonce']) ? sanitize_text_field(wp_unslash($_POST['plugin_access_roles_nonce'])) : '';
185 + if (wp_verify_nonce($access_roles_nonce, 'metasync_plugin_access_roles_nonce')) {
185 186
186 187 if (!Metasync::current_user_has_plugin_access()) {
187 - wp_redirect(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=advanced&access_roles_error=1&message=' . urlencode('Insufficient permissions')));
188 + wp_safe_redirect(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=advanced&access_roles_error=1&message=' . urlencode('Insufficient permissions')));
188 189 exit;
189 190 }
190 191
191 192 self::$defer_settings_recovery_lock_release = true;
@@ -203,9 +204,9 @@
203 204 $metasync_options['general'] = array();
204 205 }
205 206
206 207 if (isset($_POST['plugin_access_roles']) && is_array($_POST['plugin_access_roles'])) {
207 - $metasync_options['general']['plugin_access_roles'] = array_map('sanitize_text_field', $_POST['plugin_access_roles']);
208 + $metasync_options['general']['plugin_access_roles'] = array_map('sanitize_text_field', wp_unslash($_POST['plugin_access_roles']));
208 209 } else {
209 210 $metasync_options['general']['plugin_access_roles'] = array();
210 211 }
211 212
@@ -211,13 +212,13 @@
211 212
212 213 Metasync::set_option($metasync_options);
213 214
214 215 $redirect_url = admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=advanced&access_roles_saved=1');
215 - wp_redirect($redirect_url);
216 + wp_safe_redirect($redirect_url);
216 217 exit;
217 218 } else {
218 219 $redirect_url = admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=advanced&access_roles_error=1&message=' . urlencode('Invalid security token'));
219 - wp_redirect($redirect_url);
220 + wp_safe_redirect($redirect_url);
220 221 exit;
221 222 }
222 223 }
223 224 }
@@ -342,9 +343,9 @@
342 343 $value = isset($settings['max_posts']) ? (int) $settings['max_posts'] : 50;
343 344 echo '<div class="llms-txt-dependent">';
344 345 printf(
345 346 '<input type="number" id="llms_txt_max_posts" name="metasync_llms_txt_settings[max_posts]" min="1" max="500" value="%d" />',
346 - $value
347 + absint($value)
347 348 );
348 349 echo '</div>';
349 350 },
350 351 $page_slug . '_general',
@@ -415,9 +416,9 @@
415 416 $value = isset($settings['max_posts_full']) ? (int) $settings['max_posts_full'] : 25;
416 417 echo '<div class="llms-full-dependent">';
417 418 printf(
418 419 '<input type="number" id="llms_txt_max_posts_full" name="metasync_llms_txt_settings[max_posts_full]" min="1" max="500" value="%d" />',
419 - $value
420 + absint($value)
420 421 );
421 422 echo '<p class="description">' . esc_html__('Maximum posts for /llms-full.txt (lower than summary to protect server performance).', 'metasync') . '</p>';
422 423 echo '</div>';
423 424 },
@@ -453,12 +454,12 @@
453 454 'Disallow Empty Archives',
454 455 function() use ($option_key) {
455 456 $noindex_empty_archives = Metasync::get_option('seo_controls')['noindex_empty_archives'] ?? 'false';
456 457 printf(
457 - '<input type="checkbox" id="noindex_empty_archives" name="' . $option_key . '[seo_controls][noindex_empty_archives]" value="true" %s />',
458 + '<input type="checkbox" id="noindex_empty_archives" name="' . esc_attr($option_key) . '[seo_controls][noindex_empty_archives]" value="true" %s />',
458 459 isset($noindex_empty_archives) && $noindex_empty_archives == 'true' ? 'checked' : ''
459 460 );
460 - printf('<span class="description"><strong>Empty Archives:</strong> When checked, automatically adds noindex to category, tag, author, and format archive pages that have no posts. Once posts are added to these archives, they will automatically be allowed for indexing. This prevents thin content pages from being indexed.</span>');
461 + echo '<span class="description"><strong>Empty Archives:</strong> When checked, automatically adds noindex to category, tag, author, and format archive pages that have no posts. Once posts are added to these archives, they will automatically be allowed for indexing. This prevents thin content pages from being indexed.</span>';
461 462 },
462 463 $page_slug . '_seo-controls',
463 464 $SECTION_SEO_CONTROLS
464 465 );
@@ -468,12 +469,12 @@
468 469 'Disallow Date Archives',
469 470 function() use ($option_key) {
470 471 $index_date_archives = Metasync::get_option('seo_controls')['index_date_archives'] ?? 'false';
471 472 printf(
472 - '<input type="checkbox" id="index_date_archives" name="' . $option_key . '[seo_controls][index_date_archives]" value="true" %s />',
473 + '<input type="checkbox" id="index_date_archives" name="' . esc_attr($option_key) . '[seo_controls][index_date_archives]" value="true" %s />',
473 474 isset($index_date_archives) && $index_date_archives == 'true' ? 'checked' : ''
474 475 );
475 - printf('<span class="description"><strong>Date Archives:</strong> When checked, prevents search engines from indexing date-based archive pages (e.g., /2024/01/, /2024/01/15/). These pages often have thin content and can dilute your site\'s SEO value. Recommended for most sites.</span>');
476 + echo '<span class="description"><strong>Date Archives:</strong> When checked, prevents search engines from indexing date-based archive pages (e.g., /2024/01/, /2024/01/15/). These pages often have thin content and can dilute your site\'s SEO value. Recommended for most sites.</span>';
476 477 },
477 478 $page_slug . '_seo-controls',
478 479 $SECTION_SEO_CONTROLS
479 480 );
@@ -483,12 +484,12 @@
483 484 'Disallow Tag Archives',
484 485 function() use ($option_key) {
485 486 $index_tag_archives = Metasync::get_option('seo_controls')['index_tag_archives'] ?? 'false';
486 487 printf(
487 - '<input type="checkbox" id="index_tag_archives" name="' . $option_key . '[seo_controls][index_tag_archives]" value="true" %s />',
488 + '<input type="checkbox" id="index_tag_archives" name="' . esc_attr($option_key) . '[seo_controls][index_tag_archives]" value="true" %s />',
488 489 isset($index_tag_archives) && $index_tag_archives == 'true' ? 'checked' : ''
489 490 );
490 - printf('<span class="description"><strong>Tag Archives:</strong> When checked, prevents search engines from indexing tag archive pages (e.g., /tag/technology/). Useful if you have many low-quality tag pages or want to focus on category-based organization instead.</span>');
491 + echo '<span class="description"><strong>Tag Archives:</strong> When checked, prevents search engines from indexing tag archive pages (e.g., /tag/technology/). Useful if you have many low-quality tag pages or want to focus on category-based organization instead.</span>';
491 492 },
492 493 $page_slug . '_seo-controls',
493 494 $SECTION_SEO_CONTROLS
494 495 );
@@ -498,12 +499,12 @@
498 499 'Disallow Author Archives',
499 500 function() use ($option_key) {
500 501 $index_author_archives = Metasync::get_option('seo_controls')['index_author_archives'] ?? 'false';
501 502 printf(
502 - '<input type="checkbox" id="index_author_archives" name="' . $option_key . '[seo_controls][index_author_archives]" value="true" %s />',
503 + '<input type="checkbox" id="index_author_archives" name="' . esc_attr($option_key) . '[seo_controls][index_author_archives]" value="true" %s />',
503 504 isset($index_author_archives) && $index_author_archives == 'true' ? 'checked' : ''
504 505 );
505 - printf('<span class="description"><strong>Author Archives:</strong> When checked, prevents search engines from indexing author archive pages (e.g., /author/john-doe/). Recommended for single-author sites or when author pages don\'t provide unique value. Multi-author sites may want to keep this unchecked.</span>');
506 + echo '<span class="description"><strong>Author Archives:</strong> When checked, prevents search engines from indexing author archive pages (e.g., /author/john-doe/). Recommended for single-author sites or when author pages don\'t provide unique value. Multi-author sites may want to keep this unchecked.</span>';
506 507 },
507 508 $page_slug . '_seo-controls',
508 509 $SECTION_SEO_CONTROLS
509 510 );
@@ -513,12 +514,12 @@
513 514 'Disallow Format Archives',
514 515 function() use ($option_key) {
515 516 $index_format_archives = Metasync::get_option('seo_controls')['index_format_archives'] ?? 'false';
516 517 printf(
517 - '<input type="checkbox" id="index_format_archives" name="' . $option_key . '[seo_controls][index_format_archives]" value="true" %s />',
518 + '<input type="checkbox" id="index_format_archives" name="' . esc_attr($option_key) . '[seo_controls][index_format_archives]" value="true" %s />',
518 519 isset($index_format_archives) && $index_format_archives == 'true' ? 'checked' : ''
519 520 );
520 - printf('<span class="description"><strong>Format Archives:</strong> When checked, prevents search engines from indexing post format archive pages (e.g., /type/aside/, /type/gallery/). These are rarely useful for SEO and can create duplicate content issues. Recommended for most sites.</span>');
521 + echo '<span class="description"><strong>Format Archives:</strong> When checked, prevents search engines from indexing post format archive pages (e.g., /type/aside/, /type/gallery/). These are rarely useful for SEO and can create duplicate content issues. Recommended for most sites.</span>';
521 522 },
522 523 $page_slug . '_seo-controls',
523 524 $SECTION_SEO_CONTROLS
524 525 );
@@ -528,12 +529,12 @@
528 529 'Disallow Category Archives',
529 530 function() use ($option_key) {
530 531 $index_category_archives = Metasync::get_option('seo_controls')['index_category_archives'] ?? 'false';
531 532 printf(
532 - '<input type="checkbox" id="index_category_archives" name="' . $option_key . '[seo_controls][index_category_archives]" value="true" %s />',
533 + '<input type="checkbox" id="index_category_archives" name="' . esc_attr($option_key) . '[seo_controls][index_category_archives]" value="true" %s />',
533 534 isset($index_category_archives) && $index_category_archives == 'true' ? 'checked' : ''
534 535 );
535 - printf('<span class="description"><strong>Category Archives:</strong> When checked, prevents search engines from indexing category archive pages (e.g., /category/news/). This may be useful if your category pages have thin content or if you want to consolidate SEO value on main pages instead. Use with caution as category pages can be valuable for site organization.</span>');
536 + echo '<span class="description"><strong>Category Archives:</strong> When checked, prevents search engines from indexing category archive pages (e.g., /category/news/). This may be useful if your category pages have thin content or if you want to consolidate SEO value on main pages instead. Use with caution as category pages can be valuable for site organization.</span>';
536 537 },
537 538 $page_slug . '_seo-controls',
538 539 $SECTION_SEO_CONTROLS
539 540 );
@@ -543,9 +544,9 @@
543 544 'Add No-follow to External Links',
544 545 function() use ($option_key) {
545 546 $add_nofollow_to_external_links = Metasync::get_option('seo_controls')['add_nofollow_to_external_links'] ?? 'false';
546 547 printf(
547 - '<input type="checkbox" id="add_nofollow_to_external_links" name="' . $option_key . '[seo_controls][add_nofollow_to_external_links]" value="true" %s />',
548 + '<input type="checkbox" id="add_nofollow_to_external_links" name="' . esc_attr($option_key) . '[seo_controls][add_nofollow_to_external_links]" value="true" %s />',
548 549 isset($add_nofollow_to_external_links) && $add_nofollow_to_external_links == 'true' ? 'checked' : ''
549 550 );
550 551 printf('<span class="description"><strong>No-follow External Links:</strong> When checked, automatically adds <code>rel="nofollow"</code> attribute to all external links appearing in posts, pages, and other post types when rendered by %s. This tells search engines not to follow these links, which can help preserve your site\'s SEO value and prevent passing link juice to external sites.</span>', esc_html(Metasync::get_whitelabel_otto_name()));
551 552 },
@@ -571,12 +572,12 @@
571 572 'Override Other Plugins\' Robots Tags',
572 573 function() use ($option_key) {
573 574 $override_robots_tags = Metasync::get_option('seo_controls')['override_robots_tags'] ?? 'false';
574 575 printf(
575 - '<input type="checkbox" id="override_robots_tags" name="' . $option_key . '[seo_controls][override_robots_tags]" value="true" %s />',
576 + '<input type="checkbox" id="override_robots_tags" name="' . esc_attr($option_key) . '[seo_controls][override_robots_tags]" value="true" %s />',
576 577 isset($override_robots_tags) && $override_robots_tags == 'true' ? 'checked' : ''
577 578 );
578 - printf('<span class="description"><strong>Override Robots Tags:</strong> When checked, ' . Metasync_Settings_Registration::instance()->get_effective_menu_title() . ' will take precedence over robots meta tags from other SEO plugins (Yoast, Rank Math, All in One SEO, etc.). This removes their noindex tags when you want to allow indexing on archive pages. Only enable this if other plugins are conflicting with your indexation settings.</span>');
579 + echo '<span class="description"><strong>Override Robots Tags:</strong> When checked, ' . esc_html(Metasync_Settings_Registration::instance()->get_effective_menu_title()) . ' will take precedence over robots meta tags from other SEO plugins (Yoast, Rank Math, All in One SEO, etc.). This removes their noindex tags when you want to allow indexing on archive pages. Only enable this if other plugins are conflicting with your indexation settings.</span>';
579 580 },
580 581 $page_slug . '_seo-controls',
581 582 $SECTION_SEO_CONTROLS_ADVANCED
582 583 );
@@ -599,13 +600,13 @@
599 600 'Enable Google Instant Indexing',
600 601 function() use ($option_key) {
601 602 $enable_googleinstantindex = Metasync::get_option('seo_controls')['enable_googleinstantindex'] ?? 'false';
602 603 printf(
603 - '<input type="checkbox" id="enable_googleinstantindex" name="' . $option_key . '[seo_controls][enable_googleinstantindex]" value="true" %s />',
604 + '<input type="checkbox" id="enable_googleinstantindex" name="' . esc_attr($option_key) . '[seo_controls][enable_googleinstantindex]" value="true" %s />',
604 605 isset($enable_googleinstantindex) && $enable_googleinstantindex == 'true' ? 'checked' : ''
605 606 );
606 607 Metasync::render_tooltip_icon('enable_googleinstantindex', 'Turns on direct URL submission to Google. It only works after you add a service-account key below and verify your site in Search Console — set those up first.');
607 - printf('<span class="description" style="margin-left:8px;"><strong>Enable Instant Indexing:</strong> When checked, enables the Google Instant Indexing feature which allows you to submit URLs directly to Google for faster indexing. Configure it in the Google Instant Indexing section below.</span>');
608 + echo '<span class="description" style="margin-left:8px;"><strong>Enable Instant Indexing:</strong> When checked, enables the Google Instant Indexing feature which allows you to submit URLs directly to Google for faster indexing. Configure it in the Google Instant Indexing section below.</span>';
608 609 },
609 610 $page_slug . '_seo-controls',
610 611 $SECTION_SEO_CONTROLS_INSTANT_INDEX
611 612 );
@@ -646,13 +647,13 @@
646 647 $enable_binginstantindex = 'true';
647 648 }
648 649
649 650 printf(
650 - '<input type="checkbox" id="enable_binginstantindex" name="' . $option_key . '[seo_controls][enable_binginstantindex]" value="true" %s %s />',
651 + '<input type="checkbox" id="enable_binginstantindex" name="' . esc_attr($option_key) . '[seo_controls][enable_binginstantindex]" value="true" %s %s />',
651 652 isset($enable_binginstantindex) && $enable_binginstantindex == 'true' ? 'checked' : '',
652 653 $has_api_key ? 'data-auto-enabled="true"' : ''
653 654 );
654 - printf('<span class="description"><strong>Enable Bing Instant Indexing:</strong> Automatically enabled when you configure an API key below. When enabled, this activates the IndexNow protocol to instantly notify Bing, Yandex, and other search engines about URL changes.</span>');
655 + echo '<span class="description"><strong>Enable Bing Instant Indexing:</strong> Automatically enabled when you configure an API key below. When enabled, this activates the IndexNow protocol to instantly notify Bing, Yandex, and other search engines about URL changes.</span>';
655 656 },
656 657 $page_slug . '_seo-controls',
657 658 'seo_controls_bing_instant_index'
658 659 );
@@ -693,9 +694,9 @@
693 694 'otto_pixel_uuid',
694 695 $whitelabel_otto_name . ' Pixel UUID',
695 696 function() use ($option_key) {
696 697 $value = Metasync::get_option('general')['otto_pixel_uuid'] ?? '';
697 - printf('<input type="text" size="40" value = "'.esc_attr($value).'" name="' . $option_key . '[general][otto_pixel_uuid]"/>');
698 + echo '<input type="text" size="40" value = "'.esc_attr($value).'" name="' . esc_attr($option_key) . '[general][otto_pixel_uuid]"/>';
698 699 },
699 700 $page_slug . '_general',
700 701 $SECTION_METASYNC
701 702 );
@@ -707,12 +708,12 @@
707 708 'Disable ' . $whitelabel_otto_name . ' for Logged in Users',
708 709 function() use ($whitelabel_otto_name, $option_key) {
709 710 $otto_disable_on_loggedin = Metasync::get_option('general')['otto_disable_on_loggedin'] ?? '';
710 711 printf(
711 - '<input type="checkbox" id="otto_disable_on_loggedin" name="' . $option_key . '[general][otto_disable_on_loggedin]" value="true" %s />',
712 + '<input type="checkbox" id="otto_disable_on_loggedin" name="' . esc_attr($option_key) . '[general][otto_disable_on_loggedin]" value="true" %s />',
712 713 isset($otto_disable_on_loggedin) && $otto_disable_on_loggedin == 'true' ? 'checked' : ''
713 714 );
714 - printf('<span class="description"> This disables '.$whitelabel_otto_name.' when logged in to allow editing original page contents</span>');
715 + echo '<span class="description"> This disables '.esc_html($whitelabel_otto_name).' when logged in to allow editing original page contents</span>';
715 716 },
716 717 $page_slug . '_general',
717 718 $SECTION_METASYNC
718 719 );
@@ -723,9 +724,9 @@
723 724 'Disable ' . $whitelabel_otto_name . ' Frontend Toolbar',
724 725 function() use ($whitelabel_otto_name, $option_key) {
725 726 $otto_disable_toolbar = Metasync::get_option('general')['otto_disable_preview_button'] ?? false;
726 727 printf(
727 - '<input type="checkbox" id="otto_disable_preview_button" name="' . $option_key . '[general][otto_disable_preview_button]" value="true" %s />',
728 + '<input type="checkbox" id="otto_disable_preview_button" name="' . esc_attr($option_key) . '[general][otto_disable_preview_button]" value="true" %s />',
728 729 filter_var($otto_disable_toolbar, FILTER_VALIDATE_BOOLEAN) ? 'checked' : ''
729 730 );
730 731 printf('<span class="description"> Hide the entire frontend toolbar (status indicator, preview button, and debug button) on the frontend. %s functionality will still work, but the toolbar controls will be hidden.</span>', esc_html($whitelabel_otto_name));
731 732 },
@@ -744,19 +745,19 @@
744 745 # from the stored post meta, so unchecking it restores the custom value
745 746 # untouched. Applies to the SEO title and meta description only.
746 747 add_settings_field(
747 748 'seo_priority',
748 - 'SEO Title &amp; Description Priority',
749 + 'SEO Title & Description Priority',
749 750 function() use ($whitelabel_otto_name, $option_key) {
750 751 $seo_priority = Metasync::get_option('general')['seo_priority'] ?? '';
751 752 $checked = $seo_priority === Metasync_Seo_Precedence::PRIORITY_OTTO ? 'checked' : '';
752 - $name = esc_attr($option_key) . '[general][seo_priority]';
753 + $name = $option_key . '[general][seo_priority]';
753 754
754 755 echo '<div class="metasync-seo-priority-control">';
755 756 printf(
756 757 '<label for="seo_priority"><input type="checkbox" id="seo_priority" name="%s" value="otto" %s /> <strong>Prioritize approved %s values</strong></label>',
757 - $name,
758 - $checked,
758 + esc_attr($name),
759 + esc_attr($checked),
759 760 esc_html($whitelabel_otto_name)
760 761 );
761 762 printf(
762 763 '<p class="description" id="seo_priority_description"><strong>Off:</strong> Your custom title and description appear first; %1$s fills in only where you have no value.<br><strong>On:</strong> Approved %1$s values appear first; your custom values remain the fallback.<br><span class="metasync-seo-priority-note">Your saved values are never deleted or overwritten.</span></p>',
@@ -775,9 +776,9 @@
775 776 function() use ($whitelabel_otto_name, $option_key) {
776 777 $value = Metasync::get_option('general')['otto_wp_rocket_compat'] ?? 'auto';
777 778 $wp_rocket_active = class_exists('WP_Rocket');
778 779
779 - echo '<select id="otto_wp_rocket_compat" name="' . $option_key . '[general][otto_wp_rocket_compat]">';
780 + echo '<select id="otto_wp_rocket_compat" name="' . esc_attr($option_key) . '[general][otto_wp_rocket_compat]">';
780 781 echo '<option value="auto"' . selected($value, 'auto', false) . '>Auto (Recommended)</option>';
781 782 echo '<option value="buffer"' . selected($value, 'buffer', false) . '>Buffer Mode (Faster)</option>';
782 783 echo '<option value="http"' . selected($value, 'http', false) . '>HTTP Mode (Safer)</option>';
783 784 echo '<option value="disable_otto"' . selected($value, 'disable_otto', false) . '>Disable ' . esc_html($whitelabel_otto_name) . ' when WP Rocket is active</option>';
@@ -805,9 +806,9 @@
805 806 function() use ($option_key) {
806 807 $value = Metasync::get_option('general')['otto_sg_optimizer_compat'] ?? 'auto';
807 808 $sg_optimizer_active = defined('SiteGround_Optimizer\VERSION') || class_exists('SiteGround_Optimizer\Parser\Parser');
808 809
809 - echo '<select id="otto_sg_optimizer_compat" name="' . $option_key . '[general][otto_sg_optimizer_compat]">';
810 + echo '<select id="otto_sg_optimizer_compat" name="' . esc_attr($option_key) . '[general][otto_sg_optimizer_compat]">';
810 811 echo '<option value="auto"' . selected($value, 'auto', false) . '>Auto (Recommended)</option>';
811 812 echo '<option value="buffer"' . selected($value, 'buffer', false) . '>Buffer Mode (Faster)</option>';
812 813 echo '<option value="http"' . selected($value, 'http', false) . '>HTTP Mode (Safer)</option>';
813 814 echo '</select>';
@@ -837,9 +838,9 @@
837 838 'BookingPress Session Compatibility',
838 839 function() use ($option_key) {
839 840 $value = Metasync::get_option('general')['bookingpress_session_compat'] ?? 'auto';
840 841
841 - echo '<select id="bookingpress_session_compat" name="' . $option_key . '[general][bookingpress_session_compat]">';
842 + echo '<select id="bookingpress_session_compat" name="' . esc_attr($option_key) . '[general][bookingpress_session_compat]">';
842 843 echo '<option value="auto"' . selected($value, 'auto', false) . '>Auto (Recommended)</option>';
843 844 echo '<option value="off"' . selected($value, 'off', false) . '>Disabled</option>';
844 845 echo '</select>';
845 846
@@ -867,9 +868,9 @@
867 868 'Disable ' . $whitelabel_otto_name . ' for Bot Traffic',
868 869 function() use ($whitelabel_otto_name, $option_key) {
869 870 $otto_disable_for_bots = Metasync::get_option('general')['otto_disable_for_bots'] ?? false;
870 871 printf(
871 - '<input type="checkbox" id="otto_disable_for_bots" name="' . $option_key . '[general][otto_disable_for_bots]" value="true" %s />',
872 + '<input type="checkbox" id="otto_disable_for_bots" name="' . esc_attr($option_key) . '[general][otto_disable_for_bots]" value="true" %s />',
872 873 filter_var($otto_disable_for_bots, FILTER_VALIDATE_BOOLEAN) ? 'checked' : ''
873 874 );
874 875 printf('<span class="description"> Skip %s processing for detected bots (search engines, crawlers, SEO tools) to reduce unnecessary API calls. View bot statistics in the Bot Statistics page under the SEO dropdown.</span>', esc_html($whitelabel_otto_name));
875 876 },
@@ -883,9 +884,9 @@
883 884 'Bot Whitelist',
884 885 function() use ($whitelabel_otto_name, $option_key) {
885 886 $otto_bot_whitelist = Metasync::get_option('general')['otto_bot_whitelist'] ?? '';
886 887 printf(
887 - '<textarea id="otto_bot_whitelist" name="' . $option_key . '[general][otto_bot_whitelist]" rows="5" cols="50" style="width: 100%%; max-width: 500px; font-family: monospace;">%s</textarea>',
888 + '<textarea id="otto_bot_whitelist" name="' . esc_attr($option_key) . '[general][otto_bot_whitelist]" rows="5" cols="50" style="width: 100%%; max-width: 500px; font-family: monospace;">%s</textarea>',
888 889 esc_textarea($otto_bot_whitelist)
889 890 );
890 891 echo '<p class="description">';
891 892 echo 'Enter bot names or user-agent patterns (one per line) that should <strong>always</strong> be processed by ' . esc_html($whitelabel_otto_name) . ', even when "Disable for Bots" is enabled.<br>';
@@ -904,9 +905,9 @@
904 905 'Bot Blacklist',
905 906 function() use ($whitelabel_otto_name, $option_key) {
906 907 $otto_bot_blacklist = Metasync::get_option('general')['otto_bot_blacklist'] ?? '';
907 908 printf(
908 - '<textarea id="otto_bot_blacklist" name="' . $option_key . '[general][otto_bot_blacklist]" rows="5" cols="50" style="width: 100%%; max-width: 500px; font-family: monospace;">%s</textarea>',
909 + '<textarea id="otto_bot_blacklist" name="' . esc_attr($option_key) . '[general][otto_bot_blacklist]" rows="5" cols="50" style="width: 100%%; max-width: 500px; font-family: monospace;">%s</textarea>',
909 910 esc_textarea($otto_bot_blacklist)
910 911 );
911 912 echo '<p class="description">';
912 913 echo 'Enter bot names or user-agent patterns (one per line) that should <strong>always</strong> be blocked from ' . esc_html($whitelabel_otto_name) . ' processing, regardless of other settings.<br>';
@@ -928,9 +929,9 @@
928 929 printf(
929 930 '<a href="%s" class="button button-secondary"><span class="dashicons dashicons-chart-bar" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> View Bot Statistics</a>',
930 931 esc_url(admin_url('admin.php?page=' . $page_slug . '-bot-statistics'))
931 932 );
932 - printf('<p class="description">View detailed bot detection statistics, breakdown by bot type, and unique bot entries with hit counts.</p>');
933 + echo '<p class="description">View detailed bot detection statistics, breakdown by bot type, and unique bot entries with hit counts.</p>';
933 934 },
934 935 $page_slug . '_general',
935 936 $SECTION_METASYNC
936 937 );
@@ -941,22 +942,20 @@
941 942 'periodic_clear_ottopage_cache',
942 943 'Clear Page Cache',
943 944 function() use ($option_key) {
944 945 $periodic_clear_ottopage_cache = Metasync::get_option('general')['periodic_clear_ottopage_cache'] ?? 'default';
945 - printf('<select style = "width : 250px" name="' . $option_key . '[general][periodic_clear_ottopage_cache]" id="heading_style">');
946 - printf('<option value="24" '. selected($periodic_clear_ottopage_cache, '24', false) . '>Clear Daily</option>');
947 - printf('<option value="36" '. selected($periodic_clear_ottopage_cache, '36', false) . '>Clear Every 2 days</option>');
948 - printf('<option value="40" '. selected($periodic_clear_ottopage_cache, '40', false) . '>Clear Weekly</option>');
949 - printf('<option value="0"'.selected($periodic_clear_ottopage_cache, '0', false).'>Clear Monthly (Default)</option>');
950 - printf('</select>');
946 + echo '<select style = "width : 250px" name="' . esc_attr($option_key) . '[general][periodic_clear_ottopage_cache]" id="heading_style">';
947 + echo '<option value="24" '. selected($periodic_clear_ottopage_cache, '24', false) . '>Clear Daily</option>';
948 + echo '<option value="36" '. selected($periodic_clear_ottopage_cache, '36', false) . '>Clear Every 2 days</option>';
949 + echo '<option value="40" '. selected($periodic_clear_ottopage_cache, '40', false) . '>Clear Weekly</option>';
950 + echo '<option value="0"'.selected($periodic_clear_ottopage_cache, '0', false).'>Clear Monthly (Default)</option>';
951 + echo '</select>';
951 952
952 953 # get last cleared timestamp
953 954 $timestamp = get_option('metasync_refresh_all_caches')['pages'] ?? false;
954 955
955 956 if($timestamp > 0){
956 - printf(
957 - '<p class="descriptionValue">last Cleared : '.date('y-m-d H:i:s', $timestamp).'</p>'
958 - );
957 + echo '<p class="descriptionValue">last Cleared : '.esc_html(gmdate('y-m-d H:i:s', $timestamp)).'</p>';
959 958 }
960 959
961 960 },
962 961 $page_slug . '_otto_cache',
@@ -967,22 +966,20 @@
967 966 'periodic_clear_ottopost_cache',
968 967 'Clear Post Cache',
969 968 function() use ($option_key) {
970 969 $periodic_clear_ottopost_cache = Metasync::get_option('general')['periodic_clear_ottopost_cache'] ?? 'default';
971 - printf('<select style = "width : 250px" name="' . $option_key . '[general][periodic_clear_ottopost_cache]" id="heading_style">');
972 - printf('<option value="24" '. selected($periodic_clear_ottopost_cache, '24', false) . '>Clear Daily</option>');
973 - printf('<option value="36" '. selected($periodic_clear_ottopost_cache, '36', false) . '>Clear Every 2 days</option>');
974 - printf('<option value="40" '. selected($periodic_clear_ottopost_cache, '40', false) . '>Clear Weekly</option>');
975 - printf('<option value="0"'.selected($periodic_clear_ottopost_cache, '0', false).'>Clear Monthly (Default)</option>');
976 - printf('</select>');
970 + echo '<select style = "width : 250px" name="' . esc_attr($option_key) . '[general][periodic_clear_ottopost_cache]" id="heading_style">';
971 + echo '<option value="24" '. selected($periodic_clear_ottopost_cache, '24', false) . '>Clear Daily</option>';
972 + echo '<option value="36" '. selected($periodic_clear_ottopost_cache, '36', false) . '>Clear Every 2 days</option>';
973 + echo '<option value="40" '. selected($periodic_clear_ottopost_cache, '40', false) . '>Clear Weekly</option>';
974 + echo '<option value="0"'.selected($periodic_clear_ottopost_cache, '0', false).'>Clear Monthly (Default)</option>';
975 + echo '</select>';
977 976
978 977 # get last cleared timestamp
979 978 $timestamp = get_option('metasync_refresh_all_caches')['posts'] ?? false;
980 979
981 980 if($timestamp > 0){
982 - printf(
983 - '<p class="descriptionValue">last Cleared : '.date('y-m-d H:i:s', $timestamp).'</p>'
984 - );
981 + echo '<p class="descriptionValue">last Cleared : '.esc_html(gmdate('y-m-d H:i:s', $timestamp)).'</p>';
985 982 }
986 983 },
987 984 $page_slug . '_otto_cache',
988 985 $SECTION_METASYNC
@@ -992,22 +989,20 @@
992 989 'periodic_clear_otto_cache',
993 990 'Clear all cache',
994 991 function() use ($option_key) {
995 992 $periodic_clear_otto_cache = Metasync::get_option('general')['periodic_clear_otto_cache'] ?? 'default';
996 - printf('<select style = "width : 250px" name="' . $option_key . '[general][periodic_clear_otto_cache]" id="heading_style">');
997 - printf('<option value="24" '. selected($periodic_clear_otto_cache, '24', false) . '>Clear Daily</option>');
998 - printf('<option value="36" '. selected($periodic_clear_otto_cache, '36', false) . '>Clear Every 2 days</option>');
999 - printf('<option value="40" '. selected($periodic_clear_otto_cache, '40', false) . '>Clear Weekly</option>');
1000 - printf('<option value="0"'.selected($periodic_clear_otto_cache, '0', false).'>Clear Monthly (Default)</option>');
1001 - printf('</select>');
993 + echo '<select style = "width : 250px" name="' . esc_attr($option_key) . '[general][periodic_clear_otto_cache]" id="heading_style">';
994 + echo '<option value="24" '. selected($periodic_clear_otto_cache, '24', false) . '>Clear Daily</option>';
995 + echo '<option value="36" '. selected($periodic_clear_otto_cache, '36', false) . '>Clear Every 2 days</option>';
996 + echo '<option value="40" '. selected($periodic_clear_otto_cache, '40', false) . '>Clear Weekly</option>';
997 + echo '<option value="0"'.selected($periodic_clear_otto_cache, '0', false).'>Clear Monthly (Default)</option>';
998 + echo '</select>';
1002 999
1003 1000 # get last cleared timestamp
1004 1001 $timestamp = get_option('metasync_refresh_all_caches')['general'] ?? false;
1005 1002
1006 1003 if($timestamp > 0){
1007 - printf(
1008 - '<p class="descriptionValue">last Cleared : '.date('y-m-d H:i:s', $timestamp).'</p>'
1009 - );
1004 + echo '<p class="descriptionValue">last Cleared : '.esc_html(gmdate('y-m-d H:i:s', $timestamp)).'</p>';
1010 1005 }
1011 1006
1012 1007 },
1013 1008 $page_slug . '_otto_cache',
@@ -1022,12 +1017,12 @@
1022 1017 'Disable Common Robots Meta Box',
1023 1018 function() use ($option_key) {
1024 1019 $disabled = Metasync::get_option('general')['disable_common_robots_metabox'] ?? false;
1025 1020 printf(
1026 - '<input type="checkbox" id="disable_common_robots_metabox" name="' . $option_key . '[general][disable_common_robots_metabox]" value="1" %s />',
1021 + '<input type="checkbox" id="disable_common_robots_metabox" name="' . esc_attr($option_key) . '[general][disable_common_robots_metabox]" value="1" %s />',
1027 1022 $disabled ? 'checked' : ''
1028 1023 );
1029 - printf('<span class="description"> Hide the Common Robots meta box and stop %1$s emitting index/follow robots directives on the front end. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', Metasync::get_effective_plugin_name('MetaSync'));
1024 + printf('<span class="description"> Hide the Common Robots meta box and stop %1$s emitting index/follow robots directives on the front end. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')));
1030 1025 },
1031 1026 $page_slug . '_general',
1032 1027 $SECTION_METASYNC
1033 1028 );
@@ -1037,12 +1032,12 @@
1037 1032 'Disable Advance Robots Meta Box',
1038 1033 function() use ($option_key) {
1039 1034 $disabled = Metasync::get_option('general')['disable_advance_robots_metabox'] ?? false;
1040 1035 printf(
1041 - '<input type="checkbox" id="disable_advance_robots_metabox" name="' . $option_key . '[general][disable_advance_robots_metabox]" value="1" %s />',
1036 + '<input type="checkbox" id="disable_advance_robots_metabox" name="' . esc_attr($option_key) . '[general][disable_advance_robots_metabox]" value="1" %s />',
1042 1037 $disabled ? 'checked' : ''
1043 1038 );
1044 - printf('<span class="description"> Hide the Advance Robots meta box and stop %1$s emitting max-snippet, max-image-preview and max-video-preview directives. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', Metasync::get_effective_plugin_name('MetaSync'));
1039 + printf('<span class="description"> Hide the Advance Robots meta box and stop %1$s emitting max-snippet, max-image-preview and max-video-preview directives. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')));
1045 1040 },
1046 1041 $page_slug . '_general',
1047 1042 $SECTION_METASYNC
1048 1043 );
@@ -1052,12 +1047,12 @@
1052 1047 'Disable Redirection Meta Box',
1053 1048 function() use ($option_key) {
1054 1049 $disabled = Metasync::get_option('general')['disable_redirection_metabox'] ?? false;
1055 1050 printf(
1056 - '<input type="checkbox" id="disable_redirection_metabox" name="' . $option_key . '[general][disable_redirection_metabox]" value="1" %s />',
1051 + '<input type="checkbox" id="disable_redirection_metabox" name="' . esc_attr($option_key) . '[general][disable_redirection_metabox]" value="1" %s />',
1057 1052 $disabled ? 'checked' : ''
1058 1053 );
1059 - printf('<span class="description"> Hide the Redirection meta box and stop redirects created by it from running. Rules added on the Redirections screen are unaffected. Saved redirects are kept.</span>');
1054 + echo '<span class="description"> Hide the Redirection meta box and stop redirects created by it from running. Rules added on the Redirections screen are unaffected. Saved redirects are kept.</span>';
1060 1055 },
1061 1056 $page_slug . '_general',
1062 1057 $SECTION_METASYNC
1063 1058 );
@@ -1067,12 +1062,12 @@
1067 1062 'Disable Canonical Meta Box',
1068 1063 function() use ($option_key) {
1069 1064 $disabled = Metasync::get_option('general')['disable_canonical_metabox'] ?? false;
1070 1065 printf(
1071 - '<input type="checkbox" id="disable_canonical_metabox" name="' . $option_key . '[general][disable_canonical_metabox]" value="1" %s />',
1066 + '<input type="checkbox" id="disable_canonical_metabox" name="' . esc_attr($option_key) . '[general][disable_canonical_metabox]" value="1" %s />',
1072 1067 $disabled ? 'checked' : ''
1073 1068 );
1074 - printf('<span class="description"> Hide the Canonical meta box and stop %1$s emitting or overriding the canonical URL. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', Metasync::get_effective_plugin_name('MetaSync'));
1069 + printf('<span class="description"> Hide the Canonical meta box and stop %1$s emitting or overriding the canonical URL. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')));
1075 1070 },
1076 1071 $page_slug . '_general',
1077 1072 $SECTION_METASYNC
1078 1073 );
@@ -1082,12 +1077,12 @@
1082 1077 'Disable Social Media & Open Graph Meta Box',
1083 1078 function() use ($option_key) {
1084 1079 $disabled = Metasync::get_option('general')['disable_social_opengraph_metabox'] ?? false;
1085 1080 printf(
1086 - '<input type="checkbox" id="disable_social_opengraph_metabox" name="' . $option_key . '[general][disable_social_opengraph_metabox]" value="1" %s />',
1081 + '<input type="checkbox" id="disable_social_opengraph_metabox" name="' . esc_attr($option_key) . '[general][disable_social_opengraph_metabox]" value="1" %s />',
1087 1082 $disabled ? 'checked' : ''
1088 1083 );
1089 - printf('<span class="description"> Hide the Social Media &amp; Open Graph meta box and stop %1$s emitting Open Graph and Twitter tags. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', Metasync::get_effective_plugin_name('MetaSync'));
1084 + printf('<span class="description"> Hide the Social Media &amp; Open Graph meta box and stop %1$s emitting Open Graph and Twitter tags. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')));
1090 1085 },
1091 1086 $page_slug . '_general',
1092 1087 $SECTION_METASYNC
1093 1088 );
@@ -1097,12 +1092,12 @@
1097 1092 'Disable Schema Markup Meta Box',
1098 1093 function() use ($option_key) {
1099 1094 $disabled = Metasync::get_option('general')['disable_schema_markup_metabox'] ?? false;
1100 1095 printf(
1101 - '<input type="checkbox" id="disable_schema_markup_metabox" name="' . $option_key . '[general][disable_schema_markup_metabox]" value="1" %s />',
1096 + '<input type="checkbox" id="disable_schema_markup_metabox" name="' . esc_attr($option_key) . '[general][disable_schema_markup_metabox]" value="1" %s />',
1102 1097 $disabled ? 'checked' : ''
1103 1098 );
1104 - printf('<span class="description"> Hide the Schema Markup meta box and stop %1$s emitting any JSON-LD, including site-wide Local SEO markup. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', Metasync::get_effective_plugin_name('MetaSync'));
1099 + printf('<span class="description"> Hide the Schema Markup meta box and stop %1$s emitting any JSON-LD, including site-wide Local SEO markup. While disabled, %1$s does not manage or override this data — another SEO plugin, or none, is free to handle it. Saved values are kept.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')));
1105 1100 },
1106 1101 $page_slug . '_general',
1107 1102 $SECTION_METASYNC
1108 1103 );
@@ -1112,12 +1107,12 @@
1112 1107 'Disable Language Alternates Meta Box',
1113 1108 function() use ($option_key) {
1114 1109 $disabled = Metasync::get_option('general')['disable_language_alternates_metabox'] ?? false;
1115 1110 printf(
1116 - '<input type="checkbox" id="disable_language_alternates_metabox" name="' . $option_key . '[general][disable_language_alternates_metabox]" value="1" %s />',
1111 + '<input type="checkbox" id="disable_language_alternates_metabox" name="' . esc_attr($option_key) . '[general][disable_language_alternates_metabox]" value="1" %s />',
1117 1112 $disabled ? 'checked' : ''
1118 1113 );
1119 - printf('<span class="description"> Hide the Language Alternates (hreflang) panel in the block editor and stop %1$s emitting any %2$slink rel=&quot;alternate&quot; hreflang%3$s 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.</span>', Metasync::get_effective_plugin_name('MetaSync'), '&lt;', '&gt;');
1114 + printf('<span class="description"> Hide the Language Alternates (hreflang) panel in the block editor and stop %1$s emitting any %2$slink rel=&quot;alternate&quot; hreflang%3$s 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.</span>', esc_html(Metasync::get_effective_plugin_name('MetaSync')), '&lt;', '&gt;');
1120 1115 },
1121 1116 $page_slug . '_general',
1122 1117 $SECTION_METASYNC
1123 1118 );
@@ -1127,12 +1122,12 @@
1127 1122 'Disable SEO Meta Box',
1128 1123 function() use ($option_key) {
1129 1124 $disabled = Metasync::get_option('general')['disable_seo_metabox'] ?? false;
1130 1125 printf(
1131 - '<input type="checkbox" id="disable_seo_metabox" name="' . $option_key . '[general][disable_seo_metabox]" value="1" %s />',
1126 + '<input type="checkbox" id="disable_seo_metabox" name="' . esc_attr($option_key) . '[general][disable_seo_metabox]" value="1" %s />',
1132 1127 $disabled ? 'checked' : ''
1133 1128 );
1134 - printf('<span class="description"> Hide the SEO Title & Meta Description meta box on post/page edit screens</span>');
1129 + echo '<span class="description"> Hide the SEO Title & Meta Description meta box on post/page edit screens</span>';
1135 1130 },
1136 1131 $page_slug . '_general',
1137 1132 $SECTION_METASYNC
1138 1133 );
@@ -1142,9 +1137,9 @@
1142 1137 'Open External Links in New Tab/Window',
1143 1138 function() use ($option_key) {
1144 1139 $enabled = Metasync::get_option('general')['open_external_links'] ?? false;
1145 1140 printf(
1146 - '<input type="checkbox" id="open_external_links" name="' . $option_key . '[general][open_external_links]" value="1" %s />',
1141 + '<input type="checkbox" id="open_external_links" name="' . esc_attr($option_key) . '[general][open_external_links]" value="1" %s />',
1147 1142 $enabled ? 'checked' : ''
1148 1143 );
1149 1144 printf('<span class="description"> Automatically add <code>target="_blank"</code> attribute to external links appearing in your posts, pages, and other post types when rendered by %s. The attribute is applied when the url is displayed.</span>', esc_html(Metasync::get_whitelabel_otto_name()));
1150 1145 },
@@ -1160,11 +1155,11 @@
1160 1155 $current_permalink_structure = get_option('permalink_structure');
1161 1156 $current_rewrite_rules = get_option('rewrite_rules');
1162 1157 // Check if the current permalink structure is set to "Plain"
1163 1158 if (($current_permalink_structure == '/%post_id%/' || $current_permalink_structure == '') && $current_rewrite_rules == '') {
1164 - printf('<span class="description" style="color:#ff0000;opacity:1;">To ensure compatibility, Please Update your Permalink structure to any option other than "plain. For any Inquiries contact support <a href="' . get_admin_url() . 'options-permalink.php">Check Setting</a> </span>');
1159 + echo '<span class="description" style="color:#ff0000;opacity:1;">To ensure compatibility, Please Update your Permalink structure to any option other than "plain. For any Inquiries contact support <a href="' . esc_url(get_admin_url() . 'options-permalink.php') . '">Check Setting</a> </span>';
1165 1160 } else {
1166 - printf('<span class="description" style="color:#008000;opacity:1;">Permalink is Okay </span>');
1161 + echo '<span class="description" style="color:#008000;opacity:1;">Permalink is Okay </span>';
1167 1162 }
1168 1163 },
1169 1164 $page_slug . '_general',
1170 1165 $SECTION_METASYNC
@@ -1176,9 +1171,9 @@
1176 1171 'Hide Dashboard',
1177 1172 function() use ($option_key) {
1178 1173 $hide_dashboard = Metasync::get_option('general')['hide_dashboard_framework'] ?? '';
1179 1174 printf(
1180 - '<input type="checkbox" id="hide_dashboard_framework" name="' . $option_key . '[general][hide_dashboard_framework]" value="true" %s />',
1175 + '<input type="checkbox" id="hide_dashboard_framework" name="' . esc_attr($option_key) . '[general][hide_dashboard_framework]" value="true" %s />',
1181 1176 isset($hide_dashboard) && $hide_dashboard == 'true' ? 'checked' : ''
1182 1177 );
1183 1178 printf('<span class="description"> Hide the %s dashboard</span>', esc_html(Metasync_Settings_Registration::instance()->get_effective_menu_title()));
1184 1179 },
@@ -1194,9 +1189,9 @@
1194 1189 'Show ' . $this->get_effective_menu_title() . ' Status in Admin Bar',
1195 1190 function() use ($option_key) {
1196 1191 $show_admin_bar = Metasync::get_option('general')['show_admin_bar_status'] ?? true;
1197 1192 printf(
1198 - '<input type="checkbox" id="show_admin_bar_status" name="' . $option_key . '[general][show_admin_bar_status]" value="true" %s />',
1193 + '<input type="checkbox" id="show_admin_bar_status" name="' . esc_attr($option_key) . '[general][show_admin_bar_status]" value="true" %s />',
1199 1194 $show_admin_bar ? 'checked' : ''
1200 1195 );
1201 1196 printf('<span class="description">Show the %s status indicator in the WordPress admin bar.</span>', esc_html(Metasync::get_effective_plugin_name()));
1202 1197 },
@@ -1203,24 +1198,8 @@
1203 1198 $page_slug . '_general',
1204 1199 $SECTION_METASYNC
1205 1200 );
1206 1201
1207 - # Adding the "Enable Auto-updates" setting
1208 - add_settings_field(
1209 - 'enable_auto_updates',
1210 - 'Enable Automatic Updates',
1211 - function() use ($option_key) {
1212 - $enable_auto_updates = Metasync::get_option('general')['enable_auto_updates'] ?? false;
1213 - printf(
1214 - '<input type="checkbox" id="enable_auto_updates" name="' . $option_key . '[general][enable_auto_updates]" value="true" %s />',
1215 - $enable_auto_updates ? 'checked' : ''
1216 - );
1217 - printf('<span class="description">Allow WordPress to automatically update this plugin when new versions are available.</span>');
1218 - },
1219 - $page_slug . '_general',
1220 - $SECTION_METASYNC
1221 - );
1222 -
1223 1202 # Add User Role Sync Setting
1224 1203 add_settings_field(
1225 1204 'content_genius_sync_roles',
1226 1205 'Content Genius User Roles to Sync',
@@ -1322,9 +1301,9 @@
1322 1301 echo '<select id="default_page_builder" name="' . esc_attr($option_key) . '[general][default_page_builder]" style="min-width: 300px;">';
1323 1302
1324 1303 foreach ($builders as $key => $builder) {
1325 1304 $is_detected = $builder['detected'];
1326 - $label = esc_html($builder['label']);
1305 + $label = $builder['label'];
1327 1306 if ($is_detected && $key !== 'gutenberg') {
1328 1307 $label .= ' — Detected';
1329 1308 } elseif (!$is_detected) {
1330 1309 $label .= ' — Not Detected';
@@ -1336,9 +1315,9 @@
1336 1315 '<option value="%s" %s%s>%s</option>',
1337 1316 esc_attr($key),
1338 1317 selected(empty($current) ? 'gutenberg' : $current, $key, false),
1339 1318 $is_detected ? '' : ' disabled',
1340 - $label
1319 + esc_html($label)
1341 1320 );
1342 1321 }
1343 1322
1344 1323 echo '</select>';
@@ -1387,9 +1366,9 @@
1387 1366 printf(
1388 1367 '<a href="%s" class="button button-secondary"><span class="dashicons dashicons-download" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Import from SEO Plugins</a>',
1389 1368 esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-import-external'))
1390 1369 );
1391 - printf('<p class="description">Import settings and data from other SEO plugins (Yoast, Rank Math, AIOSEO, etc).</p>');
1370 + echo '<p class="description">Import settings and data from other SEO plugins (Yoast, Rank Math, AIOSEO, etc).</p>';
1392 1371 },
1393 1372 $page_slug . '_general',
1394 1373 $SECTION_METASYNC
1395 1374 );
@@ -1404,10 +1383,10 @@
1404 1383 'white_label_plugin_name',
1405 1384 'Plugin Name' . Metasync::get_tooltip_icon_html('wl_plugin_name_tooltip', 'The name shown for this plugin throughout the WordPress admin — menus, page titles, and system messages. Replace it with your own brand name (max 18 characters).'),
1406 1385 function() use ($option_key) {
1407 1386 $value = Metasync::get_option('general')['white_label_plugin_name'] ?? '';
1408 - printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_name]" value="' . esc_attr($value) . '" maxlength="18" />');
1409 - printf('<p class="description">This name will be used for general plugin branding (WordPress menus, page titles, and system messages). Maximum 18 characters.</p>');
1387 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_name]" value="' . esc_attr($value) . '" maxlength="18" />';
1388 + echo '<p class="description">This name will be used for general plugin branding (WordPress menus, page titles, and system messages). Maximum 18 characters.</p>';
1410 1389 },
1411 1390 $page_slug . '_branding',
1412 1391 $SECTION_METASYNC
1413 1392 );
@@ -1416,9 +1395,9 @@
1416 1395 'whitelabel_otto_name',
1417 1396 'OTTO Name' . Metasync::get_tooltip_icon_html('wl_otto_name_tooltip', 'The name used wherever OTTO is mentioned in the plugin (e.g. "Enable [Name] Server Side Rendering"). Rename it to match your own product branding.'),
1418 1397 function() use ($option_key) {
1419 1398 $value = Metasync::get_option('general')['whitelabel_otto_name'] ?? '';
1420 - printf('<input type="text" name="' . $option_key . '[general][whitelabel_otto_name]" value="' . esc_attr($value) . '" />');
1399 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][whitelabel_otto_name]" value="' . esc_attr($value) . '" />';
1421 1400 $example_name = !empty($value) ? $value : 'OTTO';
1422 1401 printf('<p class="description">This name will be used for OTTO feature references (e.g., "Enable %s Server Side Rendering").</p>', esc_html($example_name));
1423 1402 },
1424 1403 $page_slug . '_branding',
@@ -1430,9 +1409,9 @@
1430 1409 'Logo (Light Theme)' . Metasync::get_tooltip_icon_html('wl_logo_light_tooltip', 'Your logo shown in the admin dashboard when it\'s set to light mode. Leave blank to use the default logo.'),
1431 1410 function() use ($option_key) {
1432 1411 $whitelabel_settings = Metasync::get_whitelabel_settings();
1433 1412 $value = $whitelabel_settings['logo_light'] ?? '';
1434 - printf('<input type="url" name="' . $option_key . '[whitelabel][logo_light]" value="' . esc_attr($value) . '" size="60" />');
1413 + echo '<input type="url" name="' . esc_attr($option_key) . '[whitelabel][logo_light]" value="' . esc_attr($value) . '" size="60" />';
1435 1414 printf('<p class="description">Displayed when the admin UI is in light mode. Leave blank to use the default %s logo.</p>', esc_html(Metasync::get_effective_plugin_name()));
1436 1415 },
1437 1416 $page_slug . '_branding',
1438 1417 $SECTION_METASYNC
@@ -1443,9 +1422,9 @@
1443 1422 'Logo (Dark Theme)' . Metasync::get_tooltip_icon_html('wl_logo_dark_tooltip', 'Your logo shown in the admin dashboard when it\'s set to dark mode. Leave blank to use the default logo.'),
1444 1423 function() use ($option_key) {
1445 1424 $whitelabel_settings = Metasync::get_whitelabel_settings();
1446 1425 $value = $whitelabel_settings['logo_dark'] ?? '';
1447 - printf('<input type="url" name="' . $option_key . '[whitelabel][logo_dark]" value="' . esc_attr($value) . '" size="60" />');
1426 + echo '<input type="url" name="' . esc_attr($option_key) . '[whitelabel][logo_dark]" value="' . esc_attr($value) . '" size="60" />';
1448 1427 printf('<p class="description">Displayed when the admin UI is in dark mode. Leave blank to use the default %s logo.</p>', esc_html(Metasync::get_effective_plugin_name()));
1449 1428 },
1450 1429 $page_slug . '_branding',
1451 1430 $SECTION_METASYNC
@@ -1456,10 +1435,10 @@
1456 1435 'Dashboard URL' . Metasync::get_tooltip_icon_html('wl_dashboard_url_tooltip', 'The web address of your own branded dashboard, shown to clients instead of the Search Atlas dashboard link. This is cosmetic only — it doesn\'t change where your data is actually stored.'),
1457 1436 function() use ($option_key) {
1458 1437 $whitelabel_settings = Metasync::get_whitelabel_settings();
1459 1438 $value = $whitelabel_settings['domain'] ?? '';
1460 - printf('<input type="url" name="' . $option_key . '[whitelabel][domain]" value="' . esc_attr($value) . '" size="60" />');
1461 - printf('<p class="description">Enter your whitelabel dashboard URL (e.g., https://yourdashboard.com). Used for branding purposes.</p>');
1439 + echo '<input type="url" name="' . esc_attr($option_key) . '[whitelabel][domain]" value="' . esc_attr($value) . '" size="60" />';
1440 + echo '<p class="description">Enter your whitelabel dashboard URL (e.g., https://yourdashboard.com). Used for branding purposes.</p>';
1462 1441 },
1463 1442 $page_slug . '_branding',
1464 1443 $SECTION_METASYNC
1465 1444 );
@@ -1467,9 +1446,9 @@
1467 1446 'white_label_plugin_description',
1468 1447 'Plugin Description',
1469 1448 function() use ($option_key) {
1470 1449 $value = Metasync::get_option('general')['white_label_plugin_description'] ?? '';
1471 - printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_description]" value="' . esc_attr($value) . '" />');
1450 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_description]" value="' . esc_attr($value) . '" />';
1472 1451 },
1473 1452 $page_slug . '_branding',
1474 1453 $SECTION_METASYNC
1475 1454 );
@@ -1478,9 +1457,9 @@
1478 1457 'white_label_plugin_author',
1479 1458 'Author',
1480 1459 function() use ($option_key) {
1481 1460 $value = Metasync::get_option('general')['white_label_plugin_author'] ?? '';
1482 - printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_author]" value="' . esc_attr($value) . '" />');
1461 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_author]" value="' . esc_attr($value) . '" />';
1483 1462 },
1484 1463 $page_slug . '_branding',
1485 1464 $SECTION_METASYNC
1486 1465 );
@@ -1489,9 +1468,9 @@
1489 1468 'white_label_plugin_author_uri',
1490 1469 'Author URI',
1491 1470 function() use ($option_key) {
1492 1471 $value = Metasync::get_option('general')['white_label_plugin_author_uri'] ?? '';
1493 - # printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_author_uri]" value="' . esc_attr($value) . '" />');
1472 + # printf('<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_author_uri]" value="' . esc_attr($value) . '" />');
1494 1473 # Fixed printf usage
1495 1474 printf('<input type="text" name="%s" value="%s" />', esc_attr($option_key . '[general][white_label_plugin_author_uri]'), esc_attr($value) );
1496 1475
1497 1476 },
@@ -1502,9 +1481,9 @@
1502 1481 'white_label_plugin_uri',
1503 1482 'Plugin URI',
1504 1483 function() use ($option_key) {
1505 1484 $value = Metasync::get_option('general')['white_label_plugin_uri'] ?? ''; // New option for Plugin URI
1506 - printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_uri]" value="' . esc_attr($value) . '" />');
1485 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_uri]" value="' . esc_attr($value) . '" />';
1507 1486 },
1508 1487 $page_slug . '_branding',
1509 1488 $SECTION_METASYNC
1510 1489 );
@@ -1518,10 +1497,10 @@
1518 1497 'white_label_plugin_menu_slug',
1519 1498 'Menu Slug',
1520 1499 function() use ($option_key) {
1521 1500 $value = Metasync::get_option('general')['white_label_plugin_menu_slug'] ?? '';
1522 - printf('<input type="text" name="' . $option_key . '[general][white_label_plugin_menu_slug]" value="' . esc_attr($value) . '" placeholder="hip-advanced-seo" />');
1523 - printf('<p class="description">Short identifier for the plugin\'s admin URL &mdash; lowercase letters, numbers, and dashes only (e.g. <code>hip-advanced-seo</code>). Don\'t paste a full web address.</p>');
1501 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_menu_slug]" value="' . esc_attr($value) . '" placeholder="hip-advanced-seo" />';
1502 + echo '<p class="description">Short identifier for the plugin\'s admin URL &mdash; lowercase letters, numbers, and dashes only (e.g. <code>hip-advanced-seo</code>). Don\'t paste a full web address.</p>';
1524 1503 },
1525 1504 $page_slug . '_branding',
1526 1505 $SECTION_METASYNC
1527 1506 );
@@ -1529,9 +1508,9 @@
1529 1508 'white_label_plugin_menu_icon',
1530 1509 'Menu Icon',
1531 1510 function() use ($option_key) {
1532 1511 $value = Metasync::get_option('general')['white_label_plugin_menu_icon'] ?? '';
1533 - echo '<input type="text" name="' . $option_key . '[general][white_label_plugin_menu_icon]" value="' . esc_attr($value) . '" />';
1512 + echo '<input type="text" name="' . esc_attr($option_key) . '[general][white_label_plugin_menu_icon]" value="' . esc_attr($value) . '" />';
1534 1513 },
1535 1514 $page_slug . '_branding',
1536 1515 $SECTION_METASYNC
1537 1516 );
@@ -1546,22 +1525,22 @@
1546 1525
1547 1526 // Output radio button for Default Style.css active
1548 1527
1549 1528 printf(
1550 - '<input type="radio" id="enable_default" name="' . $option_key . '[general][enabled_plugin_css]" value="default" %s />',
1529 + '<input type="radio" id="enable_default" name="' . esc_attr($option_key) . '[general][enabled_plugin_css]" value="default" %s />',
1551 1530 ($enabled_plugin_css == 'default'||$enabled_plugin_css =='') ? 'checked' : ''
1552 1531 );
1553 - printf('<label for="enable_default">Default</label><br>');
1532 + echo '<label for="enable_default">Default</label><br>';
1554 1533
1555 1534
1556 1535 // Output radio button for Metasync Style
1557 1536 printf(
1558 - '<input type="radio" id="enable_metasync" name="' . $option_key . '[general][enabled_plugin_css]" value="metasync" %s />',
1537 + '<input type="radio" id="enable_metasync" name="' . esc_attr($option_key) . '[general][enabled_plugin_css]" value="metasync" %s />',
1559 1538 ($enabled_plugin_css == 'metasync') ? 'checked' : ''
1560 1539 );
1561 - printf('<label for="enable_metasync">Metasync Style</label>');
1540 + echo '<label for="enable_metasync">Metasync Style</label>';
1562 1541
1563 - printf('<p class="description"> Choose the default page Style Sheet: Default or MetaSync.</p>');
1542 + echo '<p class="description"> Choose the default page Style Sheet: Default or MetaSync.</p>';
1564 1543 },
1565 1544 $page_slug . '_branding',
1566 1545 $SECTION_METASYNC
1567 1546 );
@@ -1571,12 +1550,12 @@
1571 1550 'enabled_elementor_plugin_css',
1572 1551 'Choose Elementor Font Style',
1573 1552 function() use ($option_key) {
1574 1553 $enabled_elementor_plugin_css = Metasync::get_option('general')['enabled_elementor_plugin_css'] ?? 'default';
1575 - printf('<select name="' . $option_key . '[general][enabled_elementor_plugin_css]" id="heading_style">');
1576 - printf('<option value="default"'.selected($enabled_elementor_plugin_css, 'default', false).'>Default</option>');
1577 - printf('<option value="custom" '. selected($enabled_elementor_plugin_css, 'custom', false) . '>Custom</option>');
1578 - printf('</select>');
1554 + echo '<select name="' . esc_attr($option_key) . '[general][enabled_elementor_plugin_css]" id="heading_style">';
1555 + echo '<option value="default"'.selected($enabled_elementor_plugin_css, 'default', false).'>Default</option>';
1556 + echo '<option value="custom" '. selected($enabled_elementor_plugin_css, 'custom', false) . '>Custom</option>';
1557 + echo '</select>';
1579 1558 },
1580 1559 $page_slug . '_branding',
1581 1560 $SECTION_METASYNC
1582 1561 );
@@ -1584,9 +1563,9 @@
1584 1563 'enabled_elementor_plugin_css_color',
1585 1564 'Choose Elementor Font Color',
1586 1565 function() use ($option_key) {
1587 1566 $enabled_elementor_plugin_css_color = Metasync::get_option('general')['enabled_elementor_plugin_css_color'] ?? '#000000';
1588 - printf('<input type="color" id="elementor_default_color_metasync" name="' . $option_key . '[general][enabled_elementor_plugin_css_color]" value="'.$enabled_elementor_plugin_css_color.'">');
1567 + echo '<input type="color" id="elementor_default_color_metasync" name="' . esc_attr($option_key) . '[general][enabled_elementor_plugin_css_color]" value="'.esc_attr($enabled_elementor_plugin_css_color).'">';
1589 1568 },
1590 1569 $page_slug . '_branding',
1591 1570 $SECTION_METASYNC
1592 1571 );
@@ -1597,10 +1576,10 @@
1597 1576 function() use ($option_key) {
1598 1577 // Decrypted for display — this field only renders after the
1599 1578 // whitelabel password gate has been passed.
1600 1579 $value = Metasync::get_whitelabel_password();
1601 - printf('<input type="password" name="' . $option_key . '[whitelabel][settings_password]" value="' . esc_attr($value) . '" size="30" autocomplete="new-password" />');
1602 - printf('<p class="description">Set a custom password to protect the branding settings section.</p>');
1580 + echo '<input type="password" name="' . esc_attr($option_key) . '[whitelabel][settings_password]" value="' . esc_attr($value) . '" size="30" autocomplete="new-password" />';
1581 + echo '<p class="description">Set a custom password to protect the branding settings section.</p>';
1603 1582 },
1604 1583 $page_slug . '_branding',
1605 1584 $SECTION_METASYNC
1606 1585 );
@@ -1611,10 +1590,10 @@
1611 1590 function() use ($option_key) {
1612 1591 $whitelabel_settings = Metasync::get_whitelabel_settings();
1613 1592 $value = $whitelabel_settings['recovery_email'] ?? '';
1614 1593 $has_password = !empty($whitelabel_settings['settings_password']);
1615 - printf('<input type="email" name="' . $option_key . '[whitelabel][recovery_email]" value="' . esc_attr($value) . '" size="30" autocomplete="email" %s />', $has_password ? 'required' : '');
1616 - printf('<p class="description">Email address to receive password recovery. <strong>Required when password is set.</strong></p>');
1594 + printf('<input type="email" name="' . esc_attr($option_key) . '[whitelabel][recovery_email]" value="' . esc_attr($value) . '" size="30" autocomplete="email" %s />', $has_password ? 'required' : '');
1595 + echo '<p class="description">Email address to receive password recovery. <strong>Required when password is set.</strong></p>';
1617 1596 },
1618 1597 $page_slug . '_branding',
1619 1598 $SECTION_METASYNC
1620 1599 );
@@ -1644,11 +1623,11 @@
1644 1623 <td><input type="text" name="%s[whitelabel][quick_links][%d][label]" value="%s" placeholder="Link label" style="width:100%%" /></td>
1645 1624 <td><input type="url" name="%s[whitelabel][quick_links][%d][url]" value="%s" placeholder="https://…" style="width:100%%" /></td>
1646 1625 <td style="text-align:center"><input type="checkbox" name="%s[whitelabel][quick_links][%d][external]" value="1" %s /></td>
1647 1626 </tr>',
1648 - esc_attr($option_key), $i, $label,
1649 - esc_attr($option_key), $i, $url,
1650 - esc_attr($option_key), $i, checked($external, true, false)
1627 + esc_attr($option_key), esc_attr($i), esc_attr($label),
1628 + esc_attr($option_key), esc_attr($i), esc_attr($url),
1629 + esc_attr($option_key), esc_attr($i), checked($external, true, false)
1651 1630 );
1652 1631 }
1653 1632 echo '</tbody></table>';
1654 1633 },
@@ -1675,9 +1654,9 @@
1675 1654 'Organization Name',
1676 1655 function() use ($option_key) {
1677 1656 $schema = Metasync::get_option('schema') ?? [];
1678 1657 $value = $schema['org_name'] ?? '';
1679 - printf('<input type="text" name="' . $option_key . '[schema][org_name]" value="%s" size="40" placeholder="Your organization name" />', esc_attr($value));
1658 + printf('<input type="text" name="' . esc_attr($option_key) . '[schema][org_name]" value="%s" size="40" placeholder="Your organization name" />', esc_attr($value));
1680 1659 },
1681 1660 $schema_page,
1682 1661 'metasync_schema_org'
1683 1662 );
@@ -1687,9 +1666,9 @@
1687 1666 'Organization URL',
1688 1667 function() use ($option_key) {
1689 1668 $schema = Metasync::get_option('schema') ?? [];
1690 1669 $value = $schema['org_url'] ?? '';
1691 - printf('<input type="url" name="' . $option_key . '[schema][org_url]" value="%s" size="40" placeholder="https://example.com" />', esc_attr($value));
1670 + printf('<input type="url" name="' . esc_attr($option_key) . '[schema][org_url]" value="%s" size="40" placeholder="https://example.com" />', esc_attr($value));
1692 1671 },
1693 1672 $schema_page,
1694 1673 'metasync_schema_org'
1695 1674 );
@@ -1699,9 +1678,9 @@
1699 1678 'Organization Logo URL' . Metasync::get_tooltip_icon_html('schema_org_logo_tooltip', 'A direct link to your logo image. Google uses this in your Organization knowledge panel — use a clear, square-ish image on a plain background, ideally at least 112x112 pixels.'),
1700 1679 function() use ($option_key) {
1701 1680 $schema = Metasync::get_option('schema') ?? [];
1702 1681 $value = $schema['org_logo'] ?? '';
1703 - printf('<input type="url" name="' . $option_key . '[schema][org_logo]" value="%s" size="40" placeholder="https://example.com/logo.png" />', esc_attr($value));
1682 + printf('<input type="url" name="' . esc_attr($option_key) . '[schema][org_logo]" value="%s" size="40" placeholder="https://example.com/logo.png" />', esc_attr($value));
1704 1683 },
1705 1684 $schema_page,
1706 1685 'metasync_schema_org'
1707 1686 );
@@ -1711,9 +1690,9 @@
1711 1690 'Contact Telephone',
1712 1691 function() use ($option_key) {
1713 1692 $schema = Metasync::get_option('schema') ?? [];
1714 1693 $value = $schema['org_contact_telephone'] ?? '';
1715 - printf('<input type="text" name="' . $option_key . '[schema][org_contact_telephone]" value="%s" size="30" placeholder="+1-555-1234" />', esc_attr($value));
1694 + printf('<input type="text" name="' . esc_attr($option_key) . '[schema][org_contact_telephone]" value="%s" size="30" placeholder="+1-555-1234" />', esc_attr($value));
1716 1695 },
1717 1696 $schema_page,
1718 1697 'metasync_schema_org'
1719 1698 );
@@ -1723,9 +1702,9 @@
1723 1702 'Contact Type' . Metasync::get_tooltip_icon_html('schema_org_contact_type_tooltip', 'What this phone number is for, using schema.org wording such as "customer support", "sales", or "technical support". It tells search engines how the number should be labeled.'),
1724 1703 function() use ($option_key) {
1725 1704 $schema = Metasync::get_option('schema') ?? [];
1726 1705 $value = $schema['org_contact_type'] ?? '';
1727 - printf('<input type="text" name="' . $option_key . '[schema][org_contact_type]" value="%s" size="30" placeholder="customer support" />', esc_attr($value));
1706 + printf('<input type="text" name="' . esc_attr($option_key) . '[schema][org_contact_type]" value="%s" size="30" placeholder="customer support" />', esc_attr($value));
1728 1707 },
1729 1708 $schema_page,
1730 1709 'metasync_schema_org'
1731 1710 );
@@ -1735,9 +1714,9 @@
1735 1714 'Social Profile URLs' . Metasync::get_tooltip_icon_html('schema_org_same_as_tooltip', 'Links to your official profiles on other sites (Facebook, X/Twitter, LinkedIn, Wikipedia, etc.), one per line. This is the schema.org "sameAs" property — it helps search engines confirm these accounts are really you.'),
1736 1715 function() use ($option_key) {
1737 1716 $schema = Metasync::get_option('schema') ?? [];
1738 1717 $value = $schema['org_same_as'] ?? '';
1739 - printf('<textarea name="' . $option_key . '[schema][org_same_as]" rows="4" cols="50" placeholder="One URL per line&#10;https://facebook.com/...&#10;https://twitter.com/...">%s</textarea>', esc_textarea($value));
1718 + printf('<textarea name="' . esc_attr($option_key) . '[schema][org_same_as]" rows="4" cols="50" placeholder="One URL per line&#10;https://facebook.com/...&#10;https://twitter.com/...">%s</textarea>', esc_textarea($value));
1740 1719 echo '<p class="description">Enter one social profile URL per line.</p>';
1741 1720 },
1742 1721 $schema_page,
1743 1722 'metasync_schema_org'
@@ -1757,9 +1736,9 @@
1757 1736 'WebSite Name',
1758 1737 function() use ($option_key) {
1759 1738 $schema = Metasync::get_option('schema') ?? [];
1760 1739 $value = $schema['website_name'] ?? '';
1761 - printf('<input type="text" name="' . $option_key . '[schema][website_name]" value="%s" size="40" placeholder="Your website name" />', esc_attr($value));
1740 + printf('<input type="text" name="' . esc_attr($option_key) . '[schema][website_name]" value="%s" size="40" placeholder="Your website name" />', esc_attr($value));
1762 1741 },
1763 1742 $schema_page,
1764 1743 'metasync_schema_website'
1765 1744 );
@@ -1769,9 +1748,9 @@
1769 1748 'WebSite URL',
1770 1749 function() use ($option_key) {
1771 1750 $schema = Metasync::get_option('schema') ?? [];
1772 1751 $value = $schema['website_url'] ?? '';
1773 - printf('<input type="url" name="' . $option_key . '[schema][website_url]" value="%s" size="40" placeholder="https://example.com" />', esc_attr($value));
1752 + printf('<input type="url" name="' . esc_attr($option_key) . '[schema][website_url]" value="%s" size="40" placeholder="https://example.com" />', esc_attr($value));
1774 1753 },
1775 1754 $schema_page,
1776 1755 'metasync_schema_website'
1777 1756 );
@@ -1782,9 +1761,9 @@
1782 1761 function() use ($option_key) {
1783 1762 $schema = Metasync::get_option('schema') ?? [];
1784 1763 $value = $schema['website_searchbox'] ?? false;
1785 1764 printf(
1786 - '<input type="checkbox" name="' . $option_key . '[schema][website_searchbox]" value="1" %s />',
1765 + '<input type="checkbox" name="' . esc_attr($option_key) . '[schema][website_searchbox]" value="1" %s />',
1787 1766 checked($value, true, false)
1788 1767 );
1789 1768 echo '<span class="description">Adds SearchAction to WebSite schema for Google Sitelinks Searchbox.</span>';
1790 1769 },
@@ -1807,9 +1786,9 @@
1807 1786 function() use ($option_key) {
1808 1787 $schema = Metasync::get_option('schema') ?? [];
1809 1788 $value = $schema['override_woocommerce_schema'] ?? false;
1810 1789 printf(
1811 - '<input type="checkbox" name="' . $option_key . '[schema][override_woocommerce_schema]" value="1" %s />',
1790 + '<input type="checkbox" name="' . esc_attr($option_key) . '[schema][override_woocommerce_schema]" value="1" %s />',
1812 1791 checked($value, true, false)
1813 1792 );
1814 1793 echo '<span class="description">When unchecked (default), MetaSync suppresses its Product schema when WooCommerce is active to avoid duplication. Check to override WooCommerce\'s native Product schema.</span>';
1815 1794 },
@@ -1863,9 +1842,9 @@
1863 1842 'baidu_site_verification',
1864 1843 'Baidu' . Metasync::get_tooltip_icon_html('baidu_site_verification_tooltip', 'Paste the verification code Baidu Webmaster gave you. This lets Baidu confirm you own this site — it does not change anything visitors see.'),
1865 1844 function() use ($option_key) {
1866 1845 $value = Metasync::get_option('searchengines')['baidu_site_verification'] ?? '';
1867 - printf( '<input type="text" id="baidu_site_verification" name="' . $option_key . '[searchengines][baidu_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1846 + printf( '<input type="text" id="baidu_site_verification" name="' . esc_attr($option_key) . '[searchengines][baidu_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1868 1847 },
1869 1848 $page_slug . '_searchengines-verification',
1870 1849 $SECTION_SEARCHENGINE
1871 1850 );
@@ -1874,9 +1853,9 @@
1874 1853 'alexa_site_verification',
1875 1854 'Alexa' . Metasync::get_tooltip_icon_html('alexa_site_verification_tooltip', 'Paste the verification code your Alexa/site directory gave you. This confirms you own this site — it does not change anything visitors see.'),
1876 1855 function() use ($option_key) {
1877 1856 $value = Metasync::get_option('searchengines')['alexa_site_verification'] ?? '';
1878 - printf( '<input type="text" id="alexa_site_verification" name="' . $option_key . '[searchengines][alexa_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1857 + printf( '<input type="text" id="alexa_site_verification" name="' . esc_attr($option_key) . '[searchengines][alexa_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1879 1858 },
1880 1859 $page_slug . '_searchengines-verification',
1881 1860 $SECTION_SEARCHENGINE
1882 1861 );
@@ -1885,9 +1864,9 @@
1885 1864 'norton_save_site_verification',
1886 1865 'Norton Safe Web' . Metasync::get_tooltip_icon_html('norton_site_verification_tooltip', 'Paste the verification code Norton Safe Web gave you. This confirms you own this site — it does not change anything visitors see.'),
1887 1866 function() use ($option_key) {
1888 1867 $value = Metasync::get_option('searchengines')['norton_save_site_verification'] ?? '';
1889 - printf( '<input type="text" id="norton_save_site_verification" name="' . $option_key . '[searchengines][norton_save_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1868 + printf( '<input type="text" id="norton_save_site_verification" name="' . esc_attr($option_key) . '[searchengines][norton_save_site_verification]" value="%s" size="50" />', esc_attr( $value ) );
1890 1869 },
1891 1870 $page_slug . '_searchengines-verification',
1892 1871 $SECTION_SEARCHENGINE
1893 1872 );
@@ -2103,10 +2082,10 @@
2103 2082 * @return array Sanitized settings
2104 2083 */
2105 2084 public function sanitize($input)
2106 2085 {
2107 - $submitted_generation = isset($_POST['metasync_pw_reset_generation'])
2108 - ? absint($_POST['metasync_pw_reset_generation'])
2086 + $submitted_generation = isset($_POST['metasync_pw_reset_generation']) // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2087 + ? absint($_POST['metasync_pw_reset_generation']) // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2109 2088 : null;
2110 2089 // Legacy/stale forms that include a password but no reset generation
2111 2090 // must never be allowed to overwrite a newer recovery password.
2112 2091 if (!self::$allow_recovery_password_write
@@ -2150,15 +2129,15 @@
2150 2129
2151 2130 # Determine which tab is being submitted (same logic as AJAX handler)
2152 2131 # Check POST first (from AJAX), then GET (from form action URL)
2153 2132 $active_tab = 'general';
2154 - if (isset($_POST['active_tab'])) {
2155 - $active_tab = sanitize_text_field($_POST['active_tab']);
2156 - } elseif (isset($_GET['tab'])) {
2157 - $active_tab = sanitize_text_field($_GET['tab']);
2158 - } elseif (isset($_POST['_wp_http_referer'])) {
2133 + if (isset($_POST['active_tab'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2134 + $active_tab = sanitize_text_field(wp_unslash($_POST['active_tab'])); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2135 + } elseif (isset($_GET['tab'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- sanitize callback, the caller already verified the nonce
2136 + $active_tab = sanitize_text_field(wp_unslash($_GET['tab'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- sanitize callback, the caller already verified the nonce
2137 + } elseif (isset($_POST['_wp_http_referer'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2159 2138 # Parse referer URL to get tab parameter
2160 - $referer = wp_parse_url($_POST['_wp_http_referer']);
2139 + $referer = wp_parse_url(esc_url_raw(wp_unslash($_POST['_wp_http_referer']))); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- sanitize callback, the caller already verified the nonce
2161 2140 if (isset($referer['query'])) {
2162 2141 parse_str($referer['query'], $referer_params);
2163 2142 if (isset($referer_params['tab'])) {
2164 2143 $active_tab = sanitize_text_field($referer_params['tab']);
@@ -2748,9 +2727,10 @@
2748 2727 * Moved from Metasync_Admin – the admin class now delegates here.
2749 2728 */
2750 2729 public function meta_sync_save_settings()
2751 2730 {
2752 - if (!isset($_POST['meta_sync_nonce']) || !wp_verify_nonce($_POST['meta_sync_nonce'], 'meta_sync_general_setting_nonce')) {
2731 + $meta_sync_nonce = isset($_POST['meta_sync_nonce']) ? sanitize_text_field(wp_unslash($_POST['meta_sync_nonce'])) : '';
2732 + if (!wp_verify_nonce($meta_sync_nonce, 'meta_sync_general_setting_nonce')) {
2753 2733 wp_send_json_error(array('message' => 'Invalid nonce'));
2754 2734 return;
2755 2735 }
2756 2736
@@ -2797,9 +2777,9 @@
2797 2777 $url_fields = [
2798 2778 'white_label_plugin_author_uri', 'white_label_plugin_uri'
2799 2779 ];
2800 2780
2801 - $bool_fields = ['otto_disable_on_loggedin', 'otto_disable_preview_button', 'otto_disable_for_bots' , 'hide_dashboard_framework', 'show_admin_bar_status', 'enable_auto_updates', 'disable_common_robots_metabox', 'disable_advance_robots_metabox', 'disable_redirection_metabox', 'disable_canonical_metabox', 'disable_social_opengraph_metabox', 'disable_schema_markup_metabox', 'disable_language_alternates_metabox', 'disable_seo_metabox', 'open_external_links', Metasync_Seo_Backup::CONSENT_OPTION_KEY];
2781 + $bool_fields = ['otto_disable_on_loggedin', 'otto_disable_preview_button', 'otto_disable_for_bots' , 'hide_dashboard_framework', 'show_admin_bar_status','disable_common_robots_metabox', 'disable_advance_robots_metabox', 'disable_redirection_metabox', 'disable_canonical_metabox', 'disable_social_opengraph_metabox', 'disable_schema_markup_metabox', 'disable_language_alternates_metabox', 'disable_seo_metabox', 'open_external_links', Metasync_Seo_Backup::CONSENT_OPTION_KEY];
2802 2782
2803 2783 $url_fields = ['white_label_plugin_author_uri', 'white_label_plugin_uri'];
2804 2784
2805 2785 $metasync_options = Metasync::get_option();
@@ -2811,9 +2791,9 @@
2811 2791 }
2812 2792
2813 2793 $validation_errors = [];
2814 2794
2815 - $active_tab = isset($_POST['active_tab']) ? sanitize_text_field($_POST['active_tab']) : 'general';
2795 + $active_tab = isset($_POST['active_tab']) ? sanitize_text_field(wp_unslash($_POST['active_tab'])) : 'general';
2816 2796 $general_tab_submitted = ($active_tab === 'general');
2817 2797 $whitelabel_tab_submitted = ($active_tab === 'whitelabel');
2818 2798
2819 2799 // Whitelabel field validation issues are collected per-field and reported back
@@ -2823,9 +2803,10 @@
2823 2803 foreach ($text_fields as $field) {
2824 2804
2825 2805 if (isset($_POST['metasync_options']['general'][$field])) {
2826 2806
2827 - $value = trim($_POST['metasync_options']['general'][$field]);
2807 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- every storing branch below sanitises via sanitize_text_field/sanitize_title/esc_url_raw before the value reaches the option.
2808 + $value = trim(wp_unslash($_POST['metasync_options']['general'][$field]));
2828 2809
2829 2810 // searchatlas_api_key must be clearable to support key removal
2830 2811 $clearable_fields = [
2831 2812 'searchatlas_api_key',
@@ -2862,9 +2843,9 @@
2862 2843 } elseif (filter_var($value, FILTER_VALIDATE_URL)) {
2863 2844
2864 2845 $image_extensions = ['png', 'svg'];
2865 2846
2866 - $path = parse_url($value, PHP_URL_PATH);
2847 + $path = wp_parse_url($value, PHP_URL_PATH);
2867 2848 $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
2868 2849
2869 2850 if (in_array($extension, $image_extensions, true)) {
2870 2851 $metasync_options['general'][$field] = esc_url_raw($value);
@@ -2878,9 +2859,9 @@
2878 2859 // Self-correcting slug: a URL-shaped value is reduced to a valid WP
2879 2860 // menu slug so it can never break sidebar link generation.
2880 2861 $metasync_options['general'][$field] = sanitize_title($value);
2881 2862 } else {
2882 - $metasync_options['general'][$field] = sanitize_text_field($_POST['metasync_options']['general'][$field]);
2863 + $metasync_options['general'][$field] = sanitize_text_field(wp_unslash($_POST['metasync_options']['general'][$field]));
2883 2864 }
2884 2865 }
2885 2866 }
2886 2867
@@ -2887,9 +2868,9 @@
2887 2868 $textarea_fields = ['otto_bot_whitelist', 'otto_bot_blacklist'];
2888 2869 if ($general_tab_submitted) {
2889 2870 foreach ($textarea_fields as $field) {
2890 2871 if (isset($_POST['metasync_options']['general'][$field])) {
2891 - $metasync_options['general'][$field] = sanitize_textarea_field($_POST['metasync_options']['general'][$field]);
2872 + $metasync_options['general'][$field] = sanitize_textarea_field(wp_unslash($_POST['metasync_options']['general'][$field]));
2892 2873 } else {
2893 2874 $metasync_options['general'][$field] = '';
2894 2875 }
2895 2876 }
@@ -2897,9 +2878,9 @@
2897 2878
2898 2879 if ($general_tab_submitted) {
2899 2880 foreach ($bool_fields as $field) {
2900 2881 if (isset($_POST['metasync_options']['general'][$field])) {
2901 - $metasync_options['general'][$field] = filter_var($_POST['metasync_options']['general'][$field], FILTER_VALIDATE_BOOLEAN);
2882 + $metasync_options['general'][$field] = filter_var(wp_unslash($_POST['metasync_options']['general'][$field]), FILTER_VALIDATE_BOOLEAN);
2902 2883 }else {
2903 2884 $metasync_options['general'][$field] = false;
2904 2885 }
2905 2886 }
@@ -2918,9 +2899,10 @@
2918 2899 if ($general_tab_submitted || $whitelabel_tab_submitted) {
2919 2900 foreach ($url_fields as $field) {
2920 2901 if (isset($_POST['metasync_options']['general'][$field])) {
2921 2902
2922 - $value = trim($_POST['metasync_options']['general'][$field]);
2903 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- every storing branch below sanitises via sanitize_text_field/sanitize_title/esc_url_raw before the value reaches the option.
2904 + $value = trim(wp_unslash($_POST['metasync_options']['general'][$field]));
2923 2905
2924 2906 $whitelabel_clearable_url_fields = [
2925 2907 'white_label_plugin_author_uri',
2926 2908 'white_label_plugin_uri'
@@ -2936,9 +2918,9 @@
2936 2918 }
2937 2919
2938 2920 if (filter_var($value, FILTER_VALIDATE_URL)) {
2939 2921
2940 - $parsed_url = parse_url($value);
2922 + $parsed_url = wp_parse_url($value);
2941 2923 $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
2942 2924
2943 2925 if (strpos($host, '.') !== false || filter_var($host, FILTER_VALIDATE_IP)) {
2944 2926 $metasync_options['general'][$field] = esc_url_raw($value);
@@ -2955,9 +2937,9 @@
2955 2937 }
2956 2938
2957 2939 if ($general_tab_submitted) {
2958 2940 if (isset($_POST['metasync_options']['general']['content_genius_sync_roles']) && is_array($_POST['metasync_options']['general']['content_genius_sync_roles'])) {
2959 - $metasync_options['general']['content_genius_sync_roles'] = array_map('sanitize_text_field', $_POST['metasync_options']['general']['content_genius_sync_roles']);
2941 + $metasync_options['general']['content_genius_sync_roles'] = array_map('sanitize_text_field', wp_unslash($_POST['metasync_options']['general']['content_genius_sync_roles']));
2960 2942 } else {
2961 2943 $metasync_options['general']['content_genius_sync_roles'] = array();
2962 2944 }
2963 2945 }
@@ -2969,9 +2951,10 @@
2969 2951 if ($general_tab_submitted
2970 2952 && isset($_POST['metasync_options']['breadcrumbs'])
2971 2953 && is_array($_POST['metasync_options']['breadcrumbs'])
2972 2954 ) {
2973 - $breadcrumbs_input = $_POST['metasync_options']['breadcrumbs'];
2955 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- each key is sanitised below (bool cast for flags, sanitize_text_field for the separator).
2956 + $breadcrumbs_input = wp_unslash($_POST['metasync_options']['breadcrumbs']);
2974 2957
2975 2958 if (!isset($metasync_options['breadcrumbs']) || !is_array($metasync_options['breadcrumbs'])) {
2976 2959 $metasync_options['breadcrumbs'] = array();
2977 2960 }
@@ -3022,9 +3005,9 @@
3022 3005 $metasync_options['common_meta_settings'] = [];
3023 3006 }
3024 3007 foreach ($og_toggle_fields as $field) {
3025 3008 $posted = isset($_POST['metasync_options']['common_meta_settings'][$field])
3026 - ? sanitize_text_field($_POST['metasync_options']['common_meta_settings'][$field])
3009 + ? sanitize_text_field(wp_unslash($_POST['metasync_options']['common_meta_settings'][$field]))
3027 3010 : '';
3028 3011 $metasync_options['common_meta_settings'][$field] = ($posted === 'true') ? 'true' : 'false';
3029 3012 }
3030 3013 }
@@ -3031,14 +3014,15 @@
3031 3014
3032 3015 if ($general_tab_submitted && isset($_POST['metasync_options']['general']['default_page_builder'])) {
3033 3016 require_once plugin_dir_path(dirname(__FILE__)) . 'custom-pages/class-metasync-html-to-builder-converter.php';
3034 3017 $available_builders = Metasync_HTML_To_Builder_Converter::get_available_builders();
3035 - $builder_value = sanitize_text_field($_POST['metasync_options']['general']['default_page_builder']);
3018 + $builder_value = sanitize_text_field(wp_unslash($_POST['metasync_options']['general']['default_page_builder']));
3036 3019 if (isset($available_builders[$builder_value]) && $available_builders[$builder_value]['detected']) {
3037 3020 $metasync_options['general']['default_page_builder'] = $builder_value;
3038 3021 } else {
3039 3022 $metasync_options['general']['default_page_builder'] = 'gutenberg';
3040 3023 if (isset($available_builders[$builder_value]) && !$available_builders[$builder_value]['detected']) {
3024 + /* translators: %s: page builder name. */
3041 3025 $validation_errors[] = sprintf(__('Builder "%s" is not detected on this site. Reverted to Gutenberg.', 'metasync'), $available_builders[$builder_value]['label']);
3042 3026 }
3043 3027 }
3044 3028 }
@@ -3043,10 +3027,13 @@
3043 3027 }
3044 3028 }
3045 3029
3046 3030 if ($whitelabel_tab_submitted && isset($_POST['metasync_options']['whitelabel'])) {
3047 - $gp = isset($_POST['metasync_options']['general']) && is_array($_POST['metasync_options']['general']) ? $_POST['metasync_options']['general'] : [];
3048 - $wp = isset($_POST['metasync_options']['whitelabel']) && is_array($_POST['metasync_options']['whitelabel']) ? $_POST['metasync_options']['whitelabel'] : [];
3031 + // Values are only previewed from the just-sanitised $metasync_options below; nothing here is stored.
3032 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- preview-only read
3033 + $gp = isset($_POST['metasync_options']['general']) && is_array($_POST['metasync_options']['general']) ? wp_unslash($_POST['metasync_options']['general']) : [];
3034 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- preview-only read
3035 + $wp = isset($_POST['metasync_options']['whitelabel']) && is_array($_POST['metasync_options']['whitelabel']) ? wp_unslash($_POST['metasync_options']['whitelabel']) : [];
3049 3036 $plugin_name = isset($gp['white_label_plugin_name']) ? trim((string) $gp['white_label_plugin_name']) : '';
3050 3037 $logo_light = isset($wp['logo_light']) ? trim((string) $wp['logo_light']) : '';
3051 3038 $logo_dark = isset($wp['logo_dark']) ? trim((string) $wp['logo_dark']) : '';
3052 3039 $author = isset($gp['white_label_plugin_author']) ? trim((string) $gp['white_label_plugin_author']) : '';
@@ -3210,9 +3197,10 @@
3210 3197 }
3211 3198
3212 3199 if (isset($_POST['metasync_options']['whitelabel'])) {
3213 3200
3214 - $whitelabel_data = $_POST['metasync_options']['whitelabel'];
3201 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- every stored key below is validated/sanitised (FILTER_VALIDATE_URL + esc_url_raw for logos, sanitize_* for text).
3202 + $whitelabel_data = wp_unslash($_POST['metasync_options']['whitelabel']);
3215 3203 $existing_whitelabel = $metasync_options['whitelabel'] ?? [];
3216 3204
3217 3205 if (isset($whitelabel_data['logo'])) {
3218 3206 $logo_value = trim($whitelabel_data['logo']);
@@ -3253,9 +3241,9 @@
3253 3241 }
3254 3242 }
3255 3243
3256 3244 if (isset($whitelabel_data['settings_password'])) {
3257 - $password_value = (string) wp_unslash($whitelabel_data['settings_password']);
3245 + $password_value = (string) $whitelabel_data['settings_password'];
3258 3246
3259 3247 $hide_settings_enabled = isset($whitelabel_data['hide_settings']) && $whitelabel_data['hide_settings'] == '1';
3260 3248
3261 3249 if ($hide_settings_enabled && empty($password_value)) {
@@ -3353,9 +3341,9 @@
3353 3341
3354 3342 }
3355 3343
3356 3344 $redirect_url = isset($_GET['tab']) ?
3357 - admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-settings&tab='.$_GET['tab']) :
3345 + admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-settings&tab=' . sanitize_text_field(wp_unslash($_GET['tab']))) :
3358 3346 admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-settings');
3359 3347
3360 3348 // Also save edge cache settings if present in the payload (section renders inside this form)
3361 3349 if (class_exists('Metasync_Edge_Cache_Settings')) {
@@ -3362,8 +3350,9 @@
3362 3350 Metasync_Edge_Cache_Settings::save_from_post();
3363 3351 }
3364 3352
3365 3353 if ($general_tab_submitted && isset($_POST['metasync_llms_txt_settings'])) {
3354 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitised by sanitize_llms_txt_settings() before storage.
3366 3355 $llms_input = wp_unslash($_POST['metasync_llms_txt_settings']);
3367 3356 if (is_array($llms_input)) {
3368 3357 update_option('metasync_llms_txt_settings', $this->sanitize_llms_txt_settings($llms_input));
3369 3358 }
@@ -3415,9 +3404,10 @@
3415 3404 * Moved from Metasync_Admin – the admin class now delegates here.
3416 3405 */
3417 3406 public function ajax_save_execution_settings()
3418 3407 {
3419 - if (!isset($_POST['execution_settings_nonce']) || !wp_verify_nonce($_POST['execution_settings_nonce'], 'metasync_execution_settings_nonce')) {
3408 + $execution_nonce = isset($_POST['execution_settings_nonce']) ? sanitize_text_field(wp_unslash($_POST['execution_settings_nonce'])) : '';
3409 + if (!wp_verify_nonce($execution_nonce, 'metasync_execution_settings_nonce')) {
3420 3410 wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.'));
3421 3411 return;
3422 3412 }
3423 3413
@@ -4008,16 +3998,19 @@
4008 3998 public function ajax_save_headless_settings()
4009 3999 {
4010 4000 $prefix = self::HEADLESS_FIELD_PREFIX;
4011 4001
4012 - if (!isset($_POST[self::HEADLESS_NONCE_FIELD]) || !wp_verify_nonce($_POST[self::HEADLESS_NONCE_FIELD], self::HEADLESS_NONCE_ACTION)) {
4002 + $headless_nonce = isset($_POST[self::HEADLESS_NONCE_FIELD]) ? sanitize_text_field(wp_unslash($_POST[self::HEADLESS_NONCE_FIELD])) : '';
4003 + if (!wp_verify_nonce($headless_nonce, self::HEADLESS_NONCE_ACTION)) {
4013 4004 wp_send_json_error(array('message' => 'Invalid security token. Please refresh the page and try again.'));
4014 4005 } elseif (!Metasync::current_user_has_plugin_access()) {
4015 4006 wp_send_json_error(array('message' => 'Insufficient permissions to save settings.'));
4016 4007 } else {
4008 + // phpcs:disable WordPress.Security.ValidatedSanitizedInput -- sanitised via Metasync_Headless_Config::normalize_domain() inside save_headless_settings().
4017 4009 $submitted_domain = isset($_POST[$prefix . 'frontend_domain'])
4018 4010 ? trim((string) wp_unslash($_POST[$prefix . 'frontend_domain']))
4019 4011 : '';
4012 + // phpcs:enable WordPress.Security.ValidatedSanitizedInput
4020 4013
4021 4014 $stored = $this->save_headless_settings($_POST);
4022 4015
4023 4016 if (!$this->headless_settings_persisted($stored)) {
@@ -4059,13 +4052,26 @@
4059 4052 */
4060 4053 public function meta_sync_save_seo_controls()
4061 4054 {
4062 4055 try {
4063 - if (!isset($_POST['meta_sync_seo_controls_nonce']) || !wp_verify_nonce($_POST['meta_sync_seo_controls_nonce'], 'meta_sync_seo_controls_nonce')) {
4056 + $seo_controls_nonce = isset($_POST['meta_sync_seo_controls_nonce']) ? sanitize_text_field(wp_unslash($_POST['meta_sync_seo_controls_nonce'])) : '';
4057 + if (!wp_verify_nonce($seo_controls_nonce, 'meta_sync_seo_controls_nonce')) {
4064 4058 wp_send_json_error(array('message' => 'Invalid nonce'));
4065 4059 return;
4066 4060 }
4067 4061
4062 + // Same nonce-then-capability shape as the other settings save
4063 + // handlers: the nonce is not authorization, so a logged-in user
4064 + // without plugin access must still be refused here.
4065 + if (!Metasync::current_user_has_plugin_access()) {
4066 + wp_send_json_error(array('message' => 'Insufficient permissions.'), 403);
4067 + // The return is unreachable in WordPress (wp_send_json_error
4068 + // terminates the request) but keeps the handler safe under the
4069 + // non-terminating stubs the unit tests run with.
4070 + // @phpstan-ignore-next-line deadCode.unreachable
4071 + return;
4072 + }
4073 +
4068 4074 $current_options = Metasync::get_option();
4069 4075
4070 4076 $original_options = json_decode(json_encode($current_options), true);
4071 4077
@@ -4131,10 +4137,9 @@
4131 4137 }
4132 4138 wp_send_json_error(array('message' => $error_message));
4133 4139 }
4134 4140 } catch (Exception $e) {
4135 - error_log('Indexation Control AJAX Exception: ' . $e->getMessage());
4136 - error_log('Indexation Control AJAX Stack trace: ' . $e->getTraceAsString());
4141 + error_log('Indexation Control AJAX Exception: ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
4137 4142 wp_send_json_error(array('message' => 'Error saving settings: ' . $e->getMessage()));
4138 4143 }
4139 4144 }
4140 4145
@@ -4146,12 +4151,12 @@
4146 4151 * @return bool True on success, false on failure
4147 4152 */
4148 4153 public function save_bing_inline_settings_ajax()
4149 4154 {
4150 - $api_key = isset($_POST['metasync_bing_api_key_inline']) ? sanitize_text_field(wp_unslash($_POST['metasync_bing_api_key_inline'])) : '';
4151 - $endpoint = isset($_POST['metasync_bing_endpoint_inline']) ? sanitize_text_field(wp_unslash($_POST['metasync_bing_endpoint_inline'])) : 'indexnow';
4152 - $post_types = isset($_POST['metasync_bing_post_types_inline']) && is_array($_POST['metasync_bing_post_types_inline']) ? array_map('sanitize_title', wp_unslash($_POST['metasync_bing_post_types_inline'])) : [];
4153 - $disable_other_plugins = isset($_POST['metasync_bing_disable_other_plugins_inline']) ? true : false;
4155 + $api_key = isset($_POST['metasync_bing_api_key_inline']) ? sanitize_text_field(wp_unslash($_POST['metasync_bing_api_key_inline'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- caller verifies the SEO controls nonce first
4156 + $endpoint = isset($_POST['metasync_bing_endpoint_inline']) ? sanitize_text_field(wp_unslash($_POST['metasync_bing_endpoint_inline'])) : 'indexnow'; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- caller verifies the SEO controls nonce first
4157 + $post_types = isset($_POST['metasync_bing_post_types_inline']) && is_array($_POST['metasync_bing_post_types_inline']) ? array_map('sanitize_title', wp_unslash($_POST['metasync_bing_post_types_inline'])) : []; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- caller verifies the SEO controls nonce first
4158 + $disable_other_plugins = isset($_POST['metasync_bing_disable_other_plugins_inline']) ? true : false; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- caller verifies the SEO controls nonce first
4154 4159
4155 4160 $existing_settings = get_option('metasync_options_bing_instant_indexing', []);
4156 4161
4157 4162 $new_settings = [
@@ -4175,9 +4180,9 @@
4175 4180 $safe_key = sanitize_file_name( $api_key );
4176 4181 $file_path = ABSPATH . $safe_key . '.txt';
4177 4182 $real_path = realpath( ABSPATH );
4178 4183 if ( false === $real_path || 0 !== strpos( realpath( dirname( $file_path ) ), $real_path ) ) {
4179 - error_log( 'Bing IndexNow: Refusing to write outside ABSPATH' );
4184 + error_log( 'Bing IndexNow: Refusing to write outside ABSPATH' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- genuine failure path, bounded, no secrets
4180 4185 return false;
4181 4186 }
4182 4187 // serve the key virtually via template_redirect instead of
4183 4188 // writing a physical {key}.txt. An on-disk file is served statically
@@ -4190,9 +4195,9 @@
4190 4195 }
4191 4196 Metasync_Bing_Instant_Index::register_virtual_key($safe_key);
4192 4197
4193 4198 if (file_exists($file_path)) {
4194 - @unlink($file_path);
4199 + @wp_delete_file($file_path);
4195 4200 }
4196 4201
4197 4202 $current_options = Metasync::get_option();
4198 4203 if (!isset($current_options['seo_controls']['enable_binginstantindex']) || $current_options['seo_controls']['enable_binginstantindex'] !== 'true') {