is_rayetun_ag_screen() ) {
return;
}
remove_all_actions( 'admin_notices' );
remove_all_actions( 'all_admin_notices' );
remove_all_actions( 'user_admin_notices' );
remove_all_actions( 'network_admin_notices' );
}
/**
* True when the current admin screen is a AgentGarrison page.
*/
private function is_rayetun_ag_screen() {
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$screen = get_current_screen();
return $screen && false !== strpos( $screen->id, 'agentgarrison' );
}
// -------------------------------------------------------------------------
// Menu
// -------------------------------------------------------------------------
public function register_menu() {
// Shield-with-check icon — represents the plugin's role as an AI bot guardian.
// Solid fills render reliably at the ~20px admin-menu size.
$svg = 'data:image/svg+xml;base64,' . base64_encode( '' );
add_menu_page(
__( 'AgentGarrison', 'agentgarrison' ),
__( 'AgentGarrison', 'agentgarrison' ),
'manage_options',
'agentgarrison',
array( $this, 'render_page' ),
$svg,
76
);
}
// -------------------------------------------------------------------------
// Assets
// -------------------------------------------------------------------------
public function enqueue_assets( $hook ) {
$css_file = RAYETUN_AG_DIR . 'admin/css/rayetun-ag-admin.css';
$js_file = RAYETUN_AG_DIR . 'admin/js/rayetun-ag-admin.js';
$css_ver = RAYETUN_AG_VERSION . '.' . ( file_exists( $css_file ) ? filemtime( $css_file ) : '0' );
$js_ver = RAYETUN_AG_VERSION . '.' . ( file_exists( $js_file ) ? filemtime( $js_file ) : '0' );
// Menu-icon styling loads on every admin page so the top-level icon can
// react to hover/focus/current-page state. It is tiny and self-contained.
$icon_file = RAYETUN_AG_DIR . 'admin/css/rayetun-ag-menu-icon.css';
$icon_ver = RAYETUN_AG_VERSION . '.' . ( file_exists( $icon_file ) ? filemtime( $icon_file ) : '0' );
wp_enqueue_style( 'rayetun-ag-menu-icon', RAYETUN_AG_URL . 'admin/css/rayetun-ag-menu-icon.css', array(), $icon_ver );
// Post edit + post list screens: load CSS + core JS (for the score/schema
// metaboxes) but NOT Chart.js — the charts only render on AgentGarrison pages.
$post_screens = array( 'post.php', 'post-new.php', 'edit.php' );
if ( false === strpos( $hook, 'agentgarrison' ) ) {
if ( in_array( $hook, $post_screens, true ) ) {
wp_enqueue_style( 'rayetun-ag-admin', RAYETUN_AG_URL . 'admin/css/rayetun-ag-admin.css', array(), $css_ver );
wp_enqueue_script( 'rayetun-ag-admin', RAYETUN_AG_URL . 'admin/js/rayetun-ag-admin.js', array( 'jquery' ), $js_ver, true );
$this->localize_data();
}
return;
}
wp_enqueue_style( 'rayetun-ag-admin', RAYETUN_AG_URL . 'admin/css/rayetun-ag-admin.css', array(), $css_ver );
// On our own screens only, stop WordPress from replacing the emoji sidebar
// icons with Twemoji tags after load. That async swap caused a visible
// icon flash and a one-pixel layout shift ("shake"). Native emoji render once.
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
// Chart.js — bundled, no CDN. Enqueue before the admin script so it's available.
$chart_file = RAYETUN_AG_DIR . 'admin/js/vendor/chart.min.js';
if ( file_exists( $chart_file ) ) {
wp_enqueue_script( 'rayetun-ag-chartjs', RAYETUN_AG_URL . 'admin/js/vendor/chart.min.js', array(), '4.4.3', true );
}
wp_enqueue_script( 'rayetun-ag-admin', RAYETUN_AG_URL . 'admin/js/rayetun-ag-admin.js', array( 'jquery' ), $js_ver, true );
$this->localize_data();
}
private function localize_data() {
wp_localize_script( 'rayetun-ag-admin', 'rayetunAgData', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'rayetun_ag_ajax' ),
'exportNonce' => wp_create_nonce( 'rayetun_ag_export' ),
'strings' => array(
'saved' => __( 'Settings saved.', 'agentgarrison' ),
'error' => __( 'Something went wrong. Please try again.', 'agentgarrison' ),
'confirm' => __( 'Are you sure?', 'agentgarrison' ),
'loading' => __( 'Loading...', 'agentgarrison' ),
'regenerating' => __( 'Regenerating...', 'agentgarrison' ),
),
) );
}
// -------------------------------------------------------------------------
// Page router
// -------------------------------------------------------------------------
public function render_page() {
$tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'dashboard'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$allowed_tabs = array( 'dashboard', 'bot-control', 'agent-control', 'mcp', 'llms-txt', 'markdown', 'analytics', 'referrals', 'content-scorer', 'schema', 'citation-monitor', 'reports', 'settings' );
if ( ! in_array( $tab, $allowed_tabs, true ) ) {
$tab = 'dashboard';
}
// If the requested tab belongs to a disabled module, fall back to the dashboard.
$tab_module = self::tab_module_map();
if ( isset( $tab_module[ $tab ] ) && ! Rayetun_AG_Modules::is_enabled( $tab_module[ $tab ] ) ) {
$tab = 'dashboard';
}
$view_map = array(
'dashboard' => 'dashboard.php',
'bot-control' => 'bot-control.php',
'agent-control' => 'agent-control.php',
'mcp' => 'mcp.php',
'llms-txt' => 'llms-txt.php',
'markdown' => 'markdown-agents.php',
'analytics' => 'analytics.php',
'referrals' => 'referrals.php',
'content-scorer' => 'content-scorer.php',
'schema' => 'schema.php',
'citation-monitor' => 'citation-monitor.php',
'reports' => 'reports.php',
'settings' => 'settings.php',
);
// Read dark mode preference HERE — before the wrap div is rendered.
$rayetun_ag_dark_pref = get_user_meta( get_current_user_id(), 'rayetun_ag_dark_mode', true );
$view_file = RAYETUN_AG_DIR . 'admin/views/' . $view_map[ $tab ];
?>