| @@ -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', |
| @@ -787,14 +771,19 @@ | ||
| 787 | 771 | |
| 788 | 772 | // ── Connect slug ────────────────────────────────────────────────── |
| 789 | 773 | $connect_slug = $menu_slug . '-connect'; |
| 790 | 774 | |
| 775 | + // Keep a slug-to-title map so hidden pages can still provide WordPress | |
| 776 | + // with a browser-tab title when they are registered without a parent. | |
| 777 | + $admin_page_titles = array(); | |
| 778 | + | |
| 791 | 779 | // Local helper: register a submenu page under the plugin parent slug |
| 792 | 780 | // when the item keeps its WordPress admin sidebar row, or under an |
| 793 | 781 | // empty parent slug (hidden from the sidebar but still reachable) |
| 794 | 782 | // otherwise. The Settings page (general) is registered against the |
| 795 | 783 | // bare $menu_slug so it doubles as the parent menu callback. |
| 796 | - $register_submenu = function($item_key, $page_title, $menu_title, $capability, $menu_page_slug, $callback) use ($menu_slug) { | |
| 784 | + $register_submenu = function($item_key, $page_title, $menu_title, $capability, $menu_page_slug, $callback) use ($menu_slug, &$admin_page_titles) { | |
| 785 | + $admin_page_titles[$menu_page_slug] = $page_title; | |
| 797 | 786 | $parent = self::item_keeps_wp_sidebar_row($item_key) ? $menu_slug : ''; |
| 798 | 787 | add_submenu_page($parent, $page_title, $menu_title, $capability, $menu_page_slug, $callback); |
| 799 | 788 | }; |
| 800 | 789 | |
| @@ -828,13 +817,8 @@ | ||
| 828 | 817 | |
| 829 | 818 | // Site Verification (in-page sidebar only in short mode) |
| 830 | 819 | $register_submenu('site_verification', 'Site Verification', 'Site Verification', $menu_capability, $menu_slug . '-search-engine-verify', array($admin, 'create_admin_search_engine_verification_page')); |
| 831 | 820 | |
| 832 | - // Instant Indexing (conditional) | |
| 833 | - if ($seo_controls['enable_googleinstantindex'] ?? false) { | |
| 834 | - $register_submenu('instant_index', 'Instant Indexing', 'Instant Indexing', $menu_capability, $menu_slug . '-instant-index', array($admin, 'create_admin_google_instant_index_page')); | |
| 835 | - } | |
| 836 | - | |
| 837 | 821 | // Google Console (conditional) |
| 838 | 822 | if ($general_options['enable_google_console'] ?? false) { |
| 839 | 823 | $register_submenu('google_console', 'Google Console', 'Google Console', $menu_capability, $menu_slug . '-google-console', array($admin, 'create_admin_google_console_page')); |
| 840 | 824 | } |
| @@ -908,11 +892,36 @@ | ||
| 908 | 892 | add_submenu_page($menu_slug, $connect_label, $connect_label, $menu_capability, $connect_slug, array($admin, 'create_admin_settings_page')); |
| 909 | 893 | } |
| 910 | 894 | |
| 911 | 895 | // ── Hidden pages (no sidebar entry needed) ──────────────────────── |
| 896 | + $admin_page_titles[$menu_slug . '-setup-wizard'] = 'Setup Wizard'; | |
| 912 | 897 | add_submenu_page('', 'Setup Wizard', 'Setup Wizard', $menu_capability, $menu_slug . '-setup-wizard', array($admin->setup_wizard, 'render_wizard_page')); |
| 913 | 898 | |
| 899 | + // WordPress cannot resolve titles for pages registered under an empty | |
| 900 | + // parent, even though those pages remain reachable by URL. Restore the | |
| 901 | + // standard admin title format for our registered pages only. | |
| 902 | + add_filter('admin_title', function($admin_title, $title) use (&$admin_page_titles) { | |
| 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 | |
| 905 | + : ''; | |
| 914 | 906 | |
| 907 | + if (empty($title) && isset($admin_page_titles[$current_page])) { | |
| 908 | + $site_title = is_network_admin() || is_user_admin() | |
| 909 | + ? get_network()->site_name | |
| 910 | + : get_bloginfo('name'); | |
| 911 | + | |
| 912 | + $admin_title = sprintf( | |
| 913 | + /* translators: 1: Admin page title, 2: Site or network title. */ | |
| 914 | + __('%1$s ‹ %2$s — WordPress', 'metasync'), | |
| 915 | + $admin_page_titles[$current_page], | |
| 916 | + $site_title | |
| 917 | + ); | |
| 918 | + } | |
| 919 | + | |
| 920 | + return $admin_title; | |
| 921 | + }, 10, 2); | |
| 922 | + | |
| 923 | + | |
| 915 | 924 | // Rename auto-generated first submenu from plugin name to "Settings" and reorder |
| 916 | 925 | add_action('admin_menu', function() use ($menu_slug) { |
| 917 | 926 | global $submenu; |
| 918 | 927 | if (!isset($submenu[$menu_slug])) { |
| @@ -1448,10 +1457,10 @@ | ||
| 1448 | 1457 | |
| 1449 | 1458 | <?php |
| 1450 | 1459 | $page_slug = Metasync_Admin::$page_slug; |
| 1451 | 1460 | $is_plugin_page = ( |
| 1452 | - (isset($_GET['page']) && strpos($_GET['page'], $page_slug) !== false) || | |
| 1453 | - (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 | |
| 1454 | 1463 | ); |
| 1455 | 1464 | if ($is_plugin_page): |
| 1456 | 1465 | ?> |
| 1457 | 1466 | <script type="text/javascript"> |
| @@ -1747,15 +1756,19 @@ | ||
| 1747 | 1756 | { |
| 1748 | 1757 | ?> |
| 1749 | 1758 | </main><!-- /.metasync-layout-main --> |
| 1750 | 1759 | |
| 1751 | - <?php if ($show_promo): | |
| 1752 | - $general = Metasync::get_option('general') ?? []; | |
| 1753 | - $is_connected = Metasync_Heartbeat_Manager::instance()->is_heartbeat_connected($general); | |
| 1760 | + <?php | |
| 1761 | + // The sidebar exists to get an unconnected site connected. Once the | |
| 1762 | + // site is linked it has nothing further to offer, so the whole | |
| 1763 | + // column is dropped and the grid reflows to two columns. | |
| 1764 | + $show_promo = $show_promo && ! Metasync_Heartbeat_Manager::instance() | |
| 1765 | + ->is_heartbeat_connected(Metasync::get_option('general') ?? []); | |
| 1766 | + if ($show_promo): | |
| 1754 | 1767 | ?> |
| 1755 | 1768 | <!-- Right promo sidebar --> |
| 1756 | 1769 | <aside class="metasync-layout-promo"> |
| 1757 | - <?php $this->render_promo_sidebar($is_connected); ?> | |
| 1770 | + <?php $this->render_promo_sidebar(); ?> | |
| 1758 | 1771 | </aside> |
| 1759 | 1772 | <?php endif; ?> |
| 1760 | 1773 | |
| 1761 | 1774 | </div><!-- /.metasync-layout --> |
| @@ -1810,11 +1823,19 @@ | ||
| 1810 | 1823 | $plugin_items[$key] = $item; |
| 1811 | 1824 | } |
| 1812 | 1825 | } |
| 1813 | 1826 | |
| 1827 | + $total_items = count($seo_items) + count($plugin_items); | |
| 1828 | + | |
| 1814 | 1829 | ?> |
| 1815 | 1830 | <nav class="metasync-sidenav"> |
| 1816 | 1831 | |
| 1832 | + <?php if ($total_items === 0): ?> | |
| 1833 | + <div class="metasync-sidenav-empty"> | |
| 1834 | + <?php esc_html_e('No pages are available.', 'metasync'); ?> | |
| 1835 | + </div> | |
| 1836 | + <?php else: ?> | |
| 1837 | + | |
| 1817 | 1838 | <!-- SEO Features group --> |
| 1818 | 1839 | <div class="metasync-sidenav-group"> |
| 1819 | 1840 | <div class="metasync-sidenav-group-title">SEO Features</div> |
| 1820 | 1841 | <ul> |
| @@ -1880,8 +1901,10 @@ | ||
| 1880 | 1901 | </ul> |
| 1881 | 1902 | </div> |
| 1882 | 1903 | <?php endif; ?> |
| 1883 | 1904 | |
| 1905 | + <?php endif; ?> | |
| 1906 | + | |
| 1884 | 1907 | </nav> |
| 1885 | 1908 | <?php |
| 1886 | 1909 | } |
| 1887 | 1910 | |
| @@ -1887,17 +1910,22 @@ | ||
| 1887 | 1910 | |
| 1888 | 1911 | /** |
| 1889 | 1912 | * Render the right promotional sidebar. |
| 1890 | 1913 | * |
| 1891 | - * @param bool $is_connected Whether the plugin is authenticated. | |
| 1914 | + * Only reached while the site is unconnected; the caller drops the | |
| 1915 | + * whole column once the plugin is linked. | |
| 1892 | 1916 | */ |
| 1893 | - public function render_promo_sidebar($is_connected = false) | |
| 1917 | + public function render_promo_sidebar() | |
| 1894 | 1918 | { |
| 1895 | 1919 | $plugin_name = Metasync::get_effective_plugin_name(); |
| 1896 | 1920 | $otto_name = Metasync::get_whitelabel_otto_name(); |
| 1897 | 1921 | $settings_url = esc_url(admin_url('admin.php?page=' . Metasync_Admin::$page_slug)); |
| 1898 | 1922 | $homepage = Metasync::HOMEPAGE_DOMAIN; |
| 1899 | - $is_default_brand = ( $plugin_name === 'Search Atlas' ); | |
| 1923 | + $docs_url = Metasync::DOCUMENTATION_DOMAIN; | |
| 1924 | + // These links point at Search Atlas properties, so they only belong | |
| 1925 | + // here while the plugin runs under its own name AND branding — a | |
| 1926 | + // reseller can leave the name untouched and still be whitelabeled. | |
| 1927 | + $is_default_brand = ( $plugin_name === 'Search Atlas' ) && ! Metasync::is_whitelabel_enabled(); | |
| 1900 | 1928 | $whitelabel = Metasync::get_whitelabel_settings(); |
| 1901 | 1929 | $custom_links = isset($whitelabel['quick_links']) && is_array($whitelabel['quick_links']) |
| 1902 | 1930 | ? array_filter($whitelabel['quick_links'], function($l) { return !empty($l['url']); }) |
| 1903 | 1931 | : []; |
| @@ -1902,9 +1930,8 @@ | ||
| 1902 | 1930 | ? array_filter($whitelabel['quick_links'], function($l) { return !empty($l['url']); }) |
| 1903 | 1931 | : []; |
| 1904 | 1932 | ?> |
| 1905 | 1933 | |
| 1906 | - <?php if (!$is_connected): ?> | |
| 1907 | 1934 | <!-- Connect CTA card --> |
| 1908 | 1935 | <div class="metasync-promo-card metasync-promo-card--connect"> |
| 1909 | 1936 | <div class="metasync-promo-card-header"> |
| 1910 | 1937 | <div class="metasync-promo-card-icon"> |
| @@ -1924,36 +1951,8 @@ | ||
| 1924 | 1951 | <a href="<?php echo $settings_url; ?>" class="metasync-promo-btn metasync-promo-btn--primary"> |
| 1925 | 1952 | Connect Now |
| 1926 | 1953 | </a> |
| 1927 | 1954 | </div> |
| 1928 | - <?php else: ?> | |
| 1929 | - <!-- Connected — feature highlights --> | |
| 1930 | - <div class="metasync-promo-card metasync-promo-card--accent"> | |
| 1931 | - <div class="metasync-promo-card-header"> | |
| 1932 | - <div class="metasync-promo-card-icon"> | |
| 1933 | - <span class="dashicons dashicons-performance"></span> | |
| 1934 | - </div> | |
| 1935 | - <div> | |
| 1936 | - <h3><?php echo esc_html($otto_name); ?> Active</h3> | |
| 1937 | - </div> | |
| 1938 | - </div> | |
| 1939 | - <p class="metasync-promo-tagline">Your site is connected and <?php echo esc_html($otto_name); ?> is optimizing pages automatically.</p> | |
| 1940 | - <ul class="metasync-promo-benefits"> | |
| 1941 | - <li><span class="promo-check">✓</span> Schema markup auto-applied</li> | |
| 1942 | - <li><span class="promo-check">✓</span> Meta titles & descriptions optimized</li> | |
| 1943 | - <li><span class="promo-check">✓</span> Internal linking suggestions active</li> | |
| 1944 | - </ul> | |
| 1945 | - <?php if ($is_default_brand): ?> | |
| 1946 | - <a href="<?php echo esc_url($homepage); ?>" target="_blank" rel="noopener" class="metasync-promo-btn metasync-promo-btn--outline"> | |
| 1947 | - View <?php echo esc_html($plugin_name); ?> Dashboard | |
| 1948 | - </a> | |
| 1949 | - <?php elseif (!empty($whitelabel['domain'])): ?> | |
| 1950 | - <a href="<?php echo esc_url($whitelabel['domain']); ?>" target="_blank" rel="noopener" class="metasync-promo-btn metasync-promo-btn--outline"> | |
| 1951 | - View <?php echo esc_html($plugin_name); ?> Dashboard | |
| 1952 | - </a> | |
| 1953 | - <?php endif; ?> | |
| 1954 | - </div> | |
| 1955 | - <?php endif; ?> | |
| 1956 | 1955 | |
| 1957 | 1956 | <?php |
| 1958 | 1957 | // Quick Links: show default links for default brand, custom links if whitelabeled + provided, hide if whitelabeled + none |
| 1959 | 1958 | $show_quick_links = $is_default_brand || !empty($custom_links); |
| @@ -1963,12 +1962,20 @@ | ||
| 1963 | 1962 | <div class="metasync-promo-card"> |
| 1964 | 1963 | <h3 style="margin:0 0 12px;font-size:13px;font-weight:700;color:var(--dashboard-text-primary);">Quick Links</h3> |
| 1965 | 1964 | <ul class="metasync-promo-links"> |
| 1966 | 1965 | <?php if ($is_default_brand): ?> |
| 1966 | + <li><a href="<?php echo esc_url($docs_url); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-book-alt"></span> Documentation</a></li> | |
| 1967 | 1967 | <li><a href="<?php echo esc_url($homepage . '/blog/'); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-rss"></span> SEO Blog</a></li> |
| 1968 | - <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> | |
| 1968 | + <li><a href="<?php echo esc_url($homepage . '/contact/'); ?>" target="_blank" rel="noopener"><span class="dashicons dashicons-email-alt"></span> Contact Support</a></li> | |
| 1969 | 1969 | <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> |
| 1970 | + <?php | |
| 1971 | + // Mirrors the gate that decides whether the page is registered | |
| 1972 | + // at all. Without it the link outlives the page for any user | |
| 1973 | + // the access rules hide it from, and leads nowhere. | |
| 1974 | + if (Metasync_Access_Control::user_can_access('hide_report_issue')): | |
| 1975 | + ?> | |
| 1970 | 1976 | <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> |
| 1977 | + <?php endif; ?> | |
| 1971 | 1978 | <?php else: ?> |
| 1972 | 1979 | <?php foreach ($custom_links as $link): ?> |
| 1973 | 1980 | <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> |
| 1974 | 1981 | <?php endforeach; ?> |