PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
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-admin-navigation.php +50 -72 2.6.23 → 2.7.2 View file →
@@ -286,12 +286,8 @@
286 286 if (empty($whitelabel_settings['hide_indexation_control'])) {
287 287 $menu_items['seo_controls'] = ['title' => 'Indexation Control', 'slug_suffix' => '-seo-controls'];
288 288 }
289 289
290 - if ($general_options['enable_googleinstantindex'] ?? false) {
291 - $menu_items['instant_index'] = ['title' => 'Instant Indexing', 'slug_suffix' => '-instant-index'];
292 - }
293 -
294 290 if ($general_options['enable_google_console'] ?? false) {
295 291 $menu_items['google_console'] = ['title' => 'Google Console', 'slug_suffix' => '-google-console'];
296 292 }
297 293
@@ -506,20 +502,8 @@
506 502 'group' => 'seo'
507 503 ];
508 504 }
509 505
510 - // Instant Indexing - setting now stored in seo_controls (moved from Settings to Indexation Control)
511 - $seo_controls = Metasync::get_option('seo_controls');
512 - if ($seo_controls['enable_googleinstantindex'] ?? false) {
513 - $menu_items['instant_index'] = [
514 - 'title' => 'Instant Indexing',
515 - 'slug_suffix' => '-instant-index',
516 - 'callback' => 'create_admin_google_instant_index_page',
517 - 'internal_nav' => 'Instant Indexing',
518 - 'group' => 'seo'
519 - ];
520 - }
521 -
522 506 if ($general_options['enable_google_console'] ?? false) {
523 507 $menu_items['google_console'] = [
524 508 'title' => 'Google Console',
525 509 'slug_suffix' => '-google-console',
@@ -833,13 +817,8 @@
833 817
834 818 // Site Verification (in-page sidebar only in short mode)
835 819 $register_submenu('site_verification', 'Site Verification', 'Site Verification', $menu_capability, $menu_slug . '-search-engine-verify', array($admin, 'create_admin_search_engine_verification_page'));
836 820
837 - // Instant Indexing (conditional)
838 - if ($seo_controls['enable_googleinstantindex'] ?? false) {
839 - $register_submenu('instant_index', 'Instant Indexing', 'Instant Indexing', $menu_capability, $menu_slug . '-instant-index', array($admin, 'create_admin_google_instant_index_page'));
840 - }
841 -
842 821 // Google Console (conditional)
843 822 if ($general_options['enable_google_console'] ?? false) {
844 823 $register_submenu('google_console', 'Google Console', 'Google Console', $menu_capability, $menu_slug . '-google-console', array($admin, 'create_admin_google_console_page'));
845 824 }
@@ -920,10 +899,10 @@
920 899 // WordPress cannot resolve titles for pages registered under an empty
921 900 // parent, even though those pages remain reachable by URL. Restore the
922 901 // standard admin title format for our registered pages only.
923 902 add_filter('admin_title', function($admin_title, $title) use (&$admin_page_titles) {
924 - $current_page = isset($_GET['page']) && is_string($_GET['page'])
925 - ? sanitize_text_field(wp_unslash($_GET['page']))
903 + $current_page = isset($_GET['page']) && is_string($_GET['page']) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page slug for the admin title
904 + ? sanitize_text_field(wp_unslash($_GET['page'])) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page slug for the admin title
926 905 : '';
927 906
928 907 if (empty($title) && isset($admin_page_titles[$current_page])) {
929 908 $site_title = is_network_admin() || is_user_admin()
@@ -931,9 +910,9 @@
931 910 : get_bloginfo('name');
932 911
933 912 $admin_title = sprintf(
934 913 /* translators: 1: Admin page title, 2: Site or network title. */
935 - __('%1$s ‹ %2$s — WordPress'),
914 + __('%1$s ‹ %2$s — WordPress', 'metasync'),
936 915 $admin_page_titles[$current_page],
937 916 $site_title
938 917 );
939 918 }
@@ -1396,9 +1375,9 @@
1396 1375 $status_title_header = 'Not Synced - Heartbeat API not responding or unreachable';
1397 1376 $status_text_header = 'Not Synced';
1398 1377 }
1399 1378 ?>
1400 - <div class="metasync-integration-status <?php echo $status_class_header; ?>"
1379 + <div class="metasync-integration-status <?php echo esc_attr($status_class_header); ?>"
1401 1380 title="<?php echo esc_attr($status_title_header); ?>">
1402 1381 <span class="status-indicator"></span>
1403 1382 <span class="status-text"><?php echo esc_html($status_text_header); ?></span>
1404 1383 </div>
@@ -1478,10 +1457,10 @@
1478 1457
1479 1458 <?php
1480 1459 $page_slug = Metasync_Admin::$page_slug;
1481 1460 $is_plugin_page = (
1482 - (isset($_GET['page']) && strpos($_GET['page'], $page_slug) !== false) ||
1483 - (isset($_GET['page']) && strpos($_GET['page'], 'searchatlas') !== false)
1461 + (isset($_GET['page']) && strpos($_GET['page'], $page_slug) !== false) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page check for an inline config script
1462 + (isset($_GET['page']) && strpos($_GET['page'], 'searchatlas') !== false) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page check for an inline config script
1484 1463 );
1485 1464 if ($is_plugin_page):
1486 1465 ?>
1487 1466 <script type="text/javascript">
@@ -1658,9 +1637,9 @@
1658 1637 * @param string $description Optional subtitle shown below the title.
1659 1638 */
1660 1639 public function render_layout_open($page_title = '', $current_page = '', $description = '')
1661 1640 {
1662 - $theme = esc_attr(get_option('metasync_theme', 'dark'));
1641 + $theme = get_option('metasync_theme', 'dark');
1663 1642 $plugin_name = Metasync::get_effective_plugin_name();
1664 1643 $general = Metasync::get_option('general') ?? [];
1665 1644 $is_connected = Metasync_Heartbeat_Manager::instance()->is_heartbeat_connected($general);
1666 1645 // badge reflects confirmed heartbeat health (connected / stale /
@@ -1669,9 +1648,9 @@
1669 1648 $logo = $this->resolve_logo_data();
1670 1649
1671 1650 $current_theme = get_option('metasync_theme', 'dark');
1672 1651 ?>
1673 - <div class="wrap metasync-dashboard-wrap" data-theme="<?php echo $theme; ?>">
1652 + <div class="wrap metasync-dashboard-wrap" data-theme="<?php echo esc_attr($theme); ?>">
1674 1653
1675 1654 <?php
1676 1655 // Inject whitelabel color palette overrides
1677 1656 $wl_settings = Metasync::get_whitelabel_settings();
@@ -1701,8 +1680,9 @@
1701 1680 $vars .= '--' . esc_attr($grad_var) . ':linear-gradient(135deg,' . esc_attr($from) . ' 0%,' . esc_attr($to) . ' 100%);';
1702 1681 }
1703 1682 }
1704 1683 if ($vars) {
1684 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSS custom properties inside a raw <style> element; every value is a regex-validated hex color and HTML entities are not decoded in raw text elements
1705 1685 echo $selector . '{' . $vars . '}';
1706 1686 }
1707 1687 }
1708 1688 }
@@ -1777,15 +1757,19 @@
1777 1757 {
1778 1758 ?>
1779 1759 </main><!-- /.metasync-layout-main -->
1780 1760
1781 - <?php if ($show_promo):
1782 - $general = Metasync::get_option('general') ?? [];
1783 - $is_connected = Metasync_Heartbeat_Manager::instance()->is_heartbeat_connected($general);
1761 + <?php
1762 + // The sidebar exists to get an unconnected site connected. Once the
1763 + // site is linked it has nothing further to offer, so the whole
1764 + // column is dropped and the grid reflows to two columns.
1765 + $show_promo = $show_promo && ! Metasync_Heartbeat_Manager::instance()
1766 + ->is_heartbeat_connected(Metasync::get_option('general') ?? []);
1767 + if ($show_promo):
1784 1768 ?>
1785 1769 <!-- Right promo sidebar -->
1786 1770 <aside class="metasync-layout-promo">
1787 - <?php $this->render_promo_sidebar($is_connected); ?>
1771 + <?php $this->render_promo_sidebar(); ?>
1788 1772 </aside>
1789 1773 <?php endif; ?>
1790 1774
1791 1775 </div><!-- /.metasync-layout -->
@@ -1840,11 +1824,19 @@
1840 1824 $plugin_items[$key] = $item;
1841 1825 }
1842 1826 }
1843 1827
1828 + $total_items = count($seo_items) + count($plugin_items);
1829 +
1844 1830 ?>
1845 1831 <nav class="metasync-sidenav">
1846 1832
1833 + <?php if ($total_items === 0): ?>
1834 + <div class="metasync-sidenav-empty">
1835 + <?php esc_html_e('No pages are available.', 'metasync'); ?>
1836 + </div>
1837 + <?php else: ?>
1838 +
1847 1839 <!-- SEO Features group -->
1848 1840 <div class="metasync-sidenav-group">
1849 1841 <div class="metasync-sidenav-group-title">SEO Features</div>
1850 1842 <ul>
@@ -1850,12 +1842,12 @@
1850 1842 <ul>
1851 1843 <?php foreach ($seo_items as $key => $item):
1852 1844 $is_active = ($current_page === $key);
1853 1845 $icon = $icons[$key] ?? 'admin-generic';
1854 - $url = esc_url(admin_url('admin.php?page=' . $page_slug . $item['slug_suffix']));
1846 + $url = admin_url('admin.php?page=' . $page_slug . $item['slug_suffix']);
1855 1847 ?>
1856 1848 <li class="<?php echo $is_active ? 'metasync-sidenav-active' : ''; ?>">
1857 - <a href="<?php echo $url; ?>">
1849 + <a href="<?php echo esc_url($url); ?>">
1858 1850 <span class="metasync-sidenav-icon"><span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span></span>
1859 1851 <?php echo esc_html($item['title']); ?>
1860 1852 </a>
1861 1853 </li>
@@ -1870,12 +1862,12 @@
1870 1862 <?php foreach ($plugin_items as $key => $item):
1871 1863 if ($key === 'report_issue') continue;
1872 1864 $is_active = ($current_page === $key);
1873 1865 $icon = $icons[$key] ?? 'admin-generic';
1874 - $url = esc_url(admin_url('admin.php?page=' . $page_slug . $item['slug_suffix']));
1866 + $url = admin_url('admin.php?page=' . $page_slug . $item['slug_suffix']);
1875 1867 ?>
1876 1868 <li class="<?php echo $is_active ? 'metasync-sidenav-active' : ''; ?>">
1877 - <a href="<?php echo $url; ?>">
1869 + <a href="<?php echo esc_url($url); ?>">
1878 1870 <span class="metasync-sidenav-icon"><span class="dashicons dashicons-<?php echo esc_attr($icon); ?>"></span></span>
1879 1871 <?php echo esc_html($item['title']); ?>
1880 1872 </a>
1881 1873 </li>
@@ -1910,8 +1902,10 @@
1910 1902 </ul>
1911 1903 </div>
1912 1904 <?php endif; ?>
1913 1905
1906 + <?php endif; ?>
1907 +
1914 1908 </nav>
1915 1909 <?php
1916 1910 }
1917 1911
@@ -1917,17 +1911,22 @@
1917 1911
1918 1912 /**
1919 1913 * Render the right promotional sidebar.
1920 1914 *
1921 - * @param bool $is_connected Whether the plugin is authenticated.
1915 + * Only reached while the site is unconnected; the caller drops the
1916 + * whole column once the plugin is linked.
1922 1917 */
1923 - public function render_promo_sidebar($is_connected = false)
1918 + public function render_promo_sidebar()
1924 1919 {
1925 1920 $plugin_name = Metasync::get_effective_plugin_name();
1926 1921 $otto_name = Metasync::get_whitelabel_otto_name();
1927 - $settings_url = esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug));
1922 + $settings_url = admin_url('admin.php?page=' . Metasync_Admin::$page_slug);
1928 1923 $homepage = Metasync::HOMEPAGE_DOMAIN;
1929 - $is_default_brand = ( $plugin_name === 'Search Atlas' );
1924 + $docs_url = Metasync::DOCUMENTATION_DOMAIN;
1925 + // These links point at Search Atlas properties, so they only belong
1926 + // here while the plugin runs under its own name AND branding — a
1927 + // reseller can leave the name untouched and still be whitelabeled.
1928 + $is_default_brand = ( $plugin_name === 'Search Atlas' ) && ! Metasync::is_whitelabel_enabled();
1930 1929 $whitelabel = Metasync::get_whitelabel_settings();
1931 1930 $custom_links = isset($whitelabel['quick_links']) && is_array($whitelabel['quick_links'])
1932 1931 ? array_filter($whitelabel['quick_links'], function($l) { return !empty($l['url']); })
1933 1932 : [];
@@ -1932,9 +1931,8 @@
1932 1931 ? array_filter($whitelabel['quick_links'], function($l) { return !empty($l['url']); })
1933 1932 : [];
1934 1933 ?>
1935 1934
1936 - <?php if (!$is_connected): ?>
1937 1935 <!-- Connect CTA card -->
1938 1936 <div class="metasync-promo-card metasync-promo-card--connect">
1939 1937 <div class="metasync-promo-card-header">
1940 1938 <div class="metasync-promo-card-icon">
@@ -1950,40 +1948,12 @@
1950 1948 <li><span class="promo-check">&#10003;</span> Real-time keyword tracking</li>
1951 1949 <li><span class="promo-check">&#10003;</span> Automated schema markup</li>
1952 1950 <li><span class="promo-check">&#10003;</span> Instant Google indexing</li>
1953 1951 </ul>
1954 - <a href="<?php echo $settings_url; ?>" class="metasync-promo-btn metasync-promo-btn--primary">
1952 + <a href="<?php echo esc_url($settings_url); ?>" class="metasync-promo-btn metasync-promo-btn--primary">
1955 1953 Connect Now
1956 1954 </a>
1957 1955 </div>
1958 - <?php else: ?>
1959 - <!-- Connected — feature highlights -->
1960 - <div class="metasync-promo-card metasync-promo-card--accent">
1961 - <div class="metasync-promo-card-header">
1962 - <div class="metasync-promo-card-icon">
1963 - <span class="dashicons dashicons-performance"></span>
1964 - </div>
1965 - <div>
1966 - <h3><?php echo esc_html($otto_name); ?> Active</h3>
1967 - </div>
1968 - </div>
1969 - <p class="metasync-promo-tagline">Your site is connected and <?php echo esc_html($otto_name); ?> is optimizing pages automatically.</p>
1970 - <ul class="metasync-promo-benefits">
1971 - <li><span class="promo-check">&#10003;</span> Schema markup auto-applied</li>
1972 - <li><span class="promo-check">&#10003;</span> Meta titles &amp; descriptions optimized</li>
1973 - <li><span class="promo-check">&#10003;</span> Internal linking suggestions active</li>
1974 - </ul>
1975 - <?php if ($is_default_brand): ?>
1976 - <a href="<?php echo esc_url($homepage); ?>" target="_blank" rel="noopener" class="metasync-promo-btn metasync-promo-btn--outline">
1977 - View <?php echo esc_html($plugin_name); ?> Dashboard
1978 - </a>
1979 - <?php elseif (!empty($whitelabel['domain'])): ?>
1980 - <a href="<?php echo esc_url($whitelabel['domain']); ?>" target="_blank" rel="noopener" class="metasync-promo-btn metasync-promo-btn--outline">
1981 - View <?php echo esc_html($plugin_name); ?> Dashboard
1982 - </a>
1983 - <?php endif; ?>
1984 - </div>
1985 - <?php endif; ?>
1986 1956
1987 1957 <?php
1988 1958 // Quick Links: show default links for default brand, custom links if whitelabeled + provided, hide if whitelabeled + none
1989 1959 $show_quick_links = $is_default_brand || !empty($custom_links);
@@ -1993,12 +1963,20 @@
1993 1963 <div class="metasync-promo-card">
1994 1964 <h3 style="margin:0 0 12px;font-size:13px;font-weight:700;color:var(--dashboard-text-primary);">Quick Links</h3>
1995 1965 <ul class="metasync-promo-links">
1996 1966 <?php if ($is_default_brand): ?>
1967 + <li><a href="<?php echo esc_url($docs_url); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-book-alt"></span> Documentation</a></li>
1997 1968 <li><a href="<?php echo esc_url($homepage . '/blog/'); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-rss"></span> SEO Blog</a></li>
1998 - <li><a href="<?php echo esc_url($homepage . '/academy/'); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-welcome-learn-more"></span> SEO Academy</a></li>
1969 + <li><a href="<?php echo esc_url($homepage . '/contact/'); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-email-alt"></span> Contact Support</a></li>
1999 1970 <li><a href="<?php echo esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-setup-wizard')); ?>"><span class="dashicons dashicons-admin-customizer"></span> Setup Wizard</a></li>
1971 + <?php
1972 + // Mirrors the gate that decides whether the page is registered
1973 + // at all. Without it the link outlives the page for any user
1974 + // the access rules hide it from, and leads nowhere.
1975 + if (Metasync_Access_Control::user_can_access('hide_report_issue')):
1976 + ?>
2000 1977 <li><a href="<?php echo esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '-report-issue')); ?>"><span class="dashicons dashicons-sos"></span> Report Issue</a></li>
1978 + <?php endif; ?>
2001 1979 <?php else: ?>
2002 1980 <?php foreach ($custom_links as $link): ?>
2003 1981 <li><a href="<?php echo esc_url($link['url']); ?>" <?php echo !empty($link['external']) ? 'target="_blank" rel="noopener"' : ''; ?>><span class="dashicons dashicons-admin-links"></span> <?php echo esc_html($link['label'] ?: $link['url']); ?></a></li>
2004 1982 <?php endforeach; ?>