| @@ -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">✓</span> Real-time keyword tracking</li> |
| 1951 | 1949 | <li><span class="promo-check">✓</span> Automated schema markup</li> |
| 1952 | 1950 | <li><span class="promo-check">✓</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">✓</span> Schema markup auto-applied</li> | |
| 1972 | - <li><span class="promo-check">✓</span> Meta titles & descriptions optimized</li> | |
| 1973 | - <li><span class="promo-check">✓</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; ?> |